diff --git a/.gitignore b/.gitignore index a9eee0f..3cc60d5 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,3 @@ .dots/ +btop/btop.log + diff --git a/BetterDiscord/data/betterdiscord.asar b/BetterDiscord/data/betterdiscord.asar new file mode 100644 index 0000000..a30ba10 Binary files /dev/null and b/BetterDiscord/data/betterdiscord.asar differ diff --git a/BetterDiscord/data/stable/custom.css b/BetterDiscord/data/stable/custom.css new file mode 100644 index 0000000..e69de29 diff --git a/BetterDiscord/data/stable/misc.json b/BetterDiscord/data/stable/misc.json new file mode 100644 index 0000000..4f7024a --- /dev/null +++ b/BetterDiscord/data/stable/misc.json @@ -0,0 +1,12 @@ +{ + "version": "1.9.6", + "drawerStates": { + "settings": { + "addons": false, + "customcss": false, + "editor": false, + "window": false, + "developer": false + } + } +} \ No newline at end of file diff --git a/BetterDiscord/data/stable/plugins.json b/BetterDiscord/data/stable/plugins.json new file mode 100644 index 0000000..0eb5dfe --- /dev/null +++ b/BetterDiscord/data/stable/plugins.json @@ -0,0 +1,23 @@ +{ + "PluginRepo": true, + "BDFDB": true, + "ZeresPluginLibrary": false, + "BetterAudioPlayer": false, + "CallTimeCounter": true, + "BetterRoleColors": false, + "SplitLargeMessages": false, + "BetterVolume": false, + "TypingUsersAvatars": true, + "ThemeRepo": false, + "AutoIdleOnAFK": true, + "BetterAnimations": false, + "BetterBotTags": false, + "BetterInvites": false, + "BetterMediaPlayer": false, + "BetterNsfwTag": false, + "CharCounter": false, + "ActivityIcons": false, + "HideDisabledEmojis": true, + "WhoReacted": false, + "TeX": true +} \ No newline at end of file diff --git a/BetterDiscord/data/stable/settings.json b/BetterDiscord/data/stable/settings.json new file mode 100644 index 0000000..ad80ba2 --- /dev/null +++ b/BetterDiscord/data/stable/settings.json @@ -0,0 +1,37 @@ +{ + "general": { + "voiceDisconnect": true, + "showToasts": true, + "mediaKeys": false + }, + "addons": { + "addonErrors": true, + "editAction": "detached" + }, + "customcss": { + "customcss": true, + "liveUpdate": false, + "openAction": "settings" + }, + "editor": { + "lineNumbers": true, + "minimap": true, + "hover": true, + "quickSuggestions": true, + "fontSize": 14, + "renderWhitespace": "selection" + }, + "window": { + "transparency": true, + "removeMinimumSize": false, + "frame": false + }, + "developer": { + "debugLogs": false, + "devTools": false, + "debuggerHotkey": false, + "reactDevTools": false, + "inspectElement": false, + "devToolsWarning": false + } +} \ No newline at end of file diff --git a/BetterDiscord/data/stable/themes.json b/BetterDiscord/data/stable/themes.json new file mode 100644 index 0000000..f9e23b1 --- /dev/null +++ b/BetterDiscord/data/stable/themes.json @@ -0,0 +1,6 @@ +{ + "Frosted Glass": false, + "Dark Matter": false, + "Discord+": false, + "ClearVision": true +} \ No newline at end of file diff --git a/BetterDiscord/plugins/0BDFDB.config.json b/BetterDiscord/plugins/0BDFDB.config.json new file mode 100644 index 0000000..f3e1786 --- /dev/null +++ b/BetterDiscord/plugins/0BDFDB.config.json @@ -0,0 +1,25 @@ +{ + "all": { + "changeLogs": { + "BDFDB": "3.5.3", + "BetterNsfwTag": "1.3.2", + "CharCounter": "1.6.2", + "PluginRepo": "2.5.4", + "SplitLargeMessages": "1.8.0", + "ThemeRepo": "2.4.8" + }, + "choices": { + "toastPosition": "right" + }, + "general": { + "shareData": true, + "showToasts": true, + "showSupportBadges": false, + "useChromium": false + }, + "hashes": { + "0BDFDB.data.json": "b6093b5b3b9b66f4ea01370fe723f0834b6739c4", + "0BDFDB.raw.css": "8c083e2a87a82aded5f3b2e27fb8b2f816b5f74a" + } + } +} \ No newline at end of file diff --git a/BetterDiscord/plugins/0BDFDB.data.json b/BetterDiscord/plugins/0BDFDB.data.json new file mode 100644 index 0000000..e9a6af1 --- /dev/null +++ b/BetterDiscord/plugins/0BDFDB.data.json @@ -0,0 +1,5508 @@ +{ + "PluginNameMap": {}, + "PluginUrlMap": { + "BDFDB": "https://mwittrien.github.io/BetterDiscordAddons/Library/0BDFDB.plugin.js", + "CopyRawMessage": "https://mwittrien.github.io/BetterDiscordAddons/_DEAD/CopyRawMessage.plugin.js", + "CreationDate": "https://mwittrien.github.io/BetterDiscordAddons/_DEAD/CreationDate.plugin.js", + "ForceImagePreviews": "https://mwittrien.github.io/BetterDiscordAddons/_DEAD/ForceImagePreviews.plugin.js", + "JoinedAtDate": "https://mwittrien.github.io/BetterDiscordAddons/_DEAD/JoinedAtDate.plugin.js", + "ShowHiddenChannels": "https://mwittrien.github.io/BetterDiscordAddons/_DEAD/ShowHiddenChannels.plugin.js", + "ThemeSettings": "https://mwittrien.github.io/BetterDiscordAddons/_DEAD/ThemeSettings.plugin.js" + }, + "LibraryRequires": ["electron", "fs", "path", "process", "request"], + "DiscordObjects": { + "Channel": {"props": ["getRecipientId", "isManaged", "getGuildId"]}, + "Guild": {"strings": ["hasCommunityInfoSubheader()", "publicUpdatesChannelId", ".GuildFeatures.NON_COMMUNITY_RAID_ALERTS"]}, + "Invite": {"props": ["getExpiresAt", "isExpired"]}, + "Message": {"props": ["getReaction", "isEdited", "getChannelId"]}, + "Messages": {"props": ["jumpToMessage", "hasAfterCached", "forEach"]}, + "Timestamp": {"props": ["add", "dayOfYear", "hasAlignedHourOffset"]}, + "User": {"props": ["addGuildAvatarHash", "isLocalBot"]} + }, + "CustomDiscordConstants": { + "MenuItemColors": { + "BRAND": "brand", + "DANGER": "danger", + "DEFAULT": "default", + "PREMIUM": "premium", + "PREMIUMGRADIENT": "premiumgradient", + "SUCCESS": "success" + }, + "ToastIcons": { + "info": "INFO", + "danger": "CLOSE_CIRCLE", + "success": "CHECKMARK_CIRCLE", + "warning": "WARNING" + }, + "ToastPositions": { + "center": "toastscenter", + "left": "toastsleft", + "right": "toastsright" + }, + "UserBadges": { + "active_developer": "6bdc42827a38498929a4920da12695d9", + "automod": "f2459b691ac7453ed6039bbcfaccbfcd", + "bot_commands": "6f9e37f9029ff57aef81db857890005e", + "bug_hunter_lvl1": "2717692c7dca7289b35297368a940dd0", + "bug_hunter_lvl2": "848f79194d4be5ff5f81505cbd0ce1e6", + "certified_moderator": "fee1624003e2fee35cb398e125dc479b", + "guild_booster_lvl1": "51040c70d4f20a921ad6674ff86fc95c", + "guild_booster_lvl2": "0e4080d1d333bc7ad29ef6528b6f2fb7", + "guild_booster_lvl3": "72bed924410c304dbe3d00a6e593ff59", + "guild_booster_lvl4": "df199d2050d3ed4ebf84d64ae83989f8", + "guild_booster_lvl5": "996b3e870e8a22ce519b3a50e6bdd52f", + "guild_booster_lvl6": "991c9f39ee33d7537d9f408c3e53141e", + "guild_booster_lvl7": "cb3ae83c15e970e8f3d410bc62cb8b99", + "guild_booster_lvl8": "7142225d31238f6387d9f09efaa02759", + "guild_booster_lvl9": "ec92202290b48d0879b7413d2dde3bab", + "hypesquad": "bf01d1073931f921909045f3a39fd264", + "hypesquad_house_1": "8a88d63823d8a71cd5e390baa45efa02", + "hypesquad_house_2": "011940fd013da3f7fb926e4a1cd2e618", + "hypesquad_house_3": "3aa41de486fa12454c3761e8e223442e", + "partner": "3f9748e53446a137a052f3454e2de41e", + "premium": "2ba85e8026a8614b640c2837bcdfe21b", + "premium_early_supporter": "7060786766c9c840eb3019e725d2b358", + "staff": "5e74e9b61934fc1f67c65515d1f7e60d", + "verified_developer": "6df5892e0f35b051f8b61eace34f4967" + }, + "UserPremiumLevels": {"1": 1, "2": 2, "3": 3, "4": 6, "5": 9, "6": 12, "7": 15, "8": 18, "9": 24} + }, + "DiscordConstants": { + "AutocompleterResultTypes": ["VOICE_CHANNEL", "TEXT_CHANNEL", "USER", "LINK"], + "ChannelTextAreaTypes": ["CREATE_FORUM_POST", "PROFILE_BIO_INPUT", "FORUM_CHANNEL_GUIDELINES"], + "Colors": ["SPOTIFY", "RED_100", "BLUE"], + "InboxTabs": ["MENTIONS", "UNREADS"], + "MessageTypeGroups": ["USER_MESSAGE", "DELETABLE"], + "ReadStateTypes": ["GUILD_EVENT", "NOTIFICATION_CENTER", "CHANNEL"], + "UserSettingsActionTypes": ["SLOW_USER_ACTION", "DAILY"] + }, + "LibraryModules": { + "AckUtils": {"props": ["ack", "bulkAck", "localAck"]}, + "ActivityUtils": {"props": ["sendActivityInvite", "updateActivity"]}, + "AnalyticsUtils": {"props": ["isThrottled", "track"]}, + "AnimationUtils": {"props": ["spring", "decay"]}, + "APIEncodeUtils": {"props": ["stringify", "parse", "encode"]}, + "APIUtils": {"props": ["getAPIBaseURL"]}, + "AppearanceSettingsUtils": {"props": ["updateLocale", "updateTheme"]}, + "ApplicationAssetUtils": {"props": ["getAssetImage", "getAssetIds"]}, + "AppUtils": {"props": ["clipboard", "os"]}, + "ArrayUtils": {"props": ["isArrayLike", "zipObject"]}, + "CategoryCollapseUtils": {"props": ["categoryCollapse", "categoryExpand"]}, + "ChannelUtils": {"props": ["selectChannel", "selectPrivateChannel"]}, + "ChatRestrictionUtils": {"props": ["applyChatRestrictions"]}, + "ConnectionMetadataUtils": {"props": ["generateEbayMetadataItems", "generatePaypalMetadataItems"], "exported": false, "value": "exports", "map": { + "getEbay": ["generateEbayMetadataItems"], + "getPaypal": ["generatePaypalMetadataItems"], + "getReddit": ["generateRedditMetadataItems"], + "getSteam": ["generateSteamMetadataItems"], + "getTiktok": ["generateTikTokMetadataItems"], + "getTwitter": ["generateTwitterMetadataItems"] + }}, + "ConnectionProviderUtils": {"props": ["get", "isSupported", "filter"]}, + "ConnectionUtils": {"props": ["setShowActivity", "setVisibility"]}, + "ContextMenuUtils": {"props": ["openContextMenu", "closeContextMenu"]}, + "CurrentUserStore": {"props": ["getCurrentUser"]}, + "DesktopNotificationUtils": {"props": ["showNotification", "requestPermission"]}, + "DispatchApiUtils": {"props": ["dispatch", "isDispatching"]}, + "DispatchUtils": {"props": ["ComponentDispatch", "ComponentDispatcher"]}, + "EmojiUtils": {"props": ["translateInlineEmojiToSurrogates", "translateSurrogatesToInlineEmoji"]}, + "EmojiStateUtils": {"props": ["getURL", "isEmojiDisabled"]}, + "Env": {"props": ["env"]}, + "FolderSettingsUtils": {"props": ["saveGuildFolders", "saveClientTheme"]}, + "GuildBoostUtils": {"props": ["getNextTier", "getAppliedGuildBoostMonths"]}, + "GuildNotificationsUtils": {"props": ["updateChannelOverrideSettings", "updateGuildNotificationSettings"]}, + "GuildUtils": {"props": ["selectGuild", "transitionToGuildSync"]}, + "HistoryUtils": {"props": ["transitionTo", "transitionToGuild"]}, + "IconUtils": {"props": ["getGuildIconURL", "getGuildBannerURL"]}, + "InviteUtils": {"props": ["acceptInvite", "createInvite"]}, + "InternalReactUtils": {"props": ["jsx", "jsxs", "Fragment"]}, + "KeyCodeUtils": {"props": ["codeToKey", "keyToCode"]}, + "KeyEvents": {"props": ["aliases", "code", "codes"]}, + "LoginUtils": {"props": ["login", "logout"]}, + "MediaComponentUtils": {"props": ["renderImageComponent", "renderVideoComponent"]}, + "MediaEngineUtils": {"props": ["setOutputDevice", "setInputDevice"]}, + "MemberDisplayUtils": {"props": ["getDisplayProfile", "default"], "exported": false, "value": "exports", "map": { + "getUserProfile": ["default"] + }}, + "MentionUtils": {"props": ["isRawMessageMentioned", "isMentioned"]}, + "MessageAuthorUtils": {"props": ["getMessageAuthor", "getUserAuthor"], "exported": false, "value": "exports", "map": { + "getAuthor": ["default"] + }}, + "MessageComponentUtils": {"props": ["useClickReply", "useHoveredMessage"]}, + "MessageManageUtils": {"props": ["copyLink", "replyToMessage"]}, + "MessageParser": {"props": ["parseEmbedTitle", "defaultRules"]}, + "MessagePinUtils": {"props": ["pinMessage", "unpinMessage"]}, + "MessageUtils": {"props": ["receiveMessage", "editMessage"]}, + "ModalUtils": {"props": ["openModal", "hasModalOpen", "useHasAnyModalOpen"]}, + "NitroUtils": {"props": ["canUseIncreasedMessageLength", "canUsePremiumGuildMemberProfile"]}, + "NotificationSettingsUtils": {"props": ["setDesktopType", "setTTSType"]}, + "PlatformUtils": {"props": ["isAndroid", "isWindows"]}, + "PermissionRoleUtils": {"props": ["can", "ALLOW", "DENY"]}, + "PreferencesContext": {"strings": ["reducedMotion", "forcedColors", "alwaysShowLinkDecorations", "createContext"]}, + "PrivateChannelUtils": {"props": ["addRecipient", "openPrivateChannel"]}, + "QuerySearchUtils": {"props": ["queryGuilds", "queryGroupDMs"]}, + "ReactionEmojiUtils": {"props": ["getReactionEmojiName", "getAccessibleEmojiDisplayName"]}, + "RecentMentionUtils": {"props": ["deleteRecentMention", "fetchRecentMentions"]}, + "RelationshipUtils": {"props": ["addRelationship", "removeRelationship"]}, + "RoleIconUtils": {"props": ["canGuildUseRoleIcons", "getRoleIconData"]}, + "RTCConnectionUtils": {"props": ["getChannelId", "getGuildId", "getRTCConnectionId"]}, + "SearchPageUtils": {"props": ["changePage", "search", "clearHistory"]}, + "SimpleMarkdownParser": {"props": ["parseBlock", "parseInline", "defaultOutput"]}, + "SlateRichUtils": {"props": ["createState", "toRichValue"]}, + "SlateTextUtils": {"props": ["serializeDescendant", "toTextValue"]}, + "SoundParser": {"strings": ["discodo", "ddr-down", "mute"]}, + "SoundUtils": {"props": ["createSound", "createSoundForPack", "playSound"]}, + "SpotifyUtils": {"props": ["getAccessToken", "pause", "play"]}, + "StoreChangeUtils": {"props": ["get", "set", "clear", "remove"]}, + "TimeoutUtils": {"strings": ["_timeout.isStarted()"]}, + "TimestampUtils": {"props": ["fromTimestamp", "extractTimestamp"]}, + "UploadUtils": {"props": ["upload", "instantBatchUpload"]}, + "URLParser": {"props": ["parse", "resolveObject"]}, + "UserNameUtils": {"props": ["getNickname", "getName", "useName"]}, + "UserPopoutUtils": {"props": ["openUserContextMenu", "openModerateRoleContextMenu"]}, + "UserProfileUtils": {"props": ["fetchProfile", "getUser"]}, + "UserSettingsProtoUtils": {"props": ["addDismissedContent", "updateUserChannelSettings"]}, + "UserSettingsUtils": {"props": ["open", "updateAccount"]}, + "Utilities": {"props": ["flatMap", "cloneDeep"]}, + "WindowUtils": {"props": ["minimize", "maximize", "close"]} + }, + "NativeSubComponents": { + "Button": {"props": ["Colors", "Hovers", "Looks"]}, + "Checkbox": {"props": ["Aligns", "Shapes", "Types"]}, + "Clickable": {"strings": [".ENTER", "renderNonInteractive", ".handleKeyPress"]}, + "KeybindRecorder": {"strings": [".RECORDING", ".DEFAULT", "toggleRecordMode"]}, + "PopoutContainer": {"props": ["Animation", "defaultProps"]}, + "RadioGroup": {"strings": ["itemInfoClassName", "hasSelection", ".radioItemIconClassName"]}, + "SearchBar": {"strings": ["this.inputRef", ".containerRef", ".handleOnChange"]}, + "SearchableSelect": {"strings": ["serialize", "haspopup", "maxVisibleItems"]}, + "Slider": {"strings": [".stickToMarkers", "sortedMarkers"]}, + "TabBar": {"props": ["Item", "Header", "Panel"]}, + "Table": {"props": ["SortDirection", "defaultProps"]}, + "TextArea": {"strings": ["onKeyDown", "defaultDirty"]}, + "TextInput": {"strings": ["onFocus", "MAXIMUM_LENGTH_ERROR", "getIsOverFlowing"]}, + "TooltipContainer": {"strings": ["Tooltip cannot find DOM node", "shouldShowTooltip", "clickable"]} + }, + "LibraryComponents": { + "Anchor": {"strings": ["anchorUnderlineOnHover", "noreferrer noopener"]}, + "Animations": {"props": ["Controller", "Spring", "animated"]}, + "AppReferencePositionLayer": {"props": ["AppReferencePositionLayer", "appLayerContext"], "funcStrings": ["{children:(", "{ref:"]}, + "AutocompleterComponents": {"props": ["AutocompleterQuerySymbols", "createHeaderResult"]}, + "AvatarConstants": {"strings": ["\"SIZE_20\"", "\"SIZE_32\""], "exported": false, "value": "exports", "map": { + "Sizes": ["\"SIZE_20\"", "\"SIZE_32\""] + }}, + "Avatars": {"props": ["AnimatedAvatar", "Avatar"], "exported": false, "value": "exports", "map": { + "AnimatedAvatar": ["AnimatedAvatar"], + "Avatar": ["Avatar"] + }}, + "Badges": {"strings": ["disableColor", "baseShapeRound", "textBadge"], "exported": false, "value": "exports", "map": { + "BadgeShapes": ["baseShapeRoundRight", "baseShapeRoundLeft"], + "IconBadge": [".iconBadge"], + "NumberBadge": [".numberBadge"], + "TextBadge": [".textBadge"] + }}, + "Checkmark": {"strings": ["d:\"M8.99991 16.17L4.82991"], "funcStrings": ["foreground:"]}, + "Emoji": {"strings": ["emojiName", "shouldAnimate", "jumboable"], "funcStrings": ["autoplay"]}, + "EmojiButton": {"strings": ["renderButtonContents", "PickerPerRow", "SELECT_EMOJI"]}, + "EmojiPicker": {"strings": ["emojiSize", "disableEmojiTutorial", ",emojiPaddingHorizontal:"], "funcStrings": ["compare"]}, + "Flex": {"props": ["Wrap", "Direction", "Child"]}, + "FlowerStar": {"strings": ["flowerStarContainer", ".jsx)"]}, + "FocusRingScope": {"strings": ["FocusRing", "ringTarget was not"]}, + "FormComponents": {"props": ["FormText", "FormTitleTags", "FormDivider"], "exported": false, "value": "exports", "map": { + "FormDivider": ["FormDivider"], + "FormItem": ["FormItem"], + "FormNotice": ["FormNotice"], + "FormSection": ["FormSection"], + "FormTags": ["FormTitleTags"], + "FormText": ["FormText"], + "FormTextTypes": ["FormTextTypes"], + "FormTitle": ["FormTitle"] + }}, + "GuildBadge": {"strings": ["foregroundDarkColor", "getBadgeCategory"]}, + "GuildIcon": {"strings": ["SMOL", "badgeStrokeColor"], "funcStrings": ["render()"]}, + "GuildItem": {"strings": ["guildNode:", "useShouldShowInvitesDisabledNotif", "clearGuildBurstPreview"]}, + "GuildTooltipMutedText": {"strings": [".muteConfig", "color:\"text-muted\""]}, + "Heading": {"strings": ["data-excessive", ".defaultColor"]}, + "HeaderBarComponents": {"strings": ["isAuthenticated", ".HEADER_BAR"]}, + "Image": {"strings": ["containerRef", "zoomable", "LOADING"]}, + "ImageModal": {"strings": ["renderLinkComponent", ".Messages.OPEN_IN_BROWSER", "animated"]}, + "ImageVideoModal": {"strings": ["renderLinkComponent", ".Messages.OPEN_IN_BROWSER", "videoWrapper"]}, + "LayerContainerComponents": {"strings": ["layerContainerElement", "LayerProvider"], "exported": false, "value": "exports", "map": { + "createLayer": [".createContext"], + "LayerContainer": [".layerContainer", "missing parent"] + }}, + "LazyImage": {"protos": ["loadImage", "getRatio", "getSrc"]}, + "Mask": {"props": "Masks"}, + "Menu": {"strings": ["Menu API only allows Items and groups of Items as children"]}, + "MessageGroup": {"strings": [".ephemeral", "animateAvatar:", "getMessageByReference"]}, + "MessagesPopoutComponents": {"strings": ["canCloseAllMessages", "loadingMore"], "exported": false, "value": "exports", "map": { + "EmptyState": [".emptyPlaceholder", "backgroundImage"], + "Header": [".header", "heading"], + "Popout": ["renderHeader", "renderEmptyState"] + }}, + "ModalComponents": {"props": ["Modal", "ModalCloseButton"], "exported": false, "value": "exports", "map": { + "Modal": ["Modal"], + "ModalCloseButton": ["ModalCloseButton"], + "ModalContent": ["ModalContent"], + "ModalFooter": ["ModalFooter"], + "ModalHeader": ["ModalHeader"], + "ModalListContent": ["ModalListContent"], + "ModalRoot": ["ModalRoot"], + "ModalSize": ["ModalSize"] + }}, + "Paginator": {"strings": ["totalPageCount", "selectedPage", "endButtonInner"]}, + "PanelButton": {"strings": ["Masks.PANEL_BUTTON"]}, + "PopoutFocusLock": {"strings": ["impressionName", "useImperativeHandle"], "funcStrings": ["impressionName"]}, + "PrivateChannelItems": {"strings": ["getRecipientId", "LEAVE_GROUP_DM_MANAGED_BODY"], "exported": false, "value": "exports", "map": { + "DirectMessage": ["getUser", "getRecipientId"], + "LinkButton": [".apply(this"] + }}, + "Scrollers": {"props": ["ScrollerThin", "ScrollerAuto", "ScrollerNone"], "exported": false, "value": "exports", "map": { + "Auto": ["ScrollerAuto"], + "None": ["ScrollerNone"], + "Thin": ["ScrollerThin"] + }}, + "SearchResultsPagination": {"strings": ["pageLength", "changePage", "maxVisiblePages"]}, + "SpinnerComponents": {"strings": ["WANDERING_CUBES", "wanderingCubes", ".spinningCircleInner"], "exported": false, "value": "exports", "map": { + "Spinner": [".spinningCircleInner"], + "Types": ["WANDERING_CUBES", "wanderingCubes"] + }}, + "StatusComponents": {"props": ["Status", "StatusTypes"], "exported": false, "value": "exports", "map": { + "Status": ["Status"], + "Types": ["StatusTypes"] + }}, + "Text": {"strings": ["lineClamp:", ".selectable", ".defaultColor"]}, + "TextElement": {"strings": [".strong]", ".STANDARD"]}, + "Timeout": {"protos": ["start", "stop", "isStarted"]}, + "UserBadgeComponents": {"strings": [".PROFILE_USER_BADGES", "SIZE_24"], "exported": false, "value": "exports", "map": { + "UserBadges": [".PROFILE_USER_BADGES"], + "Sizes": ["\"SIZE_24\":"] + }}, + "UserPopout": {"strings": ["{user:", "userId:", "\"Unexpected missing user\")", "getUser"]}, + "UserPopoutSection": {"strings": [".section,", ".lastSection"]}, + "UserSummaryItem": {"strings": ["popoutUserId:", ".clickableAvatar"]}, + "VideoForwardRef": {"strings": ["HTMLSourceElement", "autoPlay"], "funcStrings": ["externalRef"]} + }, + "ContextMenuTypes": { + "AudioDeviceContextMenu": "audio-device-context", + "ChannelContextMenu": "channel-context", + "DeveloperContextMenu": "dev-context", + "GroupDMContextMenu": "gdm-context", + "GuildContextMenu": "guild-context", + "GuilDiscoveryContextMenu": "guild-discovery-context-menu", + "GuildEntryContextMenu": "guild-entry-context", + "GuildHeaderContextMenu": "guild-header-popout", + "ImageContextMenu": "image-context", + "MessageContextMenu": "message", + "TextAreaContextMenu": "textarea-context", + "ThreadContextMenu": "thread-context", + "UserContextMenu": "user-context", + "UserSettingsCogContextMenu": "user-settings-cog" + }, + "PatchModules": { + "Account": {"protos": ["renderAvatarWithPopout", "renderCopiableNameTag", "renderCopiedSuccess"]}, + "AnalyticsContext": {"props": ["ObjectTypes", "Objects", "Pages", "Sections"]}, + "AuditLogEntry": {"protos": ["renderRoleUpdate", "renderChangeDetails", "getActionTypeColor", "renderPermissionUpdate"]}, + "AuditLogs": {"strings": ["Messages.SEARCH_MEMBERS", "._lastExpandedRef", ".handleActionFilterQueryClear"]}, + "AuthWrapper": {"protos": ["renderDefault", "renderMobile", "mobileReplaceWith"]}, + "Autocomplete": {"strings": ["autocompleteInner", "innerClassName:", "autocomplete"]}, + "AutocompleteChannelResult": {"strings": ["channel:", "category:", ".type===", ".GUILD_CATEGORY?", "AutocompleteRowHeading"], "noSearch": true}, + "AutocompleteRoleResult": {"strings": ["hideDescription:", "colorString:", ".Messages.MENTION_USERS_WITH_ROLE"], "noSearch": true}, + "AutocompleteRowContentPrimary": {"strings": [".jsx)(\"div\"", "autocompleteRowContentPrimary"]}, + "AutocompleteRowContentSecondary": {"strings": [".jsx)(", "autocompleteRowContentSecondary"]}, + "AutocompleteRowIcon": {"strings": [".jsx)(\"div\"", "autocompleteRowIcon"]}, + "AutocompleteUserResult": {"strings": ["hidePersonalInformation:", ".descriptionDiscriminator", "\"#\"", "Sizes.SIZE_24"], "noSearch": true}, + "BlobMask": {"protos": ["getBadgePositionInterpolation", "getUpperBadgeStyles"]}, + "BlockedMessageGroup": {"strings": ["collapsed-message-item", "isBeforeGroup", "collapsedReason"]}, + "ChannelCall": {"strings": ["maybeLeaveFullScreen", "handleFullscreenParticipant", "CHANNEL_CALL_POPOUT"]}, + "ChannelCallHeader": {"strings": ["focusedApplication:", "focusedParticipant:", ".getSelectedParticipant", "appContext"]}, + "ChannelCallGrid": {"strings": ["totalNumberOfParticipants:", "keyExtractor:", "channel_user_limit:"]}, + "ChannelCallVideoParticipants": {"strings": [".tileSizer", "participantTileWidth:", ".COVER"]}, + "ChannelEmptyMessages": {"strings": [".Messages.BEGINNING_CHANNEL_WELCOME", ".Messages.BEGINNING_GROUP_DM_MANAGED", "showingBanner:"]}, + "ChannelFloatingSidebar": {"strings": ["messageRequestSidebarWidth", "floatingLayer:", "chatLayerWrapper"]}, + "ChannelItem": {"strings": ["canHaveDot", "unreadRelevant", "UNREAD_HIGHLIGHT"]}, + "ChannelItemIcon": {"strings": ["hasActiveThreads:", "locked:", ".iconContainer"]}, + "ChannelMembers": {"strings": ["MEMBER_LIST_VIEWED", "getDimensions", "member-"]}, + "ChannelPins": {"strings": ["PINNED_MESSAGES", "renderEmptyState", "listName"]}, + "ChannelReply": {"strings": [".messageReference", ".REPLY_MENTION_OFF", ".mentionIcon"]}, + "ChannelSidebar": {"strings": ["ACCOUNT_PANEL", "RTC_CONNECTION_PANEL", "ACCOUNT_A11Y_LABEL"]}, + "ChannelsList": {"protos": ["isUnreadVisible", "renderBottomUnread", "getSectionRowsFromChannel"]}, + "ChannelTextAreaButtons": {"strings": [".buttons", "\"expression\"", "activeCommandOption"]}, + "ChannelTextAreaContainer": {"strings": ["TOGGLE_EMOJI_POPOUT", "textValue", "richValue"]}, + "ChannelTextAreaCounter": {"strings": [".flairContainer", "CHARACTER_COUNT_CLOSE_TO_LIMIT"]}, + "ChannelTextAreaEditor": {"strings": ["onHideAutocomplete", "onMaybeShowAutocomplete", "uploadPromptCharacterCount", "_focusBlurQueue"]}, + "ChannelTextAreaForm": {"strings": ["renderApplicationCommandIcon", "handleTextareaChange", "handleSendMessage"]}, + "ChannelThreadItem": {"strings": [".typeThread", ".modeUnread", ".Messages.GUILD_SIDEBAR_THREAD"]}, + "CircleIconButton": {"strings": ["circleIconButton", "lowerBadge", "circleButtonMask"]}, + "Clickable": {"strings": [".ENTER", "renderNonInteractive", ".handleKeyPress"]}, + "CloseButton": {"strings": [".closeButton", ".closeIcon", "offset:{"]}, + "ConnectedLazyImageZoomable": {"strings": ["isWindowFocused:", ",appContext:"]}, + "CustomStatusModal": {"protos": ["renderCustomStatusInput", "renderClearAfter", "getEmojiButtonRenderer"]}, + "DefaultChannelEmptyMessage": {"strings": [".CHANNEL_WELCOME", ".titleName", "handlePersonalize"]}, + "DirectMessage": {"strings": ["getRecipientId", "Controller", "handleContextMenu", ".DM"]}, + "DirectMessageAddPopout": {"strings": [".COPY_INSTANT_INVITE", ".handleInviteUsers=", ".selectedUsers", ".mobileToolsContainer"]}, + "DirectMessageAddPopoutRow": {"strings": [".weightMedium", ".friendSelected", ".friendWrapper"]}, + "DiscordTag": {"strings": ["hidePersonalInformation", "isVerifiedBot", "botType"]}, + "Embed": {"strings": ["gridContainer", "renderEmbedContent", "hasThumbnail"]}, + "EmojiPicker": {"strings": ["emojiSize", "disableEmojiTutorial", ",emojiPaddingHorizontal:"]}, + "EmojiPickerHeader": {"strings": ["pickerIntention", "headerClassName", ".EmojiIntention.REACTION"]}, + "EmojiPickerListRow": {"strings": ["emojiSize", "surrogateCodePoint", "showEmojiFavoriteTooltip"]}, + "FocusRingScope": {"strings": ["FocusRing", "ringTarget was not"]}, + "FolderHeader": {"strings": ["folderNode:", "expandedFolderIconWrapper", "forceCircular"]}, + "FolderContextMenu": {"strings": ["GUILD_ACTIONS_MENU_LABEL", "navId:\"guild", "folderId:"], "noSearch": true}, + "FolderIcon": {"strings": ["folderNode:", "folderIconWrapper", "closedFolderIconWrapper"]}, + "FolderItem": {"strings": ["folderNode:", "expandedFolderBackground", "onExpandCollapse"]}, + "FolderItemWrapper": {"strings": ["folderNode:", "isFolderExpanded", "defaultFolderName:"]}, + "FolderSettingsModal": {"strings": [".handleNameChange=", ".handleSubmit", "folderName:"], "noSearch": true}, + "GuildBans": {"strings": [".Messages.BANS_NO_USERS_BANNED", ".Messages.BANS_SEARCH_PLACEHOLDER", ".getSortedBans"]}, + "GuildChannelListContextMenu": {"strings": ["GUILD_ACTIONS_MENU_LABEL", "\"guild-context\",\"aria-label\""], "noSearch": true}, + "GuildContextMenu": {"strings": ["GUILD_ACTIONS_MENU_LABEL", "navId:\"guild", "GuildContextMenu"], "noSearch": true}, + "GuildEmojis": {"strings": ["getMaxEmojiSlots", "lightSrc:", ".Messages.NO_EMOJI"]}, + "GuildFavorites": {"strings": [".favoriteIcon", "FAVORITES_GUILD_NAME"]}, + "GuildHeader": {"strings": ["bannerVisible:", "disableBannerAnimation:", "onContextMenu"]}, + "GuildIcon": {"strings": [",animate:", ",size:", "{active:!0"], "noSearch": true}, + "GuildIconWrapper": {"strings": ["badgeStrokeColor:", "textScale:"]}, + "GuildInvitationRow": {"strings": [".getSelectedInviteMetadata", ".getSuggestedProps", "inviteKey:"], "noSearch": true}, + "GuildInvites": {"strings": [".NO_INVITES_BODY", ".DISABLE_INVITES", "submitting"]}, + "GuildItem": {"strings": ["guildNode:", "guildJoinRequestStatus", "lowerBadgeWidth"]}, + "GuildItemWrapper": {"strings": [".pauseBackground", ".isUnavailable", "guildJoinRequestStatus"]}, + "GuildMemberEntry": {"strings": [".renderOwnerHelpIcon(", ".overflowButton", ".roleWrapper"]}, + "GuildsBar": {"strings": ["unreadMentionsIndicatorTop", "disableAppDownload", ".GUILD_MEMBER_VERIFICATION"]}, + "GuildSidebar": {"strings": ["\"guildsnav\"", "unreadMentionsIndicatorBottom"]}, + "GuildTooltip": {"strings": ["tooltipClassName", "includeActivity", "listItemTooltip"]}, + "HeaderBar": {"strings": [".hamburger", "scrollable", "themed"]}, + "HeaderBarChannelName": {"strings": ["onDoubleClick", ".setName", "maxLen:"]}, + "HeaderBarContainer": {"strings": ["isAuthenticated", ".HEADER_BAR"]}, + "HeaderBarRecipient": {"strings": [".Messages.DIRECT_MESSAGE", "getRecipientId", ".avatar", "getName"]}, + "HeaderBarTitle": {"strings": [".titleWrapper", ",onContextMenu", "forceLevel:"]}, + "HeaderBarTitleIcon": {"strings": ["HEADER_BAR_BADGE", ".iconBadge", "iconClassName:"]}, + "HomeButtonDefault": {"strings": ["BUTTON_HOME", ".homeIcon", ".DISCODO_ENABLED"]}, + "I18nLoader": {"strings": [".Messages.I18N_LOCALE_LOADING_ERROR", ".loading"]}, + "ImageModal": {"strings": ["renderLinkComponent", ".Messages.OPEN_IN_BROWSER", "animated"], "noSearch": true}, + "ImageVideoModal": {"strings": ["renderLinkComponent", ".Messages.OPEN_IN_BROWSER", "videoWrapper"], "noSearch": true}, + "InboxHeader": {"strings": [".UNREADS", ".TODOS", ".controls"]}, + "IncomingCallModal": {"strings": [".Messages.INCOMING_VIDEO_CALL_ELLIPSIS", ".isFocused", "maxX:window.innerWidth"]}, + "InviteGuildName": {"strings": ["{guild:",".guildName",".guildNameWrapper", ".name", "jsx)(\"span\","], "noSearch": true}, + "KeybindRecorder": {"strings": [".RECORDING", ".DEFAULT", "toggleRecordMode"], "noSearch": true}, + "LayerProvider": {"strings": ["layerContainerElement", "getContextValue"]}, + "LayersProvider": {"strings": ["._currentlyTransitioningKeys", "._keysToEnter", "._keyChildMapping"]}, + "LazyImage": {"protos": ["loadImage", "getRatio", "getSrc"]}, + "LazyImageZoomable": {"strings": ["renderLinkComponent", "onCloseImage"]}, + "ListItemTooltip": {"strings": [".listItemWrapper", "tooltipClassName", "disableWrapper"]}, + "MemberListItem": {"strings": ["ownerTooltipText", "onClickPremiumGuildIcon:", "Messages.GUILD_OWNER"]}, + "Mention": {"strings": ["iconType:", "color:", "),backgroundColor:"]}, + "Menu": {"strings": ["\"empty\"", "getItemProps", "isUsingKeyboardNavigation"]}, + "Message": {"strings": ["childrenMessageContent", "childrenRepliedMessage", "zalgo", ".buttonContainer"]}, + "MessageAccessories": {"protos": ["renderGiftCodes", "renderEmbeds", "renderActivityInvite"]}, + "MessageActionsContextMenu": {"strings": ["canReport:", "navId:\"message-actions\"", "useMessageMenu"]}, + "MessageContent": {"strings": [".SEND_FAILED", "editedTimestamp", ".MESSAGE_EDITED"]}, + "MessageContextMenu": {"strings": ["MESSAGE_ACTIONS_MENU_LABEL", "navId:\"message", "getGuildId"]}, + "MessageGroup": {"strings": [".ephemeral", "animateAvatar:", "getMessageByReference"]}, + "MessageHeader": {"strings": ["showTimestampOnHover", "usernameClassName", "isVisibleOnlyOnHover:"], "noSearch": true}, + "MessageReply": {"strings": [".isFirstMessageInForumPost", "referencedUsernameProfile:", "referencedMessage"]}, + "MessageReplyHeader": {"strings": ["pollBadgeReplied", "referencedMessage"]}, + "Messages": {"strings": ["messageGroupSpacing", "groupSpacing", "jumpToPresent"]}, + "MessageSearchResultContextMenu": {"strings": ["MESSAGE_ACTIONS_MENU_LABEL", "navId:\"message"], "nonStrings": ["getGuildId"]}, + "MessageTimestamp": {"strings": [".timestampVisibleOnHover", "MESSAGE_EDITED_TIMESTAMP", ".timestampTooltip"], "noSearch": true}, + "MessageToolbar": {"strings": ["Messages.MORE", "hasDeveloperMode", "emojiPicker:"], "noSearch": true}, + "MessageUsername": {"strings": ["\"username\"", "colorString", "compact"]}, + "ModalCarousel": {"strings": [".gotoNext", "currentIndex", "gotoPrevThrottled"]}, + "ModalHeader": {"strings": ["headerIdIsManaged:", ".header,", "Direction.HORIZONTAL"]}, + "ModalFooter": {"strings": [".footerSeparator", ".separator", "Direction.HORIZONTAL_REVERSE"]}, + "ModalRoot": {"strings": ["rootWithShadow", "ImpressionTypes.MODAL", "ModalTransitionState"]}, + "NameContainer": {"strings": ["\"listitem\"", "wrappedName", "innerClassName"]}, + "NameTag": {"strings": ["invertColor:", "usernameClass", "discriminatorClass"]}, + "NavItem": {"strings": [".selected", ".childWrapper", "fontSize:"]}, + "Note": {"strings": [".noteRef", "handleBlur", ".SPACE"]}, + "NowPlayingHeader": {"strings": ["ACTIVITY_FEED_NOW_PLAYING_HEADER", "partiedMembers"]}, + "NowPlayingItem": {"strings": [".wrapper", "padded:"]}, + "PanelButton": {"strings": ["Masks.PANEL_BUTTON"]}, + "ParticipantsForSelectedParticipant": {"strings": ["ACTIVITY", "maxVisibleUsers", "participantType"]}, + "PeopleList": {"strings": [".SECTION_NO_RESULTS", ".section", "SUGGESTIONS"]}, + "PeopleListItem": {"strings": ["height:new", "isFocused", "onOtherHover"]}, + "PeopleListItemBlocked": {"strings": [".listItemContents", "UNKNOWN", ".BLOCKED"]}, + "PeopleListItemFriend": {"strings": [".getMutablePrivateChannels", ".handleOpenPrivateChannel", "isActiveRow"]}, + "PeopleListItemPending": {"strings": ["PENDING_INCOMING", ".DENY", "addRelationship"]}, + "PeopleListSectionedLazy": {"strings": ["statusSections", "peopleList", "id:\"people-list\""]}, + "PeopleListSectionedNonLazy": {"strings": ["statusSections", "peopleList", "id:\"people\""]}, + "PictureInPictureVideo": {"strings": [".topControls", ".bottomControl", "screenMessage", "renderBottomLeftControls:"]}, + "PrivateChannel": {"strings": ["LEAVE_GROUP_DM_MANAGED_BODY", "PrivateChannel.renderAvatar"]}, + "PrivateChannelRecipients": {"strings": [".membersWrap", "recipients"]}, + "PrivateChannelsList": {"strings": ["privateChannelIds", "privateChannelRecipientsInviteButtonIcon"]}, + "QuickMessage": {"strings": ["QUICK_DM_USER", ".NORMAL"]}, + "QuickSwitchChannelResult": {"strings": ["getAccessibilityLabel", ".contentUnread", "renderVoiceStates"]}, + "QuickSwitcher": {"protos": ["getRowId", "renderSection", "renderResults", "renderProtip", "search"]}, + "QuickSwitchGroupDMResult": {"strings": ["getAccessibilityLabel", ".contentUnread", "dmIconContainer"]}, + "QuickSwitchGuildResult": {"strings": ["getAccessibilityLabel", ".contentUnread", "guildIconContainer"]}, + "QuickSwitchUserResult": {"strings": ["getAccessibilityLabel", ".contentUnread", "getDisplayNickname"]}, + "Reactions": {"strings": ["showImmediate:", ".BURST", "reactions:", ".emoji.name"], "noSearch": true}, + "ReactionsModal": {"strings": [".BURST", ".reactions", ".emoji.name", "selectedReaction"], "noSearch": true}, + "ReactionsModalUser": {"strings": ["currentUser cannot be undefined", "reactorDefault"]}, + "ReactionsModalUsers": {"strings": ["hasMore", "emoji", "spinnerMore"], "noSearch": true}, + "RecentMentions": {"strings": ["canCloseAllMessages", "RECENT_MENTIONS", "badgeState:"]}, + "RecentsChannelHeader": {"strings": [".subtextContainer", ".channelNameSpan", ".channelNameHeader", "gotoChannel:"]}, + "RichChannelMention": {"strings": ["iconType:", "Messages.UNKNOWN_CHANNEL", "\"locked\""], "noSearch": true}, + "RichRoleMention": {"strings": ["\"@deleted-role\"", "guildId:", ".RoleDot"], "noSearch": true}, + "RichUserMention": {"strings": ["hidePersonalInformation", "getUser", "userTooltip"], "noSearch": true}, + "RTCConnection": {"strings": [".NOISE_CANCELLATION_POPOUT", ".voicePanelIntroductionButton"]}, + "RTCConnectionVoiceUsers": {"strings": [".Messages.VOICE_PANEL_SHOW_EXTRAS", ".Messages.VOICE_PANEL_USERS", "voiceStates:"]}, + "SearchBar": {"strings": ["this.inputRef", ".containerRef", ".handleOnChange"]}, + "SearchPopout": {"strings": [".handleHintClick", ".selectedIndex", "token.start"]}, + "SearchPopoutOption": {"strings": [".plusIcon", ".option,", ".filter"]}, + "SearchResult": {"strings": ["onJump:", "message:", "handleMessageClick"]}, + "SearchResults": {"strings": ["SEARCH_ERROR", "emptyResultsText", "isSearching"]}, + "SearchResultsHeader": {"strings": [".Messages.SEARCH_OLDEST_SHOR", ".Messages.SEARCH_MOST_RELEVANT_SHORT", "searchByMode"]}, + "SearchResultsInner": {"strings": ["SEARCH_HIDE_BLOCKED_MESSAGES", "totalResults", "\"search-result-\""]}, + "SearchResultsPagination": {"strings": ["pageLength", "changePage", "maxVisiblePages"]}, + "SettingsView": {"strings": ["tabBarItemContainer", "PROFILE_CUSTOMIZATION", "badgeCount"]}, + "Shakeable": {"protos": ["shake", "getDefaultAnimProps", "stop"]}, + "Spoiler": {"strings": [".removeObscurity", "onReveal:"]}, + "SpoilerWarning": {"strings": [".spoilerWarning", ".SPOILER"]}, + "StandardSidebarView": {"strings": ["standardSidebarView", "sidebarTheme:", "mobileSidebarHeader"]}, + "SystemMessageThreadCreated": {"strings": ["threadOnClick:", ".SYSTEM_MESSAGE_THREAD_CREATED", "viewThreadsOnClick:"]}, + "SystemMessageWrapper": {"strings": ["unknown message type", "\"SystemMessage\""]}, + "TabBar": {"props": ["Item", "Header", "Panel"]}, + "TextChannelEmptyMessage": {"strings": ["MANAGE_CHANNELS", ".BEGINNING_CHANNEL_DESCRIPTION", "topicHook:"]}, + "TextInput": {"strings": ["onFocus", "MAXIMUM_LENGTH_ERROR", "getIsOverFlowing"], "noSearch": true}, + "ThreadCard": {"strings": ["threadId:", ".container", ".threadName"]}, + "ThreadCardDescription": {"strings": [".Messages.THREAD_BROWSER_STARTED_BY", ".bullet", ".lastMessageId"]}, + "ThreadEmptyMessage": {"strings": [",{channel:", ".name", "{channelId:", ".iconWrapper", ".icon}"]}, + "ThreadEmptyMessageAuthor": {"strings": [".threadCreatorName", ".unknownCreatorName", ".getUser"]}, + "ThreadMessageAccessories": {"strings": ["threadMetadata", "NO_RECENT_THREAD_MESSAGES", ".Messages.THREAD"]}, + "ThreadMessageAccessoryMessage": {"strings": [".threadMessageAccessoryPlaceholder", ".threadMessageAccessoryContent", "formatInline:"]}, + "ThreadSidebar": {"strings": ["baseChannelId:", ".SIDEBAR", ".THREAD_HEADER_BAR"]}, + "TooltipContainer": {"protos": ["renderTooltip", "setDomElement", "shouldShowTooltip", "toggleShow"]}, + "TransitionGroup": {"protos": ["performAppear", "performEnter", "performLeave"]}, + "TypingUsers": {"strings": ["Easing.quad", ".emphasizeSlowmodeCooldown", ".sequence("]}, + "UnavailableGuildsButton": {"strings": [".errorInner", ".guildsError", "Messages.PARTIAL_OUTAGE"]}, + "UnreadDMs": {"strings": ["getMutablePrivateChannels", "selectedVoiceGuildId", "selectedVoiceChannelId:"]}, + "UseCopyIdItem": {"strings": ["\"devmode-copy-id"]}, + "UserBadges": {"strings": [".PROFILE_USER_BADGES", "SIZE_24"]}, + "UserBanner": {"strings": ["showPremiumBadgeUpsell:", ".isPremiumAtLeast", ".SETTINGS"]}, + "UserBannerMask": {"strings": [".bannerSVGWrapper", "getAvatarSpecs", "foreignObject"]}, + "UserBioSection": {"strings": ["isUsingGuildBio:", "aboutMeGuildIcon", "GUILD_IDENTITY_BIO_TOAST"]}, + "UserConnectionsSection": {"strings": [".applicationRoleConnections)", ".CONNECTIONS_ROLE_POPOUT_VIEW_ALL"]}, + "UserGenericContextMenu": {"strings": ["USER_ACTIONS_MENU_LABEL", "navId:\"user", ".USER_GENERIC_MENU"], "noSearch": true}, + "UserInfo": {"strings": [".botTag", "isMobile:", ".discordTag"]}, + "UserMemberContextMenu": {"strings": ["USER_ACTIONS_MENU_LABEL", "navId:\"user", ".GUILD_CHANNEL_USER_MENU"], "noSearch": true}, + "UserMemberSince": {"strings": [".memberSinceContainer", ".USER_PROFILE_DISCORD_MEMBER_SINCE"]}, + "UserMemberSinceSection": {"strings": ["userId:", "headingClassName", ".title", "guildMember:"]}, + "UserMention": {"strings": ["inlinePreview", "\"@\"", "renderPopout:"]}, + "UsernameSection": {"strings": ["shouldCopyOnClick:", ".userTagDiscriminatorNoNickname"]}, + "UserPopout": {"strings": ["{user:", "userId:", "\"Unexpected missing user\")", "getUser"]}, + "UserPopoutAvatar": {"strings": [".avatarDecorationHint", ".VIEW_PROFILE"], "noSearch": true}, + "UserPopoutBody": {"strings": ["customStatusActivity:", "canDM:", "hidePersonalInformation:", "showCopiableUsername:"]}, + "UserProfile": {"strings": [".PROFILE_MODAL", "USER_INFO_CONNECTIONS", ".topSection"], "noSearch": true}, + "UserProfileBody": {"strings": [".userInfoSectionHeader", "Messages.NOTE", "connectedAccounts:"], "noSearch": true}, + "UserProfileHeader": {"strings": ["friendToken:", ".additionalActionsIcon", "onMessage"], "noSearch": true}, + "UserProfileMutualFriends": {"strings": [".getMutualFriends", ".listScroller", ".emptyText"]}, + "UserProfileMutualGuilds": {"strings": [".getMutualGuilds", ".listScroller", ".emptyText"]}, + "UserProfileUsername": {"strings": [".customStatusEmoji", ".customStatusSoloEmoji", ".findActivity(", ".CUSTOM_STATUS"], "noSearch": true}, + "UserRolesSection": {"strings": [".rolePillBorder", "numRoles", "roleClassName"]}, + "UserSummaryItem": {"protos": ["renderMoreUsers", "renderUsers", "renderIcon"]}, + "UserSettingsAppearance": {"protos": ["renderTheme", "renderDisplayMode", "renderMessageGroupSpacingSlider"]}, + "UserTheme": {"strings": [".profileColors", "useDefaultClientTheme:"], "noSearch": true}, + "VideoBackground": {"strings": ["backgroundSrc:", "pulseSpeakingIndicator:", ".avatarWrapper"]}, + "VoiceUser": {"protos": ["renderPrioritySpeaker", "renderIcons", "renderAvatar"]}, + "VoiceUsers": {"strings": ["hidePreview", "previewIsOpen", "previewUserIdAfterDelay"]} + }, + "ModuleUtilsConfig": { + "QueuedComponents": [], + "ContextMenuTypes": [], + "ContextMenuTypesMap": {}, + "ContextMenuSubItemsMap": {}, + "PatchTypes": [], + "InstanceFunctions": [], + "PatchMap": {}, + "Finder": {}, + "LoadedInComponents": {} + }, + "SvgIcons": { + "ACTIVITY": { + "defaultProps": { + "width": 16, + "height": 16 + }, + "icon": "" + }, + "ACTIVITY_DISABLED": { + "defaultProps": { + "width": 16, + "height": 16, + "foreground": 16 + }, + "icon": "" + }, + "ARROW_DOWN": { + "defaultProps": { + "width": 18, + "height": 18 + }, + "icon": "" + }, + "ARROW_UP": { + "defaultProps": { + "width": 18, + "height": 18 + }, + "icon": "" + }, + "BOOST": { + "defaultProps": { + "width": 24, + "height": 24, + "foreground": "" + }, + "icon": "" + }, + "CALENDAR": { + "icon": "" + }, + "CHANGELOG": { + "icon": "" + }, + "CHECKBOX": { + "defaultProps": { + "background": "", + "foreground": "" + }, + "icon": "" + }, + "CHECKBOX_EMPTY": { + "defaultProps": { + "foreground": "" + }, + "icon": "" + }, + "CHECKMARK": { + "defaultProps": { + "width": 18, + "height": 18 + }, + "icon": "" + }, + "CHECKMARK_CIRCLE": { + "icon": "" + }, + "CLOCK": { + "icon": "" + }, + "CLOSE": { + "defaultProps": { + "width": 12, + "height": 12 + }, + "icon": "" + }, + "CLOSE_CIRCLE": { + "icon": "" + }, + "COG": { + "icon": "" + }, + "CROWN": { + "icon": "" + }, + "DOWNLOAD": { + "defaultProps": { + "width": 16, + "height": 16 + }, + "icon": "" + }, + "DROPPER": { + "defaultProps": { + "width": 16, + "height": 16, + "foreground": "" + }, + "icon": "" + }, + "EYE": { + "icon": "" + }, + "FAVORITE": { + "icon": "" + }, + "FAVORITE_FILLED": { + "icon": "" + }, + "FOLDER": { + "icon": "" + }, + "GAMEPAD": { + "icon": "" + }, + "GAMEPAD_DISABLED": { + "defaultProps": { + "foreground": "" + }, + "icon": "" + }, + "GITHUB": { + "icon": "" + }, + "GLOBE": { + "defaultProps": { + "width": 20, + "height": 20 + }, + "icon": "" + }, + "GRADIENT": { + "defaultProps": { + "width": 36, + "height": 36 + }, + "icon": "" + }, + "HEART": { + "defaultProps": { + "width": 16, + "height": 16 + }, + "icon": "" + }, + "INFO": { + "icon": "" + }, + "LEFT_CARET": { + "icon": "" + }, + "LEFT_DOUBLE_CARET": { + "icon": "" + }, + "LOCK_CLOSED": { + "icon": "" + }, + "LOCK_OPEN": { + "icon": "" + }, + "MENU_CARET": { + "getClassName": { + "": ["menucaretarrow"], + "props.open": ["menucaretopen"] + }, + "icon": "" + }, + "METAMASK": { + "icon": "" + }, + "MORE": { + "icon": "" + }, + "NOVA_AT": { + "icon": "" + }, + "NOVA_PIN": { + "icon": "" + }, + "NOVA_TRASH": { + "icon": "" + }, + "NUMPAD": { + "icon": "" + }, + "OPEN_EXTERNAL": { + "icon": "" + }, + "OVERLAY": { + "icon": "" + }, + "OVERLAY_DISABLED": { + "icon": "" + }, + "PATREON": { + "icon": "" + }, + "PAYPAL": { + "icon": "" + }, + "PENCIL": { + "defaultProps": { + "width": 16, + "height": 16 + }, + "icon": "" + }, + "PHANTOM": { + "icon": "" + }, + "PIN": { + "defaultProps": { + "width": 16, + "height": 16 + }, + "icon": "" + }, + "PODIUM": { + "icon": "" + }, + "QUESTIONMARK": { + "icon": "" + }, + "QUESTIONMARK_ACTIVITY": { + "defaultProps": { + "width": 40, + "height": 40 + }, + "icon": "" + }, + "QUOTE": { + "icon": "" + }, + "RAW_TEXT": { + "icon": "" + }, + "RIGHT_CARET": { + "icon": "" + }, + "RIGHT_DOUBLE_CARET": { + "icon": "" + }, + "SEARCH": { + "defaultProps": { + "width": 18, + "height": 18 + }, + "icon": "" + }, + "SPEAKER": { + "icon": "" + }, + "STREAM": { + "icon": "" + }, + "TRASH": { + "icon": "" + }, + "WARNING": { + "icon": "" + } + }, + "CustomClassModules": { + "BDFDB": { + "BDFDBundefined": "BDFDB_undefined", + "avatarDisabled": "disabled_6G33EE", + "badge": "badge_7R_W3s", + "badgeAvatar": "avatar_hF52Er", + "bdRepo": "repo_k8F4Sd", + "bdRepoEntry": "entry_9JnAPs", + "bdRepoFooterControls": "controls_p0SrvV", + "bdRepoHeaderControls": "controls_18FQsW", + "bdRepoListHeader": "repoHeader_2KfNvH", + "bdRepoListScroller": "repoScroller_9JnAPs", + "cardDisabled": "cardDisabled_wnh5ZW", + "cardHorizontal": "horizontal_0ffRsT", + "cardInner": "inner_OP_8zd", + "cardWrapper": "card_rT4Wbb", + "changeLogButton": "changeLogButton_L9FFs5", + "charCounter": "counter_uAzbKp", + "changeLogModal": "changeLogModal_ny_dHC", + "collapseContainer": "container_fAVkOf", + "collapseContainerCollapsed": "collapsed_2BUBZm", + "collapseContainerHeader": "header_2s6x-5", + "collapseContainerInner": "inner_TkGytd", + "collapseContainerMini": "container_fAVkOf containerMini__k6Rts", + "collapseContainerTitle": "title_ROsJi-", + "colorPicker": "colorPicker_h5sF8g", + "colorPickerAlpha": "alpha_VcPGeR", + "colorPickerAlphaCheckered": "alpha-checkered", + "colorPickerAlphaCursor": "alpha_cursor", + "colorPickerAlphaHorizontal": "alpha-horizontal", + "colorPickerGradient": "gradient_TJOYTr", + "colorPickerGradientCheckered": "gradient-checkered", + "colorPickerGradientCursor": "gradient_cursor", + "colorPickerGradientCursorEdge": "gradient-cursor-edge", + "colorPickerGradientCursorSelected": "gradient-cursor-selected", + "colorPickerGradientHorizontal": "gradient-horizontal", + "colorPickerGradientButton": "gradientButton_eBBuwD", + "colorPickerGradientButtonEnabled": "enabled_MypHME", + "colorPickerSwatches": "swatches_QxZw_N", + "colorPickerSwatchesDisabled": "disabled_2JgNxl", + "colorPickerSwatchSelected": "selected_f5IVXN", + "colorPickerSwatchSingle": "single_Fbb1wB", + "colorPickerSwatchSingleWrapper": "swatch_7FsRaa", + "confirmModal": "confirmModal_t-WDWJ", + "cursorPointer": "cursorPointer_B3uwDA", + "dateInputButton": "dateInputButton_jTGUJg", + "dateInputButtonSelected": "selected_8F23Ra", + "dateInputControls": "dateInputControls_cgfPf9", + "dateInputField": "dateInputField_2vhGP1", + "dateInputInner": "dateInputInner_ORXFwT", + "dateInputPreview": "dateInputPreview_lG5sMM", + "dateInputPreviewPrefix": "dateInputPreviewPrefix_6Fww21", + "dateInputPreviewSuffix": "dateInputPreviewSuffix_f4Sr32", + "dateInputWrapper": "dateInputWrapper_6tQOYp", + "dev": "dev_A7f2Rx", + "discoveryCard": "card_x5zW6z", + "discoveryCardAuthor": "author_d2JIcl", + "discoveryCardButton": "button_coQXfM", + "discoveryCardControls": "controls_T0w-Wc", + "discoveryCardCover": "cover_qyY86I", + "discoveryCardCoverBadge": "coverBadge_I41ZIY", + "discoveryCardCoverWrapper": "coverWrapper_J-mDGJ", + "discoveryCardDescription": "description_zXiu5E", + "discoveryCardFooter": "footer_C1KpS6", + "discoveryCardHeader": "header_4hHJgv", + "discoveryCardIcon": "icon_0jmVrJ", + "discoveryCardIconLoading": "loading_f8Gs22", + "discoveryCardIconWrapper": "iconWrapper_eK7GO6", + "discoveryCardInfo": "info_xw-o76", + "discoveryCardName": "name_OSUjF9", + "discoveryCards": "list_QuW8QR", + "discoveryCardStat": "stat_eRknnn", + "discoveryCardStatIcon": "statIcon_o40EX1", + "discoveryCardStats": "stats_Pc5tYW", + "discoveryCardTag": "tag_PbPfwP", + "discoveryCardTags": "tags_9KJ-1X", + "discoveryCardTitle": "title_SSzExd", + "discoveryCardTitleButton": "button_SxdSzE", + "favButtonContainer": "favbutton_8Fzu45", + "guild": "guild_r3yAE_", + "guildLowerLeftBadge": "lowerLeftBadge_zr4T_9", + "guildsLabel": "label_2wRs_g", + "guildSummaryClickableIcon": "clickableIcon_7I6aVc", + "guildSummaryContainer": "container_5VyO4t", + "guildSummaryEmptyGuild": "emptyGuild_Am9XfC", + "guildSummaryIcon": "icon_r6DlKo", + "guildSummaryIconContainer": "iconContainer_IBAtWs", + "guildSummaryIconContainerMasked": "iconContainerMasked_G-akdf iconContainer_IBAtWs", + "guildSummaryMoreGuilds": "moreGuilds_c5JVlC", + "guildSummarySvgIcon": "icon_5TsFrr", + "guildUpperLeftBadge": "upperLeftBadge_e35IpL", + "guildVoiceList": "guildVoiceList_9ads4k", + "hasBadge": "hasBadge_4rT8_u", + "hotkeyResetButton": "resetButton_hI9Ax7", + "hotkeyWrapper": "recorder_can0vx", + "inputNumberButton": "button_J9muv5", + "inputNumberButtonDown": "down_cOY7Qp button_J9muv5", + "inputNumberButtonUp": "up_mUs_72 button_J9muv5", + "inputNumberButtons": "buttons_our3p-", + "inputNumberWrapper": "numberInputWrapper_j4svZS", + "inputNumberWrapperDefault": "numberInputWrapperDefault_gRxcuK numberInputWrapper_j4svZS", + "inputNumberWrapperMini": "numberInputWrapperMini_wtUU31 numberInputWrapper_j4svZS", + "layerContainerZIndexDisabled": "zIndexDisabled_Pf4Txx", + "listInput": "listInput_11g5Sr", + "listInputDelete": "delete_M_nPTt", + "listInputItem": "item_wGC8aX", + "listInputItems": "items_D9PGwH", + "listRow": "listRow_7SfZww", + "loadingIcon": "loadingIcon_cOYMPl", + "loadingIconWrapper": "loadingIconWrapper_PsVJ9m", + "overflowEllipsis": "ellipsis_qlo9sA", + "pagination": "pagination_09Sd5R", + "paginationBottom": "bottom_jTb32i", + "paginationList": "list_PIKebU", + "paginationListAlphabet": "alphabet_2ANo0x", + "paginationListAlphabetChar": "alphabetChar_bq-8Go", + "paginationListAlphabetCharDisabled": "disabled_XmhCq2", + "paginationListContent": "listContent_aG3Fq8", + "paginationListMini": "mini_GMiniS", + "paginationMini": "mini_hODxXf", + "paginationTop": "top_v-18jZ", + "popoutArrow": "popoutArrow_g8f4Ss", + "popoutArrowBottom": "popoutArrowBottom_r5Tzui", + "popoutArrowTop": "popoutArrowTop_lK9F3a", + "popoutWrapper": "popout_xwjvsX", + "quickSelectWrapper": "quickSelectWrapper_UCfTKz", + "marginLeft4": "marginLeft4_58StzA", + "marginLeft8": "marginLeft8_3RhhAa", + "menuColorCustom": "colorCustom_44asd2", + "menuItemHint": "hint_BK71lM", + "modalHeaderShade": "shade_h6F4sT", + "modalHeaderHasSibling": "hasSiblings_fRyjyl", + "modalNoScroller": "noScroller_YgPpF3", + "modalSidebar": "sidebar__0OpfR", + "modalTabContent": "tabContent_nZ-1U5", + "modalTabContentOpen": "open_yICTYu", + "modalSubInner": "inner_t84Frz", + "modalTextContent": "text_8fSrts", + "modalWrapper": "modal_6GHvdM", + "multiInput": "multiInput_Ft9zQo", + "multiInputField": "multiInputField_GfMBpr", + "multiInputFirst": "multiInputFirst_5rMj_O", + "multiInputLast": "multiInputLast_HWxgTr", + "multiInputWrapper": "multiInputWrapper_g6Srtv", + "noticeClosing": "closing_g5Srr2", + "noticeText": "text_kRs522", + "noticeUpdate": "updateNotice_2DjjHs", + "noticeUpdateButtonAll": "all_iF7Saa", + "noticeUpdateButtonReload": "reload_LkMrac", + "noticeUpdateEntries": "entries_Br6Sda", + "noticeUpdateEntry": "entry_oFts5R", + "noticeUpdateText": "notice-message", + "noticeUpdateSeparator": "separator_g5DsaR", + "noticeWrapper": "noticeWrapper_8z511t", + "searchBarWrapper": "searchBarWrapper_1GpKvB", + "selectWrapper": "selectWrapper_yPjeij", + "settingsGuild": "guild_J3Egt5", + "settingsGuildDisabled": "disabled_b2o83O", + "settingsPanel": "settingsPanel_w2ySNR", + "settingsPanelList": "settingsList_eZjkXj", + "settingsPanelListWrapper": "wrapper_kRsR33", + "settingsPanelListWrapperMini": "mini_2Iug3W", + "settingsRow": "settingsRow_o9Ft44", + "settingsTableCard": "settingsTableCard_628t52", + "settingsTableCardConfigs": "settingsTableCardConfigs_w5X9-Z", + "settingsTableCardLabel": "settingsTableCardLabel_MElgIg", + "settingsTableHeaderOptions": "headerOptions_8F_5Ss", + "settingsTableHeaders": "settingsTableHeaders_WKzw9_", + "settingsTableHeaderVertical": "headerVertical_4MNxqk", + "settingsTableList": "settingsTableList_f6sW2y", + "sidebar": "sidebar_frSZx3", + "sidebarContent": "content_1SbgDG", + "sidebarList": "list_VCoBc2", + "sliderBubble": "bubble_3we2di", + "switchMini": "mini_6F2SSa", + "supporter": "supporter_Z3FfwL", + "supporterTier1": "tier1_841W86", + "supporterTier2": "tier2_VDTzC3", + "supporterTier3": "tier3_yTxjsy", + "supporterTier4": "tier4_5RNl6k", + "svgIcon": "icon_GhnIRB", + "svgIconWrapper": "iconWrapper_g20jFn", + "tabBarContainerBottom": "bottom_b8sdfs", + "table": "table_moqjM0", + "tableBodyCell": "bodyCell_dQam9V", + "tableHeaderCell": "headerCell_T6Fo3K", + "textScroller": "textScroller_dc9_kz", + "themedPopout": "themedPopout_1TrfdI", + "tooltipCustom": "tooltipCustom_hH39_Z", + "tooltipNote": "note_e4Jh6_", + "tooltipRowExtra": "extraRow_6F2Dss" + }, + "BD": { + "bdAddonCard": "bd-addon-card", + "bdAddonList": "bd-addon-list", + "bdAddonModal": "bd-addon-modal", + "bdAddonModalFooter": "bd-addon-modal-footer", + "bdAddonModalHeader": "bd-addon-modal-header", + "bdAddonModalSettings": "bd-addon-modal-settings", + "bdAuthor": "bd-author", + "bdButton": "bd-button", + "bdButtonDanger": "bd-button-danger", + "bdControls": "bd-controls", + "bdControlsButton": "bd-addon-button", + "bdDescription": "bd-description", + "bdDescriptionWrap": "bd-description-wrap", + "bdFolderButton": "bd-pfbtn", + "bdFooter": "bd-footer", + "bdGuild": "bd-guild", + "bdGuildAnimatable": "bd-animatable", + "bdGuildAudio": "bd-audio", + "bdGuildSelected": "bd-selected", + "bdGuildSeparator": "bd-guild-separator", + "bdGuildUnread": "bd-unread", + "bdGuildVideo": "bd-video", + "bdHeader": "bd-addon-header", + "bdHeaderTitle": "bd-title", + "bdIcon": "bd-icon", + "bdLink": "bd-link", + "bdLinks": "bd-links", + "bdMeta": "bd-meta", + "bdMinimalMode": "bd-minimal", + "bdName": "bd-name", + "bdPillSelected": "bd-selected", + "bdPillUnread": "bd-unread", + "bdSwitch": "bd-switch", + "bdSwitchChecked": "bd-switch-checked", + "bdSwitchInner": "bd-checkbox", + "bdUpdatebtn": "bd-updatebtn", + "bdVersion": "bd-version", + "settings": "plugin-settings", + "settingsOpen": "settings-open", + "settingsClosed": "settings-closed", + "switch": "ui-switch", + "switchCheckbox": "ui-switch-checkbox", + "switchChecked": "checked", + "switchItem": "ui-switch-item", + "switchWrapper": "ui-switch-wrapper" + }, + "BetterFriendList": { + "mutualGuilds": "mutualGuilds_s7F2aa", + "nameCell": "nameCell_7F4sRs", + "title": "title_3aDrFs" + }, + "BetterNsfwTag": { + "nsfwTag": "nsfwTag_666omg" + }, + "BetterSearchPage": { + "pagination": "pagination_shit69" + }, + "ChatFilter": { + "blocked": "blocked_jUhayi", + "blockedStamp": "blockedStamp_ijVeNn", + "censored": "censored_UYfeYg", + "censoredStamp": "censoredStamp_fb2cYb" + }, + "CharCounter": { + "charCounter": "charCounter_7fw40k", + "chatCounter": "chatCounter_XOMPsh", + "counterAdded": "charCounterAdded_zz9O4t", + "customStatusCounter": "customStatusCounter_G8FrsT", + "editCounter": "editCounter_pNT1Xe", + "popoutNoteCounter": "popoutNoteCounter_62U4Rh", + "profileNoteCounter": "profileNoteCounter_p0fWA_", + "threadCreation": "threadCreation_p0fWA_", + "uploadCounter": "uploadCounter_iEGQQk" + }, + "CustomStatusPresets": { + "customStatusItem": "customStatusItem_f5Trsa", + "deleteButton": "deleteButton_k8a4Sa", + "deleteIcon": "deleteIcon_22SgHa", + "dragPreview": "dragPreview_3F0ssT", + "sortableCard": "sortableCard_oF6sTT", + "sortDivider": "sortDivider_d8SzzU", + "status": "status_0f5Sr2" + }, + "DisplayServersAsChannels": { + "badge": "badge_fxFrUP", + "muted": "muted_k5Fraa", + "name": "name_z5133D", + "styled": "styledGuildsAsChannels_DNHtg_" + }, + "EmojiStatistics": { + "amountCell": "amountCell_g_W6Rx", + "iconCell": "iconCell_-wniOu", + "nameCell": "nameCell_xyXENZ", + "statisticsButton": "statisticsButton_nW2KoM" + }, + "FriendNotifications": { + "friendsOnline": "friendsOnline_2JkivW", + "friendsOnlineWrap": "friendsOnlineWrap_0gZ63m", + "logAvatar": "avatar_GgGssS", + "logContent": "content__3_FFs", + "logTime": "time_00Fs44", + "timeLogModal": "timeLogModal_9s4Rts", + "typeLabel": "label_9FgsSa" + }, + "GameActivityToggle": { + "added": "gameActivityToggleAdded_Yd-YxC" + }, + "ImageUtilities": { + "details": "details_9dkFPc", + "detailsAdded": "detailsAdded_fHiJlm", + "detailsLabel": "label_mrlccN", + "detailsWrapper": "detailsWrapper_TE1mu5", + "gallery": "gallery_JViwKR", + "imageDetails": "imageDetails_1t6Zms", + "imageDetailsAdded": "imageDetailsAdded_sda9Fa", + "lens": "zoomLens_uOK8xV", + "lensBackdrop": "lensBackdrop_yEm7Om", + "next": "next_SHEZrz", + "operations": "operations_3V47CY", + "previous": "previous_xsNq6B", + "sibling": "sibling_6vI7Pu", + "switchIcon": "switchIcon_QY6cR4" + }, + "LastMessageDate": { + "icon": "icon_ocEw13" + }, + "OldTitleBar": { + "oldTitleBarEnabled": "oldTitleBarEnabled_D8ppJQ", + "settingsToolbar": "settingsToolbar_wu4yfQ", + "toolbar": "toolbar_hRzFw-" + }, + "PersonalPins": { + "messageTag": "messageTag_yRSUYg", + "messageTagAdd": "messageTagAdd_vKM0VY", + "messageTagAddActive": "active_G0VvAY", + "messageTagDelete": "messageTagDelete_QaGh6V", + "messageTagName": "messageTagName_dT8OZ4" + }, + "PinDMs": { + "dragPreview": "dragPreview_nXiByA", + "dmChannelPinned": "pinned_0lM4wD", + "dmChannelPlaceholder": "placeholder_7bhR5s", + "pinnedChannelsHeaderAmount": "headerAmount__-7GrS", + "pinnedChannelsHeaderArrow": "pinnedChannelsHeaderArrow_44rrTz", + "pinnedChannelsHeaderCollapsed": "collapsed_3w_-ff", + "pinnedChannelsHeaderColored": "colored_oIzG5s", + "pinnedChannelsHeaderContainer": "pinnedChannelsHeaderContainer_89Gjv4", + "recentPinned": "pinned_jHvFrr", + "recentPlaceholder": "placeholder_Uff-gH", + "unpinButton": "unpinButton_z3-UVO", + "unpinIcon": "unpinIcon_79ZnEr" + }, + "PluginRepo": { + "failNotice": "failNotice_OtHUGb", + "loadingIcon": "loadingIcon_dB3qSe", + "loadingTooltip": "loadingTooltip_SzsTEU", + "newEntriesNotice": "newEntriesNotice_qvvpPx", + "outdatedNotice": "outdatedNotice_9Q9gJv", + "repoNotice": "repoNotice_P5ss6R" + }, + "ReadAllNotificationsButton": { + "button": "button_Jt-tIg", + "frame": "frame_oXWS21", + "innerFrame": "innerFrame_8Hg64E" + }, + "ServerCounter": { + "serverCount": "serverCount_FsTTs1", + "serverCountWrap": "serverCountWrap_k8F5De" + }, + "ServerDetails": { + "details": "details_08FrsT", + "icon": "icon_hSL42R", + "tooltip": "detailsTooltip_G9hSSa" + }, + "ServerFolders": { + "dragPreview": "dragPreview_nXiByA", + "folderContent": "content_Pph8t6", + "folderContentClosed": "closed_j55_T-", + "folderContentIsOpen": "folderContentIsOpen_zz6FgW", + "guildPlaceholder": "placeholder_7bhR5s", + "hasSideBar": "hasSideBar_sda9Fa", + "iconSwatch": "iconSwatch__78Ghj", + "iconSwatchInner": "iconInner_aOY-qk", + "iconSwatchPreview": "preview_Bbg_24", + "iconSwatchNoPreview": "noPreview_G8SS4t", + "iconSwatchSelected": "selected_P5oePO" + }, + "SpellCheck": { + "error": "error_k9z2IV", + "overlay": "spellCheckOverlay_cNSap5" + }, + "ShowBadgesInChat": { + "badges": "badges_XRnWAp", + "badgesChat": "badgesChat_f_cbR8", + "badgesDMs": "badgesDMs_Aw_p52", + "badgesMembers": "badgesMembers_8Kf3S1", + "badgesSettings": "badgesSettings_ychoGn", + "indicator": "indicator_8F4GGw" + }, + "ShowConnections": { + "connection": "connection_9Gh4sa", + "connectionIcon": "connectionIcon_888F4s", + "connections": "connections_pPr4as", + "verifiedBadge": "verifiedBadge_mH2YpO" + }, + "SpotifyControls": { + "activityButton": "activityButton_5Fs4Aa", + "bar": "bar_g2ZMIm", + "barGabber": "grabber_7sd5f5", + "barFill": "barFill_Dhkah7", + "barText": "barText_lmqc5O", + "buttonActive": "active_6TsW-_", + "container": "container_6sXIoE", + "containerInner": "inner_WRV6k5", + "containerMaximized": "maximized_vv2Wr0", + "containerPaused": "paused_l9f3As", + "containerWithTimeline": "withTimeline_824fT_", + "cover": "cover_SwJ-ck", + "coverMaximizer": "maximizer_RVu85p", + "coverWrapper": "coverWrapper_YAplwJ", + "details": "details_ntX2k5", + "interpret": "interpret_F93iqP", + "settingsIcon": "icon_F4SSra", + "settingsLabel": "label_3f00Sr", + "song": "song_tIdBpF", + "timeline": "timeline_UWmgAx", + "volumeSlider": "volumeSlider_sR5g00" + }, + "StaffTag": { + "adminIcon": "admin_Kv1Hp_", + "forumCreatorIcon": "forumCreator_5sOT2A", + "groupOwnerIcon": "groupOwner_g6FlmA", + "managementIcon": "management_3fF_o8", + "ownerIcon": "owner_FfFh3B", + "threadCreatorIcon": "threadCreator_lp5sXo" + }, + "ThemeRepo": { + "dragBar": "dragBar_4FsRaa", + "dragCorner": "dragCorner_PgJJk2", + "dragInterface": "dragInterface_OT5s2A", + "failNotice": "failNotice_pZAzjZ", + "loadingIcon": "loadingIcon_h7OqHY", + "loadingTooltip": "loadingTooltip_YszEmb", + "moveBar": "moveBar_lZ555a", + "newEntriesNotice": "newEntriesNotice_abF6e1", + "preview": "preview_7Gh5tS", + "previewFullscreen": "fullscreen_h78TsW", + "previewMoving": "moving_Wa22fA", + "outdatedNotice": "outdatedNotice_fw8ams", + "repoNotice": "repoNotice_TV3RFr" + }, + "ThemeSettings": { + "card": "card_h4Rts2", + "cardName": "card_jdf7Rs", + "grid": "settingsButton_60jFra" + }, + "TimedLightDarkMode": { + "dateGrabber": "dateGrabber_QrRkIX", + "timerGrabber": "timerGrabber_zpRAIk", + "timerSettings": "timerSettings_wkvEfF" + }, + "TopRolesEverywhere": { + "badgeStyle": "badgeStyle_tFiEQ8", + "chatTag": "chatTag_Y-5TDc", + "memberTag": "memberTag_QVWzGc", + "roleStyle": "roleStyle_jQ7KI2", + "tag": "tag_wWVHyf", + "voiceTag": "voiceTag_0F4Ss0" + }, + "Translator": { + "configButton": "configButton_lLgMs2", + "translateButton": "translateButton_DhP9x8", + "translated": "translated_5YO8i3", + "translating": "translating_Yi-YxC" + }, + "WriteUpperCase": { + "enabled": "enabled_7KI3Q2", + "quickToggleButton": "quickToggleButton_P2xDh8" + }, + "NotFound": { + "_": "", + "emoji": "emoji", + "carouselModal": "carouselModal_c0d5b7", + "highlight": "highlight", + "hoverCardButton": "button_1yVL_7", + "loginScreen": "wrapper_eebc2d", + "mention": "mention", + "mentionInteractive": "interactive", + "mentionWrapper": "wrapper_f46140", + "messagesLoadingWrapper": "wrapper_b8c724", + "nameContainer": "container__4f20e", + "nameContainerNameContainer": "container_de798d", + "hueCursor": "hue_cursor", + "hueHorizontal": "hue-horizontal", + "hueVertical": "hue-vertical", + "saturationBlack": "saturation-black", + "saturationColor": "saturation-color", + "saturationCursor": "saturation-cursor", + "saturationWhite": "saturation-white", + "searchResultsPagination": "container__68284", + "stopAnimations": "stop-animations", + "subtext": "subtext__8f869", + "themeCustomBackground": "custom-theme-background", + "themeDark": "theme-dark", + "themeLight": "theme-light", + "themeUndefined": "theme-undefined", + "userPopoutNote": "note_6O4w9y" + }, + "Toast": { + "avatar": "avatar_3xmbmC", + "bar": "bar_95Sdw1", + "barInner": "barInner_u7Frsw", + "bg": "bg_8df5St", + "brand": "colorBrand_WfDfzD", + "center": "center_9SpJeg", + "closable": "closable_7ShrRt", + "closeIcon": "closeIcon_8R553s", + "closing": "closing_K489s7", + "custom": "colorCustom_WKsPgT", + "customBar": "customBar_3RsUzs", + "danger": "colorDanger_O5wq9n", + "default": "colorDefault_XdNdIp", + "icon": "icon_OZgBPQ", + "info": "colorInfo_ujWH4-", + "inner": "inner_6Fsw34", + "left": "left_IXnX5I", + "opening": "opening_3TzSw2", + "right": "right_Eka_pI", + "success": "colorSuccess_6GreWs", + "text": "text_28VADE", + "toast": "toast_g4bfgI", + "toasts": "toasts_tApoHv", + "warning": "colorWarning_qZNMsx" + } + }, + "DiscordClassModules": { + "AccountDetails": {"props": ["usernameContainer", "container"]}, + "AccountDetailsButtons": {"props": ["button", "enabled", "disabled"]}, + "Anchor": {"props": ["anchor", "anchorUnderlineOnHover"]}, + "AnimationContainer": {"props": ["animatorLeft", "didRender"]}, + "AppBase": {"props": ["container", "base", "panels"]}, + "AppInner": {"props": ["app", "layers"]}, + "AppMount": {"props": ["appMount"]}, + "ApplicationStore": {"props": ["applicationStore", "navigation"]}, + "AppOuter": {"props": ["app", "mobileApp"]}, + "Attachment": {"props": ["wrapper", "video", "metadataDownload"]}, + "AttachmentCover": {"props": ["cover", "icon", "iconWrapper"]}, + "AuditLog": {"props": ["auditLog", "divider"]}, + "AuthBox": {"props": ["authBox", "authBoxExpanded"]}, + "AuthBoxBackground": {"props": ["splashBackground", "characterBackground"]}, + "Autocomplete": {"props": ["autocomplete", "autocompleteRow"]}, + "AutocompleteAliases": {"props": ["autocomplete", "stickerAutoComplete"]}, + "Avatar": {"props": ["avatar", "mask", "wrapper"]}, + "AvatarIcon": {"props": ["iconActiveLarge", "iconActiveMedium"]}, + "Backdrop": {"props": ["backdrop", "withLayer"]}, + "Badge": {"props": ["numberBadge", "textBadge", "iconBadge"]}, + "BotTag": {"props": ["botTag", "botTagInvert"]}, + "Button": {"props": ["colorBrand", "lookBlank"]}, + "CallCurrent": {"props": ["wrapper", "fullScreen"]}, + "CallDetails": {"props": ["container", "hotspot"]}, + "CallIncoming": {"props": ["wrapper", "mainChannelInfo"]}, + "CallScreenshare": {"props": ["videoControls", "topControls", "screenMessage"]}, + "Card": {"props": ["card", "cardBrand"]}, + "Category": {"props": ["wrapper", "children", "addButtonIcon"]}, + "CategoryArrow": {"props": ["arrow", "open"]}, + "ChangeLog": {"props": ["added", "fixed", "improved", "progress"]}, + "Channel": {"props": ["wrapper", "newBadge", "modeSelected"]}, + "ChannelContainer": {"props": ["actionIcon", "containerDefault"]}, + "ChannelContainerMentionsBadge": {"props": ["mentionsBadge", "iconBase"]}, + "ChannelLimit": {"props": ["users", "total", "wrapper"]}, + "ChannelTextArea": {"props": ["textArea", "buttons"]}, + "ChannelTextAreaAttachButton": {"props": ["attachButton", "attachWrapper"]}, + "ChannelTextAreaButton": {"props": ["buttonWrapper", "active"]}, + "ChannelTextAreaCharCounter": {"props": ["characterCount", "error"]}, + "ChannelTextAreaSlate": {"props": ["slateContainer", "placeholder"]}, + "ChatThreadSidebar": {"props": ["container", "floating", "resizeHandle"]}, + "ChatWindow": {"props": ["chat", "channelTextArea", "chatContent"]}, + "Checkbox": {"props": ["checkboxWrapper", "round"]}, + "ColorPicker": {"props": ["customColorPicker", "colorPickerDropper"]}, + "ColorPickerInner": {"props": ["hue", "saturation"]}, + "Combobox": {"props": ["combobox", "itemLabel"]}, + "Cursor": {"props": ["cursorDefault", "userSelectNone"]}, + "CustomStatus": {"props": ["customStatusSoloEmoji", "customStatusText", "customStatus"]}, + "CustomStatusIcon": {"props": ["textRuler", "emoji", "icon"]}, + "CustomStatusModal": {"props": ["inputContainer", "emojiButton", "modalRoot"]}, + "CustomStatusPopout": {"props": ["customStatusItem", "customEmoji"]}, + "DmAddPopout": {"props": ["popout", "searchBarComponent"]}, + "DmAddPopoutItems": {"props": ["friendSelected", "friendWrapper"]}, + "DefaultChannelWelcome": {"props": ["titleName", "card", "playCircleIcon"]}, + "DownloadLink": {"props": ["downloadLink"]}, + "Embed": {"props": ["embed", "embedAuthorIcon"]}, + "EmbedActions": {"props": ["iconPlay", "iconWrapperActive"]}, + "Emoji": {"props": ["emoji"], "length": 1}, + "EmojiButton": {"props": ["emojiButton", "sprite"]}, + "EmojiPicker": {"props": ["emojiPicker", "inspector"]}, + "EmojiPickerCustomEmoji": {"props": ["image", "imageLoading"]}, + "EmojiPickerDiversitySelector": {"props": ["diversityEmojiItemImage", "diversitySelectorOptions"]}, + "EmojiPickerItem": {"props": ["emojiSpriteImage"]}, + "EmojiPickerInspector": {"props": ["inspector", "graphicPrimary"]}, + "EmojiPickerInspectorEmoji": {"props": ["emoji", "glyphEmoji"]}, + "ErrorScreen": {"props": ["wrapper", "flexWrapper", "note"]}, + "ExpressionPicker": {"props": ["contentWrapper", "navButton", "navList"]}, + "FavButton": {"props": ["gifFavoriteButton", "selected", "icon"]}, + "File": {"props": ["downloadButton", "fileNameLink"]}, + "Flex": {"props": ["alignBaseline", "alignCenter"], "length": 24, "smaller": true}, + "FlexChild": {"props": ["flexChild", "flex"]}, + "FlowerStar": {"props": ["flowerStarContainer", "flowerStar"]}, + "FormText": {"props": ["description", "modeDefault"]}, + "ForumPage": {"props": ["newPostsButton", "list", "searchIcon"]}, + "Game": {"props": ["game", "gameName"]}, + "GameIcon": {"props": ["gameIcon", "small", "xsmall"]}, + "GameLibraryTable": {"props": ["stickyHeader", "emptyStateText"]}, + "GifFavoriteButton": {"props": ["gifFavoriteButton", "showPulse"]}, + "GoLiveDetails": {"props": ["panel", "gameWrapper"]}, + "Guild": {"props": ["wrapper", "lowerBadge", "svg"]}, + "GuildChannels": {"props": ["positionedContainer", "unreadBar"]}, + "GuildChannelsWrapper": {"props": ["hubContainer", "container"]}, + "GuildDiscovery": {"props": ["pageWrapper", "guildList"]}, + "GuildDm": {"props": ["pill"], "length": 1}, + "GuildEdges": {"props": ["wrapper", "target", "centerTarget"]}, + "GuildFolder": {"props": ["folder", "expandedFolderIconWrapper"]}, + "GuildHeader": {"props": ["header", "name", "bannerImage"]}, + "GuildHeaderButton": {"props": ["button", "open"]}, + "GuildItem": {"props": ["listItem", "serverEmoji"]}, + "GuildIcon": {"props": ["acronym", "selected", "wrapper"]}, + "GuildInvite": {"props": ["wrapper", "guildIconJoined"]}, + "GuildsButton": {"props": ["circleIconButton", "circleIcon"], "length": 6, "smaller": true}, + "GuildsDragPlaceholder": {"props": ["dragInner", "placeholderMask"], "length": 6, "smaller": true}, + "GuildSeparator": {"props": ["guildSeparator"]}, + "GuildServer": {"props": ["blobContainer", "pill"]}, + "GuildSettingsBanned": {"props": ["bannedUser", "bannedUserAvatar"]}, + "GuildSettingsEmoji": {"props": ["emojiRow", "emojiAliasPlaceholder"]}, + "GuildSettingsInvite": {"props": ["countdownColumn", "inviteSettingsInviteRow"]}, + "GuildSettingsMember": {"props": ["member", "membersFilterPopout"]}, + "GuildsListItem": {"props": ["listItemWrapper", "listItemTooltip"], "length": 6, "smaller": true}, + "GuildsItems": {"props": ["circleIcon", "guildsError"]}, + "GuildsWrapper": {"props": ["scroller", "unreadMentionsBar", "wrapper"]}, + "HeaderBar": {"props": ["container", "children", "toolbar"]}, + "HeaderBarExtras": {"props": ["headerBarLoggedOut", "search"]}, + "HeaderBarSearch": {"props": ["search", "searchBar", "open"]}, + "HeaderBarTopic": {"props": ["topic", "expandable", "content"]}, + "Heading": {"props": ["eyebrow", "code"]}, + "HomeIcon": {"props": ["homeIcon"]}, + "HotKeyRecorder": {"props": ["editIcon", "recording"]}, + "HoverCard": {"props": ["card", "active"]}, + "HoverCardRemoveButton": {"props": ["button", "filled", "default"]}, + "IconDirection": {"props": ["directionDown", "directionUp"]}, + "ImageAssets": {"props": ["pngImage", "stickerAsset"]}, + "ImageModal": {"props": ["image", "modal"], "length": 4, "smaller": true}, + "ImageMosaic": {"props": ["lazyImg", "oneByOneGridSingle"]}, + "ImageWrapper": {"props": ["clickable", "imageWrapperBackground"]}, + "Input": {"props": ["inputMini", "inputDefault"]}, + "InviteModal": {"props": ["inviteRow", "modal"]}, + "Item": {"props": ["item", "side", "header"]}, + "ItemRole": {"props": ["role", "dragged"]}, + "ItemLayerContainer": {"props": ["layer", "layerContainer"]}, + "Layers": {"props": ["layer", "layers"]}, + "LiveTag": {"props": ["liveLarge", "live"]}, + "LoadingScreen": {"props": ["container", "problemsText", "problems"]}, + "Margins": {"props": ["marginBottom4", "marginCenterHorz"]}, + "Menu": {"props": ["menu", "colorPremiumGradient", "item"]}, + "MenuCaret": {"props": ["arrow", "open"]}, + "MenuReactButton": {"props": ["wrapper", "icon", "focused", "button"]}, + "MenuSlider": {"props": ["slider", "sliderContainer"]}, + "Member": {"props": ["member", "ownerIcon"]}, + "MemberSince": {"props": ["memberSinceContainer", "discordIcon"]}, + "MembersWrapper": {"props": ["membersWrap", "membersGroup"]}, + "Message": {"props": ["message", "mentioned"]}, + "MessageAccessory": {"props": ["embedWrapper", "gifFavoriteButton"]}, + "MessageAttachment": {"props": ["removeAttachmentButton", "inline"]}, + "MessageBlocked": {"props": ["blockedMessageText", "expanded"]}, + "MessageBody": {"props": ["markupRtl", "edited"]}, + "MessageDivider": {"props": ["isUnread", "divider"]}, + "MessageElements": {"props": ["messageGroupBlockedBtn", "dividerRed"]}, + "MessageFile": {"props": ["cancelButton", "filenameLinkWrapper"]}, + "MessageLocalBot": {"props": ["ephemeralMessage", "icon"]}, + "MessageMarkup": {"props": ["markup"]}, + "MessagePopout": {"props": ["message", "spacing"]}, + "MessageOperations": {"props": ["operations"], "length": 1}, + "MessageReactions": {"props": ["reactions", "reactionMe"]}, + "MessageReactionsModal": {"props": ["reactor", "reactionSelected"]}, + "MessageReply": {"props": ["container", "text", "closeButton"]}, + "MessagesPopout": {"props": ["messagesPopoutWrap", "jumpButton"]}, + "MessagesPopoutButtons": {"props": ["secondary", "tertiary", "button"]}, + "MessagesPopoutTabBar": {"props": ["header", "tabBar", "active", "controls"]}, + "MessagesLoading": {"props": ["attachment", "blob", "cozy"]}, + "MessagesWelcome": {"props": ["emptyChannelIcon", "description", "header"]}, + "MessagesWelcomeButton": {"props": ["button", "buttonIcon"], "length": 2}, + "MessagesWelcomeThread": {"props": ["iconWrapper", "threadCreatorName"]}, + "MessagesWrap": {"props": ["messagesWrapper", "messageGroupBlocked"]}, + "MessageSystem": {"props": ["container", "actionAnchor"]}, + "MessageSystemAccessories": {"props": ["name", "spine", "cta"]}, + "MessageToolbar": {"props": ["container", "icon", "isHeader"]}, + "MessageToolbarItems": {"props": ["wrapper", "button", "separator"]}, + "Modal": {"props": ["root", "small", "medium"]}, + "ModalCarouselNav": {"props": ["nav", "navNext"]}, + "ModalCarouselWrapper": {"props": ["modalCarouselWrapper", "zoomedCarouselModalRoot"]}, + "ModalDivider": {"props": ["divider"], "length": 1}, + "ModalItems": {"props": ["guildName", "checkboxContainer"]}, + "ModalLayer": {"props": ["layer", "hidden", "backdrop"]}, + "ModalMiniContent": {"props": ["modal", "content"], "length": 2}, + "ModalSub": {"props": ["modal", "sizeLarge"]}, + "NameContainer": {"props": ["nameAndDecorators", "name"]}, + "NameContainerState": {"props": ["interactive", "selected", "muted"]}, + "NameTag": {"props": ["bot", "nameTag"]}, + "NitroStore": {"props": ["applicationStore", "marketingHeader"]}, + "NoteTextarea": {"props": ["textarea"], "length": 1}, + "Notice": {"props": ["notice", "platformIcon"]}, + "NoticePlatform": {"props": ["iconAndroid", "textLink"]}, + "PageImage": {"props": ["title", "image", "wrapper", "text"], "length": 5, "smaller": true}, + "Pagination": {"props": ["pageButton", "pageControl"]}, + "PeopleItem": {"props": ["peopleListItem", "active"]}, + "PeopleItemInfo": {"props": ["listItemContents", "actions"], "length": 2}, + "PeopleItemUser": {"props": ["userInfo", "discordTag"]}, + "PeopleList": {"props": ["peopleList", "emptyStateContainer"]}, + "Peoples": {"props": ["peopleColumn", "tabBar"]}, + "PeoplesNowPlayingMember": {"props": ["memberItem", "unknown"]}, + "PictureInPicture": {"props": ["pictureInPicture", "pictureInPictureWindow"]}, + "PillWrapper": {"props": ["item", "wrapper"], "length": 2}, + "PrivateChannel": {"props": ["channel", "closeButton"]}, + "PrivateChannelList": {"props": ["privateChannels", "searchBar"]}, + "PrivateChannelListScroller": {"props": ["privateChannelsHeaderContainer", "headerText"]}, + "PopoutActivity": {"props": ["ellipsis", "activityActivityFeed"]}, + "QuickMessage": {"props": ["input"], "length": 1}, + "QuickSelect": {"props": ["quickSelectArrow", "selected"]}, + "QuickSwitch": {"props": ["result", "guildIconContainer"]}, + "QuickSwitchWrap": {"props": ["container", "miscContainer"]}, + "RadioGroup": {"props": ["radioBar", "item"]}, + "Reactions": {"props": ["reactionBtn", "reaction"]}, + "RecentMentions": {"props": ["recentMentionsPopout"]}, + "RecentMentionsHeader": {"props": ["channelName", "channelHeader", "dmIcon"]}, + "Role": {"props": ["roleName", "roleRemoveIcon"]}, + "RoleCircle": {"props": ["roleCircle", "dot"]}, + "RoleIcon": {"props": ["clickable", "roleIcon"], "length": 4, "smaller": true}, + "Roles": {"props": ["rolePill", "roles", "rolePillBorder"]}, + "Scrollbar": {"props": ["scrollbar", "scrollbarGhost"]}, + "Scroller": {"props": ["scrollerBase", "none", "fade"]}, + "SearchBar": {"props": ["clear", "container", "pointer"]}, + "SearchPopout": {"props": ["datePicker", "searchResultChannelIconBackground"]}, + "SearchPopoutWrap": {"props": ["container", "queryContainer"]}, + "SearchResults": {"props": ["noResults", "searchResultsWrap"]}, + "SearchResultsElements": {"props": ["totalResults", "searchHeaderTabList"]}, + "SearchResultsGroup": {"props": ["searchResultGroup", "channelNameContainer"]}, + "SearchResultsMessage": {"props": ["message", "searchResult"]}, + "Select": {"props": ["searchable", "option", "selectedIcon"]}, + "SelectFilterPopout": {"props": ["selectFilterPopout", "avatar", "row"]}, + "SettingsCloseButton": {"props": ["closeButton", "keybind"]}, + "SettingsItems": {"props": ["labelRow", "note"]}, + "SettingsTable": {"props": ["headerOption", "headerName"]}, + "SettingsWindow": {"props": ["contentRegion", "standardSidebarView"]}, + "SettingsWindowScroller": {"props": ["sidebarScrollable", "content", "scroller"]}, + "Slider": {"props": ["slider", "grabber"]}, + "Spinner": {"props": ["spinner", "chasingDots"]}, + "Spoiler": {"props": ["spoilerContainer", "hidden"]}, + "Switch": {"props": ["container", "slider", "input"]}, + "Table": {"props": ["stickyHeader", "sortIcon"]}, + "Text": {"props": ["defaultColor", "defaultMarginh1"]}, + "Text2": {"props": ["defaultColor", "selectable", "lineClamp1"]}, + "TextColor": {"props": ["colorStandard", "colorMuted", "colorError"]}, + "TextColor2": {"props": ["muted", "wrapper", "base"]}, + "TextSize": {"props": ["size10", "size14", "size20"]}, + "TextStyle": {"props": ["strikethrough", "underline", "bold"]}, + "ThreadCard": {"props": ["threadName", "container", "facepile"]}, + "Tip": {"props": ["pro", "inline"]}, + "TitleBar": {"props": ["titleBar", "wordmark"]}, + "Tooltip": {"props": ["tooltip", "tooltipTop"]}, + "TooltipGuild": {"props": ["rowIcon", "rowGuildName"]}, + "Typing": {"props": ["cooldownWrapper", "typing"]}, + "UnreadBar": {"props": ["active", "bar", "unread"]}, + "UploadModal": {"props": ["uploadModal", "bgScale"]}, + "UserBadges": {"props": ["profileBadge", "container"]}, + "UserBanner": {"props": ["popoutBanner", "popoutBannerPremium"]}, + "UserPopoutFooter": {"props": ["wumpusTooltip", "messageInputContainer"]}, + "UserPopoutHeader": {"props": ["avatarHint", "profileBadges", "avatarWrapper"]}, + "UserPopoutSection": {"props": ["section", "lastSection"]}, + "UserPopoutSectionTitle": {"props": ["title", "body", "clydeMoreInfo"]}, + "UserPopoutUsernameSection": {"props": ["userTagNoNickname", "nickname", "headerBotTag"]}, + "UserProfile": {"props": ["root", "tabBarContainer"]}, + "UserProfileHeader": {"props": ["additionalActionsIcon", "header", "relationshipButtons"]}, + "UserProfileInfoSection": {"props": ["userInfoSection", "note"]}, + "UserProfileList": {"props": ["listAvatar", "listRow"]}, + "UserProfileListGuild": {"props": ["guildAvatar", "guildAvatarWithoutIcon"]}, + "UserProfileUsernameSection": {"props": ["customStatusSoloEmoji", "nameTag", "discriminator"]}, + "UserSettingsAppearancePreview": {"props": ["preview", "firstMessage"]}, + "UserSettingsSocialLinks": {"props": ["socialLinks", "link"]}, + "UserSummaryItem": {"props": ["avatarContainerMasked", "container"]}, + "UserTheme": {"props": ["userPopoutOuter", "userProfileOuter"]}, + "Video": {"props": ["video", "fullScreen", "chatSidebarOpen"]}, + "VoiceChannel": {"props": ["avatarSpeaking", "voiceUser"]}, + "VoiceChannelLimit": {"props": ["total", "users", "wrapper"]}, + "VoiceChannelList": {"props": ["list", "collapsed"]}, + "VoiceDetails": {"props": ["container", "customStatusContainer"]}, + "VoiceDetailsPing": {"props": ["rtcConnectionQualityBad", "rtcConnectionQualityFine"]}, + "WebhookCard": {"props": ["pulseBorder", "copyButton"]} + }, + "DiscordClasses": { + "_bdguild": ["BD", "bdGuild"], + "_bdguildanimatable": ["BD", "bdGuildAnimatable"], + "_bdguildaudio": ["BD", "bdGuildAudio"], + "_bdguildselected": ["BD", "bdGuildSelected"], + "_bdguildseparator": ["BD", "bdGuildSeparator"], + "_bdguildunread": ["BD", "bdGuildUnread"], + "_bdguildvideo": ["BD", "bdGuildVideo"], + "_bdminimalmode": ["BD", "bdMinimalMode"], + "_bdpillselected": ["BD", "bdPillSelected"], + "_bdpillunread": ["BD", "bdPillUnread"], + "_betterfriendlistmutualguilds": ["BetterFriendList", "mutualGuilds"], + "_betterfriendlistnamecell": ["BetterFriendList", "nameCell"], + "_betterfriendlisttitle": ["BetterFriendList", "title"], + "_betternsfwtagtag": ["BetterNsfwTag", "nsfwTag"], + "_bettersearchpagepagination": ["BetterSearchPage", "pagination"], + "_chatfilterblocked": ["ChatFilter", "blocked"], + "_chatfilterblockedstamp": ["ChatFilter", "blockedStamp"], + "_chatfiltercensored": ["ChatFilter", "censored"], + "_chatfiltercensoredstamp": ["ChatFilter", "censoredStamp"], + "_charcountercounter": ["CharCounter", "charCounter"], + "_charcounterchatcounter": ["CharCounter", "chatCounter"], + "_charcountercounteradded": ["CharCounter", "counterAdded"], + "_charcountercustomstatuscounter": ["CharCounter", "customStatusCounter"], + "_charcountereditcounter": ["CharCounter", "editCounter"], + "_charcounterpopoutnotecounter": ["CharCounter", "popoutNoteCounter"], + "_charcounterprofilenotecounter": ["CharCounter", "profileNoteCounter"], + "_charcounterthreadcreationcounter": ["CharCounter", "threadCreation"], + "_charcounteruploadcounter": ["CharCounter", "uploadCounter"], + "_customstatuspresetscustomstatusitem": ["CustomStatusPresets", "customStatusItem"], + "_customstatuspresetsdeletebutton": ["CustomStatusPresets", "deleteButton"], + "_customstatuspresetsdeleteicon": ["CustomStatusPresets", "deleteIcon"], + "_customstatuspresetsdragpreview": ["CustomStatusPresets", "dragPreview"], + "_customstatuspresetssortablecard": ["CustomStatusPresets", "sortableCard"], + "_customstatuspresetssortdivider": ["CustomStatusPresets", "sortDivider"], + "_customstatuspresetsstatus": ["CustomStatusPresets", "status"], + "_displayserversaschannelsbadge": ["DisplayServersAsChannels", "badge"], + "_displayserversaschannelsmuted": ["DisplayServersAsChannels", "muted"], + "_displayserversaschannelsname": ["DisplayServersAsChannels", "name"], + "_displayserversaschannelsstyled": ["DisplayServersAsChannels", "styled"], + "_emojistatisticsstatisticsbutton": ["EmojiStatistics", "statisticsButton"], + "_emojistatisticsamountcell": ["EmojiStatistics", "amountCell"], + "_emojistatisticsiconcell": ["EmojiStatistics", "iconCell"], + "_emojistatisticsnamecell": ["EmojiStatistics", "nameCell"], + "_friendnotificationslogavatar": ["FriendNotifications", "logAvatar"], + "_friendnotificationslogcontent": ["FriendNotifications", "logContent"], + "_friendnotificationslogtime": ["FriendNotifications", "logTime"], + "_friendnotificationsfriendsonline": ["FriendNotifications", "friendsOnline"], + "_friendnotificationsfriendsonlinewrap": ["FriendNotifications", "friendsOnlineWrap"], + "_friendnotificationstimelogmodal": ["FriendNotifications", "timeLogModal"], + "_friendnotificationstypelabel": ["FriendNotifications", "typeLabel"], + "_gameactivitytoggleadded": ["GameActivityToggle", "added"], + "_imageutilitiesdetails": ["ImageUtilities", "details"], + "_imageutilitiesdetailsadded": ["ImageUtilities", "detailsAdded"], + "_imageutilitiesdetailslabel": ["ImageUtilities", "detailsLabel"], + "_imageutilitiesdetailswrapper": ["ImageUtilities", "detailsWrapper"], + "_imageutilitiesgallery": ["ImageUtilities", "gallery"], + "_imageutilitiesimagedetails": ["ImageUtilities", "imageDetails"], + "_imageutilitiesimagedetailsadded": ["ImageUtilities", "imageDetailsAdded"], + "_imageutilitieslense": ["ImageUtilities", "lens"], + "_imageutilitieslensebackdrop": ["ImageUtilities", "lensBackdrop"], + "_imageutilitiesnext": ["ImageUtilities", "next"], + "_imageutilitiesoperations": ["ImageUtilities", "operations"], + "_imageutilitiesprevious": ["ImageUtilities", "previous"], + "_imageutilitiessibling": ["ImageUtilities", "sibling"], + "_imageutilitiesswitchicon": ["ImageUtilities", "switchIcon"], + "_lastmessagedateicon": ["LastMessageDate", "icon"], + "_oldtitlebarenabled": ["OldTitleBar", "oldTitleBarEnabled"], + "_oldtitlebarsettingstoolbar": ["OldTitleBar", "settingsToolbar"], + "_oldtitlebartoolbar": ["OldTitleBar", "toolbar"], + "_personalpinsmessagetag": ["PersonalPins", "messageTag"], + "_personalpinsmessagetagadd": ["PersonalPins", "messageTagAdd"], + "_personalpinsmessagetagaddactive": ["PersonalPins", "messageTagAddActive"], + "_personalpinsmessagetagdelete": ["PersonalPins", "messageTagDelete"], + "_personalpinsmessagetagname": ["PersonalPins", "messageTagName"], + "_pindmsdragpreview": ["PinDMs", "dragPreview"], + "_pindmsdmchannelpinned": ["PinDMs", "dmChannelPinned"], + "_pindmsdmchannelplaceholder": ["PinDMs", "dmChannelPlaceholder"], + "_pindmspinnedchannelsheaderamount": ["PinDMs", "pinnedChannelsHeaderAmount"], + "_pindmspinnedchannelsheaderarrow": ["PinDMs", "pinnedChannelsHeaderArrow"], + "_pindmspinnedchannelsheadercollapsed": ["PinDMs", "pinnedChannelsHeaderCollapsed"], + "_pindmspinnedchannelsheadercolored": ["PinDMs", "pinnedChannelsHeaderColored"], + "_pindmspinnedchannelsheadercontainer": ["PinDMs", "pinnedChannelsHeaderContainer"], + "_pindmsrecentpinned": ["PinDMs", "recentPinned"], + "_pindmsrecentplaceholder": ["PinDMs", "recentPlaceholder"], + "_pindmsunpinbutton": ["PinDMs", "unpinButton"], + "_pindmsunpinicon": ["PinDMs", "unpinIcon"], + "_pluginrepofailnotice": ["PluginRepo", "failNotice"], + "_pluginrepoloadingicon": ["PluginRepo", "loadingIcon"], + "_pluginrepoloadingtooltip": ["PluginRepo", "loadingTooltip"], + "_pluginreponewentriesnotice": ["PluginRepo", "newEntriesNotice"], + "_pluginreponotice": ["PluginRepo", "repoNotice"], + "_pluginrepooutdatednotice": ["PluginRepo", "outdatedNotice"], + "_readallnotificationsbuttonbutton": ["ReadAllNotificationsButton", "button"], + "_readallnotificationsbuttonframe": ["ReadAllNotificationsButton", "frame"], + "_readallnotificationsbuttoninner": ["ReadAllNotificationsButton", "innerFrame"], + "_servercounterservercount": ["ServerCounter", "serverCount"], + "_servercounterservercountwrap": ["ServerCounter", "serverCountWrap"], + "_serverdetailsdetails": ["ServerDetails", "details"], + "_serverdetailsicon": ["ServerDetails", "icon"], + "_serverdetailstooltip": ["ServerDetails", "tooltip"], + "_serverfoldersdragpreview": ["ServerFolders", "dragPreview"], + "_serverfoldersfoldercontent": ["ServerFolders", "folderContent"], + "_serverfoldersfoldercontentclosed": ["ServerFolders", "folderContentClosed"], + "_serverfoldersfoldercontentisopen": ["ServerFolders", "folderContentIsOpen"], + "_serverfoldersguildplaceholder": ["ServerFolders", "guildPlaceholder"], + "_serverfoldershassidebar": ["ServerFolders", "hasSideBar"], + "_serverfoldersiconswatch": ["ServerFolders", "iconSwatch"], + "_serverfoldersiconswatchinner": ["ServerFolders", "iconSwatchInner"], + "_serverfoldersiconswatchpreview": ["ServerFolders", "iconSwatchPreview"], + "_serverfoldersiconswatchnopreview": ["ServerFolders", "iconSwatchNoPreview"], + "_serverfoldersiconswatchselected": ["ServerFolders", "iconSwatchSelected"], + "_showbadgesinchatbadges": ["ShowBadgesInChat", "badges"], + "_showbadgesinchatbadgeschat": ["ShowBadgesInChat", "badgesChat"], + "_showbadgesinchatbadgesdmslist": ["ShowBadgesInChat", "badgesDMs"], + "_showbadgesinchatbadgesmemberlist": ["ShowBadgesInChat", "badgesMembers"], + "_showbadgesinchatbadgessettings": ["ShowBadgesInChat", "badgesSettings"], + "_showbadgesinchatindicator": ["ShowBadgesInChat", "indicator"], + "_showconnectionsconnection": ["ShowConnections", "connection"], + "_showconnectionsconnections": ["ShowConnections", "connections"], + "_showconnectionsicon": ["ShowConnections", "connectionIcon"], + "_showconnectionsverifiedbadge": ["ShowConnections", "verifiedBadge"], + "_spellcheckerror": ["SpellCheck", "error"], + "_spellcheckoverlay": ["SpellCheck", "overlay"], + "_spotifycontrolsactivitybutton": ["SpotifyControls", "activityButton"], + "_spotifycontrolsbar": ["SpotifyControls", "bar"], + "_spotifycontrolsbarfill": ["SpotifyControls", "barFill"], + "_spotifycontrolsbargrabber": ["SpotifyControls", "barGabber"], + "_spotifycontrolsbartext": ["SpotifyControls", "barText"], + "_spotifycontrolsbuttonactive": ["SpotifyControls", "buttonActive"], + "_spotifycontrolscontainer": ["SpotifyControls", "container"], + "_spotifycontrolscontainerinner": ["SpotifyControls", "containerInner"], + "_spotifycontrolscontainermaximized": ["SpotifyControls", "containerMaximized"], + "_spotifycontrolscontainerpaused": ["SpotifyControls", "containerPaused"], + "_spotifycontrolscontainerwithtimeline": ["SpotifyControls", "containerWithTimeline"], + "_spotifycontrolscover": ["SpotifyControls", "cover"], + "_spotifycontrolscovermaximizer": ["SpotifyControls", "coverMaximizer"], + "_spotifycontrolscoverwrapper": ["SpotifyControls", "coverWrapper"], + "_spotifycontrolsdetails": ["SpotifyControls", "details"], + "_spotifycontrolsinterpret": ["SpotifyControls", "interpret"], + "_spotifycontrolssettingsicon": ["SpotifyControls", "settingsIcon"], + "_spotifycontrolssettingslabel": ["SpotifyControls", "settingsLabel"], + "_spotifycontrolssong": ["SpotifyControls", "song"], + "_spotifycontrolstimeline": ["SpotifyControls", "timeline"], + "_spotifycontrolsvolumeslider": ["SpotifyControls", "volumeSlider"], + "_stafftagadminicon": ["StaffTag", "adminIcon"], + "_stafftagforumcreatoricon": ["StaffTag", "forumCreatorIcon"], + "_stafftaggroupownericon": ["StaffTag", "groupOwnerIcon"], + "_stafftagmanagementicon": ["StaffTag", "managementIcon"], + "_stafftagownericon": ["StaffTag", "ownerIcon"], + "_stafftagthreadcreatoricon": ["StaffTag", "threadCreatorIcon"], + "_themerepodragbar": ["ThemeRepo", "dragBar"], + "_themerepodragcorner": ["ThemeRepo", "dragCorner"], + "_themerepodraginterface": ["ThemeRepo", "dragInterface"], + "_themerepofailnotice": ["ThemeRepo", "failNotice"], + "_themerepoloadingicon": ["ThemeRepo", "loadingIcon"], + "_themerepoloadingtooltip": ["ThemeRepo", "loadingTooltip"], + "_themerepomovebar": ["ThemeRepo", "moveBar"], + "_themereponewentriesnotice": ["ThemeRepo", "newEntriesNotice"], + "_themereponotice": ["ThemeRepo", "repoNotice"], + "_themerepooutdatednotice": ["ThemeRepo", "outdatedNotice"], + "_themerepopreview": ["ThemeRepo", "preview"], + "_themerepopreviewfullscreen": ["ThemeRepo", "previewFullscreen"], + "_themerepopreviewmoving": ["ThemeRepo", "previewMoving"], + "_themesettingscard": ["ThemeSettings", "card"], + "_themesettingscardname": ["ThemeSettings", "cardName"], + "_themesettingsgrid": ["ThemeSettings", "grid"], + "_timedlightdarkmodedategrabber": ["TimedLightDarkMode", "dateGrabber"], + "_timedlightdarkmodetimergrabber": ["TimedLightDarkMode", "timerGrabber"], + "_timedlightdarkmodetimersettings": ["TimedLightDarkMode", "timerSettings"], + "_toproleseverywherebadgestyle": ["TopRolesEverywhere", "badgeStyle"], + "_toproleseverywherechattag": ["TopRolesEverywhere", "chatTag"], + "_toproleseverywheremembertag": ["TopRolesEverywhere", "memberTag"], + "_toproleseverywhererolestyle": ["TopRolesEverywhere", "roleStyle"], + "_toproleseverywheretag": ["TopRolesEverywhere", "tag"], + "_toproleseverywherevoicetag": ["TopRolesEverywhere", "voiceTag"], + "_translatorconfigbutton": ["Translator", "configButton"], + "_translatortranslatebutton": ["Translator", "translateButton"], + "_translatortranslated": ["Translator", "translated"], + "_translatortranslating": ["Translator", "translating"], + "_writeuppercasequicktogglebutton": ["WriteUpperCase", "quickToggleButton"], + "_writeuppercasequicktogglebuttonenabled": ["WriteUpperCase", "enabled"], + "_repoauthor": ["BD", "bdAuthor"], + "_repobutton": ["BD", "bdButton"], + "_repobuttondanger": ["BD", "bdButtonDanger"], + "_repocard": ["BD", "bdAddonCard"], + "_repochangelogbutton": ["BDFDB", "changeLogButton"], + "_repocheckbox": ["BD", "switchCheckbox"], + "_repocheckboxchecked": ["BD", "switchChecked"], + "_repocheckboxinner": ["BD", "switch"], + "_repocheckboxitem": ["BD", "switchItem"], + "_repocheckboxwrap": ["BD", "switchWrapper"], + "_repocontrols": ["BD", "bdControls"], + "_repocontrolsbutton": ["BD", "bdControlsButton"], + "_repodescription": ["BD", "bdDescription"], + "_repodescriptionwrap": ["BD", "bdDescriptionWrap"], + "_repoentry": ["BDFDB", "bdRepoEntry"], + "_repofolderbutton": ["BD", "bdFolderButton"], + "_repofooter": ["BD", "bdFooter"], + "_repofootercontrols": ["BDFDB", "bdRepoFooterControls"], + "_repoheader": ["BD", "bdHeader"], + "_repoheadercontrols": ["BDFDB", "bdRepoHeaderControls"], + "_repoheadertitle": ["BD", "bdHeaderTitle"], + "_repo": ["BDFDB", "bdRepo"], + "_repoicon": ["BD", "bdIcon"], + "_repolist": ["BD", "bdAddonList"], + "_repolistheader": ["BDFDB", "bdRepoListHeader"], + "_repolistscroller": ["BDFDB", "bdRepoListScroller"], + "_repolink": ["BD", "bdLink"], + "_repolinks": ["BD", "bdLinks"], + "_repometa": ["BD", "bdMeta"], + "_repomodal": ["BD", "bdAddonModal"], + "_repomodalfooter": ["BD", "bdAddonModalFooter"], + "_repomodalheader": ["BD", "bdAddonModalHeader"], + "_repomodalsettings": ["BD", "bdAddonModalSettings"], + "_reponame": ["BD", "bdName"], + "_reposettings": ["BD", "settings"], + "_reposettingsopen": ["BD", "settingsOpen"], + "_reposettingsclosed": ["BD", "settingsClosed"], + "_reposwitch": ["BD", "bdSwitch"], + "_reposwitchchecked": ["BD", "bdSwitchChecked"], + "_reposwitchinner": ["BD", "bdSwitchInner"], + "_repoupdatebutton": ["BD", "bdUpdatebtn"], + "_repoversion": ["BD", "bdVersion"], + "accountinfo": ["AccountDetails", "container"], + "accountinfoavatar": ["AccountDetails", "avatar"], + "accountinfoavatarwrapper": ["AccountDetails", "avatarWrapper"], + "accountinfobutton": ["AccountDetailsButtons", "button"], + "accountinfobuttondisabled": ["AccountDetailsButtons", "disabled"], + "accountinfobuttonenabled": ["AccountDetailsButtons", "enabled"], + "accountinfobuttonstrikethrough": ["AccountDetails", "strikethrough"], + "accountinfodetails": ["AccountDetails", "usernameContainer"], + "accountinfonametag": ["AccountDetails", "nameTag"], + "accountinfowithtagasbutton": ["AccountDetails", "withTagAsButton"], + "accountinfowithtagless": ["AccountDetails", "withTagAsButton"], + "aliasautocomplete": ["AutocompleteAliases", "autocomplete"], + "anchor": ["Anchor", "anchor"], + "anchorunderlineonhover": ["Anchor", "anchorUnderlineOnHover"], + "animationcontainerbottom": ["AnimationContainer", "animatorBottom"], + "animationcontainercenter": ["AnimationContainer", "animatorCenter"], + "animationcontainerfade": ["AnimationContainer", "fade"], + "animationcontainerleft": ["AnimationContainer", "animatorLeft"], + "animationcontainerright": ["AnimationContainer", "animatorRight"], + "animationcontainertop": ["AnimationContainer", "animatorTop"], + "animationcontainerrender": ["AnimationContainer", "didRender"], + "animationcontainerscale": ["AnimationContainer", "scale"], + "animationcontainertranslate": ["AnimationContainer", "translate"], + "app": ["AppOuter", "app"], + "appcontainer": ["AppBase", "container"], + "appinner": ["AppInner", "app"], + "appmount": ["AppMount", "appMount"], + "applayers": ["AppInner", "layers"], + "applicationstore": ["ApplicationStore", "applicationStore"], + "attachment": ["Attachment", "wrapper"], + "attachmentcontrolshidden": ["Attachment", "wrapperControlsHidden"], + "attachmentcover": ["AttachmentCover", "cover"], + "attachmentcovericon": ["AttachmentCover", "icon"], + "attachmentvideo": ["Attachment", "video"], + "attachmentvideocontrols": ["Attachment", "videoControls"], + "auditlog": ["AuditLog", "auditLog"], + "auditlogoverflowellipsis": ["AuditLog", "overflowEllipsis"], + "auditlogtimestamp": ["AuditLog", "timestamp"], + "auditloguserhook": ["AuditLog", "userHook"], + "authbox": ["AuthBox", "authBox"], + "authboxcharacterbackground": ["AuthBoxBackground", "characterBackground"], + "authboxsplashbackground": ["AuthBoxBackground", "splashBackground"], + "autocomplete": ["Autocomplete", "autocomplete"], + "autocompletecontenttitle": ["Autocomplete", "contentTitle"], + "autocompletedescriptiondiscriminator": ["Autocomplete", "descriptionDiscriminator"], + "autocompleteemoji": ["Autocomplete", "emojiImage"], + "autocompleteicon": ["Autocomplete", "icon"], + "autocompleteiconforeground": ["Autocomplete", "iconForeground"], + "autocompleteinner": ["Autocomplete", "autocompleteInner"], + "autocompleterow": ["Autocomplete", "autocompleteRow"], + "autocompleterowcontent": ["Autocomplete", "autocompleteRowContent"], + "autocompleterowcontentprimary": ["Autocomplete", "autocompleteRowContentPrimary"], + "autocompleterowcontentsecondary": ["Autocomplete", "autocompleteRowContentSecondary"], + "autocompleterowhorizontal": ["Autocomplete", "autocompleteRowHorizontal"], + "autocompleterowicon": ["Autocomplete", "autocompleteRowIcon"], + "autocompleterowsubheading": ["Autocomplete", "autocompleteRowSubheading"], + "autocompleterowvertical": ["Autocomplete", "autocompleteRowVertical"], + "avatar": ["Avatar", "avatar"], + "avatarcursordefault": ["Avatar", "cursorDefault"], + "avatardisabled": ["BDFDB", "avatarDisabled"], + "avataricon": ["AvatarIcon", "icon"], + "avatariconactivelarge": ["AvatarIcon", "iconActiveLarge"], + "avatariconactivemedium": ["AvatarIcon", "iconActiveMedium"], + "avatariconactivemini": ["AvatarIcon", "iconActiveMini"], + "avatariconactivesmall": ["AvatarIcon", "iconActiveSmall"], + "avatariconactivexlarge": ["AvatarIcon", "iconActiveXLarge"], + "avatariconinactive": ["AvatarIcon", "iconInactive"], + "avatariconsizelarge": ["AvatarIcon", "iconSizeLarge"], + "avatariconsizemedium": ["AvatarIcon", "iconSizeMedium"], + "avatariconsizemini": ["AvatarIcon", "iconSizeMini"], + "avatariconsizesmol": ["AvatarIcon", "iconSizeSmol"], + "avatariconsizesmall": ["AvatarIcon", "iconSizeSmall"], + "avatariconsizexlarge": ["AvatarIcon", "iconSizeXLarge"], + "avatarmask": ["Avatar", "mask"], + "avatarnoicon": ["AvatarIcon", "noIcon"], + "avatarpointer": ["Avatar", "pointer"], + "avatarpointerevents": ["Avatar", "pointerEvents"], + "avatarstack": ["Avatar", "avatarStack"], + "avatarsvg": ["Avatar", "svg"], + "avatarwrapper": ["Avatar", "wrapper"], + "backdrop": ["Backdrop", "backdrop"], + "backdropwithlayer": ["Backdrop", "withLayer"], + "badgebase": ["Badge", "base"], + "badgeicon": ["Badge", "icon"], + "badgeiconbadge": ["Badge", "iconBadge"], + "badgenumberbadge": ["Badge", "numberBadge"], + "badgeshaperound": ["Badge", "baseShapeRound"], + "badgeshaperoundleft": ["Badge", "baseShapeRoundLeft"], + "badgeshaperoundright": ["Badge", "baseShapeRoundRight"], + "badgetextbadge": ["Badge", "textBadge"], + "bdfdbbadge": ["BDFDB", "badge"], + "bdfdbbadgeavatar": ["BDFDB", "badgeAvatar"], + "bdfdbdev": ["BDFDB", "dev"], + "bdfdbhasbadge": ["BDFDB", "hasBadge"], + "bdfdbsupporter": ["BDFDB", "supporter"], + "bdfdbsupportert1": ["BDFDB", "supporterTier1"], + "bdfdbsupportert2": ["BDFDB", "supporterTier2"], + "bdfdbsupportert3": ["BDFDB", "supporterTier3"], + "bdfdbsupportert4": ["BDFDB", "supporterTier4"], + "bold": ["TextStyle", "bold"], + "bottag": ["BotTag", "botTag"], + "bottaginvert": ["BotTag", "botTagInvert"], + "bottagmember": ["Member", "botTag"], + "bottagnametag": ["NameTag", "bot"], + "bottagpx": ["BotTag", "px"], + "bottagregular": ["BotTag", "botTagRegular"], + "bottagrem": ["BotTag", "rem"], + "bottagtext": ["BotTag", "botText"], + "bottagverified": ["BotTag", "botTagVerified"], + "button": ["Button", "button"], + "buttoncolorbrand": ["Button", "colorBrand"], + "buttoncolorbrandnew": ["Button", "colorBrandNew"], + "buttoncolorgreen": ["Button", "colorGreen"], + "buttoncolorlink": ["Button", "colorLink"], + "buttoncolorprimary": ["Button", "colorPrimary"], + "buttoncolorred": ["Button", "colorRed"], + "buttoncolortransparent": ["Button", "colorTransparent"], + "buttoncolorwhite": ["Button", "colorWhite"], + "buttoncoloryellow": ["Button", "colorYellow"], + "buttoncontents": ["Button", "contents"], + "buttondisabledoverlay": ["Button", "disabledButtonOverlay"], + "buttondisabledwrapper": ["Button", "disabledButtonWrapper"], + "buttonfullwidth": ["Button", "fullWidth"], + "buttongrow": ["Button", "grow"], + "buttonhashover": ["Button", "hasHover"], + "buttonhoverbrand": ["Button", "hoverBrand"], + "buttonhoverbrandnew": ["Button", "hoverBrandNew"], + "buttonhovergreen": ["Button", "hoverGreen"], + "buttonhoverlink": ["Button", "hoverLink"], + "buttonhoverprimary": ["Button", "hoverPrimary"], + "buttonhoverred": ["Button", "hoverRed"], + "buttonhovertransparent": ["Button", "hoverTransparent"], + "buttonhoverwhite": ["Button", "hoverWhite"], + "buttonhoveryellow": ["Button", "hoverYellow"], + "buttonlookblank": ["Button", "lookBlank"], + "buttonlookfilled": ["Button", "lookFilled"], + "buttonlookinverted": ["Button", "lookInverted"], + "buttonlooklink": ["Button", "lookLink"], + "buttonlookoutlined": ["Button", "lookOutlined"], + "buttonsizeicon": ["Button", "sizeIcon"], + "buttonsizelarge": ["Button", "sizeLarge"], + "buttonsizemax": ["Button", "sizeMax"], + "buttonsizemedium": ["Button", "sizeMedium"], + "buttonsizemin": ["Button", "sizeMin"], + "buttonsizesmall": ["Button", "sizeSmall"], + "buttonsizexlarge": ["Button", "sizeXlarge"], + "buttonspinner": ["Button", "spinner"], + "buttonspinneritem": ["Button", "spinnerItem"], + "buttonsubmitting": ["Button", "submitting"], + "callcurrentchatsidebaropen": ["Video", "chatSidebarOpen"], + "callcurrentcontainer": ["CallCurrent", "wrapper"], + "callcurrentdetails": ["CallDetails", "container"], + "callcurrentvideo": ["Video", "video"], + "callincomingicon": ["CallIncoming", "icon"], + "callincomingroot": ["CallIncoming", "root"], + "callincomingtitle": ["CallIncoming", "title"], + "callincomingwrapper": ["CallIncoming", "wrapper"], + "callscreensharebottomcontrols": ["CallScreenshare", "bottomControls"], + "callscreensharechatbutton": ["CallScreenshare", "chatButton"], + "callscreensharetopcontrols": ["CallScreenshare", "topControls"], + "callscreensharevideocontrols": ["CallScreenshare", "videoControls"], + "card": ["Card", "card"], + "cardbrand": ["Card", "cardBrand"], + "cardbrandoutline": ["Card", "cardBrandOutline"], + "carddanger": ["Card", "cardDanger"], + "carddangeroutline": ["Card", "cardDangerOutline"], + "cardprimary": ["Card", "cardPrimary"], + "cardprimaryeditable": ["Card", "cardPrimaryEditable"], + "cardprimaryoutline": ["Card", "cardPrimaryOutline"], + "cardprimaryoutlineeditable": ["Card", "cardPrimaryOutlineEditable"], + "cardsuccess": ["Card", "cardSuccess"], + "cardsuccessoutline": ["Card", "cardSuccessOutline"], + "cardwarning": ["Card", "cardWarning"], + "cardwarningoutline": ["Card", "cardWarningOutline"], + "categoryarrow": ["CategoryArrow", "arrow"], + "categoryarrowopen": ["CategoryArrow", "open"], + "categoryaddbutton": ["Category", "addButton"], + "categoryaddbuttonicon": ["Category", "addButtonIcon"], + "categorychildren": ["Category", "children"], + "categoryclickable": ["Category", "clickable"], + "categorycollapsed": ["Category", "collapsed"], + "categorycontainerdefault": ["Category", "containerDefault"], + "categoryforcevisible": ["Category", "forceVisible"], + "categoryicon": ["Category", "icon"], + "categoryiconvisibility": ["Category", "iconVisibility"], + "categorymaincontent": ["Category", "mainContent"], + "categorymuted": ["Category", "muted"], + "categoryname": ["Category", "name"], + "categorywrapper": ["Category", "wrapper"], + "changelogadded": ["ChangeLog", "added"], + "changelogcontainer": ["ChangeLog", "container"], + "changelogfixed": ["ChangeLog", "fixed"], + "changelogfooter": ["ChangeLog", "footer"], + "changelogimproved": ["ChangeLog", "improved"], + "changelogprogress": ["ChangeLog", "progress"], + "changelogsociallink": ["ChangeLog", "socialLink"], + "changelogtitle": ["ChangeLog", "title"], + "channelactionicon": ["ChannelContainer", "actionIcon"], + "channelchildicon": ["ChannelContainer", "iconItem"], + "channelchildiconbase": ["ChannelContainer", "iconBase"], + "channelchildren": ["Channel", "children"], + "channelcontainerdefault": ["ChannelContainer", "containerDefault"], + "channelcontent": ["Channel", "link"], + "channelheaderavatar": ["ChatWindow", "avatar"], + "channelheaderchannelname": ["ChatWindow", "channelName"], + "channelheaderchildren": ["HeaderBar", "children"], + "channelheadercursorpointer": ["ChatWindow", "cursorPointer"], + "channelheaderdivider": ["HeaderBar", "divider"], + "channelheaderheaderbar": ["HeaderBar", "container"], + "channelheaderheaderbarthemed": ["HeaderBar", "themed"], + "channelheaderheaderbartitle": ["HeaderBar", "title"], + "channelheadericon": ["HeaderBar", "icon"], + "channelheadericonbadge": ["HeaderBar", "iconBadge"], + "channelheadericonclickable": ["HeaderBar", "clickable"], + "channelheadericonselected": ["HeaderBar", "selected"], + "channelheadericonwrapper": ["HeaderBar", "iconWrapper"], + "channelheadertitle": ["ChatWindow", "title"], + "channelheadertitlewrapper": ["ChatWindow", "titleWrapper"], + "channelheadersearch": ["HeaderBarExtras", "search"], + "channelheadersearchbar": ["HeaderBarSearch", "searchBar"], + "channelheadersearchicon": ["HeaderBarSearch", "icon"], + "channelheadersearchinner": ["HeaderBarSearch", "search"], + "channelheadertoolbar": ["HeaderBar", "toolbar"], + "channelheadertoolbar2": ["HeaderBarExtras", "toolbar"], + "channelheadertopic": ["HeaderBarTopic", "topic"], + "channelheadertopicexpandable": ["HeaderBarTopic", "expandable"], + "channelicon": ["Channel", "icon"], + "channeliconcontainer": ["Channel", "iconContainer"], + "channeliconitem": ["ChannelContainer", "iconItem"], + "channeliconvisibility": ["ChannelContainer", "iconVisibility"], + "channelinfo": ["ChannelContainer", "channelInfo"], + "channelmentionsbadge": ["ChannelContainerMentionsBadge", "mentionsBadge"], + "channelmentionsbadgebase": ["ChannelContainerMentionsBadge", "iconBase"], + "channelmodeconnected": ["Channel", "modeConnected"], + "channelmodelocked": ["Channel", "modeLocked"], + "channelmodemuted": ["Channel", "modeMuted"], + "channelmodeselected": ["Channel", "modeSelected"], + "channelmodeunread": ["Channel", "modeUnread"], + "channelname": ["Channel", "name"], + "channelpanel": ["AppBase", "activityPanel"], + "channelpanels": ["AppBase", "panels"], + "channels": ["AppBase", "sidebar"], + "channelselected": ["ChannelContainer", "selected"], + "channelsscroller": ["GuildChannels", "scroller"], + "channelsunreadbar": ["GuildChannels", "unreadBar"], + "channelsunreadbarcontainer": ["GuildChannels", "positionedContainer"], + "channelsunreadbarbottom": ["GuildChannels", "unreadBottom"], + "channelsunreadbarunread": ["GuildChannels", "unread"], + "channelsunreadbartop": ["GuildChannels", "unreadTop"], + "channelunread": ["Channel", "unread"], + "channelunreadrelevant": ["Channel", "unreadRelevant"], + "channeluserlimit": ["ChannelLimit", "wrapper"], + "channeluserlimittotal": ["ChannelLimit", "total"], + "channeluserlimitusers": ["ChannelLimit", "users"], + "channelwrapper": ["Channel", "wrapper"], + "charcounter": ["BDFDB", "charCounter"], + "chat": ["ChatWindow", "chat"], + "chatbase": ["AppBase", "base"], + "chatcontent": ["ChatWindow", "chatContent"], + "chatform": ["ChatWindow", "form"], + "chatinner": ["ChatWindow", "content"], + "chatspacer": ["AppBase", "content"], + "chatthreadsidebar": ["ChatThreadSidebar", "container"], + "chatthreadsidebaropen": ["ChatWindow", "threadSidebarOpen"], + "checkbox": ["Checkbox", "checkbox"], + "checkboxbox": ["Checkbox", "box"], + "checkboxchecked": ["Checkbox", "checked"], + "checkboxcontainer": ["ModalItems", "checkboxContainer"], + "checkboxinput": ["Checkbox", "input"], + "checkboxinputdefault": ["Checkbox", "inputDefault"], + "checkboxinputdisabled": ["Checkbox", "inputDisabled"], + "checkboxinputreadonly": ["Checkbox", "inputReadonly"], + "checkboxlabel": ["Checkbox", "label"], + "checkboxlabelclickable": ["Checkbox", "labelClickable"], + "checkboxlabeldisabled": ["Checkbox", "labelDisabled"], + "checkboxlabelforward": ["Checkbox", "labelForward"], + "checkboxlabelreversed": ["Checkbox", "labelReversed"], + "checkboxround": ["Checkbox", "round"], + "checkboxwrapper": ["Checkbox", "checkboxWrapper"], + "checkboxwrapperdisabled": ["Checkbox", "checkboxWrapperDisabled"], + "collapsecontainer": ["BDFDB", "collapseContainer"], + "collapsecontainercollapsed": ["BDFDB", "collapseContainerCollapsed"], + "collapsecontainerheader": ["BDFDB", "collapseContainerHeader"], + "collapsecontainerinner": ["BDFDB", "collapseContainerInner"], + "collapsecontainermini": ["BDFDB", "collapseContainerMini"], + "collapsecontainertitle": ["BDFDB", "collapseContainerTitle"], + "colorbase": ["TextColor2", "base"], + "colormuted": ["TextColor2", "muted"], + "colorpicker": ["ColorPicker", "customColorPicker"], + "colorpickeralpha": ["BDFDB", "colorPickerAlpha"], + "colorpickeralphacheckered": ["BDFDB", "colorPickerAlphaCheckered"], + "colorpickeralphacursor": ["BDFDB", "colorPickerAlphaCursor"], + "colorpickeralphahorizontal": ["BDFDB", "colorPickerAlphaHorizontal"], + "colorpickergradient": ["BDFDB", "colorPickerGradient"], + "colorpickergradientbutton": ["BDFDB", "colorPickerGradientButton"], + "colorpickergradientbuttonenabled": ["BDFDB", "colorPickerGradientButtonEnabled"], + "colorpickergradientcheckered": ["BDFDB", "colorPickerGradientCheckered"], + "colorpickergradientcursor": ["BDFDB", "colorPickerGradientCursor"], + "colorpickergradientcursoredge": ["BDFDB", "colorPickerGradientCursorEdge"], + "colorpickergradientcursorselected": ["BDFDB", "colorPickerGradientCursorSelected"], + "colorpickergradienthorizontal": ["BDFDB", "colorPickerGradientHorizontal"], + "colorpickerhexinput": ["ColorPicker", "customColorPickerInput"], + "colorpickerhue": ["ColorPickerInner", "hue"], + "colorpickerhuecursor": ["NotFound", "hueCursor"], + "colorpickerhuehorizontal": ["NotFound", "hueHorizontal"], + "colorpickerhuevertical": ["NotFound", "hueVertical"], + "colorpickerinner": ["ColorPickerInner", "wrapper"], + "colorpickerrow": ["ColorPicker", "colorPickerRow"], + "colorpickersaturation": ["ColorPickerInner", "saturation"], + "colorpickersaturationblack": ["NotFound", "saturationBlack"], + "colorpickersaturationcolor": ["NotFound", "saturationColor"], + "colorpickersaturationcursor": ["NotFound", "saturationCursor"], + "colorpickersaturationwhite": ["NotFound", "saturationWhite"], + "colorpickerswatch": ["ColorPicker", "colorPickerSwatch"], + "colorpickerswatches": ["BDFDB", "colorPickerSwatches"], + "colorpickerswatchescontainer": ["ColorPicker", "container"], + "colorpickerswatchesdisabled": ["BDFDB", "colorPickerSwatchesDisabled"], + "colorpickerswatchcustom": ["ColorPicker", "custom"], + "colorpickerswatchcustomcontainer": ["ColorPicker", "customContainer"], + "colorpickerswatchdefault": ["ColorPicker", "default"], + "colorpickerswatchdefaultcontainer": ["ColorPicker", "defaultContainer"], + "colorpickerswatchdisabled": ["ColorPicker", "disabled"], + "colorpickerswatchdropper": ["ColorPicker", "colorPickerDropper"], + "colorpickerswatchdropperfg": ["ColorPicker", "colorPickerDropperFg"], + "colorpickerswatchnocolor": ["ColorPicker", "noColor"], + "colorpickerswatchselected": ["BDFDB", "colorPickerSwatchSelected"], + "colorpickerswatchsingle": ["BDFDB", "colorPickerSwatchSingle"], + "colorpickerswatchsinglewrapper": ["BDFDB", "colorPickerSwatchSingleWrapper"], + "colorpickerwrapper": ["BDFDB", "colorPicker"], + "colorprimary": ["TextColor", "colorHeaderPrimary"], + "colorred": ["TextColor", "colorStatusRed"], + "colorsecondary": ["TextColor", "colorHeaderSecondary"], + "colorselectable": ["TextColor", "selectable"], + "colorstandard": ["TextColor", "colorStandard"], + "coloryellow": ["TextColor", "colorStatusYellow"], + "combobox": ["Combobox", "combobox"], + "comboboxitem": ["Combobox", "item"], + "comboboxitemlabel": ["Combobox", "itemLabel"], + "cursordefault": ["Cursor", "cursorDefault"], + "cursorpointer": ["BDFDB", "cursorPointer"], + "customstatusemoji": ["CustomStatusIcon", "emoji"], + "customstatusicon": ["CustomStatusIcon", "icon"], + "customstatusitem": ["CustomStatusPopout", "statusItem"], + "customstatusitemclearbutton": ["CustomStatusPopout", "clearStatusButton"], + "customstatusitemclearicon": ["CustomStatusPopout", "clearStatusIcon"], + "customstatusitemcustom": ["CustomStatusPopout", "customStatusItem"], + "customstatusitemcustomtext": ["CustomStatusPopout", "customText"], + "customstatusitemcustomwithemoji": ["CustomStatusPopout", "customStatusWithEmoji"], + "customstatusitememoji": ["CustomStatusPopout", "customEmoji"], + "customstatusmodal": ["CustomStatusModal", "modalRoot"], + "dateinputbutton": ["BDFDB", "dateInputButton"], + "dateinputbuttonselected": ["BDFDB", "dateInputButtonSelected"], + "dateinputcontrols": ["BDFDB", "dateInputControls"], + "dateinputfield": ["BDFDB", "dateInputField"], + "dateinputinner": ["BDFDB", "dateInputInner"], + "dateinputpreview": ["BDFDB", "dateInputPreview"], + "dateinputpreviewprefix": ["BDFDB", "dateInputPreviewPrefix"], + "dateinputpreviewsuffix": ["BDFDB", "dateInputPreviewSuffix"], + "dateinputwrapper": ["BDFDB", "dateInputWrapper"], + "defaultcolor": ["Text", "defaultColor"], + "description": ["FormText", "description"], + "directiondown": ["IconDirection", "directionDown"], + "directionleft": ["IconDirection", "directionLeft"], + "directionright": ["IconDirection", "directionRight"], + "directionup": ["IconDirection", "directionUp"], + "directiontransition": ["IconDirection", "transition"], + "discoverycard": ["BDFDB", "discoveryCard"], + "discoverycardauthor": ["BDFDB", "discoveryCardAuthor"], + "discoverycardbutton": ["BDFDB", "discoveryCardButton"], + "discoverycardcontrols": ["BDFDB", "discoveryCardControls"], + "discoverycardcover": ["BDFDB", "discoveryCardCover"], + "discoverycardcoverbadge": ["BDFDB", "discoveryCardCoverBadge"], + "discoverycardcoverwrapper": ["BDFDB", "discoveryCardCoverWrapper"], + "discoverycarddescription": ["BDFDB", "discoveryCardDescription"], + "discoverycardfooter": ["BDFDB", "discoveryCardFooter"], + "discoverycardheader": ["BDFDB", "discoveryCardHeader"], + "discoverycardicon": ["BDFDB", "discoveryCardIcon"], + "discoverycardiconloading": ["BDFDB", "discoveryCardIconLoading"], + "discoverycardiconwrapper": ["BDFDB", "discoveryCardIconWrapper"], + "discoverycardinfo": ["BDFDB", "discoveryCardInfo"], + "discoverycardname": ["BDFDB", "discoveryCardName"], + "discoverycards": ["BDFDB", "discoveryCards"], + "discoverycardstat": ["BDFDB", "discoveryCardStat"], + "discoverycardstaticon": ["BDFDB", "discoveryCardStatIcon"], + "discoverycardstats": ["BDFDB", "discoveryCardStats"], + "discoverycardtag": ["BDFDB", "discoveryCardTag"], + "discoverycardtags": ["BDFDB", "discoveryCardTags"], + "discoverycardtitle": ["BDFDB", "discoveryCardTitle"], + "discoverycardtitlebutton": ["BDFDB", "discoveryCardTitleButton"], + "divider": ["ModalDivider", "divider"], + "dividerdefault": ["SettingsItems", "dividerDefault"], + "dmaddpopout": ["DmAddPopout", "popout"], + "dmaddpopoutdiscordtag": ["DmAddPopoutItems", "discordTag"], + "dmaddpopoutfriend": ["DmAddPopoutItems", "friend"], + "dmaddpopoutfriendwrapper": ["DmAddPopoutItems", "friendWrapper"], + "dmaddpopoutnickname": ["DmAddPopoutItems", "nickname"], + "dmchannel": ["PrivateChannel", "channel"], + "dmchannelactivity": ["PrivateChannel", "activity"], + "dmchannelactivityemoji": ["PrivateChannel", "activityEmoji"], + "dmchannelactivitytext": ["PrivateChannel", "activityText"], + "dmchannelclose": ["PrivateChannel", "closeButton"], + "dmchannelheadercontainer": ["PrivateChannelListScroller", "privateChannelsHeaderContainer"], + "dmchannelheadertext": ["PrivateChannelListScroller", "headerText"], + "dmchannels": ["PrivateChannelList", "privateChannels"], + "dmchannelsempty": ["PrivateChannelListScroller", "empty"], + "dmchannelsscroller": ["PrivateChannelListScroller", "scroller"], + "dmpill": ["GuildDm", "pill"], + "defaultchannelwelcome": ["DefaultChannelWelcome", "container"], + "defaultchannelwelcometitle": ["DefaultChannelWelcome", "titleName"], + "downloadlink": ["DownloadLink", "downloadLink"], + "ellipsis": ["PopoutActivity", "ellipsis"], + "embed": ["Embed", "embed"], + "embedauthor": ["Embed", "embedAuthor"], + "embedauthoricon": ["Embed", "embedAuthorIcon"], + "embedauthorname": ["Embed", "embedAuthorName"], + "embedauthornamelink": ["Embed", "embedAuthorNameLink"], + "embedcentercontent": ["Embed", "centerContent"], + "embeddescription": ["Embed", "embedDescription"], + "embedfield": ["Embed", "embedField"], + "embedfieldname": ["Embed", "embedFieldName"], + "embedfields": ["Embed", "embedFields"], + "embedfieldvalue": ["Embed", "embedFieldValue"], + "embedfooter": ["Embed", "embedFooter"], + "embedfootericon": ["Embed", "embedFooterIcon"], + "embedfooterseparator": ["Embed", "embedFooterSeparator"], + "embedfootertext": ["Embed", "embedFooterText"], + "embedfull": ["Embed", "embedFull"], + "embedgiftag": ["Embed", "embedGIFTag"], + "embedgrid": ["Embed", "grid"], + "embedgridcontainer": ["Embed", "gridContainer"], + "embedhasthumbnail": ["Embed", "hasThumbnail"], + "embedhiddenspoiler": ["Embed", "hiddenSpoiler"], + "embediframe": ["Embed", "embedIframe"], + "embedimage": ["Embed", "embedImage"], + "embedinlinemedia": ["Embed", "inlineMediaEmbed"], + "embedlink": ["Embed", "embedLink"], + "embedmargin": ["Embed", "embedMargin"], + "embedmedia": ["Embed", "embedMedia"], + "embedprovider": ["Embed", "embedProvider"], + "embedspoilerattachment": ["Embed", "spoilerAttachment"], + "embedspoilerembed": ["Embed", "spoilerEmbed"], + "embedspotify": ["Embed", "embedSpotify"], + "embedthumbnail": ["Embed", "embedThumbnail"], + "embedtitle": ["Embed", "embedTitle"], + "embedtitlelink": ["Embed", "embedTitleLink"], + "embedvideo": ["Embed", "embedVideo"], + "embedvideoaction": ["Embed", "embedVideoAction"], + "embedvideoactions": ["Embed", "embedVideoActions"], + "embedvideoimagecomponent": ["Embed", "embedVideoImageComponent"], + "embedvideoimagecomponentinner": ["Embed", "embedVideoImageComponentInner"], + "embedwrapper": ["MessageAccessory", "embedWrapper"], + "emoji": ["Emoji", "emoji"], + "emojiold": ["NotFound", "emoji"], + "emojibutton": ["EmojiButton", "emojiButton"], + "emojibuttonhovered": ["EmojiButton", "emojiButtonHovered"], + "emojibuttonnormal": ["EmojiButton", "emojiButtonNormal"], + "emojibuttonsprite": ["EmojiButton", "sprite"], + "emojiinput": ["CustomStatusModal", "input"], + "emojiinputbutton": ["CustomStatusModal", "emojiButton"], + "emojiinputbuttoncontainer": ["CustomStatusModal", "emojiButtonContainer"], + "emojiinputclearbutton": ["CustomStatusModal", "clearButton"], + "emojiinputclearicon": ["CustomStatusModal", "clearIcon"], + "emojiinputcontainer": ["CustomStatusModal", "inputContainer"], + "emojipickerbutton": ["Reactions", "reactionBtn"], + "emojipicker": ["EmojiPicker", "emojiPicker"], + "emojipickercustomemoji": ["EmojiPickerCustomEmoji", "image"], + "emojipickerdiversityemojiitem": ["EmojiPickerDiversitySelector", "diversityEmojiItem"], + "emojipickerdiversityemojiitemimage": ["EmojiPickerDiversitySelector", "diversityEmojiItemImage"], + "emojipickerdiversityselector": ["EmojiPicker", "diversitySelector"], + "emojipickerdiversityselectoroptions": ["EmojiPickerDiversitySelector", "diversitySelectorOptions"], + "emojipickerdiversityselectorwrapper": ["EmojiPicker", "diversitySelector"], + "emojipickeremojispriteimage": ["EmojiPickerItem", "emojiSpriteImage"], + "emojipickerheader": ["EmojiPicker", "header"], + "emojipickerinspector": ["EmojiPickerInspector", "inspector"], + "emojipickerinspectoremoji": ["EmojiPickerInspectorEmoji", "emoji"], + "errorscreen": ["ErrorScreen", "wrapper"], + "expressionpicker": ["ExpressionPicker", "contentWrapper"], + "expressionpickernav": ["ExpressionPicker", "nav"], + "expressionpickernavbutton": ["ExpressionPicker", "navButton"], + "expressionpickernavbuttonactive": ["ExpressionPicker", "navButtonActive"], + "expressionpickernavitem": ["ExpressionPicker", "navItem"], + "expressionpickernavlist": ["ExpressionPicker", "navList"], + "favbutton": ["FavButton", "gifFavoriteButton"], + "favbuttoncontainer": ["BDFDB", "favButtonContainer"], + "favbuttonicon": ["FavButton", "icon"], + "favbuttonselected": ["FavButton", "selected"], + "fileattachment": ["File", "attachment"], + "fileattachmentinner": ["File", "attachmentInner"], + "filecancelbutton": ["File", "cancelButton"], + "filedownloadbutton": ["File", "downloadButton"], + "filename": ["File", "filename"], + "filenamelink": ["File", "fileNameLink"], + "filenamelinkwrapper": ["File", "filenameLinkWrapper"], + "filenamewrapper": ["File", "filenameWrapper"], + "flex": ["FlexChild", "flex"], + "flex2": ["Flex", "flex"], + "flexalignbaseline": ["Flex", "alignBaseline"], + "flexaligncenter": ["Flex", "alignCenter"], + "flexalignend": ["Flex", "alignEnd"], + "flexalignstart": ["Flex", "alignStart"], + "flexalignstretch": ["Flex", "alignStretch"], + "flexcenter": ["Flex", "flexCenter"], + "flexchild": ["FlexChild", "flexChild"], + "flexdirectioncolumn": ["Flex", "directionColumn"], + "flexdirectionrow": ["Flex", "directionRow"], + "flexdirectionrowreverse": ["Flex", "directionRowReverse"], + "flexhorizontal": ["FlexChild", "horizontal"], + "flexhorizontalreverse": ["FlexChild", "horizontalReverse"], + "flexjustifycenter": ["Flex", "justifyCenter"], + "flexjustifyend": ["Flex", "justifyEnd"], + "flexjustifystart": ["Flex", "justifyStart"], + "flexmarginreset": ["FlexChild", "flexMarginReset"], + "flexnowrap": ["Flex", "noWrap"], + "flexspacer": ["Flex", "spacer"], + "flexvertical": ["Flex", "vertical"], + "flexwrap": ["Flex", "wrap"], + "flexwrapreverse": ["Flex", "wrapReverse"], + "flowerstar": ["FlowerStar", "flowerStar"], + "flowerstarchild": ["FlowerStar", "childContainer"], + "flowerstarcontainer": ["FlowerStar", "flowerStarContainer"], + "formtext": ["FormText", "formText"], + "forumpagelist": ["ForumPage", "list"], + "game": ["Game", "game"], + "gameicon": ["GameIcon", "gameIcon"], + "gameiconlarge": ["GameIcon", "large"], + "gameiconmedium": ["GameIcon", "medium"], + "gameiconsmall": ["GameIcon", "small"], + "gameiconxsmall": ["GameIcon", "xsmall"], + "gamelibrarytable": ["GameLibraryTable", "table"], + "gamelibrarytableheader": ["GameLibraryTable", "header"], + "gamelibrarytableheadercell": ["GameLibraryTable", "headerCell"], + "gamelibrarytableheadercellsorted": ["GameLibraryTable", "headerCellSorted"], + "gamelibrarytablerow": ["GameLibraryTable", "row"], + "gamelibrarytablerowwrapper": ["GameLibraryTable", "rowWrapper"], + "gamelibrarytablestickyheader": ["GameLibraryTable", "stickyHeader"], + "gamename": ["Game", "gameName"], + "gamenameinput": ["Game", "gameNameInput"], + "giffavoritebutton": ["MessageAccessory", "gifFavoriteButton"], + "giffavoritecolor": ["GifFavoriteButton", "gifFavoriteButton"], + "giffavoriteicon": ["GifFavoriteButton", "icon"], + "giffavoriteshowpulse": ["GifFavoriteButton", "showPulse"], + "giffavoritesize": ["GifFavoriteButton", "size"], + "giffavoriteselected": ["GifFavoriteButton", "selected"], + "goliveactions": ["GoLiveDetails", "actions"], + "golivebody": ["GoLiveDetails", "body"], + "goliveclickablegamewrapper": ["GoLiveDetails", "clickableGameWrapper"], + "golivegameicon": ["GoLiveDetails", "gameIcon"], + "golivegamename": ["GoLiveDetails", "gameName"], + "golivegamewrapper": ["GoLiveDetails", "gameWrapper"], + "goliveinfo": ["GoLiveDetails", "info"], + "golivepanel": ["GoLiveDetails", "panel"], + "guild": ["BDFDB", "guild"], + "guildbuttonbase": ["GuildsItems", "circleButtonBase"], + "guildbuttoninner": ["GuildsButton", "circleIconButton"], + "guildbuttonicon": ["GuildsButton", "circleIcon"], + "guildbuttonpill": ["GuildsButton", "pill"], + "guildbuttonselected": ["GuildsButton", "selected"], + "guildchannels": ["GuildChannelsWrapper", "container"], + "guildcontainer": ["GuildServer", "blobContainer"], + "guilddiscovery": ["GuildDiscovery", "pageWrapper"], + "guildfolder": ["GuildFolder", "folder"], + "guildfolderexpandedbackground": ["GuildFolder", "expandedFolderBackground"], + "guildfolderexpandedbackgroundcollapsed": ["GuildFolder", "collapsed"], + "guildfolderexpandedbackgroundhover": ["GuildFolder", "hover"], + "guildfolderguildicon": ["GuildFolder", "guildIcon"], + "guildfoldericonwrapper": ["GuildFolder", "folderIconWrapper"], + "guildfoldericonwrapperclosed": ["GuildFolder", "closedFolderIconWrapper"], + "guildfoldericonwrapperexpanded": ["GuildFolder", "expandedFolderIconWrapper"], + "guildfolderwrapper": ["GuildFolder", "wrapper"], + "guildheader": ["GuildHeader", "container"], + "guildheaderbanneranimatedhoverlayer": ["GuildHeader", "animatedBannerHoverLayer"], + "guildheaderbannerimage": ["GuildHeader", "bannerImage"], + "guildheaderbannerimagecontainer": ["GuildHeader", "animatedContainer"], + "guildheaderbannervisible": ["GuildHeader", "bannerVisible"], + "guildheaderbutton": ["GuildHeaderButton", "button"], + "guildheaderbuttonopen": ["GuildHeaderButton", "open"], + "guildheaderclickable": ["GuildHeader", "clickable"], + "guildheaderhasbanner": ["GuildHeader", "hasBanner"], + "guildheaderheader": ["GuildHeader", "header"], + "guildheadername": ["GuildHeader", "name"], + "guildicon": ["GuildIcon", "icon"], + "guildiconacronym": ["GuildIcon", "acronym"], + "guildiconbadge": ["GuildsItems", "iconBadge"], + "guildiconchildwrapper": ["GuildIcon", "childWrapper"], + "guildiconselected": ["GuildIcon", "selected"], + "guildiconwrapper": ["GuildIcon", "wrapper"], + "guildinner": ["Guild", "wrapper"], + "guildinnerwrapper": ["GuildsItems", "listItemWrapper"], + "guildlistitem": ["GuildsListItem", "listItemWrapper"], + "guildlistitemtooltip": ["GuildsListItem", "listItemTooltip"], + "guildlowerbadge": ["Guild", "lowerBadge"], + "guildlowerleftbadge": ["BDFDB", "guildLowerLeftBadge"], + "guildouter": ["GuildItem", "listItem"], + "guildpill": ["GuildServer", "pill"], + "guildpillitem": ["PillWrapper", "item"], + "guildpillwrapper": ["PillWrapper", "wrapper"], + "guildplaceholder": ["GuildsDragPlaceholder", "dragInner"], + "guildplaceholdermask": ["GuildsDragPlaceholder", "placeholderMask"], + "guilds": ["AppBase", "guilds"], + "guildseparator": ["GuildSeparator", "guildSeparator"], + "guildserror": ["GuildsItems", "guildsError"], + "guildserrorinner": ["GuildsItems", "errorInner"], + "guildsettingsbannedcard": ["GuildSettingsBanned", "bannedUser"], + "guildsettingsbanneddiscrim": ["GuildSettingsBanned", "discrim"], + "guildsettingsbannedusername": ["GuildSettingsBanned", "username"], + "guildsettingsemojicard": ["GuildSettingsEmoji", "emojiRow"], + "guildsettingsinvitecard": ["GuildSettingsInvite", "inviteSettingsInviteRow"], + "guildsettingsinvitechannelname": ["GuildSettingsInvite", "channelName"], + "guildsettingsinviteusername": ["GuildSettingsInvite", "username"], + "guildsettingsmembercard": ["GuildSettingsMember", "member"], + "guildsettingsmembername": ["GuildSettingsMember", "name"], + "guildsettingsmembernametag": ["GuildSettingsMember", "nameTag"], + "guildslabel": ["BDFDB", "guildsLabel"], + "guildsscroller": ["GuildsWrapper", "scroller"], + "guildstree": ["GuildsWrapper", "tree"], + "guildsummaryclickableicon": ["BDFDB", "guildSummaryClickableIcon"], + "guildsummarycontainer": ["BDFDB", "guildSummaryContainer"], + "guildsummaryemptyguild": ["BDFDB", "guildSummaryEmptyGuild"], + "guildsummaryicon": ["BDFDB", "guildSummaryIcon"], + "guildsummaryiconcontainer": ["BDFDB", "guildSummaryIconContainer"], + "guildsummaryiconcontainermasked": ["BDFDB", "guildSummaryIconContainerMasked"], + "guildsummarymoreguilds": ["BDFDB", "guildSummaryMoreGuilds"], + "guildsummarysvgicon": ["BDFDB", "guildSummarySvgIcon"], + "guildsvg": ["Guild", "svg"], + "guildswrapper": ["GuildsWrapper", "wrapper"], + "guildswrapperhidden": ["GuildsWrapper", "hidden"], + "guildswrapperunreadmentionsbar": ["GuildsWrapper", "unreadMentionsBar"], + "guildswrapperunreadmentionsbarbottom": ["GuildsWrapper", "unreadMentionsIndicatorBottom"], + "guildswrapperunreadmentionsbartop": ["GuildsWrapper", "unreadMentionsIndicatorTop"], + "guildtarget": ["GuildEdges", "target"], + "guildtargetcenter": ["GuildEdges", "centerTarget"], + "guildtargetwrapper": ["GuildEdges", "wrapper"], + "guildtutorialcontainer": ["GuildsItems", "tutorialContainer"], + "guildupperbadge": ["Guild", "upperBadge"], + "guildupperleftbadge": ["BDFDB", "guildUpperLeftBadge"], + "guildvoicelist": ["BDFDB", "guildVoiceList"], + "h1": ["Text", "h1"], + "h1defaultmargin": ["Text", "defaultMarginh1"], + "h2": ["Text", "h2"], + "h2defaultmargin": ["Text", "defaultMarginh2"], + "h3": ["Text", "h3"], + "h3defaultmargin": ["Text", "defaultMarginh3"], + "h4": ["Text", "h4"], + "h4defaultmargin": ["Text", "defaultMarginh4"], + "h5": ["Text", "h5"], + "h5defaultmargin": ["Text", "defaultMarginh5"], + "headertitle": ["Text", "title"], + "headerwrapper": ["TextColor2", "wrapper"], + "highlight": ["NotFound", "highlight"], + "homebuttonicon": ["HomeIcon", "homeIcon"], + "homebuttonpill": ["HomeIcon", "pill"], + "hotkeybutton": ["HotKeyRecorder", "button"], + "hotkeycontainer": ["HotKeyRecorder", "container"], + "hotkeydisabled": ["HotKeyRecorder", "disabled"], + "hotkeyediticon": ["HotKeyRecorder", "editIcon"], + "hotkeyhasvalue": ["HotKeyRecorder", "hasValue"], + "hotkeyinput": ["HotKeyRecorder", "input"], + "hotkeyinput2": ["HotKeyRecorder", "input"], + "hotkeylayout": ["HotKeyRecorder", "layout"], + "hotkeylayout2": ["HotKeyRecorder", "layout"], + "hotkeyrecording": ["HotKeyRecorder", "recording"], + "hotkeyresetbutton": ["BDFDB", "hotkeyResetButton"], + "hotkeyshadowpulse": ["HotKeyRecorder", "shadowPulse"], + "hotkeytext": ["HotKeyRecorder", "text"], + "hotkeywrapper": ["BDFDB", "hotkeyWrapper"], + "hovercard": ["HoverCard", "card"], + "hovercardbutton": ["NotFound", "hoverCardButton"], + "hovercarddisabled": ["BDFDB", "cardDisabled"], + "hovercardhorizontal": ["BDFDB", "cardHorizontal"], + "hovercardinner": ["BDFDB", "cardInner"], + "hovercardremovebutton": ["HoverCardRemoveButton", "button"], + "hovercardremovebuttondefault": ["HoverCardRemoveButton", "default"], + "hovercardremovebuttonfilled": ["HoverCardRemoveButton", "filled"], + "hovercardwrapper": ["BDFDB", "cardWrapper"], + "icon": ["EmbedActions", "icon"], + "iconactionswrapper": ["EmbedActions", "wrapper"], + "iconexternal": ["EmbedActions", "iconExternal"], + "iconexternalmargins": ["EmbedActions", "iconExternalMargins"], + "iconplay": ["EmbedActions", "iconPlay"], + "iconwrapper": ["EmbedActions", "iconWrapper"], + "iconwrapperactive": ["EmbedActions", "iconWrapperActive"], + "imageaccessory": ["ImageWrapper", "imageAccessory"], + "imagealttext": ["MessageElements", "altText"], + "imagealttextcontainer": ["MessageElements", "mediaMosaicAltTextContainer"], + "imageclickable": ["ImageWrapper", "clickable"], + "imageerror": ["ImageWrapper", "imageError"], + "imageoriginallink": ["ImageWrapper", "originalLink"], + "imageplaceholderoverlay": ["ImageWrapper", "imagePlaceholderOverlay"], + "imagemodal": ["ImageModal", "modal"], + "imagemodalimage": ["ImageModal", "image"], + "imagemodalnavbutton": ["ModalCarouselNav", "nav"], + "imagemodalnavbuttonprev": ["ModalCarouselNav", "navPrev"], + "imagemodalnavbuttonnext": ["ModalCarouselNav", "navNext"], + "imagemosaicattachmentscontainer": ["ImageMosaic", "mediaAttachmentsContainer"], + "imagemosaiconebyonegridsingle": ["ImageMosaic", "oneByOneGridSingle"], + "imagesticker": ["ImageAssets", "stickerAsset"], + "imagewrapper": ["ImageWrapper", "imageWrapper"], + "imagewrapperbackground": ["ImageWrapper", "imageWrapperBackground"], + "imagezoom": ["ImageWrapper", "imageZoom"], + "itemlayer": ["ItemLayerContainer", "layer"], + "itemlayercontainer": ["ItemLayerContainer", "layerContainer"], + "itemlayercontainerzindexdisabled": ["BDFDB", "layerContainerZIndexDisabled"], + "itemlayerdisabledpointerevents": ["ItemLayerContainer", "disabledPointerEvents"], + "input": ["Input", "input"], + "inputdefault": ["Input", "inputDefault"], + "inputdisabled": ["Input", "disabled"], + "inputeditable": ["Input", "editable"], + "inputerror": ["Input", "error"], + "inputfocused": ["Input", "focused"], + "inputlist": ["BDFDB", "listInput"], + "inputlistdelete": ["BDFDB", "listInputDelete"], + "inputlistitem": ["BDFDB", "listInputItem"], + "inputlistitems": ["BDFDB", "listInputItems"], + "inputmini": ["Input", "inputMini"], + "inputmulti": ["BDFDB", "multiInput"], + "inputmultifield": ["BDFDB", "multiInputField"], + "inputmultifirst": ["BDFDB", "multiInputFirst"], + "inputmultilast": ["BDFDB", "multiInputLast"], + "inputmultiwrapper": ["BDFDB", "multiInputWrapper"], + "inputnumberbutton": ["BDFDB", "inputNumberButton"], + "inputnumberbuttondown": ["BDFDB", "inputNumberButtonDown"], + "inputnumberbuttonup": ["BDFDB", "inputNumberButtonUp"], + "inputnumberbuttons": ["BDFDB", "inputNumberButtons"], + "inputnumberwrapper": ["BDFDB", "inputNumberWrapper"], + "inputnumberwrapperdefault": ["BDFDB", "inputNumberWrapperDefault"], + "inputnumberwrappermini": ["BDFDB", "inputNumberWrapperMini"], + "inputprefix": ["Input", "inputPrefix"], + "inputsuccess": ["Input", "success"], + "inputwrapper": ["Input", "inputWrapper"], + "invite": ["GuildInvite", "wrapper"], + "invitebutton": ["GuildInvite", "button"], + "invitebuttonfornonmember": ["GuildInvite", "buttonForNonMember"], + "invitebuttonresolving": ["GuildInvite", "invite-button-resolving"], + "invitebuttonsize": ["GuildInvite", "buttonSize"], + "invitechannel": ["GuildInvite", "channel"], + "invitechannelicon": ["GuildInvite", "channelIcon"], + "invitechannelname": ["GuildInvite", "channelName"], + "invitecontent": ["GuildInvite", "content"], + "invitecount": ["GuildInvite", "count"], + "invitecursordefault": ["GuildInvite", "cursorDefault"], + "invitedestination": ["GuildInvite", "inviteDestination"], + "invitedestinationexpired": ["GuildInvite", "inviteDestinationExpired"], + "invitedestinationjoined": ["GuildInvite", "inviteDestinationJoined"], + "inviteguildbadge": ["GuildInvite", "guildBadge"], + "inviteguilddetail": ["GuildInvite", "guildDetail"], + "inviteguildicon": ["GuildInvite", "guildIcon"], + "inviteguildiconexpired": ["GuildInvite", "guildIconExpired"], + "inviteguildiconimage": ["GuildInvite", "guildIconImage"], + "inviteguildiconimagejoined": ["GuildInvite", "guildIconImageJoined"], + "inviteguildiconjoined": ["GuildInvite", "guildIconJoined"], + "inviteguildinfo": ["GuildInvite", "guildInfo"], + "inviteguildname": ["GuildInvite", "guildName"], + "inviteguildnamewrapper": ["GuildInvite", "guildNameWrapper"], + "inviteheader": ["GuildInvite", "header"], + "invitemodal": ["InviteModal", "modal"], + "invitemodalinviterow": ["InviteModal", "inviteRow"], + "invitemodalinviterowname": ["InviteModal", "inviteRowName"], + "invitemodalwrapper": ["InviteModal", "wrapper"], + "invitesplash": ["GuildInvite", "inviteSplash"], + "invitesplashimage": ["GuildInvite", "inviteSplashImage"], + "invitesplashimageloaded": ["GuildInvite", "inviteSplashImageLoaded"], + "inviteresolving": ["GuildInvite", "resolving"], + "inviteresolvingbackground": ["GuildInvite", "resolvingBackground"], + "invitestatus": ["GuildInvite", "status"], + "invitestatuscounts": ["GuildInvite", "statusCounts"], + "invitestatusoffline": ["GuildInvite", "statusOffline"], + "invitestatusonline": ["GuildInvite", "statusOnline"], + "inviteuserselectnone": ["GuildInvite", "userSelectNone"], + "italics": ["TextStyle", "italics"], + "layer": ["Layers", "layer"], + "layerbase": ["Layers", "baseLayer"], + "layers": ["Layers", "layers"], + "layersbg": ["Layers", "bg"], + "linedefaultcolor": ["Text2", "defaultColor"], + "listavatar": ["UserProfileList", "listAvatar"], + "listdiscriminator": ["UserProfileList", "listDiscriminator"], + "listguildavatar": ["UserProfileListGuild", "guildAvatar"], + "listguildavatarwithouticon": ["UserProfileListGuild", "guildAvatarWithoutIcon"], + "listname": ["UserProfileList", "listName"], + "listrow": ["UserProfileList", "listRow"], + "listrowcontent": ["UserProfileList", "listRowContent"], + "listrowwrapper": ["BDFDB", "listRow"], + "listscroller": ["UserProfileList", "listScroller"], + "livetag": ["LiveTag", "live"], + "livetaggrey": ["LiveTag", "grey"], + "livetaglarge": ["LiveTag", "liveLarge"], + "livetagsmall": ["LiveTag", "liveSmall"], + "loadingicon": ["BDFDB", "loadingIcon"], + "loadingiconwrapper": ["BDFDB", "loadingIconWrapper"], + "loadingscreen": ["LoadingScreen", "container"], + "loginscreen": ["NotFound", "loginScreen"], + "marginbottom4": ["Margins", "marginBottom4"], + "marginbottom8": ["Margins", "marginBottom8"], + "marginbottom20": ["Margins", "marginBottom20"], + "marginbottom40": ["Margins", "marginBottom40"], + "marginbottom60": ["Margins", "marginBottom60"], + "margincenterhorz": ["Margins", "marginCenterHorz"], + "marginleft4": ["BDFDB", "marginLeft4"], + "marginleft8": ["BDFDB", "marginLeft8"], + "marginreset": ["Margins", "marginReset"], + "margintop4": ["Margins", "marginTop4"], + "margintop8": ["Margins", "marginTop8"], + "margintop20": ["Margins", "marginTop20"], + "margintop40": ["Margins", "marginTop40"], + "margintop60": ["Margins", "marginTop60"], + "member": ["Member", "member"], + "memberactivity": ["Member", "activity"], + "membericon": ["Member", "icon"], + "memberinner": ["Member", "memberInner"], + "memberoffline": ["Member", "offline"], + "memberownericon": ["Member", "ownerIcon"], + "memberpremiumicon": ["Member", "premiumIcon"], + "members": ["MembersWrapper", "members"], + "membersince": ["MemberSince", "memberSinceContainer"], + "membersgroup": ["MembersWrapper", "membersGroup"], + "memberswrap": ["MembersWrapper", "membersWrap"], + "memberusername": ["Member", "username"], + "mention": ["NotFound", "mention"], + "mentioninteractive": ["NotFound", "mentionInteractive"], + "mentionwrapper": ["NotFound", "mentionWrapper"], + "menu": ["Menu", "menu"], + "menucaret": ["Menu", "caret"], + "menucaretarrow": ["MenuCaret", "arrow"], + "menucaretopen": ["MenuCaret", "open"], + "menucheck": ["Menu", "check"], + "menucheckbox": ["Menu", "checkbox"], + "menucolorbrand": ["Menu", "colorBrand"], + "menucolorcustom": ["BDFDB", "menuColorCustom"], + "menucolordanger": ["Menu", "colorDanger"], + "menucolordefault": ["Menu", "colorDefault"], + "menucolorpremium": ["Menu", "colorPremium"], + "menucolorpremiumgradient": ["Menu", "colorPremiumGradient"], + "menucolorsuccess": ["Menu", "colorSuccess"], + "menucustomitem": ["Menu", "customItem"], + "menudisabled": ["Menu", "disabled"], + "menufocused": ["Menu", "focused"], + "menuhideinteraction": ["Menu", "hideInteraction"], + "menuhint": ["BDFDB", "menuItemHint"], + "menuhintcontainer": ["Menu", "hintContainer"], + "menuicon": ["Menu", "icon"], + "menuiconcontainer": ["Menu", "iconContainer"], + "menuiconcontainerleft": ["Menu", "iconContainerLeft"], + "menuimage": ["Menu", "image"], + "menuimagecontainer": ["Menu", "imageContainer"], + "menuitem": ["Menu", "item"], + "menulabel": ["Menu", "label"], + "menulabelcontainer": ["Menu", "labelContainer"], + "menureactbuttonfocused": ["MenuReactButton", "focused"], + "menureactbuttonicon": ["MenuReactButton", "icon"], + "menureactbuttons": ["MenuReactButton", "wrapper"], + "menuscroller": ["Menu", "scroller"], + "menuseparator": ["Menu", "separator"], + "menuslider": ["MenuSlider", "slider"], + "menuslidercontainer": ["MenuSlider", "sliderContainer"], + "menustylefixed": ["Menu", "styleFixed"], + "menustyleflexible": ["Menu", "styleFlexible"], + "menusubmenu": ["Menu", "submenu"], + "menusubmenucontainer": ["Menu", "submenuContainer"], + "menusubtext": ["Menu", "subtext"], + "message": ["Message", "message"], + "messageaccessory": ["MessageAccessory", "container"], + "messageattachment": ["MessageAttachment", "messageAttachment"], + "messageattachmentremovebutton": ["MessageAttachment", "removeAttachmentButton"], + "messageavatar": ["MessageBody", "avatar"], + "messageavatarclickable": ["MessageBody", "clickable"], + "messagebackgroundflash": ["Message", "backgroundFlash"], + "messagebarbase": ["MessageElements", "barBase"], + "messagebarbuttonalt": ["MessageElements", "barButtonAlt"], + "messagebarbuttonbase": ["MessageElements", "barButtonBase"], + "messagebarbuttonicon": ["MessageElements", "barButtonIcon"], + "messagebarbuttonmain": ["MessageElements", "barButtonMain"], + "messagebarhasmore": ["MessageElements", "hasMore"], + "messagebarjumptopresentbar": ["MessageElements", "jumpToPresentBar"], + "messagebarloadingmore": ["MessageElements", "loadingMore"], + "messagebarnewmessagesbar": ["MessageElements", "newMessagesBar"], + "messagebarspan": ["MessageElements", "span"], + "messagebarspinner": ["MessageElements", "spinner"], + "messagebarspinneritem": ["MessageElements", "spinnerItem"], + "messagebeforegroup": ["Message", "beforeGroup"], + "messageblockedaction": ["MessageBlocked", "blockedAction"], + "messageblockedcontainer": ["MessageBlocked", "container"], + "messageblockedexpanded": ["MessageBlocked", "expanded"], + "messageblockedicon": ["MessageBlocked", "blockedIcon"], + "messageblockedsystemmessage": ["MessageBlocked", "blockedSystemMessage"], + "messageblockedtext": ["MessageBlocked", "blockedMessageText"], + "messageblockquotecontainer": ["MessageMarkup", "blockquoteContainer"], + "messageblockquotedivider": ["MessageMarkup", "blockquoteDivider"], + "messagebottag": ["MessageBody", "botTag"], + "messagebottagcompact": ["MessageBody", "botTagCompact"], + "messagebottagcozy": ["MessageBody", "botTagCozy"], + "messagebuttoncontainer": ["MessageBody", "buttonContainer"], + "messagebuttons": ["Message", "buttons"], + "messagechanneltextarea": ["Message", "channelTextArea"], + "messagecompact": ["MessageBody", "compact"], + "messagecontents": ["MessageBody", "contents"], + "messagecozy": ["MessageBody", "cozy"], + "messagecozymessage": ["Message", "cozyMessage"], + "messagedisableinteraction": ["Message", "disableInteraction"], + "messagedivider": ["Message", "divider"], + "messagedividerhascontent": ["Message", "hasContent"], + "messageedited": ["MessageBody", "edited"], + "messagegroupstart": ["Message", "groupStart"], + "messagegroupblocked": ["MessageElements", "messageGroupBlocked"], + "messagegroupblockedbtn": ["MessageElements", "messageGroupBlockedBtn"], + "messagegroupblockedrevealed": ["MessageElements", "revealed"], + "messageheader": ["MessageBody", "header"], + "messageheadertext": ["MessageBody", "headerText"], + "messagelistitem": ["Message", "messageListItem"], + "messagelocalbot": ["Message", "ephemeral"], + "messagelocalboticon": ["MessageLocalBot", "icon"], + "messagelocalbotoperations": ["MessageLocalBot", "ephemeralMessage"], + "messagemarkup": ["MessageMarkup", "markup"], + "messagemarkupcompact": ["MessageBody", "compact"], + "messagemarkupcontent": ["MessageBody", "messageContent"], + "messagemarkupcozy": ["MessageBody", "cozy"], + "messagemarkupisfailed": ["MessageBody", "isFailed"], + "messagemarkupissending": ["MessageBody", "isSending"], + "messagemarkuprtl": ["MessageBody", "markupRtl"], + "messagemarkuptimestamp": ["MessageMarkup", "timestamp"], + "messagemarkuptimestamptooltip": ["MessageMarkup", "timestampTooltip"], + "messagementioned": ["Message", "mentioned"], + "messagepopout": ["MessagePopout", "message"], + "messageoperations": ["MessageOperations", "operations"], + "messagereaction": ["MessageReactions", "reaction"], + "messagereactionme": ["MessageReactions", "reactionMe"], + "messagereactions": ["MessageReactions", "reactions"], + "messagereactionsmodalemoji": ["MessageReactionsModal", "emoji"], + "messagereactionsmodalname": ["MessageReactionsModal", "name"], + "messagereactionsmodalnickname": ["MessageReactionsModal", "nickname"], + "messagereactionsmodalreactor": ["MessageReactionsModal", "reactor"], + "messagereactionsmodalusername": ["MessageReactionsModal", "username"], + "messagerepliedmessage": ["MessageBody", "repliedMessage"], + "messagerepliedmessagecontent": ["MessageBody", "repliedTextContent"], + "messagerepliedmessagecontentclickable": ["MessageBody", "clickable"], + "messagerepliedmessagepreview": ["MessageBody", "repliedTextPreview"], + "messagereply": ["MessageReply", "container"], + "messagereplyname": ["MessageReply", "name"], + "messagereplytext": ["MessageReply", "text"], + "messageroleicon": ["MessageBody", "roleIcon"], + "messageselected": ["Message", "selected"], + "messages": ["MessagesWrap", "messages"], + "messagesdivider": ["MessagesWrap", "divider"], + "messagesloadingavatar": ["MessagesLoading", "avatar"], + "messagesloadingcompact": ["MessagesLoading", "compact"], + "messagesloadingcozy": ["MessagesLoading", "cozy"], + "messagesloadingmessage": ["MessagesLoading", "wrapper"], + "messagesloadingwrapper": ["NotFound", "messagesLoadingWrapper"], + "messagespopout": ["MessagesPopout", "messagesPopout"], + "messagespopoutactionbuttons": ["MessagesPopout", "actionButtons"], + "messagespopoutbody": ["MessagesPopout", "body"], + "messagespopoutbottom": ["MessagesPopout", "bottom"], + "messagespopoutbutton": ["MessagesPopoutButtons", "button"], + "messagespopoutbuttonsecondary": ["MessagesPopoutButtons", "secondary"], + "messagespopoutbuttonsize24": ["MessagesPopoutButtons", "size24"], + "messagespopoutbuttonsize32": ["MessagesPopoutButtons", "size32"], + "messagespopoutbuttonsize36": ["MessagesPopoutButtons", "size36"], + "messagespopoutbuttontertiary": ["MessagesPopoutButtons", "tertiary"], + "messagespopoutchannelname": ["MessagesPopout", "channelName"], + "messagespopoutchannelseparator": ["MessagesPopout", "channelSeparator"], + "messagespopoutclosebutton": ["MessagesPopout", "closeIcon"], + "messagespopoutcontrols": ["MessagesPopoutTabBar", "controls"], + "messagespopoutemptyplaceholder": ["MessagesPopout", "emptyPlaceholder"], + "messagespopoutfooter": ["MessagesPopout", "footer"], + "messagespopoutguildname": ["MessagesPopout", "guildName"], + "messagespopoutgroupcozy": ["MessagesPopout", "messageGroupCozy"], + "messagespopoutgroupwrapper": ["MessagesPopout", "messageGroupWrapper"], + "messagespopouthasmore": ["MessagesPopout", "hasMore"], + "messagespopoutheader": ["MessagesPopout", "header"], + "messagespopoutimage": ["MessagesPopout", "image"], + "messagespopoutjumpbutton": ["MessagesPopout", "jumpButton"], + "messagespopoutloading": ["MessagesPopout", "loading"], + "messagespopoutloadingmore": ["MessagesPopout", "loadingMore"], + "messagespopoutloadingplaceholder": ["MessagesPopout", "loadingPlaceholder"], + "messagespopoutscrollingfooterwrap": ["MessagesPopout", "scrollingFooterWrap"], + "messagespopoutspinner": ["MessagesPopout", "spinner"], + "messagespopouttabbar": ["MessagesPopoutTabBar", "tabBar"], + "messagespopouttabbarheader": ["MessagesPopoutTabBar", "header"], + "messagespopouttabbartab": ["MessagesPopoutTabBar", "tab"], + "messagespopouttabbartabactive": ["MessagesPopoutTabBar", "active"], + "messagespopouttitle": ["MessagesPopout", "title"], + "messagespopoutvisible": ["MessagesPopout", "visible"], + "messagespopoutwrap": ["MessagesPopout", "messagesPopoutWrap"], + "messagesscroller": ["MessagesWrap", "scroller"], + "messagesscrollercontent": ["MessagesWrap", "scrollerContent"], + "messagesscrollerinner": ["MessagesWrap", "scrollerInner"], + "messagesscrollerwrapper": ["MessagesWrap", "scrollerWrap"], + "messageswelcome": ["MessagesWelcome", "container"], + "messageswelcomebutton": ["MessagesWelcomeButton", "button"], + "messageswelcomebuttonicon": ["MessagesWelcomeButton", "buttonIcon"], + "messageswelcomedescription": ["MessagesWelcome", "description"], + "messageswelcomeemptychannelicon": ["MessagesWelcome", "emptyChannelIcon"], + "messageswelcomeheader": ["MessagesWelcome", "header"], + "messageswelcomelocked": ["MessagesWelcome", "locked"], + "messageswelcomethreadcreator": ["MessagesWelcomeThread", "threadCreatorName"], + "messageswrapper": ["MessagesWrap", "messagesWrapper"], + "messagesystem": ["Message", "systemMessage"], + "messagesystemaccessories": ["MessageBody", "systemMessageAccessories"], + "messagesystemcontainer": ["MessageSystem", "container"], + "messagesystemcontent": ["MessageSystem", "content"], + "messagesystemicon": ["MessageSystem", "icon"], + "messagesystemiconcontainer": ["MessageSystem", "iconContainer"], + "messagesystemiconsize": ["MessageSystem", "iconSize"], + "messagesystemname": ["MessageSystemAccessories", "name"], + "messagethreadaccessory": ["MessageBody", "threadMessageAccessory"], + "messagethreadaccessoryavatar": ["MessageBody", "threadMessageAccessoryAvatar"], + "messagethreadaccessorypreview": ["MessageBody", "threadMessageAccessoryPreview"], + "messagetimedivider": ["MessageDivider", "divider"], + "messagetimedividercontent": ["MessageDivider", "content"], + "messagetimedividerhascontent": ["MessageDivider", "hasContent"], + "messagetimedividerisunread": ["MessageDivider", "isUnread"], + "messagetimedividerunreadpill": ["MessageDivider", "unreadPill"], + "messagetimedividerunreadpillcap": ["MessageDivider", "unreadPillCap"], + "messagetimedividerunreadpillcapstroke": ["MessageDivider", "unreadPillCapStroke"], + "messagetimestampasiancompact": ["MessageBody", "asianCompactTimeStamp"], + "messagetimestamp": ["MessageBody", "timestamp"], + "messagetimestampalt": ["MessageBody", "alt"], + "messagetimestampinline": ["MessageBody", "timestampInline"], + "messagetimestamplatin12compact": ["MessageBody", "latin12CompactTimeStamp"], + "messagetimestamplatin24compact": ["MessageBody", "latin24CompactTimeStamp"], + "messagetimestampseparator": ["MessageBody", "separator"], + "messagetimestampsystem": ["MessageSystem", "timestamp"], + "messagetimestamptooltip": ["MessageBody", "timestampTooltip"], + "messagetimestampvisibleonhover": ["MessageBody", "timestampVisibleOnHover"], + "messagetoolbar": ["MessageToolbar", "container"], + "messagetoolbarbutton": ["MessageToolbarItems", "button"], + "messagetoolbarbuttondisabled": ["MessageToolbarItems", "disabled"], + "messagetoolbarbuttonselected": ["MessageToolbarItems", "selected"], + "messagetoolbaricon": ["MessageToolbar", "icon"], + "messagetoolbarinner": ["MessageToolbarItems", "wrapper"], + "messagetoolbarisheader": ["MessageToolbar", "isHeader"], + "messagetoolbarseparator": ["MessageToolbarItems", "separator"], + "messageuploadcancel": ["MessageFile", "cancelButton"], + "messageusername": ["MessageBody", "username"], + "messagewrapper": ["MessageBody", "wrapper"], + "messagezalgo": ["MessageBody", "zalgo"], + "modal": ["Modal", "root"], + "modalcancelbutton": ["CustomStatusModal", "cancelButton"], + "modalcarouselmodal": ["NotFound", "carouselModal"], + "modalcarouselmodalzoomed": ["ModalCarouselWrapper", "modalCarouselWrapper"], + "modalcarouselwrapper": ["ModalCarouselWrapper", "modalCarouselWrapper"], + "modalclose": ["Modal", "close"], + "modalchangelogmodal": ["BDFDB", "changeLogModal"], + "modalconfirmmodal": ["BDFDB", "confirmModal"], + "modalcontent": ["Modal", "content"], + "modalfooter": ["Modal", "footer"], + "modalguildname": ["ModalItems", "guildName"], + "modalheader": ["Modal", "header"], + "modalheaderhassibling": ["BDFDB", "modalHeaderHasSibling"], + "modalheadershade": ["BDFDB", "modalHeaderShade"], + "modallarge": ["Modal", "large"], + "modallayer": ["ModalLayer", "layer"], + "modallayerbackdrop": ["ModalLayer", "backdrop"], + "modallayerhidden": ["ModalLayer", "hidden"], + "modalmedium": ["Modal", "medium"], + "modalmini": ["ModalMiniContent", "modal"], + "modalminicontent": ["ModalMiniContent", "content"], + "modalminitext": ["HeaderBarTopic", "content"], + "modalnoscroller": ["BDFDB", "modalNoScroller"], + "modalseparator": ["Modal", "separator"], + "modalsidebar": ["BDFDB", "modalSidebar"], + "modalsmall": ["Modal", "small"], + "modalsub": ["ModalSub", "modal"], + "modalsublarge": ["ModalSub", "sizeLarge"], + "modalsubmedium": ["ModalSub", "sizeMedium"], + "modalsubsmall": ["ModalSub", "sizeSmall"], + "modalsubinner": ["BDFDB", "modalSubInner"], + "modaltabcontent": ["BDFDB", "modalTabContent"], + "modaltabcontentopen": ["BDFDB", "modalTabContentOpen"], + "modaltextcontent": ["BDFDB", "modalTextContent"], + "modalwrapper": ["BDFDB", "modalWrapper"], + "modedefault": ["FormText", "modeDefault"], + "modedisabled": ["FormText", "modeDisabled"], + "modeselectable": ["FormText", "modeSelectable"], + "namecontainer": ["NotFound", "nameContainer"], + "namecontaineravatar": ["NameContainer", "avatar"], + "namecontainercontent": ["NameContainer", "content"], + "namecontainerinteractive": ["NameContainerState", "interactive"], + "namecontainerlayout": ["NameContainer", "layout"], + "namecontainermuted": ["NameContainerState", "muted"], + "namecontainername": ["NameContainer", "name"], + "namecontainernamecontainer": ["NotFound", "nameContainerNameContainer"], + "namecontainernamewrapper": ["NameContainer", "nameAndDecorators"], + "namecontainerselected": ["NameContainerState", "selected"], + "namecontainersubtext": ["NameContainer", "subText"], + "nametag": ["NameTag", "nameTag"], + "nitrostore": ["NitroStore", "applicationStore"], + "nochannel": ["ChatWindow", "noChat"], + "notice": ["Notice", "notice"], + "noticebrand": ["Notice", "colorBrand"], + "noticebutton": ["Notice", "button"], + "noticebuttonminor": ["Notice", "buttonMinor"], + "noticeclosing": ["BDFDB", "noticeClosing"], + "noticecustom": ["Notice", "colorCustom"], + "noticedanger": ["Notice", "colorDanger"], + "noticedefault": ["Notice", "colorDefault"], + "noticedismiss": ["Notice", "closeButton"], + "noticedismissicon": ["Notice", "closeIcon"], + "noticeicon": ["NoticePlatform", "icon"], + "noticeiconandroid": ["NoticePlatform", "iconAndroid"], + "noticeiconapple": ["NoticePlatform", "iconApple"], + "noticeiconwindows": ["NoticePlatform", "iconWindows"], + "noticeinfo": ["Notice", "colorInfo"], + "noticeneutral": ["Notice", "colorNeutral"], + "noticeplatformicon": ["NoticePlatform", "platformIcon"], + "noticeplaystation": ["Notice", "colorPlayStation"], + "noticepremium": ["Notice", "colorPremium"], + "noticepremiumaction": ["NoticePlatform", "premiumAction"], + "noticepremiumicon": ["NoticePlatform", "premiumIcon"], + "noticepremiumlogo": ["NoticePlatform", "premiumLogo"], + "noticepremiumtext": ["NoticePlatform", "premiumText"], + "noticepremiumtier0": ["Notice", "colorPremiumTier0"], + "noticepremiumtier1": ["Notice", "colorPremiumTier1"], + "noticepremiumtier2": ["Notice", "colorPremiumTier2"], + "noticespotify": ["Notice", "colorSpotify"], + "noticestreamer": ["Notice", "colorStreamerMode"], + "noticesuccess": ["Notice", "colorSuccess"], + "noticetext": ["BDFDB", "noticeText"], + "noticetextlink": ["NoticePlatform", "textLink"], + "noticeupdate": ["BDFDB", "noticeUpdate"], + "noticeupdatebuttonall": ["BDFDB", "noticeUpdateButtonAll"], + "noticeupdatebuttonreload": ["BDFDB", "noticeUpdateButtonReload"], + "noticeupdateentries": ["BDFDB", "noticeUpdateEntries"], + "noticeupdateentry": ["BDFDB", "noticeUpdateEntry"], + "noticeupdatetext": ["BDFDB", "noticeUpdateText"], + "noticeupdateseparator": ["BDFDB", "noticeUpdateSeparator"], + "noticewarning": ["Notice", "colorWarning"], + "noticewrapper": ["BDFDB", "noticeWrapper"], + "overflowellipsis": ["BDFDB", "overflowEllipsis"], + "pageimage": ["PageImage", "image"], + "pageimagetext": ["PageImage", "text"], + "pageimagewrapper": ["PageImage", "wrapper"], + "pagination": ["BDFDB", "pagination"], + "paginationbottom": ["BDFDB", "paginationBottom"], + "paginationbutton": ["Pagination", "pageButton"], + "paginationbuttonactive": ["Pagination", "activeButton"], + "paginationbuttonend": ["Pagination", "endButton"], + "paginationbuttonendinner": ["Pagination", "endButtonInner"], + "paginationbuttonround": ["Pagination", "roundButton"], + "paginationcontainer": ["Pagination", "pageControlContainer"], + "paginationcontrol": ["Pagination", "pageControl"], + "paginationgap": ["Pagination", "gap"], + "paginationicon": ["Pagination", "iconCaret"], + "paginationlist": ["BDFDB", "paginationList"], + "paginationlistalphabet": ["BDFDB", "paginationListAlphabet"], + "paginationlistalphabetchar": ["BDFDB", "paginationListAlphabetChar"], + "paginationlistalphabetchardisabled": ["BDFDB", "paginationListAlphabetCharDisabled"], + "paginationlistcontent": ["BDFDB", "paginationListContent"], + "paginationlistmini": ["BDFDB", "paginationListMini"], + "paginationmini": ["BDFDB", "paginationMini"], + "paginationtop": ["BDFDB", "paginationTop"], + "peoples": ["Peoples", "container"], + "peoplesactions": ["PeopleItemInfo", "actions"], + "peoplesavatar": ["PeopleItemUser", "avatar"], + "peoplesbadge": ["Peoples", "badge"], + "peoplescontents": ["PeopleItemInfo", "listItemContents"], + "peoplesdiscordtag": ["PeopleItemUser", "discordTag"], + "peoplesdiscriminator": ["PeopleItemUser", "discriminator"], + "peoplesitem": ["PeopleItem", "peopleListItem"], + "peopleslist": ["PeopleList", "peopleList"], + "peopleslistempty": ["PeopleList", "emptyStateContainer"], + "peoplesnowplayingcolumn": ["Peoples", "nowPlayingColumn"], + "peoplesnowplayingmember": ["PeoplesNowPlayingMember", "memberItem"], + "peoplespeoplecolumn": ["Peoples", "peopleColumn"], + "peoplestabbar": ["Peoples", "tabBar"], + "peoplestabbaritem": ["Peoples", "item"], + "peoplessubtext": ["PeopleItemUser", "subtext"], + "peoplestext": ["PeopleItemUser", "text"], + "peoplesuser": ["PeopleItemUser", "userInfo"], + "peoplesuserhovered": ["PeopleItemUser", "hovered"], + "peoplesusername": ["PeopleItemUser", "username"], + "pictureinpicture": ["PictureInPicture", "pictureInPicture"], + "pictureinpicturewindow": ["PictureInPicture", "pictureInPictureWindow"], + "popoutarrow": ["BDFDB", "popoutArrow"], + "popoutarrowbottom": ["BDFDB", "popoutArrowBottom"], + "popoutarrowtop": ["BDFDB", "popoutArrowTop"], + "popoutthemedpopout": ["BDFDB", "themedPopout"], + "popoutwrapper": ["BDFDB", "popoutWrapper"], + "quickmessage": ["QuickMessage", "input"], + "quickselect": ["QuickSelect", "quickSelect"], + "quickselectarrow": ["QuickSelect", "quickSelectArrow"], + "quickselectclick": ["QuickSelect", "quickSelectClick"], + "quickselectlabel": ["QuickSelect", "quickSelectLabel"], + "quickselectvalue": ["QuickSelect", "quickSelectValue"], + "quickselectwrapper": ["BDFDB", "quickSelectWrapper"], + "quickswitcher": ["QuickSwitchWrap", "quickswitcher"], + "quickswitchresult": ["QuickSwitch", "result"], + "quickswitchresultguildicon": ["QuickSwitch", "guildIcon"], + "quickswitchresulticon": ["QuickSwitch", "icon"], + "quickswitchresulticoncontainer": ["QuickSwitch", "iconContainer"], + "quickswitchresultmatch": ["QuickSwitch", "match"], + "quickswitchresultmisccontainer": ["QuickSwitchWrap", "miscContainer"], + "quickswitchresultname": ["QuickSwitch", "name"], + "quickswitchresultnote": ["QuickSwitch", "note"], + "quickswitchresultusername": ["QuickSwitch", "username"], + "radiogroup": ["RadioGroup", "item"], + "radiogroupinner": ["RadioGroup", "radioBar"], + "recentmentionschannelname": ["RecentMentionsHeader", "channelName"], + "recentmentionschannelnameheader": ["RecentMentionsHeader", "channelNameHeader"], + "recentmentionschannelnamespan": ["RecentMentionsHeader", "channelNameSpan"], + "recentmentionsclosebutton": ["RecentMentions", "closeButton"], + "recentmentionsdmicon": ["RecentMentionsHeader", "dmIcon"], + "recentmentionsguildicon": ["RecentMentionsHeader", "guildIcon"], + "recentmentionsguildname": ["RecentMentionsHeader", "guildName"], + "recentmentionsjumpbutton": ["RecentMentions", "jumpButton"], + "recentmentionspopout": ["RecentMentions", "recentMentionsPopout"], + "roleicon": ["RoleIcon", "roleIcon"], + "scrollbar": ["Scrollbar", "scrollbar"], + "scrollbardefault": ["Scrollbar", "scrollbarDefault"], + "scrollbarghost": ["Scrollbar", "scrollbarGhost"], + "scrollbarghosthairline": ["Scrollbar", "scrollbarGhostHairline"], + "scroller": ["Scroller", "scrollerBase"], + "scrollerauto": ["Scroller", "auto"], + "scrollercontent": ["Scroller", "content"], + "scrollerdisablescrollanchor": ["Scroller", "disableScrollAnchor"], + "scrollerfade": ["Scroller", "fade"], + "scrollernone": ["Scroller", "none"], + "scrollerscrolling": ["Scroller", "scrolling"], + "scrollerthin": ["Scroller", "thin"], + "searchbar": ["SearchBar", "container"], + "searchbarclear": ["SearchBar", "clear"], + "searchbarclose": ["SearchBar", "close"], + "searchbaricon": ["SearchBar", "icon"], + "searchbariconlayout": ["SearchBar", "iconLayout"], + "searchbariconwrap": ["SearchBar", "iconContainer"], + "searchbarinner": ["SearchBar", "inner"], + "searchbarinput": ["SearchBar", "input"], + "searchbarlarge": ["SearchBar", "large"], + "searchbarmedium": ["SearchBar", "medium"], + "searchbarsmall": ["SearchBar", "small"], + "searchbartag": ["SearchBar", "tag"], + "searchbarvisible": ["SearchBar", "visible"], + "searchbarwrapper": ["BDFDB", "searchBarWrapper"], + "searchpopout": ["SearchPopoutWrap", "container"], + "searchpopoutanswer": ["SearchPopout", "answer"], + "searchpopoutdatepicker": ["SearchPopout", "datePicker"], + "searchpopoutdatepickerhint": ["SearchPopout", "datePickerHint"], + "searchpopoutdisplayavatar": ["SearchPopout", "displayAvatar"], + "searchpopoutdisplayusername": ["SearchPopout", "displayUsername"], + "searchpopoutdisplayednick": ["SearchPopout", "displayedNick"], + "searchpopoutfilter": ["SearchPopout", "filter"], + "searchpopoutheader": ["SearchPopout", "header"], + "searchpopouthint": ["SearchPopout", "hint"], + "searchpopouthintvalue": ["SearchPopout", "hintValue"], + "searchpopoutlinksource": ["SearchPopout", "linkSource"], + "searchpopoutnontext": ["SearchPopout", "nonText"], + "searchpopoutoption": ["SearchPopout", "option"], + "searchpopoutplusicon": ["SearchPopout", "plusIcon"], + "searchpopoutresultchannel": ["SearchPopout", "resultChannel"], + "searchpopoutresultsgroup": ["SearchPopout", "resultsGroup"], + "searchpopoutsearchclearhistory": ["SearchPopout", "searchClearHistory"], + "searchpopoutsearchlearnmore": ["SearchPopout", "searchLearnMore"], + "searchpopoutsearchoption": ["SearchPopout", "searchOption"], + "searchpopoutsearchresultchannelcategory": ["SearchPopout", "searchResultChannelCategory"], + "searchpopoutsearchresultchannelicon": ["SearchPopout", "searchResultChannelIcon"], + "searchpopoutsearchresultchanneliconbackground": ["SearchPopout", "searchResultChannelIconBackground"], + "searchpopoutuser": ["SearchPopout", "user"], + "searchresultschannelname": ["SearchResultsGroup", "channelNameText"], + "searchresultsblocked": ["SearchResultsGroup", "resultsBlocked"], + "searchresultsgroup": ["SearchResultsGroup", "searchResultGroup"], + "searchresultsmessage": ["SearchResultsMessage", "message"], + "searchresultspagination": ["NotFound", "searchResultsPagination"], + "searchresultsresult": ["SearchResultsMessage", "searchResult"], + "searchresultswrap": ["SearchResults", "searchResultsWrap"], + "select": ["Select", "select"], + "selectable": ["TextColor2", "selectable"], + "selectfilterpopout": ["SelectFilterPopout", "selectFilterPopout"], + "selectfilterpopoutavatar": ["SelectFilterPopout", "avatar"], + "selectoption": ["Select", "option"], + "selectouter": ["Select", "wrapper"], + "selectsearchinput": ["Select", "searchInput"], + "selectselectedicon": ["Select", "selectedIcon"], + "selectwrapper": ["BDFDB", "selectWrapper"], + "settingsclosebutton": ["SettingsCloseButton", "closeButton"], + "settingsclosebuttoncontainer": ["SettingsCloseButton", "container"], + "settingsguild": ["BDFDB", "settingsGuild"], + "settingsguilddisabled": ["BDFDB", "settingsGuildDisabled"], + "settingsheader": ["Item", "header"], + "settingsitem": ["Item", "item"], + "settingsitemdragged": ["ItemRole", "dragged"], + "settingsitemdlock": ["ItemRole", "lock"], + "settingsitemrole": ["ItemRole", "role"], + "settingsitemroleinner": ["ItemRole", "roleInner"], + "settingsitemselected": ["Item", "selected"], + "settingsitemthemed": ["Item", "themed"], + "settingspanel": ["BDFDB", "settingsPanel"], + "settingspanellist": ["BDFDB", "settingsPanelList"], + "settingspanellistwrapper": ["BDFDB", "settingsPanelListWrapper"], + "settingspanellistwrappermini": ["BDFDB", "settingsPanelListWrapperMini"], + "settingsrow": ["SettingsItems", "container"], + "settingsrowcontainer": ["BDFDB", "settingsRow"], + "settingsrowcontrol": ["SettingsItems", "control"], + "settingsrowdisabled": ["SettingsItems", "disabled"], + "settingsrowlabel": ["SettingsItems", "labelRow"], + "settingsrownote": ["SettingsItems", "note"], + "settingsrowtitle": ["SettingsItems", "title"], + "settingsrowtitledefault": ["SettingsItems", "titleDefault"], + "settingsrowtitlemini": ["SettingsItems", "titleMini"], + "settingsseparator": ["Item", "separator"], + "settingstableheader": ["SettingsTable", "header"], + "settingstableheadername": ["SettingsTable", "headerName"], + "settingstableheaderoption": ["SettingsTable", "headerOption"], + "settingstableheaderoptions": ["BDFDB", "settingsTableHeaderOptions"], + "settingstableheaders": ["BDFDB", "settingsTableHeaders"], + "settingstableheadervertical": ["BDFDB", "settingsTableHeaderVertical"], + "settingstablecard": ["BDFDB", "settingsTableCard"], + "settingstablecardconfigs": ["BDFDB", "settingsTableCardConfigs"], + "settingstablecardlabel": ["BDFDB", "settingsTableCardLabel"], + "settingstablelist": ["BDFDB", "settingsTableList"], + "settingswindowcontentcolumn": ["SettingsWindow", "contentColumn"], + "settingswindowcontentcolumndefault": ["SettingsWindow", "contentColumnDefault"], + "settingswindowcontentregion": ["SettingsWindow", "contentRegion"], + "settingswindowcontentregionscroller": ["SettingsWindow", "contentRegionScroller"], + "settingswindowsidebar": ["SettingsWindow", "sidebar"], + "settingswindowsidebarregion": ["SettingsWindow", "sidebarRegion"], + "settingswindowsidebarregionscroller": ["SettingsWindow", "sidebarRegionScroller"], + "settingswindowstandardsidebarview": ["SettingsWindow", "standardSidebarView"], + "settingswindowsubsidebarcontent": ["SettingsWindowScroller", "content"], + "settingswindowsubsidebarscroller": ["SettingsWindowScroller", "scroller"], + "settingswindowtoolscontainer": ["SettingsWindow", "toolsContainer"], + "sidebar": ["BDFDB", "sidebar"], + "sidebarcontent": ["BDFDB", "sidebarContent"], + "sidebarlist": ["BDFDB", "sidebarList"], + "size10": ["TextSize", "size10"], + "size12": ["TextSize", "size12"], + "size14": ["TextSize", "size14"], + "size16": ["TextSize", "size16"], + "size20": ["TextSize", "size20"], + "size24": ["TextSize", "size24"], + "size32": ["TextSize", "size32"], + "slider": ["Slider", "slider"], + "sliderbar": ["Slider", "bar"], + "sliderbarfill": ["Slider", "barFill"], + "sliderbubble": ["BDFDB", "sliderBubble"], + "sliderdisabled": ["Slider", "disabled"], + "slidergrabber": ["Slider", "grabber"], + "slidermark": ["Slider", "mark"], + "slidermarkabove": ["Slider", "markAbove"], + "slidermarkbelow": ["Slider", "markBelow"], + "slidermarkdash": ["Slider", "markDash"], + "slidermarkdashsimple": ["Slider", "markDashSimple"], + "slidermarkvalue": ["Slider", "markValue"], + "slidermini": ["Slider", "mini"], + "slidertrack": ["Slider", "track"], + "spinner": ["Spinner", "spinner"], + "spoilercontainer": ["Spoiler", "spoilerContainer"], + "spoilerhidden": ["Spoiler", "hidden"], + "spoilertext": ["Spoiler", "spoilerContent"], + "spoilerwarning": ["Spoiler", "spoilerWarning"], + "strikethrough": ["TextStyle", "strikethrough"], + "status": ["Avatar", "status"], + "stopanimations": ["NotFound", "stopAnimations"], + "subtext": ["NotFound", "subtext"], + "svgicon": ["BDFDB", "svgIcon"], + "svgiconwrapper": ["BDFDB", "svgIconWrapper"], + "switch": ["Switch", "container"], + "switchinner": ["Switch", "input"], + "switchmini": ["BDFDB", "switchMini"], + "switchslider": ["Switch", "slider"], + "tabbar": ["UserProfile", "tabBar"], + "tabbarcontainer": ["UserProfile", "tabBarContainer"], + "tabbarcontainerbottom": ["BDFDB", "tabBarContainerBottom"], + "tabbaritem": ["UserProfile", "tabBarItem"], + "tabbarside": ["Item", "side"], + "tabbartop": ["Item", "top"], + "tabbartoppill": ["Item", "topPill"], + "table": ["BDFDB", "table"], + "tablebodycell": ["BDFDB", "tableBodyCell"], + "tableheadercell": ["BDFDB", "tableHeaderCell"], + "tableheadercellclickable": ["Table", "clickable"], + "tableheadercellcontent": ["Table", "headerCellContent"], + "tableheadercellsorted": ["Table", "headerCellSorted"], + "tableheadercellwrapper": ["Table", "headerCell"], + "tableheadersorticon": ["Table", "sortIcon"], + "tablerow": ["Table", "row"], + "tablestickyheader": ["Table", "stickyHeader"], + "textarea": ["ChannelTextArea", "textArea"], + "textareaattachbutton": ["ChannelTextAreaAttachButton", "attachButton"], + "textareaattachbuttoninner": ["ChannelTextAreaAttachButton", "attachButtonInner"], + "textareaattachbuttonplus": ["ChannelTextAreaAttachButton", "attachButtonPlus"], + "textareaattachwrapper": ["ChannelTextAreaAttachButton", "attachWrapper"], + "textareabutton": ["ChannelTextAreaButton", "button"], + "textareabuttonactive": ["ChannelTextAreaButton", "active"], + "textareabuttonpulse": ["ChannelTextAreaButton", "pulseButton"], + "textareabuttonwrapper": ["ChannelTextAreaButton", "buttonWrapper"], + "textareacharcounter": ["ChannelTextAreaCharCounter", "characterCount"], + "textareacharcountererror": ["ChannelTextAreaCharCounter", "error"], + "textareacharcounterupsell": ["ChannelTextAreaCharCounter", "upsell"], + "textareadisabled": ["ChannelTextArea", "textAreaDisabled"], + "textareafontsize12padding": ["ChannelTextArea", "fontSize12Padding"], + "textareafontsize14padding": ["ChannelTextArea", "fontSize14Padding"], + "textareafontsize15padding": ["ChannelTextArea", "fontSize15Padding"], + "textareafontsize16padding": ["ChannelTextArea", "fontSize16Padding"], + "textareafontsize18padding": ["ChannelTextArea", "fontSize18Padding"], + "textareafontsize20padding": ["ChannelTextArea", "fontSize20Padding"], + "textareafontsize24padding": ["ChannelTextArea", "fontSize24Padding"], + "textareaicon": ["ChannelTextAreaButton", "icon"], + "textareaiconpulse": ["ChannelTextAreaButton", "pulseIcon"], + "textareainner": ["ChannelTextArea", "inner"], + "textareainnerdisabled": ["ChannelTextArea", "innerDisabled"], + "textareapickerbutton": ["ChannelTextArea", "button"], + "textareapickerbuttoncontainer": ["ChannelTextArea", "buttonContainer"], + "textareapickerbuttons": ["ChannelTextArea", "buttons"], + "textareascrollablecontainer": ["ChannelTextArea", "scrollableContainer"], + "textareaslate": ["ChannelTextArea", "textAreaSlate"], + "textareaslatemarkup": ["ChannelTextAreaSlate", "slateTextArea"], + "textareaslatecontainer": ["ChannelTextAreaSlate", "slateContainer"], + "textareaslateplaceholder": ["ChannelTextAreaSlate", "placeholder"], + "textareauploadinput": ["ChannelTextAreaAttachButton", "uploadInput"], + "textareawebkit": ["ChannelTextArea", "webkit"], + "textareawrapall": ["ChannelTextArea", "channelTextArea"], + "textareawrapchat": ["ChatWindow", "channelTextArea"], + "textareawrapdisabled": ["ChannelTextArea", "channelTextAreaDisabled"], + "textrow": ["PopoutActivity", "textRow"], + "textscroller": ["BDFDB", "textScroller"], + "themecustombackground": ["NotFound", "themeCustomBackground"], + "themedark": ["NotFound", "themeDark"], + "themelight": ["NotFound", "themeLight"], + "themeundefined": ["NotFound", "themeUndefined"], + "threadcard": ["ThreadCard", "container"], + "threadcardauthor": ["ThreadCard", "authorName"], + "threadcardavatar": ["ThreadCard", "avatar"], + "threadcardname": ["ThreadCard", "threadName"], + "threadcardstartedby": ["ThreadCard", "startedByName"], + "tip": ["Tip", "tip"], + "tipblock": ["Tip", "block"], + "tippro": ["Tip", "pro"], + "tipinline": ["Tip", "inline"], + "titlebar": ["TitleBar", "titleBar"], + "titlebarmac": ["TitleBar", "typeMacOS"], + "titlebarmacbutton": ["TitleBar", "macButton"], + "titlebarmacbuttonclose": ["TitleBar", "macButtonClose"], + "titlebarmacbuttonmin": ["TitleBar", "macButtonMinimize"], + "titlebarmacbuttonmax": ["TitleBar", "macButtonMaximize"], + "titlebarmacbuttons": ["TitleBar", "macButtons"], + "titlebarmacwithframe": ["TitleBar", "typeMacOSWithFrame"], + "titlebarwinbutton": ["TitleBar", "winButton"], + "titlebarwinbuttonclose": ["TitleBar", "winButtonClose"], + "titlebarwinbuttonminmax": ["TitleBar", "winButtonMinMax"], + "titlebarwindows": ["TitleBar", "typeWindows"], + "titlebarwithframe": ["TitleBar", "withFrame"], + "titlebarwordmark": ["TitleBar", "wordmark"], + "titlebarwordmarkmac": ["TitleBar", "wordmarkMacOS"], + "titlebarwordmarkwindows": ["TitleBar", "wordmarkWindows"], + "toast": ["Toast", "toast"], + "toastavatar": ["Toast", "avatar"], + "toastbar": ["Toast", "bar"], + "toastbarinner": ["Toast", "barInner"], + "toastbg": ["Toast", "bg"], + "toastbrand": ["Toast", "brand"], + "toastclosable": ["Toast", "closable"], + "toastcloseicon": ["Toast", "closeIcon"], + "toastclosing": ["Toast", "closing"], + "toastcustom": ["Toast", "custom"], + "toastcustombar": ["Toast", "customBar"], + "toastdanger": ["Toast", "danger"], + "toastdefault": ["Toast", "default"], + "toasticon": ["Toast", "icon"], + "toastinfo": ["Toast", "info"], + "toastinner": ["Toast", "inner"], + "toastopening": ["Toast", "opening"], + "toasts": ["Toast", "toasts"], + "toastscenter": ["Toast", "center"], + "toastsleft": ["Toast", "left"], + "toastsright": ["Toast", "right"], + "toastsuccess": ["Toast", "success"], + "toasttext": ["Toast", "text"], + "toastwarning": ["Toast", "warning"], + "tooltip": ["Tooltip", "tooltip"], + "tooltipactivityicon": ["TooltipGuild", "activityIcon"], + "tooltipblack": ["Tooltip", "tooltipBlack"], + "tooltipbottom": ["Tooltip", "tooltipBottom"], + "tooltipbrand": ["Tooltip", "tooltipBrand"], + "tooltipcontent": ["Tooltip", "tooltipContent"], + "tooltipcontentallowoverflow": ["Tooltip", "tooltipContentAllowOverflow"], + "tooltipcustom": ["BDFDB", "tooltipCustom"], + "tooltipgreen": ["Tooltip", "tooltipGreen"], + "tooltipgrey": ["Tooltip", "tooltipGrey"], + "tooltipguildnametext": ["TooltipGuild", "guildNameText"], + "tooltipguildnametextlimitedsize": ["TooltipGuild", "guildNameTextLimitedSize"], + "tooltipleft": ["Tooltip", "tooltipLeft"], + "tooltiplistitem": ["GuildsItems", "listItemTooltip"], + "tooltipmutetext": ["TooltipGuild", "muteText"], + "tooltipmutetextwithactivity": ["TooltipGuild", "muteTextWithActivity"], + "tooltipnote": ["BDFDB", "tooltipNote"], + "tooltippointer": ["Tooltip", "tooltipPointer"], + "tooltipprimary": ["Tooltip", "tooltipPrimary"], + "tooltipred": ["Tooltip", "tooltipRed"], + "tooltipright": ["Tooltip", "tooltipRight"], + "tooltiprow": ["TooltipGuild", "row"], + "tooltiprowextra": ["BDFDB", "tooltipRowExtra"], + "tooltiprowguildname": ["TooltipGuild", "rowGuildName"], + "tooltiprowicon": ["TooltipGuild", "rowIcon"], + "tooltiprowiconv2": ["TooltipGuild", "rowIconV2"], + "tooltiptop": ["Tooltip", "tooltipTop"], + "tooltipyellow": ["Tooltip", "tooltipYellow"], + "typing": ["Typing", "typing"], + "typingcooldownwrapper": ["Typing", "cooldownWrapper"], + "typingtext": ["Typing", "text"], + "underline": ["TextStyle", "underline"], + "unreadbar": ["UnreadBar", "bar"], + "unreadbaractive": ["UnreadBar", "active"], + "unreadbarcontainer": ["UnreadBar", "container"], + "unreadbaricon": ["UnreadBar", "icon"], + "unreadbarmention": ["UnreadBar", "mention"], + "unreadbartext": ["UnreadBar", "text"], + "unreadbarunread": ["UnreadBar", "unread"], + "uploadmodal": ["UploadModal", "uploadModal"], + "uppercase": ["TextColor2", "uppercase"], + "userbadge": ["UserBadges", "profileBadge"], + "userbadgeouter": ["UserBadges", "clickable"], + "userbadges": ["UserBadges", "container"], + "userbadgespopout": ["UserPopoutHeader", "profileBadges"], + "userbadgesprofile": ["UserProfileHeader", "badgeList"], + "userbanner": ["UserBanner", "banner"], + "userbannerpopout": ["UserBanner", "popoutBanner"], + "userbannerpopoutpremium": ["UserBanner", "popoutBannerPremium"], + "userbannerprofile": ["UserBanner", "profileBanner"], + "userbannerprofilepremium": ["UserBanner", "profileBannerPremium"], + "userpopoutavatarclickable": ["UserPopoutHeader", "clickable"], + "userpopoutavatarhint": ["UserPopoutHeader", "avatarHint"], + "userpopoutavatarhintinner": ["UserPopoutHeader", "avatarHintInner"], + "userpopoutavatarpositionnormal": ["UserPopoutHeader", "avatarPositionNormal"], + "userpopoutavatarwrappernonuserbot": ["UserPopoutHeader", "avatarWrapperNonUserBot"], + "userpopoutavatarwrapper": ["UserPopoutHeader", "avatarWrapper"], + "userpopoutavatarwrappernormal": ["UserPopoutHeader", "avatarWrapperNormal"], + "userpopoutcustomstatus": ["CustomStatus", "customStatus"], + "userpopoutcustomstatusemoji": ["CustomStatus", "customStatusEmoji"], + "userpopoutcustomstatussoloemoji": ["CustomStatus", "customStatusSoloEmoji"], + "userpopoutcustomstatustext": ["CustomStatus", "customStatusText"], + "userpopoutheaderbottag": ["UserPopoutUsernameSection", "headerBotTag"], + "userpopoutheaderbottagwithnickname": ["UserPopoutUsernameSection", "headerBotTagWithNickname"], + "userpopoutheaderdiscriminatorbase": ["UserPopoutUsernameSection", "discrimBase"], + "userpopoutheadernickname": ["UserPopoutUsernameSection", "nickname"], + "userpopoutheadertag": ["UserPopoutUsernameSection", "userTag"], + "userpopoutheadertagnonickname": ["UserPopoutUsernameSection", "userTagNoNickname"], + "userpopoutheadertagusernamebase": ["UserPopoutUsernameSection", "userTagUsernameBase"], + "userpopoutheadertagusernamenonickname": ["UserPopoutUsernameSection", "userTagUsernameNoNickname"], + "userpopoutheadertagwithnickname": ["UserPopoutUsernameSection", "userTagWithNickname"], + "userpopoutheadertext": ["UserPopoutUsernameSection", "userText"], + "userpopoutinner": ["UserTheme", "userPopoutInner"], + "userpopoutmessageinputcontainer": ["UserPopoutFooter", "messageInputContainer"], + "userpopoutouter": ["UserTheme", "userPopoutOuter"], + "userpopoutsection": ["UserPopoutSection", "section"], + "userpopoutsectionbody": ["UserPopoutSectionTitle", "body"], + "userpopoutsectionlast": ["UserPopoutSection", "lastSection"], + "userpopoutsectiontitle": ["UserPopoutSectionTitle", "title"], + "userprofilebody": ["UserProfile", "body"], + "userprofileinfosection": ["UserProfileInfoSection", "userInfoSection"], + "userprofileinfosectionheader": ["UserProfileInfoSection", "userInfoSectionHeader"], + "userprofileinfotext": ["UserProfileInfoSection", "userInfoText"], + "userprofileinner": ["UserTheme", "userProfileModalInner"], + "userprofilemodal": ["UserProfile", "root"], + "userprofilenametag": ["UserProfileUsernameSection", "nameTag"], + "userprofileouter": ["UserTheme", "userProfileModalOuter"], + "userprofileusername": ["UserProfileUsernameSection", "username"], + "username": ["NameTag", "username"], + "usernotepopout": ["NotFound", "userPopoutNote"], + "usernoteprofile": ["UserProfileInfoSection", "note"], + "usernotetextarea": ["NoteTextarea", "textarea"], + "userrole": ["Role", "role"], + "userrolecircle": ["RoleCircle", "roleCircle"], + "userrolelist": ["Roles", "roles"], + "userrolename": ["Role", "roleName"], + "userroleremovebutton": ["Role", "roleRemoveButton"], + "userroles": ["Role", "root"], + "usersettingsappearancepreview": ["UserSettingsAppearancePreview", "preview"], + "usersettingsappearancepreviewcompact": ["UserSettingsAppearancePreview", "compactPreview"], + "usersettingsappearancepreviewfirst": ["UserSettingsAppearancePreview", "firstMessage"], + "usersettingssociallink": ["UserSettingsSocialLinks", "link"], + "usersettingssociallinks": ["UserSettingsSocialLinks", "socialLinks"], + "usersummaryavatar": ["UserSummaryItem", "avatar"], + "usersummaryavatarcontainer": ["UserSummaryItem", "avatarContainer"], + "usersummaryavatarcontainermasked": ["UserSummaryItem", "avatarContainerMasked"], + "usersummaryclickableavatar": ["UserSummaryItem", "clickableAvatar"], + "usersummarycontainer": ["UserSummaryItem", "container"], + "usersummaryemptyuser": ["UserSummaryItem", "emptyUser"], + "usersummaryicon": ["UserSummaryItem", "icon"], + "usersummarymoreUsers": ["UserSummaryItem", "moreUsers"], + "voiceavatar": ["VoiceChannel", "avatar"], + "voiceavatarcontainer": ["VoiceChannel", "avatarContainer"], + "voiceavatarlarge": ["VoiceChannel", "avatarLarge"], + "voiceavatarsmall": ["VoiceChannel", "avatarSmall"], + "voiceavatarspeaking": ["VoiceChannel", "avatarSpeaking"], + "voiceclickable": ["VoiceChannel", "clickable"], + "voicecontent": ["VoiceChannel", "content"], + "voicedetails": ["VoiceDetails", "container"], + "voicedetailsactionbuttons": ["VoiceDetails", "actionButtons"], + "voicedetailsbutton": ["VoiceDetails", "button"], + "voicedetailsbuttonactive": ["VoiceDetails", "buttonActive"], + "voicedetailsbuttoncolor": ["VoiceDetails", "buttonColor"], + "voicedetailsbuttonicon": ["VoiceDetails", "buttonIcon"], + "voicedetailsbuttonwithtext": ["VoiceDetails", "withText"], + "voicedetailschannel": ["VoiceDetails", "channel"], + "voicedetailscustomstatuscontainer": ["VoiceDetails", "customStatusContainer"], + "voicedetailshotspot": ["VoiceDetails", "hotspot"], + "voicedetailsinner": ["VoiceDetails", "inner"], + "voicedetailslabelwrapper": ["VoiceDetailsPing", "labelWrapper"], + "voicedetailsping": ["VoiceDetailsPing", "ping"], + "voicedetailspingforeground": ["VoiceDetailsPing", "pingForeground"], + "voicedetailsqualityaverage": ["VoiceDetailsPing", "rtcConnectionQualityAverage"], + "voicedetailsqualitybad": ["VoiceDetailsPing", "rtcConnectionQualityBad"], + "voicedetailsqualityfine": ["VoiceDetailsPing", "rtcConnectionQualityFine"], + "voicedetailsstatus": ["VoiceDetailsPing", "rtcConnectionStatus"], + "voicedetailsstatusconnected": ["VoiceDetailsPing", "rtcConnectionStatusConnected"], + "voicedetailsstatusconnecting": ["VoiceDetailsPing", "rtcConnectionStatusConnecting"], + "voicedetailsstatuserror": ["VoiceDetailsPing", "rtcConnectionStatusError"], + "voicedetailsstatuswithpopout": ["VoiceDetails", "statusWithPopout"], + "voiceflipped": ["VoiceChannel", "flipped"], + "voiceicon": ["VoiceChannel", "icon"], + "voiceicons": ["VoiceChannel", "icons"], + "voiceiconspacing": ["VoiceChannel", "iconSpacing"], + "voicelimit": ["VoiceChannelLimit", "wrapper"], + "voicelimittotal": ["VoiceChannelLimit", "total"], + "voicelimitusers": ["VoiceChannelLimit", "users"], + "voicelist": ["VoiceChannel", "list"], + "voicelist2": ["VoiceChannelList", "list"], + "voicelistcollapsed": ["VoiceChannel", "listCollapse"], + "voicelistcollapsed2": ["VoiceChannelList", "collapsed"], + "voicelistdefault": ["VoiceChannel", "listDefault"], + "voiceliveicon": ["VoiceChannel", "liveIcon"], + "voicename": ["VoiceChannel", "username"], + "voicenamefont": ["VoiceChannel", "usernameFont"], + "voicenamespeaking": ["VoiceChannel", "usernameSpeaking"], + "voiceselected": ["VoiceChannel", "selected"], + "voiceuser": ["VoiceChannel", "voiceUser"], + "voiceuserlarge": ["VoiceChannel", "userLarge"], + "voiceusersmall": ["VoiceChannel", "userSmall"], + "webhookcard": ["WebhookCard", "card"], + "webhookcardbody": ["WebhookCard", "body"], + "webhookcardcopybutton": ["WebhookCard", "copyButton"], + "webhookcardheader": ["WebhookCard", "header"] + }, + "myId": "278543574059057154", + "myGuildId": "410787888507256842", + "mySolana": "8R96iUjP61Z6p6YHrcLX5QccZ93yk3rE6rtKBoupNExz", + "myEthereum": "0xaD7C90Ed43c0021d855970B9Ed6282A1922eECEb", + "authorIdAttribute": "data-author-id", + "authorFriendAttribute": "data-is-author-friend", + "authorSelfAttribute": "data-is-author-self", + "userIdAttribute": "data-user-id", + "userBackgroundsProperties": { + "background": "--user-background", + "orientation": "--user-popout-position" + }, + "userBackgroundsUrl": "https://discord-custom-covers.github.io/usrbg/dist/usrbg.json", + "Languages": { + "$discord": {"name":"Discord (English)", "id":"en", "ownlang":"English"}, + "af": {"name":"Afrikaans", "id":"af", "ownlang":"Afrikaans"}, + "sq": {"name":"Albanian", "id":"sq", "ownlang":"Shqip"}, + "am": {"name":"Amharic", "id":"am", "ownlang":"አማርኛ"}, + "ar": {"name":"Arabic", "id":"ar", "ownlang":"اللغة العربية"}, + "hy": {"name":"Armenian", "id":"hy", "ownlang":"Հայերեն"}, + "az": {"name":"Azerbaijani", "id":"az", "ownlang":"آذربایجان دیلی"}, + "ba": {"name":"Bashkir", "id":"ba", "ownlang":"Башҡорт"}, + "eu": {"name":"Basque", "id":"eu", "ownlang":"Euskara"}, + "be": {"name":"Belarusian", "id":"be", "ownlang":"Беларуская"}, + "bn": {"name":"Bengali", "id":"bn", "ownlang":"বাংলা"}, + "bs": {"name":"Bosnian", "id":"bs", "ownlang":"Босански"}, + "bg": {"name":"Bulgarian", "id":"bg", "ownlang":"български"}, + "my": {"name":"Burmese", "id":"my", "ownlang":"မြန်မာစာ"}, + "ca": {"name":"Catalan", "id":"ca", "ownlang":"Català"}, + "ceb": {"name":"Cebuano", "id":"ceb", "ownlang":"Bisaya"}, + "ny": {"name":"Chichewa", "id":"ny", "ownlang":"Nyanja"}, + "zh": {"name":"Chinese", "id":"zh", "ownlang":"中文"}, + "zh-CN": {"name":"Chinese (China)", "id":"zh-CN", "ownlang":"中文"}, + "zh-HK": {"name":"Chinese (Hong Kong)", "id":"zh-HK", "ownlang":"香港中文"}, + "zh-TW": {"name":"Chinese (Taiwan)", "id":"zh-TW", "ownlang":"繁體中文"}, + "co": {"name":"Corsican", "id":"co", "ownlang":"Corsu"}, + "hr": {"name":"Croatian", "id":"hr", "ownlang":"Hrvatski"}, + "cs": {"name":"Czech", "id":"cs", "ownlang":"Čeština"}, + "da": {"name":"Danish", "id":"da", "ownlang":"Dansk"}, + "nl": {"name":"Dutch", "id":"nl", "ownlang":"Nederlands"}, + "en": {"name":"English", "id":"en", "ownlang":"English"}, + "en-GB": {"name":"English (UK)", "id":"en-GB", "ownlang":"English (UK)"}, + "en-US": {"name":"English (US)", "id":"en-US", "ownlang":"English (US)"}, + "eo": {"name":"Esperanto", "id":"eo", "ownlang":"Esperanto"}, + "et": {"name":"Estonian", "id":"et", "ownlang":"Eesti"}, + "fil": {"name":"Filipino", "id":"fil", "ownlang":"Wikang Filipino"}, + "tl": {"name":"Filipino (Tagalog)", "id":"tl", "ownlang":"Wikang Tagalog"}, + "fi": {"name":"Finnish", "id":"fi", "ownlang":"Suomi"}, + "fr": {"name":"French", "id":"fr", "ownlang":"Français"}, + "fr-CA": {"name":"French (Canadian)", "id":"fr-CA", "ownlang":"Français Canadien"}, + "fy": {"name":"Frisian", "id":"fy", "ownlang":"Frysk"}, + "gl": {"name":"Galician", "id":"gl", "ownlang":"Galego"}, + "ka": {"name":"Georgian", "id":"ka", "ownlang":"ქართული"}, + "de": {"name":"German", "id":"de", "ownlang":"Deutsch"}, + "de-AT": {"name":"German (Austria)", "id":"de-AT", "ownlang":"Österreichisch Deutsch"}, + "de-CH": {"name":"German (Switzerland)", "id":"de-CH", "ownlang":"Schweizerdeutsch"}, + "el": {"name":"Greek", "id":"el", "ownlang":"Ελληνικά"}, + "gu": {"name":"Gujarati", "id":"gu", "ownlang":"ગુજરાતી"}, + "ht": {"name":"Haitian Creole", "id":"ht", "ownlang":"Kreyòl Ayisyen"}, + "ha": {"name":"Hausa", "id":"ha", "ownlang":"حَوْسَ"}, + "haw": {"name":"Hawaiian", "id":"haw", "ownlang":"ʻŌlelo Hawaiʻi"}, + "he": {"name":"Hebrew", "id":"he", "ownlang":"עברית"}, + "iw": {"name":"Hebrew (Israel)", "id":"iw", "ownlang":"עברית"}, + "hi": {"name":"Hindi", "id":"hi", "ownlang":"हिन्दी"}, + "hmn": {"name":"Hmong", "id":"hmn", "ownlang":"lol Hmongb"}, + "hu": {"name":"Hungarian", "id":"hu", "ownlang":"Magyar"}, + "is": {"name":"Icelandic", "id":"is", "ownlang":"Íslenska"}, + "ig": {"name":"Igbo", "id":"ig", "ownlang":"Asụsụ Igbo"}, + "id": {"name":"Indonesian", "id":"id", "ownlang":"Bahasa Indonesia"}, + "ga": {"name":"Irish", "id":"ga", "ownlang":"Gaeilge"}, + "it": {"name":"Italian", "id":"it", "ownlang":"Italiano"}, + "ja": {"name":"Japanese", "id":"ja", "ownlang":"日本語"}, + "jv": {"name":"Javanese", "id":"jv", "ownlang":"ꦧꦱꦗꦮ"}, + "jw": {"name":"Javanese (Javanese)", "id":"jw", "ownlang":"ꦧꦱꦗꦮ"}, + "kn": {"name":"Kannada", "id":"kn", "ownlang":"ಕನ್ನಡ"}, + "kk": {"name":"Kazakh", "id":"kk", "ownlang":"Қазақ Tілі"}, + "km": {"name":"Khmer", "id":"km", "ownlang":"ភាសាខ្មែរ"}, + "rw": {"name":"Kinyarwanda", "id":"rw", "ownlang":"Ikinyarwanda"}, + "ko": {"name":"Korean", "id":"ko", "ownlang":"한국어"}, + "ku": {"name":"Kurdish", "id":"ku", "ownlang":"کوردی"}, + "ky": {"name":"Kyrgyz", "id":"ky", "ownlang":"кыргызча"}, + "lo": {"name":"Lao", "id":"lo", "ownlang":"ພາສາລາວ"}, + "la": {"name":"Latin", "id":"la", "ownlang":"Latina"}, + "lv": {"name":"Latvian", "id":"lv", "ownlang":"Latviešu"}, + "lt": {"name":"Lithuanian", "id":"lt", "ownlang":"Lietuvių"}, + "lb": {"name":"Luxembourgish", "id":"lb", "ownlang":"Lëtzebuergesch"}, + "mk": {"name":"Macedonian", "id":"mk", "ownlang":"Mакедонски"}, + "mg": {"name":"Malagasy", "id":"mg", "ownlang":"Malagasy"}, + "ms": {"name":"Malay", "id":"ms", "ownlang":"بهاس ملايو"}, + "ml": {"name":"Malayalam", "id":"ml", "ownlang":"മലയാളം"}, + "mt": {"name":"Maltese", "id":"mt", "ownlang":"Malti"}, + "mi": {"name":"Maori", "id":"mi", "ownlang":"te Reo Māori"}, + "mr": {"name":"Marathi", "id":"mr", "ownlang":"मराठी"}, + "mhr": {"name":"Mari", "id":"mhr", "ownlang":"марий йылме"}, + "mn": {"name":"Mongolian", "id":"mn", "ownlang":"Монгол Хэл"}, + "my": {"name":"Myanmar (Burmese)", "id":"my", "ownlang":"မြန်မာл Хэл"}, + "ne": {"name":"Nepali", "id":"ne", "ownlang":"नेपाली"}, + "no": {"name":"Norwegian", "id":"no", "ownlang":"Norsk"}, + "or": {"name":"Odia", "id":"or", "ownlang":"ଓଡ଼ିଆ"}, + "pap": {"name":"Papiamento", "id":"pap", "ownlang":"Papiamentu"}, + "ps": {"name":"Pashto", "id":"ps", "ownlang":"پښتو"}, + "fa": {"name":"Persian", "id":"fa", "ownlang":"فارسی"}, + "pl": {"name":"Polish", "id":"pl", "ownlang":"Polski"}, + "pt": {"name":"Portuguese", "id":"pt", "ownlang":"Português"}, + "pt-BR": {"name":"Portuguese (Brazil)", "id":"pt-BR", "ownlang":"Português do Brasil"}, + "pt-PT": {"name":"Portuguese (Portugal)", "id":"pt-PT", "ownlang":"Português do Portugal"}, + "pa": {"name":"Punjabi", "id":"pa", "ownlang":"पंजाबी"}, + "ro": {"name":"Romanian", "id":"ro", "ownlang":"Română"}, + "ru": {"name":"Russian", "id":"ru", "ownlang":"Pусский"}, + "sm": {"name":"Samoan", "id":"sm", "ownlang":"Gagana Sāmoa"}, + "gd": {"name":"Scottish Gaelic", "id":"gd", "ownlang":"Gàidhlig"}, + "sr": {"name":"Serbian", "id":"sr", "ownlang":"Српски"}, + "st": {"name":"Sesotho", "id":"st", "ownlang":"Sesotho"}, + "sn": {"name":"Shona", "id":"sn", "ownlang":"Shona"}, + "sd": {"name":"Sindhi", "id":"sd", "ownlang":"سنڌي"}, + "si": {"name":"Sinhala", "id":"si", "ownlang":"සිංහල"}, + "sk": {"name":"Slovak", "id":"sk", "ownlang":"Slovenčina"}, + "sl": {"name":"Slovenian", "id":"sl", "ownlang":"Slovenščina"}, + "so": {"name":"Somali", "id":"so", "ownlang":"Soomaali"}, + "es": {"name":"Spanish", "id":"es", "ownlang":"Español"}, + "es-419": {"name":"Spanish (Latin America)", "id":"es-419", "ownlang":"Español latinoamericano"}, + "su": {"name":"Sundanese", "id":"su", "ownlang":"Basa Sunda"}, + "sw": {"name":"Swahili", "id":"sw", "ownlang":"Kiswahili"}, + "sv": {"name":"Swedish", "id":"sv", "ownlang":"Svenska"}, + "tg": {"name":"Tajik", "id":"tg", "ownlang":"тоҷикӣ"}, + "ta": {"name":"Tamil", "id":"ta", "ownlang":"தமிழ்"}, + "tt": {"name":"Tatar", "id":"tt", "ownlang":"татарча"}, + "te": {"name":"Telugu", "id":"te", "ownlang":"తెలుగు"}, + "th": {"name":"Thai", "id":"th", "ownlang":"ภาษาไทย"}, + "tr": {"name":"Turkish", "id":"tr", "ownlang":"Türkçe"}, + "tk": {"name":"Turkmen", "id":"tk", "ownlang":"Türkmençe"}, + "udm": {"name":"Udmurt", "id":"udm", "ownlang":"удмурт кыл"}, + "uk": {"name":"Ukrainian", "id":"uk", "ownlang":"Yкраїнський"}, + "ur": {"name":"Urdu", "id":"ur", "ownlang":"اُردُو"}, + "ug": {"name":"Uyghur", "id":"ug", "ownlang":"ئۇيغۇر تىلى"}, + "uz": {"name":"Uzbek", "id":"uz", "ownlang":"اوزبیک"}, + "vi": {"name":"Vietnamese", "id":"vi", "ownlang":"Tiếng Việt Nam"}, + "cy": {"name":"Welsh", "id":"cy", "ownlang":"Cymraeg"}, + "xh": {"name":"Xhosa", "id":"xh", "ownlang":"Xhosa"}, + "yi": {"name":"Yiddish", "id":"yi", "ownlang":"ייִדיש ייִדיש‬"}, + "yo": {"name":"Yoruba", "id":"yo", "ownlang":"Èdè Yorùbá"}, + "zu": {"name":"Zulu", "id":"zu", "ownlang":"Zulu"} + }, + "LibraryStrings": { + "bg": { + "add_to": "Добавяне към {{var0}}", + "ascending": "Възходящ", + "center": "Центрирано", + "changelog_added": "Нови функции", + "changelog_fixed": "Отстраняване на неизправности", + "changelog_improved": "Подобрения", + "changelog_progress": "Напредък", + "check_for_updates": "Провери за актуализации", + "clipboard_success": "Копирано {{var0}} в клипборда", + "confirm": "Сигурен ли си?", + "copy": "Копиране {{var0}}", + "delete_fail": "{{var0}} не може да бъде изтрито", + "delete_success": "{{var0}} изтрито успешно", + "descending": "Низходящ", + "developer": "Разработчик", + "donate_message": "Подкрепете ме за повече актуализации!", + "download": "Изтегли", + "download_fail": "{{var0}} не може да бъде изтеглен", + "download_success": "{{var0}} изтеглено успешно", + "file_navigator_text": "Преглед на файл", + "first": "Първо", + "from": "От", + "gradient": "Градиент", + "guildbanner": "Банер", + "guildicon": "Икона", + "installed": "Инсталирани", + "last": "Последно", + "left": "Наляво", + "loading": "Зарежда се {{var0}}", + "location": "Местоположение", + "order": "Последователност", + "outdated": "Остарял", + "please_wait": "Приятно чакане", + "right": "Нали", + "save_fail": "{{var0}} не може да бъде запазен", + "save_success": "{{var0}} запазено успешно", + "send": "Изпрати {{var0}}", + "server": "Сървър", + "settings_shareData_description": "Синхронизира конфигурациите на приставките между акаунти в Discord", + "settings_showSupportBadges_description": "Показва малки значки за потребители, които поддържат моя Patreon", + "settings_showToasts_description": "Показва тостове за стартиране и спиране на приставката", + "settings_showToasts_note": "Деактивирайте общата настройка '{{var0}}' на BD, преди да ги деактивирате", + "settings_toastPosition_description": "Позиция за тост по подразбиране", + "settings_toastPosition_note": "Променя само позицията на тостове, създадени от моите приставки", + "settings_useChromium_description": "Отворете връзки в Discord вместо вашия браузър", + "sort_by": "Сортиране по", + "status": "Състояние", + "time": "Време", + "timezone": "Часова зона", + "to": "Да се", + "toast_plugin_loaded": "{{var0}} е зареден", + "toast_plugin_started": "{{var0}} започна", + "toast_plugin_stopped": "{{var0}} спря", + "toast_plugin_translated": "преведено на {{var0}}", + "toast_plugin_unloaded": "{{var0}} е разтоварен", + "toast_plugin_update_failed": "Актуализацията за {{var0}} не може да бъде изтеглена", + "toast_plugin_updated": "{{var0}} {{var1}} е заменено с {{var2}} {{var3}}", + "update_check_complete": "Проверката за актуализация на приставката завърши", + "update_check_complete_outdated": "Проверката за актуализация на приставката завърши - {{var0}} остаряла!", + "update_check_info": "Проверете приставки, които поддържат проверката на актуализацията", + "update_notice_click": "Щракнете за актуализация!", + "update_notice_reload": "Презаредете, за да завършите актуализацията", + "update_notice_update": "Следните приставки трябва да бъдат актуализирани: ", + "updated": "Актуализирано" + }, + "cs": { + "add_to": "Přidat do {{var0}}", + "ascending": "Vzestupně", + "center": "Na střed", + "changelog_added": "Nové vlastnosti", + "changelog_fixed": "Oprava chyb", + "changelog_improved": "Vylepšení", + "changelog_progress": "Pokrok", + "check_for_updates": "Kontrola aktualizací", + "clipboard_success": "{{var0}} zkopírováno do schránky", + "confirm": "Jsi si jistá?", + "copy": "Kopírovat {{var0}}", + "delete_fail": "Aplikaci {{var0}} nelze smazat", + "delete_success": "{{var0}} úspěšně smazán", + "descending": "Klesající", + "developer": "Vývojář", + "donate_message": "Podpořte mě, abyste získali další aktualizace!", + "download": "Stažení", + "download_fail": "Aplikaci {{var0}} nelze stáhnout", + "download_success": "Aplikace {{var0}} byla úspěšně stažena", + "file_navigator_text": "Procházet soubor", + "first": "První", + "from": "Z", + "gradient": "Spád", + "guildbanner": "Ikona", + "guildicon": "Prapor", + "installed": "Nainstalováno", + "last": "Poslední", + "left": "Vlevo, odjet", + "loading": "Načítání {{var0}}", + "location": "Umístění", + "order": "Objednat", + "outdated": "Zastaralý", + "please_wait": "Prosím, čekejte", + "right": "Že jo", + "save_fail": "{{var0}} nelze uložit", + "save_success": "{{var0}} úspěšně uložen", + "send": "Odeslat {{var0}}", + "server": "Server", + "settings_shareData_description": "Synchronizuje konfigurace zásuvných modulů mezi účty Discord", + "settings_showSupportBadges_description": "Zobrazuje malé odznaky pro uživatele, kteří podporují můj Patreon", + "settings_showToasts_description": "Zobrazuje spuštění a zastavení pluginu", + "settings_showToasts_note": "Před deaktivací deaktivujte obecné nastavení BD {{var0}}", + "settings_toastPosition_description": "Výchozí pozice toastu", + "settings_toastPosition_note": "Změní pouze pozici toastů vytvořených mými pluginy", + "settings_useChromium_description": "Otevřete odkazy v programu Discord namísto v prohlížeči", + "sort_by": "Seřazeno podle", + "status": "Postavení", + "time": "Čas", + "timezone": "Časové pásmo", + "to": "Na", + "toast_plugin_loaded": "{{var0}} byl načten", + "toast_plugin_started": "Byla spuštěna aplikace {{var0}}", + "toast_plugin_stopped": "Aplikace {{var0}} byla zastavena", + "toast_plugin_translated": "přeloženo do jazyka {{var0}}", + "toast_plugin_unloaded": "Aplikace {{var0}} byla uvolněna", + "toast_plugin_update_failed": "Aktualizaci pro {{var0}} nelze stáhnout", + "toast_plugin_updated": "{{var0}} {{var1}} byl nahrazen {{var2}} {{var3}}", + "update_check_complete": "Kontrola aktualizace pluginu dokončena", + "update_check_complete_outdated": "Kontrola aktualizace pluginu dokončena - {{var0}} zastaralá!", + "update_check_info": "Zkontrolujte doplňky, které podporují kontrolu aktualizace", + "update_notice_click": "Klikněte pro aktualizaci!", + "update_notice_reload": "Aktualizaci dokončete znovu", + "update_notice_update": "Je třeba aktualizovat následující doplňky:", + "updated": "Aktualizováno" + }, + "da": { + "add_to": "Føj til {{var0}}", + "ascending": "Stigende", + "center": "Centreret", + "changelog_added": "Nye funktioner", + "changelog_fixed": "Fejlfinding", + "changelog_improved": "Forbedringer", + "changelog_progress": "Fremskridt", + "check_for_updates": "Søg efter opdateringer", + "clipboard_success": "Kopieret {{var0}} til udklipsholder", + "confirm": "Er du sikker?", + "copy": "Kopiér {{var0}}", + "delete_fail": "{{var0}} kan ikke slettes", + "delete_success": "{{var0}} blev slettet", + "descending": "Aftagende", + "developer": "Udvikler", + "donate_message": "Støt mig for flere opdateringer!", + "download": "Hent", + "download_fail": "{{var0}} kan ikke downloades", + "download_success": "{{var0}} downloadet med succes", + "file_navigator_text": "Gennemse fil", + "first": "Først", + "from": "Fra", + "gradient": "Gradient", + "guildbanner": "Banner", + "guildicon": "Ikon", + "installed": "Installeret", + "last": "Sidst", + "left": "Venstre", + "loading": "Indlæser {{var0}}", + "location": "Beliggenhed", + "order": "Sekvens", + "outdated": "Forældet", + "please_wait": "Vent venligst", + "right": "Ret", + "save_fail": "{{var0}} kan ikke gemmes", + "save_success": "{{var0}} blev gemt", + "send": "Send {{var0}}", + "server": "Server", + "settings_shareData_description": "Synkroniserer plugin-konfigurationerne mellem Discord-konti", + "settings_showSupportBadges_description": "Viser små badges til brugere, der understøtter min Patreon", + "settings_showToasts_description": "Viser plugin start og stop toasts", + "settings_showToasts_note": "Deaktiver den generelle indstilling '{{var0}}' for BD'er, inden du deaktiverer dem", + "settings_toastPosition_description": "Standard skålposition", + "settings_toastPosition_note": "Ændrer kun placeringen af ​​skåle oprettet af mine plugins", + "settings_useChromium_description": "Åbn Links i Discord i stedet for din browser", + "sort_by": "Sorter efter", + "status": "Status", + "time": "Tid", + "timezone": "Tidszone", + "to": "Til", + "toast_plugin_loaded": "{{var0}} er indlæst", + "toast_plugin_started": "{{var0}} er startet", + "toast_plugin_stopped": "{{var0}} er stoppet", + "toast_plugin_translated": "oversat til {{var0}}", + "toast_plugin_unloaded": "{{var0}} er blevet aflæst", + "toast_plugin_update_failed": "Opdatering til {{var0}} kan ikke downloades", + "toast_plugin_updated": "{{var0}} {{var1}} er blevet erstattet af {{var2}} {{var3}}", + "update_check_complete": "Kontrollen af plugin-opdatering afsluttet", + "update_check_complete_outdated": "Kontrollen af ​​plugin-opdatering afsluttet - {{var0}} forældet!", + "update_check_info": "Kontroller plugins, der understøtter opdateringskontrollen", + "update_notice_click": "Klik for at opdatere!", + "update_notice_reload": "Genindlæs for at fuldføre opdateringen", + "update_notice_update": "Følgende plugins skal opdateres: ", + "updated": "Opdateret" + }, + "de": { + "add_to": "Zu {{var0}} hinzufügen", + "ascending": "Aufsteigend", + "center": "Zentriert", + "changelog_added": "Neue Features", + "changelog_fixed": "Fehlerbehebung", + "changelog_improved": "Verbesserungen", + "changelog_progress": "Fortschritt", + "check_for_updates": "Suche nach Updates", + "clipboard_success": "{{var0}} in die Zwischenablage kopiert", + "confirm": "Bist du sicher?", + "copy": "{{var0}} kopieren", + "delete_fail": "{{var0}} kann nicht gelöscht werden", + "delete_success": "{{var0}} erfolgreich gelöscht", + "descending": "Absteigend", + "developer": "Entwickler", + "donate_message": "Unterstütze mich, um weitere Updates zu erhalten!", + "download": "Herunterladen", + "download_fail": "{{var0}} kann nicht heruntergeladen werden", + "download_success": "{{var0}} erfolgreich heruntergeladen", + "file_navigator_text": "Datei durchsuchen", + "first": "Zuerst", + "from": "Von", + "gradient": "Gradient", + "guildbanner": "Banner", + "guildicon": "Symbol", + "installed": "Installiert", + "last": "Zuletzt", + "left": "Links", + "loading": "Lädt {{var0}}", + "location": "Ort", + "order": "Reihenfolge", + "outdated": "Veraltet", + "please_wait": "Bitte warten", + "right": "Rechts", + "save_fail": "{{var0}} kann nicht gespeichert werden", + "save_success": "{{var0}} erfolgreich gespeichert", + "send": "{{var0}} senden", + "server": "Server", + "settings_shareData_description": "Synchronisiert die Plugin-Konfigurationen zwischen Discord-Konten", + "settings_showSupportBadges_description": "Zeigt kleine Abzeichen für Benutzer, die meinen Patreon unterstützen", + "settings_showToasts_description": "Zeigt Plugin Start und Stopp Toasts", + "settings_showToasts_note": "Deaktiviere die allgemeine BD Einstellung '{{var0}}', bevor du diese deaktivierst", + "settings_toastPosition_description": "Standard Toast Position", + "settings_toastPosition_note": "Ändert nur die Position von Toasts, die von meinen Plugins erstellt wurden", + "settings_useChromium_description": "Öffne Links in Discord anstelle deines Browsers", + "sort_by": "Sortieren nach", + "status": "Status", + "time": "Zeit", + "timezone": "Zeitzone", + "to": "Zu", + "toast_plugin_loaded": "{{var0}} wurde geladen", + "toast_plugin_started": "{{var0}} wurde gestartet", + "toast_plugin_stopped": "{{var0}} wurde gestoppt", + "toast_plugin_translated": "übersetzt nach {{var0}}", + "toast_plugin_unloaded": "{{var0}} wurde entladen", + "toast_plugin_update_failed": "Update für {{var0}} kann nicht heruntergeladen werden", + "toast_plugin_updated": "{{var0}} {{var1}} wurde durch {{var2}} {{var3}} ersetzt", + "update_check_complete": "Plugin Update Check abgeschlossen", + "update_check_complete_outdated": "Plugin Update Check abgeschlossen - {{var0}} veraltet!", + "update_check_info": "Check Plugins, die den Update Check unterstützen", + "update_notice_click": "Zum Aktualisieren klicken!", + "update_notice_reload": "Neu laden, um die Aktualisierung abzuschließen", + "update_notice_update": "Die folgenden Plugins müssen aktualisiert werden: ", + "updated": "Aktualisiert" + }, + "el": { + "add_to": "Προσθήκη στο {{var0}}", + "ascending": "Αύξουσα", + "center": "Στο κέντρο", + "changelog_added": "Νέα χαρακτηριστικά", + "changelog_fixed": "Διορθώσεις σφαλμάτων", + "changelog_improved": "Βελτιώσεις", + "changelog_progress": "Πρόοδος", + "check_for_updates": "Ελεγχος για ενημερώσεις", + "clipboard_success": "Αντιγραφή του {{var0}} στο Πρόχειρο", + "confirm": "Σίγουρα;", + "copy": "Αντιγραφή {{var0}}", + "delete_fail": "Δεν είναι δυνατή η διαγραφή του {{var0}}", + "delete_success": "Το {{var0}} διαγράφηκε με επιτυχία", + "descending": "Φθίνουσα", + "developer": "Προγραμματιστής", + "donate_message": "Υποστηρίξτε με για περισσότερες ενημερώσεις!", + "download": "Λήψη", + "download_fail": "Αδυνατή η λήψη του {{var0}}", + "download_success": "Η λήψη του {{var0}} ολοκληρώθηκε με επιτυχία", + "file_navigator_text": "Αναζήτηση αρχείου", + "first": "Πρώτα", + "from": "Από", + "gradient": "Βαθμίδα", + "guildbanner": "Διαφημιστικό", + "guildicon": "Εικονίδιο", + "installed": "Εγκατεστημένο", + "last": "Τελευταίος", + "left": "Αριστερά", + "loading": "Φόρτωση {{var0}}", + "location": "Τοποθεσία", + "order": "Ταξινόμηση", + "outdated": "Παλαιό", + "please_wait": "Αναμείνατε...", + "right": "Δεξιά", + "save_fail": "Αδυνατή η αποθήκευση του {{var0}}", + "save_success": "Το {{var0}} αποθηκεύτηκε με επιτυχία", + "send": "Αποστολή {{var0}}", + "server": "Διακομιστής", + "settings_shareData_description": "Συγχρ. ρύθμισης παραμέτρων Προσθέτου μεταξύ λογαριασμών Discord", + "settings_showSupportBadges_description": "Εμφανίζει μικρά παράσημα για χρήστες που υποστηρίζουν το Patreon μου", + "settings_showToasts_description": "Εμφάνιση επισημάνσεων έναρξης και τερματισμού Προσθέτου", + "settings_showToasts_note": "Απενεργοποιεί τη γενική ρύθμιση «{{var0}}» του BD πριν απενεργοποιηθεί", + "settings_toastPosition_description": "Προεπιλεγμένη Θέση Επεξήγησης", + "settings_toastPosition_note": "Αλλάζει μόνο τη Θέση των Επεξηγήσεων που δημιουργήθηκαν από τις προσθήκες μου", + "settings_useChromium_description": "Ανοίξτε τους Συνδέσμους στο Discord αντί για τον φυλλομετρητή σας", + "sort_by": "Ταξινόμηση κατά", + "status": "Κατάσταση", + "time": "Χρόνος", + "timezone": "Ζώνη ώρας", + "to": "Προς το", + "toast_plugin_loaded": "Το {{var0}} έχει φορτωθεί", + "toast_plugin_started": "Το {{var0}} έχει ξεκινήσει", + "toast_plugin_stopped": "Το {{var0}} έχει τερματιστεί", + "toast_plugin_translated": "μεταφράστηκε σε {{var0}}", + "toast_plugin_unloaded": "Το {{var0}} έχει εκφορτωθεί", + "toast_plugin_update_failed": "Δεν είναι δυνατή η λήψη της ενημέρωσης για το {{var0}}", + "toast_plugin_updated": "Το {{var0}} {{var1}} αντικαταστάθηκε από το {{var2}} {{var3}}", + "update_check_complete": "Ο Έλεγχος Ενημέρωσης Πρόσθετου ολοκληρώθηκε", + "update_check_complete_outdated": "Ο Έλεγχος Ενημέρωσης Πρόσθετου ολοκληρώθηκε - {{var0}} δεν είναι ενημερωμένο!", + "update_check_info": "Έλεγχος Προσθέτων με υποστήριξη ενημέρωσης", + "update_notice_click": "Πατήστε για ενημέρωση!", + "update_notice_reload": "Επαναφορτώστε για ολοκλήρωση της ενημέρωσης", + "update_notice_update": "Τα ακόλουθα Πρόσθετα πρέπει να ενημερωθούν: ", + "updated": "Ενημερωμένο" + }, + "es": { + "add_to": "Agregar a {{var0}}", + "ascending": "Ascendente", + "center": "Centrado", + "changelog_added": "Nuevas características", + "changelog_fixed": "Solución de problemas", + "changelog_improved": "Mejoras", + "changelog_progress": "Progreso", + "check_for_updates": "Buscar actualizaciones", + "clipboard_success": "Copiado {{var0}} al Portapapeles", + "confirm": "¿Estás seguro?", + "copy": "Copiar {{var0}}", + "delete_fail": "{{var0}} no se puede eliminar", + "delete_success": "{{var0}} eliminado correctamente", + "descending": "Descendiendo", + "developer": "Desarrollador", + "donate_message": "¡Apóyanme para obtener más actualizaciones!", + "download": "Descargar", + "download_fail": "{{var0}} no se puede descargar", + "download_success": "{{var0}} descargado correctamente", + "file_navigator_text": "Buscar Archivo", + "first": "Primero", + "from": "Desde", + "gradient": "Degradado", + "guildbanner": "Bandera", + "guildicon": "Icono", + "installed": "Instalado", + "last": "Último", + "left": "Izquierda", + "loading": "Cargando {{var0}}", + "location": "Localización", + "order": "Secuencia", + "outdated": "Anticuado", + "please_wait": "Por favor espera", + "right": "Correcto", + "save_fail": "{{var0}} no se puede guardar", + "save_success": "{{var0}} guardado correctamente", + "send": "Enviar {{var0}}", + "server": "Servidor", + "settings_shareData_description": "Sincroniza las configuraciones del complemento entre las cuentas de Discord", + "settings_showSupportBadges_description": "Muestra pequeñas insignias para los usuarios que apoyan mi Patreon", + "settings_showToasts_description": "Muestra el inicio y la parada del complemento.", + "settings_showToasts_note": "Desactive la configuración general '{{var0}}' de los BD antes de desactivarlos", + "settings_toastPosition_description": "Posición de tostada predeterminada", + "settings_toastPosition_note": "Solo cambia la posición de los brindis creados por mis complementos", + "settings_useChromium_description": "Abra enlaces en Discord en lugar de su navegador", + "sort_by": "Ordenar por", + "status": "Estado", + "time": "Hora", + "timezone": "Zona horaria", + "to": "A", + "toast_plugin_loaded": "Se cargó {{var0}}", + "toast_plugin_started": "{{var0}} ha comenzado", + "toast_plugin_stopped": "{{var0}} se detuvo", + "toast_plugin_translated": "traducido a {{var0}}", + "toast_plugin_unloaded": "{{var0}} se ha descargado", + "toast_plugin_update_failed": "No se puede descargar la actualización para {{var0}}", + "toast_plugin_updated": "{{var0}} {{var1}} ha sido reemplazado por {{var2}} {{var3}}", + "update_check_complete": "Comprobación de actualización del complemento completada", + "update_check_complete_outdated": "Comprobación de actualización del complemento completada: {{var0}} desactualizada.", + "update_check_info": "Verifique los complementos que admiten la verificación de actualizaciones", + "update_notice_click": "¡Haz clic para actualizar!", + "update_notice_reload": "Recargar para completar la actualización", + "update_notice_update": "Los siguientes complementos deben actualizarse: ", + "updated": "Actualizado" + }, + "fi": { + "add_to": "Lisää {{var0}}", + "ascending": "Nouseva", + "center": "Keskitetty", + "changelog_added": "Uudet ominaisuudet", + "changelog_fixed": "Ongelmien karttoittaminen", + "changelog_improved": "Parannuksia", + "changelog_progress": "Edistystä", + "check_for_updates": "Tarkista päivitykset", + "clipboard_success": "Kopioitu {{var0}} leikepöydälle", + "confirm": "Oletko varma?", + "copy": "Kopioi {{var0}}", + "delete_fail": "{{var0}} ei voi poistaa", + "delete_success": "{{var0}} poistettu", + "descending": "Laskeva", + "developer": "Kehittäjä", + "donate_message": "Tue minua lisää päivityksiä varten!", + "download": "Ladata", + "download_fail": "{{var0}} ei voi ladata", + "download_success": "{{var0}} ladattu onnistuneesti", + "file_navigator_text": "Selaa tiedostoa", + "first": "Ensimmäinen", + "from": "Alkaen", + "gradient": "Kaltevuus", + "guildbanner": "Banneri", + "guildicon": "Kuvake", + "installed": "Asennettu", + "last": "Kestää", + "left": "Vasen", + "loading": "Ladataan {{var0}}", + "location": "Sijainti", + "order": "Järjestys", + "outdated": "Vanhentunut", + "please_wait": "Odota", + "right": "Aivan", + "save_fail": "Kohdetta {{var0}} ei voi tallentaa", + "save_success": "{{var0}} tallennettu onnistuneesti", + "send": "Lähetä {{var0}}", + "server": "Palvelin", + "settings_shareData_description": "Synkronoi laajennusasetukset Discord-tilien välillä", + "settings_showSupportBadges_description": "Näyttää pienet merkit käyttäjille, jotka tukevat Patreoniani", + "settings_showToasts_description": "Näyttää laajennuksen aloitus- ja lopetusleivokset", + "settings_showToasts_note": "Poista BD-levyjen yleinen asetus {{var0}} ennen niiden poistamista käytöstä", + "settings_toastPosition_description": "Oletus paahtoleipää", + "settings_toastPosition_note": "Muuttaa vain laajennukset luomien paahtoleivien sijaintia", + "settings_useChromium_description": "Avaa Linkit ristiriidassa selaimen sijaan", + "sort_by": "Järjestä", + "status": "Tila", + "time": "Aika", + "timezone": "Aikavyöhyke", + "to": "Vastaanottaja", + "toast_plugin_loaded": "{{var0}} on ladattu", + "toast_plugin_started": "{{var0}} on alkanut", + "toast_plugin_stopped": "{{var0}} on pysähtynyt", + "toast_plugin_translated": "käännetty kielelle {{var0}}", + "toast_plugin_unloaded": "{{var0}} on purettu", + "toast_plugin_update_failed": "Verkkotunnuksen {{var0}} päivitystä ei voi ladata", + "toast_plugin_updated": "{{var0}} {{var1}} on korvattu sanalla {{var2}} {{var3}}", + "update_check_complete": "Laajennuksen päivityksen tarkistus valmis", + "update_check_complete_outdated": "Laajennuksen päivityksen tarkistus valmis - {{var0}} vanhentunut!", + "update_check_info": "Tarkista päivitystarkistusta tukevat laajennukset", + "update_notice_click": "Napsauta päivittääksesi!", + "update_notice_reload": "Lataa päivitys loppuun", + "update_notice_update": "Seuraavat laajennukset on päivitettävä: ", + "updated": "Päivitetty" + }, + "fr": { + "add_to": "Ajouter à {{var0}}", + "ascending": "Ascendant", + "center": "Centré", + "changelog_added": "Nouvelles fonctionnalités", + "changelog_fixed": "Dépannage", + "changelog_improved": "Améliorations", + "changelog_progress": "Le progrès", + "check_for_updates": "Rechercher des mises à jour", + "clipboard_success": "{{var0}} copié dans le presse-papiers", + "confirm": "Êtes-vous sûr?", + "copy": "Copiez {{var0}}", + "delete_fail": "{{var0}} ne peut pas être supprimé", + "delete_success": "{{var0}} supprimé avec succès", + "descending": "Descendant", + "developer": "Développeur", + "donate_message": "Soutenez-moi pour plus de mises à jour!", + "download": "Télécharger", + "download_fail": "{{var0}} ne peut pas être téléchargé", + "download_success": "{{var0}} téléchargé avec succès", + "file_navigator_text": "Parcourir le fichier", + "first": "Première", + "from": "De", + "gradient": "Pente", + "guildbanner": "Bannière", + "guildicon": "Icône", + "installed": "Installée", + "last": "Dernier", + "left": "Gauche", + "loading": "Chargement de {{var0}}", + "location": "Emplacement", + "order": "Séquence", + "outdated": "Dépassé", + "please_wait": "S'il vous plaît, attendez", + "right": "Droite", + "save_fail": "{{var0}} ne peut pas être enregistré", + "save_success": "{{var0}} a bien été enregistré", + "send": "Envoyer {{var0}}", + "server": "Serveur", + "settings_shareData_description": "Synchronise les configurations de plugin entre les comptes Discord", + "settings_showSupportBadges_description": "Affiche de petits badges pour les utilisateurs qui soutiennent mon Patreon", + "settings_showToasts_description": "Affiche les toasts de démarrage et d'arrêt du plugin", + "settings_showToasts_note": "Désactivez le paramètre général '{{var0}}' des BD avant de les désactiver", + "settings_toastPosition_description": "Position de toast par défaut", + "settings_toastPosition_note": "Modifie uniquement la position des toasts créés par mes plugins", + "settings_useChromium_description": "Ouvrez les liens dans Discord au lieu de votre navigateur", + "sort_by": "Trier par", + "status": "Statut", + "time": "Temps", + "timezone": "Fuseau horaire", + "to": "À", + "toast_plugin_loaded": "{{var0}} a été chargé", + "toast_plugin_started": "{{var0}} a commencé", + "toast_plugin_stopped": "{{var0}} s'est arrêté", + "toast_plugin_translated": "traduit en {{var0}}", + "toast_plugin_unloaded": "{{var0}} a été déchargé", + "toast_plugin_update_failed": "La mise à jour pour {{var0}} ne peut pas être téléchargée", + "toast_plugin_updated": "{{var0}} {{var1}} a été remplacé par {{var2}} {{var3}}", + "update_check_complete": "Vérification de la mise à jour du plugin terminée", + "update_check_complete_outdated": "Vérification de la mise à jour du plugin terminée - {{var0}} obsolète!", + "update_check_info": "Vérifiez les plugins prenant en charge la vérification des mises à jour", + "update_notice_click": "Cliquez pour mettre à jour!", + "update_notice_reload": "Recharger pour terminer la mise à jour", + "update_notice_update": "Les plugins suivants doivent être mis à jour: ", + "updated": "Mis à jour" + }, + "hi": { + "add_to": "{{var0}} में जोड़ें", + "ascending": "आरोही", + "center": "केंद्रित", + "changelog_added": "नए विशेषताएँ", + "changelog_fixed": "कंप्यूटर प्रोग्राम या प्रणाली में बग को दूर करना", + "changelog_improved": "सुधार", + "changelog_progress": "प्रगति", + "check_for_updates": "अद्यतन के लिए जाँच", + "clipboard_success": "{{var0}} को क्लिपबोर्ड पर कॉपी किया गया", + "confirm": "क्या आपको यकीन है?", + "copy": "कॉपी {{var0}}", + "delete_fail": "{{var0}} को हटाया नहीं जा सकता", + "delete_success": "{{var0}} सफलतापूर्वक हटाया गया", + "descending": "अवरोही", + "developer": "डेवलपर", + "donate_message": "आगे के अपडेट प्राप्त करने के लिए मेरा समर्थन करें!", + "download": "डाउनलोड", + "download_fail": "{{var0}} डाउनलोड नहीं किया जा सकता", + "download_success": "{{var0}} सफलतापूर्वक डाउनलोड किया गया", + "file_navigator_text": "फाइल खोजो", + "first": "प्रथम", + "from": "से", + "gradient": "ढाल", + "guildbanner": "बैनर", + "guildicon": "आइकन", + "installed": "स्थापित", + "last": "पिछले", + "left": "बाएं", + "loading": "लोड हो रहा है {{var0}}", + "location": "स्थान", + "order": "गण", + "outdated": "रगड़ा हुआ", + "please_wait": "कृपया प्रतीक्षा करें", + "right": "सही", + "save_fail": "{{var0}} सहेजा नहीं जा सकता", + "save_success": "{{var0}} सफलतापूर्वक सहेजा गया", + "send": "भेजें {{var0}}", + "server": "सर्वर", + "settings_shareData_description": "डिस्कॉर्ड खातों के बीच प्लगइन कॉन्फिग को सिंक्रोनाइज़ करता है", + "settings_showSupportBadges_description": "मेरे Patreon का समर्थन करने वाले उपयोगकर्ताओं के लिए छोटे बैज दिखाता है", + "settings_showToasts_description": "प्लगिन को दिखाता है टोस्ट शुरू और बंद करो", + "settings_showToasts_note": "इसे अक्षम करने से पहले BDs सामान्य '{{var0}}' सेटिंग अक्षम करें", + "settings_toastPosition_description": "डिफ़ॉल्ट टोस्ट स्थिति", + "settings_toastPosition_note": "केवल मेरे प्लगइन्स द्वारा बनाए गए टोस्ट की स्थिति बदलता है", + "settings_useChromium_description": "अपने ब्राउज़र के बजाय डिस्कॉर्ड में लिंक खोलें", + "sort_by": "इसके अनुसार क्रमबद्ध करें", + "status": "स्थिति", + "time": "समय", + "timezone": "समय क्षेत्र", + "to": "सेवा", + "toast_plugin_loaded": "{{var0}} लोड कर दिया गया है", + "toast_plugin_started": "{{var0}} शुरू कर दिया गया है", + "toast_plugin_stopped": "{{var0}} रोक दिया गया है", + "toast_plugin_translated": "{{var0}} में अनुवादित", + "toast_plugin_unloaded": "{{var0}} अनलोड कर दिया गया है", + "toast_plugin_update_failed": "{{var0}} के लिए अपडेट डाउनलोड नहीं किया जा सकता", + "toast_plugin_updated": "{{var0}} {{var1}} को {{var2}} {{var3}} से बदल दिया गया है", + "update_check_complete": "प्लगइन अद्यतन जाँच पूर्ण", + "update_check_complete_outdated": "प्लगिन अपडेट जांच पूरी हुई - {{var0}} पुरानी!", + "update_check_info": "चेक प्लगइन्स जो अपडेट चेक का समर्थन करते हैं", + "update_notice_click": "अपडेट करने के लिए क्लिक करें!", + "update_notice_reload": "अद्यतन पूरा करने के लिए पुनः लोड करें", + "update_notice_update": "निम्नलिखित प्लगइन्स को अद्यतन करने की आवश्यकता है:", + "updated": "अद्यतन" + }, + "hr": { + "add_to": "Dodaj u {{var0}}", + "ascending": "Uzlazni", + "center": "Centrirano", + "changelog_added": "Nove značajke", + "changelog_fixed": "Rješavanje problema", + "changelog_improved": "Poboljšanja", + "changelog_progress": "Napredak", + "check_for_updates": "Provjerite ima li ažuriranja", + "clipboard_success": "Kopirano {{var0}} u međuspremnik", + "confirm": "Jesi li siguran?", + "copy": "Kopiraj {{var0}}", + "delete_fail": "{{var0}} nije moguće izbrisati", + "delete_success": "{{var0}} uspješno je izbrisano", + "descending": "Silazni", + "developer": "Programer", + "donate_message": "Podržite me za još novosti!", + "download": "Preuzimanje datoteka", + "download_fail": "{{var0}} nije moguće preuzeti", + "download_success": "{{var0}} uspješno preuzeto", + "file_navigator_text": "Pregledaj datoteku", + "first": "Prvi", + "from": "Iz", + "gradient": "Gradijent", + "guildbanner": "Natpis", + "guildicon": "Ikona", + "installed": "Instaliran", + "last": "Posljednji", + "left": "Lijevo", + "loading": "Učitavanje {{var0}}", + "location": "Mjesto", + "order": "Slijed", + "outdated": "Zastario", + "please_wait": "Drago mi je čekati", + "right": "Pravo", + "save_fail": "{{var0}} nije moguće spremiti", + "save_success": "{{var0}} uspješno spremljeno", + "send": "Pošalji {{var0}}", + "server": "Poslužitelju", + "settings_shareData_description": "Sinkronizira konfiguracije dodataka između Discord računa", + "settings_showSupportBadges_description": "Prikazuje male značke za korisnike koji podržavaju moj Patreon", + "settings_showToasts_description": "Prikazuje tost za pokretanje i zaustavljanje dodatka", + "settings_showToasts_note": "Onemogućite opću postavku '{{var0}}' BD-ova prije nego što ih onemogućite", + "settings_toastPosition_description": "Zadani položaj tosta", + "settings_toastPosition_note": "Mijenja samo položaj zdravica koje su stvorili moji dodatke", + "settings_useChromium_description": "Otvorite veze u Discordu umjesto u pregledniku", + "sort_by": "Poredati po", + "status": "Status", + "time": "Vrijeme", + "timezone": "Vremenska zona", + "to": "Do", + "toast_plugin_loaded": "Učitana je {{var0}}", + "toast_plugin_started": "{{var0}} je započeo", + "toast_plugin_stopped": "{{var0}} je zaustavljen", + "toast_plugin_translated": "prevedeno na {{var0}}", + "toast_plugin_unloaded": "{{var0}} je istovaren", + "toast_plugin_update_failed": "Ažuriranje za {{var0}} nije moguće preuzeti", + "toast_plugin_updated": "{{var0}} {{var1}} zamijenjen je s {{var2}} {{var3}}", + "update_check_complete": "Provjera ažuriranja dodatka dovršena", + "update_check_complete_outdated": "Provjera ažuriranja dodatka dovršena - {{var0}} zastarjelo!", + "update_check_info": "Provjerite dodatke koji podržavaju provjeru ažuriranja", + "update_notice_click": "Kliknite za ažuriranje!", + "update_notice_reload": "Ponovo učitajte da biste dovršili ažuriranje", + "update_notice_update": "Treba ažurirati sljedeće dodatke: ", + "updated": "Ažurirano" + }, + "hu": { + "add_to": "Hozzáadás a következőhöz: {{var0}}", + "ascending": "Növekvő", + "center": "Középre", + "changelog_added": "Új funkciók", + "changelog_fixed": "Hibaelhárítás", + "changelog_improved": "Fejlesztések", + "changelog_progress": "Előrehalad", + "check_for_updates": "Frissítések keresése", + "clipboard_success": "{{var0}} a vágólapra másolva", + "confirm": "Biztos vagy ebben?", + "copy": "Másolás {{var0}}", + "delete_fail": "A {{var0}} nem törölhető", + "delete_success": "{{var0}} sikeresen törölve", + "descending": "Csökkenő", + "developer": "Fejlesztő", + "donate_message": "Támogasson további frissítésekért!", + "download": "Letöltés", + "download_fail": "A {{var0}} nem tölthető le", + "download_success": "A {{var0}} letöltése sikeresen megtörtént", + "file_navigator_text": "Tallózás a fájlban", + "first": "Első", + "from": "Erről", + "gradient": "Gradiens", + "guildbanner": "Banner", + "guildicon": "Ikon", + "installed": "Telepítve", + "last": "Utolsó", + "left": "Bal", + "loading": "{{var0}} betöltése", + "location": "Elhelyezkedés", + "order": "Sorrend", + "outdated": "Elavult", + "please_wait": "Várj", + "right": "Jobb", + "save_fail": "A {{var0}} nem menthető", + "save_success": "{{var0}} sikeresen mentve", + "send": "{{var0}} küldése", + "server": "Szerver", + "settings_shareData_description": "Szinkronizálja a beépülő modulok beállításait a Discord fiókok között", + "settings_showSupportBadges_description": "Apró jelvényeket mutat azoknak a felhasználóknak, akik támogatják a Patreon-t", + "settings_showToasts_description": "Mutatja a bővítmény start és stop pirítósokat", + "settings_showToasts_note": "Mielőtt letiltaná őket, tiltsa le a BD-k '{{var0}} ” általános beállítását", + "settings_toastPosition_description": "Alapértelmezett pirítós pozíció", + "settings_toastPosition_note": "Csak a bővítményeket által létrehozott pirítósok pozícióját változtatja meg", + "settings_useChromium_description": "Nyissa meg a Linkeket a diszkordban a böngészője helyett", + "sort_by": "Sorrend", + "status": "Állapot", + "time": "Idő", + "timezone": "Időzóna", + "to": "Erre", + "toast_plugin_loaded": "{{var0}} betöltve", + "toast_plugin_started": "A {{var0}} elindult", + "toast_plugin_stopped": "A {{var0}} leállt", + "toast_plugin_translated": "lefordítva {{var0}} nyelvre", + "toast_plugin_unloaded": "{{var0}} kirakva", + "toast_plugin_update_failed": "A {{var0}} frissítése nem tölthető le", + "toast_plugin_updated": "A {{var0}} {{var1}} helyébe a következő lépett: {{var2}} {{var3}}", + "update_check_complete": "A bővítmény modul frissítésének ellenőrzése befejeződött", + "update_check_complete_outdated": "A bővítmény modul frissítésének ellenőrzése befejeződött - {{var0}} elavult!", + "update_check_info": "Ellenőrizze a Frissítés ellenőrzését támogató bővítményeket modulokat", + "update_notice_click": "Kattintson a frissítéshez!", + "update_notice_reload": "Töltse be újra a frissítés befejezéséhez", + "update_notice_update": "A következő bővítményeket frissíteni kell: ", + "updated": "Frissítve" + }, + "it": { + "add_to": "Aggiungi a {{var0}}", + "ascending": "Ascendente", + "center": "Centrato", + "changelog_added": "Nuove caratteristiche", + "changelog_fixed": "Risoluzione dei problemi", + "changelog_improved": "Miglioramenti", + "changelog_progress": "Progresso", + "check_for_updates": "Controlla gli aggiornamenti", + "clipboard_success": "{{var0}} copiato negli Appunti", + "confirm": "Sei sicuro?", + "copy": "Copia {{var0}}", + "delete_fail": "{{var0}} non può essere eliminato", + "delete_success": "{{var0}} eliminato correttamente", + "descending": "Discendente", + "developer": "Sviluppatore", + "donate_message": "Supportami per ulteriori aggiornamenti!", + "download": "Scarica", + "download_fail": "{{var0}} non può essere scaricato", + "download_success": "{{var0}} scaricato correttamente", + "file_navigator_text": "Sfoglia file", + "first": "Primo", + "from": "A partire dal", + "gradient": "Pendenza", + "guildbanner": "Banner", + "guildicon": "Icona", + "installed": "Installato", + "last": "Scorso", + "left": "Sinistra", + "loading": "Caricamento di {{var0}}", + "location": "Posizione", + "order": "Sequenza", + "outdated": "Obsoleto", + "please_wait": "Attendere prego", + "right": "Destra", + "save_fail": "{{var0}} non può essere salvato", + "save_success": "{{var0}} salvato correttamente", + "send": "Invia {{var0}}", + "server": "Server", + "settings_shareData_description": "Sincronizza le configurazioni dei plug-in tra gli account Discord", + "settings_showSupportBadges_description": "Mostra piccoli badge per gli utenti che supportano il mio Patreon", + "settings_showToasts_description": "Mostra l'avvio e l'arresto del plugin", + "settings_showToasts_note": "Disabilita l'impostazione generale '{{var0}}' dei BD prima di disabilitarli", + "settings_toastPosition_description": "Posizione predefinita del toast", + "settings_toastPosition_note": "Cambia solo la posizione dei toast creati dai miei plugins", + "settings_useChromium_description": "Apri link in Discord invece che nel tuo browser", + "sort_by": "Ordina per", + "status": "Stato", + "time": "Tempo", + "timezone": "Fuso orario", + "to": "Per", + "toast_plugin_loaded": "{{var0}} è stato caricato", + "toast_plugin_started": "{{var0}} è iniziato", + "toast_plugin_stopped": "{{var0}} si è fermato", + "toast_plugin_translated": "tradotto in {{var0}}", + "toast_plugin_unloaded": "{{var0}} è stato scaricato", + "toast_plugin_update_failed": "Impossibile scaricare l'aggiornamento per {{var0}}", + "toast_plugin_updated": "{{var0}} {{var1}} è stato sostituito da {{var2}} {{var3}}", + "update_check_complete": "Controllo dell'aggiornamento del plugin completato", + "update_check_complete_outdated": "Controllo dell'aggiornamento del plugin completato - {{var0}} non aggiornato!", + "update_check_info": "Controlla i plugins che supportano il controllo degli aggiornamenti", + "update_notice_click": "Fare clic per aggiornare!", + "update_notice_reload": "Ricarica per completare l'aggiornamento", + "update_notice_update": "I seguenti plugins devono essere aggiornati: ", + "updated": "Aggiornato" + }, + "ja": { + "add_to": "{{var0}} に追加", + "ascending": "昇順", + "center": "中央揃え", + "changelog_added": "新機能", + "changelog_fixed": "修正点", + "changelog_improved": "改善点", + "changelog_progress": "進行中", + "check_for_updates": "アップデートを確認", + "clipboard_success": "{{var0}}をクリップボードにコピーしました", + "confirm": "本気ですか?", + "copy": "{{var0}}をコピーします", + "delete_fail": "{{var0}}の削除に失敗しました", + "delete_success": "{{var0}}が正常に削除されました", + "descending": "降順", + "developer": "開発者", + "donate_message": "もっといっぱいアップデートするために私をサポートしてください!", + "download": "ダウンロード", + "download_fail": "{{var0}} のダウンロードに失敗しました", + "download_success": "{{var0}} が正常にダウンロードされました", + "file_navigator_text": "ファイルの参照", + "first": "最初", + "from": "から", + "gradient": "変化の割合", + "guildbanner": "バナー", + "guildicon": "アイコン", + "installed": "インストール済み", + "last": "最後", + "left": "左", + "loading": "{{var0} を読み込んでいます}", + "location": "位置", + "order": "順序", + "outdated": "古いバージョン", + "please_wait": "お待ちください", + "right": "右", + "save_fail": "{{var0}}は保存できませんでした", + "save_success": "{{var0}}が正常に保存されました", + "send": "{{var0}}を送信します", + "server": "サーバー", + "settings_shareData_description": "プラグイン構成の同期", + "settings_showSupportBadges_description": "私へ寄付いただいたユーザーに小さなバッジを表示します", + "settings_showToasts_description": "プラグインの開始と停止のトースト通知を表示します", + "settings_showToasts_note": "先にBD の一般設定の '{{var0}}' を無効にしてください", + "settings_toastPosition_description": "デフォルトのトースト通知の位置", + "settings_toastPosition_note": "プラグインによるトーストの位置のみを変更します", + "settings_useChromium_description": "ブラウザの代わりにDiscord内でリンクを開く", + "sort_by": "並べ替え", + "status": "状態", + "time": "時間", + "timezone": "タイムゾーン", + "to": "に", + "toast_plugin_loaded": "{{var0}} が読み込まれました", + "toast_plugin_started": "{{var0}} を開始しました", + "toast_plugin_stopped": "{{var0}} を停止しました", + "toast_plugin_translated": "{{var0}} に変換されました", + "toast_plugin_unloaded": "{{var0}} をアンロードしました", + "toast_plugin_update_failed": "{{var0}} の更新に失敗しました", + "toast_plugin_updated": "{{var0}} {{var1}} は {{var2}} {{var3}} に置き換えられました", + "update_check_complete": "プラグインの更新チェックが完了しました", + "update_check_complete_outdated": "プラグインの更新チェックが完了しました- {{var0}} が古くなっています!", + "update_check_info": "アップデート機能を持つプラグインをチェックします", + "update_notice_click": "クリックして更新!", + "update_notice_reload": "リロードして更新を完了します", + "update_notice_update": "プラグインを更新する必要があります。", + "updated": "更新が完了しました" + }, + "ko": { + "add_to": "{{var0}}에 추가", + "ascending": "오름차순", + "center": "중앙", + "changelog_added": "새로운 기능", + "changelog_fixed": "문제 해결", + "changelog_improved": "개선됨", + "changelog_progress": "진행중", + "check_for_updates": "업데이트 확인", + "clipboard_success": "{{var0}}을(를) 클립보드에 복사했습니다.", + "confirm": "확실한가요?", + "copy": "{{var0}} 복사", + "delete_fail": "{{var0}}을(를) 삭제할 수 없습니다.", + "delete_success": "{{var0}}이(가) 성공적으로 삭제되었습니다.", + "descending": "내림차순", + "developer": "개발자", + "donate_message": "더 많은 업데이트를 위해 저를 후원해주세요!", + "download": "다운로드", + "download_fail": "{{var0}}을(를) 다운로드 할 수 없습니다.", + "download_success": "{{var0}}을(를) 다운로드 완료했습니다", + "file_navigator_text": "파일 찾아보기", + "first": "먼저", + "from": "에서", + "gradient": "그라데이션", + "guildbanner": "배너", + "guildicon": "상", + "installed": "설치됨", + "last": "마지막", + "left": "좌측", + "loading": "로드 중 {{var0}}", + "location": "위치", + "order": "순서", + "outdated": "구버전", + "please_wait": "잠시만 기다려주세요", + "right": "우측", + "save_fail": "{{var0}}을(를) 저장할 수 없습니다.", + "save_success": "{{var0}}이(가) 성공적으로 저장되었습니다.", + "send": "{{var0}} 보내기", + "server": "서버", + "settings_shareData_description": "Discord 계정 간에 플러그인 구성을 동기화합니다.", + "settings_showSupportBadges_description": "개발자의 Patreon으로 후원해주는 사용자에게 작은 배지를 표시합니다.", + "settings_showToasts_description": "플러그인 시작 및 중지 알림 표시", + "settings_showToasts_note": "BD를 비활성화하기 전에 BD를 일반 설정 '{{var0}}'을(를) 비활성화하세요.", + "settings_toastPosition_description": "기본 토스트창(Toast) 위치", + "settings_toastPosition_note": "위 옵션은 내 플러그인에 의해 생성된 토스트창에만 해당됩니다.", + "settings_useChromium_description": "브라우저 대신 Discord 에서 링크 열기", + "sort_by": "정렬 기준", + "status": "상태", + "time": "시각", + "timezone": "시간대", + "to": "에", + "toast_plugin_loaded": "{{var0}}이(가) 로드되었습니다.", + "toast_plugin_started": "{{var0}}이(가) 시작되었습니다.", + "toast_plugin_stopped": "{{var0}}이(가) 중지되었습니다.", + "toast_plugin_translated": "{{var0}} 로 번역됨", + "toast_plugin_unloaded": "{{var0}}이(가) 언로드되었습니다.", + "toast_plugin_update_failed": "{{var0}} 에 대한 업데이트를 다운로드 할 수 없습니다.", + "toast_plugin_updated": "{{var0}} {{var1}}이(가) {{var2}} {{var3}}으(로) 교체되었습니다.", + "update_check_complete": "플러그인 업데이트 확인 완료", + "update_check_complete_outdated": "플러그인 업데이트 확인 완료 - {{var0}}이(가) 구버전입니다!", + "update_check_info": "업데이트 기능을 지원하는 모든 플러그인 업데이트", + "update_notice_click": "업데이트하려면 클릭하세요!", + "update_notice_reload": "업데이트를 완료하려면 새로고침하세요.", + "update_notice_update": "다음 플러그인을 업데이트해야합니다: ", + "updated": "업데이트됨" + }, + "lt": { + "add_to": "Pridėti prie {{var0}}", + "ascending": "Kylanti", + "center": "Centruotas", + "changelog_added": "Naujos savybės", + "changelog_fixed": "Problemų sprendimas", + "changelog_improved": "Patobulinimai", + "changelog_progress": "Progresas", + "check_for_updates": "Tikrinti, ar yra atnaujinimų", + "clipboard_success": "{{var0}} nukopijuotas į mainų sritį", + "confirm": "Ar tu tuo tikras?", + "copy": "Kopijuoti {{var0}}", + "delete_fail": "{{var0}} negalima ištrinti", + "delete_success": "{{var0}} sėkmingai ištrinta", + "descending": "Mažėjantis", + "developer": "Programuotojas", + "donate_message": "Palaikykite mane, kad gautumėte daugiau naujinių!", + "download": "Parsisiųsti", + "download_fail": "{{var0}} negalima atsisiųsti", + "download_success": "{{var0}} sėkmingai atsisiųsta", + "file_navigator_text": "Naršyti failą", + "first": "Pirmas", + "from": "Nuo", + "gradient": "Gradientas", + "guildbanner": "Reklamjuostė", + "guildicon": "Piktograma", + "installed": "Įdiegta", + "last": "Paskutinis", + "left": "Kairėje", + "loading": "Įkeliama {{var0}}", + "location": "Vieta", + "order": "Seka", + "outdated": "Pasenęs", + "please_wait": "Maloniai palauk", + "right": "Teisingai", + "save_fail": "{{var0}} negalima išsaugoti", + "save_success": "{{var0}} išsaugota sėkmingai", + "send": "Siųsti {{var0}}", + "server": "Serverio", + "settings_shareData_description": "Sinchronizuoja įskiepio konfigūracijas tarp „Discord“ paskyrų", + "settings_showSupportBadges_description": "Rodo mažus ženklelius vartotojams, palaikantiems mano Patreon", + "settings_showToasts_description": "Parodo įskiepių paleidimo ir sustabdymo tostus", + "settings_showToasts_note": "Prieš išjungdami, išjunkite bendrą BD nustatymą '{{var0}}'", + "settings_toastPosition_description": "Numatytoji skrudintos duonos padėtis", + "settings_toastPosition_note": "Keičia tik mano įskiepiai sukurtų tostų poziciją", + "settings_useChromium_description": "Vietoj savo naršyklės atidarykite nuorodas nesantaikoje", + "sort_by": "Rūšiuoti pagal", + "status": "Būsena", + "time": "Laikas", + "timezone": "Laiko zona", + "to": "Į", + "toast_plugin_loaded": "{{var0}} buvo įkelta", + "toast_plugin_started": "{{var0}} prasidėjo", + "toast_plugin_stopped": "{{var0}} sustabdyta", + "toast_plugin_translated": "išversta į {{var0}}", + "toast_plugin_unloaded": "{{var0}} buvo iškrautas", + "toast_plugin_update_failed": "Negalima atsisiųsti {{var0}} naujinio", + "toast_plugin_updated": "{{var0}} {{var1}} pakeista į {{var2}} {{var3}}", + "update_check_complete": "Įskiepių atnaujinimo patikrinimas baigtas", + "update_check_complete_outdated": "Įskiepių atnaujinimo patikrinimas baigtas - {{var0}} pasenęs!", + "update_check_info": "Patikrinkite įskiepiai, kurie palaiko naujinimo patikrą", + "update_notice_click": "Spustelėkite norėdami atnaujinti!", + "update_notice_reload": "Įkelkite iš naujo, kad užbaigtumėte atnaujinimą", + "update_notice_update": "Reikia atnaujinti šiuos įskiepiai: ", + "updated": "Atnaujinta" + }, + "nl": { + "add_to": "Toevoegen aan {{var0}}", + "ascending": "Oplopend", + "center": "Gecentreerd", + "changelog_added": "Nieuwe features", + "changelog_fixed": "Probleemoplossen", + "changelog_improved": "Verbeteringen", + "changelog_progress": "Vooruitgang", + "check_for_updates": "Controleer op updates", + "clipboard_success": "{{var0}} gekopieerd naar klembord", + "confirm": "Weet je zeker dat?", + "copy": "Kopieer {{var0}}", + "delete_fail": "{{var0}} kan niet worden verwijderd", + "delete_success": "{{var0}} is verwijderd", + "descending": "Aflopend", + "developer": "Ontwikkelaar", + "donate_message": "Steun mij voor meer updates!", + "download": "Downloaden", + "download_fail": "{{var0}} kan niet worden gedownload", + "download_success": "{{var0}} succesvol gedownload", + "file_navigator_text": "Bestand zoeken", + "first": "Eerste", + "from": "Van", + "gradient": "Verloop", + "guildbanner": "Banner", + "guildicon": "Icoon", + "installed": "Geïnstalleerd", + "last": "Laatste", + "left": "Links", + "loading": "Laden {{var0}}", + "location": "Plaats", + "order": "Volgorde", + "outdated": "Verouderd", + "please_wait": "Wacht even", + "right": "Rechtsaf", + "save_fail": "{{var0}} kan niet worden opgeslagen", + "save_success": "{{var0}} succesvol opgeslagen", + "send": "Stuur {{var0}}", + "server": "Server", + "settings_shareData_description": "Synchroniseert de plug-inconfiguraties tussen Discord-accounts", + "settings_showSupportBadges_description": "Toont kleine badges voor gebruikers die mijn Patreon ondersteunen", + "settings_showToasts_description": "Toont plugin start en stop toasts", + "settings_showToasts_note": "Schakel de algemene instelling '{{var0}}' van BD's uit voordat u ze uitschakelt", + "settings_toastPosition_description": "Standaard toastpositie", + "settings_toastPosition_note": "Verandert alleen de positie van toast gemaakt door mijn plugins", + "settings_useChromium_description": "Open links in Discord in plaats van uw browser", + "sort_by": "Sorteer op", + "status": "Toestand", + "time": "Tijd", + "timezone": "Tijdzone", + "to": "Naar", + "toast_plugin_loaded": "{{var0}} is geladen", + "toast_plugin_started": "{{var0}} is gestart", + "toast_plugin_stopped": "{{var0}} is gestopt", + "toast_plugin_translated": "vertaald naar {{var0}}", + "toast_plugin_unloaded": "{{var0}} is verwijderd", + "toast_plugin_update_failed": "Update voor {{var0}} kan niet worden gedownload", + "toast_plugin_updated": "{{var0}} {{var1}} is vervangen door {{var2}} {{var3}}", + "update_check_complete": "Controle op update van plugin voltooid", + "update_check_complete_outdated": "Controle van update van plugin voltooid - {{var0}} verouderd!", + "update_check_info": "Controleer plugins die de updatecontrole ondersteunen", + "update_notice_click": "Klik om te updaten!", + "update_notice_reload": "Laad opnieuw om de update te voltooien", + "update_notice_update": "De volgende plugins moeten worden bijgewerkt: ", + "updated": "Bijgewerkt" + }, + "no": { + "add_to": "Legg til i {{var0}}", + "ascending": "Stigende", + "center": "Sentrert", + "changelog_added": "Nye funksjoner", + "changelog_fixed": "Feilsøking", + "changelog_improved": "Forbedringer", + "changelog_progress": "Framgang", + "check_for_updates": "Se etter oppdateringer", + "clipboard_success": "Kopierte {{var0}} til utklippstavlen", + "confirm": "Er du sikker?", + "copy": "Kopier {{var0}}", + "delete_fail": "{{var0}} kan ikke slettes", + "delete_success": "{{var0}} ble slettet", + "descending": "Fallende", + "developer": "Utvikler", + "donate_message": "Støtt meg for flere oppdateringer!", + "download": "Nedlasting", + "download_fail": "{{var0}} kan ikke lastes ned", + "download_success": "{{var0}} lastet ned", + "file_navigator_text": "Bla gjennom filen", + "first": "Først", + "from": "Fra", + "gradient": "Gradient", + "guildbanner": "Banner", + "guildicon": "Ikon", + "installed": "Installert", + "last": "Siste", + "left": "Venstre", + "loading": "Laster inn {{var0}}", + "location": "Plassering", + "order": "Sekvens", + "outdated": "Utdatert", + "please_wait": "Vennligst vent", + "right": "Ikke sant", + "save_fail": "{{var0}} kan ikke lagres", + "save_success": "{{var0}} lagret", + "send": "Send {{var0}}", + "server": "Server", + "settings_shareData_description": "Synkroniserer plugin-konfigurasjonene mellom Discord-kontoer", + "settings_showSupportBadges_description": "Viser små merker for brukere som støtter min Patreon", + "settings_showToasts_description": "Viser plugin start og stopp toasts", + "settings_showToasts_note": "Deaktiver den generelle innstillingen '{{var0}}' for BD-er før du deaktiverer dem", + "settings_toastPosition_description": "Standard toastposisjon", + "settings_toastPosition_note": "Endrer bare posisjonen for ristet brød opprettet av plugins mine", + "settings_useChromium_description": "Åpne koblinger i Discord i stedet for nettleseren din", + "sort_by": "Sorter etter", + "status": "Status", + "time": "Tid", + "timezone": "Tidssone", + "to": "Til", + "toast_plugin_loaded": "{{var0}} er lastet inn", + "toast_plugin_started": "{{var0}} har startet", + "toast_plugin_stopped": "{{var0}} har stoppet", + "toast_plugin_translated": "oversatt til {{var0}}", + "toast_plugin_unloaded": "{{var0}} er lastet ut", + "toast_plugin_update_failed": "Oppdatering for {{var0}} kan ikke lastes ned", + "toast_plugin_updated": "{{var0}} {{var1}} er erstattet av {{var2}} {{var3}}", + "update_check_complete": "Sjekk plugin-oppdatering fullført", + "update_check_complete_outdated": "Sjekk plugin-oppdatering fullført - {{var0}} utdatert!", + "update_check_info": "Sjekk plugins som støtter oppdateringskontrollen", + "update_notice_click": "Klikk for å oppdatere!", + "update_notice_reload": "Last inn for å fullføre oppdateringen", + "update_notice_update": "Følgende plugins må oppdateres: ", + "updated": "Oppdatert" + }, + "pl": { + "add_to": "Dodaj do {{var0}}", + "ascending": "Rosnąco", + "center": "Środek", + "changelog_added": "Dodano", + "changelog_fixed": "Naprawiono", + "changelog_improved": "Ulepszono", + "changelog_progress": "Postęp", + "check_for_updates": "Sprawdź aktualizacje", + "clipboard_success": "Skopiowano {{var0}} do schowka", + "confirm": "Na pewno?", + "copy": "Kopiuj {{var0}}", + "delete_fail": "Nie można usunąć {{var0}}", + "delete_success": "Pomyślnie usunięto {{var0}}", + "descending": "Malejąco", + "developer": "Deweloper", + "donate_message": "Wesprzyj mnie, aby uzyskać więcej aktualizacji!", + "download": "Pobieranie", + "download_fail": "Nie można pobrać {{var0}}", + "download_success": "Pomyślnie pobrano {{var0}}", + "file_navigator_text": "Przeglądaj plik", + "first": "Pierwszy", + "from": "Od", + "gradient": "Gradient", + "guildbanner": "Transparent", + "guildicon": "Ikona", + "installed": "Zainstalowany", + "last": "Ostatni", + "left": "Lewo", + "loading": "Ładowanie {{var0}}", + "location": "Lokalizacja", + "order": "Kolejność", + "outdated": "Przestarzały", + "please_wait": "Proszę czekać", + "right": "Prawo", + "save_fail": "Nie można zapisać {{var0}}", + "save_success": "Pomyślnie zapisano {{var0}}", + "send": "Wyślij {{var0}}", + "server": "Serwer", + "settings_shareData_description": "Synchronizuje konfiguracje wtyczek między kontami Discord", + "settings_showSupportBadges_description": "Pokazuje małe odznaki dla wspierających mojego Patreona", + "settings_showToasts_description": "Pokazuje toasty uruchamiające i zatrzymujące wtyczki", + "settings_showToasts_note": "Wyłącz ogólne ustawienie '{{var0}} ” dysków BD przed ich wyłączeniem", + "settings_toastPosition_description": "Domyślna pozycja toastów", + "settings_toastPosition_note": "Zmienia tylko pozycję toastów utworzonych przez moje wtyczki", + "settings_useChromium_description": "Otwieraj linki w Discordzie zamiast w przeglądarce", + "sort_by": "Sortuj według", + "status": "Status", + "time": "Czas", + "timezone": "Strefa czasowa", + "to": "Do", + "toast_plugin_loaded": "Załadowano {{var0}}", + "toast_plugin_started": "Rozpoczęto {{var0}}", + "toast_plugin_stopped": "{{var0}} został zatrzymany", + "toast_plugin_translated": "przetłumaczone na {{var0}}", + "toast_plugin_unloaded": "Usunięto {{var0}}", + "toast_plugin_update_failed": "Nie można pobrać aktualizacji dla {{var0}}", + "toast_plugin_updated": "{{var0}} {{var1}} został zastąpiony przez {{var2}} {{var3}}", + "update_check_complete": "Sprawdzanie aktualizacji wtyczki zakończone", + "update_check_complete_outdated": "Sprawdzanie aktualizacji wtyczki zakończone - {{var0}} nieaktualne!", + "update_check_info": "Sprawdź wtyczki obsługujące sprawdzanie aktualizacji", + "update_notice_click": "Kliknij, aby zaktualizować!", + "update_notice_reload": "Załaduj ponownie, aby zakończyć aktualizację", + "update_notice_update": "Należy zaktualizować następujące wtyczki: ", + "updated": "Zaktualizowano" + }, + "pt-BR": { + "add_to": "Adicionar a {{var0}}", + "ascending": "Ascendente", + "center": "Centrado", + "changelog_added": "Novas características", + "changelog_fixed": "Solução de problemas", + "changelog_improved": "Melhorias", + "changelog_progress": "Progresso", + "check_for_updates": "Verifique se há atualizações", + "clipboard_success": "Copiado {{var0}} para a área de transferência", + "confirm": "Você tem certeza?", + "copy": "Copiar {{var0}}", + "delete_fail": "{{var0}} não pode ser excluído", + "delete_success": "{{var0}} excluído com sucesso", + "descending": "Descendente", + "developer": "Desenvolvedor", + "donate_message": "Apoie-me para mais atualizações!", + "download": "Baixar", + "download_fail": "{{var0}} não pode ser baixado", + "download_success": "{{var0}} baixado com sucesso", + "file_navigator_text": "Procurar arquivo", + "first": "Primeiro", + "from": "De", + "gradient": "Gradiente", + "guildbanner": "Bandeira", + "guildicon": "Ícone", + "installed": "Instalado", + "last": "Último", + "left": "Esquerda", + "loading": "Carregando {{var0}}", + "location": "Localização", + "order": "Seqüência", + "outdated": "Desatualizado", + "please_wait": "Por favor espere", + "right": "Direito", + "save_fail": "{{var0}} não pode ser salvo", + "save_success": "{{var0}} salvo com sucesso", + "send": "Enviar {{var0}}", + "server": "Servidor", + "settings_shareData_description": "Sincroniza as configurações de plug-in entre contas do Discord", + "settings_showSupportBadges_description": "Mostra pequenos emblemas para usuários que apóiam meu Patreon", + "settings_showToasts_description": "Mostra o início e o fim do plugin do brinde", + "settings_showToasts_note": "Desative a configuração geral '{{var0}}' de BDs antes de desativá-los", + "settings_toastPosition_description": "Posição padrão do brinde", + "settings_toastPosition_note": "Apenas altera a posição dos brindes criados pelos meus plugins", + "settings_useChromium_description": "Abra links no Discord em vez do seu navegador", + "sort_by": "Ordenar por", + "status": "Status", + "time": "Tempo", + "timezone": "Fuso horário", + "to": "Para", + "toast_plugin_loaded": "{{var0}} foi carregado", + "toast_plugin_started": "{{var0}} começou", + "toast_plugin_stopped": "{{var0}} parou", + "toast_plugin_translated": "traduzido para {{var0}}", + "toast_plugin_unloaded": "{{var0}} foi descarregado", + "toast_plugin_update_failed": "A atualização para {{var0}} não pode ser baixada", + "toast_plugin_updated": "{{var0}} {{var1}} foi substituído por {{var2}} {{var3}}", + "update_check_complete": "Verificação de atualização do plugin concluída", + "update_check_complete_outdated": "Verificação de atualização do plugin concluída - {{var0}} desatualizado!", + "update_check_info": "Verifique os plugins que suportam a verificação de atualização", + "update_notice_click": "Clique para atualizar!", + "update_notice_reload": "Recarregue para completar a atualização", + "update_notice_update": "Os seguintes plugins precisam ser atualizados: ", + "updated": "Atualizada" + }, + "ro": { + "add_to": "Adăugați la {{var0}}", + "ascending": "Ascendent", + "center": "Centrat", + "changelog_added": "Functii noi", + "changelog_fixed": "Depanare", + "changelog_improved": "Îmbunătățiri", + "changelog_progress": "Progres", + "check_for_updates": "Verifică pentru actualizări", + "clipboard_success": "{{var0}} a fost copiat în Clipboard", + "confirm": "Esti sigur?", + "copy": "Copiați {{var0}}", + "delete_fail": "{{var0}} nu poate fi șters", + "delete_success": "{{var0}} șters cu succes", + "descending": "Descendentă", + "developer": "Dezvoltator", + "donate_message": "Sprijină-mă pentru mai multe actualizări!", + "download": "Descarca", + "download_fail": "{{var0}} nu poate fi descărcat", + "download_success": "{{var0}} descărcat cu succes", + "file_navigator_text": "Răsfoiți fișierul", + "first": "Primul", + "from": "Din", + "gradient": "Gradient", + "guildbanner": "Banner", + "guildicon": "Pictogramă", + "installed": "Instalat", + "last": "Ultimul", + "left": "Stânga", + "loading": "Se încarcă {{var0}}", + "location": "Locație", + "order": "Secvenţă", + "outdated": "Învechit", + "please_wait": "Așteptați plăcut", + "right": "Dreapta", + "save_fail": "{{var0}} nu poate fi salvat", + "save_success": "{{var0}} salvat cu succes", + "send": "Trimite {{var0}}", + "server": "Server", + "settings_shareData_description": "Sincronizează configurațiile pluginurilor între conturile Discord", + "settings_showSupportBadges_description": "Afișează insigne mici pentru utilizatorii care acceptă Patreon", + "settings_showToasts_description": "Afișează pornirea și oprirea toastelor pluginului", + "settings_showToasts_note": "Dezactivați setarea generală '{{var0}} ” a BD-urilor înainte de a le dezactiva", + "settings_toastPosition_description": "Poziție implicită Toast", + "settings_toastPosition_note": "Modifică doar poziția toastelor create de pluginuri mele", + "settings_useChromium_description": "Deschideți linkuri în discordie în loc de browser", + "sort_by": "Filtrează după", + "status": "Stare", + "time": "Timp", + "timezone": "Fus orar", + "to": "La", + "toast_plugin_loaded": "{{var0}} a fost încărcat", + "toast_plugin_started": "{{var0}} a început", + "toast_plugin_stopped": "{{var0}} s-a oprit", + "toast_plugin_translated": "tradus în {{var0}}", + "toast_plugin_unloaded": "{{var0}} a fost descărcat", + "toast_plugin_update_failed": "Actualizarea pentru {{var0}} nu poate fi descărcată", + "toast_plugin_updated": "{{var0}} {{var1}} a fost înlocuit cu {{var2}} {{var3}}", + "update_check_complete": "Verificarea actualizării pluginului s-a finalizat", + "update_check_complete_outdated": "Verificarea actualizării pluginului s-a finalizat - {{var0}} nu este actualizată!", + "update_check_info": "Verificați pluginuri care acceptă verificarea actualizării", + "update_notice_click": "Faceți clic pentru a actualiza!", + "update_notice_reload": "Reîncărcați pentru a finaliza actualizarea", + "update_notice_update": "Următoarele pluginuri trebuie actualizate: ", + "updated": "La curent" + }, + "ru": { + "add_to": "Добавить в {{var0}}", + "ascending": "Возрастанию", + "center": "По центру", + "changelog_added": "Новые возможности", + "changelog_fixed": "Исправления ошибок", + "changelog_improved": "Улучшения", + "changelog_progress": "Прогресс", + "check_for_updates": "Проверить наличие обновлений", + "clipboard_success": "Скопировано {{var0}} в буфер обмена", + "confirm": "Подтвердить?", + "copy": "Копировать {{var0}}", + "delete_fail": "Не удалось удалить: {{var0}}", + "delete_success": "Успешно удалено: {{var0}}", + "descending": "Убыванию", + "developer": "Разработчик", + "donate_message": "Поддержите меня, чтобы получать дальнейшие обновления!", + "download": "Скачать", + "download_fail": "Не удалось скачать: {{var0}}", + "download_success": "Успешно скачено: {{var0}}", + "file_navigator_text": "Посмотреть файл", + "first": "Первый", + "from": "Из", + "gradient": "Градиент", + "guildbanner": "Баннер", + "guildicon": "Икона", + "installed": "Установлено", + "last": "Последний", + "left": "Слева", + "loading": "Загрузка {{var0}}", + "location": "Расположение", + "order": "Порядок", + "outdated": "Устаревшее", + "please_wait": "Подождите", + "right": "Справа", + "save_fail": "Не удалось сохранить: {{var0}}", + "save_success": "Успешно сохранено: {{var0}}", + "send": "Отправить {{var0}}", + "server": "Сервер", + "settings_shareData_description": "Синхронизирует конфигурации плагинов между учетными записями Discord.", + "settings_showSupportBadges_description": "Показывать маленькие значки у пользователей, которые поддерживают мой Patreon", + "settings_showToasts_description": "Показывать всплывающие уведомления запуска и остановки плагинов", + "settings_showToasts_note": "Отключите общий параметр BD '{{var0}}' перед отключением этого параметра", + "settings_toastPosition_description": "Местоположение всплывающего уведомления по умолчанию", + "settings_toastPosition_note": "Изменяет только положение всплывающих уведомлений, созданных моими плагинами", + "settings_useChromium_description": "Открывайте ссылки через Discord вместо браузера", + "sort_by": "Сортировать по", + "status": "Состояние", + "time": "Время", + "timezone": "Часовой пояс", + "to": "В", + "toast_plugin_loaded": "{{var0}} загружен", + "toast_plugin_started": "{{var0}} запущен", + "toast_plugin_stopped": "{{var0}} остановлен", + "toast_plugin_translated": "переведён на {{var0}}", + "toast_plugin_unloaded": "{{var0}} выгружен", + "toast_plugin_update_failed": "Обновление для {{var0}} не может быть загружено", + "toast_plugin_updated": "{{var0}} {{var1}} заменён на {{var2}} {{var3}}", + "update_check_complete": "Проверка плагинов на обновления завершена", + "update_check_complete_outdated": "Проверка плагинов на обновления завершена - {{var0}} устарел!", + "update_check_info": "Проверьте поддерживаемые плагины на обновления", + "update_notice_click": "Нажмите, чтобы обновить!", + "update_notice_reload": "Перезагрузите, чтобы завершить обновление", + "update_notice_update": "Необходимо обновить следующие плагины: ", + "updated": "Обновлено" + }, + "sv": { + "add_to": "Lägg till i {{var0}}", + "ascending": "Stigande", + "center": "Centrerad", + "changelog_added": "Nya egenskaper", + "changelog_fixed": "Felsökning", + "changelog_improved": "Förbättringar", + "changelog_progress": "Framsteg", + "check_for_updates": "Sök efter uppdateringar", + "clipboard_success": "Kopierade {{var0}} till Urklipp", + "confirm": "Är du säker?", + "copy": "Kopiera {{var0}}", + "delete_fail": "{{var0}} kan inte raderas", + "delete_success": "{{var0}} har tagits bort", + "descending": "Nedåtgående", + "developer": "Utvecklaren", + "donate_message": "Stöd mig för fler uppdateringar!", + "download": "Ladda ner", + "download_fail": "{{var0}} kan inte laddas ner", + "download_success": "{{var0}} laddades ner", + "file_navigator_text": "Bläddra i filen", + "first": "Först", + "from": "Från", + "gradient": "Lutning", + "guildbanner": "Baner", + "guildicon": "Ikon", + "installed": "Installerat", + "last": "Sista", + "left": "Vänster", + "loading": "Laddar {{var0}}", + "location": "Plats", + "order": "Sekvens", + "outdated": "Föråldrad", + "please_wait": "Vänligen vänta", + "right": "Höger", + "save_fail": "{{var0}} kan inte sparas", + "save_success": "{{var0}} har sparats", + "send": "Skicka {{var0}}", + "server": "Server", + "settings_shareData_description": "Synkroniserar plugin-konfigurationerna mellan Discord-konton", + "settings_showSupportBadges_description": "Visar små märken för användare som stöder min Patreon", + "settings_showToasts_description": "Visar plugin start och stopp toasts", + "settings_showToasts_note": "Inaktivera den allmänna inställningen '{{var0}}' för BD-skivor innan du inaktiverar dem", + "settings_toastPosition_description": "Standard toastposition", + "settings_toastPosition_note": "Ändrar bara positionen för rostat bröd som skapats av mina plugins", + "settings_useChromium_description": "Öppna Länkar i Discord istället för din webbläsare", + "sort_by": "Sortera efter", + "status": "Status", + "time": "Tid", + "timezone": "Tidszon", + "to": "Till", + "toast_plugin_loaded": "{{var0}} har laddats", + "toast_plugin_started": "{{var0}} har startat", + "toast_plugin_stopped": "{{var0}} har slutat", + "toast_plugin_translated": "översatt till {{var0}}", + "toast_plugin_unloaded": "{{var0}} har lossats", + "toast_plugin_update_failed": "Uppdatering för {{var0}} kan inte laddas ner", + "toast_plugin_updated": "{{var0}} {{var1}} har ersatts med {{var2}} {{var3}}", + "update_check_complete": "Kontrollen av plugin-uppdateringen slutförd", + "update_check_complete_outdated": "Kontrollen av plugin-uppdateringen slutförd - {{var0}} inaktuell!", + "update_check_info": "Kontrollera plugins som stöder uppdateringskontrollen", + "update_notice_click": "Klicka för att uppdatera!", + "update_notice_reload": "Ladda om för att slutföra uppdateringen", + "update_notice_update": "Följande plugins måste uppdateras: ", + "updated": "Uppdaterad" + }, + "th": { + "add_to": "เพิ่มใน{{var0}}", + "ascending": "จากน้อยไปมาก", + "center": "อยู่กึ่งกลาง", + "changelog_added": "คุณสมบัติใหม่", + "changelog_fixed": "การแก้ไขปัญหา", + "changelog_improved": "การปรับปรุง", + "changelog_progress": "ความคืบหน้า", + "check_for_updates": "ตรวจสอบสำหรับการอัพเดต", + "clipboard_success": "คัดลอก {{var0}} ไปยังคลิปบอร์ดแล้ว", + "confirm": "คุณแน่ใจไหม?", + "copy": "คัดลอก {{var0}}", + "delete_fail": "ไม่สามารถลบ{{var0}}ได้", + "delete_success": "ลบ{{var0}}เรียบร้อยแล้ว", + "descending": "จากมากไปน้อย", + "developer": "ผู้พัฒนา", + "donate_message": "สนับสนุนฉันสำหรับการอัปเดตเพิ่มเติม!", + "download": "ดาวน์โหลด", + "download_fail": "ไม่สามารถดาวน์โหลด{{var0}}", + "download_success": "ดาวน์โหลด{{var0}}สำเร็จ", + "file_navigator_text": "เรียกดูไฟล์", + "first": "อันดับแรก", + "from": "จาก", + "gradient": "ไล่ระดับสี", + "guildbanner": "แบนเนอร์", + "guildicon": "ไอคอน", + "installed": "ติดตั้งแล้ว", + "last": "ล่าสุด", + "left": "ซ้าย", + "loading": "กำลังโหลด{{var0}}", + "location": "สถานที่", + "order": "ลำดับ", + "outdated": "เก่า", + "please_wait": "โปรดรอ", + "right": "ขวา", + "save_fail": "ไม่สามารถบันทึก{{var0}}ได้", + "save_success": "บันทึก{{var0}}เรียบร้อยแล้ว", + "send": "ส่ง {{var0}}", + "server": "เซิร์ฟเวอร์", + "settings_shareData_description": "ประสานการกำหนดค่าปลั๊กอินระหว่างบัญชีที่ไม่ลงรอยกัน", + "settings_showSupportBadges_description": "แสดงป้ายขนาดเล็กสำหรับผู้ใช้ที่สนับสนุน Patreon ของฉัน", + "settings_showToasts_description": "แสดงปลั๊กอินเริ่มและหยุดขนมปังปิ้ง", + "settings_showToasts_note": "ปิดการใช้งานการตั้งค่าทั่วไป '{{var0}}' ของ BD ก่อนปิดใช้งาน", + "settings_toastPosition_description": "ตำแหน่งขนมปังเริ่มต้น", + "settings_toastPosition_note": "เปลี่ยนเฉพาะตำแหน่งของขนมปังที่สร้างโดยปลั๊กอินของฉัน", + "settings_useChromium_description": "เปิดลิงค์ใน Discord แทนเบราว์เซอร์ของคุณ", + "sort_by": "จัดเรียงตาม", + "status": "สถานะ", + "time": "เวลา", + "timezone": "เขตเวลา", + "to": "ถึง", + "toast_plugin_loaded": "โหลด {{var0}} แล้ว", + "toast_plugin_started": "{{var0}} เริ่มแล้ว", + "toast_plugin_stopped": "{{var0}} หยุดทำงาน", + "toast_plugin_translated": "แปลเป็น {{var0}}", + "toast_plugin_unloaded": "ยกเลิกการโหลด {{var0}} แล้ว", + "toast_plugin_update_failed": "ไม่สามารถดาวน์โหลดการอัปเดตสำหรับ {{var0}}", + "toast_plugin_updated": "{{var0}} {{var1}} ถูกแทนที่ด้วย {{var2}} {{var3}}", + "update_check_complete": "การตรวจสอบการอัปเดตปลั๊กอินเสร็จสมบูรณ์", + "update_check_complete_outdated": "การตรวจสอบการอัปเดตปลั๊กอินเสร็จสมบูรณ์ - {{var0}} ล้าสมัย!", + "update_check_info": "ตรวจสอบปลั๊กอินที่รองรับการตรวจสอบการอัปเดต", + "update_notice_click": "คลิกเพื่ออัพเดท!", + "update_notice_reload": "โหลดซ้ำเพื่ออัปเดตให้เสร็จสมบูรณ์", + "update_notice_update": "จำเป็นต้องอัปเดตปลั๊กอินต่อไปนี้: ", + "updated": "อัปเดตแล้ว" + }, + "tr": { + "add_to": "{{var0}} ekle", + "ascending": "Artan", + "center": "Ortalanmış", + "changelog_added": "Yeni özellikler", + "changelog_fixed": "Sorun giderme", + "changelog_improved": "İyileştirmeler", + "changelog_progress": "Ilerleme", + "check_for_updates": "Güncellemeleri kontrol et", + "clipboard_success": "{{var0}} Panoya kopyalandı", + "confirm": "Emin misiniz?", + "copy": "{{var0}} kopyala", + "delete_fail": "{{var0}} silinemez", + "delete_success": "{{var0}} başarıyla silindi", + "descending": "Azalan", + "developer": "Geliştirici", + "donate_message": "Daha fazla güncelleme için beni destekleyin!", + "download": "İndir", + "download_fail": "{{var0}} indirilemez", + "download_success": "{{var0}} başarıyla indirildi", + "file_navigator_text": "Dosyaya Gözat", + "first": "İlk", + "from": "Nereden", + "gradient": "Gradyan", + "guildbanner": "Afiş", + "guildicon": "Simge", + "installed": "Kurulmuş", + "last": "Son", + "left": "Ayrıldı", + "loading": "Yükleniyor {{var0}}", + "location": "Yer", + "order": "Sıra", + "outdated": "Modası geçmiş", + "please_wait": "Lütfen bekle", + "right": "Sağ", + "save_fail": "{{var0}} kaydedilemez", + "save_success": "{{var0}} başarıyla kaydedildi", + "send": "{{var0}} gönder", + "server": "Sunucu", + "settings_shareData_description": "Eklenti Yapılandırmalarını Discord Hesapları arasında senkronize eder", + "settings_showSupportBadges_description": "Patreon'umu destekleyen kullanıcılar için küçük rozetler gösterir", + "settings_showToasts_description": "Eklenti başlangıç ​​ve bitiş tostlarını gösterir", + "settings_showToasts_note": "Devre dışı bırakmadan önce BD'lerin genel ayarını '{{var0}}' devre dışı bırakın", + "settings_toastPosition_description": "Varsayılan Tost Konumu", + "settings_toastPosition_note": "Yalnızca Eklentileri tarafından oluşturulan Toastların Konumunu değiştirir", + "settings_useChromium_description": "Tarayıcınız yerine Discord'da Bağlantıları Açın", + "sort_by": "Göre sırala", + "status": "Durum", + "time": "Zaman", + "timezone": "Saat dilimi", + "to": "İçin", + "toast_plugin_loaded": "{{var0}} yüklendi", + "toast_plugin_started": "{{var0}} başladı", + "toast_plugin_stopped": "{{var0}} durdu", + "toast_plugin_translated": "{{var0}} diline çevrildi", + "toast_plugin_unloaded": "{{var0}} kaldırıldı", + "toast_plugin_update_failed": "{{var0}} için güncelleme indirilemiyor", + "toast_plugin_updated": "{{var0}} {{var1}}, {{var2}} {{var3}} ile değiştirildi", + "update_check_complete": "Eklenti güncelleme kontrolü tamamlandı", + "update_check_complete_outdated": "Eklenti güncelleme kontrolü tamamlandı - {{var0}} güncel değil!", + "update_check_info": "Güncelleme Kontrolünü destekleyen Eklentileri kontrol edin", + "update_notice_click": "Güncellemek için tıklayın!", + "update_notice_reload": "Güncellemeyi tamamlamak için yeniden yükleyin", + "update_notice_update": "Aşağıdaki Eklentileri güncellenmesi gerekiyor: ", + "updated": "Güncellenmiş" + }, + "uk": { + "add_to": "Додати до {{var0}}", + "ascending": "Висхідний", + "center": "По центру", + "changelog_added": "Нові можливості", + "changelog_fixed": "Вирішення проблем", + "changelog_improved": "Покращення", + "changelog_progress": "Прогрес", + "check_for_updates": "Перевірити наявність оновлень", + "clipboard_success": "Скопійовано {{var0}} в буфер обміну", + "confirm": "Ти впевнений?", + "copy": "Копіювати {{var0}}", + "delete_fail": "{{var0}} не можна видалити", + "delete_success": "{{var0}} успішно видалено", + "descending": "За спаданням", + "developer": "Розробник", + "donate_message": "Підтримайте мене, щоб отримати більше оновлень!", + "download": "Завантажити", + "download_fail": "{{var0}} не можна завантажити", + "download_success": "{{var0}} завантажено успішно", + "file_navigator_text": "Переглянути файл", + "first": "Спочатку", + "from": "Від", + "gradient": "Градієнт", + "guildbanner": "Банер", + "guildicon": "Піктограма", + "installed": "Встановлено", + "last": "Останній", + "left": "Ліворуч", + "loading": "Завантаження {{var0}}", + "location": "Розташування", + "order": "Послідовність", + "outdated": "Застарілий", + "please_wait": "Задоволення почекайте", + "right": "Правильно", + "save_fail": "{{var0}} не можна зберегти", + "save_success": "{{var0}} успішно збережено", + "send": "Надіслати {{var0}}", + "server": "Сервер", + "settings_shareData_description": "Синхронізує конфігурації плагінів між обліковими записами Discord", + "settings_showSupportBadges_description": "Показує невеликі значки для користувачів, які підтримують мій Patreon", + "settings_showToasts_description": "Показує тости запуску та зупинки плагіна", + "settings_showToasts_note": "Вимкніть загальне налаштування '{{var0}}' BD, перш ніж їх вимикати", + "settings_toastPosition_description": "Позиція тостів за замовчуванням", + "settings_toastPosition_note": "Змінює лише позицію тостів, створених моїми плагіни", + "settings_useChromium_description": "Відкрийте посилання в Discord замість браузера", + "sort_by": "Сортувати за", + "status": "Статус", + "time": "Час", + "timezone": "Часовий пояс", + "to": "До", + "toast_plugin_loaded": "{{var0}} завантажено", + "toast_plugin_started": "{{var0}} розпочато", + "toast_plugin_stopped": "{{var0}} зупинено", + "toast_plugin_translated": "перекладено на {{var0}}", + "toast_plugin_unloaded": "{{var0}} вивантажено", + "toast_plugin_update_failed": "Не вдається завантажити оновлення для {{var0}}", + "toast_plugin_updated": "{{var0}} {{var1}} замінено на {{var2}} {{var3}}", + "update_check_complete": "Завершено перевірку оновлення плагіна", + "update_check_complete_outdated": "Завершено перевірку оновлення плагіна - {{var0}} застаріло!", + "update_check_info": "Перевірте плагіни, які підтримують перевірку оновлення", + "update_notice_click": "Натисніть, щоб оновити!", + "update_notice_reload": "Перезавантажте, щоб завершити оновлення", + "update_notice_update": "Потрібно оновити такі плагіни: ", + "updated": "Оновлено" + }, + "vi": { + "add_to": "Thêm vào {{var0}}", + "ascending": "Tăng dần", + "center": "Căn giữa", + "changelog_added": "Các tính năng mới", + "changelog_fixed": "Xử lý sự cố", + "changelog_improved": "Cải tiến", + "changelog_progress": "Phát triển", + "check_for_updates": "Kiểm tra cập nhật", + "clipboard_success": "Đã sao chép {{var0}} vào Clipboard", + "confirm": "Bạn có chắc không?", + "copy": "Sao chép {{var0}}", + "delete_fail": "{{var0}} không thể bị xóa", + "delete_success": "Đã xóa {{var0}} thành công", + "descending": "Giảm dần", + "developer": "Người phát triển", + "donate_message": "Hỗ trợ tôi để cập nhật thêm!", + "download": "Tải xuống", + "download_fail": "Không thể tải xuống {{var0}}", + "download_success": "Đã tải xuống {{var0}} thành công", + "file_navigator_text": "Chọn thư mục", + "first": "Đầu tiên", + "from": "Từ", + "gradient": "Dốc", + "guildbanner": "Ảnh bìa", + "guildicon": "Biểu tượng", + "installed": "Cài đặt", + "last": "Cuối cùng", + "left": "Trái", + "loading": "Đang tải {{var0}}", + "location": "Vị trí", + "order": "Sự nối tiếp", + "outdated": "Lỗi thời", + "please_wait": "Hân hạnh chờ đợi", + "right": "Đúng", + "save_fail": "{{var0}} không thể lưu được", + "save_success": "{{var0}} đã được lưu thành công", + "send": "Gửi cho {{var0}}", + "server": "Người phục vụ", + "settings_shareData_description": "Đồng bộ hóa cấu hình plugin giữa các tài khoản Discord", + "settings_showSupportBadges_description": "Hiển thị các huy hiệu nhỏ cho những người dùng ủng hộ Patreon của tôi", + "settings_showToasts_description": "Hiển thị plugin bắt đầu và dừng nâng cốc", + "settings_showToasts_note": "Tắt cài đặt chung '{{var0}}' của BD trước khi tắt chúng", + "settings_toastPosition_description": "Vị trí bánh mì nướng mặc định", + "settings_toastPosition_note": "Chỉ thay đổi Vị trí của Bánh nướng được tạo bởi plugins của tôi", + "settings_useChromium_description": "Mở Liên kết trong Discord thay vì Trình duyệt của bạn", + "sort_by": "Sắp xếp theo", + "status": "Trạng thái", + "time": "Thời gian", + "timezone": "Múi giờ", + "to": "Đến", + "toast_plugin_loaded": "{{var0}} đã được tải", + "toast_plugin_started": "{{var0}} đã bắt đầu", + "toast_plugin_stopped": "{{var0}} đã dừng", + "toast_plugin_translated": "đã dịch sang {{var0}}", + "toast_plugin_unloaded": "{{var0}} đã được tải xuống", + "toast_plugin_update_failed": "Không thể tải xuống bản cập nhật cho {{var0}}", + "toast_plugin_updated": "{{var0}} {{var1}} đã được thay thế bằng {{var2}} {{var3}}", + "update_check_complete": "Đã hoàn tất kiểm tra cập nhật plugin", + "update_check_complete_outdated": "Đã hoàn tất kiểm tra cập nhật plugin - {{var0}} đã lỗi thời!", + "update_check_info": "Kiểm tra các plugins hỗ trợ Kiểm tra cập nhật", + "update_notice_click": "Bấm để cập nhật!", + "update_notice_reload": "Tải lại để hoàn tất cập nhật", + "update_notice_update": "Các plugins sau cần được cập nhật: ", + "updated": "Đã cập nhật" + }, + "zh-CN": { + "add_to": "添加到 {{var0}}", + "ascending": "上升", + "center": "居中", + "changelog_added": "新的功能", + "changelog_fixed": "故障排除", + "changelog_improved": "改进之处", + "changelog_progress": "进展", + "check_for_updates": "检查更新", + "clipboard_success": "已将 {{var0}} 复制到剪贴板", + "confirm": "你确定吗?", + "copy": "复制 {{var0}}", + "delete_fail": "{{var0}} 无法删除", + "delete_success": "{{var0}} 成功删除", + "descending": "降序", + "developer": "开发商", + "donate_message": "支持我更多更新!", + "download": "下载", + "download_fail": "{{var0}} 无法下载", + "download_success": "{{var0}} 已成功下载", + "file_navigator_text": "浏览文件", + "first": "第一", + "from": "从", + "gradient": "梯度", + "guildbanner": "旗帜", + "guildicon": "图标", + "installed": "已安装", + "last": "持续", + "left": "剩下", + "loading": "正在加载 {{var0}}", + "location": "地点", + "order": "顺序", + "outdated": "过时的", + "please_wait": "请稍等", + "right": "对", + "save_fail": "{{var0}} 无法保存", + "save_success": "{{var0}} 保存成功", + "send": "发送 {{var0}}", + "server": "服务器", + "settings_shareData_description": "在 Discord 帐户之间同步插件配置", + "settings_showSupportBadges_description": "为支持我的 Patreon 的用户显示小徽章", + "settings_showToasts_description": "显示插件开始和停止烤面包", + "settings_showToasts_note": "禁用 BD 的常规设置 '{{var0}}' 之前将其禁用", + "settings_toastPosition_description": "默认吐司位置", + "settings_toastPosition_note": "仅更改由我的插件创建的吐司位置", + "settings_useChromium_description": "在 Discord (而不是浏览器)中打开链接", + "sort_by": "排序方式", + "status": "地位", + "time": "时间", + "timezone": "时区", + "to": "至", + "toast_plugin_loaded": "{{var0}} 已加载", + "toast_plugin_started": "{{var0}} 已开始", + "toast_plugin_stopped": "{{var0}} 已停止", + "toast_plugin_translated": "转换为 {{var0}}", + "toast_plugin_unloaded": "{{var0}} 已卸载", + "toast_plugin_update_failed": "无法下载 {{var0}} 的更新", + "toast_plugin_updated": "{{var0}} {{var1}} 已替换为 {{var2}} {{var3}}", + "update_check_complete": "插件更新检查完成", + "update_check_complete_outdated": "插件更新检查完成 - {{var0}} 已过期!", + "update_check_info": "检查支持更新检查的插件", + "update_notice_click": "点击更新!", + "update_notice_reload": "重新加载以完成更新", + "update_notice_update": "以下插件需要更新: ", + "updated": "更新" + }, + "zh-TW": { + "add_to": "添加至 {{var0}}", + "ascending": "升序", + "center": "置中", + "changelog_added": "新的功能", + "changelog_fixed": "故障排除", + "changelog_improved": "改進之處", + "changelog_progress": "進展", + "check_for_updates": "檢查更新", + "clipboard_success": "已將 {{var0}} 複製到剪貼板", + "confirm": "你確定嗎?", + "copy": "複製 {{var0}}", + "delete_fail": "{{var0}} 無法刪除", + "delete_success": "{{var0}} 成功刪除", + "descending": "降序", + "developer": "開發商", + "donate_message": "支持我更多更新!", + "download": "下載", + "download_fail": "{{var0}} 無法下載", + "download_success": "{{var0}} 已成功下載", + "file_navigator_text": "瀏覽文件", + "first": "第一", + "from": "從", + "gradient": "梯度", + "guildbanner": "橫幅", + "guildicon": "圖標", + "installed": "已安裝", + "last": "最後", + "left": "左邊", + "loading": "正在加載 {{var0}}", + "location": "地點", + "order": "舊的", + "outdated": "過時的", + "please_wait": "請稍等", + "right": "右邊", + "save_fail": "{{var0}} 無法保存", + "save_success": "{{var0}} 保存成功", + "send": "發送 {{var0}}", + "server": "伺服器", + "settings_shareData_description": "在 Discord 帳戶之間同步插件配置", + "settings_showSupportBadges_description": "為在 Patreon 支持我的用戶顯示小徽章", + "settings_showToasts_description": "顯示插件啟動和停止通知框", + "settings_showToasts_note": "在禁用此功能之前先關閉 BD 的 '{{var0}}' 設置", + "settings_toastPosition_description": "預設通知框位置", + "settings_toastPosition_note": "僅更改由我的插件創建的通知框位置", + "settings_useChromium_description": "在 Discord (而不是瀏覽器)中打開連結", + "sort_by": "排序方式", + "status": "狀態", + "time": "時間", + "timezone": "時區", + "to": "至", + "toast_plugin_loaded": "{{var0}} 已加載", + "toast_plugin_started": "{{var0}} 已啟動", + "toast_plugin_stopped": "{{var0}} 已停止", + "toast_plugin_translated": "轉換為 {{var0}}", + "toast_plugin_unloaded": "{{var0}} 已被卸載", + "toast_plugin_update_failed": "無法下載 {{var0}} 的更新", + "toast_plugin_updated": "{{var0}} {{var1}} 已替換為 {{var2}} {{var3}}", + "update_check_complete": "插件更新檢查完成", + "update_check_complete_outdated": "插件更新檢查完成 - {{var0}} 已過期!", + "update_check_info": "檢查支持更新檢查的插件", + "update_notice_click": "點擊更新!", + "update_notice_reload": "重新加載以完成更新", + "update_notice_update": "以下插件需要更新: ", + "updated": "更新" + }, + "default": { + "add_to": "Add to {{var0}}", + "ascending": "Ascending", + "center": "Centered", + "changelog_added": "New Features", + "changelog_fixed": "Bug Fixes", + "changelog_improved": "Improvements", + "changelog_progress": "Progress", + "check_for_updates": "Check for Updates", + "clipboard_success": "Copied {{var0}} to Clipboard", + "confirm": "Are you sure?", + "copy": "Copy {{var0}}", + "delete_fail": "{{var0}} cannot be deleted", + "delete_success": "{{var0}} deleted successfully", + "descending": "Descending", + "developer": "Developer", + "donate_message": "Support me to receive further Updates!", + "download": "Download", + "download_fail": "{{var0}} cannot be downloaded", + "download_success": "{{var0}} downloaded successfully", + "file_navigator_text": "Browse File", + "first": "First", + "from": "From", + "gradient": "Gradient", + "guildbanner": "Banner", + "guildicon": "Icon", + "installed": "Installed", + "last": "Last", + "left": "Left", + "loading": "Loading {{var0}}", + "location": "Location", + "order": "Order", + "outdated": "Outdated", + "please_wait": "Please wait", + "right": "Right", + "save_fail": "{{var0}} cannot be saved", + "save_success": "{{var0}} saved successfully", + "send": "Send {{var0}}", + "server": "Server", + "settings_shareData_description": "Synchronizes the Plugin Configs between Discord Accounts", + "settings_showSupportBadges_description": "Shows small Badges for Users who support my Patreon", + "settings_showToasts_description": "Shows Plugin start and stop Toasts", + "settings_showToasts_note": "Disable BDs general '{{var0}}' setting before disabling this", + "settings_toastPosition_description": "Default Toast Position", + "settings_toastPosition_note": "Only changes Position of Toasts created by my Plugins", + "settings_useChromium_description": "Open Links in Discord instead of your Browser", + "sort_by": "Sort by", + "status": "Status", + "time": "Time", + "timezone": "Timezone", + "to": "To", + "toast_plugin_loaded": "{{var0}} has been loaded", + "toast_plugin_started": "{{var0}} has been started", + "toast_plugin_stopped": "{{var0}} has been stopped", + "toast_plugin_translated": "translated to {{var0}}", + "toast_plugin_unloaded": "{{var0}} has been unloaded", + "toast_plugin_update_failed": "Update for {{var0}} cannot be downloaded", + "toast_plugin_updated": "{{var0}} {{var1}} has been replaced by {{var2}} {{var3}}", + "update_check_complete": "Plugin Update Check completed", + "update_check_complete_outdated": "Plugin Update Check completed - {{var0}} outdated!", + "update_check_info": "Check Plugins that support the Update Check", + "update_notice_click": "Click to update!", + "update_notice_reload": "Reload to complete the Update", + "update_notice_update": "The following Plugins need to be updated: ", + "updated": "Updated" + } + }, + "BDFDB_Patrons": { + "864982372751245313": {"active": true, "tier": "t2", "text": "", "color": "", "id": "windowsuss (fynn)"}, + "148234730523852800": {"active": true, "tier": "t2", "text": "", "color": "", "id": "inbroso (MaEp)"}, + "105509397211406336": {"active": true, "tier": "t2", "text": "", "color": "", "id": "samtino (SgJeff)"}, + "323494393828999168": {"active": true, "tier": "t2", "text": "", "color": "", "id": "polak (SzKu)"}, + "507464069100601363": {"active": true, "tier": "t2", "text": "", "color": "", "id": "cracky (MiPo)"}, + "620397524494057513": {"active": true, "tier": "t2", "text": "", "color": "", "id": "FUSL"}, + "798499176220327966": {"active": true, "tier": "t2", "text": "", "color": "", "id": "void (JaUt)"} + }, + "BDFDB_Patron_Tiers": { + "t1": { + "text": "" + }, + "t2": { + "text": "BDFDB Patron" + }, + "t3": { + "text": "BDFDB Patron+" + }, + "t4": { + "text": "BDFDB Special Supporter" + } + } +} diff --git a/BetterDiscord/plugins/0BDFDB.plugin.js b/BetterDiscord/plugins/0BDFDB.plugin.js new file mode 100644 index 0000000..85a6882 --- /dev/null +++ b/BetterDiscord/plugins/0BDFDB.plugin.js @@ -0,0 +1,8690 @@ +/** + * @name BDFDB + * @author DevilBro + * @authorId 278543574059057154 + * @version 3.5.3 + * @description Required Library for DevilBro's Plugins + * @invite Jx3TjNS + * @donate https://www.paypal.me/MircoWittrien + * @patreon https://www.patreon.com/MircoWittrien + * @website https://mwittrien.github.io/ + * @source https://github.com/mwittrien/BetterDiscordAddons/tree/master/Library/ + * @updateUrl https://mwittrien.github.io/BetterDiscordAddons/Library/0BDFDB.plugin.js + */ + +module.exports = (_ => { + if (window.BDFDB_Global && window.BDFDB_Global.PluginUtils && typeof window.BDFDB_Global.PluginUtils.cleanUp == "function") window.BDFDB_Global.PluginUtils.cleanUp(window.BDFDB_Global); + + const fs = require("fs"), path = require("path"); + + var BDFDB, Internal; + var LibraryRequires = {}; + var DiscordObjects = {}, DiscordConstants = {}; + var LibraryStores = {}, LibraryModules = {}; + var LibraryComponents = {}, NativeSubComponents = {}, CustomComponents = {}; + var PluginStores = {}; + + BDFDB = { + started: true, + changeLog: { + fixed: { + "Special Characters": "Fixed an Issue, which caused downloaded Files (for example Plugins downloaded via Plugin Repo) to have their special characters (polish, chinese, etc. letters) get corrupted" + } + } + }; + + return class BDFDB_Frame { + constructor (meta) {for (let key in meta) { + if (!this[key]) this[key] = meta[key]; + if (!BDFDB[key]) BDFDB[key] = meta[key]; + }} + getName () {return this.name;} + getAuthor () {return this.author;} + getVersion () {return this.version;} + getDescription () {return this.description;} + + load () { + const BdApi = window.BdApi; + + const Cache = {data: {}, modules: {}}; + + var changeLogs = {}; + + Internal = Object.assign({}, BDFDB, { + patchPriority: 0, + forceSyncData: true, + settings: {}, + defaults: { + general: { + shareData: { + value: true, + onChange: _ => Cache.data = {} + }, + showToasts: { + value: true, + isDisabled: data => data.nativeValue, + hasNote: data => data.disabled && data.value + }, + showSupportBadges: { + value: false + }, + useChromium: { + value: false, + isHidden: data => !Internal.LibraryRequires.electron || !Internal.LibraryRequires.electron.remote, + getValue: data => !data.disabled + } + }, + choices: { + toastPosition: { + value: "right", + items: "ToastPositions" + } + } + }, + }); + for (let key in Internal.defaults) Internal.settings[key] = {}; + + PluginStores = { + loaded: {}, + delayed: { + loads: [], + starts: [] + }, + updateData: { + plugins: {}, + timeouts: [], + downloaded: [], + interval: null + }, + modulePatches: {} + }; + + const Plugin = function (changeLog) { + return class Plugin { + constructor (meta) {for (let key in meta) if (!this[key]) this[key] = meta[key];} + getName () {return this.name;} + getAuthor () {return this.author;} + getVersion () {return this.version;} + getDescription () {return this.description;} + load () { + this.changeLog = changeLog; + this.loaded = true; + this.defaults = {}; + this.labels = {}; + if (window.BDFDB_Global.loading) { + if (!PluginStores.delayed.loads.includes(this)) PluginStores.delayed.loads.push(this); + } + else BDFDB.TimeUtils.suppress(_ => { + PluginStores.loaded[this.name] = this; + BDFDB.PluginUtils.load(this); + if (typeof this.onLoad == "function") this.onLoad(); + }, "Failed to load Plugin!", this)(); + } + start () { + if (!this.loaded) this.load(); + if (window.BDFDB_Global.loading) { + if (!PluginStores.delayed.starts.includes(this)) PluginStores.delayed.starts.push(this); + } + else { + if (this.started) return; + this.started = true; + BDFDB.TimeUtils.suppress(_ => { + BDFDB.PluginUtils.init(this); + if (typeof this.onStart == "function") this.onStart(); + }, "Failed to start Plugin!", this)(); + delete this.stopping; + } + } + stop () { + if (window.BDFDB_Global.loading) { + if (PluginStores.delayed.starts.includes(this)) PluginStores.delayed.starts.splice(PluginStores.delayed.starts.indexOf(this), 1); + } + else { + if (this.stopping) return; + this.stopping = true; + BDFDB.TimeUtils.timeout(_ => {delete this.stopping;}); + + BDFDB.TimeUtils.suppress(_ => { + if (typeof this.onStop == "function") this.onStop(); + BDFDB.PluginUtils.clear(this); + }, "Failed to stop Plugin!", this)(); + + delete this.started; + } + } + }; + }; + + const requestFunction = function (...args) { + let url = typeof args[0] == "string" && args[0]; + if (!url) return; + let callback = typeof args[1] == "function" && args[1] || typeof args[2] == "function" && args[2]; + if (typeof callback != "function") return; + if (url.indexOf("data:") == 0) callback(null, { + aborted: false, + complete: true, + end: undefined, + headers: {"content-type": url.slice(5).split(";")[0]}, + method: null, + rawHeaders: [], + statusCode: 200, + statusMessage: "OK", + url: "" + }, url); + else { + let config = args[1] && typeof args[1] == "object" ? args[1] : {}; + let timeout = 600000; + if (!isNaN(parseInt(config.timeout)) && config.timeout > 0) timeout = config.timeout; + if (config.form && typeof config.form == "object") { + let query = Object.entries(config.form).map(n => n[0] + "=" + n[1]).join("&"); + if (query) url += `?${query}`; + } + if (config.method) config.method = config.method.toUpperCase(); + let killed = false, timeoutObj = BDFDB.TimeUtils.timeout(_ => { + killed = true; + BDFDB.TimeUtils.clear(timeoutObj); + callback(new Error(`Request Timeout after ${timeout}ms`), { + aborted: false, + complete: true, + end: undefined, + headers: {}, + method: null, + rawHeaders: [], + statusCode: 408, + statusMessage: "OK", + url: "" + }, null); + }, timeout); + let response = null, isFallback = false; + return (config.bdVersion && BdApi && BdApi.Net && BdApi.Net.fetch ? BdApi.Net.fetch : fetch)(url, config).catch(error => { + if (!config.bdVersion) requestFunction(url, Object.assign({}, config, {bdVersion: true}), callback); + else callback(new Error(error), { + aborted: false, + complete: true, + end: undefined, + headers: {}, + method: null, + rawHeaders: [], + statusCode: 408, + statusMessage: "OK", + url: "" + }, null); + }).then(r => { + response = r; + if (!response) return; + response.statusCode = response.status; + if (response.headers) response.headers["content-type"] = response.headers.get("content-type"); + BDFDB.TimeUtils.clear(timeoutObj); + return config.toBuffer ? response.arrayBuffer() : response.text(); + }).then(body => { + if (!killed && response) callback(response.status != 200 ? new Error(response.statusText || "Fetch Failed") : null, response, body); + }); + } + }; + + BDFDB.LogUtils = {}; + Internal.console = function (type, config = {}) { + if (!console[type]) return; + let name, version; + if (typeof config.name == "string" && config.name) { + name = config.name; + version = typeof config.version == "string" ? config.version : ""; + } + else { + name = BDFDB.name; + version = BDFDB.version; + } + console[type](...[[name && `%c[${name}]`, version && `%c(v${version})`].filter(n => n).join(" "), name && "color: #3a71c1; font-weight: 700;", version && "color: #666; font-weight: 600; font-size: 11px;", [config.strings].flat(10).filter(n => n).join(" ").trim()].filter(n => n)); + }; + BDFDB.LogUtils.log = function (strings, config = {}) { + Internal.console("log", Object.assign({}, config, {name: typeof config == "string" ? config : config.name, strings})); + }; + BDFDB.LogUtils.warn = function (strings, config = {}) { + Internal.console("warn", Object.assign({}, config, {name: typeof config == "string" ? config : config.name, strings})); + }; + BDFDB.LogUtils.error = function (strings, config = {}) { + Internal.console("error", Object.assign({}, config, {name: typeof config == "string" ? config : config.name, strings: ["Fatal Error:", strings]})); + }; + + BDFDB.TimeUtils = {}; + BDFDB.TimeUtils.interval = function (callback, delay, ...args) { + if (typeof callback != "function" || typeof delay != "number" || delay < 1) return; + else { + let count = 0, interval = setInterval(_ => BDFDB.TimeUtils.suppress(callback, "Interval")(...[interval, count++, args].flat()), delay); + return interval; + } + }; + BDFDB.TimeUtils.timeout = function (callback, delay, ...args) { + delay = parseFloat(delay); + if (typeof callback != "function") return; + if (isNaN(delay) || typeof delay != "number" || delay < 1) { + let immediate = setImmediate(_ => BDFDB.TimeUtils.suppress(callback, "Immediate")(...[immediate, args].flat())); + return immediate; + } + else { + let start, paused = true, timeout = { + pause: _ => { + if (paused) return; + paused = true; + BDFDB.TimeUtils.clear(timeout.timer); + delay -= performance.now() - start; + }, + resume: _ => { + if (!paused) return; + paused = false; + start = performance.now(); + timeout.timer = setTimeout(_ => BDFDB.TimeUtils.suppress(callback, "Timeout")(...[timeout, args].flat()), delay) + } + }; + timeout.resume(); + return timeout; + } + }; + BDFDB.TimeUtils.clear = function (...timeObjects) { + for (let t of timeObjects.flat(10).filter(n => n)) { + t = t.timer != undefined ? t.timer : t; + if (typeof t == "number") { + clearInterval(t); + clearTimeout(t); + } + else if (typeof t == "object") clearImmediate(t); + } + }; + BDFDB.TimeUtils.suppress = function (callback, strings, config) {return function (...args) { + try {return callback(...args);} + catch (err) {BDFDB.LogUtils.error([strings, err], config);} + }}; + + BDFDB.LogUtils.log("Loading Library"); + + BDFDB.sameProto = function (a, b) { + if (a != null && typeof a == "object") return a.constructor && a.constructor.prototype && typeof a.constructor.prototype.isPrototypeOf == "function" && a.constructor.prototype.isPrototypeOf(b); + else return typeof a == typeof b; + }; + BDFDB.equals = function (mainA, mainB, sorted) { + let i = -1; + if (sorted === undefined || typeof sorted !== "boolean") sorted = false; + return equal(mainA, mainB); + function equal(a, b) { + i++; + let result = true; + if (i > 1000) result = null; + else { + if (typeof a !== typeof b) result = false; + else if (typeof a == "function") result = a.toString() == b.toString(); + else if (typeof a === "undefined") result = true; + else if (typeof a === "symbol") result = true; + else if (typeof a === "boolean") result = a == b; + else if (typeof a === "string") result = a == b; + else if (typeof a === "number") { + if (isNaN(a) || isNaN(b)) result = isNaN(a) == isNaN(b); + else result = a == b; + } + else if (!a && !b) result = true; + else if (!a || !b) result = false; + else if (typeof a === "object") { + let keysA = Object.getOwnPropertyNames(a); + let keysB = Object.getOwnPropertyNames(b); + if (keysA.length !== keysB.length) result = false; + else for (let j = 0; result === true && j < keysA.length; j++) { + if (sorted) result = equal(a[keysA[j]], b[keysB[j]]); + else result = equal(a[keysA[j]], b[keysA[j]]); + } + } + } + i--; + return result; + } + }; + + BDFDB.ObjectUtils = {}; + BDFDB.ObjectUtils.is = function (obj) { + return obj && !Array.isArray(obj) && !Set.prototype.isPrototypeOf(obj) && (typeof obj == "function" || typeof obj == "object"); + }; + BDFDB.ObjectUtils.get = function (nodeOrObj, valuePath) { + if (!nodeOrObj || !valuePath) return null; + let obj = Node.prototype.isPrototypeOf(nodeOrObj) ? BDFDB.ReactUtils.getInstance(nodeOrObj) : nodeOrObj; + if (!BDFDB.ObjectUtils.is(obj)) return null; + let found = obj; + for (const value of valuePath.split(".").filter(n => n)) { + if (!found) return null; + found = found[value]; + } + return found; + }; + BDFDB.ObjectUtils.invert = function (obj) { + let newObj = {}; + if (BDFDB.ObjectUtils.is(obj)) for (let entry of Object.entries(obj)) newObj[entry[1]] = entry[0]; + return newObj; + }; + BDFDB.ObjectUtils.extract = function (obj, ...keys) { + let newObj = {}; + if (BDFDB.ObjectUtils.is(obj)) for (let key of keys.flat(10).filter(n => n)) if (obj[key] != null) newObj[key] = obj[key]; + return newObj; + }; + BDFDB.ObjectUtils.exclude = function (obj, ...keys) { + let newObj = Object.assign({}, obj); + BDFDB.ObjectUtils.delete(newObj, ...keys) + return newObj; + }; + BDFDB.ObjectUtils.delete = function (obj, ...keys) { + if (BDFDB.ObjectUtils.is(obj)) for (let key of keys.flat(10).filter(n => n)) delete obj[key]; + }; + BDFDB.ObjectUtils.sort = function (obj, sort, except) { + if (!BDFDB.ObjectUtils.is(obj)) return {}; + let newObj = {}; + if (sort === undefined || !sort) for (let key of Object.keys(obj).sort()) newObj[key] = obj[key]; + else { + let values = []; + for (let key in obj) values.push(obj[key]); + values = BDFDB.ArrayUtils.keySort(values, sort, except); + for (let value of values) for (let key in obj) if (BDFDB.equals(value, obj[key])) { + newObj[key] = value; + break; + } + } + return newObj; + }; + BDFDB.ObjectUtils.filter = function (obj, filter, byKey = false) { + if (!BDFDB.ObjectUtils.is(obj)) return {}; + if (typeof filter != "function") return obj; + return Object.keys(obj).filter(key => filter(byKey ? key : obj[key])).reduce((newObj, key) => (newObj[key] = obj[key], newObj), {}); + }; + BDFDB.ObjectUtils.map = function (obj, mapFunc) { + if (!BDFDB.ObjectUtils.is(obj)) return {}; + if (typeof mapFunc != "string" && typeof mapFunc != "function") return obj; + let newObj = {}; + for (let key in obj) if (BDFDB.ObjectUtils.is(obj[key])) newObj[key] = typeof mapFunc == "string" ? obj[key][mapFunc] : mapFunc(obj[key], key); + return newObj; + }; + BDFDB.ObjectUtils.toArray = function (obj) { + if (!BDFDB.ObjectUtils.is(obj)) return []; + return Object.entries(obj).map(n => n[1]); + }; + BDFDB.ObjectUtils.deepAssign = function (obj, ...objs) { + if (!objs.length) return obj; + let nextObj = objs.shift(); + if (BDFDB.ObjectUtils.is(obj) && BDFDB.ObjectUtils.is(nextObj)) { + for (let key in nextObj) { + if (BDFDB.ObjectUtils.is(nextObj[key])) { + if (!obj[key]) Object.assign(obj, {[key]:{}}); + BDFDB.ObjectUtils.deepAssign(obj[key], nextObj[key]); + } + else Object.assign(obj, {[key]:nextObj[key]}); + } + } + return BDFDB.ObjectUtils.deepAssign(obj, ...objs); + }; + BDFDB.ObjectUtils.isEmpty = function (obj) { + return !BDFDB.ObjectUtils.is(obj) || Object.getOwnPropertyNames(obj).length == 0; + }; + BDFDB.ObjectUtils.copy = function (obj) { + if (!BDFDB.ObjectUtils.is(obj)) return obj; + let copy = {}; + for (let key in obj) copy[key] = obj[key]; + for (let key of Reflect.ownKeys(obj.constructor.prototype)) if (!copy[key] && obj[key] !== undefined) copy[key] = obj[key]; + return copy; + }; + + BDFDB.ArrayUtils = {}; + BDFDB.ArrayUtils.is = function (array) { + return array && Array.isArray(array); + }; + BDFDB.ArrayUtils.sum = function (array) { + return Array.isArray(array) ? array.reduce((total, num) => total + Math.round(num), 0) : 0; + }; + BDFDB.ArrayUtils.keySort = function (array, key, except) { + if (!BDFDB.ArrayUtils.is(array)) return []; + if (key == null) return array; + if (except === undefined) except = null; + return array.sort((x, y) => { + let xValue = x[key], yValue = y[key]; + if (xValue !== except) return xValue < yValue ? -1 : xValue > yValue ? 1 : 0; + }); + }; + BDFDB.ArrayUtils.numSort = function (array) { + return array.sort((x, y) => (x < y ? -1 : x > y ? 1 : 0)); + }; + BDFDB.ArrayUtils.includes = function (array, ...values) { + if (!BDFDB.ArrayUtils.is(array)) return null; + if (!array.length) return false; + let all = values.pop(); + if (typeof all != "boolean") { + values.push(all); + all = true; + } + if (!values.length) return false; + let contained = undefined; + for (let v of values) { + if (contained === undefined) contained = all; + if (all && !array.includes(v)) contained = false; + if (!all && array.includes(v)) contained = true; + } + return contained; + }; + BDFDB.ArrayUtils.remove = function (array, value, all = false) { + if (!BDFDB.ArrayUtils.is(array)) return []; + if (!array.includes(value)) return array; + if (!all) array.splice(array.indexOf(value), 1); + else while (array.indexOf(value) > -1) array.splice(array.indexOf(value), 1); + return array; + }; + BDFDB.ArrayUtils.removeCopies = function (array) { + if (!BDFDB.ArrayUtils.is(array)) return []; + return [...new Set(array)]; + }; + BDFDB.ArrayUtils.getAllIndexes = function (array, value) { + if (!BDFDB.ArrayUtils.is(array) && typeof array != "string") return []; + var indexes = [], index = -1; + while ((index = array.indexOf(value, index + 1)) !== -1) indexes.push(index); + return indexes; + }; + + BDFDB.BDUtils = {}; + BDFDB.BDUtils.getPluginsFolder = function () { + if (BdApi && BdApi.Plugins && BdApi.Plugins.folder && typeof BdApi.Plugins.folder == "string") return BdApi.Plugins.folder; + else if (Internal.LibraryRequires.process.env.BETTERDISCORD_DATA_PATH) return Internal.LibraryRequires.path.resolve(Internal.LibraryRequires.process.env.BETTERDISCORD_DATA_PATH, "plugins/"); + else if (Internal.LibraryRequires.process.env.injDir) return Internal.LibraryRequires.path.resolve(Internal.LibraryRequires.process.env.injDir, "plugins/"); + else switch (Internal.LibraryRequires.process.platform) { + case "win32": + return Internal.LibraryRequires.path.resolve(Internal.LibraryRequires.process.env.appdata, "BetterDiscord/plugins/"); + case "darwin": + return Internal.LibraryRequires.path.resolve(Internal.LibraryRequires.process.env.HOME, "Library/Preferences/BetterDiscord/plugins/"); + default: + if (Internal.LibraryRequires.process.env.XDG_CONFIG_HOME) return Internal.LibraryRequires.path.resolve(Internal.LibraryRequires.process.env.XDG_CONFIG_HOME, "BetterDiscord/plugins/"); + else if (Internal.LibraryRequires.process.env.HOME) return Internal.LibraryRequires.path.resolve(Internal.LibraryRequires.process.env.HOME, ".config/BetterDiscord/plugins/"); + else return ""; + } + }; + BDFDB.BDUtils.getThemesFolder = function () { + if (BdApi && BdApi.Themes && BdApi.Themes.folder && typeof BdApi.Themes.folder == "string") return BdApi.Themes.folder; + else if (Internal.LibraryRequires.process.env.BETTERDISCORD_DATA_PATH) return Internal.LibraryRequires.path.resolve(Internal.LibraryRequires.process.env.BETTERDISCORD_DATA_PATH, "themes/"); + else if (Internal.LibraryRequires.process.env.injDir) return Internal.LibraryRequires.path.resolve(Internal.LibraryRequires.process.env.injDir, "plugins/"); + else switch (Internal.LibraryRequires.process.platform) { + case "win32": + return Internal.LibraryRequires.path.resolve(Internal.LibraryRequires.process.env.appdata, "BetterDiscord/themes/"); + case "darwin": + return Internal.LibraryRequires.path.resolve(Internal.LibraryRequires.process.env.HOME, "Library/Preferences/BetterDiscord/themes/"); + default: + if (Internal.LibraryRequires.process.env.XDG_CONFIG_HOME) return Internal.LibraryRequires.path.resolve(Internal.LibraryRequires.process.env.XDG_CONFIG_HOME, "BetterDiscord/themes/"); + else if (Internal.LibraryRequires.process.env.HOME) return Internal.LibraryRequires.path.resolve(Internal.LibraryRequires.process.env.HOME, ".config/BetterDiscord/themes/"); + else return ""; + } + }; + BDFDB.BDUtils.isPluginEnabled = function (pluginName) { + if (BdApi && BdApi.Plugins && typeof BdApi.Plugins.isEnabled == "function") return BdApi.Plugins.isEnabled(pluginName); + }; + BDFDB.BDUtils.reloadPlugin = function (pluginName) { + if (BdApi && BdApi.Plugins && typeof BdApi.Plugins.reload == "function") BdApi.Plugins.reload(pluginName); + }; + BDFDB.BDUtils.enablePlugin = function (pluginName) { + if (BdApi && BdApi.Plugins && typeof BdApi.Plugins.enable == "function") BdApi.Plugins.enable(pluginName); + }; + BDFDB.BDUtils.disablePlugin = function (pluginName) { + if (BdApi && BdApi.Plugins && typeof BdApi.Plugins.disable == "function") BdApi.Plugins.disable(pluginName); + }; + BDFDB.BDUtils.getPlugin = function (pluginName, hasToBeEnabled = false, overHead = false) { + if (BdApi && !hasToBeEnabled || BDFDB.BDUtils.isPluginEnabled(pluginName) && BdApi.Plugins && typeof BdApi.Plugins.get == "function") { + let plugin = BdApi.Plugins.get(pluginName); + if (!plugin) return null; + if (overHead) return plugin.filename && plugin.exports && plugin.instance ? plugin : {filename: Internal.LibraryRequires.fs.existsSync(Internal.LibraryRequires.path.join(BDFDB.BDUtils.getPluginsFolder(), `${pluginName}.plugin.js`)) ? `${pluginName}.plugin.js` : null, id: pluginName, name: pluginName, plugin: plugin}; + else return plugin.filename && plugin.exports && plugin.instance ? plugin.instance : plugin; + } + return null; + }; + BDFDB.BDUtils.isThemeEnabled = function (themeName) { + if (BdApi && BdApi.Themes && typeof BdApi.Themes.isEnabled == "function") return BdApi.Themes.isEnabled(themeName); + }; + BDFDB.BDUtils.enableTheme = function (themeName) { + if (BdApi && BdApi.Themes && typeof BdApi.Themes.enable == "function") BdApi.Themes.enable(themeName); + }; + BDFDB.BDUtils.disableTheme = function (themeName) { + if (BdApi && BdApi.Themes && typeof BdApi.Themes.disable == "function") BdApi.Themes.disable(themeName); + }; + BDFDB.BDUtils.getTheme = function (themeName, hasToBeEnabled = false) { + if (BdApi && !hasToBeEnabled || BDFDB.BDUtils.isThemeEnabled(themeName) && BdApi.Themes && typeof BdApi.Themes.get == "function") return BdApi.Themes.get(themeName); + return null; + }; + BDFDB.BDUtils.settingsIds = { + automaticLoading: "settings.addons.autoReload", + coloredText: "settings.appearance.coloredText", + normalizedClasses: "settings.general.classNormalizer", + showToasts: "settings.general.showToasts" + }; + BDFDB.BDUtils.toggleSettings = function (key, state) { + if (BdApi && typeof key == "string") { + let path = key.split("."); + let currentState = BDFDB.BDUtils.getSettings(key); + if (state === true) { + if (currentState === false && typeof BdApi.enableSetting == "function") BdApi.enableSetting(...path); + } + else if (state === false) { + if (currentState === true && typeof BdApi.disableSetting == "function") BdApi.disableSetting(...path); + } + else if (currentState === true || currentState === false) BDFDB.BDUtils.toggleSettings(key, !currentState); + } + }; + BDFDB.BDUtils.getSettings = function (key) { + if (!BdApi) return {}; + if (typeof key == "string") return typeof BdApi.isSettingEnabled == "function" && BdApi.isSettingEnabled(...key.split(".")); + else return BDFDB.ArrayUtils.is(BdApi.settings) ? BdApi.settings.map(n => n.settings.map(m => m.settings.map(l => ({id: [n.id, m.id, l.id].join("."), value: l.value})))).flat(10).reduce((newObj, setting) => (newObj[setting.id] = setting.value, newObj), {}) : {}; + }; + BDFDB.BDUtils.getSettingsProperty = function (property, key) { + if (!BdApi || !BDFDB.ArrayUtils.is(BdApi.settings)) return key ? "" : {}; + else { + let settingsMap = BdApi.settings.map(n => n.settings.map(m => m.settings.map(l => ({id: [n.id, m.id, l.id].join("."), value: l[property]})))).flat(10).reduce((newObj, setting) => (newObj[setting.id] = setting.value, newObj), {}); + return key ? (settingsMap[key] != null ? settingsMap[key] : "") : ""; + } + }; + + const cssFileName = "0BDFDB.raw.css", dataFileName = "0BDFDB.data.json"; + const cssFilePath = path.join(BDFDB.BDUtils.getPluginsFolder(), cssFileName), dataFilePath = path.join(BDFDB.BDUtils.getPluginsFolder(), dataFileName); + BDFDB.PluginUtils = {}; + BDFDB.PluginUtils.buildPlugin = function (changeLog) { + return [Plugin(changeLog), BDFDB]; + }; + BDFDB.PluginUtils.load = function (plugin) { + if (!PluginStores.updateData.timeouts.includes(plugin.name)) { + PluginStores.updateData.timeouts.push(plugin.name); + const url = Internal.getPluginURL(plugin); + + PluginStores.updateData.plugins[url] = {name: plugin.name, raw: url, version: plugin.version}; + + BDFDB.PluginUtils.checkUpdate(plugin.name, url); + + if (plugin.changeLog && !BDFDB.ObjectUtils.isEmpty(plugin.changeLog) && typeof plugin.getSettingsPanel != "function") plugin.getSettingsPanel = _ => BDFDB.PluginUtils.createSettingsPanel(plugin, { + children: BDFDB.ReactUtils.createElement(Internal.LibraryComponents.MessagesPopoutComponents.EmptyState, { + msg: "No Settings available for this Plugin", + image: BDFDB.DiscordUtils.getTheme() == BDFDB.disCN.themelight ? "/assets/9b0d90147f7fab54f00dd193fe7f85cd.svg" : "/assets/308e587f3a68412f137f7317206e92c2.svg" + }) + }); + + if (!PluginStores.updateData.interval) PluginStores.updateData.interval = BDFDB.TimeUtils.interval(_ => { + BDFDB.PluginUtils.checkAllUpdates(); + }, 1000*60*60*4); + + BDFDB.TimeUtils.timeout(_ => BDFDB.ArrayUtils.remove(PluginStores.updateData.timeouts, plugin.name, true), 30000); + } + }; + BDFDB.PluginUtils.init = function (plugin) { + BDFDB.PluginUtils.load(plugin); + + plugin.settings = BDFDB.DataUtils.get(plugin); + + BDFDB.LogUtils.log(BDFDB.LanguageUtils.LibraryStringsFormat("toast_plugin_started", ""), plugin); + if (Internal.settings.general.showToasts && !BDFDB.BDUtils.getSettings(BDFDB.BDUtils.settingsIds.showToasts)) BDFDB.NotificationUtils.toast(BDFDB.LanguageUtils.LibraryStringsFormat("toast_plugin_started", `${plugin.name} v${plugin.version}`), { + disableInteractions: true, + barColor: "var(--status-positive)" + }); + + if (plugin.css) BDFDB.DOMUtils.appendLocalStyle(plugin.name, plugin.css); + + BDFDB.PatchUtils.unpatch(plugin); + Internal.addModulePatches(plugin); + Internal.addContextPatches(plugin); + + BDFDB.PluginUtils.translate(plugin); + + BDFDB.PluginUtils.checkChangeLog(plugin); + }; + BDFDB.PluginUtils.clear = function (plugin) { + BDFDB.LogUtils.log(BDFDB.LanguageUtils.LibraryStringsFormat("toast_plugin_stopped", ""), plugin); + if (Internal.settings.general.showToasts && !BDFDB.BDUtils.getSettings(BDFDB.BDUtils.settingsIds.showToasts)) BDFDB.NotificationUtils.toast(BDFDB.LanguageUtils.LibraryStringsFormat("toast_plugin_stopped", `${plugin.name} v${plugin.version}`), { + disableInteractions: true, + barColor: "var(--status-danger)" + }); + + const url = Internal.getPluginURL(plugin); + + BDFDB.PluginUtils.cleanUp(plugin); + + for (const modal of document.querySelectorAll(`.${plugin.name}-modal, .${plugin.name.toLowerCase()}-modal, .${plugin.name}-settingsmodal, .${plugin.name.toLowerCase()}-settingsmodal`)) { + const closeButton = modal.querySelector(BDFDB.dotCN.modalclose); + if (closeButton) closeButton.click(); + } + + delete Cache.data[plugin.name] + delete PluginStores.updateData.plugins[url]; + }; + BDFDB.PluginUtils.translate = function (plugin) { + if (typeof plugin.setLabelsByLanguage == "function" || typeof plugin.changeLanguageStrings == "function") { + const translate = _ => { + if (typeof plugin.setLabelsByLanguage == "function") plugin.labels = plugin.setLabelsByLanguage(); + if (typeof plugin.changeLanguageStrings == "function") plugin.changeLanguageStrings(); + }; + if (BDFDB.DiscordUtils.getLanguage()) translate(); + else BDFDB.TimeUtils.interval(interval => { + if (BDFDB.DiscordUtils.getLanguage()) { + BDFDB.TimeUtils.clear(interval); + translate(); + } + }, 100); + } + }; + BDFDB.PluginUtils.cleanUp = function (plugin) { + BDFDB.TimeUtils.suppress(_ => { + if (!BDFDB.ObjectUtils.is(plugin)) return; + if (plugin == window.BDFDB_Global) { + plugin = BDFDB; + let updateNotice = BDFDB.dotCN && document.querySelector(BDFDB.dotCN.noticeupdate); + if (updateNotice) updateNotice.close(); + BDFDB.TimeUtils.clear(PluginStores && PluginStores.updateData && PluginStores.updateData.interval); + delete window.BDFDB_Global.loaded; + if (PluginStores) BDFDB.TimeUtils.interval((interval, count) => { + if (count > 60 || window.BDFDB_Global.loaded) BDFDB.TimeUtils.clear(interval); + if (window.BDFDB_Global.loaded) for (let pluginName in BDFDB.ObjectUtils.sort(PluginStores.loaded)) BDFDB.TimeUtils.timeout(_ => { + if (PluginStores.loaded[pluginName].started) BDFDB.BDUtils.reloadPlugin(pluginName); + }); + }, 1000); + } + if (BDFDB.DOMUtils && BDFDB.DOMUtils.removeLocalStyle) BDFDB.DOMUtils.removeLocalStyle(plugin.name); + if (BDFDB.ListenerUtils && BDFDB.ListenerUtils.remove) BDFDB.ListenerUtils.remove(plugin); + if (BDFDB.ListenerUtils && BDFDB.ListenerUtils.removeGlobal) BDFDB.ListenerUtils.removeGlobal(plugin); + if (BDFDB.StoreChangeUtils && BDFDB.StoreChangeUtils.remove) BDFDB.StoreChangeUtils.remove(plugin); + if (BDFDB.PatchUtils && BDFDB.PatchUtils.unpatch) BDFDB.PatchUtils.unpatch(plugin); + + for (const patchType in PluginStores.modulePatches) { + for (const type in PluginStores.modulePatches[patchType]) { + for (const priority in PluginStores.modulePatches[patchType][type]) BDFDB.ArrayUtils.remove(PluginStores.modulePatches[patchType][type][priority], plugin, true); + if (!PluginStores.modulePatches[patchType][type].flat(10).length) delete PluginStores.modulePatches[patchType][type]; + } + if (BDFDB.ObjectUtils.isEmpty(PluginStores.modulePatches[patchType])) delete PluginStores.modulePatches[patchType]; + } + }, "Failed to clean up Plugin!", plugin)(); + }; + BDFDB.PluginUtils.checkUpdate = function (pluginName, url) { + if (pluginName && url && PluginStores.updateData.plugins[url]) return new Promise(callback => { + requestFunction(url, {timeout: 60000}, (error, response, body) => { + if (error || !PluginStores.updateData.plugins[url]) return callback(null); + let newName = (body.match(/"name"\s*:\s*"([^"]+)"/) || [])[1] || pluginName; + let newVersion = (body.match(/@version ([0-9]+\.[0-9]+\.[0-9]+)|['"]([0-9]+\.[0-9]+\.[0-9]+)['"]/i) || []).filter(n => n)[1]; + if (!newVersion) return callback(null); + if (BDFDB.NumberUtils.compareVersions(newVersion, PluginStores.updateData.plugins[url].version)) { + if (PluginStores.updateData.plugins[url]) PluginStores.updateData.plugins[url].outdated = true; + BDFDB.PluginUtils.showUpdateNotice(pluginName, url); + return callback(1); + } + else { + BDFDB.PluginUtils.removeUpdateNotice(pluginName); + return callback(0); + } + }); + }); + return new Promise(callback => callback(null)); + }; + BDFDB.PluginUtils.checkAllUpdates = function () { + return new Promise(callback => { + let finished = 0, amount = 0; + for (let url in PluginStores.updateData.plugins) { + let plugin = PluginStores.updateData.plugins[url]; + if (plugin) BDFDB.PluginUtils.checkUpdate(plugin.name, plugin.raw).then(state => { + finished++; + if (state == 1) amount++; + if (finished >= Object.keys(PluginStores.updateData.plugins).length) callback(amount); + }); + } + }); + }; + BDFDB.PluginUtils.hasUpdateCheck = function (url) { + if (!url || typeof url != "string") return false; + let updateStore = Object.assign({}, window.PluginUpdates && window.PluginUpdates.plugins, PluginStores.updateData.plugins); + if (updateStore[url]) return true; + else { + let temp = url.replace("//raw.githubusercontent.com", "//").split("/"); + let gitName = temp.splice(3, 1); + temp.splice(4, 1); + temp.splice(2, 1, gitName + ".github.io"); + let pagesUrl = temp.join("/"); + return !!updateStore[pagesUrl]; + } + }; + BDFDB.PluginUtils.showUpdateNotice = function (pluginName, url) { + if (!pluginName || !url) return; + let updateNotice = document.querySelector(BDFDB.dotCN.noticeupdate); + if (!updateNotice) { + let vanishObserver = new MutationObserver(changes => { + if (!document.contains(updateNotice)) { + if (updateNotice.querySelector(BDFDB.dotCN.noticeupdateentry)) { + let layers = document.querySelector(BDFDB.dotCN.layers) || document.querySelector(BDFDB.dotCN.appmount); + if (layers) layers.parentElement.insertBefore(updateNotice, layers); + } + else vanishObserver.disconnect(); + } + else if (document.contains(updateNotice) && !updateNotice.querySelector(BDFDB.dotCNC.noticeupdateentry + BDFDB.dotCN.noticebutton)) vanishObserver.disconnect(); + }); + vanishObserver.observe(document.body, {childList: true, subtree: true}); + updateNotice = BDFDB.NotificationUtils.notice(`${BDFDB.LanguageUtils.LibraryStrings.update_notice_update}    
`, { + type: "info", + className: BDFDB.disCN.noticeupdate, + html: true, + customIcon: ``, + buttons: [{ + className: BDFDB.disCN.noticeupdatebuttonall, + contents: BDFDB.LanguageUtils.LanguageStrings.FORM_LABEL_ALL, + onClick: _ => {for (let notice of updateNotice.querySelectorAll(BDFDB.dotCN.noticeupdateentry)) notice.click();} + }], + onClose: _ => vanishObserver.disconnect() + }); + updateNotice.style.setProperty("position", "relative", "important"); + updateNotice.style.setProperty("visibility", "visible", "important"); + updateNotice.style.setProperty("opacity", "1", "important"); + updateNotice.style.setProperty("z-index", "100000", "important"); + let reloadButton = updateNotice.querySelector(BDFDB.dotCN.noticeupdatebuttonreload); + if (reloadButton) BDFDB.DOMUtils.hide(reloadButton); + } + if (updateNotice) { + let updateNoticeList = updateNotice.querySelector(BDFDB.dotCN.noticeupdateentries); + if (updateNoticeList && !updateNoticeList.querySelector(`#${pluginName}-notice`)) { + if (updateNoticeList.childElementCount) updateNoticeList.appendChild(BDFDB.DOMUtils.create(`
,
`)); + let updateEntry = BDFDB.DOMUtils.create(`
${pluginName}
`); + updateEntry.addEventListener("click", _ => { + if (!updateEntry.wasClicked) { + updateEntry.wasClicked = true; + BDFDB.PluginUtils.downloadUpdate(pluginName, url); + } + }); + updateNoticeList.appendChild(updateEntry); + if (!updateNoticeList.hasTooltip) { + updateNoticeList.hasTooltip = true; + updateNotice.tooltip = BDFDB.TooltipUtils.create(updateNoticeList, BDFDB.LanguageUtils.LibraryStrings.update_notice_click, { + type: "bottom", + zIndex: 100001, + delay: 500, + onHide: _ => {updateNoticeList.hasTooltip = false;} + }); + } + } + } + }; + BDFDB.PluginUtils.removeUpdateNotice = function (pluginName, updateNotice = document.querySelector(BDFDB.dotCN.noticeupdate)) { + if (!pluginName || !updateNotice) return; + let updateNoticeList = updateNotice.querySelector(BDFDB.dotCN.noticeupdateentries); + if (updateNoticeList) { + let noticeEntry = updateNoticeList.querySelector(`#${pluginName}-notice`); + if (noticeEntry) { + let nextSibling = noticeEntry.nextSibling; + let prevSibling = noticeEntry.prevSibling; + if (nextSibling && BDFDB.DOMUtils.containsClass(nextSibling, BDFDB.disCN.noticeupdateseparator)) nextSibling.remove(); + else if (prevSibling && BDFDB.DOMUtils.containsClass(prevSibling, BDFDB.disCN.noticeupdateseparator)) prevSibling.remove(); + noticeEntry.remove(); + } + if (!updateNoticeList.childElementCount) { + let reloadButton = updateNotice.querySelector(BDFDB.dotCN.noticeupdatebuttonreload); + if (reloadButton) { + updateNotice.querySelector(BDFDB.dotCN.noticetext).innerText = BDFDB.LanguageUtils.LibraryStrings.update_notice_reload; + BDFDB.DOMUtils.show(reloadButton); + } + else updateNotice.querySelector(BDFDB.dotCN.noticedismiss).click(); + } + } + }; + BDFDB.PluginUtils.downloadUpdate = function (pluginName, url) { + if (pluginName && url) requestFunction(url, {timeout: 60000}, (error, response, body) => { + if (error) { + BDFDB.PluginUtils.removeUpdateNotice(pluginName); + BDFDB.NotificationUtils.toast(BDFDB.LanguageUtils.LibraryStringsFormat("toast_plugin_update_failed", pluginName), { + type: "danger", + disableInteractions: true + }); + } + else { + let wasEnabled = BDFDB.BDUtils.isPluginEnabled(pluginName); + let newName = (body.match(/@name ([^"^\n^\t^\t]+)|['"]([^"^\n^\t^\t]+)['"]/i) || []).filter(n => n)[1] || pluginName; + let newVersion = (body.match(/@version ([0-9]+\.[0-9]+\.[0-9]+)|['"]([0-9]+\.[0-9]+\.[0-9]+)['"]/i) || []).filter(n => n)[1]; + let oldVersion = PluginStores.updateData.plugins[url].version; + let fileName = pluginName == "BDFDB" ? "0BDFDB" : pluginName; + let newFileName = newName == "BDFDB" ? "0BDFDB" : newName; + Internal.LibraryRequires.fs.writeFile(Internal.LibraryRequires.path.join(BDFDB.BDUtils.getPluginsFolder(), newFileName + ".plugin.js"), body, _ => { + if (PluginStores.updateData.plugins[url]) PluginStores.updateData.plugins[url].version = newVersion; + if (fileName != newFileName) { + Internal.LibraryRequires.fs.unlink(Internal.LibraryRequires.path.join(BDFDB.BDUtils.getPluginsFolder(), fileName + ".plugin.js"), _ => {}); + let configPath = Internal.LibraryRequires.path.join(BDFDB.BDUtils.getPluginsFolder(), fileName + ".config.json"); + Internal.LibraryRequires.fs.exists(configPath, exists => { + if (exists) Internal.LibraryRequires.fs.rename(configPath, Internal.LibraryRequires.path.join(BDFDB.BDUtils.getPluginsFolder(), newFileName + ".config.json"), _ => {}); + }); + BDFDB.TimeUtils.timeout(_ => {if (wasEnabled && !BDFDB.BDUtils.isPluginEnabled(newName)) BDFDB.BDUtils.enablePlugin(newName);}, 3000); + } + BDFDB.NotificationUtils.toast(BDFDB.LanguageUtils.LibraryStringsFormat("toast_plugin_updated", pluginName, "v" + oldVersion, newName, "v" + newVersion), { + disableInteractions: true + }); + let updateNotice = document.querySelector(BDFDB.dotCN.noticeupdate); + if (updateNotice) { + if (updateNotice.querySelector(BDFDB.dotCN.noticebutton) && !PluginStores.updateData.downloaded.includes(pluginName)) { + PluginStores.updateData.downloaded.push(pluginName); + } + BDFDB.PluginUtils.removeUpdateNotice(pluginName, updateNotice); + } + }); + } + }); + }; + BDFDB.PluginUtils.checkChangeLog = function (plugin) { + if (!BDFDB.ObjectUtils.is(plugin) || !BDFDB.ObjectUtils.is(plugin.changeLog) || plugin.changeLog.info) return; + if (!changeLogs[plugin.name] || BDFDB.NumberUtils.compareVersions(plugin.version, changeLogs[plugin.name])) { + changeLogs[plugin.name] = plugin.version; + BDFDB.DataUtils.save(changeLogs, BDFDB, "changeLogs"); + BDFDB.PluginUtils.openChangeLog(plugin); + } + }; + BDFDB.PluginUtils.openChangeLog = function (plugin) { + if (!BDFDB.ObjectUtils.is(plugin) || !BDFDB.ObjectUtils.is(plugin.changeLog)) return; + let changeLogEntries = [], headers = { + added: "New Features", + fixed: "Bug Fixes", + improved: "Improvements", + progress: "Progress" + }; + for (let type in plugin.changeLog) { + type = type.toLowerCase(); + if (InternalData.DiscordClasses["changelog" + type]) changeLogEntries.push([ + BDFDB.ReactUtils.createElement("h1", { + className: BDFDB.disCNS["changelog" + type] + BDFDB.disCN.margintop20, + style: {"margin-top": !changeLogEntries.length ? 0 : null}, + children: BDFDB.LanguageUtils && BDFDB.LanguageUtils.LibraryStrings && BDFDB.LanguageUtils.LibraryStrings["changelog_" + type] || headers[type] + }), + BDFDB.ReactUtils.createElement("ul", { + children: Object.keys(plugin.changeLog[type]).map(key => BDFDB.ReactUtils.createElement("li", { + children: [ + BDFDB.ReactUtils.createElement("strong", {children: key}), + plugin.changeLog[type][key] ? `: ${plugin.changeLog[type][key]}.` : "" + ] + })) + }) + ]); + } + if (changeLogEntries.length) BDFDB.ModalUtils.open(plugin, { + header: `${plugin.name} ${BDFDB.LanguageUtils.LanguageStrings.CHANGE_LOG}`, + subHeader: `Version ${plugin.version}`, + className: BDFDB.disCN.modalchangelogmodal, + contentClassName: BDFDB.disCNS.changelogcontainer + BDFDB.disCN.modalminicontent, + footerDirection: Internal.LibraryComponents.Flex.Direction.HORIZONTAL, + children: changeLogEntries.flat(10).filter(n => n), + footerChildren: (plugin == BDFDB || plugin == this || PluginStores.loaded[plugin.name] && PluginStores.loaded[plugin.name] == plugin && plugin.author == "DevilBro") && BDFDB.ReactUtils.createElement("div", { + className: BDFDB.disCN.changelogfooter, + children: [{ + href: "https://www.paypal.me/MircoWittrien", + name: "PayPal", + icon: "PAYPAL" + }, { + href: "https://www.patreon.com/MircoWittrien", + name: "Patreon", + icon: "PATREON" + }, { + name: BDFDB.LanguageUtils.LibraryStringsFormat("send", "Solana"), + icon: "PHANTOM", + onClick: _ => { + BDFDB.LibraryModules.WindowUtils.copy(InternalData.mySolana); + BDFDB.NotificationUtils.toast(BDFDB.LanguageUtils.LibraryStringsFormat("clipboard_success", "Phantom Wallet Key"), { + type: "success" + }); + } + }, { + name: BDFDB.LanguageUtils.LibraryStringsFormat("send", "Ethereum"), + icon: "METAMASK", + onClick: _ => { + BDFDB.LibraryModules.WindowUtils.copy(InternalData.myEthereum); + BDFDB.NotificationUtils.toast(BDFDB.LanguageUtils.LibraryStringsFormat("clipboard_success", "MetaMask Wallet Key"), { + type: "success" + }); + } + }].map(data => BDFDB.ReactUtils.createElement(data.href ? Internal.LibraryComponents.Anchor : Internal.LibraryComponents.Clickable, { + className: BDFDB.disCN.changelogsociallink, + href: data.href || "", + onClick: !data.onClick ? (_ => {}) : data.onClick, + children: BDFDB.ReactUtils.createElement(Internal.LibraryComponents.TooltipContainer, { + text: data.name, + children: BDFDB.ReactUtils.createElement(Internal.LibraryComponents.SvgIcon, { + name: Internal.LibraryComponents.SvgIcon.Names[data.icon], + width: 16, + height: 16 + }) + }) + })).concat(BDFDB.ReactUtils.createElement(Internal.LibraryComponents.TextElement, { + size: Internal.LibraryComponents.TextElement.Sizes.SIZE_12, + children: BDFDB.LanguageUtils.LibraryStrings.donate_message + })) + }) + }); + }; + BDFDB.PluginUtils.addLoadingIcon = function (icon) { + if (!Node.prototype.isPrototypeOf(icon)) return; + let app = document.querySelector(BDFDB.dotCN.app); + if (!app) return; + BDFDB.DOMUtils.addClass(icon, BDFDB.disCN.loadingicon); + let loadingIconWrapper = document.querySelector(BDFDB.dotCN.app + ">" + BDFDB.dotCN.loadingiconwrapper); + if (!loadingIconWrapper) { + loadingIconWrapper = BDFDB.DOMUtils.create(`
`); + app.appendChild(loadingIconWrapper); + let killObserver = new MutationObserver(changes => { + if (!loadingIconWrapper.firstElementChild) { + killObserver.disconnect(); + BDFDB.DOMUtils.remove(loadingIconWrapper); + } + }); + killObserver.observe(loadingIconWrapper, {childList: true}); + } + loadingIconWrapper.appendChild(icon); + }; + BDFDB.PluginUtils.createSettingsPanel = function (addon, props) { + if (!window.BDFDB_Global.loaded) return BdApi.React.createElement("div", { + style: {"color": "var(--header-secondary)", "white-space": "pre-wrap"}, + children: [ + "Could not initiate BDFDB Library Plugin! Can not create Settings Panel!\n\nTry deleting the ", + BdApi.React.createElement("strong", {children: dataFileName}), + " File in your ", + BdApi.React.createElement("strong", {children: BDFDB.BDUtils.getPluginsFolder()}), + "\nDirectory and reload Discord afterwards!" + ] + }); + addon = addon == BDFDB && Internal || addon; + if (!BDFDB.ObjectUtils.is(addon)) return; + let settingsProps = props; + if (settingsProps && !BDFDB.ObjectUtils.is(settingsProps) && (BDFDB.ReactUtils.isValidElement(settingsProps) || BDFDB.ArrayUtils.is(settingsProps))) settingsProps = { + children: settingsProps + }; + return BDFDB.ReactUtils.createElement(Internal.LibraryComponents.SettingsPanel, Object.assign({ + addon: addon, + collapseStates: settingsProps && settingsProps.collapseStates + }, settingsProps)); + }; + BDFDB.PluginUtils.refreshSettingsPanel = function (plugin, settingsPanel, ...args) { + if (BDFDB.ObjectUtils.is(plugin)) { + if (settingsPanel && settingsPanel.props && BDFDB.ObjectUtils.is(settingsPanel.props._instance)) { + settingsPanel.props._instance.props = Object.assign({}, settingsPanel.props._instance.props, ...args); + BDFDB.ReactUtils.forceUpdate(settingsPanel.props._instance); + } + else if (typeof plugin.getSettingsPanel == "function" && Node.prototype.isPrototypeOf(settingsPanel) && settingsPanel.parentElement) { + settingsPanel.parentElement.appendChild(plugin.getSettingsPanel(...args)); + settingsPanel.remove(); + } + } + }; + + window.BDFDB_Global = Object.assign({ + started: true, + loading: true, + PluginUtils: { + buildPlugin: BDFDB.PluginUtils.buildPlugin, + cleanUp: BDFDB.PluginUtils.cleanUp + } + }, window.BDFDB_Global); + + Internal.writeConfig = function (plugin, path, config) { + let allData = {}; + try {allData = JSON.parse(fs.readFileSync(path));} + catch (err) {allData = {};} + try {fs.writeFileSync(path, JSON.stringify(Object.assign({}, allData, {[Internal.shouldSyncConfig(plugin) ? "all" : BDFDB.UserUtils.me.id]: config}), null, " "));} + catch (err) {} + }; + Internal.readConfig = function (plugin, path) { + let sync = Internal.shouldSyncConfig(plugin); + try { + let config = JSON.parse(fs.readFileSync(path)); + if (config && Object.keys(config).some(n => !(n == "all" || parseInt(n)))) { + config = {[Internal.shouldSyncConfig(plugin) ? "all" : BDFDB.UserUtils.me.id]: config}; + try {fs.writeFileSync(path, JSON.stringify(config, null, " "));} + catch (err) {} + } + return config && config[sync ? "all" : BDFDB.UserUtils.me.id] || {}; + } + catch (err) {return {};} + }; + Internal.shouldSyncConfig = function (plugin) { + return plugin.neverSyncData !== undefined ? !plugin.neverSyncData : (plugin.forceSyncData || Internal.settings.general.shareData); + }; + + BDFDB.DataUtils = {}; + BDFDB.DataUtils.save = function (data, plugin, key, id) { + plugin = plugin == BDFDB && Internal || plugin; + let pluginName = typeof plugin === "string" ? plugin : plugin.name; + let fileName = pluginName == "BDFDB" ? "0BDFDB" : pluginName; + let configPath = path.join(BDFDB.BDUtils.getPluginsFolder(), fileName + ".config.json"); + + let config = Cache.data[pluginName] !== undefined ? Cache.data[pluginName] : (Internal.readConfig(plugin, configPath) || {}); + + if (key === undefined) config = BDFDB.ObjectUtils.is(data) ? BDFDB.ObjectUtils.sort(data) : data; + else { + if (id === undefined) config[key] = BDFDB.ObjectUtils.is(data) ? BDFDB.ObjectUtils.sort(data) : data; + else { + if (!BDFDB.ObjectUtils.is(config[key])) config[key] = {}; + config[key][id] = BDFDB.ObjectUtils.is(data) ? BDFDB.ObjectUtils.sort(data) : data; + } + } + + let configIsObject = BDFDB.ObjectUtils.is(config); + if (key !== undefined && configIsObject && BDFDB.ObjectUtils.is(config[key]) && BDFDB.ObjectUtils.isEmpty(config[key])) delete config[key]; + if (BDFDB.ObjectUtils.isEmpty(config)) { + delete Cache.data[pluginName]; + if (fs.existsSync(configPath)) fs.unlinkSync(configPath); + } + else { + if (configIsObject) config = BDFDB.ObjectUtils.sort(config); + Cache.data[pluginName] = configIsObject ? BDFDB.ObjectUtils.deepAssign({}, config) : config; + Internal.writeConfig(plugin, configPath, config); + } + }; + + BDFDB.DataUtils.load = function (plugin, key, id) { + plugin = plugin == BDFDB && Internal || plugin; + let pluginName = typeof plugin === "string" ? plugin : plugin.name; + let fileName = pluginName == "BDFDB" ? "0BDFDB" : pluginName; + let configPath = path.join(BDFDB.BDUtils.getPluginsFolder(), fileName + ".config.json"); + + let config = Cache.data[pluginName] !== undefined ? Cache.data[pluginName] : (Internal.readConfig(plugin, configPath) || {}); + let configIsObject = BDFDB.ObjectUtils.is(config); + Cache.data[pluginName] = configIsObject ? BDFDB.ObjectUtils.deepAssign({}, config) : config; + + if (key === undefined) return config; + else { + let keyData = configIsObject ? (BDFDB.ObjectUtils.is(config[key]) || config[key] === undefined ? BDFDB.ObjectUtils.deepAssign({}, config[key]) : config[key]) : null; + if (id === undefined) return keyData; + else return !BDFDB.ObjectUtils.is(keyData) || keyData[id] === undefined ? null : keyData[id]; + } + }; + BDFDB.DataUtils.remove = function (plugin, key, id) { + plugin = plugin == BDFDB && Internal || plugin; + let pluginName = typeof plugin === "string" ? plugin : plugin.name; + let fileName = pluginName == "BDFDB" ? "0BDFDB" : pluginName; + let configPath = path.join(BDFDB.BDUtils.getPluginsFolder(), fileName + ".config.json"); + + let config = Cache.data[pluginName] !== undefined ? Cache.data[pluginName] : (Internal.readConfig(plugin, configPath) || {}); + let configIsObject = BDFDB.ObjectUtils.is(config); + + if (key === undefined || !configIsObject) config = {}; + else { + if (id === undefined) delete config[key]; + else if (BDFDB.ObjectUtils.is(config[key])) delete config[key][id]; + } + + if (BDFDB.ObjectUtils.is(config[key]) && BDFDB.ObjectUtils.isEmpty(config[key])) delete config[key]; + if (BDFDB.ObjectUtils.isEmpty(config)) { + delete Cache.data[pluginName]; + if (fs.existsSync(configPath)) fs.unlinkSync(configPath); + } + else { + if (configIsObject) config = BDFDB.ObjectUtils.sort(config); + Cache.data[pluginName] = configIsObject ? BDFDB.ObjectUtils.deepAssign({}, config) : config; + Internal.writeConfig(plugin, configPath, config); + } + }; + BDFDB.DataUtils.get = function (plugin, key, id) { + plugin = plugin == BDFDB && Internal || plugin; + plugin = typeof plugin == "string" ? BDFDB.BDUtils.getPlugin(plugin) : plugin; + const defaults = plugin && plugin.defaults; + if (!BDFDB.ObjectUtils.is(defaults) || key && !BDFDB.ObjectUtils.is(defaults[key])) return id === undefined ? {} : null; + let oldC = BDFDB.DataUtils.load(plugin), newC = {}, update = false; + const checkLayer = (i, j) => { + let isObj = BDFDB.ObjectUtils.is(defaults[i][j].value); + if (!newC[i]) newC[i] = {}; + if (oldC[i] == null || oldC[i][j] == null || isObj && (!BDFDB.ObjectUtils.is(oldC[i][j]) || Object.keys(defaults[i][j].value).some(n => defaults[i][j].value[n] != null && !BDFDB.sameProto(defaults[i][j].value[n], oldC[i][j][n])))) { + newC[i][j] = isObj ? BDFDB.ObjectUtils.deepAssign({}, defaults[i][j].value) : defaults[i][j].value; + update = true; + } + else newC[i][j] = oldC[i][j]; + }; + if (key) {for (let j in defaults[key]) checkLayer(key, j);} + else {for (let i in defaults) if (BDFDB.ObjectUtils.is(defaults[i])) for (let j in defaults[i]) checkLayer(i, j);} + if (update) BDFDB.DataUtils.save(Object.assign({}, oldC, newC), plugin); + + if (key === undefined) return newC; + else if (id === undefined) return newC[key] === undefined ? {} : newC[key]; + else return newC[key] === undefined || newC[key][id] === undefined ? null : newC[key][id]; + }; + let InternalData, libHashes = {}, oldLibHashes = BDFDB.DataUtils.load(BDFDB, "hashes"), libraryCSS; + + const getBackup = (fileName, path) => { + return {backup: fs.existsSync(path) && (fs.readFileSync(path) || "").toString(), hashIsSame: libHashes[fileName] && oldLibHashes[fileName] && libHashes[fileName] == oldLibHashes[fileName]}; + }; + const requestLibraryHashes = tryAgain => { + requestFunction("https://api.github.com/repos/mwittrien/BetterDiscordAddons/contents/Library/_res/", {timeout: 60000}, (e, r, b) => { + if ((e || !b || r.statusCode != 200) && tryAgain) return BDFDB.TimeUtils.timeout(_ => requestLibraryHashes(), 10000); + else { + try { + b = JSON.parse(b); + libHashes[cssFileName] = (b.find(n => n && n.name == cssFileName) || {}).sha; + libHashes[dataFileName] = (b.find(n => n && n.name == dataFileName) || {}).sha; + BDFDB.DataUtils.save(libHashes, BDFDB, "hashes"); + } + catch (err) {} + requestLibraryData(true); + } + }); + }; + const requestLibraryData = tryAgain => { + const parseCSS = css => { + libraryCSS = css; + + const backupObj = getBackup(dataFileName, dataFilePath); + const UserStore = BdApi.Webpack.getModule(BdApi.Webpack.Filters.byProps("getCurrentUser")); + if (backupObj.backup && backupObj.hashIsSame || UserStore && UserStore.getCurrentUser().id == "278543574059057154") parseData(backupObj.backup); + else requestFunction(`https://mwittrien.github.io/BetterDiscordAddons/Library/_res/${dataFileName}`, {timeout: 60000}, (e, r, b) => { + if ((e || !b || r.statusCode != 200) && tryAgain) return BDFDB.TimeUtils.timeout(_ => requestLibraryData(), 10000); + if (!e && b && r.statusCode == 200) { + if (backupObj.backup && backupObj.backup.replace(/\s/g, "") == b.replace(/\s/g, "")) { + libHashes[dataFileName] = oldLibHashes[dataFileName]; + BDFDB.DataUtils.save(libHashes, BDFDB, "hashes"); + } + parseData(b, true); + } + else parseData(fs.existsSync(dataFilePath) && (fs.readFileSync(dataFilePath) || "").toString()); + }); + }; + const parseData = (dataString, fetched) => { + try {InternalData = JSON.parse(dataString);} + catch (err) { + if (fetched) { + try { + dataString = fs.existsSync(dataFilePath) && (fs.readFileSync(dataFilePath) || "").toString(); + InternalData = JSON.parse(dataString); + } + catch (err2) {BDFDB.LogUtils.error(["Failed to initiate Library!", "Failed Fetch!", dataString ? "Corrupt Backup." : "No Backup.", , err2]);} + } + else BDFDB.LogUtils.error(["Failed to initiate Library!", dataString ? "Corrupt Backup." : "No Backup.", err]); + } + if (fetched && dataString) fs.writeFile(dataFilePath, dataString, _ => {}); + + Internal.getWebModuleReq = function () { + if (!Internal.getWebModuleReq.req) { + const id = "BDFDB-WebModules_" + Math.floor(Math.random() * 10000000000000000); + const req = webpackChunkdiscord_app.push([[id], {}, req => req]); + delete req.m[id]; + delete req.c[id]; + Internal.getWebModuleReq.req = req; + } + return Internal.getWebModuleReq.req; + }; + + if (InternalData) loadLibrary(); + else BdApi.alert("Error", "Could not initiate BDFDB Library Plugin. Check your Internet Connection and make sure GitHub isn't blocked by your Network or try disabling your VPN/Proxy."); + }; + + const backupObj = getBackup(cssFileName, cssFilePath); + if (backupObj.backup && backupObj.hashIsSame) parseCSS(backupObj.backup); + else requestFunction(`https://mwittrien.github.io/BetterDiscordAddons/Library/_res/${cssFileName}`, {timeout: 60000}, (e, r, b) => { + if ((e || !b || r.statusCode != 200) && tryAgain) return BDFDB.TimeUtils.timeout(_ => requestLibraryData(), 10000); + if (!e && b && r.statusCode == 200) { + if (backupObj.backup && backupObj.backup.replace(/\s/g, "") == b.replace(/\s/g, "")) { + libHashes[cssFileName] = oldLibHashes[cssFileName]; + BDFDB.DataUtils.save(libHashes, BDFDB, "hashes"); + } + fs.writeFile(cssFilePath, b, _ => {}); + parseCSS(b); + } + else parseCSS(fs.existsSync(cssFilePath) && (fs.readFileSync(cssFilePath) || "").toString()); + }); + }; + const loadLibrary = _ => { + Internal.getPluginURL = function (plugin) { + plugin = plugin == BDFDB && Internal || plugin; + if (BDFDB.ObjectUtils.is(plugin)) { + if (InternalData.PluginUrlMap && InternalData.PluginUrlMap[plugin.name]) return InternalData.PluginUrlMap[plugin.name]; + else if (plugin.updateUrl) return plugin.updateUrl; + else { + let name = InternalData.PluginNameMap && InternalData.PluginNameMap[plugin.name] || plugin.name; + return `https://mwittrien.github.io/BetterDiscordAddons/Plugins/${name}/${name}.plugin.js`; + } + } + else return ""; + }; + + Internal.findModule = function (type, cacheString, filter, config = {}) { + if (!BDFDB.ObjectUtils.is(Cache.modules[type])) Cache.modules[type] = {module: {}, export: {}}; + let defaultExport = typeof config.defaultExport != "boolean" ? true : config.defaultExport; + if (!config.all && defaultExport && Cache.modules[type].export[cacheString]) return Cache.modules[type].export[cacheString]; + else if (!config.all && !defaultExport && Cache.modules[type].module[cacheString]) return Cache.modules[type].module[cacheString]; + else { + let m = BDFDB.ModuleUtils.find(filter, config); + if (m) { + if (!config.all) { + if (defaultExport) Cache.modules[type].export[cacheString] = m; + else Cache.modules[type].module[cacheString] = m; + } + return m; + } + else if (!config.noWarnings) BDFDB.LogUtils.warn(`${cacheString} [${type}] not found in WebModules`); + } + }; + Internal.checkModuleStrings = function (module, strings, config = {}) { + const check = (s1, s2) => { + s1 = config.ignoreCase ? s1.toString().toLowerCase() : s1.toString(); + return config.hasNot ? s1.indexOf(s2) == -1 : s1.indexOf(s2) > -1; + }; + return [strings].flat(10).filter(n => typeof n == "string").map(config.ignoreCase ? (n => n.toLowerCase()) : (n => n)).every(string => module && ((typeof module == "function" || typeof module == "string") && (check(module, string) || typeof module.__originalFunction == "function" && check(module.__originalFunction, string)) || typeof module.type == "function" && check(module.type, string) || (typeof module == "function" || typeof module == "object") && module.prototype && Object.keys(module.prototype).filter(n => n.indexOf("render") == 0).some(n => check(module.prototype[n], string)))); + }; + Internal.checkModuleProps = function (module, properties, config = {}) { + return [properties].flat(10).filter(n => typeof n == "string").every(prop => { + const value = module[prop]; + return config.hasNot ? value === undefined : (value !== undefined && !(typeof value == "string" && !value)); + }); + }; + Internal.checkModuleProtos = function (module, protoProps, config = {}) { + return module.prototype && [protoProps].flat(10).filter(n => typeof n == "string").every(prop => { + const value = module.prototype[prop]; + return config.hasNot ? value === undefined : (value !== undefined && !(typeof value == "string" && !value)); + }); + }; + Internal.getModuleString = function (module) { + const id = (BDFDB.ModuleUtils.find(m => m == module && m, {defaultExport: false}) || {}).id; + if (!id) return ""; + const req = Internal.getWebModuleReq(); + return (req.m[id] || "").toString(); + }; + + Internal.lazyLoadModuleImports = function (moduleString) { + return new Promise(callback => { + if (typeof moduleString !== "string") moduleString = Internal.getModuleString(moduleString); + if (!moduleString || typeof moduleString !== "string") { + BDFDB.LogUtils.error("Trying to lazy load Imports but Module is not a String"); + return callback(null); + } + let run = true, imports = [], menuIndexes = []; + while (run) { + const [matchString, promiseMatch, menuRequest] = moduleString.match(/return .*?(Promise\.all\(.+?\))\.then\((.+?)\)\)/) ?? []; + if (!promiseMatch) run = false; + else { + imports = imports.concat(promiseMatch.match(/\d+/g)?.map(e => Number(e))); + menuIndexes.push(menuRequest.match(/\d+/)?.[0]); + moduleString = moduleString.replace(matchString, ""); + } + } + if (!imports.length || !menuIndexes.length) { + BDFDB.LogUtils.error("Trying to lazy load Imports but could not find Indexes"); + return callback(null); + } + const req = Internal.getWebModuleReq(); + Promise.all(BDFDB.ArrayUtils.removeCopies(imports).map(i => req.e(i))).then(_ => Promise.all(BDFDB.ArrayUtils.removeCopies(menuIndexes).map(i => req(i)))).then(callback); + }); + }; + + BDFDB.ModuleUtils = {}; + BDFDB.ModuleUtils.find = function (filter, config = {}) { + let defaultExport = typeof config.defaultExport != "boolean" ? true : config.defaultExport; + let onlySearchUnloaded = typeof config.onlySearchUnloaded != "boolean" ? false : config.onlySearchUnloaded; + let all = typeof config.all != "boolean" ? false : config.all; + const req = Internal.getWebModuleReq(); + const found = []; + if (!onlySearchUnloaded) for (let i in req.c) if (req.c.hasOwnProperty(i) && req.c[i].exports != window) { + let m = req.c[i].exports, r = null; + if (m && (typeof m == "object" || typeof m == "function")) { + if (!!(r = filter(m))) { + if (all) found.push(defaultExport ? r : req.c[i]); + else return defaultExport ? r : req.c[i]; + } + else if (Object.keys(m).length < 400) for (let key of Object.keys(m)) try { + if (m[key] && !!(r = filter(m[key]))) { + if (all) found.push(defaultExport ? r : req.c[i]); + else return defaultExport ? r : req.c[i]; + } + } catch (err) {} + } + if (config.moduleName && m && m[config.moduleName] && (typeof m[config.moduleName] == "object" || typeof m[config.moduleName] == "function")) { + if (!!(r = filter(m[config.moduleName]))) { + if (all) found.push(defaultExport ? r : req.c[i]); + else return defaultExport ? r : req.c[i]; + } + else if (m[config.moduleName].type && (typeof m[config.moduleName].type == "object" || typeof m[config.moduleName].type == "function") && !!(r = filter(m[config.moduleName].type))) { + if (all) found.push(defaultExport ? r : req.c[i]); + else return defaultExport ? r : req.c[i]; + } + } + if (m && m.__esModule && m.default && (typeof m.default == "object" || typeof m.default == "function")) { + if (!!(r = filter(m.default))) { + if (all) found.push(defaultExport ? r : req.c[i]); + else return defaultExport ? r : req.c[i]; + } + else if (m.default.type && (typeof m.default.type == "object" || typeof m.default.type == "function") && !!(r = filter(m.default.type))) { + if (all) found.push(defaultExport ? r : req.c[i]); + else return defaultExport ? r : req.c[i]; + } + } + } + for (let i in req.m) if (req.m.hasOwnProperty(i)) { + let m = req.m[i]; + if (m && typeof m == "function") { + if (req.c[i] && !onlySearchUnloaded && filter(m)) { + if (all) found.push(defaultExport ? req.c[i].exports : req.c[i]); + else return defaultExport ? req.c[i].exports : req.c[i]; + } + if (!req.c[i] && onlySearchUnloaded && filter(m)) { + const resolved = {}, resolved2 = {}; + m(resolved, resolved2, req); + const trueResolved = resolved2 && BDFDB.ObjectUtils.isEmpty(resolved2) ? resolved : resolved2; + if (all) found.push(defaultExport ? trueResolved.exports : trueResolved); + else return defaultExport ? trueResolved.exports : trueResolved; + } + } + } + if (all) return found; + }; + BDFDB.ModuleUtils.findByProperties = function (...properties) { + properties = properties.flat(10); + let config = properties.pop(); + if (typeof config == "string") { + properties.push(config); + config = {}; + } + return Internal.findModule("props", JSON.stringify(properties), m => Internal.checkModuleProps(m, properties) && m, config); + }; + BDFDB.ModuleUtils.findByName = function (name, config = {}) { + return Internal.findModule("name", JSON.stringify(name), m => m.displayName === name && m || m.render && m.render.displayName === name && m || m.constructor && m.constructor.displayName === name && m || m[name] && m[name].displayName === name && m[name] || typeof m.getName == "function" && m.getName() == name && m, config); + }; + BDFDB.ModuleUtils.findByString = function (...strings) { + strings = strings.flat(10); + let config = strings.pop(); + if (typeof config == "string") { + strings.push(config); + config = {}; + } + return Internal.findModule("string", JSON.stringify(strings), m => Internal.checkModuleStrings(m, strings) && m, config); + }; + BDFDB.ModuleUtils.findByPrototypes = function (...protoProps) { + protoProps = protoProps.flat(10); + let config = protoProps.pop(); + if (typeof config == "string") { + protoProps.push(config); + config = {}; + } + return Internal.findModule("proto", JSON.stringify(protoProps), m => Internal.checkModuleProtos(m, protoProps) && m, config); + }; + BDFDB.ModuleUtils.findStringObject = function (props, config = {}) { + let firstReturn = BDFDB.ModuleUtils.find(m => { + let amount = Object.keys(m).length; + return (!config.length || (config.smaller ? amount < config.length : amount == config.length)) && [props].flat(10).every(prop => typeof m[prop] == "string") && m; + }, {all: config.all, defaultExport: config.defaultExport}); + if (!config.all && firstReturn) return firstReturn; + let secondReturn = BDFDB.ModuleUtils.find(m => { + if (typeof m != "function") return false; + let stringified = m.toString().replace(/\s/g, ""); + if (stringified.indexOf(".exports={") == -1 || !/function\([A-z],[A-z],[A-z]\)\{"usestrict";[A-z]\.exports=\{/.test(stringified)) return false; + let amount = stringified.split(":\"").length - 1; + return (!config.length || (config.smaller ? amount < config.length : amount == config.length)) && [props].flat(10).every(string => stringified.indexOf(`${string}:`) > -1) && m; + }, {onlySearchUnloaded: true, all: config.all, defaultExport: config.defaultExport}); + if (!config.all) return secondReturn; + return BDFDB.ArrayUtils.removeCopies([firstReturn].concat(secondReturn).flat(10)); + }; + + const DiscordConstantsObject = BDFDB.ModuleUtils.findByProperties("AnalyticsSections", "ChannelTypes", "MessageTypes"); + if (InternalData.CustomDiscordConstants) DiscordConstants = Object.assign(DiscordConstants, InternalData.CustomDiscordConstants); + if (DiscordConstantsObject) DiscordConstants = Object.assign(DiscordConstants, DiscordConstantsObject); + Internal.DiscordConstants = new Proxy(DiscordConstants, { + get: function (_, item) { + if (DiscordConstants[item]) return DiscordConstants[item]; + if (!InternalData.DiscordConstants[item]) { + BDFDB.LogUtils.warn([item, "Object not found in DiscordConstants"]); + return {}; + } + DiscordConstants[item] = BDFDB.ModuleUtils.findByProperties(InternalData.DiscordConstants[item]); + return DiscordConstants[item] ? DiscordConstants[item] : {}; + } + }); + BDFDB.DiscordConstants = Internal.DiscordConstants; + + Internal.DiscordObjects = new Proxy(DiscordObjects, { + get: function (_, item) { + if (DiscordObjects[item]) return DiscordObjects[item]; + if (!InternalData.DiscordObjects[item]) return (function () {}); + let defaultExport = InternalData.DiscordObjects[item].exported == undefined ? true : InternalData.DiscordObjects[item].exported; + if (InternalData.DiscordObjects[item].props) DiscordObjects[item] = BDFDB.ModuleUtils.findByPrototypes(InternalData.DiscordObjects[item].props, {defaultExport}); + else if (InternalData.DiscordObjects[item].strings) DiscordObjects[item] = BDFDB.ModuleUtils.findByString(InternalData.DiscordObjects[item].strings, {defaultExport}); + return DiscordObjects[item] ? DiscordObjects[item] : (function () {}); + } + }); + BDFDB.DiscordObjects = Internal.DiscordObjects; + + Internal.LibraryRequires = new Proxy(LibraryRequires, { + get: function (_, item) { + if (item == "request") return requestFunction; + if (LibraryRequires[item]) return LibraryRequires[item]; + if (InternalData.LibraryRequires.indexOf(item) == -1) return (function () {}); + try {LibraryRequires[item] = require(item);} + catch (err) {} + return LibraryRequires[item] ? LibraryRequires[item] : (function () {}); + } + }); + BDFDB.LibraryRequires = Internal.LibraryRequires; + + Internal.LibraryStores = new Proxy(LibraryStores, { + get: function (_, item) { + if (LibraryStores[item]) return LibraryStores[item]; + LibraryStores[item] = BDFDB.ModuleUtils.find(m => m && typeof m.getName == "function" && m.getName() == item && m); + if (!LibraryStores[item]) BDFDB.LogUtils.warn([item, "could not be found in Webmodule Stores"]); + return LibraryStores[item] ? LibraryStores[item] : null; + } + }); + BDFDB.LibraryStores = Internal.LibraryStores; + + BDFDB.StoreChangeUtils = {}; + BDFDB.StoreChangeUtils.add = function (plugin, store, callback) { + plugin = plugin == BDFDB && Internal || plugin; + if (!BDFDB.ObjectUtils.is(plugin) || !BDFDB.ObjectUtils.is(store) || typeof store.addChangeListener != "function" || typeof callback != "function") return; + BDFDB.StoreChangeUtils.remove(plugin, store, callback); + if (!BDFDB.ArrayUtils.is(plugin.changeListeners)) plugin.changeListeners = []; + plugin.changeListeners.push({store, callback}); + store.addChangeListener(callback); + }; + BDFDB.StoreChangeUtils.remove = function (plugin, store, callback) { + plugin = plugin == BDFDB && Internal || plugin; + if (!BDFDB.ObjectUtils.is(plugin) || !BDFDB.ArrayUtils.is(plugin.changeListeners)) return; + if (!store) { + while (plugin.changeListeners.length) { + let listener = plugin.changeListeners.pop(); + listener.store.removeChangeListener(listener.callback); + } + } + else if (BDFDB.ObjectUtils.is(store) && typeof store.addChangeListener == "function") { + if (!callback) { + for (let listener of plugin.changeListeners) { + let removedListeners = []; + if (listener.store == store) { + listener.store.removeChangeListener(listener.callback); + removedListeners.push(listener); + } + if (removedListeners.length) plugin.changeListeners = plugin.changeListeners.filter(listener => !removedListeners.includes(listener)); + } + } + else if (typeof callback == "function") { + store.removeChangeListener(callback); + plugin.changeListeners = plugin.changeListeners.filter(listener => listener.store == store && listener.callback == callback); + } + } + }; + + var pressedKeys = [], mousePosition; + BDFDB.ListenerUtils = {}; + BDFDB.ListenerUtils.isPressed = function (key) { + return pressedKeys.includes(key); + }; + BDFDB.ListenerUtils.getPosition = function (key) { + return mousePosition; + }; + BDFDB.ListenerUtils.add = function (plugin, ele, actions, selectorOrCallback, callbackOrNothing) { + plugin = plugin == BDFDB && Internal || plugin; + if (!BDFDB.ObjectUtils.is(plugin) || (!Node.prototype.isPrototypeOf(ele) && ele !== window) || !actions) return; + let callbackIs4th = typeof selectorOrCallback == "function"; + let selector = callbackIs4th ? undefined : selectorOrCallback; + let callback = callbackIs4th ? selectorOrCallback : callbackOrNothing; + if (typeof callback != "function") return; + BDFDB.ListenerUtils.remove(plugin, ele, actions, selector); + for (let action of actions.split(" ")) { + action = action.split("."); + let eventName = action.shift().toLowerCase(); + if (!eventName) return; + let origEventName = eventName; + eventName = eventName == "mouseenter" || eventName == "mouseleave" ? "mouseover" : eventName; + let namespace = (action.join(".") || "") + plugin.name; + if (!BDFDB.ArrayUtils.is(plugin.eventListeners)) plugin.eventListeners = []; + let eventCallback = null; + if (selector) { + if (origEventName == "mouseenter" || origEventName == "mouseleave") eventCallback = e => { + for (let child of e.path) if (typeof child.matches == "function" && child.matches(selector) && !child[namespace + "BDFDB" + origEventName]) { + child[namespace + "BDFDB" + origEventName] = true; + if (origEventName == "mouseenter") callback(BDFDB.ListenerUtils.copyEvent(e, child)); + let mouseOut = e2 => { + if (e2.target.contains(child) || e2.target == child || !child.contains(e2.target)) { + if (origEventName == "mouseleave") callback(BDFDB.ListenerUtils.copyEvent(e, child)); + delete child[namespace + "BDFDB" + origEventName]; + document.removeEventListener("mouseout", mouseOut); + } + }; + document.addEventListener("mouseout", mouseOut); + break; + } + }; + else eventCallback = e => { + for (let child of e.path) if (typeof child.matches == "function" && child.matches(selector)) { + callback(BDFDB.ListenerUtils.copyEvent(e, child)); + break; + } + }; + } + else eventCallback = e => callback(BDFDB.ListenerUtils.copyEvent(e, ele)); + + let observer; + if (Node.prototype.isPrototypeOf(ele)) { + observer = new MutationObserver(changes => changes.forEach(change => { + const nodes = Array.from(change.removedNodes); + if (nodes.indexOf(ele) > -1 || nodes.some(n => n.contains(ele))) BDFDB.ListenerUtils.remove(plugin, ele, actions, selector); + })); + observer.observe(document.body, {subtree: true, childList: true}); + } + + plugin.eventListeners.push({ele, eventName, origEventName, namespace, selector, eventCallback, observer}); + ele.addEventListener(eventName, eventCallback, true); + } + }; + BDFDB.ListenerUtils.remove = function (plugin, ele, actions = "", selector) { + plugin = plugin == BDFDB && Internal || plugin; + if (!BDFDB.ObjectUtils.is(plugin) || !BDFDB.ArrayUtils.is(plugin.eventListeners)) return; + if (!ele) { + while (plugin.eventListeners.length) { + let listener = plugin.eventListeners.pop(); + listener.ele.removeEventListener(listener.eventName, listener.eventCallback, true); + if (listener.observer) listener.observer.disconnect(); + } + } + else if (Node.prototype.isPrototypeOf(ele) || ele === window) { + for (let action of actions.split(" ")) { + action = action.split("."); + let eventName = action.shift().toLowerCase(); + let namespace = (action.join(".") || "") + plugin.name; + for (let listener of plugin.eventListeners) { + let removedListeners = []; + if (listener.ele == ele && (!eventName || listener.origEventName == eventName) && listener.namespace == namespace && (selector === undefined || listener.selector == selector)) { + listener.ele.removeEventListener(listener.eventName, listener.eventCallback, true); + if (listener.observer) listener.observer.disconnect(); + removedListeners.push(listener); + } + if (removedListeners.length) plugin.eventListeners = plugin.eventListeners.filter(listener => !removedListeners.includes(listener)); + } + } + } + }; + const leftSideMap = { + 16: 160, + 17: 170, + 18: 164 + }; + BDFDB.ListenerUtils.addGlobal = function (plugin, id, keybind, action) { + plugin = plugin == BDFDB && Internal || plugin; + if (!BDFDB.ObjectUtils.is(plugin) || !id || !BDFDB.ArrayUtils.is(keybind) || typeof action != "function") return; + if (!BDFDB.ObjectUtils.is(plugin.globalKeybinds)) plugin.globalKeybinds = {}; + BDFDB.ListenerUtils.removeGlobal(plugin, id); + plugin.globalKeybinds[id] = BDFDB.NumberUtils.generateId(Object.entries(plugin.globalKeybinds).map(n => n[1])); + BDFDB.LibraryModules.WindowUtils.inputEventRegister(plugin.globalKeybinds[id], keybind.map(n => [0, n]), action, {blurred: true, focused: true, keydown: false, keyup: true}); + if (Object.keys(leftSideMap).some(key => keybind.indexOf(parseInt(key)) > -1)) { + const alternativeId = id + "___ALTERNATIVE"; + plugin.globalKeybinds[alternativeId] = BDFDB.NumberUtils.generateId(Object.entries(plugin.globalKeybinds).map(n => n[1])); + BDFDB.LibraryModules.WindowUtils.inputEventRegister(plugin.globalKeybinds[alternativeId], keybind.map(n => [0, leftSideMap[n] ?? n]), action, {blurred: true, focused: true, keydown: false, keyup: true}); + } + return (_ => BDFDB.ListenerUtils.removeGlobal(plugin, id)); + }; + BDFDB.ListenerUtils.removeGlobal = function (plugin, id) { + if (!BDFDB.ObjectUtils.is(plugin) || !plugin.globalKeybinds) return; + if (!id) { + for (let cachedId in plugin.globalKeybinds) BDFDB.LibraryModules.WindowUtils.inputEventUnregister(plugin.globalKeybinds[cachedId]); + plugin.globalKeybinds = {}; + } + else { + BDFDB.LibraryModules.WindowUtils.inputEventUnregister(plugin.globalKeybinds[id]); + delete plugin.globalKeybinds[id]; + const alternativeId = id + "___ALTERNATIVE"; + BDFDB.LibraryModules.WindowUtils.inputEventUnregister(plugin.globalKeybinds[alternativeId]); + delete plugin.globalKeybinds[alternativeId]; + } + }; + BDFDB.ListenerUtils.multiAdd = function (node, actions, callback) { + if (!Node.prototype.isPrototypeOf(node) || !actions || typeof callback != "function") return; + for (let action of actions.trim().split(" ").filter(n => n)) node.addEventListener(action, callback, true); + }; + BDFDB.ListenerUtils.multiRemove = function (node, actions, callback) { + if (!Node.prototype.isPrototypeOf(node) || !actions || typeof callback != "function") return; + for (let action of actions.trim().split(" ").filter(n => n)) node.removeEventListener(action, callback, true); + }; + BDFDB.ListenerUtils.addToChildren = function (node, actions, selector, callback) { + if (!Node.prototype.isPrototypeOf(node) || !actions || !selector || !selector.trim() || typeof callback != "function") return; + for (let action of actions.trim().split(" ").filter(n => n)) { + let eventCallback = callback; + if (action == "mouseenter" || action == "mouseleave") eventCallback = e => {if (e.target.matches(selector)) callback(e);}; + node.querySelectorAll(selector.trim()).forEach(child => child.addEventListener(action, eventCallback, true)); + } + }; + BDFDB.ListenerUtils.copyEvent = function (e, ele) { + if (!e || !e.constructor || !e.type) return e; + let eCopy = new e.constructor(e.type, e); + Object.defineProperty(eCopy, "originalEvent", {value: e}); + Object.defineProperty(eCopy, "which", {value: e.which}); + Object.defineProperty(eCopy, "keyCode", {value: e.keyCode}); + Object.defineProperty(eCopy, "path", {value: e.path}); + Object.defineProperty(eCopy, "relatedTarget", {value: e.relatedTarget}); + Object.defineProperty(eCopy, "srcElement", {value: e.srcElement}); + Object.defineProperty(eCopy, "target", {value: e.target}); + Object.defineProperty(eCopy, "toElement", {value: e.toElement}); + if (ele) Object.defineProperty(eCopy, "currentTarget", {value: ele}); + return eCopy; + }; + BDFDB.ListenerUtils.stopEvent = function (e) { + if (BDFDB.ObjectUtils.is(e)) { + if (typeof e.preventDefault == "function") e.preventDefault(); + if (typeof e.stopPropagation == "function") e.stopPropagation(); + if (typeof e.stopImmediatePropagation == "function") e.stopImmediatePropagation(); + if (BDFDB.ObjectUtils.is(e.originalEvent)) { + if (typeof e.originalEvent.preventDefault == "function") e.originalEvent.preventDefault(); + if (typeof e.originalEvent.stopPropagation == "function") e.originalEvent.stopPropagation(); + if (typeof e.originalEvent.stopImmediatePropagation == "function") e.originalEvent.stopImmediatePropagation(); + } + } + }; + + var Toasts = [], NotificationBars = []; + var ToastQueues = {}, DesktopNotificationQueue = {queue: [], running: false}; + for (let key in Internal.DiscordConstants.ToastPositions) ToastQueues[Internal.DiscordConstants.ToastPositions[key]] = {queue: [], full: false}; + + BDFDB.NotificationUtils = {}; + BDFDB.NotificationUtils.toast = function (children, config = {}) { + if (!children) return; + let app = document.querySelector(BDFDB.dotCN.appmount) || document.body; + if (!app) return; + let position = config.position && Internal.DiscordConstants.ToastPositions[config.position] || Internal.settings.choices.toastPosition && Internal.DiscordConstants.ToastPositions[Internal.settings.choices.toastPosition] || Internal.DiscordConstants.ToastPositions.right; + let queue = ToastQueues[position] || {}; + + const runQueue = _ => { + if (queue.full) return; + let data = queue.queue.shift(); + if (!data) return; + + let id = BDFDB.NumberUtils.generateId(Toasts); + let toasts = document.querySelector(BDFDB.dotCN.toasts + BDFDB.dotCN[position]); + if (!toasts) { + toasts = BDFDB.DOMUtils.create(`
`); + app.appendChild(toasts); + } + + if (data.config.id) data.toast.id = data.config.id.split(" ").join(""); + if (data.config.className) BDFDB.DOMUtils.addClass(data.toast, data.config.className); + if (data.config.css) BDFDB.DOMUtils.appendLocalStyle("BDFDBcustomToast" + id, data.config.css); + if (data.config.style) data.toast.style = Object.assign({}, data.toast.style, data.config.style); + + let backgroundColor, fontColor, barColor; + + let type = data.config.type && BDFDB.disCN["toast" + data.config.type]; + if (!type) { + barColor = BDFDB.ColorUtils.getType(data.config.barColor) ? BDFDB.ColorUtils.convert(data.config.barColor, "HEX") : data.config.barColor; + let comp = BDFDB.ColorUtils.convert(data.config.color, "RGBCOMP"); + if (comp) { + backgroundColor = BDFDB.ColorUtils.convert(comp, "HEX"); + fontColor = comp[0] > 180 && comp[1] > 180 && comp[2] > 180 ? "#000" : "#FFF"; + BDFDB.DOMUtils.addClass(data.toast, BDFDB.disCN.toastcustom); + } + else BDFDB.DOMUtils.addClass(data.toast, BDFDB.disCN.toastdefault); + } + else BDFDB.DOMUtils.addClass(data.toast, type); + + let loadingInterval; + let disableInteractions = data.config.disableInteractions && typeof data.config.onClick != "function"; + let timeout = typeof data.config.timeout == "number" && !disableInteractions ? data.config.timeout : 3000; + timeout = (timeout > 0 ? timeout : 600000) + 300; + if (data.config.ellipsis && typeof data.children == "string") loadingInterval = BDFDB.TimeUtils.interval(_ => data.toast.update(data.children.endsWith(".....") ? data.children.slice(0, -5) : data.children + "."), 500); + + let closeTimeout = BDFDB.TimeUtils.timeout(_ => data.toast.close(), timeout); + data.toast.close = _ => { + BDFDB.TimeUtils.clear(closeTimeout); + if (document.contains(data.toast)) { + BDFDB.DOMUtils.addClass(data.toast, BDFDB.disCN.toastclosing); + data.toast.style.setProperty("pointer-events", "none", "important"); + BDFDB.TimeUtils.timeout(_ => { + if (typeof data.config.onClose == "function") data.config.onClose(); + BDFDB.TimeUtils.clear(loadingInterval); + BDFDB.ArrayUtils.remove(Toasts, id); + BDFDB.DOMUtils.removeLocalStyle("BDFDBcustomToast" + id); + data.toast.remove(); + if (!toasts.querySelectorAll(BDFDB.dotCN.toast).length) toasts.remove(); + }, 300); + } + queue.full = false; + runQueue(); + }; + + if (disableInteractions) data.toast.style.setProperty("pointer-events", "none", "important"); + else { + BDFDB.DOMUtils.addClass(data.toast, BDFDB.disCN.toastclosable); + data.toast.addEventListener("click", event => { + if (typeof data.config.onClick == "function" && !BDFDB.DOMUtils.getParent(BDFDB.dotCN.toastcloseicon, event.target)) data.config.onClick(); + data.toast.close(); + }); + if (typeof closeTimeout.pause == "function") { + let paused = false; + data.toast.addEventListener("mouseenter", _ => { + if (paused) return; + paused = true; + closeTimeout.pause(); + }); + data.toast.addEventListener("mouseleave", _ => { + if (!paused) return; + paused = false; + closeTimeout.resume(); + }); + } + } + + toasts.appendChild(data.toast); + BDFDB.TimeUtils.timeout(_ => BDFDB.DOMUtils.removeClass(data.toast, BDFDB.disCN.toastopening)); + + let icon = data.config.avatar ? BDFDB.ReactUtils.createElement(Internal.LibraryComponents.Avatars.Avatar, { + src: data.config.avatar, + size: Internal.LibraryComponents.AvatarConstants.Sizes.SIZE_24 + }) : ((data.config.icon || data.config.type && Internal.DiscordConstants.ToastIcons[data.config.type]) ? BDFDB.ReactUtils.createElement(Internal.LibraryComponents.SvgIcon, { + name: data.config.type && Internal.DiscordConstants.ToastIcons[data.config.type] && Internal.LibraryComponents.SvgIcon.Names[Internal.DiscordConstants.ToastIcons[data.config.type]], + iconSVG: data.config.icon, + width: 18, + height: 18, + nativeClass: true + }) : null); + + BDFDB.ReactUtils.render(BDFDB.ReactUtils.createElement(class BDFDB_Toast extends Internal.LibraryModules.React.Component { + componentDidMount() { + data.toast.update = newChildren => { + if (!newChildren) return; + data.children = newChildren; + BDFDB.ReactUtils.forceUpdate(this); + }; + } + render() { + return BDFDB.ReactUtils.createElement(Internal.LibraryModules.React.Fragment, { + children: [ + BDFDB.ReactUtils.createElement("div", { + className: BDFDB.disCN.toastbg, + style: {backgroundColor: backgroundColor} + }), + BDFDB.ReactUtils.createElement("div", { + className: BDFDB.disCN.toastinner, + style: {color: fontColor}, + children: [ + icon && BDFDB.ReactUtils.createElement("div", { + className: BDFDB.DOMUtils.formatClassName(data.config.avatar && BDFDB.disCN.toastavatar, BDFDB.disCN.toasticon, data.config.iconClassName), + children: icon + }), + BDFDB.ReactUtils.createElement("div", { + className: BDFDB.DOMUtils.formatClassName(BDFDB.disCN.toasttext, data.config.textClassName), + children: data.children + }), + !disableInteractions && BDFDB.ReactUtils.createElement(Internal.LibraryComponents.SvgIcon, { + className: BDFDB.disCN.toastcloseicon, + name: Internal.LibraryComponents.SvgIcon.Names.CLOSE, + width: 16, + height: 16 + }) + ].filter(n => n) + }), + BDFDB.ReactUtils.createElement("div", { + className: BDFDB.DOMUtils.formatClassName(BDFDB.disCN.toastbar, barColor && BDFDB.disCN.toastcustombar), + style: { + backgroundColor: barColor, + animation: `toast-bar ${timeout}ms normal linear` + } + }) + ] + }); + } + }, {}), data.toast); + + queue.full = (BDFDB.ArrayUtils.sum(Array.from(toasts.childNodes).map(c => { + let height = BDFDB.DOMUtils.getRects(c).height; + return height > 50 ? height : 50; + })) - 100) > BDFDB.DOMUtils.getRects(app).height; + + if (typeof data.config.onShow == "function") data.config.onShow(); + }; + + let toast = BDFDB.DOMUtils.create(`
`); + toast.update = _ => {}; + queue.queue.push({children, config, toast}); + runQueue(); + return toast; + }; + BDFDB.NotificationUtils.desktop = function (content, config = {}) { + if (!content) return; + + const queue = _ => { + DesktopNotificationQueue.queue.push({content, config}); + runQueue(); + }; + const runQueue = _ => { + if (DesktopNotificationQueue.running) return; + let data = DesktopNotificationQueue.queue.shift(); + if (!data) return; + + DesktopNotificationQueue.running = true; + let muted = data.config.silent; + data.config.silent = data.config.silent || data.config.sound ? true : false; + let audio = new Audio(); + if (!muted && data.config.sound) { + audio.src = data.config.sound; + audio.play(); + } + let notification = new Notification(data.content, data.config); + + let disableInteractions = data.config.disableInteractions && typeof data.config.onClick != "function"; + if (disableInteractions) notification.onclick = _ => {}; + else notification.onclick = _ => { + if (typeof data.config.onClick == "function") data.config.onClick(); + notification.close(); + }; + + notification.onclose = _ => { + audio.pause(); + DesktopNotificationQueue.running = false; + BDFDB.TimeUtils.timeout(runQueue, 1000); + } + }; + + if (!("Notification" in window)) {} + else if (Notification.permission === "granted") queue(); + else if (Notification.permission !== "denied") Notification.requestPermission(function (response) {if (response === "granted") queue();}); + }; + BDFDB.NotificationUtils.notice = function (text, config = {}) { + if (!text) return; + let layers = document.querySelector(BDFDB.dotCN.layers) || document.querySelector(BDFDB.dotCN.appmount); + if (!layers) return; + let id = BDFDB.NumberUtils.generateId(NotificationBars); + let notice = BDFDB.DOMUtils.create(`
`); + layers.parentElement.insertBefore(notice, layers); + let noticeText = notice.querySelector(BDFDB.dotCN.noticetext); + if (config.platform) for (let platform of config.platform.split(" ")) if (InternalData.DiscordClasses["noticeicon" + platform]) { + let icon = BDFDB.DOMUtils.create(``); + BDFDB.DOMUtils.addClass(icon, BDFDB.disCN.noticeplatformicon); + BDFDB.DOMUtils.removeClass(icon, BDFDB.disCN.noticeicon); + notice.insertBefore(icon, noticeText); + } + if (config.customIcon) { + let icon = document.createElement("i"), iconInner = BDFDB.DOMUtils.create(config.customIcon); + if (iconInner.nodeType == Node.TEXT_NODE) icon.style.setProperty("background", `url(${config.customIcon}) center/cover no-repeat`); + else { + icon = iconInner; + if ((icon.tagName || "").toUpperCase() == "SVG") { + icon.removeAttribute("width"); + icon.setAttribute("height", "100%"); + } + } + BDFDB.DOMUtils.addClass(icon, BDFDB.disCN.noticeplatformicon); + BDFDB.DOMUtils.removeClass(icon, BDFDB.disCN.noticeicon); + notice.insertBefore(icon, noticeText); + } + if (BDFDB.ArrayUtils.is(config.buttons)) for (let data of config.buttons) { + let contents = typeof data.contents == "string" && data.contents; + if (contents) { + let button = BDFDB.DOMUtils.create(``); + button.addEventListener("click", event => { + if (data.close) notice.close(); + if (typeof data.onClick == "function") data.onClick(event, notice); + }); + if (typeof data.onMouseEnter == "function") button.addEventListener("mouseenter", event => data.onMouseEnter(event, notice)); + if (typeof data.onMouseLeave == "function") button.addEventListener("mouseleave", event => data.onMouseLeave(event, notice)); + notice.appendChild(button); + } + } + if (config.id) notice.id = config.id.split(" ").join(""); + if (config.className) BDFDB.DOMUtils.addClass(notice, config.className); + if (config.textClassName) BDFDB.DOMUtils.addClass(noticeText, config.textClassName); + if (config.css) BDFDB.DOMUtils.appendLocalStyle("BDFDBcustomNotificationBar" + id, config.css); + if (config.style) notice.style = config.style; + if (config.html) noticeText.innerHTML = text; + else { + let link = document.createElement("a"); + let newText = []; + for (let word of text.split(" ")) { + let encodedWord = BDFDB.StringUtils.htmlEscape(word); + link.href = word; + newText.push(link.host && link.host !== window.location.host ? `` : encodedWord); + } + noticeText.innerHTML = newText.join(" "); + } + let type = null; + if (config.type && !document.querySelector(BDFDB.dotCNS.chatbase + BDFDB.dotCN.noticestreamer)) { + if (type = BDFDB.disCN["notice" + config.type]) BDFDB.DOMUtils.addClass(notice, type); + if (config.type == "premium") { + let noticeButton = notice.querySelector(BDFDB.dotCN.noticebutton); + if (noticeButton) BDFDB.DOMUtils.addClass(noticeButton, BDFDB.disCN.noticepremiumaction); + BDFDB.DOMUtils.addClass(noticeText, BDFDB.disCN.noticepremiumtext); + notice.insertBefore(BDFDB.DOMUtils.create(``), noticeText); + } + } + if (!type) { + let comp = BDFDB.ColorUtils.convert(config.color, "RGBCOMP"); + if (comp) { + let fontColor = comp[0] > 180 && comp[1] > 180 && comp[2] > 180 ? "#000" : "#FFF"; + let backgroundColor = BDFDB.ColorUtils.convert(comp, "HEX"); + BDFDB.DOMUtils.appendLocalStyle("BDFDBcustomNotificationBarColorCorrection" + id, `${BDFDB.dotCN.noticewrapper}[notice-id="${id}"]{background-color: ${backgroundColor} !important;}${BDFDB.dotCN.noticewrapper}[notice-id="${id}"] ${BDFDB.dotCN.noticetext} {color: ${fontColor} !important;}${BDFDB.dotCN.noticewrapper}[notice-id="${id}"] ${BDFDB.dotCN.noticebutton} {color: ${fontColor} !important;border-color: ${BDFDB.ColorUtils.setAlpha(fontColor, 0.25, "RGBA")} !important;}${BDFDB.dotCN.noticewrapper}[notice-id="${id}"] ${BDFDB.dotCN.noticebutton}:hover {color: ${backgroundColor} !important;background-color: ${fontColor} !important;}${BDFDB.dotCN.noticewrapper}[notice-id="${id}"] ${BDFDB.dotCN.noticedismissicon} path {fill: ${fontColor} !important;}`); + BDFDB.DOMUtils.addClass(notice, BDFDB.disCN.noticecustom); + } + else BDFDB.DOMUtils.addClass(notice, BDFDB.disCN.noticedefault); + } + notice.close = _ => { + BDFDB.DOMUtils.addClass(notice, BDFDB.disCN.noticeclosing); + if (notice.tooltip && typeof notice.tooltip.removeTooltip == "function") notice.tooltip.removeTooltip(); + BDFDB.TimeUtils.timeout(_ => { + if (typeof config.onClose == "function") config.onClose(); + BDFDB.ArrayUtils.remove(NotificationBars, id); + BDFDB.DOMUtils.removeLocalStyle("BDFDBcustomNotificationBar" + id); + BDFDB.DOMUtils.removeLocalStyle("BDFDBcustomNotificationBarColorCorrection" + id); + BDFDB.DOMUtils.remove(notice); + }, 500); + }; + notice.querySelector(BDFDB.dotCN.noticedismiss).addEventListener("click", notice.close); + return notice; + }; + BDFDB.NotificationUtils.alert = function (header, body) { + if (typeof header == "string" && typeof header == "string" && BdApi && typeof BdApi.alert == "function") BdApi.alert(header, body); + }; + + var Tooltips = []; + BDFDB.TooltipUtils = {}; + BDFDB.TooltipUtils.create = function (anker, text, config = {}) { + if (!text && !config.guild) return null; + const itemLayerContainer = document.querySelector(BDFDB.dotCN.app + " ~ " + BDFDB.dotCN.itemlayercontainer) || document.querySelector(BDFDB.dotCN.itemlayercontainer); + if (!itemLayerContainer || !Node.prototype.isPrototypeOf(anker) || !document.contains(anker)) return null; + const id = BDFDB.NumberUtils.generateId(Tooltips); + const itemLayer = BDFDB.DOMUtils.create(`
`); + itemLayerContainer.appendChild(itemLayer); + + const tooltip = itemLayer.firstElementChild; + const tooltipContent = itemLayer.querySelector(BDFDB.dotCN.tooltipcontent); + const tooltipPointer = itemLayer.querySelector(BDFDB.dotCN.tooltippointer); + + if (config.id) tooltip.id = config.id.split(" ").join(""); + + if (typeof config.type != "string" || !BDFDB.disCN["tooltip" + config.type.toLowerCase()]) config.type = "top"; + let type = config.type.toLowerCase(); + BDFDB.DOMUtils.addClass(tooltip, BDFDB.disCN["tooltip" + type], config.className); + + let fontColorIsGradient = false, customBackgroundColor = false, style = ""; + if (config.style) style += config.style; + if (config.fontColor) { + fontColorIsGradient = BDFDB.ObjectUtils.is(config.fontColor); + if (!fontColorIsGradient) style = (style ? (style + " ") : "") + `color: ${BDFDB.ColorUtils.convert(config.fontColor, "RGBA")} !important;` + } + if (config.backgroundColor) { + customBackgroundColor = true; + let backgroundColorIsGradient = BDFDB.ObjectUtils.is(config.backgroundColor); + let backgroundColor = !backgroundColorIsGradient ? BDFDB.ColorUtils.convert(config.backgroundColor, "RGBA") : BDFDB.ColorUtils.createGradient(config.backgroundColor); + style = (style ? (style + " ") : "") + `background: ${backgroundColor} !important; border-color: ${backgroundColorIsGradient ? BDFDB.ColorUtils.convert(config.backgroundColor[type == "left" ? 100 : 0], "RGBA") : backgroundColor} !important;`; + } + if (style) tooltip.style = style; + const zIndexed = config.zIndex && typeof config.zIndex == "number"; + if (zIndexed) { + itemLayer.style.setProperty("z-index", config.zIndex, "important"); + tooltip.style.setProperty("z-index", config.zIndex, "important"); + tooltipContent.style.setProperty("z-index", config.zIndex, "important"); + BDFDB.DOMUtils.addClass(itemLayerContainer, BDFDB.disCN.itemlayercontainerzindexdisabled); + } + if (typeof config.width == "number" && config.width > 196) { + tooltip.style.setProperty("width", `${config.width}px`, "important"); + tooltip.style.setProperty("max-width", `${config.width}px`, "important"); + } + if (typeof config.maxWidth == "number" && config.maxWidth > 196) { + tooltip.style.setProperty("max-width", `${config.maxWidth}px`, "important"); + } + if (customBackgroundColor) BDFDB.DOMUtils.addClass(tooltip, BDFDB.disCN.tooltipcustom); + else if (config.color && BDFDB.disCN["tooltip" + config.color.toLowerCase()]) BDFDB.DOMUtils.addClass(tooltip, BDFDB.disCN["tooltip" + config.color.toLowerCase()]); + else BDFDB.DOMUtils.addClass(tooltip, BDFDB.disCN.tooltipprimary); + + if (config.list || BDFDB.ObjectUtils.is(config.guild)) BDFDB.DOMUtils.addClass(tooltip, BDFDB.disCN.tooltiplistitem); + + const removeTooltip = _ => { + document.removeEventListener("wheel", wheel); + document.removeEventListener("mousemove", mouseMove); + document.removeEventListener("mouseleave", mouseLeave); + BDFDB.DOMUtils.remove(itemLayer); + BDFDB.ArrayUtils.remove(Tooltips, id); + observer.disconnect(); + if (zIndexed) BDFDB.DOMUtils.removeClass(itemLayerContainer, BDFDB.disCN.itemlayercontainerzindexdisabled); + if (typeof config.onHide == "function") config.onHide(itemLayer, anker); + }; + const setText = newText => { + if (BDFDB.ObjectUtils.is(config.guild)) { + let isMuted = Internal.LibraryStores.UserGuildSettingsStore.isMuted(config.guild.id); + let muteConfig = Internal.LibraryStores.UserGuildSettingsStore.getMuteConfig(config.guild.id); + + let children = [typeof newText == "function" ? newText() : newText].flat(10).filter(n => typeof n == "string" || BDFDB.ReactUtils.isValidElement(n)); + BDFDB.ReactUtils.render(BDFDB.ReactUtils.createElement(Internal.LibraryModules.React.Fragment, { + children: [ + BDFDB.ReactUtils.createElement("div", { + className: BDFDB.DOMUtils.formatClassName(BDFDB.disCN.tooltiprow, BDFDB.disCN.tooltiprowguildname), + children: [ + BDFDB.ReactUtils.createElement(Internal.LibraryComponents.GuildBadge, { + guild: config.guild, + size: 16, + className: BDFDB.disCN.tooltiprowiconv2 + }), + BDFDB.ReactUtils.createElement("span", { + className: BDFDB.DOMUtils.formatClassName(BDFDB.disCN.tooltipguildnametext), + children: fontColorIsGradient ? BDFDB.ReactUtils.createElement(Internal.LibraryComponents.TextGradientElement, { + gradient: BDFDB.ColorUtils.createGradient(config.fontColor), + children: config.guild.toString() + }) : config.guild.toString() + }), + ] + }), + children.length && BDFDB.ReactUtils.createElement("div", { + className: BDFDB.DOMUtils.formatClassName(BDFDB.disCN.tooltiprow, BDFDB.disCN.tooltiprowextra), + children: children + }), + config.note && BDFDB.ReactUtils.createElement("div", { + className: BDFDB.DOMUtils.formatClassName(BDFDB.disCN.tooltiprow, BDFDB.disCN.tooltiprowextra, BDFDB.disCN.tooltipnote), + children: config.note + }), + BDFDB.ReactUtils.createElement(Internal.LibraryComponents.GuildVoiceList, {guild: config.guild}), + isMuted && muteConfig && (muteConfig.end_time == null ? BDFDB.ReactUtils.createElement(Internal.LibraryComponents.TextElement, { + className: BDFDB.DOMUtils.formatClassName(BDFDB.disCN.tooltipmutetext), + size: Internal.LibraryComponents.TextElement.Sizes.SIZE_12, + color: Internal.LibraryComponents.TextElement.Colors.MUTED, + children: BDFDB.LanguageUtils.LanguageStrings.VOICE_CHANNEL_MUTED + }) : BDFDB.ReactUtils.createElement(Internal.LibraryComponents.GuildTooltipMutedText, { + className: BDFDB.DOMUtils.formatClassName(BDFDB.disCN.tooltipmutetext), + muteConfig: muteConfig + })) + ].filter(n => n) + }), tooltipContent); + } + else { + let children = [typeof newText == "function" ? newText() : newText].flat(10).filter(n => typeof n == "string" || BDFDB.ReactUtils.isValidElement(n)); + children.length && BDFDB.ReactUtils.render(BDFDB.ReactUtils.createElement(Internal.LibraryModules.React.Fragment, { + children: [ + fontColorIsGradient ? BDFDB.ReactUtils.createElement(Internal.LibraryComponents.TextGradientElement, { + gradient: BDFDB.ColorUtils.createGradient(config.fontColor), + children: children + }) : children, + config.note && BDFDB.ReactUtils.createElement("div", { + className: BDFDB.DOMUtils.formatClassName(BDFDB.disCN.tooltiprow, BDFDB.disCN.tooltiprowextra, BDFDB.disCN.tooltipnote), + children: config.note + }) + ] + }), tooltipContent); + } + }; + const update = newText => { + if (newText) setText(newText); + let left, top; + const tRects = BDFDB.DOMUtils.getRects(anker); + const iRects = BDFDB.DOMUtils.getRects(itemLayer); + const aRects = BDFDB.DOMUtils.getRects(document.querySelector(BDFDB.dotCN.appmount)); + const positionOffsets = {height: 10, width: 10}; + const offset = typeof config.offset == "number" ? config.offset : 0; + switch (type) { + case "top": + top = tRects.top - iRects.height - positionOffsets.height + 2 - offset; + left = tRects.left + (tRects.width - iRects.width) / 2; + break; + case "bottom": + top = tRects.top + tRects.height + positionOffsets.height - 2 + offset; + left = tRects.left + (tRects.width - iRects.width) / 2; + break; + case "left": + top = tRects.top + (tRects.height - iRects.height) / 2; + left = tRects.left - iRects.width - positionOffsets.width + 2 - offset; + break; + case "right": + top = tRects.top + (tRects.height - iRects.height) / 2; + left = tRects.left + tRects.width + positionOffsets.width - 2 + offset; + break; + } + + itemLayer.style.setProperty("top", `${top}px`, "important"); + itemLayer.style.setProperty("left", `${left}px`, "important"); + + tooltipPointer.style.removeProperty("margin-left"); + tooltipPointer.style.removeProperty("margin-top"); + if (type == "top" || type == "bottom") { + if (left < 0) { + itemLayer.style.setProperty("left", "5px", "important"); + tooltipPointer.style.setProperty("margin-left", `${left - 10}px`, "important"); + } + else { + const rightMargin = aRects.width - (left + iRects.width); + if (rightMargin < 0) { + itemLayer.style.setProperty("left", `${aRects.width - iRects.width - 5}px`, "important"); + tooltipPointer.style.setProperty("margin-left", `${-1*rightMargin}px`, "important"); + } + } + } + else if (type == "left" || type == "right") { + if (top < 0) { + const bRects = BDFDB.DOMUtils.getRects(document.querySelector(BDFDB.dotCN.titlebar)); + const barCorrection = (bRects.width || 0) >= Math.round(75 * window.outerWidth / aRects.width) ? (bRects.height + 5) : 0; + itemLayer.style.setProperty("top", `${5 + barCorrection}px`, "important"); + tooltipPointer.style.setProperty("margin-top", `${top - 10 - barCorrection}px`, "important"); + } + else { + const bottomMargin = aRects.height - (top + iRects.height); + if (bottomMargin < 0) { + itemLayer.style.setProperty("top", `${aRects.height - iRects.height - 5}px`, "important"); + tooltipPointer.style.setProperty("margin-top", `${-1*bottomMargin}px`, "important"); + } + } + } + }; + + const wheel = e => { + const tRects1 = BDFDB.DOMUtils.getRects(anker); + BDFDB.TimeUtils.clear(wheel.timeout); + wheel.timeout = BDFDB.TimeUtils.timeout(_ => { + const tRects2 = BDFDB.DOMUtils.getRects(anker); + if (tRects1.x != tRects2.x || tRects1.y != tRects2.y) removeTooltip(); + }, 500); + }; + const mouseMove = e => { + const parent = e.target.parentElement.querySelector(":hover"); + if (parent && anker != parent && !anker.contains(parent)) removeTooltip(); + }; + const mouseLeave = e => removeTooltip(); + if (!config.perssist) { + document.addEventListener("wheel", wheel); + document.addEventListener("mousemove", mouseMove); + document.addEventListener("mouseleave", mouseLeave); + } + + const observer = new MutationObserver(changes => changes.forEach(change => { + const nodes = Array.from(change.removedNodes); + if (nodes.indexOf(itemLayer) > -1 || nodes.indexOf(anker) > -1 || nodes.some(n => n.contains(anker))) removeTooltip(); + })); + observer.observe(document.body, {subtree: true, childList: true}); + + tooltip.removeTooltip = itemLayer.removeTooltip = removeTooltip; + tooltip.setText = itemLayer.setText = setText; + tooltip.update = itemLayer.update = update; + setText(text); + update(); + + if (config.delay) { + BDFDB.DOMUtils.toggle(itemLayer); + BDFDB.TimeUtils.timeout(_ => { + BDFDB.DOMUtils.toggle(itemLayer); + if (typeof config.onShow == "function") config.onShow(itemLayer, anker); + }, config.delay); + } + else { + if (typeof config.onShow == "function") config.onShow(itemLayer, anker); + } + return itemLayer; + }; + + Internal.addModulePatches = function (plugin) { + plugin = plugin == BDFDB && Internal || plugin; + if (!plugin || !plugin.modulePatches) return; + let patchPriority = !isNaN(plugin.patchPriority) ? plugin.patchPriority : 5; + patchPriority = patchPriority < 1 ? (plugin == Internal ? 0 : 1) : (patchPriority > 9 ? (plugin == Internal ? 10 : 9) : Math.round(patchPriority)); + for (let patchType in plugin.modulePatches) { + if (!PluginStores.modulePatches[patchType]) PluginStores.modulePatches[patchType] = {}; + for (let type of plugin.modulePatches[patchType]) { + if (InternalData.PatchModules[type]) { + let found = false; + if (!InternalData.PatchModules[type].noSearch && (patchType == "before" || patchType == "after")) { + let exports = (BDFDB.ModuleUtils.find(m => Internal.isCorrectModule(m, type) && m, {defaultExport: false, moduleName: type}) || {}).exports; + if (exports && !exports.default) for (let key of Object.keys(exports)) if (typeof exports[key] == "function" && !(exports[key].prototype && exports[key].prototype.render) && Internal.isCorrectModule(exports[key], type, false) && exports[key].toString().length < 50000) { + found = true; + BDFDB.PatchUtils.patch(plugin, exports, key, {[patchType]: e => Internal.initiatePatch(plugin, type, { + arguments: e.methodArguments, + instance: e.instance, + returnvalue: e.returnValue, + component: exports[key], + name: type, + methodname: "render", + patchtypes: [patchType] + })}, {name: type}); + break; + } + } + if (!found) { + if (!PluginStores.modulePatches[patchType][type]) PluginStores.modulePatches[patchType][type] = []; + if (!PluginStores.modulePatches[patchType][type][patchPriority]) PluginStores.modulePatches[patchType][type][patchPriority] = []; + PluginStores.modulePatches[patchType][type][patchPriority].push(plugin); + if (PluginStores.modulePatches[patchType][type][patchPriority].length > 1) PluginStores.modulePatches[patchType][type][patchPriority] = BDFDB.ArrayUtils.keySort(PluginStores.modulePatches[patchType][type][patchPriority], "name"); + } + } + else BDFDB.LogUtils.warn(`[${type}] not found in PatchModules InternalData`, plugin); + } + } + }; + Internal.addContextPatches = function (plugin) { + if (!InternalData.ContextMenuTypes || !BdApi || !BdApi.ContextMenu || typeof BdApi.ContextMenu.patch != "function") return; + plugin = plugin == BDFDB && Internal || plugin; + if (!plugin) return; + for (let type in InternalData.ContextMenuTypes) if (typeof plugin[`on${type}`] == "function") { + if (!BDFDB.ArrayUtils.is(plugin.patchCancels)) plugin.patchCancels = []; + plugin.patchCancels.push(BdApi.ContextMenu.patch(InternalData.ContextMenuTypes[type], (returnValue, props) => typeof plugin[`on${type}`] == "function" && plugin[`on${type}`]({returnvalue: returnValue, instance: {props}}))); + } + }; + Internal.initiatePatch = function (plugin, type, e) { + plugin = plugin == BDFDB && Internal || plugin; + if (BDFDB.ObjectUtils.is(plugin) && !plugin.stopping && e.instance) { + type = BDFDB.StringUtils.upperCaseFirstChar(type).replace(/[^A-z0-9]|_/g, ""); + if (typeof plugin[`process${type}`] == "function") { + if (typeof e.methodname == "string" && ["componentDidMount", "componentDidUpdate", "componentWillUnmount"].indexOf(e.methodname) > -1) { + e.node = BDFDB.ReactUtils.findDOMNode(e.instance); + if (e.node) { + let tempReturn = plugin[`process${type}`](e); + return tempReturn !== undefined ? tempReturn : e.returnvalue; + } + else BDFDB.TimeUtils.timeout(_ => { + e.node = BDFDB.ReactUtils.findDOMNode(e.instance); + if (e.node) plugin[`process${type}`](e); + }); + } + else if (e.returnvalue !== undefined || e.patchtypes.includes("before")) { + let tempReturn = plugin[`process${type}`](e); + return tempReturn !== undefined ? tempReturn : e.returnvalue; + } + } + } + }; + + const PatchTypes = ["before", "instead", "after"]; + BDFDB.PatchUtils = {}; + BDFDB.PatchUtils.isPatched = function (plugin, module, methodName) { + plugin = plugin == BDFDB && Internal || plugin; + if (!plugin || !methodName || (!BDFDB.ObjectUtils.is(module) && !BDFDB.ArrayUtils.is(module)) || !module[methodName] || !module[methodName].BDFDB_Patches) return false; + const pluginId = (typeof plugin === "string" ? plugin : plugin.name).toLowerCase(); + return pluginId && BDFDB.ObjectUtils.toArray(module[methodName].BDFDB_Patches).some(patchObj => BDFDB.ObjectUtils.toArray(patchObj.plugins).some(priorityObj => Object.keys(priorityObj).includes(pluginId))); + }; + BDFDB.PatchUtils.patch = function (plugin, module, methodNames, patchMethods, config = {}) { + if (!BdApi || !BdApi.Patcher) return; + plugin = plugin == BDFDB && Internal || plugin; + if (!plugin || (!BDFDB.ObjectUtils.is(module) && !BDFDB.ArrayUtils.is(module)) || !methodNames || !BDFDB.ObjectUtils.is(patchMethods)) return null; + patchMethods = BDFDB.ObjectUtils.filter(patchMethods, type => PatchTypes.includes(type) && typeof BdApi.Patcher[type] == "function" && typeof patchMethods[type] == "function", true); + if (BDFDB.ObjectUtils.isEmpty(patchMethods)) return null; + + const pluginName = (typeof plugin === "string" ? plugin : plugin.name) || ""; + const pluginVersion = typeof plugin === "string" ? "" : plugin.version; + const pluginId = pluginName.toLowerCase(); + + let patchPriority = !isNaN(config.priority) ? config.priority : (BDFDB.ObjectUtils.is(plugin) && !isNaN(plugin.patchPriority) ? plugin.patchPriority : 5); + patchPriority = patchPriority < 1 ? (plugin == Internal ? 0 : 1) : (patchPriority > 9 ? (plugin == Internal ? 10 : 9) : Math.round(patchPriority)); + + methodNames = [methodNames].flat(10).filter(n => n); + let cancel = _ => BDFDB.PatchUtils.unpatch(plugin, module, methodNames); + + for (let methodName of methodNames) if (module[methodName] == null || typeof module[methodName] == "function") { + if (!module[methodName]) module[methodName] = _ => {return null}; + let patches = module[methodName].BDFDB_Patches || {}; + for (let type in patchMethods) { + const internalData = (Object.entries(InternalData.LibraryModules).find(n => n && n[0] && LibraryModules[n[0]] == module && n[1] && n[1]._originalModule && n[1]._mappedItems[methodName]) || [])[1]; + const name = internalData && internalData[0] || config.name || (module.constructor ? (module.constructor.displayName || module.constructor.name) : "module"); + try { + if (!patches[type]) { + const originalMethod = module[methodName].__originalFunction || module[methodName]; + const mainCancel = BdApi.Patcher[type](Internal.name, internalData && internalData._originalModule || module, internalData && internalData._mappedItems[methodName] || methodName, function(...args) { + let callInsteadAfterwards = false, stopInsteadCall = false; + const data = { + component: module, + methodArguments: args[1], + returnValue: args[2], + originalMethod: originalMethod, + originalMethodName: methodName + }; + if (type == "instead") { + data.callOriginalMethod = _ => data.returnValue = data.originalMethod.apply(this && this !== window ? this : {}, data.methodArguments); + data.callOriginalMethodAfterwards = _ => (callInsteadAfterwards = true, data.returnValue); + data.stopOriginalMethodCall = _ => stopInsteadCall = true; + } + if (args[0] != module) data.instance = args[0] || {props: args[1][0]}; + for (let priority in patches[type].plugins) for (let id in BDFDB.ObjectUtils.sort(patches[type].plugins[priority])) { + let tempReturn = BDFDB.TimeUtils.suppress(patches[type].plugins[priority][id], `"${type}" callback of ${methodName} in ${name}`, {name: patches[type].plugins[priority][id].pluginName, version: patches[type].plugins[priority][id].pluginVersion})(data); + if (type != "before" && tempReturn !== undefined) data.returnValue = tempReturn; + } + if (type == "instead" && callInsteadAfterwards && !stopInsteadCall) BDFDB.TimeUtils.suppress(data.callOriginalMethod, `originalMethod of ${methodName} in ${name}`, {name: "Discord"})(); + + if (type != "before") return (methodName == "render" || methodName == "type") && data.returnValue === undefined ? null : data.returnValue; + }); + module[methodName].BDFDB_Patches = patches; + patches[type] = {plugins: {}, cancel: _ => { + if (!config.noCache) BDFDB.ArrayUtils.remove(Internal.patchCancels, patches[type].cancel, true); + delete patches[type]; + if (!config.noCache && BDFDB.ObjectUtils.isEmpty(patches)) delete module[methodName].BDFDB_Patches; + mainCancel(); + }}; + if (!config.noCache) { + if (!BDFDB.ArrayUtils.is(Internal.patchCancels)) Internal.patchCancels = []; + Internal.patchCancels.push(patches[type].cancel); + } + } + if (!patches[type].plugins[patchPriority]) patches[type].plugins[patchPriority] = {}; + patches[type].plugins[patchPriority][pluginId] = (...args) => { + if (config.once || !plugin.started) cancel(); + return patchMethods[type](...args); + }; + patches[type].plugins[patchPriority][pluginId].pluginName = pluginName; + patches[type].plugins[patchPriority][pluginId].pluginVersion = pluginVersion; + } catch (err) {BDFDB.LogUtils.error(["Could not patch Component!", `"${type}" Patch of ${methodName} in ${name}`, err], plugin);} + } + } + if (BDFDB.ObjectUtils.is(plugin) && !config.once && !config.noCache) { + if (!BDFDB.ArrayUtils.is(plugin.patchCancels)) plugin.patchCancels = []; + plugin.patchCancels.push(cancel); + } + return cancel; + }; + BDFDB.PatchUtils.unpatch = function (plugin, module, methodNames) { + plugin = plugin == BDFDB && Internal || plugin; + if (!module || !methodNames) { + if (BDFDB.ObjectUtils.is(plugin) && BDFDB.ArrayUtils.is(plugin.patchCancels)) while (plugin.patchCancels.length) (plugin.patchCancels.pop())(); + } + else { + const pluginId = !plugin ? null : (typeof plugin === "string" ? plugin : plugin.name).toLowerCase(); + for (let methodName of [methodNames].flat(10).filter(n => n)) if (module[methodName] && module[methodName].BDFDB_Patches) { + let patches = module[methodName].BDFDB_Patches; + for (let type in patches) { + if (pluginId) for (let priority in patches[type].plugins) { + delete patches[type].plugins[priority][pluginId]; + if (BDFDB.ObjectUtils.isEmpty(patches[type].plugins[priority])) delete patches[type].plugins[priority]; + } + else patches[type].plugins = {}; + if (BDFDB.ObjectUtils.isEmpty(patches[type].plugins)) patches[type].cancel(); + } + } + } + }; + Internal.forceUpdate = function (pluginDataObjs, instance, type) { + pluginDataObjs = [pluginDataObjs].flat(10).filter(n => n); + if (pluginDataObjs.length && instance && type) { + let forceRender = false; + for (let pluginData of pluginDataObjs) { + let plugin = pluginData.plugin == BDFDB && Internal || pluginData.plugin, methodNames = []; + for (let patchType in plugin.modulePatches) { + if (plugin.modulePatches[patchType].indexOf(type) > -1) methodNames.push(patchType); + } + methodNames = BDFDB.ArrayUtils.removeCopies(methodNames).flat(10).filter(n => n); + if (methodNames.indexOf("componentDidMount") > -1) Internal.initiatePatch(plugin, type, { + arguments: [], + instance: instance, + returnvalue: undefined, + component: undefined, + methodname: "componentDidMount", + patchtypes: pluginData.patchTypes[type] + }); + if (methodNames.indexOf("before") > -1 || methodNames.indexOf("after") > -1) forceRender = true; + else if (!forceRender && methodNames.includes("componentDidUpdate") > -1) Internal.initiatePatch(plugin, type, { + arguments: [], + instance: instance, + returnvalue: undefined, + component: undefined, + methodname: "componentDidUpdate", + patchtypes: pluginData.patchTypes[type] + }); + } + if (forceRender) BDFDB.ReactUtils.forceUpdate(instance); + } + }; + BDFDB.PatchUtils.forceAllUpdates = function (plugins, selectedTypes) { + plugins = [plugins].flat(10).map(n => n == BDFDB && Internal || n).filter(n => BDFDB.ObjectUtils.is(n.modulePatches)); + if (!plugins.length) return; + const app = document.querySelector(BDFDB.dotCN.app); + if (!app) return; + selectedTypes = [selectedTypes].flat(10).filter(n => n); + let toBeUpdatedModulesObj = {}; + let patchTypes = {}; + for (let plugin of plugins) { + toBeUpdatedModulesObj[plugin.name] = []; + patchTypes[plugin.name] = {}; + for (let patchType in plugin.modulePatches) for (let type of plugin.modulePatches[patchType]) if (!selectedTypes.length || selectedTypes.includes(type)) { + toBeUpdatedModulesObj[plugin.name].push(type); + if (!patchTypes[plugin.name][type]) patchTypes[plugin.name][type] = []; + patchTypes[plugin.name][type].push(patchType); + } + } + let toBeUpdatedModules = BDFDB.ArrayUtils.removeCopies(BDFDB.ObjectUtils.toArray(toBeUpdatedModulesObj).flat(10)); + if (!BDFDB.ArrayUtils.sum(toBeUpdatedModules.map(n => n.length))) return; + try { + const appInsDown = BDFDB.ReactUtils.findOwner(app, {name: toBeUpdatedModules, all: true, unlimited: true, group: true}); + const appInsUp = BDFDB.ReactUtils.findOwner(app, {name: toBeUpdatedModules, all: true, unlimited: true, group: true, up: true}); + for (let type in appInsDown) { + let filteredPlugins = plugins.filter(n => toBeUpdatedModulesObj[n.name].includes(type)).map(n => ({plugin: n, patchTypes: patchTypes[n.name]})); + for (let ins of appInsDown[type]) Internal.forceUpdate(filteredPlugins, ins, type); + } + for (let type in appInsUp) { + let filteredPlugins = plugins.filter(n => toBeUpdatedModulesObj[n.name].includes(type)).map(n => ({plugin: n, patchTypes: patchTypes[n.name]})); + for (let ins of appInsUp[type]) Internal.forceUpdate(filteredPlugins, ins, type); + } + } + catch (err) {for (let plugin of plugins) BDFDB.LogUtils.error(["Could not force update Components!", err], plugin);} + }; + + Internal.isCorrectModule = function (module, type, useCache = false) { + if (!InternalData.PatchModules || !InternalData.PatchModules[type]) return false; + else if (useCache && Cache && Cache.modules && Cache.modules.patch && Cache.modules.patch[type] == module) return true; + else { + let foundModule = null; + if (InternalData.PatchModules[type].strings) foundModule = Internal.checkModuleStrings(module._originalFunction || module, InternalData.PatchModules[type].strings) ? module : null; + if (InternalData.PatchModules[type].props) foundModule = Internal.checkModuleProps(module, InternalData.PatchModules[type].props) ? module : null; + if (InternalData.PatchModules[type].protos) foundModule = Internal.checkModuleProtos(module, InternalData.PatchModules[type].protos) ? module : null; + if (foundModule) { + if (InternalData.PatchModules[type].nonStrings) foundModule = Internal.checkModuleStrings(module._originalFunction || module, InternalData.PatchModules[type].nonStrings, {hasNot: true}) ? module : null; + if (InternalData.PatchModules[type].nonProps) foundModule = Internal.checkModuleProps(module, InternalData.PatchModules[type].nonProps, {hasNot: true}) ? module : null; + if (InternalData.PatchModules[type].nonProtos) foundModule = Internal.checkModuleProtos(module, InternalData.PatchModules[type].nonProtos, {hasNot: true}) ? module : null; + } + if (foundModule) { + if (useCache) { + if (!Cache.modules.patch) Cache.modules.patch = {}; + Cache.modules.patch[type] = foundModule; + } + return true; + } + } + return false; + }; + Internal.isCorrectModuleButDontPatch = function (type) { + if (type == "MessageToolbar" && document.querySelector(BDFDB.dotCN.emojipicker)) return true; + return false; + }; + Internal.findModuleViaData = (moduleStorage, dataStorage, item) => { + if (!dataStorage[item]) return; + let defaultExport = typeof dataStorage[item].exported != "boolean" ? true : dataStorage[item].exported; + if (dataStorage[item].props) moduleStorage[item] = BDFDB.ModuleUtils.findByProperties(dataStorage[item].props, {defaultExport: defaultExport, moduleName: item}); + else if (dataStorage[item].protos) moduleStorage[item] = BDFDB.ModuleUtils.findByPrototypes(dataStorage[item].protos, {defaultExport: defaultExport, moduleName: item}); + else if (dataStorage[item].name) moduleStorage[item] = BDFDB.ModuleUtils.findByName(dataStorage[item].name, {defaultExport: defaultExport, moduleName: item}); + else if (dataStorage[item].strings) { + if (dataStorage[item].nonStrings) { + moduleStorage[item] = Internal.findModule("strings + nonStrings", JSON.stringify([dataStorage[item].strings, dataStorage[item].nonStrings].flat(10)), m => Internal.checkModuleStrings(m, dataStorage[item].strings) && Internal.checkModuleStrings(m, dataStorage[item].nonStrings, {hasNot: true}) && m, {defaultExport: defaultExport, moduleName: item}); + } + else moduleStorage[item] = BDFDB.ModuleUtils.findByString(dataStorage[item].strings, {defaultExport: defaultExport, moduleName: item}); + } + if (dataStorage[item].value) moduleStorage[item] = (moduleStorage[item] || {})[dataStorage[item].value]; + if (dataStorage[item].assign) moduleStorage[item] = Object.assign({}, moduleStorage[item]); + if (!moduleStorage[item]) return; + if (moduleStorage[item][item]) moduleStorage[item] = moduleStorage[item][item]; + if (dataStorage[item].funcStrings) moduleStorage[item] = (Object.entries(moduleStorage[item]).find(n => { + if (!n || !n[1]) return; + let funcString = typeof n[1] == "function" ? n[1].toString() : (_ => {try {return JSON.stringify(n[1])}catch(err){return n[1].toString()}})(); + let renderFuncString = typeof n[1].render == "function" && n[1].render.toString() || ""; + return (funcString || renderFuncString) && [dataStorage[item].funcStrings].flat(10).filter(s => s && typeof s == "string").every(string => funcString && funcString.indexOf(string) > -1 || renderFuncString && renderFuncString.indexOf(string) > -1); + }) || [])[1]; + if (dataStorage[item].map) { + dataStorage[item]._originalModule = moduleStorage[item]; + dataStorage[item]._mappedItems = {}; + moduleStorage[item] = new Proxy(Object.assign({}, dataStorage[item]._originalModule, dataStorage[item].map), { + get: function (_, item2) { + if (dataStorage[item]._originalModule[item2]) return dataStorage[item]._originalModule[item2]; + if (dataStorage[item]._mappedItems[item2]) return dataStorage[item]._originalModule[dataStorage[item]._mappedItems[item2]]; + if (!dataStorage[item].map[item2]) return dataStorage[item]._originalModule[item2]; + let foundFunc = dataStorage[item].map[item2] && dataStorage[item].map[item2].length == 1 && dataStorage[item]._originalModule[dataStorage[item].map[item2][0]] ? [dataStorage[item].map[item2][0], dataStorage[item]._originalModule[dataStorage[item].map[item2][0]]] : Object.entries(dataStorage[item]._originalModule).find(n => { + if (!n || !n[1]) return; + let funcString = typeof n[1] == "function" ? n[1].toString() : (_ => {try {return JSON.stringify(n[1])}catch(err){return n[1].toString()}})(); + let renderFuncString = typeof n[1].render == "function" && n[1].render.toString() || ""; + return [dataStorage[item].map[item2]].flat(10).filter(s => s && typeof s == "string").every(string => funcString && funcString.replace(/[\n\t\r]/g, "").indexOf(string) > -1 || renderFuncString && renderFuncString.replace(/[\n\t\r]/g, "").indexOf(string) > -1); + }); + if (foundFunc) { + dataStorage[item]._mappedItems[item2] = foundFunc[0]; + return foundFunc[1]; + } + return "div"; + } + }); + } + }; + + LibraryModules.LanguageStore = BDFDB.ModuleUtils.find(m => m.Messages && m.Messages.IMAGE && m); + LibraryModules.React = BDFDB.ModuleUtils.findByProperties("createElement", "cloneElement"); + LibraryModules.ReactDOM = BDFDB.ModuleUtils.findByProperties("render", "findDOMNode"); + Internal.LibraryModules = new Proxy(LibraryModules, { + get: function (_, item) { + if (LibraryModules[item]) return LibraryModules[item]; + if (!InternalData.LibraryModules[item]) return null; + + Internal.findModuleViaData(LibraryModules, InternalData.LibraryModules, item); + + return LibraryModules[item] ? LibraryModules[item] : null; + } + }); + BDFDB.LibraryModules = Internal.LibraryModules; + + if (Internal.LibraryModules.KeyCodeUtils) { + let originalModule = LibraryModules.KeyCodeUtils; + LibraryModules.KeyCodeUtils = new Proxy(originalModule, { + get: function (_, item) { + if (item == "getString") return getString; + else if (item == "_originalModule") return originalModule; + else if (originalModule[item]) return originalModule[item]; + else return null; + } + }); + + let codeMap = BDFDB.ObjectUtils.invert(Internal.LibraryModules.PlatformUtils.isLinux() ? Internal.DiscordConstants.LinuxKeyToCode : Internal.LibraryModules.PlatformUtils.isMac() ? Internal.DiscordConstants.MacosKeyToCode : Internal.LibraryModules.PlatformUtils.isWindows() ? Internal.DiscordConstants.WindowsKeyToCode : {}); + let keyMap = [["META", "⌘"], ["RIGHT META", "RIGHT ⌘"], ["SHIFT", "⇧"], ["RIGHT SHIFT", "RIGHT ⇧"], ["ALT", "⌥"], ["RIGHT ALT", "RIGHT ⌥"], ["CTRL", "⌃"], ["RIGHT CTRL", "RIGHT ⌃"], ["ENTER", "↵"], ["BACKSPACE", "⌫"], ["DEL", "⌦"], ["ESC", "⎋"], ["PAGEUP", "⇞"], ["PAGEDOWN", "⇟"], ["UP", "↑"], ["DOWN", "↓"], ["LEFT", "←"], ["RIGHT", "→"], ["HOME", "↖"], ["END", "↘"], ["TAB", "⇥"], ["SPACE", "␣"]]; + let mapKeys = key => { + let upperCaseKey = key.toUpperCase(); + for (let [name, mappedKey] of mapKeys) if (name === upperCaseKey) return mappedKey; + return key; + }; + const getString = function (keyArray, upperCase = true) { + let strings = [keyArray].flat(10).filter(n => n).map(keyCode => { + let code = Internal.LibraryModules.KeyCodeUtils.keyToCode((Object.entries(Internal.LibraryModules.KeyEvents.codes).find(n => n[1] == keyCode && Internal.LibraryModules.KeyCodeUtils.keyToCode(n[0], null)) || [])[0], null) || keyCode; + return codeMap[code] || "UNK".concat(code) + }).filter(n => n != null); + if (!upperCase) return strings.join("+"); + else { + strings = window.navigator.appVersion.indexOf("Mac OS X") != -1 ? strings.map(mapKeys) : strings; + return strings.join(" + ").toUpperCase(); + } + }; + } + + const MyReact = {}; + MyReact.childrenToArray = function (parent) { + if (parent && parent.props && parent.props.children && !BDFDB.ArrayUtils.is(parent.props.children)) { + const child = parent.props.children; + parent.props.children = []; + parent.props.children.push(child); + } + return parent.props.children; + } + MyReact.createElement = function (component, props = {}, errorWrap = false, ignoreErrors = false) { + if (component && component.defaultProps) for (let key in component.defaultProps) if (props[key] == null) props[key] = component.defaultProps[key]; + try { + let child = Internal.LibraryModules.React.createElement(component || "div", props) || null; + if (errorWrap) return Internal.LibraryModules.React.createElement(Internal.ErrorBoundary, {key: child && child.key || ""}, child) || null; + else return child; + } + catch (err) {!ignoreErrors && BDFDB.LogUtils.error(["Could not create React Element!", err]);} + return null; + }; + MyReact.objectToReact = function (obj) { + if (!obj) return null; + else if (typeof obj == "string") return obj; + else if (BDFDB.ObjectUtils.is(obj)) return BDFDB.ReactUtils.createElement(obj.type && typeof obj.type == "function" || typeof obj.type == "string" ? obj.type : (obj.props && obj.props.href && "a" || "div"), !obj.props ? {} : Object.assign({}, obj.props, { + children: obj.props.children ? MyReact.objectToReact(obj.props.children) : null + })); + else if (BDFDB.ArrayUtils.is(obj)) return obj.map(n => MyReact.objectToReact(n)); + else return null; + }; + MyReact.markdownParse = function (str) { + if (!Internal.LibraryModules.SimpleMarkdownParser) return null; + if (!MyReact.markdownParse.parser || !MyReact.markdownParse.render) { + MyReact.markdownParse.parser = Internal.LibraryModules.SimpleMarkdownParser.parserFor(Internal.LibraryModules.SimpleMarkdownParser.defaultRules); + MyReact.markdownParse.render = Internal.LibraryModules.SimpleMarkdownParser.reactFor(Internal.LibraryModules.SimpleMarkdownParser.ruleOutput(Internal.LibraryModules.SimpleMarkdownParser.defaultRules, "react")); + } + return MyReact.render && MyReact.parser ? MyReact.render(MyReact.parser(str, {inline: true})) : null; + }; + MyReact.elementToReact = function (node, ref) { + if (BDFDB.ReactUtils.isValidElement(node)) return node; + else if (!Node.prototype.isPrototypeOf(node)) return null; + else if (node.nodeType == Node.TEXT_NODE) return node.nodeValue; + let attributes = {}, importantStyles = []; + if (typeof ref == "function") attributes.ref = ref; + if (node.attributes) { + for (let attr of node.attributes) attributes[attr.name] = attr.value; + if (node.attributes.style) attributes.style = BDFDB.ObjectUtils.filter(node.style, n => node.style[n] && isNaN(parseInt(n)), true); + } + attributes.children = []; + if (node.style && node.style.cssText) for (let propStr of node.style.cssText.split(";")) if (propStr.endsWith("!important")) { + let key = propStr.split(":")[0]; + let camelprop = key.replace(/-([a-z]?)/g, (m, g) => g.toUpperCase()); + if (attributes.style[camelprop] != null) importantStyles.push(key); + } + for (let child of node.childNodes) attributes.children.push(MyReact.elementToReact(child)); + attributes.className = BDFDB.DOMUtils.formatClassName(attributes.className, attributes.class); + delete attributes.class; + return BDFDB.ReactUtils.forceStyle(BDFDB.ReactUtils.createElement(node.tagName, attributes), importantStyles); + }; + MyReact.forceStyle = function (reactEle, styles) { + if (!BDFDB.ReactUtils.isValidElement(reactEle)) return null; + if (!BDFDB.ObjectUtils.is(reactEle.props.style) || !BDFDB.ArrayUtils.is(styles) || !styles.length) return reactEle; + let ref = reactEle.ref; + reactEle.ref = instance => { + if (typeof ref == "function") ref(instance); + let node = BDFDB.ReactUtils.findDOMNode(instance); + if (Node.prototype.isPrototypeOf(node)) for (let key of styles) { + let propValue = reactEle.props.style[key.replace(/-([a-z]?)/g, (m, g) => g.toUpperCase())]; + if (propValue != null) node.style.setProperty(key, propValue, "important"); + } + }; + return reactEle; + }; + MyReact.findDOMNode = function (instance) { + if (Node.prototype.isPrototypeOf(instance)) return instance; + if (!instance || !instance.updater || typeof instance.updater.isMounted !== "function" || !instance.updater.isMounted(instance)) return null; + let node = Internal.LibraryModules.ReactDOM.findDOMNode(instance) || BDFDB.ObjectUtils.get(instance, "child.stateNode"); + return Node.prototype.isPrototypeOf(node) ? node : null; + }; + MyReact.findParent = function (nodeOrInstance, config) { + if (!nodeOrInstance || !BDFDB.ObjectUtils.is(config) || !config.name && !config.type && !config.key && !config.props && !config.filter) return [null, -1]; + let instance = Node.prototype.isPrototypeOf(nodeOrInstance) ? BDFDB.ReactUtils.getInstance(nodeOrInstance) : nodeOrInstance; + if (!BDFDB.ObjectUtils.is(instance) && !BDFDB.ArrayUtils.is(instance) || instance.props && typeof instance.props.children == "function") return [null, -1]; + + config.name = config.name && [config.name].flat().filter(n => n); + config.type = config.type && [config.type].flat().filter(n => n); + config.key = config.key && [config.key].flat().filter(n => n); + config.props = config.props && [config.props].flat().filter(n => n); + config.filter = typeof config.filter == "function" && config.filter; + + let parent, firstArray; + parent = firstArray = instance; + while (!BDFDB.ArrayUtils.is(firstArray) && firstArray.props && firstArray.props.children) firstArray = firstArray.props.children; + if (!BDFDB.ArrayUtils.is(firstArray)) { + if (parent && parent.props) { + parent.props.children = [parent.props.children]; + firstArray = parent.props.children; + } + else firstArray = []; + } + return getParent(instance); + + function getParent (children) { + let result = [firstArray, -1]; + if (!children) return result; + if (!BDFDB.ArrayUtils.is(children)) { + if (check(children)) result = found(children); + else { + if (children.props && children.props.children) { + parent = children; + result = getParent(children.props.children); + } + if (!(result && result[1] > -1) && children.props && children.props.child) { + parent = children; + result = getParent(children.props.child); + } + } + } + else { + for (let i = 0; result[1] == -1 && i < children.length; i++) if (children[i]) { + if (BDFDB.ArrayUtils.is(children[i])) { + parent = children; + result = getParent(children[i]); + } + else if (check(children[i])) { + parent = children; + result = found(children[i]); + } + else { + if (children[i].props && children[i].props.children) { + parent = children[i]; + result = getParent(children[i].props.children); + } + if (!(result && result[1] > -1) && children[i].props && children[i].props.child) { + parent = children[i]; + result = getParent(children[i].props.child); + } + } + } + } + return result; + } + function found (child) { + if (BDFDB.ArrayUtils.is(parent)) return [parent, parent.indexOf(child)]; + else { + parent.props.children = []; + parent.props.children.push(child); + return [parent.props.children, 0]; + } + } + function check (instance) { + if (!instance || instance == parent) return false; + let props = instance.stateNode ? instance.stateNode.props : instance.props; + if (config.name && instance.type && config.name.some(name => instance.type.displayName == name || instance.type.name == name || Internal.isCorrectModule(instance.type.render || instance.type.type || instance.type, name))) return true; + if (config.type && config.type.some(type => instance.type == type)) return true; + if (config.key && config.key.some(key => instance.key == key)) return true; + if (config.props && props && config.props[config.someProps ? "some" : "every"](prop => BDFDB.ArrayUtils.is(prop) ? (BDFDB.ArrayUtils.is(prop[1]) ? prop[1].some(checkValue => propCheck(props, prop[0], checkValue)) : propCheck(props, prop[0], prop[1])) : props[prop] !== undefined)) return true; + if (config.filter && config.filter(instance)) return true; + return false; + } + function propCheck (props, key, value) { + return key != null && props[key] != null && value != null && (key == "className" ? (" " + props[key] + " ").indexOf(" " + value + " ") > -1 : BDFDB.equals(props[key], value)); + } + }; + MyReact.findChild = function (nodeOrInstance, config) { + if (!nodeOrInstance || !BDFDB.ObjectUtils.is(config) || !config.name && !config.type && !config.key && !config.props && !config.filter) return config.all ? [] : null; + let instance = Node.prototype.isPrototypeOf(nodeOrInstance) ? BDFDB.ReactUtils.getInstance(nodeOrInstance) : nodeOrInstance; + if (!BDFDB.ObjectUtils.is(instance) && !BDFDB.ArrayUtils.is(instance)) return null; + + config.name = config.name && [config.name].flat().filter(n => n); + config.type = config.type && [config.type].flat().filter(n => n); + config.key = config.key && [config.key].flat().filter(n => n); + config.props = config.props && [config.props].flat().filter(n => n); + config.filter = typeof config.filter == "function" && config.filter; + + let depth = -1; + let start = performance.now(); + let maxDepth = config.unlimited ? 999999999 : (config.depth === undefined ? 30 : config.depth); + let maxTime = config.unlimited ? 999999999 : (config.time === undefined ? 150 : config.time); + + let foundChildren = []; + let singleChild = getChild(instance); + if (config.all) { + for (let i in foundChildren) delete foundChildren[i].BDFDBreactSearch; + return foundChildren; + } + else return singleChild; + + function getChild (children) { + let result = null; + if (!children || depth >= maxDepth || performance.now() - start >= maxTime) return result; + if (!BDFDB.ArrayUtils.is(children)) { + if (check(children)) { + if (config.all === undefined || !config.all) result = children; + else if (config.all) { + if (!children.BDFDBreactSearch) { + children.BDFDBreactSearch = true; + foundChildren.push(children); + } + } + } + else { + if (children.props && children.props.children) { + depth++; + result = getChild(children.props.children); + depth--; + } + if (!result && children.props && children.props.child) { + depth++; + result = getChild(children.props.child); + depth--; + } + } + } + else { + for (let child of children) if (child) { + if (BDFDB.ArrayUtils.is(child)) result = getChild(child); + else if (check(child)) { + if (config.all === undefined || !config.all) result = child; + else if (config.all) { + if (!child.BDFDBreactSearch) { + child.BDFDBreactSearch = true; + foundChildren.push(child); + } + } + } + else { + if (child.props && child.props.children) { + depth++; + result = getChild(child.props.children); + depth--; + } + if (!result && child.props && child.props.child) { + depth++; + result = getChild(child.props.child); + depth--; + } + } + if (result) break; + } + } + return result; + } + function check (instance) { + if (!instance || instance == parent) return false; + let props = instance.stateNode ? instance.stateNode.props : instance.props; + if (config.name && instance.type && config.name.some(name => instance.type.displayName == name || instance.type.name == name || Internal.isCorrectModule(instance.type.render || instance.type.type || instance.type, name))) return true; + if (config.type && config.type.some(type => instance.type == type)) return true; + if (config.key && config.key.some(key => instance.key == key)) return true; + if (config.props && props && config.props[config.someProps ? "some" : "every"](prop => BDFDB.ArrayUtils.is(prop) ? (BDFDB.ArrayUtils.is(prop[1]) ? prop[1].some(checkValue => propCheck(props, prop[0], checkValue)) : propCheck(props, prop[0], prop[1])) : props[prop] !== undefined)) return true; + if (config.filter && config.filter(instance)) return true; + return false; + } + function propCheck (props, key, value) { + return key != null && props[key] != null && value != null && (key == "className" ? (" " + props[key] + " ").indexOf(" " + value + " ") > -1 : BDFDB.equals(props[key], value)); + } + }; + MyReact.findOwner = function (nodeOrInstance, config) { + if (!BDFDB.ObjectUtils.is(config)) return null; + if (!nodeOrInstance || !config.name && !config.type && !config.key && !config.props && !config.filter) return config.all ? (config.group ? {} : []) : null; + let instance = Node.prototype.isPrototypeOf(nodeOrInstance) ? BDFDB.ReactUtils.getInstance(nodeOrInstance) : nodeOrInstance; + if (!BDFDB.ObjectUtils.is(instance)) return config.all ? (config.group ? {} : []) : null; + + config.name = config.name && [config.name].flat().filter(n => n); + config.type = config.type && [config.type].flat().filter(n => n); + config.key = config.key && [config.key].flat().filter(n => n); + config.props = config.props && [config.props].flat().filter(n => n); + config.filter = typeof config.filter == "function" && config.filter; + + let depth = -1; + let start = performance.now(); + let maxDepth = config.unlimited ? 999999999 : (config.depth === undefined ? 30 : config.depth); + let maxTime = config.unlimited ? 999999999 : (config.time === undefined ? 150 : config.time); + let whitelist = config.up ? { + return: true, + sibling: true, + default: true + } : { + child: true, + sibling: true, + default: true + }; + whitelist[BDFDB.ReactUtils.instanceKey] = true; + + let foundInstances = config.group ? {} : []; + let singleInstance = getOwner(instance); + if (config.all) { + for (let i in foundInstances) { + if (config.group) for (let j in foundInstances[i]) delete foundInstances[i][j].BDFDBreactSearch; + else delete foundInstances[i].BDFDBreactSearch; + } + return foundInstances; + } + else return singleInstance; + + function getOwner (instance) { + depth++; + let result = undefined; + if (instance && !Node.prototype.isPrototypeOf(instance) && !BDFDB.ReactUtils.getInstance(instance) && depth < maxDepth && performance.now() - start < maxTime) { + let props = instance.stateNode ? instance.stateNode.props : instance.props; + let foundName = ""; + if (instance.stateNode && !Node.prototype.isPrototypeOf(instance.stateNode) && ( + config.name && instance.type && config.name.some(name => {if (instance.type.displayName == name || instance.type.name == name || Internal.isCorrectModule(instance.type.render || instance.type.type || instance.type, name)) { + foundName = name; return true; + }}) || + config.type && instance.type && config.type.some(type => instance.type == type) || + config.key && instance.key && config.key.some(key => instance.key == key) || + config.props && props && config.props.every(prop => BDFDB.ArrayUtils.is(prop) ? (BDFDB.ArrayUtils.is(prop[1]) ? prop[1].some(checkValue => BDFDB.equals(props[prop[0]], checkValue)) : BDFDB.equals(props[prop[0]], prop[1])) : props[prop] !== undefined) || + config.filter && config.filter(instance) + )) { + if (config.all === undefined || !config.all) result = instance.stateNode; + else if (config.all) { + if (!instance.stateNode.BDFDBreactSearch) { + instance.stateNode.BDFDBreactSearch = true; + if (config.group) { + if (foundName) { + if (!BDFDB.ArrayUtils.is(foundInstances[foundName])) foundInstances[foundName] = []; + foundInstances[foundName].push(instance.stateNode); + } + } + else foundInstances.push(instance.stateNode); + } + } + } + if (result === undefined) { + let keys = Object.getOwnPropertyNames(instance); + for (let i = 0; result === undefined && i < keys.length; i++) { + let key = keys[i]; + if (key && whitelist[key] && (typeof instance[key] === "object" || typeof instance[key] == "function")) result = getOwner(instance[key]); + } + } + } + depth--; + return result; + } + }; + MyReact.findValue = function (nodeOrInstance, searchKey, config = {}) { + if (!BDFDB.ObjectUtils.is(config)) return null; + if (!nodeOrInstance || typeof searchKey != "string") return config.all ? [] : null; + let instance = Node.prototype.isPrototypeOf(nodeOrInstance) ? BDFDB.ReactUtils.getInstance(nodeOrInstance) : nodeOrInstance; + if (!BDFDB.ObjectUtils.is(instance)) return config.all ? [] : null; + instance = instance[BDFDB.ReactUtils.instanceKey] || instance; + + let depth = -1; + let start = performance.now(); + let maxDepth = config.unlimited ? 999999999 : (config.depth === undefined ? 30 : config.depth); + let maxTime = config.unlimited ? 999999999 : (config.time === undefined ? 150 : config.time); + let whitelist = { + props: true, + state: true, + stateNode: true, + updater: true, + prototype: true, + type: true, + children: config.up ? false : true, + memoizedProps: true, + memoizedState: true, + child: config.up ? false : true, + return: config.up ? true : false, + sibling: config.up ? false : true + }; + let whitelistKeys = Object.keys(whitelist); + let blacklist = { + contextSection: true + }; + if (BDFDB.ObjectUtils.is(config.whitelist)) Object.assign(whitelist, config.whiteList); + if (BDFDB.ObjectUtils.is(config.blacklist)) Object.assign(blacklist, config.blacklist); + return getKey(instance); + + function getKey(instance) { + depth++; + let result = undefined; + if (instance && !Node.prototype.isPrototypeOf(instance) && !BDFDB.ReactUtils.getInstance(instance) && depth < maxDepth && performance.now() - start < maxTime) { + let keys = Object.keys(instance).sort((x, y) => whitelistKeys.indexOf(x) < whitelistKeys.indexOf(y) ? -1 : 1); + for (let i = 0; result === undefined && i < keys.length; i++) { + let key = keys[i]; + if (key && !blacklist[key]) { + let value = instance[key]; + if (searchKey === key && (config.value === undefined || BDFDB.equals(config.value, value))) result = value; + else if ((typeof value === "object" || typeof value == "function") && (whitelist[key] || key[0] == "." || !isNaN(key[0]))) result = getKey(value); + } + } + } + depth--; + return result; + } + }; + MyReact.forceUpdate = function (...instances) { + for (let ins of instances.flat(10).filter(n => n)) if (ins.updater && typeof ins.updater.isMounted == "function" && ins.updater.isMounted(ins)) ins.forceUpdate(); + }; + MyReact.getInstance = function (node) { + if (!BDFDB.ObjectUtils.is(node)) return null; + return node[Object.keys(node).find(key => key.startsWith("__reactInternalInstance") || key.startsWith("__reactFiber"))]; + }; + MyReact.render = function (component, node, ignoreErrors = false) { + if (!BDFDB.ReactUtils.isValidElement(component) || !Node.prototype.isPrototypeOf(node)) return; + try { + Internal.LibraryModules.ReactDOM.render(component, node); + let observer = new MutationObserver(changes => changes.forEach(change => { + let nodes = Array.from(change.removedNodes); + if (nodes.indexOf(node) > -1 || nodes.some(n => n.contains(node))) { + observer.disconnect(); + BDFDB.ReactUtils.unmountComponentAtNode(node); + } + })); + observer.observe(document.body, {subtree: true, childList: true}); + } + catch (err) {!ignoreErrors && BDFDB.LogUtils.error(["Could not render React Element!", err]);} + }; + MyReact.hookCall = function (callback, args, ignoreErrors = false) { + if (typeof callback != "function") return null; + let returnValue = null, tempNode = document.createElement("div"); + BDFDB.ReactUtils.render(BDFDB.ReactUtils.createElement(_ => { + returnValue = callback(args); + return null; + }, {}, false, ignoreErrors), tempNode, ignoreErrors); + BDFDB.ReactUtils.unmountComponentAtNode(tempNode); + return returnValue; + }; + BDFDB.ReactUtils = new Proxy({}, { + get: function (_, item) { + if (MyReact[item]) return MyReact[item]; + else if (LibraryModules.React[item]) return LibraryModules.React[item]; + else if (LibraryModules.ReactDOM[item]) return LibraryModules.ReactDOM[item]; + else return null; + } + }); + + BDFDB.MessageUtils = {}; + BDFDB.MessageUtils.isSystemMessage = function (message) { + return message && !Internal.DiscordConstants.MessageTypeGroups.USER_MESSAGE.has(message.type) && (message.type !== Internal.DiscordConstants.MessageTypes.CHAT_INPUT_COMMAND || message.interaction == null); + }; + BDFDB.MessageUtils.rerenderAll = function (instant) { + BDFDB.TimeUtils.clear(BDFDB.MessageUtils.rerenderAll.timeout); + BDFDB.MessageUtils.rerenderAll.timeout = BDFDB.TimeUtils.timeout(_ => { + let channelId = Internal.LibraryStores.SelectedChannelStore.getChannelId(); + if (channelId) { + if (BDFDB.DMUtils.isDMChannel(channelId)) BDFDB.DMUtils.markAsRead(channelId); + else BDFDB.ChannelUtils.markAsRead(channelId); + } + let LayerProviderIns = BDFDB.ReactUtils.findOwner(document.querySelector(BDFDB.dotCN.chatcontent), {name: "LayerProvider", unlimited: true, up: true}); + let LayerProviderPrototype = BDFDB.ObjectUtils.get(LayerProviderIns, `${BDFDB.ReactUtils.instanceKey}.type.prototype`); + if (LayerProviderIns && LayerProviderPrototype) { + BDFDB.PatchUtils.patch({name: "BDFDB MessageUtils"}, LayerProviderPrototype, "render", {after: e => { + e.returnValue.props.children = typeof e.returnValue.props.children == "function" ? (_ => {return null;}) : []; + BDFDB.ReactUtils.forceUpdate(LayerProviderIns); + }}, {once: true}); + BDFDB.ReactUtils.forceUpdate(LayerProviderIns); + } + }, instant ? 0 : 1000); + }; + BDFDB.MessageUtils.openMenu = function (message, e = mousePosition, slim = false) { + if (!message) return; + let channel = Internal.LibraryStores.ChannelStore.getChannel(message.channel_id); + if (!channel) return; + e = BDFDB.ListenerUtils.copyEvent(e.nativeEvent || e, (e.nativeEvent || e).currentTarget); + let type = slim ? "MessageSearchResultContextMenu" : "MessageContextMenu"; + let moduleFindData = InternalData.PatchModules[type] && InternalData.PatchModules[type].strings; + if (!moduleFindData) return; + let menu = BDFDB.ModuleUtils.findByString(moduleFindData, {noWarnings: true}); + if (menu) Internal.LibraryModules.ContextMenuUtils.openContextMenu(e, e2 => BDFDB.ReactUtils.createElement(menu.default || menu, Object.assign({}, e2, {message, channel}))); + else Internal.lazyLoadModuleImports(BDFDB.ModuleUtils.findByString(slim ? InternalData.PatchModules.SearchResult && InternalData.PatchModules.SearchResult.strings : InternalData.LibraryModules.MessageComponentUtils && InternalData.LibraryModules.MessageComponentUtils.strings)).then(_ => { + menu = BDFDB.ModuleUtils.findByString(moduleFindData); + if (menu) Internal.LibraryModules.ContextMenuUtils.openContextMenu(e, e2 => BDFDB.ReactUtils.createElement(menu.default || menu, Object.assign({}, e2, {message, channel}))); + }); + }; + + BDFDB.UserUtils = {}; + BDFDB.UserUtils.is = function (user) { + return user && user instanceof Internal.DiscordObjects.User; + }; + const myDataUser = Internal.LibraryStores.UserStore && Internal.LibraryStores.UserStore.getCurrentUser && Internal.LibraryStores.UserStore.getCurrentUser(); + if (myDataUser && BDFDB.UserUtils._id != myDataUser.id) BDFDB.UserUtils._id = myDataUser.id; + BDFDB.UserUtils.me = new Proxy(myDataUser || {}, { + get: function (list, item) { + const user = Internal.LibraryStores.UserStore && Internal.LibraryStores.UserStore.getCurrentUser && Internal.LibraryStores.UserStore.getCurrentUser(); + if (user && BDFDB.UserUtils._id != user.id) { + Cache.data = {}; + BDFDB.UserUtils._id = user.id; + } + return user ? user[item] : null; + } + }); + BDFDB.UserUtils.getStatus = function (id = BDFDB.UserUtils.me.id) { + id = typeof id == "number" ? id.toFixed() : id; + let activity = BDFDB.UserUtils.getActivity(id); + return activity && activity.type == Internal.DiscordConstants.ActivityTypes.STREAMING ? "streaming" : Internal.LibraryStores.PresenceStore.getStatus(id); + }; + BDFDB.UserUtils.getStatusColor = function (status, useColor) { + if (!Internal.DiscordConstants.Colors) return null; + status = typeof status == "string" ? status.toLowerCase() : null; + let color = ""; + switch (status) { + case "online": color = (useColor ? Internal.DiscordConstants.Colors.GREEN_360 : "var(--status-positive)"); break; + case "idle": color = (useColor ? Internal.DiscordConstants.Colors.YELLOW_300 : "var(--status-warning)"); break; + case "dnd": color = (useColor ? Internal.DiscordConstants.Colors.RED_400 : "var(--status-danger)"); break; + case "playing": color = (useColor ? Internal.DiscordConstants.Colors.BRAND : "var(--bdfdb-blurple)"); break; + case "listening": color = Internal.DiscordConstants.Colors.SPOTIFY; break; + case "streaming": color = Internal.DiscordConstants.Colors.TWITCH; break; + default: color = Internal.DiscordConstants.Colors.PRIMARY_400; break; + } + return (color || Internal.DiscordConstants.Colors.GREEN_360).replace(/calc\(.+\s*\*\s*([0-9\.\%]+)\)/g, "$1"); + }; + BDFDB.UserUtils.getActivity = function (id = BDFDB.UserUtils.me.id) { + for (let activity of Internal.LibraryStores.PresenceStore.getActivities(id)) if (activity.type != Internal.DiscordConstants.ActivityTypes.CUSTOM_STATUS) return activity; + return null; + }; + BDFDB.UserUtils.getCustomStatus = function (id = BDFDB.UserUtils.me.id) { + for (let activity of Internal.LibraryStores.PresenceStore.getActivities(id)) if (activity.type == Internal.DiscordConstants.ActivityTypes.CUSTOM_STATUS) return activity; + return null; + }; + BDFDB.UserUtils.getAvatar = function (id = BDFDB.UserUtils.me.id) { + let user = Internal.LibraryStores.UserStore.getUser(id); + if (!user) return window.location.origin + "/assets/1f0bfc0865d324c2587920a7d80c609b.png"; + else return ((user.avatar ? "" : window.location.origin) + Internal.LibraryModules.IconUtils.getUserAvatarURL(user)).split("?")[0]; + }; + BDFDB.UserUtils.getBanner = function (id = BDFDB.UserUtils.me.id, guildId = Internal.LibraryStores.SelectedGuildStore.getGuildId(), canAnimate = false) { + let displayProfile = Internal.LibraryModules.MemberDisplayUtils.getDisplayProfile(id, guildId); + return (Internal.LibraryModules.IconUtils.getUserBannerURL(Object.assign({banner: displayProfile && displayProfile.banner, id: id}, {canAnimate})) || "").split("?")[0]; + }; + BDFDB.UserUtils.can = function (permission, id = BDFDB.UserUtils.me.id, channelId = Internal.LibraryStores.SelectedChannelStore.getChannelId()) { + if (!Internal.DiscordConstants.Permissions[permission]) BDFDB.LogUtils.warn([permission, "not found in Permissions"]); + else { + let channel = Internal.LibraryStores.ChannelStore.getChannel(channelId); + if (channel) return Internal.LibraryModules.PermissionRoleUtils.can({permission: Internal.DiscordConstants.Permissions[permission], user: id, context: channel}); + } + return false; + }; + BDFDB.UserUtils.openMenu = function (user, guildId, channelId, e = mousePosition) { + if (!user) return; + if (guildId && !channelId) channelId = (BDFDB.LibraryStores.GuildChannelStore.getDefaultChannel(guildId) || {}).id; + e = BDFDB.ListenerUtils.copyEvent(e.nativeEvent || e, (e.nativeEvent || e).currentTarget); + let type = channelId ? "UserMemberContextMenu" : "UserGenericContextMenu"; + let moduleFindData = InternalData.PatchModules[type] && InternalData.PatchModules[type].strings; + if (!moduleFindData) return; + let menu = BDFDB.ModuleUtils.findByString(moduleFindData, {noWarnings: true}); + if (menu) Internal.LibraryModules.ContextMenuUtils.openContextMenu(e, e2 => BDFDB.ReactUtils.createElement(menu.default || menu, Object.assign({}, e2, {user, guildId, channelId}))); + else Internal.lazyLoadModuleImports(BDFDB.ModuleUtils.findByString(channelId ? InternalData.LibraryModules.UserPopoutUtils && InternalData.LibraryModules.UserPopoutUtils.strings : InternalData.PatchModules.ParticipantsForSelectedParticipant && InternalData.PatchModules.ParticipantsForSelectedParticipant.strings)).then(_ => { + menu = BDFDB.ModuleUtils.findByString(moduleFindData); + if (menu) Internal.LibraryModules.ContextMenuUtils.openContextMenu(e, e2 => BDFDB.ReactUtils.createElement(menu.default || menu, Object.assign({}, e2, {user, guildId, channelId}))); + }); + }; + + BDFDB.GuildUtils = {}; + BDFDB.GuildUtils.is = function (guild) { + if (!BDFDB.ObjectUtils.is(guild)) return false; + let keys = Object.keys(guild); + return guild instanceof Internal.DiscordObjects.Guild || Object.keys(new Internal.DiscordObjects.Guild({})).every(key => keys.indexOf(key) > -1); + }; + BDFDB.GuildUtils.getIcon = function (id) { + let guild = Internal.LibraryStores.GuildStore.getGuild(id); + if (!guild || !guild.icon) return ""; + return Internal.LibraryModules.IconUtils.getGuildIconURL(guild).split("?")[0]; + }; + BDFDB.GuildUtils.getBanner = function (id) { + let guild = Internal.LibraryStores.GuildStore.getGuild(id); + if (!guild || !guild.banner) return ""; + return Internal.LibraryModules.IconUtils.getGuildBannerURL(guild).split("?")[0]; + }; + BDFDB.GuildUtils.getFolder = function (id) { + return Internal.LibraryStores.SortedGuildStore.getGuildFolders().filter(n => n.folderId).find(n => n.guildIds.includes(id)); + }; + BDFDB.GuildUtils.openMenu = function (guild, e = mousePosition) { + if (!guild) return; + e = BDFDB.ListenerUtils.copyEvent(e.nativeEvent || e, (e.nativeEvent || e).currentTarget); + let moduleFindData = InternalData.PatchModules.GuildContextMenu && InternalData.PatchModules.GuildContextMenu.strings; + if (!moduleFindData) return; + let menu = BDFDB.ModuleUtils.findByString(moduleFindData, {noWarnings: true}); + if (menu) Internal.LibraryModules.ContextMenuUtils.openContextMenu(e, e2 => BDFDB.ReactUtils.createElement(menu.default || menu, Object.assign({}, e2, {guild}))); + else Internal.lazyLoadModuleImports(BDFDB.ModuleUtils.findByString(InternalData.PatchModules.GuildSidebar && InternalData.PatchModules.GuildSidebar.strings)).then(_ => { + menu = BDFDB.ModuleUtils.findByString(moduleFindData); + if (menu) Internal.LibraryModules.ContextMenuUtils.openContextMenu(e, e2 => BDFDB.ReactUtils.createElement(menu.default || menu, Object.assign({}, e2, {guild}))); + }); + }; + BDFDB.GuildUtils.markAsRead = function (guildIds) { + guildIds = [guildIds].flat(10).filter(id => id && typeof id == "string" && Internal.LibraryStores.GuildStore.getGuild(id)); + if (!guildIds) return; + let channels = guildIds.map(id => [BDFDB.ObjectUtils.toArray(Internal.LibraryStores.GuildChannelStore.getChannels(id)), BDFDB.ObjectUtils.toArray(Internal.LibraryStores.ActiveThreadsStore.getThreadsForGuild(id)).map(BDFDB.ObjectUtils.toArray).flat(), Internal.LibraryStores.GuildScheduledEventStore.getGuildScheduledEventsForGuild(id)]).flat(10).map(n => n && (n.channel && n.channel.id || n.id)).flat().filter(n => n); + if (channels.length) BDFDB.ChannelUtils.markAsRead(channels); + let eventChannels = guildIds.map(id => ({ + channelId: id, + readStateType: Internal.DiscordConstants.ReadStateTypes.GUILD_EVENT, + messageId: Internal.LibraryStores.ReadStateStore.lastMessageId(id, Internal.DiscordConstants.ReadStateTypes.GUILD_EVENT) + })).filter(n => n.messageId); + if (eventChannels.length) Internal.LibraryModules.AckUtils.bulkAck(eventChannels); + }; + + BDFDB.FolderUtils = {}; + BDFDB.FolderUtils.getId = function (div) { + if (!Node.prototype.isPrototypeOf(div) || !BDFDB.ReactUtils.getInstance(div)) return; + div = BDFDB.DOMUtils.getParent(BDFDB.dotCN.guildfolderwrapper, div); + if (!div) return; + return BDFDB.ReactUtils.findValue(div, "folderId", {up: true}); + }; + BDFDB.FolderUtils.getDefaultName = function (folderId) { + let folder = Internal.LibraryStores.SortedGuildStore.getGuildFolderById(folderId); + if (!folder) return ""; + let rest = 2 * Internal.DiscordConstants.MAX_GUILD_FOLDER_NAME_LENGTH; + let names = [], allNames = folder.guildIds.map(guildId => (Internal.LibraryStores.GuildStore.getGuild(guildId) || {}).name).filter(n => n); + for (let name of allNames) if (name.length < rest || names.length === 0) { + names.push(name); + rest -= name.length; + } + return names.join(", ") + (names.length < allNames.length ? ", ..." : ""); + }; + + BDFDB.ChannelUtils = {}; + BDFDB.ChannelUtils.is = function (channel) { + if (!BDFDB.ObjectUtils.is(channel)) return false; + let keys = Object.keys(channel); + return channel instanceof Internal.DiscordObjects.Channel || Object.keys(new Internal.DiscordObjects.Channel({})).every(key => keys.indexOf(key) > -1); + }; + BDFDB.ChannelUtils.isTextChannel = function (channelOrId) { + let channel = typeof channelOrId == "string" ? Internal.LibraryStores.ChannelStore.getChannel(channelOrId) : channelOrId; + return BDFDB.ObjectUtils.is(channel) && (channel.type == Internal.DiscordConstants.ChannelTypes.GUILD_TEXT || channel.type == Internal.DiscordConstants.ChannelTypes.GUILD_STORE || channel.type == Internal.DiscordConstants.ChannelTypes.GUILD_ANNOUNCEMENT); + }; + BDFDB.ChannelUtils.isThread = function (channelOrId) { + let channel = typeof channelOrId == "string" ? Internal.LibraryStores.ChannelStore.getChannel(channelOrId) : channelOrId; + return channel && channel.isThread(); + }; + BDFDB.ChannelUtils.isForumPost = function (channelOrId) { + let channel = typeof channelOrId == "string" ? Internal.LibraryStores.ChannelStore.getChannel(channelOrId) : channelOrId; + return channel && channel.parentChannelThreadType && channel.parentChannelThreadType == Internal.DiscordConstants.ChannelTypes.GUILD_FORUM; + }; + BDFDB.ChannelUtils.isEvent = function (channelOrId) { + let channel = typeof channelOrId == "string" ? Internal.LibraryStores.GuildScheduledEventStore.getGuildScheduledEvent(channelOrId) : channelOrId; + return channel && Internal.LibraryStores.GuildScheduledEventStore.getGuildScheduledEvent(channel.id) && true; + }; + BDFDB.ChannelUtils.markAsRead = function (channelIds) { + let unreadChannels = [channelIds].flat(10).filter(id => id && typeof id == "string" && (BDFDB.LibraryStores.ChannelStore.getChannel(id) || {}).type != Internal.DiscordConstants.ChannelTypes.GUILD_CATEGORY && (Internal.LibraryStores.ReadStateStore.hasUnread(id) || Internal.LibraryStores.ReadStateStore.getMentionCount(id) > 0)).map(id => ({ + channelId: id, + readStateType: Internal.DiscordConstants.ReadStateTypes.CHANNEL, + messageId: Internal.LibraryStores.ReadStateStore.lastMessageId(id) + })); + if (unreadChannels.length) Internal.LibraryModules.AckUtils.bulkAck(unreadChannels); + }; + BDFDB.ChannelUtils.rerenderAll = function (instant) { + BDFDB.TimeUtils.clear(BDFDB.ChannelUtils.rerenderAll.timeout); + BDFDB.ChannelUtils.rerenderAll.timeout = BDFDB.TimeUtils.timeout(_ => { + let ChannelsIns = BDFDB.ReactUtils.findOwner(document.querySelector(BDFDB.dotCN.guildchannels), {name: "ChannelsList", unlimited: true}); + if (!ChannelsIns) return; + else { + if (ChannelsIns && ChannelsIns.props && ChannelsIns.props.guildChannels.categories && Object.keys(ChannelsIns.props.guildChannels.categories).length) { + let category = ChannelsIns.props.guildChannels.categories[Object.keys(ChannelsIns.props.guildChannels.categories)[0]]; + category.isCollapsed ? BDFDB.LibraryModules.CategoryCollapseUtils.categoryCollapse(category.id) : BDFDB.LibraryModules.CategoryCollapseUtils.categoryExpand(category.id); + } + else { + let ChannelsPrototype = BDFDB.ObjectUtils.get(ChannelsIns, `${BDFDB.ReactUtils.instanceKey}.type.prototype`); + if (ChannelsIns && ChannelsPrototype) { + BDFDB.PatchUtils.patch({name: "BDFDB ChannelUtils"}, ChannelsPrototype, "render", {after: e => { + e.returnValue.props.children = typeof e.returnValue.props.children == "function" ? (_ => {return null;}) : []; + BDFDB.ReactUtils.forceUpdate(ChannelsIns); + }}, {once: true}); + BDFDB.ReactUtils.forceUpdate(ChannelsIns); + } + } + } + }, instant ? 0 : 1000); + }; + + BDFDB.DMUtils = {}; + BDFDB.DMUtils.isDMChannel = function (id) { + let channel = Internal.LibraryStores.ChannelStore.getChannel(id); + return BDFDB.ObjectUtils.is(channel) && (channel.isDM() || channel.isGroupDM()); + }; + BDFDB.DMUtils.getIcon = function (id) { + let channel = Internal.LibraryStores.ChannelStore.getChannel(id); + if (!channel) return ""; + if (!channel.icon) return channel.isDM() ? BDFDB.UserUtils.getAvatar(channel.recipients[0]) : (channel.isGroupDM() ? window.location.origin + Internal.LibraryModules.IconUtils.getChannelIconURL(channel).split("?")[0] : null); + return Internal.LibraryModules.IconUtils.getChannelIconURL(channel).split("?")[0]; + }; + BDFDB.DMUtils.markAsRead = function (dmIds) { + let unreadDMs = [dmIds].flat(10).filter(id => id && typeof id == "string" && BDFDB.DMUtils.isDMChannel(id) && (Internal.LibraryStores.ReadStateStore.hasUnread(id) || Internal.LibraryStores.ReadStateStore.getMentionCount(id) > 0)); + if (unreadDMs.length) for (let i in unreadDMs) BDFDB.TimeUtils.timeout(_ => Internal.LibraryModules.AckUtils.ack(unreadDMs[i]), i * 1000); + }; + + BDFDB.ColorUtils = {}; + BDFDB.ColorUtils.convert = function (color, conv, type) { + if (typeof color == "string" && color.indexOf("var(--") == 0) return color; + if (BDFDB.ObjectUtils.is(color)) { + let newColor = {}; + for (let pos in color) newColor[pos] = BDFDB.ColorUtils.convert(color[pos], conv, type); + return newColor; + } + else { + if (typeof color == "string") color = color.replace(/calc\(.+\s*\*\s*([0-9\.\%]+)\)/g, "$1"); + conv = conv === undefined || !conv ? conv = "RGBCOMP" : conv.toUpperCase(); + type = type === undefined || !type || !["RGB", "RGBA", "RGBCOMP", "HSL", "HSLA", "HSLCOMP", "HSV", "HSVA", "HSVCOMP", "HEX", "HEXA", "INT"].includes(type.toUpperCase()) ? BDFDB.ColorUtils.getType(color) : type.toUpperCase(); + if (conv == "RGBCOMP") { + switch (type) { + case "RGBCOMP": + var rgbComp = [].concat(color); + if (rgbComp.length == 3) return processRGB(rgbComp); + else if (rgbComp.length == 4) { + let a = processA(rgbComp.pop()); + return processRGB(rgbComp).concat(a); + } + break; + case "RGB": + return processRGB(color.replace(/\s/g, "").slice(4, -1).split(",")); + case "RGBA": + var rgbComp = color.replace(/\s/g, "").slice(5, -1).split(","); + var a = processA(rgbComp.pop()); + return processRGB(rgbComp).concat(a); + case "HSLCOMP": + var hslComp = [].concat(color); + if (hslComp.length == 3) return BDFDB.ColorUtils.convert(`hsl(${processHSX(hslComp).join(",")})`, "RGBCOMP"); + else if (hslComp.length == 4) { + let a = processA(hslComp.pop()); + return BDFDB.ColorUtils.convert(`hsl(${processHSX(hslComp).join(",")})`, "RGBCOMP").concat(a); + } + break; + case "HSL": + var hslComp = processHSX(color.replace(/\s/g, "").slice(4, -1).split(",")); + var r, g, b, m, c, x, p, q; + var h = hslComp[0], s = processPercentage(hslComp[1]), l = processPercentage(hslComp[2]); + var a = s * Math.min(l, 1-l); + var f = (n, k = (n+h / 30) % 12) => parseInt((l - a * Math.max(Math.min(k-3, 9-k, 1), -1)) * 255); + return [f(0), f(8), f(4)]; + case "HSLA": + var hslComp = color.replace(/\s/g, "").slice(5, -1).split(","); + return BDFDB.ColorUtils.convert(`hsl(${hslComp.slice(0, 3).join(",")})`, "RGBCOMP").concat(processA(hslComp.pop())); + case "HSVCOMP": + var hsvComp = [].concat(color); + if (hsvComp.length == 3) return BDFDB.ColorUtils.convert(`hsv(${processHSX(hsvComp).join(",")})`, "RGBCOMP"); + else if (hsvComp.length == 4) { + let a = processA(hsvComp.pop()); + return BDFDB.ColorUtils.convert(`hsv(${processHSX(hsvComp).join(",")})`, "RGBCOMP").concat(a); + } + break; + case "HSV": + var hsvComp = processHSX(color.replace(/\s/g, "").slice(4, -1).split(",")); + var r, g, b, i, f, p, q, t; + var h = hsvComp[0] / 360, s = processPercentage(hsvComp[1]), v = processPercentage(hsvComp[2]); + i = Math.floor(h * 6), f = h * 6 - i, p = v * (1 - s), q = v * (1 - f * s), t = v * (1 - (1 - f) * s); + + switch (i % 6) { + case 0: r = v, g = t, b = p; break; + case 1: r = q, g = v, b = p; break; + case 2: r = p, g = v, b = t; break; + case 3: r = p, g = q, b = v; break; + case 4: r = t, g = p, b = v; break; + case 5: r = v, g = p, b = q; break; + } + return [Math.round(r * 255), Math.round(g * 255), Math.round(b * 255)]; + case "HSVA": + var hsvComp = color.replace(/\s/g, "").slice(5, -1).split(","); + return BDFDB.ColorUtils.convert(`hsv(${hsvComp.slice(0, 3).join(",")})`, "RGBCOMP").concat(processA(hsvComp.pop())); + case "HEX": + var hex = /^#([a-f\d]{1})([a-f\d]{1})([a-f\d]{1})$|^#([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(color); + return [parseInt(hex[1] + hex[1] || hex[4], 16), parseInt(hex[2] + hex[2] || hex[5], 16), parseInt(hex[3] + hex[3] || hex[6], 16)]; + case "HEXA": + var hex = /^#([a-f\d]{1})([a-f\d]{1})([a-f\d]{1})([a-f\d]{1})$|^#([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(color); + return [parseInt(hex[1] + hex[1] || hex[5], 16), parseInt(hex[2] + hex[2] || hex[6], 16), parseInt(hex[3] + hex[3] || hex[7], 16), Math.floor(BDFDB.NumberUtils.mapRange([0, 255], [0, 100], parseInt(hex[4] + hex[4] || hex[8], 16)))/100]; + case "INT": + color = processINT(color); + return [parseInt(color >> 16 & 255), parseInt(color >> 8 & 255), parseInt(color & 255)]; + default: + return null; + } + } + else { + if (conv && type && (conv.indexOf("HSL") == 0 && type.indexOf("HSL") == 0 || conv.indexOf("HSV") == 0 && type.indexOf("HSV") == 0)) { + let name = type.indexOf("HSL") == 0 ? "HSL" : "HSV"; + if (type == `${name}COMP`) { + let comp = [].concat(color); + switch (conv) { + case `${name}COMP`: + if (comp.length == 3) return processHSX(comp); + else if (comp.length == 4) { + var a = processA(comp.pop()); + return processHSX(comp).concat(a); + } + break; + case name: + return `${name.toLowerCase()}(${processHSX(comp.slice(0, 3)).join(",")})`; + case `${name}A`: + comp = comp.slice(0, 4); + var a = comp.length == 4 ? processA(comp.pop()) : 1; + return `${name.toLowerCase()}a(${processHSX(comp).concat(a).join(",")})`; + } + } + return BDFDB.ColorUtils.convert(color.replace(/\s/g, "").slice(color.toUpperCase().indexOf("A") == 3 ? 5 : 4, -1).split(","), conv, `${name}COMP`); + } + else { + let rgbComp = type == "RGBCOMP" ? [].concat(color) : BDFDB.ColorUtils.convert(color, "RGBCOMP", type); + if (rgbComp) switch (conv) { + case "RGB": + return `rgb(${processRGB(rgbComp.slice(0, 3)).join(",")})`; + case "RGBA": + rgbComp = rgbComp.slice(0, 4); + var a = rgbComp.length == 4 ? processA(rgbComp.pop()) : 1; + return `rgba(${processRGB(rgbComp).concat(a).join(",")})`; + case "HSLCOMP": + var a = rgbComp.length == 4 ? processA(rgbComp.pop()) : null; + var hslComp = processHSX(BDFDB.ColorUtils.convert(rgbComp, "HSL").replace(/\s/g, "").split(",")); + return a != null ? hslComp.concat(a) : hslComp; + case "HSL": + var r = processC(rgbComp[0]) / 255, g = processC(rgbComp[1]) / 255, b = processC(rgbComp[2]) / 255; + var max = Math.max(r, g, b), min = Math.min(r, g, b); + + var h, s, l; + h = s = l = (max + min) / 2; + + if (max === min) return `hsl(${processHSX([0, 0, l * 100]).join(",")})`; + + var dif = max - min; + s = l >= 0.5 ? dif / (2 - (max + min)) : dif / (max + min); + switch (max) { + case r: h = ((g - b) / dif + 0) * 60; break; + case g: h = ((b - r) / dif + 2) * 60; break; + case b: h = ((r - g) / dif + 4) * 60; break; + } + return `hsl(${processHSX([Math.round(h * 360), s * 100, l * 100]).join(",")})`; + case "HSLA": + var a = rgbComp.length == 4 ? processA(rgbComp.pop()) : 1; + return `hsla(${BDFDB.ColorUtils.convert(rgbComp, "HSL").slice(4, -1).split(",").concat(a).join(",")})`; + case "HSVCOMP": + var a = rgbComp.length == 4 ? processA(rgbComp.pop()) : null; + var hsvComp = processHSX(BDFDB.ColorUtils.convert(rgbComp, "HSV").replace(/\s/g, "").split(",")); + return a != null ? hsvComp.concat(a) : hsvComp; + case "HSV": + var r = processC(rgbComp[0]), g = processC(rgbComp[1]), b = processC(rgbComp[2]); + var max = Math.max(r, g, b), min = Math.min(r, g, b), dif = max - min, h, s = max === 0 ? 0 : dif / max, v = max / 255; + switch (max) { + case min: h = 0; break; + case r: h = g - b + dif * (g < b ? 6 : 0); h /= 6 * dif; break; + case g: h = b - r + dif * 2; h /= 6 * dif; break; + case b: h = r - g + dif * 4; h /= 6 * dif; break; + } + return `hsv(${processHSX([Math.round(h * 360), s * 100, v * 100]).join(",")})`; + case "HSVA": + var a = rgbComp.length == 4 ? processA(rgbComp.pop()) : 1; + return `hsva(${BDFDB.ColorUtils.convert(rgbComp, "HSV").slice(4, -1).split(",").concat(a).join(",")})`; + case "HEX": + return ("#" + (0x1000000 + (rgbComp[2] | rgbComp[1] << 8 | rgbComp[0] << 16)).toString(16).slice(1)).toUpperCase(); + case "HEXA": + return ("#" + (0x1000000 + (rgbComp[2] | rgbComp[1] << 8 | rgbComp[0] << 16)).toString(16).slice(1) + (0x100 + Math.round(BDFDB.NumberUtils.mapRange([0, 100], [0, 255], processA(rgbComp[3]) * 100))).toString(16).slice(1)).toUpperCase(); + case "INT": + return processINT(rgbComp[2] | rgbComp[1] << 8 | rgbComp[0] << 16); + default: + return null; + } + } + } + } + return null; + function processC (c) { + if (c == null) return 255; + else { + c = parseInt(c.toString().replace(/[^0-9\-]/g, "")); + return isNaN(c) || c > 255 ? 255 : c < 0 ? 0 : c; + } + }; + function processRGB (comp) { + return [].concat(comp).map(processC); + }; + function processA (a) { + if (a == null) return 1; + else { + a = a.toString(); + a = (a.indexOf("%") > -1 ? 0.01 : 1) * parseFloat(a.replace(/[^0-9\.\-]/g, "")); + return isNaN(a) || a > 1 ? 1 : a < 0 ? 0 : a; + } + }; + function processPercentage (p) { + if (p == null) return 1; + else return p.indexOf("%") > -1 ? parseFloat(p)/100 : p; + }; + function processSL (sl) { + if (sl == null) return "100%"; + else { + sl = parseFloat(sl.toString().replace(/[^0-9\.\-]/g, "")); + return (isNaN(sl) || sl > 100 ? 100 : sl < 0 ? 0 : sl) + "%"; + } + }; + function processHSX (comp) { + comp = [].concat(comp); + let h = parseFloat(comp.shift().toString().replace(/[^0-9\.\-]/g, "")); + h = isNaN(h) || h > 360 ? 360 : h < 0 ? 0 : h; + return [h].concat(comp.map(processSL)); + }; + function processINT (c) { + if (c == null) return 16777215; + else { + c = parseInt(c.toString().replace(/[^0-9]/g, "")); + return isNaN(c) || c > 16777215 ? 16777215 : c < 0 ? 0 : c; + } + }; + }; + BDFDB.ColorUtils.setAlpha = function (color, a, conv) { + if (typeof color == "string" && color.indexOf("var(--") == 0) return color; + if (BDFDB.ObjectUtils.is(color)) { + let newcolor = {}; + for (let pos in color) newcolor[pos] = BDFDB.ColorUtils.setAlpha(color[pos], a, conv); + return newcolor; + } + else { + let rgbComp = BDFDB.ColorUtils.convert(color, "RGBCOMP"); + if (rgbComp) { + a = a.toString(); + a = (a.indexOf("%") > -1 ? 0.01 : 1) * parseFloat(a.replace(/[^0-9\.\-]/g, "")); + a = isNaN(a) || a > 1 ? 1 : a < 0 ? 0 : a; + rgbComp[3] = a; + conv = (conv || BDFDB.ColorUtils.getType(color)).toUpperCase(); + conv = conv == "RGB" || conv == "HSL" || conv == "HEX" ? conv + "A" : conv; + return BDFDB.ColorUtils.convert(rgbComp, conv); + } + } + return null; + }; + BDFDB.ColorUtils.getAlpha = function (color) { + let rgbComp = BDFDB.ColorUtils.convert(color, "RGBCOMP"); + if (rgbComp) { + if (rgbComp.length == 3) return 1; + else if (rgbComp.length == 4) { + let a = rgbComp[3].toString(); + a = (a.indexOf("%") > -1 ? 0.01 : 1) * parseFloat(a.replace(/[^0-9\.\-]/g, "")); + return isNaN(a) || a > 1 ? 1 : a < 0 ? 0 : a; + } + } + return null; + }; + BDFDB.ColorUtils.change = function (color, value, conv) { + if (typeof color == "string" && color.indexOf("var(--") == 0) return color; + value = parseFloat(value); + if (color != null && typeof value == "number" && !isNaN(value)) { + if (BDFDB.ObjectUtils.is(color)) { + let newColor = {}; + for (let pos in color) newColor[pos] = BDFDB.ColorUtils.change(color[pos], value, conv); + return newColor; + } + else { + let rgbComp = BDFDB.ColorUtils.convert(color, "RGBCOMP"); + if (rgbComp) { + let a = BDFDB.ColorUtils.getAlpha(rgbComp); + if (parseInt(value) !== value) { + value = value.toString(); + value = (value.indexOf("%") > -1 ? 0.01 : 1) * parseFloat(value.replace(/[^0-9\.\-]/g, "")); + value = isNaN(value) ? 0 : value; + return BDFDB.ColorUtils.convert([].concat(rgbComp).slice(0, 3).map(c => { + c = Math.round(c * (1 + value)); + return c > 255 ? 255 : c < 0 ? 0 : c; + }).concat(a), conv || BDFDB.ColorUtils.getType(color)); + } + else return BDFDB.ColorUtils.convert([].concat(rgbComp).slice(0, 3).map(c => { + c = Math.round(c + value); + return c > 255 ? 255 : c < 0 ? 0 : c; + }).concat(a), conv || BDFDB.ColorUtils.getType(color)); + } + } + } + return null; + }; + BDFDB.ColorUtils.invert = function (color, conv) { + if (typeof color == "string" && color.indexOf("var(--") == 0) return color; + if (BDFDB.ObjectUtils.is(color)) { + let newColor = {}; + for (let pos in color) newColor[pos] = BDFDB.ColorUtils.invert(color[pos], conv); + return newColor; + } + else { + let comp = BDFDB.ColorUtils.convert(color, "RGBCOMP"); + if (comp) return BDFDB.ColorUtils.convert([255 - comp[0], 255 - comp[1], 255 - comp[2]], conv || BDFDB.ColorUtils.getType(color)); + } + return null; + }; + BDFDB.ColorUtils.compare = function (color1, color2) { + if (color1 && color2) { + color1 = BDFDB.ColorUtils.convert(color1, "RGBA"); + color2 = BDFDB.ColorUtils.convert(color2, "RGBA"); + if (color1 && color2) return BDFDB.equals(color1, color2); + } + return null; + }; + BDFDB.ColorUtils.isBright = function (color, compare = 160) { + if (!BDFDB.ColorUtils.getType(color)) return false; + color = BDFDB.ColorUtils.convert(color, "RGBCOMP"); + if (!color) return false; + return parseInt(compare) < Math.sqrt(0.299 * color[0]**2 + 0.587 * color[1]**2 + 0.144 * color[2]**2); + }; + BDFDB.ColorUtils.getType = function (color) { + if (color !== null) { + if (typeof color === "object" && (color.length == 3 || color.length == 4)) { + if (isRGB(color)) return "RGBCOMP"; + else if (isHSL(color)) return "HSLCOMP"; + } + else if (typeof color === "string") { + color = color.replace(/calc\(.+\s*\*\s*([0-9\.\%]+)\)/g, "$1"); + if (/^#[a-f\d]{3}$|^#[a-f\d]{6}$/i.test(color)) return "HEX"; + else if (/^#[a-f\d]{4}$|^#[a-f\d]{8}$/i.test(color)) return "HEXA"; + else { + color = color.toUpperCase(); + let comp = color.replace(/[^0-9\.\-\,\%]/g, "").split(","); + if (color.indexOf("RGB(") == 0 && comp.length == 3 && isRGB(comp)) return "RGB"; + else if (color.indexOf("RGBA(") == 0 && comp.length == 4 && isRGB(comp)) return "RGBA"; + else if (color.indexOf("HSL(") == 0 && comp.length == 3 && isHSL(comp)) return "HSL"; + else if (color.indexOf("HSLA(") == 0 && comp.length == 4 && isHSL(comp)) return "HSLA"; + else if (color.indexOf("HSV(") == 0 && comp.length == 3 && isHSL(comp)) return "HSV"; + else if (color.indexOf("HSVA(") == 0 && comp.length == 4 && isHSL(comp)) return "HSVA"; + } + } + else if (typeof color === "number" && parseInt(color) == color && color > -1 && color < 16777216) return "INT"; + } + return null; + function isRGB(comp) {return comp.slice(0, 3).every(rgb => rgb.toString().indexOf("%") == -1 && parseFloat(rgb) == parseInt(rgb));}; + function isHSL(comp) {return comp.slice(1, 3).every(hsl => hsl.toString().indexOf("%") == hsl.length - 1);}; + }; + BDFDB.ColorUtils.createGradient = function (colorObj, direction = "to right") { + let gradientString = "linear-gradient(" + direction; + for (let pos of Object.keys(colorObj).sort()) { + let color = BDFDB.ColorUtils.convert(colorObj[pos], "RGBA"); + gradientString += color ? `, ${color} ${pos*100}%` : '' + } + return gradientString += ")"; + }; + + BDFDB.DOMUtils = {}; + BDFDB.DOMUtils.getSelection = function () { + let selection = document.getSelection(); + return selection && selection.anchorNode ? selection.getRangeAt(0).toString() : ""; + }; + BDFDB.DOMUtils.addClass = function (eles, ...classes) { + if (!eles || !classes) return; + for (let ele of [eles].map(n => NodeList.prototype.isPrototypeOf(n) ? Array.from(n) : n).flat(10).filter(n => n)) { + if (Node.prototype.isPrototypeOf(ele)) add(ele); + else if (NodeList.prototype.isPrototypeOf(ele)) for (let e of ele) add(e); + else if (typeof ele == "string") for (let e of ele.split(",")) if (e && (e = e.trim())) for (let n of document.querySelectorAll(e)) add(n); + } + function add(node) { + if (node && node.classList) for (let cla of classes) for (let cl of [cla].flat(10).filter(n => n)) if (typeof cl == "string") for (let c of cl.split(" ")) if (c) node.classList.add(c); + } + }; + BDFDB.DOMUtils.removeClass = function (eles, ...classes) { + if (!eles || !classes) return; + for (let ele of [eles].map(n => NodeList.prototype.isPrototypeOf(n) ? Array.from(n) : n).flat(10).filter(n => n)) { + if (Node.prototype.isPrototypeOf(ele)) remove(ele); + else if (NodeList.prototype.isPrototypeOf(ele)) for (let e of ele) remove(e); + else if (typeof ele == "string") for (let e of ele.split(",")) if (e && (e = e.trim())) for (let n of document.querySelectorAll(e)) remove(n); + } + function remove(node) { + if (node && node.classList) for (let cla of classes) for (let cl of [cla].flat(10).filter(n => n)) if (typeof cl == "string") for (let c of cl.split(" ")) if (c) node.classList.remove(c); + } + }; + BDFDB.DOMUtils.toggleClass = function (eles, ...classes) { + if (!eles || !classes) return; + var force = classes.pop(); + if (typeof force != "boolean") { + classes.push(force); + force = undefined; + } + if (!classes.length) return; + for (let ele of [eles].map(n => NodeList.prototype.isPrototypeOf(n) ? Array.from(n) : n).flat(10).filter(n => n)) { + if (Node.prototype.isPrototypeOf(ele)) toggle(ele); + else if (NodeList.prototype.isPrototypeOf(ele)) for (let e of ele) toggle(e); + else if (typeof ele == "string") for (let e of ele.split(",")) if (e && (e = e.trim())) for (let n of document.querySelectorAll(e)) toggle(n); + } + function toggle(node) { + if (node && node.classList) for (let cla of classes) for (let cl of [cla].flat(10).filter(n => n)) if (typeof cl == "string") for (let c of cl.split(" ")) if (c) node.classList.toggle(c, force); + } + }; + BDFDB.DOMUtils.containsClass = function (eles, ...classes) { + if (!eles || !classes) return; + let all = classes.pop(); + if (typeof all != "boolean") { + classes.push(all); + all = true; + } + if (!classes.length) return; + let contained = undefined; + for (let ele of [eles].map(n => NodeList.prototype.isPrototypeOf(n) ? Array.from(n) : n).flat(10).filter(n => n)) { + if (Node.prototype.isPrototypeOf(ele)) contains(ele); + else if (NodeList.prototype.isPrototypeOf(ele)) for (let e of ele) contains(e); + else if (typeof ele == "string") for (let c of ele.split(",")) if (c && (c = c.trim())) for (let n of document.querySelectorAll(c)) contains(n); + } + return contained; + function contains(node) { + if (node && node.classList) for (let cla of classes) if (typeof cla == "string") for (let c of cla.split(" ")) if (c) { + if (contained === undefined) contained = all; + if (all && !node.classList.contains(c)) contained = false; + if (!all && node.classList.contains(c)) contained = true; + } + } + }; + BDFDB.DOMUtils.formatClassName = function (...classes) { + return BDFDB.ArrayUtils.removeCopies(classes.flat(10).filter(n => n).join(" ").split(" ")).join(" ").trim(); + }; + BDFDB.DOMUtils.removeClassFromDOM = function (...classes) { + for (let c of classes.flat(10).filter(n => n)) if (typeof c == "string") for (let a of c.split(",")) if (a && (a = a.replace(/\.|\s/g, ""))) BDFDB.DOMUtils.removeClass(document.querySelectorAll("." + a), a); + }; + BDFDB.DOMUtils.show = function (...eles) { + BDFDB.DOMUtils.toggle(...eles, true); + }; + BDFDB.DOMUtils.hide = function (...eles) { + BDFDB.DOMUtils.toggle(...eles, false); + }; + BDFDB.DOMUtils.toggle = function (...eles) { + if (!eles) return; + let force = eles.pop(); + if (typeof force != "boolean") { + eles.push(force); + force = undefined; + } + if (!eles.length) return; + for (let ele of eles.flat(10).filter(n => n)) { + if (Node.prototype.isPrototypeOf(ele)) toggle(ele); + else if (NodeList.prototype.isPrototypeOf(ele)) for (let node of ele) toggle(node); + else if (typeof ele == "string") for (let c of ele.split(",")) if (c && (c = c.trim())) for (let node of document.querySelectorAll(c)) toggle(node); + } + function toggle(node) { + if (!node || !Node.prototype.isPrototypeOf(node)) return; + let hide = force === undefined ? !BDFDB.DOMUtils.isHidden(node) : !force; + if (hide) { + let display = node.style.getPropertyValue("display"); + if (display && display != "none") node.BDFDBhideDisplayState = { + display: display, + important: (` ${node.style.cssText} `.split(` display: ${display}`)[1] || "").trim().indexOf("!important") == 0 + }; + node.style.setProperty("display", "none", "important"); + } + else { + if (node.BDFDBhideDisplayState) { + node.style.setProperty("display", node.BDFDBhideDisplayState.display, node.BDFDBhideDisplayState.important ? "important" : ""); + delete node.BDFDBhideDisplayState; + } + else node.style.removeProperty("display"); + } + } + }; + BDFDB.DOMUtils.isHidden = function (node) { + if (Node.prototype.isPrototypeOf(node) && node.nodeType != Node.TEXT_NODE) return getComputedStyle(node, null).getPropertyValue("display") == "none"; + }; + BDFDB.DOMUtils.remove = function (...eles) { + for (let ele of eles.flat(10).filter(n => n)) { + if (Node.prototype.isPrototypeOf(ele)) ele.remove(); + else if (NodeList.prototype.isPrototypeOf(ele)) { + let nodes = Array.from(ele); + while (nodes.length) nodes.shift().remove(); + } + else if (typeof ele == "string") for (let c of ele.split(",")) if (c && (c = c.trim())) { + let nodes = Array.from(document.querySelectorAll(c)); + while (nodes.length) nodes.shift().remove(); + } + } + }; + BDFDB.DOMUtils.create = function (html) { + if (typeof html != "string" || !html.trim()) return null; + let template = document.createElement("template"); + try {template.innerHTML = html.replace(/(?[\t\r\n]+<(?!pre)/g, "><");} + catch (err) {template.innerHTML = html.replace(/>[\t\r\n]+<(?!pre)/g, "><");} + if (template.content.childNodes.length == 1) return template.content.firstElementChild || template.content.firstChild; + else { + let wrapper = document.createElement("span"); + let nodes = Array.from(template.content.childNodes); + while (nodes.length) wrapper.appendChild(nodes.shift()); + return wrapper; + } + }; + BDFDB.DOMUtils.getParent = function (listOrSelector, node) { + let parent = null; + if (Node.prototype.isPrototypeOf(node) && listOrSelector) { + let list = NodeList.prototype.isPrototypeOf(listOrSelector) ? listOrSelector : typeof listOrSelector == "string" ? document.querySelectorAll(listOrSelector) : null; + if (list) for (let listNode of list) if (listNode.contains(node)) { + parent = listNode; + break; + } + } + return parent; + }; + BDFDB.DOMUtils.setText = function (node, stringOrNode) { + if (!node || !Node.prototype.isPrototypeOf(node)) return; + let textnode = node.nodeType == Node.TEXT_NODE ? node : null; + if (!textnode) for (let child of node.childNodes) if (child.nodeType == Node.TEXT_NODE || BDFDB.DOMUtils.containsClass(child, "BDFDB-textnode")) { + textnode = child; + break; + } + if (textnode) { + if (Node.prototype.isPrototypeOf(stringOrNode) && stringOrNode.nodeType != Node.TEXT_NODE) { + BDFDB.DOMUtils.addClass(stringOrNode, "BDFDB-textnode"); + node.replaceChild(stringOrNode, textnode); + } + else if (Node.prototype.isPrototypeOf(textnode) && textnode.nodeType != Node.TEXT_NODE) node.replaceChild(document.createTextNode(stringOrNode), textnode); + else textnode.textContent = stringOrNode; + } + else node.appendChild(Node.prototype.isPrototypeOf(stringOrNode) ? stringOrNode : document.createTextNode(stringOrNode)); + }; + BDFDB.DOMUtils.getText = function (node) { + if (!node || !Node.prototype.isPrototypeOf(node)) return; + for (let child of node.childNodes) if (child.nodeType == Node.TEXT_NODE) return child.textContent; + }; + BDFDB.DOMUtils.getRects = function (node) { + let rects = {}; + if (Node.prototype.isPrototypeOf(node) && node.nodeType != Node.TEXT_NODE) { + let hideNode = node; + while (hideNode) { + let hidden = BDFDB.DOMUtils.isHidden(hideNode); + if (hidden) { + BDFDB.DOMUtils.toggle(hideNode, true); + hideNode.BDFDBgetRectsHidden = true; + } + hideNode = hideNode.parentElement; + } + rects = node.getBoundingClientRect(); + hideNode = node; + while (hideNode) { + if (hideNode.BDFDBgetRectsHidden) { + BDFDB.DOMUtils.toggle(hideNode, false); + delete hideNode.BDFDBgetRectsHidden; + } + hideNode = hideNode.parentElement; + } + } + return rects; + }; + BDFDB.DOMUtils.getHeight = function (node) { + if (Node.prototype.isPrototypeOf(node) && node.nodeType != Node.TEXT_NODE) { + let rects = BDFDB.DOMUtils.getRects(node); + let style = getComputedStyle(node); + return rects.height + parseInt(style.marginTop) + parseInt(style.marginBottom); + } + return 0; + }; + BDFDB.DOMUtils.getInnerHeight = function (node) { + if (Node.prototype.isPrototypeOf(node) && node.nodeType != Node.TEXT_NODE) { + let rects = BDFDB.DOMUtils.getRects(node); + let style = getComputedStyle(node); + return rects.height - parseInt(style.paddingTop) - parseInt(style.paddingBottom); + } + return 0; + }; + BDFDB.DOMUtils.getWidth = function (node) { + if (Node.prototype.isPrototypeOf(node) && node.nodeType != Node.TEXT_NODE) { + let rects = BDFDB.DOMUtils.getRects(node); + let style = getComputedStyle(node); + return rects.width + parseInt(style.marginLeft) + parseInt(style.marginRight); + } + return 0; + }; + BDFDB.DOMUtils.getInnerWidth = function (node) { + if (Node.prototype.isPrototypeOf(node) && node.nodeType != Node.TEXT_NODE) { + let rects = BDFDB.DOMUtils.getRects(node); + let style = getComputedStyle(node); + return rects.width - parseInt(style.paddingLeft) - parseInt(style.paddingRight); + } + return 0; + }; + BDFDB.DOMUtils.appendWebScript = function (url, container) { + if (typeof url != "string") return; + if (!container && !document.body.querySelector("bd-head bd-scripts")) document.body.appendChild(BDFDB.DOMUtils.create(``)); + container = container || document.body.querySelector("bd-head bd-scripts") || document.body; + container = Node.prototype.isPrototypeOf(container) ? container : document.body; + BDFDB.DOMUtils.removeWebScript(url, container); + let script = document.createElement("script"); + script.src = url; + container.appendChild(script); + }; + BDFDB.DOMUtils.removeWebScript = function (url, container) { + if (typeof url != "string") return; + container = container || document.body.querySelector("bd-head bd-scripts") || document.body; + container = Node.prototype.isPrototypeOf(container) ? container : document.body; + BDFDB.DOMUtils.remove(container.querySelectorAll(`script[src="${url}"]`)); + }; + BDFDB.DOMUtils.appendWebStyle = function (url, container) { + if (typeof url != "string") return; + if (!container && !document.body.querySelector("bd-head bd-styles")) document.body.appendChild(BDFDB.DOMUtils.create(``)); + container = container || document.body.querySelector("bd-head bd-styles") || document.body; + container = Node.prototype.isPrototypeOf(container) ? container : document.body; + BDFDB.DOMUtils.removeWebStyle(url, container); + container.appendChild(BDFDB.DOMUtils.create(``)); + }; + BDFDB.DOMUtils.removeWebStyle = function (url, container) { + if (typeof url != "string") return; + container = container || document.body.querySelector("bd-head bd-styles") || document.body; + container = Node.prototype.isPrototypeOf(container) ? container : document.body; + BDFDB.DOMUtils.remove(container.querySelectorAll(`link[href="${url}"]`)); + }; + BDFDB.DOMUtils.appendLocalStyle = function (id, css, container) { + if (typeof id != "string" || typeof css != "string") return; + if (!container && !document.body.querySelector("bd-head bd-styles")) document.body.appendChild(BDFDB.DOMUtils.create(``)); + container = container || document.body.querySelector("bd-head bd-styles") || document.body; + container = Node.prototype.isPrototypeOf(container) ? container : document.body; + BDFDB.DOMUtils.removeLocalStyle(id, container); + container.appendChild(BDFDB.DOMUtils.create(``)); + }; + BDFDB.DOMUtils.removeLocalStyle = function (id, container) { + if (typeof id != "string") return; + container = container || document.body.querySelector("bd-head bd-styles") || document.body; + container = Node.prototype.isPrototypeOf(container) ? container : document.body; + BDFDB.DOMUtils.remove(container.querySelectorAll(`style[id="${id}CSS"]`)); + }; + + BDFDB.ModalUtils = {}; + BDFDB.ModalUtils.open = function (plugin, config) { + if (!BDFDB.ObjectUtils.is(plugin) || !BDFDB.ObjectUtils.is(config)) return; + let modalInstance, modalProps, cancels = [], closeModal = _ => { + if (BDFDB.ObjectUtils.is(modalProps) && typeof modalProps.onClose == "function") modalProps.onClose(); + }; + + let titleChildren = [], headerChildren = [], contentChildren = [], footerChildren = []; + + if (typeof config.text == "string") { + config.contentClassName = BDFDB.DOMUtils.formatClassName(config.contentClassName, BDFDB.disCN.modaltextcontent); + contentChildren.push(BDFDB.ReactUtils.createElement(Internal.LibraryComponents.TextElement, { + children: config.text + })); + } + + if (config.children) { + let tabBarItems = [], tabIns = {}; + for (let child of [config.children].flat(10).filter(n => n)) if (Internal.LibraryModules.React.isValidElement(child)) { + if (child.type == Internal.LibraryComponents.ModalComponents.ModalTabContent) { + if (!tabBarItems.length) child.props.open = true; + else delete child.props.open; + let ref = typeof child.ref == "function" ? child.ref : (_ => {}); + child.ref = instance => { + ref(instance); + if (instance) tabIns[child.props.tab] = instance; + }; + tabBarItems.push({value: child.props.tab}); + } + contentChildren.push(child); + } + if (tabBarItems.length) headerChildren.push(BDFDB.ReactUtils.createElement("div", { + className: BDFDB.disCN.tabbarcontainer, + children: [ + BDFDB.ReactUtils.createElement(Internal.LibraryComponents.TabBar, { + className: BDFDB.disCN.tabbar, + itemClassName: BDFDB.disCN.tabbaritem, + type: Internal.LibraryComponents.TabBar.Types.TOP, + items: tabBarItems, + onItemSelect: value => { + for (let key in tabIns) { + if (key == value) tabIns[key].props.open = true; + else delete tabIns[key].props.open; + } + BDFDB.ReactUtils.forceUpdate(BDFDB.ObjectUtils.toArray(tabIns)); + } + }), + config.tabBarChildren + ].flat(10).filter(n => n) + })); + } + + if (BDFDB.ArrayUtils.is(config.buttons)) for (let button of config.buttons) { + let contents = typeof button.contents == "string" && button.contents; + if (contents) { + let color = typeof button.color == "string" && Internal.LibraryComponents.Button.Colors[button.color.toUpperCase()]; + let look = typeof button.look == "string" && Internal.LibraryComponents.Button.Looks[button.look.toUpperCase()]; + let click = typeof button.click == "function" ? button.click : (typeof button.onClick == "function" ? button.onClick : _ => {}); + + if (button.cancel) cancels.push(click); + + footerChildren.push(BDFDB.ReactUtils.createElement(Internal.LibraryComponents.Button, BDFDB.ObjectUtils.exclude(Object.assign({}, button, { + look: look || (color ? Internal.LibraryComponents.Button.Looks.FILLED : Internal.LibraryComponents.Button.Looks.LINK), + color: color || Internal.LibraryComponents.Button.Colors.PRIMARY, + onClick: _ => { + if (button.close) closeModal(); + if (!(button.close && button.cancel)) click(modalInstance); + }, + children: contents + }), "click", "close", "cancel", "contents"))); + } + } + + contentChildren = contentChildren.concat(config.contentChildren).filter(n => n && (typeof n == "string" || BDFDB.ReactUtils.isValidElement(n))); + titleChildren = titleChildren.concat(config.titleChildren).filter(n => n && (typeof n == "string" || BDFDB.ReactUtils.isValidElement(n))); + headerChildren = headerChildren.concat(config.headerChildren).filter(n => n && (typeof n == "string" || BDFDB.ReactUtils.isValidElement(n))); + footerChildren = footerChildren.concat(config.footerChildren).filter(n => n && (typeof n == "string" || BDFDB.ReactUtils.isValidElement(n))); + + if (contentChildren.length) { + if (typeof config.onOpen != "function") config.onOpen = _ => {}; + if (typeof config.onClose != "function") config.onClose = _ => {}; + + let name = plugin.name || (typeof plugin.getName == "function" ? plugin.getName() : null); + name = typeof name == "string" ? name : null; + let oldTransitionState = 0; + !Internal.LibraryModules.ModalUtils ? BdApi.alert(BDFDB.ReactUtils.createElement("div", { + style: {"display": "flex", "flex-direction": "column"}, + children: [ + config.header, + typeof config.subHeader == "string" || BDFDB.ReactUtils.isValidElement(config.subHeader) ? config.subHeader : (name || "") + ].filter(n => n).map(n => BDFDB.ReactUtils.createElement("span", {children: n})) + }), config.content || config.children) : Internal.LibraryModules.ModalUtils.openModal(props => { + modalProps = props; + return BDFDB.ReactUtils.createElement(class BDFDB_Modal extends Internal.LibraryModules.React.Component { + render() { + return BDFDB.ReactUtils.createElement(Internal.LibraryComponents.ModalComponents.ModalRoot, { + className: BDFDB.DOMUtils.formatClassName(name && `${name}-modal`, BDFDB.disCN.modalwrapper, config.className), + size: typeof config.size == "string" && Internal.LibraryComponents.ModalComponents.ModalSize[config.size.toUpperCase()] || Internal.LibraryComponents.ModalComponents.ModalSize.SMALL, + transitionState: props.transitionState, + children: [ + BDFDB.ReactUtils.createElement(Internal.LibraryComponents.ModalComponents.ModalHeader, { + className: BDFDB.DOMUtils.formatClassName(config.headerClassName, config.shade && BDFDB.disCN.modalheadershade, headerChildren.length && BDFDB.disCN.modalheaderhassibling), + separator: config.headerSeparator || false, + children: [ + BDFDB.ReactUtils.createElement(Internal.LibraryComponents.Flex.Child, { + children: [ + BDFDB.ReactUtils.createElement(Internal.LibraryComponents.FormComponents.FormTitle, { + tag: Internal.LibraryComponents.FormComponents.FormTags && Internal.LibraryComponents.FormComponents.FormTags.H4, + children: config.header + }), + BDFDB.ReactUtils.createElement(Internal.LibraryComponents.TextElement, { + size: Internal.LibraryComponents.TextElement.Sizes.SIZE_12, + children: typeof config.subHeader == "string" || BDFDB.ReactUtils.isValidElement(config.subHeader) ? config.subHeader : (name || "") + }) + ] + }), + titleChildren, + BDFDB.ReactUtils.createElement(Internal.LibraryComponents.ModalComponents.ModalCloseButton, { + onClick: closeModal + }) + ].flat(10).filter(n => n) + }), + headerChildren.length ? BDFDB.ReactUtils.createElement(Internal.LibraryComponents.Flex, { + grow: 0, + shrink: 0, + children: headerChildren + }) : null, + BDFDB.ReactUtils.createElement(Internal.LibraryComponents.ModalComponents.ModalContent, { + className: config.contentClassName, + scroller: config.scroller, + direction: config.direction, + content: config.content, + children: contentChildren + }), + footerChildren.length ? BDFDB.ReactUtils.createElement(Internal.LibraryComponents.ModalComponents.ModalFooter, { + className: config.footerClassName, + direction: config.footerDirection, + children: footerChildren + }) : null + ] + }); + } + componentDidMount() { + modalInstance = this; + if (props.transitionState == 1 && props.transitionState > oldTransitionState) config.onOpen(modalInstance); + oldTransitionState = props.transitionState; + } + componentWillUnmount() { + if (props.transitionState == 3) { + for (let cancel of cancels) cancel(modalInstance); + config.onClose(modalInstance); + } + } + }, props, true); + }, { + onCloseRequest: closeModal + }); + } + }; + BDFDB.ModalUtils.confirm = function (plugin, text, callback) { + if (!BDFDB.ObjectUtils.is(plugin) || typeof text != "string") return; + BDFDB.ModalUtils.open(plugin, { + text: text, + header: BDFDB.LanguageUtils.LibraryStrings.confirm, + className: BDFDB.disCN.modalconfirmmodal, + scroller: false, + buttons: [ + {contents: BDFDB.LanguageUtils.LanguageStrings.OKAY, close: true, color: "RED", onClick: callback}, + {contents: BDFDB.LanguageUtils.LanguageStrings.CANCEL, close: true} + ] + }); + }; + + var MappedMenuItems = {}, RealMenuItems = BDFDB.ModuleUtils.findByProperties("MenuCheckboxItem", "MenuItem") || BDFDB.ModuleUtils.find(m => { + if (!m || typeof m != "function") return false; + let string = m.toString(); + return string.endsWith("{return null}}") && string.indexOf("(){return null}") > -1 && string.indexOf("catch(") == -1; + }) || BDFDB.ModuleUtils.findByString("(){return null}function"); + if (!RealMenuItems) { + RealMenuItems = {}; + BDFDB.LogUtils.error(["could not find Module for MenuItems"]); + } + BDFDB.ContextMenuUtils = {}; + BDFDB.ContextMenuUtils.open = function (plugin, e, children) { + Internal.LibraryModules.ContextMenuUtils.openContextMenu(e || mousePosition, _ => BDFDB.ReactUtils.createElement(Internal.LibraryComponents.Menu, { + navId: "bdfdb-context", + onClose: Internal.LibraryModules.ContextMenuUtils.closeContextMenu, + children: children + }, true)); + }; + BDFDB.ContextMenuUtils.close = function (nodeOrInstance) { + if (!BDFDB.ObjectUtils.is(nodeOrInstance)) return; + let instance = BDFDB.ReactUtils.findOwner(nodeOrInstance, {props: "closeContextMenu", up: true}); + if (BDFDB.ObjectUtils.is(instance) && instance.props && typeof instance.props.closeContextMenu == "function") instance.props.closeContextMenu(); + else Internal.LibraryModules.ContextMenuUtils.closeContextMenu(); + }; + BDFDB.ContextMenuUtils.createItem = function (component, props = {}) { + if (!component) return null; + else { + if (props.render || props.persisting || BDFDB.ObjectUtils.is(props.popoutProps) || (typeof props.color == "string" && !InternalData.DiscordClasses[`menu${props.color.toLowerCase()}`])) component = Internal.MenuItem; + if (BDFDB.ObjectUtils.toArray(RealMenuItems).some(c => c == component)) return BDFDB.ReactUtils.createElement(component, props); + else return BDFDB.ReactUtils.createElement(LibraryComponents.MenuItems.MenuItem, { + id: props.id, + disabled: props.disabled, + customItem: true, + render: menuItemProps => { + if (!props.state) props.state = BDFDB.ObjectUtils.extract(props, "checked", "value"); + return BDFDB.ReactUtils.createElement(Internal.CustomMenuItemWrapper, { + disabled: props.disabled, + childProps: Object.assign({}, props, menuItemProps, {color: props.color}), + children: component + }, true); + } + }); + } + }; + BDFDB.ContextMenuUtils.createItemId = function (...strings) { + return strings.map(s => typeof s == "number" ? s.toString() : s).filter(s => typeof s == "string").map(s => s.toLowerCase().replace(/\s/, "-")).join("-"); + }; + BDFDB.ContextMenuUtils.findItem = function (returnvalue, config) { + if (!returnvalue || !BDFDB.ObjectUtils.is(config) || !config.label && !config.id) return [null, -1]; + config.label = config.label && [config.label].flat().filter(n => n); + config.id = config.id && [config.id].flat().filter(n => n); + let contextMenu = BDFDB.ArrayUtils.is(returnvalue) ? {props: {children: returnvalue}} : BDFDB.ReactUtils.findChild(returnvalue, {props: "navId"}); + if (contextMenu) { + let children = BDFDB.ArrayUtils.is(contextMenu.props.children) ? contextMenu.props.children : [contextMenu.props.children]; + for (let i in children) if (children[i]) { + if (check(children[i])) return [children, parseInt(i)]; + else if (BDFDB.ArrayUtils.is(children[i])) { + for (let j in children[i]) if (check(children[i][j])) return [children[i], parseInt(j)]; + } + else if (children[i].props && children[i].props.children) { + if (BDFDB.ArrayUtils.is(children[i].props.children) && children[i].props.children.length) { + let [possibleChildren, possibleIndex] = BDFDB.ContextMenuUtils.findItem(children[i].props.children, config); + if (possibleIndex > -1) return [possibleChildren, possibleIndex]; + } + else if (check(children[i].props.children)) { + if (config.group) return [children, parseInt(i)]; + else { + children[i].props.children = [children[i].props.children]; + return [children[i].props.children, 0]; + } + } + else if (children[i].props.children.props && children[i].props.children.props.children) { + if (BDFDB.ArrayUtils.is(children[i].props.children.props.children) && children[i].props.children.props.children.length) { + let [possibleChildren, possibleIndex] = BDFDB.ContextMenuUtils.findItem(children[i].props.children.props.children, config); + if (possibleIndex > -1) return [possibleChildren, possibleIndex]; + } + else if (check(children[i].props.children.props.children)) { + if (config.group) return [children, parseInt(i)]; + else { + children[i].props.children.props.children = [children[i].props.children.props.children]; + return [children[i].props.children.props.children, 0]; + } + } + } + } + } + return [children, -1]; + } + return [null, -1]; + + function check (child) { + if (!child) return false; + let props = child.stateNode ? child.stateNode.props : child.props; + if (!props) return false; + return config.id && config.id.some(key => key == "devmode-copy-id" ? typeof props.id == "string" && props.id.startsWith(key) : props.id == key) || config.label && config.label.some(key => props.label == key); + } + }; + + BDFDB.StringUtils = {}; + BDFDB.StringUtils.upperCaseFirstChar = function (string) { + if (typeof string != "string") return ""; + else return "".concat(string.charAt(0).toUpperCase()).concat(string.slice(1)); + }; + BDFDB.StringUtils.charIsUpperCase = function (string) { + if (typeof string != "string") return false; + else string[0].toUpperCase() === string[0] && string[0].toLowerCase() !== string[0]; + }; + BDFDB.StringUtils.getAcronym = function (string) { + if (typeof string != "string") return ""; + return string.replace(/'s /g," ").replace(/\w+/g, n => n[0]).replace(/\s/g, ""); + }; + BDFDB.StringUtils.cssValueToNumber = function (string) { + if (typeof string != "string") return 0; + const value = parseInt(string, 10); + return isNaN(value) ? 0 : value; + }; + BDFDB.StringUtils.htmlEscape = function (string) { + let ele = document.createElement("div"); + ele.innerText = string; + return ele.innerHTML; + }; + BDFDB.StringUtils.regEscape = function (string) { + return typeof string == "string" && string.replace(/([\-\/\\\^\$\*\+\?\.\(\)\|\[\]\{\}])/g, "\\$1"); + }; + BDFDB.StringUtils.insertNRST = function (string) { + return typeof string == "string" && string.replace(/\\r/g, "\r").replace(/\\n/g, "\n").replace(/\\t/g, "\t").replace(/\\s/g, " "); + }; + BDFDB.StringUtils.equalCase = function (match, string) { + if (typeof match != "string" || typeof string != "string") return ""; + let first = match.charAt(0); + return first != first.toUpperCase() ? (string.charAt(0).toLowerCase() + string.slice(1)) : first != first.toLowerCase() ? (string.charAt(0).toUpperCase() + string.slice(1)) : string; + }; + BDFDB.StringUtils.extractSelection = function (original, selection) { + if (typeof original != "string") return ""; + if (typeof selection != "string") return original; + let s = [], f = [], wrong = 0, canceled = false, done = false; + for (let i of BDFDB.ArrayUtils.getAllIndexes(original, selection[0])) if (!done) { + while (i <= original.length && !done) { + let subSelection = selection.slice(s.filter(n => n != undefined).length); + if (!subSelection && s.length - 20 <= selection.length) done = true; + else for (let j in subSelection) if (!done && !canceled) { + if (original[i] == subSelection[j]) { + s[i] = subSelection[j]; + f[i] = subSelection[j]; + wrong = 0; + if (i == original.length) done = true; + } + else { + s[i] = null; + f[i] = original[i]; + wrong++; + if (wrong > 4) { + s = [], f = [], wrong = 0, canceled = true; + break; + } + } + break; + } + canceled = false; + i++; + } + } + if (s.filter(n => n).length) { + let reverseS = [].concat(s).reverse(), i = 0, j = 0; + for (let k in s) { + if (s[k] == null) i = parseInt(k) + 1; + else break; + } + for (let k in reverseS) { + if (reverseS[k] == null) j = parseInt(k) + 1; + else break; + } + return f.slice(i, f.length - j).join(""); + } + else return original; + }; + + BDFDB.SlateUtils = {}; + BDFDB.SlateUtils.isRichValue = function (richValue) { + return richValue && typeof richValue == "object" && BDFDB.SlateUtils.toRichValue("").constructor.prototype.isPrototypeOf(richValue); + }; + BDFDB.SlateUtils.toTextValue = function (richValue) { + return BDFDB.SlateUtils.isRichValue(richValue) ? Internal.LibraryModules.SlateTextUtils.toTextValue(richValue) : ""; + }; + BDFDB.SlateUtils.toRichValue = function (string) { + return typeof string == "string" ? Internal.LibraryModules.SlateRichUtils.toRichValue(string) : null; + }; + + BDFDB.NumberUtils = {}; + BDFDB.NumberUtils.formatBytes = function (bytes, sigDigits) { + bytes = parseInt(bytes); + if (isNaN(bytes) || bytes < 0) return "0 Bytes"; + if (bytes == 1) return "1 Byte"; + let size = Math.floor(Math.log(bytes) / Math.log(1024)); + return parseFloat((bytes / Math.pow(1024, size)).toFixed(sigDigits < 1 ? 0 : sigDigits > 20 ? 20 : sigDigits || 2)) + " " + ["Bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"][size]; + }; + BDFDB.NumberUtils.mapRange = function (from, to, value) { + if (parseFloat(value) < parseFloat(from[0])) return parseFloat(to[0]); + else if (parseFloat(value) > parseFloat(from[1])) return parseFloat(to[1]); + else return parseFloat(to[0]) + (parseFloat(value) - parseFloat(from[0])) * (parseFloat(to[1]) - parseFloat(to[0])) / (parseFloat(from[1]) - parseFloat(from[0])); + }; + BDFDB.NumberUtils.generateId = function (array) { + array = BDFDB.ArrayUtils.is(array) ? array : []; + let id = Math.floor(Math.random() * 10000000000000000); + if (array.includes(id)) return BDFDB.NumberUtils.generateId(array); + else { + array.push(id); + return id; + } + }; + BDFDB.NumberUtils.compareVersions = function (newV, oldV) { + if (!newV || !oldV) return true; + newV = newV.toString().replace(/["'`]/g, "").split(/,|\./g).map(n => parseInt(n)).filter(n => (n || n == 0) && !isNaN(n)); + oldV = oldV.toString().replace(/["'`]/g, "").split(/,|\./g).map(n => parseInt(n)).filter(n => (n || n == 0) && !isNaN(n)); + let length = Math.max(newV.length, oldV.length); + if (!length) return true; + if (newV.length > oldV.length) { + let tempArray = new Array(newV.length - oldV.length); + for (let i = 0; i < tempArray.length; i++) tempArray[i] = 0; + oldV = tempArray.concat(oldV); + } + else if (newV.length < oldV.length) { + let tempArray = new Array(oldV.length - newV.length); + for (let i = 0; i < tempArray.length; i++) tempArray[i] = 0; + newV = tempArray.concat(newV); + } + for (let i = 0; i < length; i++) for (let iOutdated = false, j = 0; j <= i; j++) { + if (j == i && newV[j] < oldV[j]) return false; + if (j < i) iOutdated = newV[j] == oldV[j]; + if ((j == 0 || iOutdated) && j == i && newV[j] > oldV[j]) return true; + } + return false; + }; + BDFDB.NumberUtils.getVersionDifference = function (newV, oldV) { + if (!newV || !oldV) return false; + newV = newV.toString().replace(/["'`]/g, "").split(/,|\./g).map(n => parseInt(n)).filter(n => (n || n == 0) && !isNaN(n)); + oldV = oldV.toString().replace(/["'`]/g, "").split(/,|\./g).map(n => parseInt(n)).filter(n => (n || n == 0) && !isNaN(n)); + let length = Math.max(newV.length, oldV.length); + if (!length) return false; + if (newV.length > oldV.length) { + let tempArray = new Array(newV.length - oldV.length); + for (let i = 0; i < tempArray.length; i++) tempArray[i] = 0; + oldV = tempArray.concat(oldV); + } + else if (newV.length < oldV.length) { + let tempArray = new Array(oldV.length - newV.length); + for (let i = 0; i < tempArray.length; i++) tempArray[i] = 0; + newV = tempArray.concat(newV); + } + let oldValue = 0, newValue = 0; + for (let i in oldV.reverse()) oldValue += (oldV[i] * (10 ** i)); + for (let i in newV.reverse()) newValue += (newV[i] * (10 ** i)); + return (newValue - oldValue) / (10 ** (length-1)); + }; + + BDFDB.DiscordUtils = {}; + BDFDB.DiscordUtils.getSetting = function (category, key) { + if (!category || !key) return; + return BDFDB.LibraryStores.UserSettingsProtoStore && BDFDB.LibraryStores.UserSettingsProtoStore.settings[category] && BDFDB.LibraryStores.UserSettingsProtoStore.settings[category][key] && BDFDB.LibraryStores.UserSettingsProtoStore.settings[category][key].value; + }; + BDFDB.DiscordUtils.setSetting = function (category, key, value) { + if (!category || !key) return; + let store = BDFDB.DiscordUtils.getSettingsStore(); + if (store) store.updateAsync(category, settings => { + if (!settings) return; + if (!settings[key]) settings[key] = {}; + if (BDFDB.ObjectUtils.is(value)) for (let k in value) settings[key][k] = value[k]; + else settings[key].value = value; + }, Internal.DiscordConstants.UserSettingsActionTypes.INFREQUENT_USER_ACTION); + }; + BDFDB.DiscordUtils.getSettingsStore = function () { + return BDFDB.LibraryModules.UserSettingsProtoUtils && (Object.entries(BDFDB.LibraryModules.UserSettingsProtoUtils).find(n => n && n[1] && n[1].updateAsync && n[1].ProtoClass && n[1].ProtoClass.typeName && n[1].ProtoClass.typeName.endsWith(".PreloadedUserSettings")) || [])[1]; + }; + BDFDB.DiscordUtils.openLink = function (url, config = {}) { + if ((config.inBuilt || config.inBuilt === undefined && Internal.settings.general.useChromium) && Internal.LibraryRequires.electron && Internal.LibraryRequires.electron.remote) { + let browserWindow = new Internal.LibraryRequires.electron.remote.BrowserWindow({ + frame: true, + resizeable: true, + show: true, + darkTheme: BDFDB.DiscordUtils.getTheme() == BDFDB.disCN.themedark, + webPreferences: { + nodeIntegration: false, + nodeIntegrationInWorker: false + } + }); + browserWindow.setMenu(null); + browserWindow.loadURL(url); + if (config.minimized) browserWindow.minimize(null); + } + else window.open(url, "_blank"); + }; + window.DiscordNative && window.DiscordNative.app && window.DiscordNative.app.getPath("appData").then(path => {BDFDB.DiscordUtils.getFolder.base = path;}); + BDFDB.DiscordUtils.isPlaformEmbedded = function () { + return Internal.LibraryModules.PlatformUtils && (Object.entries(Internal.LibraryModules.PlatformUtils).find(n => typeof n[1] == "boolean") || [])[1] || false; + }; + BDFDB.DiscordUtils.getFolder = function () { + if (!BDFDB.DiscordUtils.getFolder.base) return ""; + else if (BDFDB.DiscordUtils.getFolder.folder) return BDFDB.DiscordUtils.getFolder.folder; + else { + let folder; + try { + let build = BDFDB.DiscordUtils.getBuild(); + build = "discord" + (build == "stable" ? "" : build); + folder = Internal.LibraryRequires.path.resolve(BDFDB.DiscordUtils.getFolder.base, build, BDFDB.DiscordUtils.getVersion()); + } + catch (err) {folder = BDFDB.DiscordUtils.getFolder.base;} + return BDFDB.DiscordUtils.getFolder.folder = folder; + } + }; + BDFDB.DiscordUtils.getLanguage = function () { + return Internal.LibraryModules.LanguageStore && (Internal.LibraryModules.LanguageStore.chosenLocale || Internal.LibraryModules.LanguageStore._chosenLocale) || document.querySelector("html[lang]").getAttribute("lang"); + }; + BDFDB.DiscordUtils.getBuild = function () { + if (BDFDB.DiscordUtils.getBuild.build) return BDFDB.DiscordUtils.getBuild.build; + else { + let build; + try {build = window.DiscordNative.app.getReleaseChannel();} + catch (err) { + let version = BDFDB.DiscordUtils.getVersion(); + if (version) { + version = version.split("."); + if (version.length == 3 && !isNaN(version = parseInt(version[2]))) build = version > 300 ? "stable" : version > 200 ? "canary" : "ptb"; + else build = "stable"; + } + else build = "stable"; + } + return BDFDB.DiscordUtils.getBuild.build = build; + } + }; + BDFDB.DiscordUtils.getVersion = function () { + if (BDFDB.DiscordUtils.getVersion.version) return BDFDB.DiscordUtils.getVersion.version; + else { + let version; + try {version = window.DiscordNative.app.getVersion();} + catch (err) {version = "999.999.9999";} + return BDFDB.DiscordUtils.getVersion.version = version; + } + }; + BDFDB.DiscordUtils.getTheme = function () { + return BDFDB.LibraryStores.ThemeStore.theme != "dark" ? BDFDB.disCN.themelight : BDFDB.disCN.themedark; + }; + BDFDB.DiscordUtils.getZoomFactor = function () { + let aRects = BDFDB.DOMUtils.getRects(document.querySelector(BDFDB.dotCN.appmount)); + let widthZoom = Math.round(100 * window.outerWidth / aRects.width); + let heightZoom = Math.round(100 * window.outerHeight / aRects.height); + return widthZoom < heightZoom ? widthZoom : heightZoom; + }; + BDFDB.DiscordUtils.getFontScale = function () { + return parseInt(document.firstElementChild.style.fontSize.replace("%", "")); + }; + BDFDB.DiscordUtils.shake = function () { + BDFDB.ReactUtils.findOwner(document.querySelector(BDFDB.dotCN.appcontainer), {name: "Shakeable", unlimited: true, up: true}).shake(); + }; + BDFDB.DiscordUtils.rerenderAll = function (instant) { + BDFDB.TimeUtils.clear(BDFDB.DiscordUtils.rerenderAll.timeout); + BDFDB.DiscordUtils.rerenderAll.timeout = BDFDB.TimeUtils.timeout(_ => { + let LayersProviderIns = BDFDB.ReactUtils.findOwner(document.querySelector(BDFDB.dotCN.layers), {name: "LayersProvider", unlimited: true, up: true}); + let LayersProviderType = LayersProviderIns && BDFDB.ObjectUtils.get(LayersProviderIns, `${BDFDB.ReactUtils.instanceKey}.type`); + if (!LayersProviderType) return; + let parentSelector = "", notices = document.querySelector("#bd-notices"); + if (notices) { + let parentClasses = [] + for (let i = 0, parent = notices.parentElement; i < 3; i++, parent = parent.parentElement) parentClasses.push(parent.className); + parentSelector = parentClasses.reverse().map(n => !n ? "*" : `.${n.split(" ").join(".")}`).join(" > "); + } + BDFDB.PatchUtils.patch({name: "BDFDB DiscordUtils"}, LayersProviderType.prototype, "render", {after: e => { + e.returnValue = BDFDB.ReactUtils.createElement(LayersProviderType, LayersProviderIns.props); + BDFDB.ReactUtils.forceUpdate(LayersProviderIns); + if (parentSelector) BDFDB.TimeUtils.timeout(_ => { + if (!document.contains(notices)) { + let parent = document.querySelector(parentSelector) || document.querySelector(BDFDB.dotCN.app).parentElement; + if (parent) parent.insertBefore(notices, parent.firstElementChild); + } + }, 1000); + }}, {once: true}); + BDFDB.ReactUtils.forceUpdate(LayersProviderIns); + }, instant ? 0 : 1000); + }; + + const DiscordClassModules = Object.assign({}, InternalData.CustomClassModules); + Internal.DiscordClassModules = new Proxy(DiscordClassModules, { + get: function (_, item) { + if (DiscordClassModules[item]) return DiscordClassModules[item]; + if (!InternalData.DiscordClassModules[item]) return; + DiscordClassModules[item] = BDFDB.ModuleUtils.findStringObject(InternalData.DiscordClassModules[item].props, Object.assign({}, InternalData.DiscordClassModules[item])); + return DiscordClassModules[item] ? DiscordClassModules[item] : undefined; + } + }); + BDFDB.DiscordClassModules = Internal.DiscordClassModules; + for (let item in InternalData.DiscordClassModules) if (!DiscordClassModules[item]) DiscordClassModules[item] = undefined; + + const DiscordClasses = Object.assign({}, InternalData.DiscordClasses); + BDFDB.DiscordClasses = Object.assign({}, DiscordClasses); + Internal.getDiscordClass = function (item, selector) { + let className, fallbackClassName; + className = fallbackClassName = Internal.DiscordClassModules.BDFDB.BDFDBundefined + "_" + Internal.generateClassId(); + if (DiscordClasses[item] === undefined) { + BDFDB.LogUtils.warn([item, "not found in DiscordClasses"]); + return className; + } + else if (!BDFDB.ArrayUtils.is(DiscordClasses[item]) || DiscordClasses[item].length != 2) { + BDFDB.LogUtils.warn([item, "is not an Array of Length 2 in DiscordClasses"]); + return className; + } + else if (Internal.DiscordClassModules[DiscordClasses[item][0]] === undefined) { + BDFDB.LogUtils.warn([DiscordClasses[item][0], "not found in DiscordClassModules"]); + return className; + } + else if ([DiscordClasses[item][1]].flat().every(prop => Internal.DiscordClassModules[DiscordClasses[item][0]][prop] === undefined)) { + BDFDB.LogUtils.warn([DiscordClasses[item][1], "not found in", DiscordClasses[item][0], "in DiscordClassModules"]); + return className; + } + else { + for (let prop of [DiscordClasses[item][1]].flat()) { + className = Internal.DiscordClassModules[DiscordClasses[item][0]][prop]; + if (className) break; + else className = fallbackClassName; + } + if (selector) { + className = className.split(" ").filter(n => n.indexOf("da-") != 0).join(selector ? "." : " "); + className = className || fallbackClassName; + } + return BDFDB.ArrayUtils.removeCopies(className.split(" ")).join(" ") || fallbackClassName; + } + }; + const generationChars = "0123456789ABCDEFGHIJKMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_-".split(""); + Internal.generateClassId = function () { + let id = ""; + while (id.length < 6) id += generationChars[Math.floor(Math.random() * generationChars.length)]; + return id; + }; + BDFDB.disCN = new Proxy({}, { + get: function (list, item) { + return Internal.getDiscordClass(item, false).replace("#", ""); + } + }); + BDFDB.disCNS = new Proxy({}, { + get: function (list, item) { + return Internal.getDiscordClass(item, false).replace("#", "") + " "; + } + }); + BDFDB.disCNC = new Proxy({}, { + get: function (list, item) { + return Internal.getDiscordClass(item, false).replace("#", "") + ","; + } + }); + BDFDB.dotCN = new Proxy({}, { + get: function (list, item) { + let className = Internal.getDiscordClass(item, true); + return (className.indexOf("#") == 0 ? "" : ".") + className; + } + }); + BDFDB.dotCNS = new Proxy({}, { + get: function (list, item) { + let className = Internal.getDiscordClass(item, true); + return (className.indexOf("#") == 0 ? "" : ".") + className + " "; + } + }); + BDFDB.dotCNC = new Proxy({}, { + get: function (list, item) { + let className = Internal.getDiscordClass(item, true); + return (className.indexOf("#") == 0 ? "" : ".") + className + ","; + } + }); + BDFDB.notCN = new Proxy({}, { + get: function (list, item) { + return `:not(.${Internal.getDiscordClass(item, true).split(".")[0]})`; + } + }); + BDFDB.notCNS = new Proxy({}, { + get: function (list, item) { + return `:not(.${Internal.getDiscordClass(item, true).split(".")[0]}) `; + } + }); + BDFDB.notCNC = new Proxy({}, { + get: function (list, item) { + return `:not(.${Internal.getDiscordClass(item, true).split(".")[0]}),`; + } + }); + + const LanguageStrings = Internal.LibraryModules.LanguageStore && Internal.LibraryModules.LanguageStore._proxyContext ? Object.assign({}, Internal.LibraryModules.LanguageStore._proxyContext.defaultMessages) : Internal.LibraryModules.LanguageStore; + const LanguageStringsObj = Internal.LibraryModules.LanguageStore.Messages || Internal.LibraryModules.LanguageStore; + const LibraryStrings = Object.assign({}, InternalData.LibraryStrings); + BDFDB.LanguageUtils = {}; + BDFDB.LanguageUtils.languages = Object.assign({}, InternalData.Languages); + BDFDB.LanguageUtils.getLanguage = function () { + let lang = BDFDB.DiscordUtils.getLanguage() || "en"; + if (lang == "en-GB" || lang == "en-US") lang = "en"; + let langIds = lang.split("-"); + let langId = langIds[0]; + let langId2 = langIds[1] || ""; + lang = langId2 && langId.toUpperCase() !== langId2.toUpperCase() ? langId + "-" + langId2 : langId; + return BDFDB.LanguageUtils.languages[lang] || BDFDB.LanguageUtils.languages[langId] || BDFDB.LanguageUtils.languages.en; + }; + BDFDB.LanguageUtils.getName = function (language) { + if (!language || typeof language.name != "string") return ""; + if (language.name.startsWith("Discord")) return language.name.slice(0, -1) + (language.ownlang && (BDFDB.LanguageUtils.languages[language.id] || {}).name != language.ownlang ? ` / ${language.ownlang}` : "") + ")"; + else return language.name + (language.ownlang && language.name != language.ownlang ? ` / ${language.ownlang}` : ""); + }; + BDFDB.LanguageUtils.LanguageStrings = new Proxy(LanguageStrings, { + get: function (list, item) { + let stringObj = LanguageStringsObj[item]; + if (!stringObj) BDFDB.LogUtils.warn([item, "not found in BDFDB.LanguageUtils.LanguageStrings"]); + else { + if (stringObj && typeof stringObj == "object" && typeof stringObj.format == "function") return BDFDB.LanguageUtils.LanguageStringsFormat(item); + else return stringObj; + } + return ""; + } + }); + BDFDB.LanguageUtils.LanguageStringsCheck = new Proxy(LanguageStrings, { + get: function (list, item) { + return !!LanguageStringsObj[item]; + } + }); + let parseLanguageStringObj = obj => { + let string = ""; + if (typeof obj == "string") string += obj; + else if (BDFDB.ObjectUtils.is(obj)) { + if (obj.content) string += parseLanguageStringObj(obj.content); + else if (obj.children) string += parseLanguageStringObj(obj.children); + else if (obj.props) string += parseLanguageStringObj(obj.props); + } + else if (BDFDB.ArrayUtils.is(obj)) for (let ele of obj) string += parseLanguageStringObj(ele); + return string; + }; + BDFDB.LanguageUtils.LanguageStringsFormat = function (item, ...values) { + if (item) { + let stringObj = LanguageStringsObj[item]; + if (stringObj && typeof stringObj == "object" && typeof stringObj.format == "function") { + let i = 0, returnvalue, formatVars = {}; + while (!returnvalue && i < 10) { + i++; + try {returnvalue = stringObj.format(formatVars, false);} + catch (err) { + returnvalue = null; + let value = values.shift(); + formatVars[err.toString().split("for: ")[1]] = value != null ? (value === 0 ? "0" : value) : "undefined"; + if (stringObj.intMessage) { + try {for (let hook of stringObj.intMessage.format(formatVars).match(/\([^\(\)]+\)/gi)) formatVars[hook.replace(/[\(\)]/g, "")] = n => n;} + catch (err2) {} + } + if (stringObj.intlMessage) { + try {for (let hook of stringObj.intlMessage.format(formatVars).match(/\([^\(\)]+\)/gi)) formatVars[hook.replace(/[\(\)]/g, "")] = n => n;} + catch (err2) {} + } + } + } + if (returnvalue) return parseLanguageStringObj(returnvalue); + else { + BDFDB.LogUtils.warn([item, "failed to format string in BDFDB.LanguageUtils.LanguageStrings"]); + return ""; + } + } + else return BDFDB.LanguageUtils.LanguageStrings[item]; + } + else BDFDB.LogUtils.warn([item, "enter a valid key to format the string in BDFDB.LanguageUtils.LanguageStrings"]); + return ""; + }; + BDFDB.LanguageUtils.LibraryStrings = new Proxy(LibraryStrings.default || {}, { + get: function (list, item) { + let languageId = BDFDB.LanguageUtils.getLanguage().id; + if (LibraryStrings[languageId] && LibraryStrings[languageId][item]) return LibraryStrings[languageId][item]; + else if (LibraryStrings.default[item]) return LibraryStrings.default[item]; + else BDFDB.LogUtils.warn([item, "not found in BDFDB.LanguageUtils.LibraryStrings"]); + return ""; + } + }); + BDFDB.LanguageUtils.LibraryStringsCheck = new Proxy(LanguageStrings, { + get: function (list, item) { + return !!LibraryStrings.default[item]; + } + }); + BDFDB.LanguageUtils.LibraryStringsFormat = function (item, ...values) { + if (item) { + let languageId = BDFDB.LanguageUtils.getLanguage().id, string = null; + if (LibraryStrings[languageId] && LibraryStrings[languageId][item]) string = LibraryStrings[languageId][item]; + else if (LibraryStrings.default[item]) string = LibraryStrings.default[item]; + if (string) { + for (let i = 0; i < values.length; i++) if (typeof values[i] == "string" || typeof values[i] == "number") string = string.replace(new RegExp(`{{var${i}}}`, "g"), values[i]); + return string; + } + else BDFDB.LogUtils.warn([item, "not found in BDFDB.LanguageUtils.LibraryStrings"]); + } + else BDFDB.LogUtils.warn([item, "enter a valid key to format the string in BDFDB.LanguageUtils.LibraryStrings"]); + return ""; + }; + BDFDB.TimeUtils.interval(interval => { + if (BDFDB.DiscordUtils.getLanguage()) { + BDFDB.TimeUtils.clear(interval); + let language = BDFDB.LanguageUtils.getLanguage(); + if (language) BDFDB.LanguageUtils.languages.$discord = Object.assign({}, language, {name: `Discord (${language.name})`}); + } + }, 100); + for (let key in BDFDB.LanguageUtils.languages) try { + if (new Date(0).toLocaleString(key, {second: 'numeric'}) != "0") { + BDFDB.LanguageUtils.languages[key].numberMap = {}; + for (let i = 0; i < 10; i++) BDFDB.LanguageUtils.languages[key].numberMap[i] = new Date(i*1000).toLocaleString(key, {second: 'numeric'}); + } + } + catch (err) {} + + const reactInitialized = Internal.LibraryModules.React && Internal.LibraryModules.React.Component; + Internal.setDefaultProps = function (component, defaultProps) { + if (BDFDB.ObjectUtils.is(component)) component.defaultProps = Object.assign({}, component.defaultProps, defaultProps); + }; + let openedItem; + Internal.MenuItem = reactInitialized && class BDFDB_MenuItem extends Internal.LibraryModules.React.Component { + constructor(props) { + super(props); + this.state = {hovered: false}; + } + componentWillUnmount() { + if (openedItem == this.props.id) openedItem = null; + } + render() { + let color = (typeof this.props.color == "string" ? this.props.color : Internal.DiscordConstants.MenuItemColors.DEFAULT).toLowerCase(); + let isCustomColor = false; + if (color) { + if (InternalData.DiscordClasses[`menucolor${color}`]) color = color; + else if (BDFDB.ColorUtils.getType(color)) { + isCustomColor = true; + color = BDFDB.ColorUtils.convert(color, "RGBA"); + } + else color = (Internal.DiscordConstants.MenuItemColors.DEFAULT || "").toLowerCase(); + } + let renderPopout, onClose, hasPopout = BDFDB.ObjectUtils.is(this.props.popoutProps); + if (hasPopout) { + renderPopout = instance => { + openedItem = this.props.id; + return typeof this.props.popoutProps.renderPopout == "function" && this.props.popoutProps.renderPopout(instance); + }; + onClose = instance => { + openedItem = null; + typeof this.props.popoutProps.onClose == "function" && this.props.popoutProps.onClose(instance); + }; + } + let focused = !openedItem ? this.props.isFocused : openedItem == this.props.id; + let themeDark = BDFDB.DiscordUtils.getTheme() == BDFDB.disCN.themedark; + let item = BDFDB.ReactUtils.createElement(Internal.LibraryComponents.Clickable, Object.assign({ + className: BDFDB.DOMUtils.formatClassName(BDFDB.disCN.menuitem, (this.props.label || this.props.subtext) && BDFDB.disCN.menulabelcontainer, color && (isCustomColor ? BDFDB.disCN.menucolorcustom : BDFDB.disCN[`menucolor${color}`]), this.props.disabled && BDFDB.disCN.menudisabled, focused && BDFDB.disCN.menufocused), + style: { + color: isCustomColor ? ((focused || this.state.hovered) ? (BDFDB.ColorUtils.isBright(color) ? "#000000" : "#ffffff") : color) : (this.state.hovered ? "#ffffff" : null), + background: isCustomColor && (focused || this.state.hovered) && color + }, + onClick: this.props.disabled ? null : e => { + if (!this.props.action) return false; + !this.props.persisting && !hasPopout && this.props.onClose && this.props.onClose(); + this.props.action(e, this); + }, + onMouseEnter: this.props.disabled ? null : e => { + if (typeof this.props.onMouseEnter == "function") this.props.onMouseEnter(e, this); + this.setState({hovered: true}); + }, + onMouseLeave: this.props.disabled ? null : e => { + if (typeof this.props.onMouseLeave == "function") this.props.onMouseLeave(e, this); + this.setState({hovered: false}); + }, + "aria-disabled": this.props.disabled, + children: [ + this.props.icon && this.props.showIconFirst && BDFDB.ReactUtils.createElement("div", { + className: BDFDB.disCN.menuiconcontainerleft, + children: BDFDB.ReactUtils.createElement(this.props.icon, { + className: BDFDB.disCN.menuicon + }) + }), + typeof this.props.render == "function" ? this.props.render(this) : this.props.render, + (this.props.label || this.props.subtext) && BDFDB.ReactUtils.createElement("div", { + className: BDFDB.disCN.menulabel, + children: [ + typeof this.props.label == "function" ? this.props.label(this) : this.props.label, + this.props.subtext && BDFDB.ReactUtils.createElement("div", { + className: BDFDB.disCN.menusubtext, + children: typeof this.props.subtext == "function" ? this.props.subtext(this) : this.props.subtext + }) + ].filter(n => n) + }), + this.props.hint && BDFDB.ReactUtils.createElement("div", { + className: BDFDB.disCN.menuhintcontainer, + children: typeof this.props.hint == "function" ? this.props.hint(this) : this.props.hint + }), + this.props.icon && !this.props.showIconFirst && BDFDB.ReactUtils.createElement("div", { + className: BDFDB.disCN.menuiconcontainer, + children: BDFDB.ReactUtils.createElement(this.props.icon, { + className: BDFDB.disCN.menuicon + }) + }), + this.props.input && BDFDB.ReactUtils.createElement("div", { + className: BDFDB.disCN.menuiconcontainer, + children: this.props.input + }), + this.props.imageUrl && BDFDB.ReactUtils.createElement("div", { + className: BDFDB.disCN.menuimagecontainer, + children: BDFDB.ReactUtils.createElement("img", { + className: BDFDB.disCN.menuimage, + src: typeof this.props.imageUrl == "function" ? this.props.imageUrl(this) : this.props.imageUrl, + alt: "" + }) + }) + ].filter(n => n) + }, this.props.menuItemProps, {isFocused: focused})); + return hasPopout ? BDFDB.ReactUtils.createElement(Internal.LibraryComponents.PopoutContainer, Object.assign({}, this.props.popoutProps, { + children: item, + renderPopout: renderPopout, + onClose: onClose + })) : item; + } + }; + Internal.CustomMenuItemWrapper = reactInitialized && class BDFDB_CustomMenuItemWrapper extends Internal.LibraryModules.React.Component { + constructor(props) { + super(props); + this.state = {hovered: false}; + } + render() { + let isItem = this.props.children == Internal.MenuItem; + let item = BDFDB.ReactUtils.createElement(this.props.children, Object.assign({}, this.props.childProps, { + onMouseEnter: isItem ? e => { + if (this.props.childProps && typeof this.props.childProps.onMouseEnter == "function") this.props.childProps.onMouseEnter(e, this); + this.setState({hovered: true}); + } : this.props.childProps && this.props.childProps.onMouseEnter, + onMouseLeave: isItem ? e => { + if (this.props.childProps && typeof this.props.childProps.onMouseLeave == "function") this.props.childProps.onMouseLeave(e, this); + this.setState({hovered: false}); + } : this.props.childProps && this.props.childProps.onMouseLeave, + isFocused: this.state.hovered && !this.props.disabled + })); + return isItem ? item : BDFDB.ReactUtils.createElement(Internal.LibraryComponents.Clickable, { + onMouseEnter: e => this.setState({hovered: true}), + onMouseLeave: e => this.setState({hovered: false}), + children: item + }); + } + }; + Internal.ErrorBoundary = reactInitialized && class BDFDB_ErrorBoundary extends Internal.LibraryModules.React.PureComponent { + constructor(props) { + super(props); + this.state = {hasError: false}; + } + static getDerivedStateFromError(err) { + return {hasError: true}; + } + componentDidCatch(err, info) { + BDFDB.LogUtils.error(["Could not create React Element!", err]); + } + render() { + if (this.state.hasError) return Internal.LibraryModules.React.createElement("span", { + style: { + background: Internal.DiscordConstants.Colors.PRIMARY, + borderRadius: 5, + color: "var(--status-danger)", + fontSize: 12, + fontWeight: 600, + padding: 6, + textAlign: "center", + verticalAlign: "center" + }, + children: "React Component Error" + }); + return this.props.children; + } + }; + + Internal.NativeSubComponents = new Proxy(NativeSubComponents, { + get: function (_, item) { + if (NativeSubComponents[item]) return NativeSubComponents[item]; + if (!InternalData.NativeSubComponents[item]) return "div"; + + Internal.findModuleViaData(NativeSubComponents, InternalData.NativeSubComponents, item); + + return NativeSubComponents[item] ? NativeSubComponents[item] : "div"; + } + }); + + CustomComponents.AutoFocusCatcher = reactInitialized && class BDFDB_AutoFocusCatcher extends Internal.LibraryModules.React.Component { + render() { + const style = {padding: 0, margin: 0, border: "none", width: 0, maxWidth: 0, height: 0, maxHeight: 0, visibility: "hidden"}; + return BDFDB.ReactUtils.forceStyle(BDFDB.ReactUtils.createElement("input", {style}), Object.keys(style)); + } + }; + + CustomComponents.BadgeAnimationContainer = reactInitialized && class BDFDB_BadgeAnimationContainer extends Internal.LibraryModules.React.Component { + componentDidMount() {BDFDB.ReactUtils.forceUpdate(this);} + componentWillAppear(e) {if (typeof e == "function") e();} + componentWillEnter(e) {if (typeof e == "function") e();} + componentWillLeave(e) {if (typeof e == "function") this.timeoutId = setTimeout(e, 300);} + componentWillUnmount() {BDFDB.TimeUtils.clear(this.timeoutId)} + render() { + return BDFDB.ReactUtils.createElement(Internal.LibraryComponents.Animations.animated.div, { + className: this.props.className, + style: this.props.animatedStyle, + children: this.props.children + }); + } + }; + + CustomComponents.Badges = {}; + CustomComponents.Badges.getBadgePaddingForValue = function (count) { + switch (count) { + case 1: + case 4: + case 6: + return 1; + default: + return 0; + } + }; + CustomComponents.Badges.IconBadge = reactInitialized && class BDFDB_IconBadge extends Internal.LibraryModules.React.Component { + render() { + return BDFDB.ReactUtils.createElement("div", { + className: BDFDB.DOMUtils.formatClassName(this.props.className, BDFDB.disCN.badgeiconbadge, this.props.shape && Internal.LibraryComponents.Badges.BadgeShapes[this.props.shape] || Internal.LibraryComponents.Badges.BadgeShapes.ROUND), + style: Object.assign({ + backgroundColor: this.props.disableColor ? null : (this.props.color || "var(--status-danger)") + }, this.props.style), + children: BDFDB.ReactUtils.createElement(Internal.LibraryComponents.SvgIcon, { + className: BDFDB.disCN.badgeicon, + name: this.props.icon + }) + }); + } + }; + CustomComponents.Badges.NumberBadge = reactInitialized && class BDFDB_NumberBadge extends Internal.LibraryModules.React.Component { + handleClick(e) {if (typeof this.props.onClick == "function") this.props.onClick(e, this);} + handleContextMenu(e) {if (typeof this.props.onContextMenu == "function") this.props.onContextMenu(e, this);} + handleMouseEnter(e) {if (typeof this.props.onMouseEnter == "function") this.props.onMouseEnter(e, this);} + handleMouseLeave(e) {if (typeof this.props.onMouseLeave == "function") this.props.onMouseLeave(e, this);} + getBadgeWidthForValue(e) {return e < 10 ? 16 : e < 100 ? 22 : 30} + getBadgeCountString(e) {return e < 1e3 ? "" + e : Math.min(Math.floor(e/1e3), 9) + "k+"} + render() { + return BDFDB.ReactUtils.createElement("div", { + className: BDFDB.DOMUtils.formatClassName(this.props.className, BDFDB.disCN.badgenumberbadge, this.props.shape && Internal.LibraryComponents.Badges.BadgeShapes[this.props.shape] || Internal.LibraryComponents.Badges.BadgeShapes.ROUND), + style: Object.assign({ + backgroundColor: !this.props.disableColor && (this.props.color || "var(--status-danger)"), + width: this.getBadgeWidthForValue(this.props.count) + }, this.props.style), + onClick: this.handleClick.bind(this), + onContextMenu: this.handleContextMenu.bind(this), + onMouseEnter: this.handleMouseEnter.bind(this), + onMouseLeave: this.handleMouseLeave.bind(this), + children: this.getBadgeCountString(this.props.count) + }); + } + }; + + CustomComponents.BotTag = reactInitialized && class BDFDB_BotTag extends Internal.LibraryModules.React.Component { + handleClick(e) {if (typeof this.props.onClick == "function") this.props.onClick(e, this);} + handleContextMenu(e) {if (typeof this.props.onContextMenu == "function") this.props.onContextMenu(e, this);} + handleMouseEnter(e) {if (typeof this.props.onMouseEnter == "function") this.props.onMouseEnter(e, this);} + handleMouseLeave(e) {if (typeof this.props.onMouseLeave == "function") this.props.onMouseLeave(e, this);} + render() { + return BDFDB.ReactUtils.createElement("span", { + className: BDFDB.DOMUtils.formatClassName(this.props.className, this.props.invertColor ? BDFDB.disCN.bottaginvert : BDFDB.disCN.bottagregular, this.props.useRemSizes ? BDFDB.disCN.bottagrem : BDFDB.disCN.bottagpx), + style: this.props.style, + onClick: this.handleClick.bind(this), + onContextMenu: this.handleContextMenu.bind(this), + onMouseEnter: this.handleMouseEnter.bind(this), + onMouseLeave: this.handleMouseLeave.bind(this), + children: BDFDB.ReactUtils.createElement("span", { + className: BDFDB.disCN.bottagtext, + children: this.props.tag || BDFDB.LanguageUtils.LanguageStrings.BOT_TAG_BOT + }) + }); + } + }; + + CustomComponents.Button = reactInitialized && class BDFDB_Button extends Internal.LibraryModules.React.Component { + handleClick(e) {if (typeof this.props.onClick == "function") this.props.onClick(e, this);} + handleContextMenu(e) {if (typeof this.props.onContextMenu == "function") this.props.onContextMenu(e, this);} + handleMouseDown(e) {if (typeof this.props.onMouseDown == "function") this.props.onMouseDown(e, this);} + handleMouseUp(e) {if (typeof this.props.onMouseUp == "function") this.props.onMouseUp(e, this);} + handleMouseEnter(e) {if (typeof this.props.onMouseEnter == "function") this.props.onMouseEnter(e, this);} + handleMouseLeave(e) {if (typeof this.props.onMouseLeave == "function") this.props.onMouseLeave(e, this);} + render() { + let processingAndListening = (this.props.disabled || this.props.submitting) && (null != this.props.onMouseEnter || null != this.props.onMouseLeave); + let props = BDFDB.ObjectUtils.exclude(this.props, "look", "color", "hover", "size", "fullWidth", "grow", "disabled", "submitting", "type", "style", "wrapperClassName", "className", "innerClassName", "onClick", "onContextMenu", "onMouseDown", "onMouseUp", "onMouseEnter", "onMouseLeave", "children", "rel"); + let button = BDFDB.ReactUtils.createElement("button", Object.assign({}, !this.props.disabled && !this.props.submitting && props, { + className: BDFDB.DOMUtils.formatClassName(this.props.className, BDFDB.disCN.button, this.props.look != null ? this.props.look : Internal.LibraryComponents.Button.Looks.FILLED, this.props.color != null ? this.props.color : Internal.LibraryComponents.Button.Colors.BRAND, this.props.hover, this.props.size != null ? this.props.size : Internal.LibraryComponents.Button.Sizes.MEDIUM, processingAndListening && this.props.wrapperClassName, this.props.fullWidth && BDFDB.disCN.buttonfullwidth, (this.props.grow === undefined || this.props.grow) && BDFDB.disCN.buttongrow, this.props.hover && this.props.hover !== Internal.LibraryComponents.Button.Hovers.DEFAULT && BDFDB.disCN.buttonhashover, this.props.submitting && BDFDB.disCN.buttonsubmitting), + onClick: (this.props.disabled || this.props.submitting) ? e => {return e.preventDefault();} : this.handleClick.bind(this), + onContextMenu: (this.props.disabled || this.props.submitting) ? e => {return e.preventDefault();} : this.handleContextMenu.bind(this), + onMouseUp: !this.props.disabled && this.handleMouseDown.bind(this), + onMouseDown: !this.props.disabled && this.handleMouseUp.bind(this), + onMouseEnter: this.handleMouseEnter.bind(this), + onMouseLeave: this.handleMouseLeave.bind(this), + type: !this.props.type ? "button" : this.props.type, + disabled: this.props.disabled, + style: this.props.style, + rel: this.props.rel, + children: [ + this.props.submitting && !this.props.disabled ? BDFDB.ReactUtils.createElement(Internal.LibraryComponents.SpinnerComponents.Spinner, { + type: Internal.LibraryComponents.SpinnerComponents.Types.PULSING_ELLIPSIS, + className: BDFDB.disCN.buttonspinner, + itemClassName: BDFDB.disCN.buttonspinneritem + }) : null, + BDFDB.ReactUtils.createElement("div", { + className: BDFDB.DOMUtils.formatClassName(BDFDB.disCN.buttoncontents, this.props.innerClassName), + children: this.props.children + }) + ] + })); + return !processingAndListening ? button : BDFDB.ReactUtils.createElement("span", { + className: BDFDB.DOMUtils.formatClassName(BDFDB.disCN.buttondisabledwrapper, this.props.wrapperClassName, this.props.size != null ? this.props.size : Internal.LibraryComponents.Button.Sizes.MEDIUM, this.props.fullWidth && BDFDB.disCN.buttonfullwidth, (this.props.grow === undefined || this.props.grow) && BDFDB.disCN.buttongrow), + children: [ + button, + BDFDB.ReactUtils.createElement("span", { + onMouseEnter: this.handleMouseEnter.bind(this), + onMouseLeave: this.handleMouseLeave.bind(this), + className: BDFDB.disCN.buttondisabledoverlay + }) + ] + }); + } + }; + + CustomComponents.Card = reactInitialized && class BDFDB_Card extends Internal.LibraryModules.React.Component { + render() { + return BDFDB.ReactUtils.createElement("div", BDFDB.ObjectUtils.exclude(Object.assign({}, this.props, { + className: BDFDB.DOMUtils.formatClassName(BDFDB.disCN.hovercardwrapper, this.props.horizontal && BDFDB.disCN.hovercardhorizontal, this.props.backdrop && BDFDB.disCN.hovercard, this.props.className), + onMouseEnter: e => {if (typeof this.props.onMouseEnter == "function") this.props.onMouseEnter(e, this);}, + onMouseLeave: e => {if (typeof this.props.onMouseLeave == "function") this.props.onMouseLeave(e, this);}, + onClick: e => {if (typeof this.props.onClick == "function") this.props.onClick(e, this);}, + onContextMenu: e => {if (typeof this.props.onContextMenu == "function") this.props.onContextMenu(e, this);}, + children: [ + !this.props.noRemove ? BDFDB.ReactUtils.createElement(Internal.LibraryComponents.Clickable, { + "aria-label": BDFDB.LanguageUtils.LanguageStrings.REMOVE, + className: BDFDB.disCNS.hovercardbutton + BDFDB.disCNS.hovercardremovebutton + BDFDB.disCN.hovercardremovebuttondefault, + onClick: e => { + if (typeof this.props.onRemove == "function") this.props.onRemove(e, this); + BDFDB.ListenerUtils.stopEvent(e); + } + }) : null, + typeof this.props.children == "string" ? BDFDB.ReactUtils.createElement(Internal.LibraryComponents.TextElement, { + className: BDFDB.disCN.hovercardinner, + children: BDFDB.ReactUtils.createElement(Internal.LibraryComponents.TextScroller, {children: this.props.children}) + }) : this.props.children + ].flat(10).filter(n => n) + }), "backdrop", "horizontal", "noRemove")); + } + }; + Internal.setDefaultProps(CustomComponents.Card, {backdrop: true, noRemove: false}); + + CustomComponents.ChannelTextAreaButton = reactInitialized && class BDFDB_ChannelTextAreaButton extends Internal.LibraryModules.React.Component { + render() { + const inner = BDFDB.ReactUtils.createElement("div", { + className: BDFDB.disCN.textareabuttonwrapper, + children: BDFDB.ReactUtils.createElement(Internal.LibraryComponents.SvgIcon, { + name: this.props.iconName, + iconSVG: this.props.iconSVG, + className: BDFDB.DOMUtils.formatClassName(BDFDB.disCN.textareaicon, this.props.iconClassName, this.props.pulse && BDFDB.disCN.textareaiconpulse), + nativeClass: this.props.nativeClass + }) + }); + const button = BDFDB.ReactUtils.createElement(Internal.LibraryComponents.Button, { + look: Internal.LibraryComponents.Button.Looks.BLANK, + size: Internal.LibraryComponents.Button.Sizes.NONE, + "aria-label": this.props.label, + tabIndex: this.props.tabIndex, + className: BDFDB.DOMUtils.formatClassName(this.props.isActive && BDFDB.disCN.textareabuttonactive), + innerClassName: BDFDB.DOMUtils.formatClassName(BDFDB.disCN.textareabutton, this.props.className, this.props.pulse && BDFDB.disCN.textareaattachbuttonplus), + onClick: this.props.onClick, + onContextMenu: this.props.onContextMenu, + onMouseEnter: this.props.onMouseEnter, + onMouseLeave: this.props.onMouseLeave, + children: this.props.tooltip && this.props.tooltip.text ? BDFDB.ReactUtils.createElement(Internal.LibraryComponents.TooltipContainer, Object.assign({}, this.props.tooltip, {children: inner})) : inner + }); + return (this.props.className || "").indexOf(BDFDB.disCN.textareapickerbutton) > -1 ? BDFDB.ReactUtils.createElement("div", { + className: BDFDB.disCN.textareapickerbuttoncontainer, + children: button + }) : button; + } + }; + Internal.setDefaultProps(CustomComponents.ChannelTextAreaButton, {tabIndex: 0}); + + CustomComponents.CharCounter = reactInitialized && class BDFDB_CharCounter extends Internal.LibraryModules.React.Component { + getCounterString() { + let input = this.refElement || {}, string = ""; + if (BDFDB.DOMUtils.containsClass(this.refElement, BDFDB.disCN.textarea)) { + let instance = BDFDB.ReactUtils.findOwner(input, {name: "ChannelTextAreaEditor", up: true}); + if (instance) string = instance.props.textValue; + else string = input.value || input.textContent || ""; + } + else string = input.value || input.textContent || ""; + if (this.props.max && this.props.showPercentage && (string.length/this.props.max) * 100 < this.props.showPercentage) return ""; + let start = input.selectionStart || 0, end = input.selectionEnd || 0, selectlength = end - start, selection = BDFDB.DOMUtils.getSelection(); + let select = !selectlength && !selection ? 0 : (selectlength || selection.length); + select = !select ? 0 : (select > string.length ? (end || start ? string.length - (string.length - end - start) : string.length) : select); + let children = [ + typeof this.props.renderPrefix == "function" && this.props.renderPrefix(string.length), + `${string.length}${!this.props.max ? "" : "/" + this.props.max}${!select ? "" : " (" + select + ")"}`, + typeof this.props.renderSuffix == "function" && this.props.renderSuffix(string.length) + ].filter(n => n); + if (typeof this.props.onChange == "function") this.props.onChange(this); + return children.length == 1 ? children[0] : BDFDB.ReactUtils.createElement(Internal.LibraryComponents.Flex, { + align: Internal.LibraryComponents.Flex.Align.CENTER, + children: children + }); + } + updateCounter() { + if (!this.refElement) return; + BDFDB.TimeUtils.clear(this.updateTimeout); + this.updateTimeout = BDFDB.TimeUtils.timeout(this.forceUpdateCounter.bind(this), 100); + } + forceUpdateCounter() { + if (!this.refElement) return; + this.props.children = this.getCounterString(); + BDFDB.ReactUtils.forceUpdate(this); + } + handleSelection() { + if (!this.refElement) return; + let mouseMove = _ => { + BDFDB.TimeUtils.timeout(this.forceUpdateCounter.bind(this), 10); + }; + let mouseUp = _ => { + document.removeEventListener("mousemove", mouseMove); + document.removeEventListener("mouseup", mouseUp); + if (this.refElement.selectionEnd - this.refElement.selectionStart) BDFDB.TimeUtils.timeout(_ => { + document.addEventListener("click", click); + }); + }; + let click = _ => { + BDFDB.TimeUtils.timeout(this.forceUpdateCounter.bind(this), 100); + document.removeEventListener("mousemove", mouseMove); + document.removeEventListener("mouseup", mouseUp); + document.removeEventListener("click", click); + }; + document.addEventListener("mousemove", mouseMove); + document.addEventListener("mouseup", mouseUp); + } + componentDidMount() { + if (this.props.refClass) { + let node = BDFDB.ReactUtils.findDOMNode(this); + if (node && node.parentElement) { + this.refElement = node.parentElement.querySelector(this.props.refClass); + if (this.refElement) { + if (!this._updateCounter) this._updateCounter = _ => { + if (!document.contains(node)) BDFDB.ListenerUtils.multiRemove(this.refElement, "keydown click change", this._updateCounter); + else this.updateCounter(); + }; + if (!this._handleSelection) this._handleSelection = _ => { + if (!document.contains(node)) BDFDB.ListenerUtils.multiRemove(this.refElement, "mousedown", this._handleSelection); + else this.handleSelection(); + }; + BDFDB.ListenerUtils.multiRemove(this.refElement, "mousedown", this._handleSelection); + BDFDB.ListenerUtils.multiAdd(this.refElement, "mousedown", this._handleSelection); + if (this.refElement.tagName == "INPUT" || this.refElement.tagName == "TEXTAREA") { + BDFDB.ListenerUtils.multiRemove(this.refElement, "keydown click change", this._updateCounter); + BDFDB.ListenerUtils.multiAdd(this.refElement, "keydown click change", this._updateCounter); + } + else { + if (!this._mutationObserver) this._mutationObserver = new MutationObserver(changes => { + if (!document.contains(node)) this._mutationObserver.disconnect(); + else this.updateCounter(); + }); + else this._mutationObserver.disconnect(); + this._mutationObserver.observe(this.refElement, {childList: true, subtree: true}); + } + this.updateCounter(); + } + else BDFDB.LogUtils.warn(["could not find referenceElement for BDFDB_CharCounter"]); + } + } + else BDFDB.LogUtils.warn(["refClass can not be undefined for BDFDB_CharCounter"]); + } + render() { + let string = this.getCounterString(); + BDFDB.TimeUtils.timeout(_ => string != this.getCounterString() && BDFDB.ReactUtils.forceUpdate(this)); + return BDFDB.ReactUtils.createElement("div", BDFDB.ObjectUtils.exclude(Object.assign({}, this.props, { + className: BDFDB.DOMUtils.formatClassName(BDFDB.disCN.charcounter, this.props.className), + children: string + }), "parsing", "max", "refClass", "renderPrefix", "renderSuffix", "showPercentage")); + } + }; + + CustomComponents.Checkbox = reactInitialized && class BDFDB_Checkbox extends Internal.LibraryModules.React.Component { + handleMouseDown(e) {if (typeof this.props.onMouseDown == "function") this.props.onMouseDown(e, this);} + handleMouseUp(e) {if (typeof this.props.onMouseUp == "function") this.props.onMouseUp(e, this);} + handleMouseEnter(e) {if (typeof this.props.onMouseEnter == "function") this.props.onMouseEnter(e, this);} + handleMouseLeave(e) {if (typeof this.props.onMouseLeave == "function") this.props.onMouseLeave(e, this);} + getInputMode() { + return this.props.disabled ? "disabled" : this.props.readOnly ? "readonly" : "default"; + } + getStyle() { + let style = this.props.style || {}; + if (!this.props.value) return style; + style = Object.assign({}, style); + this.props.color = typeof this.props.getColor == "function" ? this.props.getColor(this.props.value) : this.props.color; + if (Internal.LibraryComponents.Checkbox.Types) switch (this.props.type) { + case Internal.LibraryComponents.Checkbox.Types.DEFAULT: + style.borderColor = this.props.color; + break; + case Internal.LibraryComponents.Checkbox.Types.GHOST: + let color = BDFDB.ColorUtils.setAlpha(this.props.color, 0.15, "RGB"); + style.backgroundColor = color; + style.borderColor = color; + break; + case Internal.LibraryComponents.Checkbox.Types.INVERTED: + style.backgroundColor = this.props.color; + style.borderColor = this.props.color; + } + return style; + } + getColor() { + return this.props.value ? (Internal.LibraryComponents.Checkbox.Types && this.props.type === Internal.LibraryComponents.Checkbox.Types.INVERTED ? Internal.DiscordConstants.Colors.WHITE : this.props.color) : "transparent"; + } + handleChange(e) { + this.props.value = typeof this.props.getValue == "function" ? this.props.getValue(this.props.value, e, this) : !this.props.value; + if (typeof this.props.onChange == "function") this.props.onChange(this.props.value, this); + BDFDB.ReactUtils.forceUpdate(this); + } + render() { + let label = this.props.children ? BDFDB.ReactUtils.createElement("div", { + className: BDFDB.DOMUtils.formatClassName(BDFDB.disCN.checkboxlabel, this.props.disabled ? BDFDB.disCN.checkboxlabeldisabled : BDFDB.disCN.checkboxlabelclickable, this.props.reverse ? BDFDB.disCN.checkboxlabelreversed : BDFDB.disCN.checkboxlabelforward), + style: { + lineHeight: this.props.size + "px" + }, + children: this.props.children + }) : null; + return BDFDB.ReactUtils.createElement("label", { + className: BDFDB.DOMUtils.formatClassName(this.props.disabled ? BDFDB.disCN.checkboxwrapperdisabled : BDFDB.disCN.checkboxwrapper, this.props.align, this.props.className), + children: [ + this.props.reverse && label, + !this.props.displayOnly && BDFDB.ReactUtils.createElement(Internal.LibraryComponents.FocusRingScope, { + children: BDFDB.ReactUtils.createElement("input", { + className: BDFDB.disCN["checkboxinput" + this.getInputMode()], + type: "checkbox", + onClick: this.props.disabled || this.props.readOnly ? (_ => {}) : this.handleChange.bind(this), + onContextMenu: this.props.disabled || this.props.readOnly ? (_ => {}) : this.handleChange.bind(this), + onMouseUp: !this.props.disabled && this.handleMouseDown.bind(this), + onMouseDown: !this.props.disabled && this.handleMouseUp.bind(this), + onMouseEnter: !this.props.disabled && this.handleMouseEnter.bind(this), + onMouseLeave: !this.props.disabled && this.handleMouseLeave.bind(this), + checked: this.props.value, + style: { + width: this.props.size, + height: this.props.size + } + }) + }), + BDFDB.ReactUtils.createElement("div", { + className: BDFDB.DOMUtils.formatClassName(BDFDB.disCN.checkbox, BDFDB.disCN["checkbox" + this.props.shape], this.props.value && BDFDB.disCN.checkboxchecked), + style: Object.assign({ + width: this.props.size, + height: this.props.size, + borderColor: this.props.checkboxColor + }, this.getStyle()), + children: BDFDB.ReactUtils.createElement(Internal.LibraryComponents.Checkmark, { + width: 18, + height: 18, + color: this.getColor(), + "aria-hidden": true + }) + }), + !this.props.reverse && label + ].filter(n => n) + }); + } + }; + CustomComponents.Checkbox.Types = { + DEFAULT: "DEFAULT", + GHOST: "GHOST", + INVERTED: "INVERTED" + }; + CustomComponents.Checkbox.Shapes = { + BOX: "box", + ROUND: "round" + }; + Internal.setDefaultProps(CustomComponents.Checkbox, {type: CustomComponents.Checkbox.Types.INVERTED, shape: CustomComponents.Checkbox.Shapes.ROUND}); + + CustomComponents.Clickable = reactInitialized && class BDFDB_Clickable extends Internal.LibraryModules.React.Component { + handleClick(e) {if (typeof this.props.onClick == "function") this.props.onClick(e, this);} + handleContextMenu(e) {if (typeof this.props.onContextMenu == "function") this.props.onContextMenu(e, this);} + handleMouseDown(e) {if (typeof this.props.onMouseDown == "function") this.props.onMouseDown(e, this);} + handleMouseUp(e) {if (typeof this.props.onMouseUp == "function") this.props.onMouseUp(e, this);} + handleMouseEnter(e) {if (typeof this.props.onMouseEnter == "function") this.props.onMouseEnter(e, this);} + handleMouseLeave(e) {if (typeof this.props.onMouseLeave == "function") this.props.onMouseLeave(e, this);} + render() { + return BDFDB.ReactUtils.createElement(Internal.NativeSubComponents.Clickable, Object.assign({}, this.props, { + className: BDFDB.DOMUtils.formatClassName(this.props.className, (this.props.className || "").toLowerCase().indexOf("disabled") == -1 && BDFDB.disCN.cursorpointer), + onClick: this.handleClick.bind(this), + onContextMenu: this.handleContextMenu.bind(this), + onMouseUp: this.handleMouseDown.bind(this), + onMouseDown: !this.props.disabled && this.handleMouseUp.bind(this), + onMouseEnter: this.handleMouseEnter.bind(this), + onMouseLeave: this.handleMouseLeave.bind(this) + })); + } + }; + + CustomComponents.CollapseContainer = reactInitialized && class BDFDB_CollapseContainer extends Internal.LibraryModules.React.Component { + render() { + if (!BDFDB.ObjectUtils.is(this.props.collapseStates)) this.props.collapseStates = {}; + this.props.collapsed = this.props.collapsed && (this.props.collapseStates[this.props.title] || this.props.collapseStates[this.props.title] === undefined); + this.props.collapseStates[this.props.title] = this.props.collapsed; + return BDFDB.ReactUtils.createElement("div", { + className: BDFDB.DOMUtils.formatClassName(this.props.collapsed && BDFDB.disCN.collapsecontainercollapsed, this.props.mini ? BDFDB.disCN.collapsecontainermini : BDFDB.disCN.collapsecontainer, this.props.className), + id: this.props.id, + children: [ + BDFDB.ReactUtils.createElement(Internal.LibraryComponents.Flex, { + className: BDFDB.disCN.collapsecontainerheader, + align: Internal.LibraryComponents.Flex.Align.CENTER, + onClick: e => { + this.props.collapsed = !this.props.collapsed; + this.props.collapseStates[this.props.title] = this.props.collapsed; + if (typeof this.props.onClick == "function") this.props.onClick(this.props.collapsed, this); + BDFDB.ReactUtils.forceUpdate(this); + }, + children: BDFDB.ReactUtils.createElement(Internal.LibraryComponents.FormComponents.FormTitle, { + tag: Internal.LibraryComponents.FormComponents.FormTags && Internal.LibraryComponents.FormComponents.FormTags.H5, + className: BDFDB.disCN.collapsecontainertitle, + children: this.props.title + }) + }), + !this.props.collapsed ? BDFDB.ReactUtils.createElement("div", { + className: BDFDB.disCN.collapsecontainerinner, + children: this.props.children + }) : null + ] + }); + } + }; + Internal.setDefaultProps(CustomComponents.CollapseContainer, {collapsed: true, mini: true}); + + CustomComponents.ColorPicker = reactInitialized && class BDFDB_ColorPicker extends Internal.LibraryModules.React.Component { + constructor(props) { + super(props); + if (!this.state) this.state = {}; + this.state.isGradient = props.gradient && props.color && BDFDB.ObjectUtils.is(props.color); + this.state.gradientBarEnabled = this.state.isGradient; + this.state.draggingAlphaCursor = false; + this.state.draggingGradientCursor = false; + this.state.selectedGradientCursor = 0; + } + handleColorChange(color) { + let changed = false; + if (color != null) { + changed = !BDFDB.equals(this.state.isGradient ? this.props.color[this.state.selectedGradientCursor] : this.props.color, color); + if (this.state.isGradient) this.props.color[this.state.selectedGradientCursor] = color; + else this.props.color = color; + } + else changed = true; + if (changed) { + if (typeof this.props.onColorChange == "function") this.props.onColorChange(BDFDB.ColorUtils.convert(this.props.color, "RGBCOMP")); + BDFDB.ReactUtils.forceUpdate(this); + } + } + render() { + if (this.state.isGradient) this.props.color = Object.assign({}, this.props.color); + + let colorFormat = this.props.alpha ? "HSVA" : "HSV"; + let hexRegex = this.props.alpha ? /^#([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i : /^#([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i; + + let selectedColor = BDFDB.ColorUtils.convert(this.state.isGradient ? this.props.color[this.state.selectedGradientCursor] : this.props.color, colorFormat) || BDFDB.ColorUtils.convert("#000000FF", colorFormat); + let currentGradient = (this.state.isGradient ? Object.entries(this.props.color, colorFormat) : [[0, selectedColor], [1, selectedColor]]); + + let [h, s, v] = BDFDB.ColorUtils.convert(selectedColor, "HSVCOMP"); + let a = BDFDB.ColorUtils.getAlpha(selectedColor); + a = a == null ? 1 : a; + + let hexColor = BDFDB.ColorUtils.convert(selectedColor, this.props.alpha ? "HEXA" : "HEX"); + let hexLength = hexColor.length; + + return BDFDB.ReactUtils.createElement(Internal.LibraryComponents.PopoutFocusLock, { + className: BDFDB.disCNS.colorpickerwrapper + BDFDB.disCN.colorpicker, + children: [ + BDFDB.ReactUtils.createElement("div", { + className: BDFDB.disCN.colorpickerinner, + children: [ + BDFDB.ReactUtils.createElement("div", { + className: BDFDB.disCN.colorpickersaturation, + children: BDFDB.ReactUtils.createElement("div", { + className: BDFDB.disCN.colorpickersaturationcolor, + style: {position: "absolute", top: 0, right: 0, bottom: 0, left: 0, cursor: "crosshair", backgroundColor: BDFDB.ColorUtils.convert([h, "100%", "50%"], "RGB")}, + onClick: event => { + let rects = BDFDB.DOMUtils.getRects(BDFDB.DOMUtils.getParent(BDFDB.dotCN.colorpickersaturationcolor, event.target)); + this.handleColorChange(BDFDB.ColorUtils.convert([h, BDFDB.NumberUtils.mapRange([rects.left, rects.left + rects.width], [0, 100], event.clientX) + "%", BDFDB.NumberUtils.mapRange([rects.top, rects.top + rects.height], [100, 0], event.clientY) + "%", a], colorFormat, "HSVCOMP")); + }, + onMouseDown: event => { + let rects = BDFDB.DOMUtils.getRects(BDFDB.DOMUtils.getParent(BDFDB.dotCN.colorpickersaturationcolor, event.target)); + let mouseUp = _ => { + document.removeEventListener("mouseup", mouseUp); + document.removeEventListener("mousemove", mouseMove); + }; + let mouseMove = event2 => { + this.handleColorChange(BDFDB.ColorUtils.convert([h, BDFDB.NumberUtils.mapRange([rects.left, rects.left + rects.width], [0, 100], event2.clientX) + "%", BDFDB.NumberUtils.mapRange([rects.top, rects.top + rects.height], [100, 0], event2.clientY) + "%", a], colorFormat, "HSVCOMP")); + }; + document.addEventListener("mouseup", mouseUp); + document.addEventListener("mousemove", mouseMove); + }, + children: [ + BDFDB.ReactUtils.createElement("style", { + children: `${BDFDB.dotCN.colorpickersaturationwhite} {background: -webkit-linear-gradient(to right, #fff, rgba(255,255,255,0));background: linear-gradient(to right, #fff, rgba(255,255,255,0));}${BDFDB.dotCN.colorpickersaturationblack} {background: -webkit-linear-gradient(to top, #000, rgba(0,0,0,0));background: linear-gradient(to top, #000, rgba(0,0,0,0));}` + }), + BDFDB.ReactUtils.createElement("div", { + className: BDFDB.disCN.colorpickersaturationwhite, + style: {position: "absolute", top: 0, right: 0, bottom: 0, left: 0}, + children: [ + BDFDB.ReactUtils.createElement("div", { + className: BDFDB.disCN.colorpickersaturationblack, + style: {position: "absolute", top: 0, right: 0, bottom: 0, left: 0} + }), + BDFDB.ReactUtils.createElement("div", { + className: BDFDB.disCN.colorpickersaturationcursor, + style: {position: "absolute", cursor: "crosshair", left: s, top: `${BDFDB.NumberUtils.mapRange([0, 100], [100, 0], parseFloat(v))}%`}, + children: BDFDB.ReactUtils.createElement("div", { + style: {width: 4, height: 4, boxShadow: "rgb(255, 255, 255) 0px 0px 0px 1.5px, rgba(0, 0, 0, 0.3) 0px 0px 1px 1px inset, rgba(0, 0, 0, 0.4) 0px 0px 1px 2px", borderRadius: "50%", transform: "translate(-2px, -2px)"} + }) + }) + ] + }) + ] + }) + }), + BDFDB.ReactUtils.createElement("div", { + className: BDFDB.disCN.colorpickerhue, + children: BDFDB.ReactUtils.createElement("div", { + style: {position: "absolute", top: 0, right: 0, bottom: 0, left: 0}, + children: BDFDB.ReactUtils.createElement("div", { + className: BDFDB.disCN.colorpickerhuehorizontal, + style: {padding: "0px 2px", position: "relative", height: "100%"}, + onClick: event => { + let rects = BDFDB.DOMUtils.getRects(BDFDB.DOMUtils.getParent(BDFDB.dotCN.colorpickerhuehorizontal, event.target)); + this.handleColorChange(BDFDB.ColorUtils.convert([BDFDB.NumberUtils.mapRange([rects.left, rects.left + rects.width], [0, 360], event.clientX), s, v, a], colorFormat, "HSVCOMP")); + }, + onMouseDown: event => { + let rects = BDFDB.DOMUtils.getRects(BDFDB.DOMUtils.getParent(BDFDB.dotCN.colorpickerhuehorizontal, event.target)); + let mouseUp = _ => { + document.removeEventListener("mouseup", mouseUp); + document.removeEventListener("mousemove", mouseMove); + }; + let mouseMove = event2 => { + this.handleColorChange(BDFDB.ColorUtils.convert([BDFDB.NumberUtils.mapRange([rects.left, rects.left + rects.width], [0, 360], event2.clientX), s, v, a], colorFormat, "HSVCOMP")); + }; + document.addEventListener("mouseup", mouseUp); + document.addEventListener("mousemove", mouseMove); + }, + children: [ + BDFDB.ReactUtils.createElement("style", { + children: `${BDFDB.dotCN.colorpickerhuehorizontal} {background: linear-gradient(to right, #f00 0%, #ff0 17%, #0f0 33%, #0ff 50%, #00f 67%, #f0f 83%, #f00 100%);background: -webkit-linear-gradient(to right, #f00 0%, #ff0 17%, #0f0 33%, #0ff 50%, #00f 67%, #f0f 83%, #f00 100%);}${BDFDB.dotCN.colorpickerhuevertical} {background: linear-gradient(to top, #f00 0%, #ff0 17%, #0f0 33%, #0ff 50%, #00f 67%, #f0f 83%, #f00 100%);background: -webkit-linear-gradient(to top, #f00 0%, #ff0 17%, #0f0 33%, #0ff 50%, #00f 67%, #f0f 83%, #f00 100%);}` + }), + BDFDB.ReactUtils.createElement("div", { + className: BDFDB.disCN.colorpickerhuecursor, + style: {position: "absolute", cursor: "ew-resize", left: `${BDFDB.NumberUtils.mapRange([0, 360], [0, 100], h)}%`}, + children: BDFDB.ReactUtils.createElement("div", { + style: {marginTop: 1, width: 4, borderRadius: 1, height: 8, boxShadow: "rgba(0, 0, 0, 0.6) 0px 0px 2px", background: "rgb(255, 255, 255)", transform: "translateX(-2px)"} + }) + }) + ] + }) + }) + }), + this.props.alpha && BDFDB.ReactUtils.createElement("div", { + className: BDFDB.disCN.colorpickeralpha, + children: [ + BDFDB.ReactUtils.createElement("div", { + style: {position: "absolute", top: 0, right: 0, bottom: 0, left: 0}, + children: BDFDB.ReactUtils.createElement("div", { + className: BDFDB.disCN.colorpickeralphacheckered, + style: {padding: "0px 2px", position: "relative", height: "100%"} + }) + }), + BDFDB.ReactUtils.createElement("div", { + style: {position: "absolute", top: 0, right: 0, bottom: 0, left: 0}, + children: BDFDB.ReactUtils.createElement("div", { + className: BDFDB.disCN.colorpickeralphahorizontal, + style: {padding: "0px 2px", position: "relative", height: "100%", background: `linear-gradient(to right, ${BDFDB.ColorUtils.setAlpha([h, s, v], 0, "RGBA")}, ${BDFDB.ColorUtils.setAlpha([h, s, v], 1, "RGBA")}`}, + onClick: event => { + let rects = BDFDB.DOMUtils.getRects(BDFDB.DOMUtils.getParent(BDFDB.dotCN.colorpickeralphahorizontal, event.target)); + this.handleColorChange(BDFDB.ColorUtils.setAlpha([h, s, v], BDFDB.NumberUtils.mapRange([rects.left, rects.left + rects.width], [0, 1], event.clientX), colorFormat)); + }, + onMouseDown: event => { + let rects = BDFDB.DOMUtils.getRects(BDFDB.DOMUtils.getParent(BDFDB.dotCN.colorpickeralphahorizontal, event.target)); + let mouseUp = _ => { + document.removeEventListener("mouseup", mouseUp); + document.removeEventListener("mousemove", mouseMove); + this.state.draggingAlphaCursor = false; + BDFDB.ReactUtils.forceUpdate(this); + }; + let mouseMove = event2 => { + this.state.draggingAlphaCursor = true; + this.handleColorChange(BDFDB.ColorUtils.setAlpha([h, s, v], BDFDB.NumberUtils.mapRange([rects.left, rects.left + rects.width], [0, 1], event2.clientX), colorFormat)); + }; + document.addEventListener("mouseup", mouseUp); + document.addEventListener("mousemove", mouseMove); + }, + children: BDFDB.ReactUtils.createElement("div", { + className: BDFDB.disCN.colorpickeralphacursor, + style: {position: "absolute", cursor: "ew-resize", left: `${a * 100}%`}, + children: [ + BDFDB.ReactUtils.createElement("div", { + style: {marginTop: 1, width: 4, borderRadius: 1, height: 8, boxShadow: "rgba(0, 0, 0, 0.6) 0px 0px 2px", background: "rgb(255, 255, 255)", transform: "translateX(-2px)"} + }), + this.state.draggingAlphaCursor && BDFDB.ReactUtils.createElement("span", { + className: BDFDB.disCN.sliderbubble, + style: {opacity: 1, visibility: "visible", left: 2}, + children: `${Math.floor(a * 100)}%` + }) + ].filter(n => n) + }) + }) + }) + ] + }), + this.state.gradientBarEnabled && BDFDB.ReactUtils.createElement("div", { + className: BDFDB.disCN.colorpickergradient, + children: [ + BDFDB.ReactUtils.createElement("div", { + style: {position: "absolute", top: 0, right: 0, bottom: 0, left: 0}, + children: BDFDB.ReactUtils.createElement("div", { + className: BDFDB.disCN.colorpickergradientcheckered, + style: {padding: "0px 2px", position: "relative", height: "100%"} + }) + }), + BDFDB.ReactUtils.createElement("div", { + style: {position: "absolute", top: 0, right: 0, bottom: 0, left: 0}, + children: BDFDB.ReactUtils.createElement("div", { + className: BDFDB.disCN.colorpickergradienthorizontal, + style: {padding: "0px 2px", position: "relative", cursor: "copy", height: "100%", background: BDFDB.ColorUtils.createGradient(currentGradient.reduce((colorObj, posAndColor) => (colorObj[posAndColor[0]] = posAndColor[1], colorObj), {}))}, + onClick: event => { + let rects = BDFDB.DOMUtils.getRects(event.target); + let pos = BDFDB.NumberUtils.mapRange([rects.left, rects.left + rects.width], [0.01, 0.99], event.clientX); + if (Object.keys(this.props.color).indexOf(pos) == -1) { + this.props.color[pos] = BDFDB.ColorUtils.convert("#000000FF", colorFormat); + this.state.selectedGradientCursor = pos; + this.handleColorChange(); + } + }, + children: currentGradient.map(posAndColor => BDFDB.ReactUtils.createElement("div", { + className: BDFDB.DOMUtils.formatClassName(BDFDB.disCN.colorpickergradientcursor, (posAndColor[0] == 0 || posAndColor[0] == 1) && BDFDB.disCN.colorpickergradientcursoredge, this.state.selectedGradientCursor == posAndColor[0] && BDFDB.disCN.colorpickergradientcursorselected), + style: {position: "absolute", cursor: "pointer", left: `${posAndColor[0] * 100}%`}, + onMouseDown: posAndColor[0] == 0 || posAndColor[0] == 1 ? _ => {} : event => { + event = event.nativeEvent || event; + let mouseMove = event2 => { + if (Math.sqrt((event.pageX - event2.pageX)**2) > 10) { + document.removeEventListener("mousemove", mouseMove); + document.removeEventListener("mouseup", mouseUp); + + this.state.draggingGradientCursor = true; + let cursor = BDFDB.DOMUtils.getParent(BDFDB.dotCN.colorpickergradientcursor, event.target); + let rects = BDFDB.DOMUtils.getRects(cursor.parentElement); + + let releasing = _ => { + document.removeEventListener("mousemove", dragging); + document.removeEventListener("mouseup", releasing); + BDFDB.TimeUtils.timeout(_ => {this.state.draggingGradientCursor = false;}); + }; + let dragging = event3 => { + let pos = BDFDB.NumberUtils.mapRange([rects.left, rects.left + rects.width], [0.01, 0.99], event3.clientX); + if (Object.keys(this.props.color).indexOf(pos) == -1) { + delete this.props.color[posAndColor[0]]; + posAndColor[0] = pos; + this.props.color[pos] = posAndColor[1]; + this.state.selectedGradientCursor = pos; + this.handleColorChange(); + } + }; + document.addEventListener("mousemove", dragging); + document.addEventListener("mouseup", releasing); + } + }; + let mouseUp = _ => { + document.removeEventListener("mousemove", mouseMove); + document.removeEventListener("mouseup", mouseUp); + }; + document.addEventListener("mousemove", mouseMove); + document.addEventListener("mouseup", mouseUp); + }, + onClick: event => { + BDFDB.ListenerUtils.stopEvent(event); + if (!this.state.draggingGradientCursor) { + this.state.selectedGradientCursor = posAndColor[0]; + BDFDB.ReactUtils.forceUpdate(this); + } + }, + onContextMenu: posAndColor[0] == 0 || posAndColor[0] == 1 ? _ => {} : event => { + BDFDB.ListenerUtils.stopEvent(event); + delete this.props.color[posAndColor[0]]; + this.state.selectedGradientCursor = 0; + this.handleColorChange(); + }, + children: BDFDB.ReactUtils.createElement("div", { + style: {background: BDFDB.ColorUtils.convert(posAndColor[1], "RGBA")} + }) + })) + }) + }) + ] + }) + ].filter(n => n) + }), + BDFDB.ReactUtils.createElement(Internal.LibraryComponents.TextInput, { + className: BDFDB.disCNS.colorpickerhexinput + BDFDB.disCN.margintop8, + maxLength: this.props.alpha ? 9 : 7, + valuePrefix: "#", + value: hexColor, + autoFocus: true, + onChange: value => { + const oldLength = hexLength; + hexLength = (value || "").length; + if (this.props.alpha && (oldLength > 8 || oldLength < 6) && hexLength == 7) value += "FF"; + if (hexRegex.test(value)) this.handleColorChange(value); + }, + inputChildren: this.props.gradient && BDFDB.ReactUtils.createElement(Internal.LibraryComponents.TooltipContainer, { + text: BDFDB.LanguageUtils.LibraryStrings.gradient, + children: BDFDB.ReactUtils.createElement(Internal.LibraryComponents.Clickable, { + className: BDFDB.DOMUtils.formatClassName(BDFDB.disCN.colorpickergradientbutton, this.state.gradientBarEnabled && BDFDB.disCN.colorpickergradientbuttonenabled), + children: BDFDB.ReactUtils.createElement(Internal.LibraryComponents.SvgIcon, { + nativeClass: true, + width: 28, + height: 28, + name: Internal.LibraryComponents.SvgIcon.Names.GRADIENT + }), + onClick: _ => { + this.state.gradientBarEnabled = !this.state.gradientBarEnabled; + if (this.state.gradientBarEnabled && !this.state.isGradient) this.props.color = {0: selectedColor, 1: selectedColor}; + else if (!this.state.gradientBarEnabled && this.state.isGradient) this.props.color = selectedColor; + this.state.isGradient = this.props.color && BDFDB.ObjectUtils.is(this.props.color); + this.handleColorChange(); + } + }) + }) + }), + BDFDB.ReactUtils.createElement("div", { + className: "move-corners", + children: [{top: 0, left: 0}, {top: 0, right: 0}, {bottom: 0, right: 0}, {bottom: 0, left: 0}].map(pos => BDFDB.ReactUtils.createElement("div", { + className: "move-corner", + onMouseDown: e => { + if (!this.domElementRef.current) return; + let rects = BDFDB.DOMUtils.getRects(this.domElementRef.current); + let left = rects.left, top = rects.top; + let oldX = e.pageX, oldY = e.pageY; + let mouseUp = _ => { + document.removeEventListener("mouseup", mouseUp); + document.removeEventListener("mousemove", mouseMove); + }; + let mouseMove = e2 => { + left = left - (oldX - e2.pageX), top = top - (oldY - e2.pageY); + oldX = e2.pageX, oldY = e2.pageY; + this.domElementRef.current.style.setProperty("left", `${left}px`, "important"); + this.domElementRef.current.style.setProperty("top", `${top}px`, "important"); + }; + document.addEventListener("mouseup", mouseUp); + document.addEventListener("mousemove", mouseMove); + }, + style: Object.assign({}, pos, {width: 10, height: 10, cursor: "move", position: "absolute"}) + })) + }) + ] + }); + } + }; + + CustomComponents.ColorSwatches = reactInitialized && class BDFDB_ColorSwatches extends Internal.LibraryModules.React.Component { + ColorSwatch(props) { + const swatches = props.swatches; + let useWhite = !BDFDB.ColorUtils.isBright(props.color); + let swatch = BDFDB.ReactUtils.createElement("button", { + type: "button", + className: BDFDB.DOMUtils.formatClassName(BDFDB.disCN.colorpickerswatch, props.isSingle && BDFDB.disCN.colorpickerswatchsingle, props.isDisabled && BDFDB.disCN.colorpickerswatchdisabled, props.isSelected && BDFDB.disCN.colorpickerswatchselected, props.isCustom && BDFDB.disCN.colorpickerswatchcustom, props.color == null && BDFDB.disCN.colorpickerswatchnocolor), + number: props.number, + disabled: props.isDisabled, + onClick: _ => { + if (!props.isSelected) { + let color = props.isCustom && props.color == null ? (swatches.props.color || swatches.props.defaultCustomColor || "rgba(0, 0, 0, 1)") : props.color; + if (typeof swatches.props.onColorChange == "function") swatches.props.onColorChange(BDFDB.ColorUtils.convert(color, "RGBCOMP")); + swatches.props.color = color; + swatches.props.customColor = props.isCustom ? color : swatches.props.customColor; + swatches.props.customSelected = props.isCustom; + BDFDB.ReactUtils.forceUpdate(swatches); + } + }, + style: Object.assign({}, props.style, { + background: BDFDB.ObjectUtils.is(props.color) ? BDFDB.ColorUtils.createGradient(props.color) : BDFDB.ColorUtils.convert(props.color, "RGBA") + }), + children: [ + props.isCustom || props.isSingle ? BDFDB.ReactUtils.createElement(Internal.LibraryComponents.SvgIcon, { + className: BDFDB.disCN.colorpickerswatchdropper, + foreground: BDFDB.disCN.colorpickerswatchdropperfg, + name: Internal.LibraryComponents.SvgIcon.Names.DROPPER, + width: props.isCustom ? 14 : 10, + height: props.isCustom ? 14 : 10, + color: useWhite ? Internal.DiscordConstants.Colors.WHITE : Internal.DiscordConstants.Colors.BLACK + }) : null, + props.isSelected && !props.isSingle ? BDFDB.ReactUtils.createElement(Internal.LibraryComponents.SvgIcon, { + name: Internal.LibraryComponents.SvgIcon.Names.CHECKMARK, + width: props.isCustom ? 32 : 16, + height: props.isCustom ? 24 : 16, + color: useWhite ? Internal.DiscordConstants.Colors.WHITE : Internal.DiscordConstants.Colors.BLACK + }) : null + ] + }); + if (props.isCustom || props.isSingle || props.color == null) swatch = BDFDB.ReactUtils.createElement(Internal.LibraryComponents.TooltipContainer, { + text: props.isCustom || props.isSingle ? BDFDB.LanguageUtils.LanguageStrings.CUSTOM_COLOR : BDFDB.LanguageUtils.LanguageStrings.DEFAULT, + tooltipConfig: {type: props.isSingle ? "top" : "bottom"}, + children: swatch + }); + if (props.isCustom || props.isSingle) swatch = BDFDB.ReactUtils.createElement(Internal.LibraryComponents.PopoutContainer, { + children: swatch, + wrap: false, + popoutClassName: BDFDB.disCNS.colorpickerwrapper + BDFDB.disCN.colorpicker, + animation: Internal.LibraryComponents.PopoutContainer.Animation.TRANSLATE, + position: Internal.LibraryComponents.PopoutContainer.Positions.BOTTOM, + align: Internal.LibraryComponents.PopoutContainer.Align.CENTER, + open: swatches.props.pickerOpen, + onClick: _ => swatches.props.pickerOpen = true, + onOpen: _ => { + swatches.props.pickerOpen = true; + if (typeof swatches.props.onPickerOpen == "function") swatches.props.onPickerOpen(this); + }, + onClose: _ => { + delete swatches.props.pickerOpen; + if (typeof swatches.props.onPickerClose == "function") swatches.props.onPickerClose(this); + }, + renderPopout: _ => BDFDB.ReactUtils.createElement(Internal.LibraryComponents.ColorPicker, Object.assign({}, swatches.props.pickerConfig, { + color: swatches.props.color, + onColorChange: color => { + if (typeof swatches.props.onColorChange == "function") swatches.props.onColorChange(color); + props.color = color; + swatches.props.color = color; + swatches.props.customColor = color; + swatches.props.customSelected = true; + BDFDB.ReactUtils.forceUpdate(swatches); + } + }), true) + }); + if (props.isCustom) swatch = BDFDB.ReactUtils.createElement("div", { + className: BDFDB.disCN.colorpickerswatchcustomcontainer, + children: swatch + }); + return swatch; + } + render() { + this.props.color = BDFDB.ObjectUtils.is(this.props.color) ? this.props.color : BDFDB.ColorUtils.convert(this.props.color, "RGBA"); + this.props.colors = (BDFDB.ArrayUtils.is(this.props.colors) ? this.props.colors : [null, 5433630, 3066993, 1752220, 3447003, 3429595, 8789737, 10181046, 15277667, 15286558, 15158332, 15105570, 15844367, 13094093, 7372936, 6513507, 16777215, 3910932, 2067276, 1146986, 2123412, 2111892, 7148717, 7419530, 11342935, 11345940, 10038562, 11027200, 12745742, 9936031, 6121581, 2894892]).map(c => BDFDB.ColorUtils.convert(c, "RGBA")); + this.props.colorRows = this.props.colors.length ? [this.props.colors.slice(0, parseInt(this.props.colors.length/2)), this.props.colors.slice(parseInt(this.props.colors.length/2))] : []; + this.props.customColor = !this.props.color || !this.props.customSelected && this.props.colors.indexOf(this.props.color) > -1 ? null : this.props.color; + this.props.defaultCustomColor = BDFDB.ObjectUtils.is(this.props.defaultCustomColor) ? this.props.defaultCustomColor : BDFDB.ColorUtils.convert(this.props.defaultCustomColor, "RGBA"); + this.props.customSelected = !!this.props.customColor; + this.props.pickerConfig = BDFDB.ObjectUtils.is(this.props.pickerConfig) ? this.props.pickerConfig : {gradient: true, alpha: true}; + + const isSingle = !this.props.colors.length; + return BDFDB.ReactUtils.createElement("div", { + className: isSingle ? BDFDB.disCN.colorpickerswatchsinglewrapper : BDFDB.DOMUtils.formatClassName(BDFDB.disCN.colorpickerswatches, BDFDB.disCN.colorpickerswatchescontainer, this.props.disabled && BDFDB.disCN.colorpickerswatchesdisabled), + children: [ + BDFDB.ReactUtils.createElement(this.ColorSwatch, { + swatches: this, + color: this.props.customColor, + isSingle: isSingle, + isCustom: !isSingle, + isSelected: this.props.customSelected, + isDisabled: this.props.disabled, + pickerOpen: this.props.pickerOpen, + style: {margin: 0} + }), + !isSingle && BDFDB.ReactUtils.createElement("div", { + children: this.props.colorRows.map(row => BDFDB.ReactUtils.createElement("div", { + className: BDFDB.disCN.colorpickerrow, + children: row.map(color => BDFDB.ReactUtils.createElement(this.ColorSwatch, { + swatches: this, + color: color, + isCustom: false, + isSelected: !this.props.customSelected && color == this.props.color, + isDisabled: this.props.disabled + })) + })) + }) + ] + }); + } + }; + + CustomComponents.DateInput = class BDFDB_DateInput extends Internal.LibraryModules.React.Component { + renderFormatButton(props) { + const button = BDFDB.ReactUtils.createElement(Internal.LibraryComponents.Clickable, { + className: BDFDB.disCN.dateinputbutton, + children: BDFDB.ReactUtils.createElement(Internal.LibraryComponents.SvgIcon, { + name: props.svgName, + width: 20, + height: 20 + }) + }); + return BDFDB.ReactUtils.createElement(Internal.LibraryComponents.PopoutContainer, { + width: props.popoutWidth || 350, + padding: 10, + animation: Internal.LibraryComponents.PopoutContainer.Animation.SCALE, + position: Internal.LibraryComponents.PopoutContainer.Positions.TOP, + align: Internal.LibraryComponents.PopoutContainer.Align.RIGHT, + onClose: instance => BDFDB.DOMUtils.removeClass(instance.domElementRef.current, BDFDB.disCN.dateinputbuttonselected), + renderPopout: instance => { + BDFDB.DOMUtils.addClass(instance.domElementRef.current, BDFDB.disCN.dateinputbuttonselected); + return props.children || BDFDB.ReactUtils.createElement(Internal.LibraryComponents.Flex, { + align: Internal.LibraryComponents.Flex.Align.CENTER, + children: [ + props.name && BDFDB.ReactUtils.createElement(Internal.LibraryComponents.SettingsLabel, { + label: props.name + }), + BDFDB.ReactUtils.createElement(Internal.LibraryComponents.TextInput, { + className: BDFDB.disCN.dateinputfield, + placeholder: props.placeholder, + value: props.getValue(), + onChange: typeof props.onChange == "function" ? props.onChange : null + }), + props.tooltipText && this.renderInfoButton(props.tooltipText) + ].filter(n => n) + }) + }, + children: props.name ? BDFDB.ReactUtils.createElement(Internal.LibraryComponents.TooltipContainer, { + text: props.name, + children: button + }) : button + }); + } + renderInfoButton(text, style) { + return BDFDB.ReactUtils.createElement(Internal.LibraryComponents.TooltipContainer, { + text: [text].flat(10).filter(n => n).map(n => BDFDB.ReactUtils.createElement("div", {children: n})), + tooltipConfig: { + type: "bottom", + zIndex: 1009, + maxWidth: 560 + }, + children: BDFDB.ReactUtils.createElement("div", { + className: BDFDB.disCN.dateinputbutton, + style: Object.assign({}, style), + children: BDFDB.ReactUtils.createElement(Internal.LibraryComponents.SvgIcon, { + name: Internal.LibraryComponents.SvgIcon.Names.QUESTIONMARK, + width: 24, + height: 24 + }) + }) + }); + } + handleChange() { + if (typeof this.props.onChange == "function") this.props.onChange(BDFDB.ObjectUtils.extract(this.props, "formatString", "dateString", "timeString", "timeOffset", "language")); + } + render() { + let input = this, formatter, preview; + const defaultOffset = ((new Date()).getTimezoneOffset() * (-1/60)); + return BDFDB.ReactUtils.createElement("div", BDFDB.ObjectUtils.exclude(Object.assign({}, this.props, { + className: BDFDB.DOMUtils.formatClassName(BDFDB.disCN.dateinputwrapper, this.props.className), + children: [ + BDFDB.ReactUtils.createElement(Internal.LibraryComponents.SettingsLabel, { + label: this.props.label + }), + BDFDB.ReactUtils.createElement("div", { + className: BDFDB.disCN.dateinputinner, + children: [ + BDFDB.ReactUtils.createElement("div", { + className: BDFDB.disCN.dateinputcontrols, + children: [ + BDFDB.ReactUtils.createElement(class DateInputPreview extends Internal.LibraryModules.React.Component { + componentDidMount() {formatter = this;} + render() { + return BDFDB.ReactUtils.createElement(Internal.LibraryComponents.TextInput, { + className: BDFDB.disCN.dateinputfield, + placeholder: Internal.LibraryComponents.DateInput.getDefaultString(input.props.language), + value: input.props.formatString, + onChange: value => { + input.props.formatString = value; + input.handleChange.apply(input, []); + BDFDB.ReactUtils.forceUpdate(formatter, preview); + } + }); + } + }), + this.renderInfoButton([ + "$date will be replaced with the Date", + "$time will be replaced with the Time", + "$time12 will be replaced with the Time (12h Form)", + "$month will be replaced with the Month Name", + "$monthS will be replaced with the Month Name (Short Form)", + "$day will be replaced with the Weekday Name", + "$dayS will be replaced with the Weekday Name (Short Form)", + "$agoAmount will be replaced with ('Today', 'Yesterday', 'x days/weeks/months ago')", + "$agoWeekday will be replaced with ('Today', 'Yesterday', $day)", + "$agoWeekdayS will be replaced with ('Today', 'Yesterday', $dayS)", + "$agoDays will be replaced with ('Today', 'Yesterday', 'x days ago')", + "$agoDate will be replaced with ('Today', 'Yesterday', $date)" + ], {marginRight: 6}), + this.renderFormatButton({ + name: BDFDB.LanguageUtils.LanguageStrings.DATE, + svgName: Internal.LibraryComponents.SvgIcon.Names.CALENDAR, + placeholder: this.props.dateString, + getValue: _ => this.props.dateString, + tooltipText: [ + "$d will be replaced with the Day", + "$dd will be replaced with the Day (Forced Zeros)", + "$m will be replaced with the Month", + "$mm will be replaced with the Month (Forced Zeros)", + "$yy will be replaced with the Year (2-Digit)", + "$yyyy will be replaced with the Year (4-Digit)", + "$month will be replaced with the Month Name", + "$monthS will be replaced with the Month Name (Short Form)", + ], + onChange: value => { + this.props.dateString = value; + this.handleChange.apply(this, []); + BDFDB.ReactUtils.forceUpdate(formatter, preview); + } + }), + this.renderFormatButton({ + name: BDFDB.LanguageUtils.LibraryStrings.time, + svgName: Internal.LibraryComponents.SvgIcon.Names.CLOCK, + placeholder: this.props.timeString, + getValue: _ => this.props.timeString, + tooltipText: [ + "$h will be replaced with the Hours", + "$hh will be replaced with the Hours (Forced Zeros)", + "$m will be replaced with the Minutes", + "$mm will be replaced with the Minutes (Forced Zeros)", + "$s will be replaced with the Seconds", + "$ss will be replaced with the Seconds (Forced Zeros)", + "$u will be replaced with the Milliseconds", + "$uu will be replaced with the Milliseconds (Forced Zeros)" + ], + onChange: value => { + this.props.timeString = value; + this.handleChange.apply(this, []); + BDFDB.ReactUtils.forceUpdate(formatter, preview); + } + }), + this.renderFormatButton({ + name: BDFDB.LanguageUtils.LibraryStrings.location, + svgName: Internal.LibraryComponents.SvgIcon.Names.GLOBE, + popoutWidth: 550, + children: [ + BDFDB.ReactUtils.createElement(Internal.LibraryComponents.AutoFocusCatcher, {}), + BDFDB.ReactUtils.createElement(Internal.LibraryComponents.Flex, { + className: BDFDB.disCN.marginbottom4, + align: Internal.LibraryComponents.Flex.Align.CENTER, + children: [ + BDFDB.ReactUtils.createElement(Internal.LibraryComponents.SettingsLabel, { + label: BDFDB.LanguageUtils.LanguageStrings.LANGUAGE + }), + BDFDB.ReactUtils.createElement(Internal.LibraryComponents.Select, { + className: BDFDB.disCN.dateinputfield, + value: this.props.language != null ? this.props.language : "$discord", + options: Object.keys(BDFDB.LanguageUtils.languages).map(id => ({ + value: id, + label: BDFDB.LanguageUtils.getName(BDFDB.LanguageUtils.languages[id]) + })), + searchable: true, + optionRenderer: lang => lang.label, + onChange: value => { + this.props.language = value == "$discord" ? undefined : value; + this.handleChange.apply(this, []); + BDFDB.ReactUtils.forceUpdate(formatter, preview); + } + }) + ] + }), + BDFDB.ReactUtils.createElement(Internal.LibraryComponents.Flex, { + align: Internal.LibraryComponents.Flex.Align.CENTER, + children: [ + BDFDB.ReactUtils.createElement(Internal.LibraryComponents.SettingsLabel, { + label: BDFDB.LanguageUtils.LibraryStrings.timezone + }), + BDFDB.ReactUtils.createElement(Internal.LibraryComponents.Select, { + className: BDFDB.disCN.dateinputfield, + value: this.props.timeOffset != null ? this.props.timeOffset : defaultOffset, + options: [-12.0, -11.0, -10.0, -9.5, -9.0, -8.0, -7.0, -6.0, -5.0, -4.0, -3.5, -3.0, -2.0, -1.0, 0.0, 1.0, 2.0, 3.0, 3.5, 4.0, 4.5, 5.0, 5.5, 5.75, 6.0, 6.5, 7.0, 8.0, 8.75, 9.0, 9.5, 10.0, 10.5, 11.0, 12.0, 12.75, 13.0, 14.0].map(offset => ({label: offset< 0 ? offset : `+${offset}`, value: offset})), + searchable: true, + onChange: value => { + this.props.timeOffset = value == defaultOffset ? undefined : value; + this.handleChange.apply(this, []); + BDFDB.ReactUtils.forceUpdate(formatter, preview); + } + }) + ] + }) + ] + }) + ] + }), + BDFDB.ReactUtils.createElement(class DateInputPreview extends Internal.LibraryModules.React.Component { + componentDidMount() {preview = this;} + render() { + return !input.props.noPreview && BDFDB.ReactUtils.createElement("div", { + className: BDFDB.disCN.dateinputpreview, + children: [ + input.props.prefix && BDFDB.ReactUtils.createElement("div", { + className: BDFDB.disCN.dateinputpreviewprefix, + children: typeof input.props.prefix == "function" ? input.props.prefix(input) : input.props.prefix, + }), + BDFDB.ReactUtils.createElement(Internal.LibraryComponents.TextScroller, { + children: Internal.LibraryComponents.DateInput.format(input.props, new Date((new Date()) - (1000*60*60*24*2))) + }), + input.props.suffix && BDFDB.ReactUtils.createElement("div", { + className: BDFDB.disCN.dateinputpreviewsuffix, + children: typeof input.props.suffix == "function" ? input.props.suffix(input) : input.props.suffix, + }) + ].filter(n => n) + }); + } + }) + ] + }) + ] + }), "onChange", "label", "formatString", "dateString", "timeString", "timeOffset", "language", "noPreview", "prefix", "suffix")); + } + }; + CustomComponents.DateInput.getDefaultString = function (language) { + language = language || BDFDB.LanguageUtils.getLanguage().id; + const date = new Date(); + return date.toLocaleString(language).replace(date.toLocaleDateString(language), "$date").replace(date.toLocaleTimeString(language, {hourCycle: "h12"}), "$time12").replace(date.toLocaleTimeString(language, {hourCycle: "h11"}), "$time12").replace(date.toLocaleTimeString(language, {hourCycle: "h24"}), "$time").replace(date.toLocaleTimeString(language, {hourCycle: "h23"}), "$time"); + }; + CustomComponents.DateInput.parseDate = function (date, offset) { + let timeObj = date; + if (typeof timeObj == "string") { + const language = BDFDB.LanguageUtils.getLanguage().id; + for (let i = 0; i < 12; i++) { + const tempDate = new Date(); + tempDate.setMonth(i); + timeObj = timeObj.replace(tempDate.toLocaleDateString(language, {month:"long"}), tempDate.toLocaleDateString("en", {month:"short"})); + } + timeObj = new Date(timeObj); + } + else if (typeof timeObj == "number") timeObj = new Date(timeObj); + + if (timeObj.toString() == "Invalid Date") timeObj = new Date(parseInt(date)); + if (timeObj.toString() == "Invalid Date" || typeof timeObj.toLocaleDateString != "function") timeObj = new Date(); + offset = offset != null && parseFloat(offset); + if ((offset || offset === 0) && !isNaN(offset)) timeObj = new Date(timeObj.getTime() + ((offset - timeObj.getTimezoneOffset() * (-1/60)) * 60*60*1000)); + return timeObj; + }; + CustomComponents.DateInput.format = function (data, time) { + if (typeof data == "string") data = {formatString: data}; + if (data && typeof data.formatString != "string") data.formatString = ""; + if (!data || typeof data.formatString != "string" || !time) return ""; + + const language = data.language || BDFDB.LanguageUtils.getLanguage().id; + const timeObj = Internal.LibraryComponents.DateInput.parseDate(time, data.timeOffset); + const now = new Date(); + const daysAgo = Math.round((Date.UTC(now.getFullYear(), now.getMonth(), now.getDate()) - Date.UTC(timeObj.getFullYear(), timeObj.getMonth(), timeObj.getDate()))/(1000*60*60*24)); + const date = data.dateString && typeof data.dateString == "string" ? Internal.LibraryComponents.DateInput.formatDate({dateString: data.dateString, language: language}, timeObj) : timeObj.toLocaleDateString(language); + + return (data.formatString || Internal.LibraryComponents.DateInput.getDefaultString(language)) + .replace(/\$date/g, date) + .replace(/\$time12/g, data.timeString && typeof data.timeString == "string" ? Internal.LibraryComponents.DateInput.formatTime({timeString: data.timeString, language: language}, timeObj, true) : timeObj.toLocaleTimeString(language, {hourCycle: "h12"})) + .replace(/\$time/g, data.timeString && typeof data.timeString == "string" ? Internal.LibraryComponents.DateInput.formatTime({timeString: data.timeString, language: language}, timeObj) : timeObj.toLocaleTimeString(language, {hourCycle: "h23"})) + .replace(/\$monthS/g, timeObj.toLocaleDateString(language, {month: "short"})) + .replace(/\$month/g, timeObj.toLocaleDateString(language, {month: "long"})) + .replace(/\$dayS/g, timeObj.toLocaleDateString(language, {weekday: "short"})) + .replace(/\$day/g, timeObj.toLocaleDateString(language, {weekday: "long"})) + .replace(/\$agoAmount/g, daysAgo < 0 ? "" : daysAgo > 1 ? Internal.DiscordObjects.Timestamp(timeObj.getTime()).fromNow() : BDFDB.LanguageUtils.LanguageStrings[`SEARCH_SHORTCUT_${daysAgo == 1 ? "YESTERDAY" : "TODAY"}`]) + .replace(/\$agoWeekdayS/g, daysAgo < 0 ? "" : daysAgo > 1 ? timeObj.toLocaleDateString(language, {weekday: "short"}) : BDFDB.LanguageUtils.LanguageStrings[`SEARCH_SHORTCUT_${daysAgo == 1 ? "YESTERDAY" : "TODAY"}`]) + .replace(/\$agoWeekday/g, daysAgo < 0 ? "" : daysAgo > 1 ? timeObj.toLocaleDateString(language, {weekday: "long"}) : BDFDB.LanguageUtils.LanguageStrings[`SEARCH_SHORTCUT_${daysAgo == 1 ? "YESTERDAY" : "TODAY"}`]) + .replace(/\$agoDays/g, daysAgo < 0 ? "" : daysAgo > 1 ? BDFDB.LanguageUtils.LanguageStringsFormat(`GAME_LIBRARY_LAST_PLAYED_DAYS`, daysAgo) : BDFDB.LanguageUtils.LanguageStrings[`SEARCH_SHORTCUT_${daysAgo == 1 ? "YESTERDAY" : "TODAY"}`]) + .replace(/\$agoDate/g, daysAgo < 0 ? "" : daysAgo > 1 ? date : BDFDB.LanguageUtils.LanguageStrings[`SEARCH_SHORTCUT_${daysAgo == 1 ? "YESTERDAY" : "TODAY"}`]) + .replace(/\(\)|\[\]/g, "").replace(/,\s*$|^\s*,/g, "").replace(/ +/g, " ").trim(); + }; + CustomComponents.DateInput.formatDate = function (data, time) { + if (typeof data == "string") data = {dateString: data}; + if (data && typeof data.dateString != "string") return ""; + if (!data || typeof data.dateString != "string" || !data.dateString || !time) return ""; + + const language = data.language || BDFDB.LanguageUtils.getLanguage().id; + const timeObj = Internal.LibraryComponents.DateInput.parseDate(time, data.timeOffset); + + return data.dateString + .replace(/\$monthS/g, timeObj.toLocaleDateString(language, {month: "short"})) + .replace(/\$month/g, timeObj.toLocaleDateString(language, {month: "long"})) + .replace(/\$dd/g, timeObj.toLocaleDateString(language, {day: "2-digit"})) + .replace(/\$d/g, timeObj.toLocaleDateString(language, {day: "numeric"})) + .replace(/\$mm/g, timeObj.toLocaleDateString(language, {month: "2-digit"})) + .replace(/\$m/g, timeObj.toLocaleDateString(language, {month: "numeric"})) + .replace(/\$yyyy/g, timeObj.toLocaleDateString(language, {year: "numeric"})) + .replace(/\$yy/g, timeObj.toLocaleDateString(language, {year: "2-digit"})) + .trim(); + }; + CustomComponents.DateInput.formatTime = function (data, time, hour12) { + if (typeof data == "string") data = {timeString: data}; + if (data && typeof data.timeString != "string") return ""; + if (!data || typeof data.timeString != "string" || !data.timeString || !time) return ""; + + const language = data.language || BDFDB.LanguageUtils.getLanguage().id; + const timeObj = Internal.LibraryComponents.DateInput.parseDate(time, data.timeOffset); + + let hours = timeObj.getHours(); + if (hour12) { + hours = hours == 0 ? 12 : hours; + if (hours > 12) hours -= 12; + } + const minutes = timeObj.getMinutes(); + const seconds = timeObj.getSeconds(); + const milli = timeObj.getMilliseconds(); + + let string = data.timeString + .replace(/\$hh/g, hours < 10 ? `0${hours}` : hours) + .replace(/\$h/g, hours) + .replace(/\$mm/g, minutes < 10 ? `0${minutes}` : minutes) + .replace(/\$m/g, minutes) + .replace(/\$ss/g, seconds < 10 ? `0${seconds}` : seconds) + .replace(/\$s/g, seconds) + .replace(/\$uu/g, milli < 10 ? `00${seconds}` : milli < 100 ? `0${milli}` : milli) + .replace(/\$u/g, milli) + .trim(); + + let digits = "\\d"; + if (BDFDB.LanguageUtils.languages[language] && BDFDB.LanguageUtils.languages[language].numberMap) { + digits = Object.entries(BDFDB.LanguageUtils.languages[language].numberMap).map(n => n[1]).join(""); + for (let number in BDFDB.LanguageUtils.languages[language].numberMap) string = string.replace(new RegExp(number, "g"), BDFDB.LanguageUtils.languages[language].numberMap[number]); + } + return hour12 ? timeObj.toLocaleTimeString(language, {hourCycle: "h12"}).replace(new RegExp(`[${digits}]{1,2}[^${digits}][${digits}]{1,2}[^${digits}][${digits}]{1,2}`, "g"), string) : string; + }; + + CustomComponents.EmojiPickerButton = reactInitialized && class BDFDB_EmojiPickerButton extends Internal.LibraryModules.React.Component { + handleEmojiChange(emoji) { + if (emoji != null) { + this.props.emoji = emoji.id ? { + id: emoji.id, + name: emoji.name, + animated: emoji.animated + } : { + id: null, + name: emoji.optionallyDiverseSequence, + animated: false + }; + if (typeof this.props.onSelect == "function") this.props.onSelect(this.props.emoji, this); + if (typeof this.close == "function" && !BDFDB.ListenerUtils.isPressed(16)) this.close(); + BDFDB.ReactUtils.forceUpdate(this); + } + } + render() { + let button = this; + return BDFDB.ReactUtils.createElement(Internal.LibraryComponents.PopoutContainer, { + children: BDFDB.ReactUtils.createElement(Internal.LibraryComponents.EmojiButton, { + className: BDFDB.DOMUtils.formatClassName(this.props.className, BDFDB.disCN.emojiinputbutton), + renderButtonContents: this.props.emoji ? _ => BDFDB.ReactUtils.createElement(Internal.LibraryComponents.Emoji, { + className: BDFDB.disCN.emoji, + emojiId: this.props.emoji.id, + emojiName: this.props.emoji.name + }) : null + }), + wrap: false, + animation: Internal.LibraryComponents.PopoutContainer.Animation.NONE, + position: Internal.LibraryComponents.PopoutContainer.Positions.TOP, + align: Internal.LibraryComponents.PopoutContainer.Align.LEFT, + renderPopout: instance => { + this.close = instance.close; + return [ + BDFDB.ReactUtils.createElement(Internal.LibraryComponents.EmojiPicker, { + closePopout: this.close, + onSelectEmoji: this.handleEmojiChange.bind(this), + allowManagedEmojis: this.props.allowManagedEmojis, + allowManagedEmojisUsage: this.props.allowManagedEmojisUsage + }), + BDFDB.ReactUtils.createElement(class extends Internal.LibraryModules.React.Component { + componentDidMount() {Internal.LibraryComponents.EmojiPickerButton.current = button;} + componentWillUnmount() {delete Internal.LibraryComponents.EmojiPickerButton.current;} + render() {return null;} + }) + ]; + } + }); + } + }; + Internal.setDefaultProps(CustomComponents.EmojiPickerButton, {allowManagedEmojis: false, allowManagedEmojisUsage: false}); + + CustomComponents.FavButton = reactInitialized && class BDFDB_FavButton extends Internal.LibraryModules.React.Component { + handleClick() { + this.props.isFavorite = !this.props.isFavorite; + if (typeof this.props.onClick == "function") this.props.onClick(this.props.isFavorite, this); + BDFDB.ReactUtils.forceUpdate(this); + } + render() { + return BDFDB.ReactUtils.createElement(Internal.LibraryComponents.Clickable, { + className: BDFDB.DOMUtils.formatClassName(BDFDB.disCN.favbuttoncontainer, BDFDB.disCN.favbutton, this.props.isFavorite && BDFDB.disCN.favbuttonselected, this.props.className), + onClick: this.handleClick.bind(this), + children: BDFDB.ReactUtils.createElement(Internal.LibraryComponents.SvgIcon, { + name: Internal.LibraryComponents.SvgIcon.Names[this.props.isFavorite ? "FAVORITE_FILLED" : "FAVORITE"], + width: this.props.width || 24, + height: this.props.height || 24, + className: BDFDB.disCN.favbuttonicon + }) + }); + } + }; + + CustomComponents.FileButton = reactInitialized && class BDFDB_FileButton extends Internal.LibraryModules.React.Component { + componentDidMount() { + if (this.props.searchFolders) { + let node = BDFDB.ReactUtils.findDOMNode(this); + if (node && (node = node.querySelector("input[type='file']")) != null) { + node.setAttribute("directory", ""); + node.setAttribute("webkitdirectory", ""); + } + } + } + render() { + let filter = this.props.filter && [this.props.filter].flat(10).filter(n => typeof n == "string") || []; + return BDFDB.ReactUtils.createElement(Internal.LibraryComponents.Button, BDFDB.ObjectUtils.exclude(Object.assign({}, this.props, { + onClick: e => {e.currentTarget.querySelector("input").click();}, + children: [ + BDFDB.LanguageUtils.LibraryStrings.file_navigator_text, + BDFDB.ReactUtils.createElement("input", { + type: "file", + accept: filter.length && (filter.join("/*,") + "/*"), + style: {display: "none"}, + onChange: e => { + let file = e.currentTarget.files[0]; + if (this.refInput && file && (!filter.length || filter.some(n => file.type.indexOf(n) == 0))) { + this.refInput.props.value = this.props.searchFolders ? file.path.split(file.name).slice(0, -1).join(file.name) : `${this.props.mode == "url" ? "url('" : ""}${(this.props.useFilePath) ? file.path : `data:${file.type};base64,${Internal.LibraryRequires.fs.readFileSync(file.path, "base64")}`}${this.props.mode ? "')" : ""}`; + BDFDB.ReactUtils.forceUpdate(this.refInput); + this.refInput.handleChange(this.refInput.props.value); + } + } + }) + ] + }), "filter", "mode", "useFilePath", "searchFolders")); + } + }; + + CustomComponents.FormComponents = {}; + CustomComponents.FormComponents.FormItem = reactInitialized && class BDFDB_FormItem extends Internal.LibraryModules.React.Component { + render() { + return BDFDB.ReactUtils.createElement("div", { + className: this.props.className, + style: this.props.style, + children: [ + BDFDB.ReactUtils.createElement(Internal.LibraryComponents.Flex, { + align: Internal.LibraryComponents.Flex.Align.BASELINE, + children: [ + this.props.title != null || this.props.error != null ? BDFDB.ReactUtils.createElement(Internal.LibraryComponents.Flex.Child, { + wrap: true, + children: BDFDB.ReactUtils.createElement(Internal.LibraryComponents.FormComponents.FormTitle, { + tag: this.props.tag || Internal.LibraryComponents.FormComponents.FormTags && Internal.LibraryComponents.FormComponents.FormTags.H5, + disabled: this.props.disabled, + required: this.props.required, + error: this.props.error, + className: this.props.titleClassName, + children: this.props.title + }) + }) : null + ].concat([this.props.titleChildren].flat(10)).filter(n => n) + }), + ].concat(this.props.children) + }); + } + }; + + CustomComponents.GuildSummaryItem = reactInitialized && class BDFDB_GuildSummaryItem extends Internal.LibraryModules.React.Component { + defaultRenderGuild(guild, isLast) { + if (!guild) return BDFDB.ReactUtils.createElement("div", { + className: BDFDB.disCN.guildsummaryemptyguild + }); + let icon = BDFDB.ReactUtils.createElement(Internal.LibraryComponents.GuildIcon, { + className: BDFDB.disCN.guildsummaryicon, + guild: guild, + showTooltip: this.props.showTooltip, + tooltipPosition: "top", + size: Internal.LibraryComponents.GuildIcon.Sizes.SMALLER + }); + return this.props.switchOnClick ? BDFDB.ReactUtils.createElement(Internal.LibraryComponents.Clickable, { + className: BDFDB.disCN.guildsummaryclickableicon, + onClick: _ => Internal.LibraryModules.HistoryUtils.transitionTo(Internal.DiscordConstants.Routes.CHANNEL(guild.id, Internal.LibraryStores.SelectedChannelStore.getChannelId(guild.id))), + key: guild.id, + tabIndex: -1, + children: icon + }) : icon; + } + renderGuilds() { + let elements = []; + let renderGuild = typeof this.props.renderGuild != "function" ? this.defaultRenderGuild : this.props.renderGuild; + let loaded = 0, max = this.props.guilds.length === this.props.max ? this.props.guilds.length : this.props.max - 1; + while (loaded < max && loaded < this.props.guilds.length) { + let isLast = loaded === this.props.guilds.length - 1; + let guild = renderGuild.apply(this, [this.props.guilds[loaded], isLast]); + elements.push(BDFDB.ReactUtils.createElement("div", { + className: isLast ? BDFDB.disCN.guildsummaryiconcontainer : BDFDB.disCN.guildsummaryiconcontainermasked, + children: guild + })); + loaded++; + } + if (loaded < this.props.guilds.length) { + let rest = Math.min(this.props.guilds.length - loaded, 99); + elements.push(BDFDB.ReactUtils.createElement(Internal.LibraryModules.React.Fragment, { + key: "more-guilds", + children: this.props.renderMoreGuilds("+" + rest, rest, this.props.guilds.slice(loaded), this.props) + })); + } + return elements; + } + renderIcon() { + return this.props.renderIcon ? BDFDB.ReactUtils.createElement(Internal.LibraryComponents.SvgIcon, { + name: Internal.LibraryComponents.SvgIcon.Names.WHATISTHIS, + className: BDFDB.disCN.guildsummarysvgicon + }) : null; + } + render() { + return BDFDB.ReactUtils.createElement("div", { + className: BDFDB.DOMUtils.formatClassName(this.props.className, BDFDB.disCN.guildsummarycontainer), + ref: this.props._ref, + children: [ + this.renderIcon.apply(this), + this.renderGuilds.apply(this) + ].flat(10).filter(n => n) + }); + } + }; + Internal.setDefaultProps(CustomComponents.GuildSummaryItem, {max: 10, renderMoreGuilds: (count, amount, restGuilds, props) => { + let icon = BDFDB.ReactUtils.createElement("div", {className: BDFDB.disCN.guildsummarymoreguilds, children: count}); + return props.showTooltip ? BDFDB.ReactUtils.createElement(Internal.LibraryComponents.TooltipContainer, { + text: restGuilds.map(guild => guild.name).join(", "), + children: icon + }) : icon; + }, renderIcon: false}); + + CustomComponents.GuildVoiceList = reactInitialized && class BDFDB_GuildVoiceList extends Internal.LibraryModules.React.Component { + render() { + let channels = Internal.LibraryStores.GuildChannelStore.getChannels(this.props.guild.id); + let voiceChannels = (channels.VOCAL || []).filter(c => c.channel.type == Internal.DiscordConstants.ChannelTypes.GUILD_VOICE).map(c => c.channel.id); + let stageChannels = (channels.VOCAL || []).filter(c => c.channel.type == Internal.DiscordConstants.ChannelTypes.GUILD_STAGE_VOICE && Internal.LibraryStores.StageInstanceStore.getStageInstanceByChannel(c.channel.id)).map(c => c.channel.id); + let streamOwnerIds = Internal.LibraryStores.ApplicationStreamingStore.getAllApplicationStreams().filter(app => app.guildId === this.props.guild.id).map(app => app.ownerId) || []; + let streamOwners = streamOwnerIds.map(ownerId => Internal.LibraryStores.UserStore.getUser(ownerId)).filter(n => n); + let voiceStates = BDFDB.ObjectUtils.toArray(Internal.LibraryStores.SortedVoiceStateStore.getVoiceStates(this.props.guild.id)).flat(10); + let connectedVoiceUsers = voiceStates.map(n => voiceChannels.includes(n.voiceState.channelId) && n.voiceState.channelId != this.props.guild.afkChannelId && !streamOwnerIds.includes(n.voiceState.userId) && Internal.LibraryStores.UserStore.getUser(n.voiceState.userId)).filter(n => n); + let connectedStageUsers = voiceStates.map(n => stageChannels.includes(n.voiceState.channelId) && n.voiceState.channelId != this.props.guild.afkChannelId && !streamOwnerIds.includes(n.voiceState.userId) && Internal.LibraryStores.UserStore.getUser(n.voiceState.userId)).filter(n => n); + let children = [ + !connectedStageUsers.length ? null : BDFDB.ReactUtils.createElement("div", { + className: BDFDB.disCN.tooltiprow, + children: [ + BDFDB.ReactUtils.createElement(Internal.LibraryComponents.SvgIcon, { + name: Internal.LibraryComponents.SvgIcon.Names.PODIUM, + className: BDFDB.disCN.tooltipactivityicon + }), + BDFDB.ReactUtils.createElement(Internal.LibraryComponents.UserSummaryItem, { + users: connectedStageUsers, + max: 6 + }) + ] + }), + !connectedVoiceUsers.length ? null : BDFDB.ReactUtils.createElement("div", { + className: BDFDB.disCN.tooltiprow, + children: [ + BDFDB.ReactUtils.createElement(Internal.LibraryComponents.SvgIcon, { + name: Internal.LibraryComponents.SvgIcon.Names.SPEAKER, + className: BDFDB.disCN.tooltipactivityicon + }), + BDFDB.ReactUtils.createElement(Internal.LibraryComponents.UserSummaryItem, { + users: connectedVoiceUsers, + max: 6 + }) + ] + }), + !streamOwners.length ? null : BDFDB.ReactUtils.createElement("div", { + className: BDFDB.disCN.tooltiprow, + children: [ + BDFDB.ReactUtils.createElement(Internal.LibraryComponents.SvgIcon, { + name: Internal.LibraryComponents.SvgIcon.Names.STREAM, + className: BDFDB.disCN.tooltipactivityicon + }), + BDFDB.ReactUtils.createElement(Internal.LibraryComponents.UserSummaryItem, { + users: streamOwners, + max: 6 + }) + ] + }) + ].filter(n => n); + return !children.length ? null : BDFDB.ReactUtils.createElement("div", { + className: BDFDB.disCN.guildvoicelist, + children: children + }); + } + }; + + CustomComponents.KeybindRecorder = reactInitialized && class BDFDB_KeybindRecorder extends Internal.LibraryModules.React.Component { + handleChange(arrays) { + this.props.value = arrays.map(platformKey => Internal.LibraryModules.KeyEvents.codes[Internal.LibraryModules.KeyCodeUtils.codeToKey(platformKey)] || platformKey[1]); + if (typeof this.props.onChange == "function") this.props.onChange(this.props.value, this); + } + handleReset() { + this.props.value = []; + if (this.recorder) this.recorder.setState({codes: []}); + if (typeof this.props.onChange == "function") this.props.onChange([], this); + if (typeof this.props.onReset == "function") this.props.onReset(this); + } + componentDidMount() { + if (!this.recorder) this.recorder = BDFDB.ReactUtils.findOwner(this, {name: "KeybindRecorder"}); + } + render() { + return BDFDB.ReactUtils.createElement(Internal.LibraryComponents.Flex, { + className: BDFDB.disCN.hotkeywrapper, + direction: Internal.LibraryComponents.Flex.Direction.HORIZONTAL, + align: Internal.LibraryComponents.Flex.Align.CENTER, + children: [ + BDFDB.ReactUtils.createElement(Internal.NativeSubComponents.KeybindRecorder, BDFDB.ObjectUtils.exclude(Object.assign({}, this.props, { + defaultValue: [this.props.defaultValue || this.props.value].flat(10).filter(n => n).map(keyCode => [Internal.DiscordConstants.KeyboardDeviceTypes.KEYBOARD_KEY, Internal.LibraryModules.KeyCodeUtils.keyToCode((Object.entries(Internal.LibraryModules.KeyEvents.codes).find(n => n[1] == keyCode && Internal.LibraryModules.KeyCodeUtils.keyToCode(n[0], null)) || [])[0], null) || keyCode]), + onChange: this.handleChange.bind(this) + }), "reset", "onReset")), + this.props.reset || this.props.onReset ? BDFDB.ReactUtils.createElement(Internal.LibraryComponents.TooltipContainer, { + text: BDFDB.LanguageUtils.LanguageStrings.REMOVE_KEYBIND, + tooltipConfig: {type: "top"}, + children: BDFDB.ReactUtils.createElement(Internal.LibraryComponents.Clickable, { + className: BDFDB.disCN.hotkeyresetbutton, + onClick: this.handleReset.bind(this), + children: BDFDB.ReactUtils.createElement(Internal.LibraryComponents.SvgIcon, { + iconSVG: ``, + }) + }) + }) : null + ].filter(n => n) + }); + } + }; + + CustomComponents.ListRow = reactInitialized && class BDFDB_ListRow extends Internal.LibraryModules.React.Component { + render() { + return BDFDB.ReactUtils.createElement("div", BDFDB.ObjectUtils.exclude(Object.assign({}, this.props, { + className: BDFDB.DOMUtils.formatClassName(BDFDB.disCN.listrowwrapper, this.props.className, BDFDB.disCN.listrow), + children: [ + this.props.prefix, + BDFDB.ReactUtils.createElement("div", { + className: BDFDB.disCN.listrowcontent, + style: {flex: "1 1 auto"}, + children: [ + BDFDB.ReactUtils.createElement("div", { + className: BDFDB.DOMUtils.formatClassName(BDFDB.disCN.listname, this.props.labelClassName), + style: {flex: "1 1 auto"}, + children: this.props.label + }), + typeof this.props.note == "string" ? BDFDB.ReactUtils.createElement(Internal.LibraryComponents.FormComponents.FormText, { + type: Internal.LibraryComponents.FormComponents.FormText.Types.DESCRIPTION, + children: this.props.note + }) : null + ].filter(n => n) + }), + this.props.suffix + ].filter(n => n) + }), "label", "note", "suffix", "prefix", "labelClassName")); + } + }; + + CustomComponents.MemberRole = reactInitialized && class BDFDB_MemberRole extends Internal.LibraryModules.React.Component { + handleClick(e) {if (typeof this.props.onClick == "function") this.props.onClick(e, this);} + handleContextMenu(e) {if (typeof this.props.onContextMenu == "function") this.props.onContextMenu(e, this);} + render() { + let color = BDFDB.ColorUtils.convert(this.props.role.colorString, "RGB") || Internal.DiscordConstants.Colors.PRIMARY_300; + return BDFDB.ReactUtils.createElement("li", { + className: BDFDB.DOMUtils.formatClassName(BDFDB.disCN.userrole, this.props.className), + style: {borderColor: BDFDB.ColorUtils.setAlpha(color, 0.6)}, + onClick: this.handleClick.bind(this), + onContextMenu: this.handleContextMenu.bind(this), + children: [ + !this.props.noCircle ? BDFDB.ReactUtils.createElement("div", { + className: BDFDB.disCN.userroleremovebutton, + children: BDFDB.ReactUtils.createElement("span", { + className: BDFDB.disCN.userrolecircle, + style: {backgroundColor: color} + }) + }) : null, + BDFDB.ReactUtils.createElement("div", { + className: BDFDB.disCN.userrolename, + children: this.props.role.name + }) + ].filter(n => n) + }); + } + }; + + CustomComponents.MenuItems = {}; + CustomComponents.MenuItems.MenuCheckboxItem = reactInitialized && class BDFDB_MenuCheckboxItem extends Internal.LibraryModules.React.Component { + handleClick() { + if (this.props.state) { + this.props.state.checked = !this.props.state.checked; + if (typeof this.props.action == "function") this.props.action(this.props.state.checked, this); + } + BDFDB.ReactUtils.forceUpdate(this); + } + render() { + return BDFDB.ReactUtils.createElement(Internal.MenuItem, Object.assign({}, this.props, { + input: this.props.state && this.props.state.checked ? BDFDB.ReactUtils.createElement(Internal.LibraryComponents.SvgIcon, { + className: BDFDB.disCN.menuicon, + background: BDFDB.disCN.menucheckbox, + foreground: BDFDB.disCN.menucheck, + name: Internal.LibraryComponents.SvgIcon.Names.CHECKBOX + }) : BDFDB.ReactUtils.createElement(Internal.LibraryComponents.SvgIcon, { + className: BDFDB.disCN.menuicon, + name: Internal.LibraryComponents.SvgIcon.Names.CHECKBOX_EMPTY + }), + action: this.handleClick.bind(this) + })); + } + }; + + CustomComponents.MenuItems.MenuHint = reactInitialized && class BDFDB_MenuHint extends Internal.LibraryModules.React.Component { + render() { + return !this.props.hint ? null : BDFDB.ReactUtils.createElement("div", { + className: BDFDB.disCN.menuhint, + children: BDFDB.ReactUtils.createElement(Internal.LibraryComponents.TextScroller, { + children: this.props.hint + }) + }); + } + }; + + CustomComponents.MenuItems.MenuIcon = reactInitialized && class BDFDB_MenuIcon extends Internal.LibraryModules.React.Component { + render() { + let isString = typeof this.props.icon == "string"; + return !this.props.icon ? null : BDFDB.ReactUtils.createElement(Internal.LibraryComponents.SvgIcon, { + className: BDFDB.disCN.menuicon, + nativeClass: true, + iconSVG: isString ? this.props.icon : null, + name: !isString ? this.props.icon : null + }); + } + }; + + CustomComponents.MenuItems.MenuControlItem = function (props) { + let effectRef = BDFDB.ReactUtils.useRef(null); + let controlRef = BDFDB.ReactUtils.useRef(null); + + BDFDB.ReactUtils.useLayoutEffect((_ => { + if (props.isFocused) { + BDFDB.LibraryStores.AccessibilityStore.keyboardModeEnabled && controlRef.current && controlRef.current.scrollIntoView({ + block: "nearest" + }); + controlRef.current && controlRef.current.focus(); + } + else controlRef.current && controlRef.current.blur && controlRef.current.blur(controlRef.current); + }), [props.isFocused]); + + return BDFDB.ReactUtils.createElement("div", Object.assign({ + className: BDFDB.DOMUtils.formatClassName(BDFDB.disCN.menuitem, BDFDB.disCN[`menucolor${(props.color && InternalData.DiscordClasses[`menucolor${props.color.toLowerCase()}`] || Internal.DiscordConstants.MenuItemColors.DEFAULT || "").toLowerCase()}`], props.disabled && BDFDB.disCN.menudisabled, props.showDefaultFocus && props.isFocused && BDFDB.disCN.menufocused, !props.showDefaultFocus && BDFDB.disCN.menuhideinteraction), + onClick: BDFDB.ReactUtils.useCallback((_ => { + if (!controlRef.current || !controlRef.current.activate || !controlRef.current.activate.call(controlRef.current)) typeof props.onClose == "function" && props.onClose(); + }), [props.onClose]), + "aria-disabled": props.disabled, + children: [ + props.label && BDFDB.ReactUtils.createElement("div", { + className: BDFDB.disCN.menulabelcontainer, + children: BDFDB.ReactUtils.createElement("div", { + className: BDFDB.disCN.menulabel, + children: props.label + }) + }), + typeof props.control == "function" && props.control({ + onClose: props.onClose, + disabled: props.disabled, + isFocused: props.isFocused + }, controlRef) + ] + }, props.menuItemProps)); + }; + + CustomComponents.MenuItems.MenuSliderItem = reactInitialized && class BDFDB_MenuSliderItem extends Internal.LibraryModules.React.Component { + handleValueChange(value) { + if (this.props.state) { + this.props.state.value = Math.round(BDFDB.NumberUtils.mapRange([0, 100], [this.props.minValue, this.props.maxValue], value) * Math.pow(10, this.props.digits)) / Math.pow(10, this.props.digits); + if (typeof this.props.onValueChange == "function") this.props.onValueChange(this.props.state.value, this); + } + BDFDB.ReactUtils.forceUpdate(this); + } + handleValueRender(value) { + let newValue = Math.round(BDFDB.NumberUtils.mapRange([0, 100], [this.props.minValue, this.props.maxValue], value) * Math.pow(10, this.props.digits)) / Math.pow(10, this.props.digits); + if (typeof this.props.onValueRender == "function") { + let tempReturn = this.props.onValueRender(newValue, this); + if (tempReturn != undefined) newValue = tempReturn; + } + return newValue; + } + render() { + let value = this.props.state && this.props.state.value || 0; + return BDFDB.ReactUtils.createElement(Internal.LibraryComponents.MenuItems.MenuControlItem, BDFDB.ObjectUtils.exclude(Object.assign({}, this.props, { + label: typeof this.props.renderLabel == "function" ? this.props.renderLabel(Math.round(value * Math.pow(10, this.props.digits)) / Math.pow(10, this.props.digits), this) : this.props.label, + control: (menuItemProps, ref) => { + return BDFDB.ReactUtils.createElement("div", { + className: BDFDB.disCN.menuslidercontainer, + children: BDFDB.ReactUtils.createElement(Internal.NativeSubComponents.Slider, Object.assign({}, menuItemProps, { + ref: ref, + className: BDFDB.disCN.menuslider, + mini: true, + initialValue: Math.round(BDFDB.NumberUtils.mapRange([this.props.minValue, this.props.maxValue], [0, 100], value) * Math.pow(10, this.props.digits)) / Math.pow(10, this.props.digits), + onValueChange: this.handleValueChange.bind(this), + onValueRender: this.handleValueRender.bind(this) + })) + }); + } + }), "digits", "renderLabel")); + } + }; + Internal.setDefaultProps(CustomComponents.MenuItems.MenuSliderItem, {minValue: 0, maxValue: 100, digits: 0}); + + CustomComponents.ModalComponents = {}; + CustomComponents.ModalComponents.ModalContent = reactInitialized && class BDFDB_ModalContent extends Internal.LibraryModules.React.Component { + render() { + return this.props.scroller ? BDFDB.ReactUtils.createElement(Internal.LibraryComponents.Scrollers.Thin, { + className: BDFDB.DOMUtils.formatClassName(BDFDB.disCN.modalcontent, this.props.className), + ref: this.props.scrollerRef, + children: this.props.children + }) : BDFDB.ReactUtils.createElement(Internal.LibraryComponents.Flex, { + className: BDFDB.DOMUtils.formatClassName(this.props.content && BDFDB.disCN.modalcontent, BDFDB.disCN.modalnoscroller, this.props.className), + direction: this.props.direction || Internal.LibraryComponents.Flex.Direction.VERTICAL, + align: Internal.LibraryComponents.Flex.Align.STRETCH, + children: this.props.children + }); + } + }; + Internal.setDefaultProps(CustomComponents.ModalComponents.ModalContent, {scroller: true, content: true}); + + CustomComponents.ModalComponents.ModalTabContent = reactInitialized && class BDFDB_ModalTabContent extends Internal.LibraryModules.React.Component { + render() { + return !this.props.open ? null : BDFDB.ReactUtils.createElement(this.props.scroller ? Internal.LibraryComponents.Scrollers.Thin : "div", Object.assign(BDFDB.ObjectUtils.exclude(this.props, "scroller", "open"), { + className: BDFDB.DOMUtils.formatClassName(BDFDB.disCN.modaltabcontent, this.props.open && BDFDB.disCN.modaltabcontentopen, this.props.className), + children: this.props.children + })); + } + }; + Internal.setDefaultProps(CustomComponents.ModalComponents.ModalTabContent, {tab: "unnamed"}); + + CustomComponents.ModalComponents.ModalFooter = reactInitialized && class BDFDB_ModalFooter extends Internal.LibraryModules.React.Component { + render() { + return BDFDB.ReactUtils.createElement(Internal.LibraryComponents.Flex, { + className: BDFDB.DOMUtils.formatClassName(BDFDB.disCN.modalfooter, this.props.className), + direction: this.props.direction || Internal.LibraryComponents.Flex.Direction.HORIZONTAL_REVERSE, + align: Internal.LibraryComponents.Flex.Align.STRETCH, + grow: 0, + shrink: 0, + children: this.props.children + }); + } + }; + + CustomComponents.MultiInput = reactInitialized && class BDFDB_MultiInput extends Internal.LibraryModules.React.Component { + constructor(props) { + super(props); + this.state = {focused: false}; + } + render() { + if (this.props.children && this.props.children.props) this.props.children.props.className = BDFDB.DOMUtils.formatClassName(this.props.children.props.className, BDFDB.disCN.inputmultifield); + return BDFDB.ReactUtils.createElement("div", { + className: BDFDB.DOMUtils.formatClassName(this.props.className, BDFDB.disCN.inputwrapper, BDFDB.disCN.inputmultiwrapper), + children: BDFDB.ReactUtils.createElement("div", { + className: BDFDB.DOMUtils.formatClassName(BDFDB.disCN.input, BDFDB.disCN.inputmulti, this.state.focused && BDFDB.disCN.inputfocused), + children: [ + BDFDB.ReactUtils.createElement("div", { + className: BDFDB.DOMUtils.formatClassName(this.props.innerClassName, BDFDB.disCN.inputwrapper, BDFDB.disCN.inputmultifirst), + children: this.props.children + }), + BDFDB.ReactUtils.createElement(Internal.LibraryComponents.TextInput, BDFDB.ObjectUtils.exclude(Object.assign({}, this.props, { + className: BDFDB.disCN.inputmultilast, + inputClassName: BDFDB.disCN.inputmultifield, + onFocus: e => this.setState({focused: true}), + onBlur: e => this.setState({focused: false}) + }), "children", "innerClassName")) + ] + }) + }); + } + }; + + CustomComponents.ListInput = reactInitialized && class BDFDB_ListInput extends Internal.LibraryModules.React.Component { + handleChange() { + if (typeof this.props.onChange) this.props.onChange(this.props.items, this); + } + render() { + if (!BDFDB.ArrayUtils.is(this.props.items)) this.props.items = []; + return BDFDB.ReactUtils.createElement(Internal.LibraryComponents.MultiInput, BDFDB.ObjectUtils.exclude(Object.assign({}, this.props, { + className: BDFDB.disCN.inputlist, + innerClassName: BDFDB.disCN.inputlistitems, + onKeyDown: e => { + if (e.which == 13 && e.target.value && e.target.value.trim()) { + let value = e.target.value.trim(); + this.props.value = ""; + if (!this.props.items.includes(value)) { + this.props.items.push(value); + BDFDB.ReactUtils.forceUpdate(this); + this.handleChange.apply(this, []); + } + } + }, + children: this.props.items.map(item => BDFDB.ReactUtils.createElement(Internal.LibraryComponents.Badges.TextBadge, { + className: BDFDB.disCN.inputlistitem, + color: "var(--bdfdb-blurple)", + style: {borderRadius: "3px"}, + text: [ + item, + BDFDB.ReactUtils.createElement(Internal.LibraryComponents.SvgIcon, { + className: BDFDB.disCN.inputlistdelete, + name: Internal.LibraryComponents.SvgIcon.Names.CLOSE, + onClick: _ => { + BDFDB.ArrayUtils.remove(this.props.items, item); + BDFDB.ReactUtils.forceUpdate(this); + this.handleChange.apply(this, []); + } + }) + ] + })) + }), "items")); + } + }; + + CustomComponents.PaginatedList = reactInitialized && class BDFDB_PaginatedList extends Internal.LibraryModules.React.Component { + constructor(props) { + super(props); + this.state = { + offset: props.offset + }; + } + handleJump(offset) { + if (offset > -1 && offset < Math.ceil(this.props.items.length/this.props.amount) && this.state.offset != offset) { + this.state.offset = offset; + if (typeof this.props.onJump == "function") this.props.onJump(offset, this); + BDFDB.ReactUtils.forceUpdate(this); + } + } + renderPagination(bottom) { + let maxOffset = Math.ceil(this.props.items.length/this.props.amount) - 1; + return this.props.items.length > this.props.amount && BDFDB.ReactUtils.createElement("nav", { + className: BDFDB.DOMUtils.formatClassName(BDFDB.disCN.pagination, bottom ? BDFDB.disCN.paginationbottom : BDFDB.disCN.paginationtop, this.props.mini && BDFDB.disCN.paginationmini), + children: [ + BDFDB.ReactUtils.createElement(Internal.LibraryComponents.Paginator, { + totalCount: this.props.items.length, + currentPage: this.state.offset + 1, + pageSize: this.props.amount, + maxVisiblePages: this.props.maxVisiblePages, + onPageChange: page => {this.handleJump(isNaN(parseInt(page)) ? -1 : page - 1);} + }), + this.props.jump && BDFDB.ReactUtils.createElement(Internal.LibraryComponents.TextInput, { + type: "number", + size: Internal.LibraryComponents.TextInput.Sizes.MINI, + value: this.state.offset + 1, + min: 1, + max: maxOffset + 1, + onKeyDown: (event, instance) => {if (event.which == 13) this.handleJump(isNaN(parseInt(instance.props.value)) ? -1 : instance.props.value - 1);} + }), + ].filter(n => n) + }); + } + render() { + let items = [], alphabet = {}; + if (BDFDB.ArrayUtils.is(this.props.items) && this.props.items.length) { + if (!this.props.alphabetKey) items = this.props.items; + else { + let unsortedItems = [].concat(this.props.items); + for (let key of ["0-9", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"]) { + let numbers = key == "0-9", alphaItems = []; + for (let item of unsortedItems) if (item && item[this.props.alphabetKey] && (numbers && !isNaN(parseInt(item[this.props.alphabetKey][0])) || item[this.props.alphabetKey].toUpperCase().indexOf(key) == 0)) alphaItems.push(item); + for (let sortedItem of alphaItems) BDFDB.ArrayUtils.remove(unsortedItems, sortedItem); + alphabet[key] = {items: BDFDB.ArrayUtils.keySort(alphaItems, this.props.alphabetKey), disabled: !alphaItems.length}; + } + alphabet["?!"] = {items: BDFDB.ArrayUtils.keySort(unsortedItems, this.props.alphabetKey), disabled: !unsortedItems.length}; + for (let key in alphabet) items.push(alphabet[key].items); + items = items.flat(10); + } + } + return typeof this.props.renderItem != "function" || !items.length ? null : BDFDB.ReactUtils.createElement(Internal.LibraryComponents.Scrollers.Thin, { + className: BDFDB.DOMUtils.formatClassName(this.props.className, BDFDB.disCN.paginationlist, this.props.mini && BDFDB.disCN.paginationlistmini), + fade: this.props.fade, + children: [ + this.renderPagination(), + items.length > this.props.amount && this.props.alphabetKey && BDFDB.ReactUtils.createElement("nav", { + className: BDFDB.disCN.paginationlistalphabet, + children: Object.keys(alphabet).map(key => BDFDB.ReactUtils.createElement(Internal.LibraryComponents.Clickable, { + className: BDFDB.DOMUtils.formatClassName(BDFDB.disCN.paginationlistalphabetchar, alphabet[key].disabled &&BDFDB.disCN.paginationlistalphabetchardisabled), + onClick: _ => {if (!alphabet[key].disabled) this.handleJump(Math.floor(items.indexOf(alphabet[key].items[0])/this.props.amount));}, + children: key + })) + }), + this.props.header, + BDFDB.ReactUtils.createElement("div", { + className: BDFDB.disCN.paginationlistcontent, + children: items.slice(this.state.offset * this.props.amount, (this.state.offset + 1) * this.props.amount).map((data, i) => {return this.props.renderItem(data, i);}).flat(10).filter(n => n) + }), + this.props.copyToBottom && this.renderPagination(true) + ].flat(10).filter(n => n) + }); + } + }; + Internal.setDefaultProps(CustomComponents.PaginatedList, {amount: 50, offset: 0, mini: true, jump: true, maxVisiblePages: 7, copyToBottom: false, fade: true}); + + CustomComponents.Popout = reactInitialized && class BDFDB_Popout extends Internal.LibraryModules.React.Component { + componentDidMount() { + this.props.containerInstance.popout = this; + if (typeof this.props.onOpen == "function") this.props.onOpen(this.props.containerInstance, this); + } + componentWillUnmount() { + delete this.props.containerInstance.popout; + if (typeof this.props.onClose == "function") this.props.onClose(this.props.containerInstance, this); + } + render() { + if (!this.props.wrap) return this.props.children; + let pos = typeof this.props.position == "string" ? this.props.position.toLowerCase() : null; + return BDFDB.ReactUtils.createElement(Internal.LibraryComponents.PopoutFocusLock, { + className: BDFDB.DOMUtils.formatClassName(BDFDB.disCN.popoutwrapper, this.props.className, this.props.themed && BDFDB.disCN.popoutthemedpopout, this.props.arrow && BDFDB.disCN.popoutarrow, this.props.arrow && (pos == "top" ? BDFDB.disCN.popoutarrowtop : BDFDB.disCN.popoutarrowbottom)), + id: this.props.id, + onClick: e => e.stopPropagation(), + style: BDFDB.ObjectUtils.extract(this.props, "padding", "height", "maxHeight", "minHeight", "width", "maxWidth", "minWidth"), + children: this.props.children + }); + } + }; + Internal.setDefaultProps(CustomComponents.Popout, {themed: true, wrap: true}); + + CustomComponents.PopoutContainer = reactInitialized && class BDFDB_PopoutContainer extends Internal.LibraryModules.React.Component { + componentDidMount() { + this.toggle = this.toggle.bind(this); + this.onDocumentClicked = this.onDocumentClicked.bind(this); + this.domElementRef = BDFDB.ReactUtils.createRef(); + this.domElementRef.current = BDFDB.ReactUtils.findDOMNode(this); + } + onDocumentClicked() { + const node = BDFDB.ReactUtils.findDOMNode(this.popout); + if (!node || !document.contains(node) || node != event.target && document.contains(event.target) && !node.contains(event.target)) this.toggle(false); + } + toggle(forceState) { + this.props.open = forceState != undefined ? forceState : !this.props.open; + BDFDB.ReactUtils.forceUpdate(this); + } + render() { + if (!this.props._rendered) { + this.props._rendered = true; + const child = (BDFDB.ArrayUtils.is(this.props.children) ? this.props.children[0] : this.props.children) || BDFDB.ReactUtils.createElement("div", {style: {height: "100%", width: "100%"}}); + child.props.className = BDFDB.DOMUtils.formatClassName(child.props.className, this.props.className); + const childProps = Object.assign({}, child.props); + child.props.onClick = (e, childThis) => { + if ((this.props.openOnClick || this.props.openOnClick === undefined)) this.toggle(); + if (typeof this.props.onClick == "function") this.props.onClick(e, this); + if (typeof childProps.onClick == "function") childProps.onClick(e, childThis); + if (this.props.killEvent || childProps.killEvent) BDFDB.ListenerUtils.stopEvent(e); + }; + child.props.onContextMenu = (e, childThis) => { + if (this.props.openOnContextMenu) this.toggle(); + if (typeof this.props.onContextMenu == "function") this.props.onContextMenu(e, this); + if (typeof childProps.onContextMenu == "function") childProps.onContextMenu(e, childThis); + if (this.props.killEvent || childProps.killEvent) BDFDB.ListenerUtils.stopEvent(e); + }; + this.props.children = child; + } + return BDFDB.ReactUtils.createElement(Internal.LibraryModules.React.Fragment, { + children: [ + this.props.children, + this.props.open && BDFDB.ReactUtils.createElement(Internal.LibraryComponents.AppReferencePositionLayer, { + onMount: _ => BDFDB.TimeUtils.timeout(_ => document.addEventListener("click", this.onDocumentClicked)), + onUnmount: _ => document.removeEventListener("click", this.onDocumentClicked), + position: this.props.position, + align: this.props.align, + reference: this.domElementRef, + children: _ => { + const popout = BDFDB.ReactUtils.createElement(Internal.LibraryComponents.Popout, BDFDB.ObjectUtils.exclude(Object.assign({}, this.props, { + className: this.props.popoutClassName, + containerInstance: this, + position: this.props.position, + style: this.props.popoutStyle, + onOpen: typeof this.props.onOpen == "function" ? this.props.onOpen.bind(this) : _ => {}, + onClose: typeof this.props.onClose == "function" ? this.props.onClose.bind(this) : _ => {}, + children: typeof this.props.renderPopout == "function" ? this.props.renderPopout(this) : null + }), "popoutStyle", "popoutClassName", "shouldShow", "changing", "renderPopout", "openOnClick", "onClick", "openOnContextMenu", "onContextMenu")); + const animation = Object.entries(Internal.LibraryComponents.PopoutContainer.Animation).find(n => n[1] == this.props.animation); + return !animation || animation[0] == Internal.LibraryComponents.PopoutContainer.Animation.NONE ? popout : BDFDB.ReactUtils.createElement(Internal.LibraryComponents.PopoutCSSAnimator, { + position: this.props.position, + type: Internal.LibraryComponents.PopoutCSSAnimator.Types[animation[0]], + children: popout + }); + } + }) + ] + }); + } + }; + CustomComponents.PopoutContainer.Align = { + BOTTOM: "bottom", + CENTER: "center", + LEFT: "left", + RIGHT: "right", + TOP: "top" + }; + CustomComponents.PopoutContainer.Positions = { + BOTTOM: "bottom", + CENTER: "center", + LEFT: "left", + RIGHT: "right", + TOP: "top", + WINDOW_CENTER: "window_center" + }; + CustomComponents.PopoutContainer.ObjectProperties = ["Animation"]; + Internal.setDefaultProps(CustomComponents.PopoutContainer, {wrap: true}); + + CustomComponents.PopoutCSSAnimator = function (props) { + let positionState = BDFDB.ReactUtils.useState(props.position != null); + let animationState = BDFDB.ReactUtils.useState((_ => new Internal.LibraryComponents.Timeout)); + BDFDB.ReactUtils.useEffect((_ => (_ => animationState[0].stop())), [animationState[0]]); + BDFDB.ReactUtils.useEffect(_ => (props.position && animationState[0].start(10, (_ => positionState[1](true)))), [props.position, animationState[0]]); + const position = typeof props.position == "string" && props.position.replace("window_", ""); + const animation = (Object.entries(Internal.LibraryComponents.PopoutContainer.Animation).find(n => n[1] == props.animation) || ["NONE"])[0].toLowerCase(); + return BDFDB.ReactUtils.createElement("div", { + className: BDFDB.DOMUtils.formatClassName(InternalData.DiscordClasses[`animationcontainer${position}`] && BDFDB.disCN[`animationcontainer${position}`], InternalData.DiscordClasses[`animationcontainer${animation}`] && BDFDB.disCN[`animationcontainer${animation}`], positionState[0] && BDFDB.disCN.animationcontainerrender), + children: props.children + }) + }; + CustomComponents.PopoutCSSAnimator.Types = { + "1": "TRANSLATE", + "2": "SCALE", + "3": "FADE", + "TRANSLATE": "1", + "SCALE": "2", + "FADE": "3" + }; + + CustomComponents.QuickSelect = reactInitialized && class BDFDB_QuickSelect extends Internal.LibraryModules.React.Component { + handleChange(option) { + this.props.value = option; + if (typeof this.props.onChange == "function") this.props.onChange(option.value || option.key, this); + BDFDB.ReactUtils.forceUpdate(this); + } + render() { + let options = (BDFDB.ArrayUtils.is(this.props.options) ? this.props.options : [{}]).filter(n => n); + let selectedOption = BDFDB.ObjectUtils.is(this.props.value) ? this.props.value : (options[0] || {}); + return BDFDB.ReactUtils.createElement("div", { + className: BDFDB.DOMUtils.formatClassName(this.props.className, BDFDB.disCN.quickselectwrapper), + children: BDFDB.ReactUtils.createElement(Internal.LibraryComponents.Flex, { + className: BDFDB.disCN.quickselect, + align: Internal.LibraryComponents.Flex.Align.CENTER, + children: [ + BDFDB.ReactUtils.createElement("div", { + className: BDFDB.disCN.quickselectlabel, + children: this.props.label + }), + BDFDB.ReactUtils.createElement(Internal.LibraryComponents.Flex, { + align: Internal.LibraryComponents.Flex.Align.CENTER, + className: BDFDB.disCN.quickselectclick, + onClick: event => { + Internal.LibraryModules.ContextMenuUtils.openContextMenu(event, _ => BDFDB.ReactUtils.createElement(Internal.LibraryComponents.Menu, { + navId: "bdfdb-quickselect", + onClose: Internal.LibraryModules.ContextMenuUtils.closeContextMenu, + className: this.props.popoutClassName, + children: BDFDB.ContextMenuUtils.createItem(Internal.LibraryComponents.MenuItems.MenuGroup, { + children: options.map((option, i) => { + let selected = option.value && option.value === selectedOption.value || option.key && option.key === selectedOption.key; + return BDFDB.ContextMenuUtils.createItem(Internal.LibraryComponents.MenuItems.MenuItem, { + label: option.label, + id: BDFDB.ContextMenuUtils.createItemId("option", option.key || option.value || i), + action: selected ? null : event2 => this.handleChange.bind(this)(option) + }); + }) + }) + })); + }, + children: [ + BDFDB.ReactUtils.createElement("div", { + className: BDFDB.disCN.quickselectvalue, + children: typeof this.props.renderValue == "function" ? this.props.renderValue(this.props.value) : this.props.value.label + }), + BDFDB.ReactUtils.createElement("div", { + className: BDFDB.disCN.quickselectarrow + }) + ] + }) + ] + }) + }); + } + }; + + CustomComponents.RadioGroup = reactInitialized && class BDFDB_RadioGroup extends Internal.LibraryModules.React.Component { + handleChange(value) { + this.props.value = value.value; + if (typeof this.props.onChange == "function") this.props.onChange(value, this); + BDFDB.ReactUtils.forceUpdate(this); + } + render() { + return BDFDB.ReactUtils.createElement(Internal.NativeSubComponents.RadioGroup, Object.assign({}, this.props, { + onChange: this.handleChange.bind(this) + })); + } + }; + + CustomComponents.SearchBar = reactInitialized && class BDFDB_SearchBar extends Internal.LibraryModules.React.Component { + handleChange(query) { + this.props.query = query; + if (typeof this.props.onChange == "function") this.props.onChange(query, this); + BDFDB.ReactUtils.forceUpdate(this); + } + handleClear() { + this.props.query = ""; + if (this.props.changeOnClear && typeof this.props.onChange == "function") this.props.onChange("", this); + if (typeof this.props.onClear == "function") this.props.onClear(this); + BDFDB.ReactUtils.forceUpdate(this); + } + render() { + let props = Object.assign({}, this.props, { + onChange: this.handleChange.bind(this), + onClear: this.handleClear.bind(this) + }); + if (typeof props.query != "string") props.query = ""; + return BDFDB.ReactUtils.createElement(Internal.NativeSubComponents.SearchBar, props); + } + }; + + CustomComponents.Select = reactInitialized && class BDFDB_Select extends Internal.LibraryModules.React.Component { + handleChange(value) { + this.props.value = value.value || value; + if (typeof this.props.onChange == "function") this.props.onChange(value, this); + BDFDB.ReactUtils.forceUpdate(this); + } + render() { + return BDFDB.ReactUtils.createElement("div", { + className: BDFDB.DOMUtils.formatClassName(this.props.className, BDFDB.disCN.selectwrapper), + children: BDFDB.ReactUtils.createElement(Internal.NativeSubComponents.SearchableSelect, BDFDB.ObjectUtils.exclude(Object.assign({}, this.props, { + className: this.props.inputClassName, + autoFocus: this.props.autoFocus ? this.props.autoFocus : false, + maxVisibleItems: this.props.maxVisibleItems || 7, + renderOptionLabel: this.props.optionRenderer, + select: this.handleChange.bind(this), + serialize: typeof this.props.serialize == "function" ? this.props.serialize : _ => {}, + isSelected: typeof this.props.isSelected == "function" ? this.props.isSelected : (value => this.props.value == value) + }), "inputClassName", "optionRenderer")) + }); + } + }; + + CustomComponents.SettingsGuildList = reactInitialized && class BDFDB_SettingsGuildList extends Internal.LibraryModules.React.Component { + render() { + this.props.disabled = BDFDB.ArrayUtils.is(this.props.disabled) ? this.props.disabled : []; + return BDFDB.ReactUtils.createElement(Internal.LibraryComponents.Flex, { + className: this.props.className, + wrap: Internal.LibraryComponents.Flex.Wrap.WRAP, + children: [this.props.includeDMs && {name: BDFDB.LanguageUtils.LanguageStrings.DIRECT_MESSAGES, acronym: "DMs", id: Internal.DiscordConstants.ME, getIconURL: _ => {}}].concat(Internal.LibraryStores.SortedGuildStore.getFlattenedGuildIds().map(Internal.LibraryStores.GuildStore.getGuild)).filter(n => n).map(guild => BDFDB.ReactUtils.createElement(Internal.LibraryComponents.TooltipContainer, { + text: guild.name, + children: BDFDB.ReactUtils.createElement("div", { + className: BDFDB.DOMUtils.formatClassName(this.props.guildClassName, BDFDB.disCN.settingsguild, this.props.disabled.includes(guild.id) && BDFDB.disCN.settingsguilddisabled), + children: BDFDB.ReactUtils.createElement(Internal.LibraryComponents.GuildIcon, { + guild: guild, + size: this.props.size || Internal.LibraryComponents.GuildIcon.Sizes.MEDIUM + }), + onClick: e => { + let isDisabled = this.props.disabled.includes(guild.id); + if (isDisabled) BDFDB.ArrayUtils.remove(this.props.disabled, guild.id, true); + else this.props.disabled.push(guild.id); + if (typeof this.props.onClick == "function") this.props.onClick(this.props.disabled, this); + BDFDB.ReactUtils.forceUpdate(this); + } + }) + })) + }); + } + }; + + CustomComponents.SettingsPanel = reactInitialized && class BDFDB_SettingsPanel extends Internal.LibraryModules.React.Component { + componentDidMount() { + this.props._instance = this; + let node = BDFDB.ReactUtils.findDOMNode(this); + if (node) this.props._node = node; + } + componentWillUnmount() { + if (BDFDB.ObjectUtils.is(this.props.addon) && typeof this.props.addon.onSettingsClosed == "function") this.props.addon.onSettingsClosed(); + } + render() { + let panelItems = [ + BDFDB.ReactUtils.createElement(Internal.LibraryComponents.AutoFocusCatcher, {}), + typeof this.props.children == "function" ? (_ => { + return this.props.children(this.props.collapseStates); + })() : this.props.children + ].flat(10).filter(n => n); + return BDFDB.ReactUtils.createElement("div", { + key: this.props.addon && this.props.addon.name && `${this.props.addon.name}-settingsPanel`, + id: this.props.addon && this.props.addon.name && `${this.props.addon.name}-settings`, + className: BDFDB.disCN.settingspanel, + children: [ + this.props.addon.changeLog && !BDFDB.ObjectUtils.isEmpty(this.props.addon.changeLog) && BDFDB.ReactUtils.createElement(Internal.LibraryComponents.TooltipContainer, { + text: BDFDB.LanguageUtils.LanguageStrings.CHANGE_LOG, + children: BDFDB.ReactUtils.createElement(Internal.LibraryComponents.Clickable, { + className: BDFDB.disCN._repochangelogbutton, + children: BDFDB.ReactUtils.createElement(Internal.LibraryComponents.SvgIcon, { + name: Internal.LibraryComponents.SvgIcon.Names.CHANGELOG, + onClick: _ => BDFDB.PluginUtils.openChangeLog(this.props.addon), + width: 24, + height: 24 + }) + }) + }), + panelItems + ] + }); + } + }; + + CustomComponents.SettingsPanelList = reactInitialized && class BDFDB_SettingsPanelInner extends Internal.LibraryModules.React.Component { + render() { + return this.props.children ? BDFDB.ReactUtils.createElement("div", { + className: BDFDB.DOMUtils.formatClassName(this.props.className, BDFDB.disCN.settingspanellistwrapper, this.props.mini && BDFDB.disCN.settingspanellistwrappermini), + children: [ + this.props.dividerTop ? BDFDB.ReactUtils.createElement(Internal.LibraryComponents.FormComponents.FormDivider, { + className: this.props.mini ? BDFDB.disCN.marginbottom4 : BDFDB.disCN.marginbottom8 + }) : null, + typeof this.props.title == "string" ? BDFDB.ReactUtils.createElement(Internal.LibraryComponents.FormComponents.FormTitle, { + className: BDFDB.disCN.marginbottom4, + tag: Internal.LibraryComponents.FormComponents.FormTags && Internal.LibraryComponents.FormComponents.FormTags.H3, + children: this.props.title + }) : null, + BDFDB.ReactUtils.createElement("div", { + className: BDFDB.disCN.settingspanellist, + children: this.props.children + }), + this.props.dividerBottom ? BDFDB.ReactUtils.createElement(Internal.LibraryComponents.FormComponents.FormDivider, { + className: this.props.mini ? BDFDB.disCN.margintop4 : BDFDB.disCN.margintop8 + }) : null + ] + }) : null; + } + }; + + CustomComponents.SettingsItem = reactInitialized && class BDFDB_SettingsItem extends Internal.LibraryModules.React.Component { + handleChange(value) { + if (typeof this.props.onChange == "function") this.props.onChange(value, this); + } + render() { + if (typeof this.props.type != "string" || !["BUTTON", "SELECT", "SLIDER", "SWITCH", "TEXTINPUT"].includes(this.props.type.toUpperCase())) return null; + let childComponent = Internal.LibraryComponents[this.props.type]; + if (!childComponent) return null; + if (this.props.mini && childComponent.Sizes) this.props.size = childComponent.Sizes.MINI || childComponent.Sizes.MIN; + let label = this.props.label ? (this.props.tag ? BDFDB.ReactUtils.createElement(Internal.LibraryComponents.FormComponents.FormTitle, { + className: BDFDB.DOMUtils.formatClassName(this.props.labelClassName, BDFDB.disCN.marginreset), + tag: this.props.tag, + children: this.props.label + }) : BDFDB.ReactUtils.createElement(Internal.LibraryComponents.SettingsLabel, { + className: BDFDB.DOMUtils.formatClassName(this.props.labelClassName), + mini: this.props.mini, + label: this.props.label + })) : null; + let margin = this.props.margin != null ? this.props.margin : (this.props.mini ? 0 : 8); + return BDFDB.ReactUtils.createElement("div", { + className: BDFDB.DOMUtils.formatClassName(this.props.className, BDFDB.disCN.settingsrow, BDFDB.disCN.settingsrowcontainer, this.props.disabled && BDFDB.disCN.settingsrowdisabled, margin != null && (InternalData.DiscordClasses[`marginbottom${margin}`] && BDFDB.disCN[`marginbottom${margin}`] || margin == 0 && BDFDB.disCN.marginreset)), + id: this.props.id, + children: [ + this.props.dividerTop ? BDFDB.ReactUtils.createElement(Internal.LibraryComponents.FormComponents.FormDivider, { + className: this.props.mini ? BDFDB.disCN.marginbottom4 : BDFDB.disCN.marginbottom8 + }) : null, + BDFDB.ReactUtils.createElement("div", { + className: BDFDB.disCN.settingsrowlabel, + children: [ + label && !this.props.basis ? BDFDB.ReactUtils.createElement(Internal.LibraryComponents.Flex.Child, { + grow: 1, + shrink: 1, + wrap: true, + children: label + }) : label, + this.props.labelChildren, + BDFDB.ReactUtils.createElement(Internal.LibraryComponents.Flex.Child, { + className: BDFDB.disCNS.settingsrowcontrol + BDFDB.disCN.flexchild, + grow: 0, + shrink: this.props.basis ? 0 : 1, + basis: this.props.basis, + wrap: true, + children: BDFDB.ReactUtils.createElement(childComponent, BDFDB.ObjectUtils.exclude(Object.assign(BDFDB.ObjectUtils.exclude(this.props, "className", "id", "type"), this.props.childProps, { + onChange: this.handleChange.bind(this), + onValueChange: this.handleChange.bind(this) + }), "basis", "margin", "dividerBottom", "dividerTop", "label", "labelClassName", "labelChildren", "tag", "mini", "note", "childProps")) + }) + ].flat(10).filter(n => n) + }), + typeof this.props.note == "string" ? BDFDB.ReactUtils.createElement(Internal.LibraryComponents.Flex.Child, { + className: BDFDB.disCN.settingsrownote, + children: BDFDB.ReactUtils.createElement(Internal.LibraryComponents.FormComponents.FormText, { + disabled: this.props.disabled, + type: Internal.LibraryComponents.FormComponents.FormText.Types.DESCRIPTION, + children: BDFDB.ReactUtils.createElement(Internal.LibraryComponents.TextScroller, {speed: 2, children: this.props.note}) + }) + }) : null, + this.props.dividerBottom ? BDFDB.ReactUtils.createElement(Internal.LibraryComponents.FormComponents.FormDivider, { + className: this.props.mini ? BDFDB.disCN.margintop4 : BDFDB.disCN.margintop8 + }) : null + ] + }); + } + }; + + CustomComponents.SettingsLabel = reactInitialized && class BDFDB_SettingsLabel extends Internal.LibraryModules.React.Component { + render() { + return BDFDB.ReactUtils.createElement(Internal.LibraryComponents.TextScroller, { + className: BDFDB.DOMUtils.formatClassName(this.props.className, BDFDB.disCN.settingsrowtitle, this.props.mini ? BDFDB.disCN.settingsrowtitlemini : BDFDB.disCN.settingsrowtitledefault, BDFDB.disCN.cursordefault), + speed: 2, + children: this.props.label + }); + } + }; + + CustomComponents.SettingsList = reactInitialized && class BDFDB_SettingsList extends Internal.LibraryModules.React.Component { + componentDidMount() { + this.checkList(); + } + componentDidUpdate() { + this.checkList(); + } + checkList() { + let list = BDFDB.ReactUtils.findDOMNode(this); + if (list && !this.props.configWidth) { + let headers = Array.from(list.querySelectorAll(BDFDB.dotCN.settingstableheader)); + headers.shift(); + if (BDFDB.DOMUtils.getRects(headers[0]).width == 0) BDFDB.TimeUtils.timeout(_ => {this.resizeList(headers);}); + else this.resizeList(headers); + } + } + resizeList(headers) { + let configWidth = 0, biggestWidth = 0; + if (!configWidth) { + for (let header of headers) { + header.style = ""; + let width = BDFDB.DOMUtils.getRects(header).width; + configWidth = width > configWidth ? width : configWidth; + } + configWidth += 4; + biggestWidth = configWidth; + } + if (headers.length * configWidth > 300) { + this.props.vertical = true; + configWidth = parseInt(290 / headers.length); + } + else if (configWidth < 36) { + configWidth = 36; + biggestWidth = configWidth; + } + this.props.configWidth = configWidth; + this.props.biggestWidth = biggestWidth; + BDFDB.ReactUtils.forceUpdate(this); + } + renderHeaderOption(props) { + return BDFDB.ReactUtils.createElement("div", { + className: BDFDB.DOMUtils.formatClassName(props.className, BDFDB.disCN.colorbase, BDFDB.disCN.size10, props.clickable && BDFDB.disCN.cursorpointer), + onClick: _ => {if (typeof this.props.onHeaderClick == "function") this.props.onHeaderClick(props.label, this);}, + onContextMenu: _ => {if (typeof this.props.onHeaderContextMenu == "function") this.props.onHeaderContextMenu(props.label, this);}, + children: BDFDB.ReactUtils.createElement("span", { + children: props.label + }) + }); + } + renderItem(props) { + return BDFDB.ReactUtils.createElement(Internal.LibraryComponents.Card, BDFDB.ObjectUtils.exclude(Object.assign({}, this.props, { + className: BDFDB.DOMUtils.formatClassName([this.props.cardClassName, props.className].filter(n => n).join(" ").indexOf(BDFDB.disCN.card) == -1 && BDFDB.disCN.cardprimaryoutline, BDFDB.disCN.settingstablecard, this.props.cardClassName, props.className), + cardId: props.key, + backdrop: false, + horizontal: true, + style: Object.assign({}, this.props.cardStyle, props.style), + children: [ + BDFDB.ReactUtils.createElement("div", { + className: BDFDB.disCN.settingstablecardlabel, + children: this.props.renderLabel(props, this) + }), + BDFDB.ReactUtils.createElement("div", { + className: BDFDB.disCN.settingstablecardconfigs, + style: { + width: props.wrapperWidth || null, + minWidth: props.wrapperWidth || null, + maxWidth: props.wrapperWidth || null + }, + children: this.props.settings.map(setting => BDFDB.ReactUtils.createElement("div", { + className: BDFDB.disCN.checkboxcontainer, + children: BDFDB.ReactUtils.createElement(Internal.LibraryComponents.TooltipContainer, { + text: setting.toUpperCase(), + children: BDFDB.ReactUtils.createElement(Internal.LibraryComponents.Checkbox, { + disabled: props.disabled, + cardId: props.key, + settingId: setting, + shape: Internal.LibraryComponents.Checkbox.Shapes && Internal.LibraryComponents.Checkbox.Shapes.ROUND, + type: Internal.LibraryComponents.Checkbox.Types && Internal.LibraryComponents.Checkbox.Types.INVERTED, + color: this.props.checkboxColor, + getColor: this.props.getCheckboxColor, + value: props[setting], + getValue: this.props.getCheckboxValue, + onChange: this.props.onCheckboxChange + }) + }) + })).flat(10).filter(n => n) + }) + ] + }), "title", "data", "settings", "renderLabel", "cardClassName", "cardStyle", "checkboxColor", "getCheckboxColor", "getCheckboxValue", "onCheckboxChange", "configWidth", "biggestWidth", "pagination")); + } + render() { + this.props.settings = BDFDB.ArrayUtils.is(this.props.settings) ? this.props.settings : []; + this.props.renderLabel = typeof this.props.renderLabel == "function" ? this.props.renderLabel : data => data.label; + this.props.data = (BDFDB.ArrayUtils.is(this.props.data) ? this.props.data : [{}]).filter(n => n); + + let wrapperWidth = this.props.configWidth && this.props.configWidth * this.props.settings.length; + let isHeaderClickable = typeof this.props.onHeaderClick == "function" || typeof this.props.onHeaderContextMenu == "function"; + let usePagination = BDFDB.ObjectUtils.is(this.props.pagination); + + let header = BDFDB.ReactUtils.createElement("div", { + className: BDFDB.disCN.settingstableheaders, + style: this.props.vertical && this.props.biggestWidth ? { + marginTop: this.props.biggestWidth - 15 || 0 + } : {}, + children: [ + this.renderHeaderOption({ + className: BDFDB.disCN.settingstableheadername, + clickable: this.props.title && isHeaderClickable, + label: this.props.title || "" + }), + BDFDB.ReactUtils.createElement("div", { + className: BDFDB.disCN.settingstableheaderoptions, + style: { + width: wrapperWidth || null, + minWidth: wrapperWidth || null, + maxWidth: wrapperWidth || null + }, + children: this.props.settings.map(setting => this.renderHeaderOption({ + className: BDFDB.DOMUtils.formatClassName(BDFDB.disCN.settingstableheaderoption, this.props.vertical && BDFDB.disCN.settingstableheadervertical), + clickable: isHeaderClickable, + label: setting + })) + }) + ] + }); + return !this.props.data.length ? null : BDFDB.ReactUtils.createElement("div", { + className: BDFDB.DOMUtils.formatClassName(BDFDB.disCN.settingstablelist, this.props.className), + children: [ + !usePagination && header, + !usePagination ? this.props.data.map(data => this.renderItem(Object.assign({}, data, {wrapperWidth}))) : BDFDB.ReactUtils.createElement(Internal.LibraryComponents.PaginatedList, Object.assign({}, this.props.pagination, { + header: header, + items: this.props.data, + renderItem: data => this.renderItem(Object.assign({}, data, {wrapperWidth})), + onJump: (offset, instance) => { + this.props.pagination.offset = offset; + if (typeof this.props.pagination.onJump == "function") this.props.pagination.onJump(offset, this, instance); + } + })) + ].filter(n => n) + }); + } + }; + + CustomComponents.SettingsSaveItem = reactInitialized && class BDFDB_SettingsSaveItem extends Internal.LibraryModules.React.Component { + saveSettings(value) { + if (!BDFDB.ArrayUtils.is(this.props.keys) || !BDFDB.ObjectUtils.is(this.props.plugin)) return; + let keys = this.props.keys.filter(n => n); + let option = keys.shift(); + if (BDFDB.ObjectUtils.is(this.props.plugin) && option) { + let data = BDFDB.DataUtils.load(this.props.plugin, option); + let newC = ""; + for (let key of keys) newC += `{"${key}":`; + value = value != null && value.value != null ? value.value : value; + let isString = typeof value == "string"; + let marker = isString ? `"` : ``; + newC += (marker + (isString ? value.replace(/\\/g, "\\\\") : value) + marker) + "}".repeat(keys.length); + newC = JSON.parse(newC); + newC = BDFDB.ObjectUtils.is(newC) ? BDFDB.ObjectUtils.deepAssign({}, data, newC) : newC; + BDFDB.DataUtils.save(newC, this.props.plugin, option); + if (!this.props.plugin.settings) this.props.plugin.settings = {}; + this.props.plugin.settings[option] = newC; + this.props.plugin.SettingsUpdated = true; + } + if (typeof this.props.onChange == "function") this.props.onChange(value, this); + } + render() { + if (typeof this.props.type != "string" || !["SELECT", "SLIDER", "SWITCH", "TEXTINPUT"].includes(this.props.type.toUpperCase())) return null; + return BDFDB.ReactUtils.createElement(Internal.LibraryComponents.SettingsItem, BDFDB.ObjectUtils.exclude(Object.assign({}, this.props, { + onChange: this.saveSettings.bind(this) + }), "keys", "key", "plugin")); + } + }; + + CustomComponents.SidebarList = reactInitialized && class BDFDB_SidebarList extends Internal.LibraryModules.React.Component { + handleItemSelect(item) { + this.props.selectedItem = item; + if (typeof this.props.onItemSelect == "function") this.props.onItemSelect(item, this); + BDFDB.ReactUtils.forceUpdate(this); + } + render() { + let items = (BDFDB.ArrayUtils.is(this.props.items) ? this.props.items : [{}]).filter(n => n); + let selectedItem = this.props.selectedItem || (items[0] || {}).value; + let selectedElements = (items.find(n => n.value == selectedItem) || {}).elements; + let renderElement = typeof this.props.renderElement == "function" ? this.props.renderElement : (_ => {}); + return BDFDB.ReactUtils.createElement("div", { + className: BDFDB.DOMUtils.formatClassName(this.props.className, BDFDB.disCN.sidebarlist), + children: [ + BDFDB.ReactUtils.createElement(Internal.LibraryComponents.Scrollers.Thin, { + className: BDFDB.DOMUtils.formatClassName(this.props.sidebarClassName, BDFDB.disCN.sidebar), + fade: true, + children: BDFDB.ReactUtils.createElement(Internal.LibraryComponents.TabBar, { + itemClassName: this.props.itemClassName, + type: Internal.LibraryComponents.TabBar.Types.SIDE, + items: items, + selectedItem: selectedItem, + renderItem: this.props.renderItem, + onItemSelect: this.handleItemSelect.bind(this) + }) + }), + BDFDB.ReactUtils.createElement(Internal.LibraryComponents.Scrollers.Thin, { + className: BDFDB.DOMUtils.formatClassName(this.props.contentClassName, BDFDB.disCN.sidebarcontent), + fade: true, + children: [selectedElements].flat(10).filter(n => n).map(data => renderElement(data)) + }) + ] + }); + } + }; + + CustomComponents.Slider = reactInitialized && class BDFDB_Slider extends Internal.LibraryModules.React.Component { + handleMarkerRender(marker) { + let newMarker = BDFDB.NumberUtils.mapRange([0, 100], this.props.edges, marker); + if (typeof this.props.digits == "number") newMarker = Math.round(newMarker * Math.pow(10, this.props.digits)) / Math.pow(10, this.props.digits); + return newMarker; + } + handleValueChange(value) { + let newValue = BDFDB.NumberUtils.mapRange([0, 100], this.props.edges, value); + if (typeof this.props.digits == "number") newValue = Math.round(newValue * Math.pow(10, this.props.digits)) / Math.pow(10, this.props.digits); + this.props.defaultValue = this.props.value = newValue; + if (typeof this.props.onValueChange == "function") this.props.onValueChange(newValue, this); + BDFDB.ReactUtils.forceUpdate(this); + } + handleValueRender(value) { + let newValue = BDFDB.NumberUtils.mapRange([0, 100], this.props.edges, value); + if (typeof this.props.digits == "number") newValue = Math.round(newValue * Math.pow(10, this.props.digits)) / Math.pow(10, this.props.digits); + if (typeof this.props.onValueRender == "function") { + let tempReturn = this.props.onValueRender(newValue, this); + if (tempReturn != undefined) newValue = tempReturn; + } + return newValue; + } + render() { + let value = this.props.value || this.props.defaultValue || 0; + if (!BDFDB.ArrayUtils.is(this.props.edges) || this.props.edges.length != 2) this.props.edges = [this.props.min || this.props.minValue || 0, this.props.max || this.props.maxValue || 100]; + this.props.minValue = 0; + this.props.maxValue = 100; + let defaultValue = BDFDB.NumberUtils.mapRange(this.props.edges, [0, 100], value); + if (typeof this.props.digits == "number") defaultValue = Math.round(defaultValue * Math.pow(10, this.props.digits)) / Math.pow(10, this.props.digits); + return BDFDB.ReactUtils.createElement(Internal.NativeSubComponents.Slider, BDFDB.ObjectUtils.exclude(Object.assign({}, this.props, { + initialValue: defaultValue, + markers: typeof this.props.markerAmount == "number" ? Array.from(Array(this.props.markerAmount).keys()).map((_, i) => i * (this.props.maxValue - this.props.minValue)/10) : undefined, + onMarkerRender: this.handleMarkerRender.bind(this), + onValueChange: this.handleValueChange.bind(this), + onValueRender: this.handleValueRender.bind(this) + }), "digits", "edges", "max", "min", "markerAmount")); + } + }; + Internal.setDefaultProps(CustomComponents.Slider, {hideBubble: false, digits: 3}); + + CustomComponents.SvgIcon = reactInitialized && class BDFDB_Icon extends Internal.LibraryModules.React.Component { + render() { + if (BDFDB.ObjectUtils.is(this.props.name)) { + let calcClassName = []; + if (BDFDB.ObjectUtils.is(this.props.name.getClassName)) for (let path in this.props.name.getClassName) { + if (!path || BDFDB.ObjectUtils.get(this, path)) calcClassName.push(BDFDB.disCN[this.props.name.getClassName[path]]); + } + if (calcClassName.length || this.props.className) this.props.nativeClass = true; + this.props.iconSVG = this.props.name.icon; + let props = Object.assign({ + width: 24, + height: 24, + color: "currentColor" + }, this.props.name.defaultProps, this.props, { + className: BDFDB.DOMUtils.formatClassName(calcClassName, this.props.className) + }); + for (let key in props) this.props.iconSVG = this.props.iconSVG.replace(new RegExp(`%%${key}`, "g"), props[key]); + } + if (this.props.iconSVG) { + let icon = BDFDB.ReactUtils.elementToReact(BDFDB.DOMUtils.create(this.props.iconSVG)); + if (BDFDB.ReactUtils.isValidElement(icon)) { + icon.props.className = BDFDB.DOMUtils.formatClassName(!this.props.nativeClass && BDFDB.disCN.svgicon, icon.props.className, this.props.className); + icon.props.style = Object.assign({}, icon.props.style, this.props.style); + icon.props = Object.assign({}, BDFDB.ObjectUtils.extract(this.props, "onClick", "onContextMenu", "onMouseDown", "onMouseUp", "onMouseEnter", "onMouseLeave"), icon.props); + return icon; + } + } + return null; + } + }; + CustomComponents.SvgIcon.Names = InternalData.SvgIcons || {}; + + const SwitchIconPaths = { + a: { + TOP: "M5.13231 6.72963L6.7233 5.13864L14.855 13.2704L13.264 14.8614L5.13231 6.72963Z", + BOTTOM: "M13.2704 5.13864L14.8614 6.72963L6.72963 14.8614L5.13864 13.2704L13.2704 5.13864Z" + }, + b: { + TOP: "M6.56666 11.0013L6.56666 8.96683L13.5667 8.96683L13.5667 11.0013L6.56666 11.0013Z", + BOTTOM: "M13.5582 8.96683L13.5582 11.0013L6.56192 11.0013L6.56192 8.96683L13.5582 8.96683Z" + }, + c: { + TOP: "M7.89561 14.8538L6.30462 13.2629L14.3099 5.25755L15.9009 6.84854L7.89561 14.8538Z", + BOTTOM: "M4.08643 11.0903L5.67742 9.49929L9.4485 13.2704L7.85751 14.8614L4.08643 11.0903Z" + } + }; + const SwitchInner = function (props) { + let reducedMotion = BDFDB.ReactUtils.useContext(Internal.LibraryModules.PreferencesContext.AccessibilityPreferencesContext).reducedMotion; + let ref = BDFDB.ReactUtils.useRef(null); + let state = BDFDB.ReactUtils.useState(false); + let animation = Internal.LibraryComponents.Animations.useSpring({ + config: { + mass: 1, + tension: 250 + }, + opacity: props.disabled ? .3 : 1, + state: state[0] ? (props.value ? .7 : .3) : (props.value ? 1 : 0) + }); + let fill = animation.state.to({ + output: [props.uncheckedColor, props.checkedColor] + }); + let mini = props.size == Internal.LibraryComponents.Switch.Sizes.MINI; + + return BDFDB.ReactUtils.createElement(Internal.LibraryComponents.Animations.animated.div, { + className: BDFDB.DOMUtils.formatClassName(props.className, BDFDB.disCN.switch, mini && BDFDB.disCN.switchmini, "default-colors"), + onMouseDown: _ => { + return !props.disabled && state[1](true); + }, + onMouseUp: _ => { + return state[1](false); + }, + onMouseLeave: _ => { + return state[1](false); + }, + style: { + opacity: animation.opacity, + backgroundColor: animation.state.to({ + output: [props.uncheckedColor, props.checkedColor] + }) + }, + tabIndex: -1, + children: [ + BDFDB.ReactUtils.createElement(Internal.LibraryComponents.Animations.animated.svg, { + className: BDFDB.disCN.switchslider, + viewBox: "0 0 28 20", + preserveAspectRatio: "xMinYMid meet", + style: { + left: animation.state.to({ + range: [0, .3, .7, 1], + output: mini ? [-1, 2, 6, 9] : [-3, 1, 8, 12] + }) + }, + children: [ + BDFDB.ReactUtils.createElement(Internal.LibraryComponents.Animations.animated.rect, { + fill: "white", + x: animation.state.to({ + range: [0, .3, .7, 1], + output: [4, 0, 0, 4] + }), + y: animation.state.to({ + range: [0, .3, .7, 1], + output: [0, 1, 1, 0] + }), + height: animation.state.to({ + range: [0, .3, .7, 1], + output: [20, 18, 18, 20] + }), + width: animation.state.to({ + range: [0, .3, .7, 1], + output: [20, 28, 28, 20] + }), + rx: "10" + }), + BDFDB.ReactUtils.createElement("svg", { + viewBox: "0 0 20 20", + fill: "none", + children: [ + BDFDB.ReactUtils.createElement(Internal.LibraryComponents.Animations.animated.path, { + fill: fill, + d: animation.state.to({ + range: [0, .3, .7, 1], + output: reducedMotion.enabled ? [SwitchIconPaths.a.TOP, SwitchIconPaths.a.TOP, SwitchIconPaths.c.TOP, SwitchIconPaths.c.TOP] : [SwitchIconPaths.a.TOP, SwitchIconPaths.b.TOP, SwitchIconPaths.b.TOP, SwitchIconPaths.c.TOP] + }) + }), + BDFDB.ReactUtils.createElement(Internal.LibraryComponents.Animations.animated.path, { + fill: fill, + d: animation.state.to({ + range: [0, .3, .7, 1], + output: reducedMotion.enabled ? [SwitchIconPaths.a.BOTTOM, SwitchIconPaths.a.BOTTOM, SwitchIconPaths.c.BOTTOM, SwitchIconPaths.c.BOTTOM] : [SwitchIconPaths.a.BOTTOM, SwitchIconPaths.b.BOTTOM, SwitchIconPaths.b.BOTTOM, SwitchIconPaths.c.BOTTOM] + }) + }) + ] + }) + ] + }), + BDFDB.ReactUtils.createElement("input", BDFDB.ObjectUtils.exclude(Object.assign({}, props, { + id: props.id, + type: "checkbox", + ref: ref, + className: BDFDB.DOMUtils.formatClassName(props.inputClassName, BDFDB.disCN.switchinner), + tabIndex: props.disabled ? -1 : 0, + onKeyDown: e => { + if (!props.disabled && !e.repeat && (e.key == " " || e.key == "Enter")) state[1](true); + }, + onKeyUp: e => { + if (!props.disabled && !e.repeat) { + state[1](false); + if (e.key == "Enter" && ref.current) ref.current.click(); + } + }, + onChange: e => { + state[1](false); + if (typeof props.onChange == "function") props.onChange(e.currentTarget.checked, e); + }, + checked: props.value, + disabled: props.disabled + }), "uncheckedColor", "checkedColor", "size", "value")) + ] + }); + }; + CustomComponents.Switch = reactInitialized && class BDFDB_Switch extends Internal.LibraryModules.React.Component { + handleChange() { + this.props.value = !this.props.value; + if (typeof this.props.onChange == "function") this.props.onChange(this.props.value, this); + BDFDB.ReactUtils.forceUpdate(this); + } + render() { + return BDFDB.ReactUtils.createElement(SwitchInner, Object.assign({}, this.props, { + onChange: this.handleChange.bind(this) + })); + } + }; + CustomComponents.Switch.Sizes = { + DEFAULT: "default", + MINI: "mini", + }; + Internal.setDefaultProps(CustomComponents.Switch, { + size: CustomComponents.Switch.Sizes.DEFAULT, + uncheckedColor: Internal.DiscordConstants.Colors.PRIMARY_400, + checkedColor: Internal.DiscordConstants.Colors.BRAND + }); + + CustomComponents.TabBar = reactInitialized && class BDFDB_TabBar extends Internal.LibraryModules.React.Component { + handleItemSelect(item) { + this.props.selectedItem = item; + if (typeof this.props.onItemSelect == "function") this.props.onItemSelect(item, this); + BDFDB.ReactUtils.forceUpdate(this); + } + render() { + let items = (BDFDB.ArrayUtils.is(this.props.items) ? this.props.items : [{}]).filter(n => n); + let selectedItem = this.props.selectedItem || (items[0] || {}).value; + let renderItem = typeof this.props.renderItem == "function" ? this.props.renderItem : (data => data.label || data.value); + return BDFDB.ReactUtils.createElement(Internal.NativeSubComponents.TabBar, BDFDB.ObjectUtils.exclude(Object.assign({}, this.props, { + selectedItem: selectedItem, + onItemSelect: this.handleItemSelect.bind(this), + children: items.map(data => BDFDB.ReactUtils.createElement(Internal.LibraryComponents.TabBar.Item, { + className: BDFDB.DOMUtils.formatClassName(this.props.itemClassName, selectedItem == data.value && this.props.itemSelectedClassName), + itemType: this.props.type, + id: data.value, + children: renderItem(data), + "aria-label": data.label || data.value + })) + }), "itemClassName", "items", "renderItem")); + } + }; + CustomComponents.TabBar.Types = { + SIDE: "side", + TOP: "top", + TOP_PILL: "top-pill" + }; + CustomComponents.TabBar.Looks = { + 0: "GREY", + 1: "BRAND", + 2: "CUSTOM", + GREY: 0, + BRAND: 1, + CUSTOM: 2 + }; + + CustomComponents.Table = reactInitialized && class BDFDB_Table extends Internal.LibraryModules.React.Component { + render() { + return BDFDB.ReactUtils.createElement(Internal.NativeSubComponents.Table, Object.assign({}, this.props, { + className: BDFDB.DOMUtils.formatClassName(BDFDB.disCN.table, this.props.className), + headerCellClassName: BDFDB.DOMUtils.formatClassName(BDFDB.disCN.tableheadercell, this.props.headerCellClassName), + sortedHeaderCellClassName: BDFDB.DOMUtils.formatClassName(BDFDB.disCN.tableheadercellsorted, this.props.sortedHeaderCellClassName), + bodyCellClassName: BDFDB.DOMUtils.formatClassName(BDFDB.disCN.tablebodycell, this.props.bodyCellClassName), + onSort: (sortKey, sortDirection) => { + this.props.sortDirection = this.props.sortKey != sortKey && sortDirection == Internal.LibraryComponents.Table.SortDirection.ASCENDING && this.props.columns.filter(n => n.key == sortKey)[0].reverse ? Internal.LibraryComponents.Table.SortDirection.DESCENDING : sortDirection; + this.props.sortKey = sortKey; + this.props.data = BDFDB.ArrayUtils.keySort(this.props.data, this.props.sortKey); + if (this.props.sortDirection == Internal.LibraryComponents.Table.SortDirection.DESCENDING) this.props.data.reverse(); + if (typeof this.props.onSort == "function") this.props.onSort(this.props.sortKey, this.props.sortDirection); + BDFDB.ReactUtils.forceUpdate(this); + } + })); + } + }; + + CustomComponents.TextArea = reactInitialized && class BDFDB_TextArea extends Internal.LibraryModules.React.Component { + handleChange(e) { + this.props.value = e; + if (typeof this.props.onChange == "function") this.props.onChange(e, this); + BDFDB.ReactUtils.forceUpdate(this); + } + handleBlur(e) {if (typeof this.props.onBlur == "function") this.props.onBlur(e, this);} + handleFocus(e) {if (typeof this.props.onFocus == "function") this.props.onFocus(e, this);} + render() { + return BDFDB.ReactUtils.createElement(Internal.NativeSubComponents.TextArea, Object.assign({}, this.props, { + onChange: this.handleChange.bind(this), + onBlur: this.handleBlur.bind(this), + onFocus: this.handleFocus.bind(this) + })); + } + }; + + CustomComponents.TextGradientElement = reactInitialized && class BDFDB_TextGradientElement extends Internal.LibraryModules.React.Component { + render() { + if (this.props.gradient && this.props.children) return BDFDB.ReactUtils.createElement("span", { + children: this.props.children, + ref: instance => { + let ele = BDFDB.ReactUtils.findDOMNode(instance); + if (ele) { + ele.style.setProperty("background-image", this.props.gradient, "important"); + ele.style.setProperty("color", "transparent", "important"); + ele.style.setProperty("text-decoration-color", BDFDB.ColorUtils.convert(this.props.gradient[0], "RGBA"), "important"); + ele.style.setProperty("-webkit-background-clip", "text", "important"); + } + } + }); + return this.props.children || null; + } + }; + + CustomComponents.TextInput = reactInitialized && class BDFDB_TextInput extends Internal.LibraryModules.React.Component { + handleChange(e) { + let value = e = BDFDB.ObjectUtils.is(e) ? e.currentTarget.value : e; + this.props.value = this.props.valuePrefix && !value.startsWith(this.props.valuePrefix) ? (this.props.valuePrefix + value) : value; + if (typeof this.props.onChange == "function") this.props.onChange(this.props.value, this); + BDFDB.ReactUtils.forceUpdate(this); + } + handleInput(e) {if (typeof this.props.onInput == "function") this.props.onInput(BDFDB.ObjectUtils.is(e) ? e.currentTarget.value : e, this);} + handleKeyDown(e) {if (typeof this.props.onKeyDown == "function") this.props.onKeyDown(e, this);} + handleBlur(e) {if (typeof this.props.onBlur == "function") this.props.onBlur(e, this);} + handleFocus(e) {if (typeof this.props.onFocus == "function") this.props.onFocus(e, this);} + handleMouseEnter(e) {if (typeof this.props.onMouseEnter == "function") this.props.onMouseEnter(e, this);} + handleMouseLeave(e) {if (typeof this.props.onMouseLeave == "function") this.props.onMouseLeave(e, this);} + handleNumberButton(ins, value) { + BDFDB.TimeUtils.clear(this.pressedTimeout); + this.pressedTimeout = BDFDB.TimeUtils.timeout(_ => { + delete this.props.focused; + BDFDB.ReactUtils.forceUpdate(this); + }, 1000); + this.props.focused = true; + this.handleChange.apply(this, [value]); + this.handleInput.apply(this, [value]); + } + componentDidMount() { + if (this.props.type == "file") { + let navigatorInstance = BDFDB.ReactUtils.findOwner(this, {name: "BDFDB_FileButton"}); + if (navigatorInstance) navigatorInstance.refInput = this; + } + let input = BDFDB.ReactUtils.findDOMNode(this); + if (!input) return; + input = input.querySelector("input") || input; + if (input && !input.patched) { + input.addEventListener("keydown", e => { + this.handleKeyDown.apply(this, [e]); + e.stopImmediatePropagation(); + }); + input.patched = true; + } + } + render() { + let inputChildren = [ + BDFDB.ReactUtils.createElement("input", BDFDB.ObjectUtils.exclude(Object.assign({}, this.props, { + className: BDFDB.DOMUtils.formatClassName(this.props.size || BDFDB.disCN.inputdefault, this.props.inputClassName, this.props.focused && BDFDB.disCN.inputfocused, this.props.error || this.props.errorMessage ? BDFDB.disCN.inputerror : (this.props.success && BDFDB.disCN.inputsuccess), this.props.disabled && BDFDB.disCN.inputdisabled, this.props.editable && BDFDB.disCN.inputeditable), + type: this.props.type == "color" || this.props.type == "file" ? "text" : this.props.type, + onChange: this.handleChange.bind(this), + onInput: this.handleInput.bind(this), + onKeyDown: this.handleKeyDown.bind(this), + onBlur: this.handleBlur.bind(this), + onFocus: this.handleFocus.bind(this), + onMouseEnter: this.handleMouseEnter.bind(this), + onMouseLeave: this.handleMouseLeave.bind(this), + maxLength: this.props.type == "file" ? false : this.props.maxLength, + style: this.props.width ? {width: `${this.props.width}px`} : {}, + ref: this.props.inputRef + }), "errorMessage", "focused", "error", "success", "inputClassName", "inputChildren", "valuePrefix", "inputPrefix", "size", "editable", "inputRef", "style", "mode", "colorPickerOpen", "noAlpha", "filter", "useFilePath", "searchFolders")), + this.props.inputChildren, + this.props.type == "color" ? BDFDB.ReactUtils.createElement(Internal.LibraryComponents.Flex.Child, { + wrap: true, + children: BDFDB.ReactUtils.createElement(Internal.LibraryComponents.ColorSwatches, { + colors: [], + color: this.props.value && this.props.mode == "comp" ? BDFDB.ColorUtils.convert(this.props.value.split(","), "RGB") : this.props.value, + onColorChange: color => this.handleChange.apply(this, [!color ? "" : (this.props.mode == "comp" ? BDFDB.ColorUtils.convert(color, "RGBCOMP").slice(0, 3).join(",") : BDFDB.ColorUtils.convert(color, this.props.noAlpha ? "RGB" : "RGBA"))]), + pickerOpen: this.props.colorPickerOpen, + onPickerOpen: _ => this.props.colorPickerOpen = true, + onPickerClose: _ => delete this.props.colorPickerOpen, + ref: this.props.controlsRef, + pickerConfig: {gradient: false, alpha: this.props.mode != "comp" && !this.props.noAlpha} + }) + }) : null, + this.props.type == "file" ? BDFDB.ReactUtils.createElement(Internal.LibraryComponents.FileButton, { + filter: this.props.filter, + mode: this.props.mode, + useFilePath: this.props.useFilePath, + searchFolders: this.props.searchFolders, + ref: this.props.controlsRef + }) : null + ].flat(10).filter(n => n); + + return BDFDB.ReactUtils.createElement("div", { + className: BDFDB.DOMUtils.formatClassName(BDFDB.disCN.inputwrapper, this.props.type == "number" && (this.props.size && Internal.LibraryComponents.TextInput.Sizes[this.props.size.toUpperCase()] && BDFDB.disCN["inputnumberwrapper" + this.props.size.toLowerCase()] || BDFDB.disCN.inputnumberwrapperdefault), this.props.className), + style: this.props.style, + children: [ + this.props.inputPrefix ? BDFDB.ReactUtils.createElement("span", { + className: BDFDB.disCN.inputprefix + }) : null, + this.props.type == "number" ? BDFDB.ReactUtils.createElement("div", { + className: BDFDB.disCN.inputnumberbuttons, + children: [ + BDFDB.ReactUtils.createElement("div", { + className: BDFDB.disCN.inputnumberbuttonup, + onClick: e => { + let min = parseInt(this.props.min); + let max = parseInt(this.props.max); + let newV = parseInt(this.props.value) + 1 || min || 0; + if (isNaN(max) || !isNaN(max) && newV <= max) this.handleNumberButton.bind(this)(e._targetInst, isNaN(min) || !isNaN(min) && newV >= min ? newV : min); + } + }), + BDFDB.ReactUtils.createElement("div", { + className: BDFDB.disCN.inputnumberbuttondown, + onClick: e => { + let min = parseInt(this.props.min); + let max = parseInt(this.props.max); + let newV = parseInt(this.props.value) - 1 || min || 0; + if (isNaN(min) || !isNaN(min) && newV >= min) this.handleNumberButton.bind(this)(e._targetInst, isNaN(max) || !isNaN(max) && newV <= max ? newV : max); + } + }) + ] + }) : null, + inputChildren.length == 1 ? inputChildren[0] : BDFDB.ReactUtils.createElement(Internal.LibraryComponents.Flex, { + align: Internal.LibraryComponents.Flex.Align.CENTER, + children: inputChildren.map((child, i) => i != 0 ? BDFDB.ReactUtils.createElement(Internal.LibraryComponents.Flex.Child, { + shrink: 0, + children: child + }) : child) + }), + this.props.errorMessage ? BDFDB.ReactUtils.createElement(Internal.LibraryComponents.TextElement, { + className: BDFDB.disCN.margintop8, + size: Internal.LibraryComponents.TextElement.Sizes.SIZE_12, + color: Internal.LibraryComponents.TextElement.Colors.STATUS_RED, + children: this.props.errorMessage + }) : null + ].filter(n => n) + }); + } + }; + + CustomComponents.TextScroller = reactInitialized && class BDFDB_TextScroller extends Internal.LibraryModules.React.Component { + render() { + let scrolling, scroll = _ => {}; + return BDFDB.ReactUtils.createElement("div", { + className: BDFDB.DOMUtils.formatClassName(BDFDB.disCN.textscroller, this.props.className), + style: Object.assign({}, this.props.style, { + position: "relative", + display: "block", + overflow: "hidden" + }), + ref: instance => { + const ele = BDFDB.ReactUtils.findDOMNode(instance); + if (ele && ele.parentElement) { + BDFDB.DOMUtils.hide(ele); + const maxWidth = BDFDB.DOMUtils.getInnerWidth(ele.parentElement); + if (maxWidth > 50) ele.style.setProperty("max-width", `${maxWidth}px`); + BDFDB.DOMUtils.show(ele); + if (!this.props.initiated) BDFDB.TimeUtils.timeout(_ => { + this.props.initiated = true; + if (document.contains(ele.parentElement)) BDFDB.ReactUtils.forceUpdate(this); + }, 3000); + const Animation = new Internal.LibraryModules.AnimationUtils.Value(0); + Animation.interpolate({inputRange: [0, 1], outputRange: [0, (BDFDB.DOMUtils.getRects(ele.firstElementChild).width - BDFDB.DOMUtils.getRects(ele).width) * -1]}).addListener(v => { + ele.firstElementChild.style.setProperty("display", v.value == 0 ? "inline" : "block", "important"); + ele.firstElementChild.style.setProperty("left", `${v.value}px`, "important"); + }); + scroll = p => { + const display = ele.firstElementChild.style.getPropertyValue("display"); + ele.firstElementChild.style.setProperty("display", "inline", "important"); + const innerWidth = BDFDB.DOMUtils.getRects(ele.firstElementChild).width; + const outerWidth = BDFDB.DOMUtils.getRects(ele).width; + ele.firstElementChild.style.setProperty("display", display, "important"); + + let w = p + parseFloat(ele.firstElementChild.style.getPropertyValue("left")) / (innerWidth - outerWidth); + w = isNaN(w) || !isFinite(w) ? p : w; + w *= innerWidth / (outerWidth * 2); + Internal.LibraryModules.AnimationUtils.parallel([Internal.LibraryModules.AnimationUtils.timing(Animation, {toValue: p, duration: Math.sqrt(w**2) * 4000 / (parseInt(this.props.speed) || 1)})]).start(); + }; + } + }, + onClick: e => { + if (typeof this.props.onClick == "function") this.props.onClick(e, this); + }, + onMouseEnter: e => { + if (BDFDB.DOMUtils.getRects(e.currentTarget).width < BDFDB.DOMUtils.getRects(e.currentTarget.firstElementChild).width || e.currentTarget.firstElementChild.style.getPropertyValue("display") != "inline") { + scrolling = true; + scroll(1); + } + }, + onMouseLeave: e => { + if (scrolling) { + scrolling = false; + scroll(0); + } + }, + children: BDFDB.ReactUtils.createElement("div", { + style: { + left: "0", + position: "relative", + display: "inline", + whiteSpace: "nowrap" + }, + children: this.props.children + }) + }); + } + }; + CustomComponents.TooltipContainer = reactInitialized && class BDFDB_TooltipContainer extends Internal.LibraryModules.React.Component { + updateTooltip(text) { + if (this.tooltip) this.tooltip.update(text); + } + render() { + let child = (typeof this.props.children == "function" ? this.props.children() : (BDFDB.ArrayUtils.is(this.props.children) ? this.props.children[0] : this.props.children)) || BDFDB.ReactUtils.createElement("div", {}); + if (!child || !child.props) return null; + child.props.className = BDFDB.DOMUtils.formatClassName(child.props.className, this.props.className); + let childProps = Object.assign({}, child.props); + let shown = false; + child.props.onMouseEnter = (e, childThis) => { + if (!shown && !e.currentTarget.__BDFDBtooltipShown && !(this.props.onlyShowOnShift && !e.shiftKey) && !(this.props.onlyShowOnCtrl && !e.ctrlKey)) { + e.currentTarget.__BDFDBtooltipShown = shown = true; + this.tooltip = BDFDB.TooltipUtils.create(e.currentTarget, typeof this.props.text == "function" ? this.props.text(this, e) : this.props.text, Object.assign({ + note: this.props.note, + delay: this.props.delay + }, this.props.tooltipConfig, { + onHide: (tooltip, anker) => { + delete anker.__BDFDBtooltipShown; + shown = false; + if (this.props.tooltipConfig && typeof this.props.tooltipConfig.onHide == "function") this.props.tooltipConfig.onHide(tooltip, anker); + } + })); + if (typeof this.props.onMouseEnter == "function") this.props.onMouseEnter(e, this); + if (typeof childProps.onMouseEnter == "function") childProps.onMouseEnter(e, childThis); + } + }; + child.props.onMouseLeave = (e, childThis) => { + if (typeof this.props.onMouseLeave == "function") this.props.onMouseLeave(e, this); + if (typeof childProps.onMouseLeave == "function") childProps.onMouseLeave(e, childThis); + }; + child.props.onClick = (e, childThis) => { + if (typeof this.props.onClick == "function") this.props.onClick(e, this); + if (typeof childProps.onClick == "function") childProps.onClick(e, childThis); + if (typeof this.props.text == "function") this.updateTooltip(this.props.text(this, e)); + }; + child.props.onContextMenu = (e, childThis) => { + if (typeof this.props.onContextMenu == "function") this.props.onContextMenu(e, this); + if (typeof childProps.onContextMenu == "function") childProps.onContextMenu(e, childThis); + if (typeof this.props.text == "function") this.updateTooltip(this.props.text(this, e)); + }; + return BDFDB.ReactUtils.createElement(Internal.LibraryModules.React.Fragment, { + children: child + }); + } + }; + CustomComponents.TooltipContainer.Positions = { + BOTTOM: "bottom", + CENTER: "center", + LEFT: "left", + RIGHT: "right", + TOP: "top", + WINDOW_CENTER: "window_center" + }; + + CustomComponents.UserPopoutContainer = reactInitialized && class BDFDB_UserPopoutContainer extends Internal.LibraryModules.React.Component { + render() { + return BDFDB.ReactUtils.createElement(Internal.LibraryComponents.PopoutContainer, BDFDB.ObjectUtils.exclude(Object.assign({}, this.props, { + wrap: false, + renderPopout: instance => BDFDB.ReactUtils.createElement(Internal.LibraryComponents.UserPopout, { + user: Internal.LibraryStores.UserStore.getUser(this.props.userId), + userId: this.props.userId, + channelId: this.props.channelId, + guildId: this.props.guildId + }), + }), "userId", "channelId", "guildId")); + } + }; + + CustomComponents.UserMention = reactInitialized && class BDFDB_UserMention extends Internal.LibraryModules.React.Component { + render() { + let user = this.props.user || Internal.LibraryStores.UserStore.getUser(this.props.userId); + let channel = Internal.LibraryStores.ChannelStore.getChannel(this.props.channelId); + let guildId = this.props.guildId || channel && channel.guild_id; + let mention = BDFDB.ReactUtils.createElement(Internal.LibraryComponents.Clickable, { + className: this.props.className, + onContextMenu: event => BDFDB.UserUtils.openMenu(user, guildId, channel.id, event), + children: "@" + BDFDB.LibraryModules.UserNameUtils.getName(guildId, this.props.channelId, user) + }); + return this.props.inlinePreview ? mention : BDFDB.ReactUtils.createElement(Internal.LibraryComponents.UserPopoutContainer, Object.assign({}, this.props, { + position: Internal.LibraryComponents.PopoutContainer.Positions.RIGHT, + align: Internal.LibraryComponents.PopoutContainer.Align.BOTTOM, + children: mention + })); + } + }; + + const VideoInner = function (props) { + let ref = BDFDB.ReactUtils.useRef(null); + BDFDB.ReactUtils.useEffect(_ => { + if (ref.current) props.play ? ref.current.play() : ref.current.pause(); + }, [props.play]); + return props.ignoreMaxSize || props.naturalWidth <= Internal.DiscordConstants.MAX_VIDEO_WIDTH && props.naturalHeight <= Internal.DiscordConstants.MAX_VIDEO_HEIGHT || props.naturalWidth <= Internal.DiscordConstants.MAX_VIDEO_HEIGHT && props.naturalHeight <= Internal.DiscordConstants.MAX_VIDEO_WIDTH ? BDFDB.ReactUtils.createElement(Internal.LibraryComponents.VideoForwardRef, { + ref: ref, + className: props.className, + poster: props.poster, + src: props.src, + width: props.width, + height: props.height, + muted: true, + loop: true, + autoPlay: props.play, + playOnHover: props.playOnHover, + preload: "none" + }) : BDFDB.ReactUtils.createElement("img", { + alt: "", + src: props.poster, + width: props.width, + height: props.height + }); + }; + CustomComponents.Video = reactInitialized && class BDFDB_Video extends Internal.LibraryModules.React.Component { + render() { + return BDFDB.ReactUtils.createElement(VideoInner, this.props); + } + }; + + Internal.LibraryComponents = new Proxy(LibraryComponents, { + get: function (_, item) { + if (LibraryComponents[item]) return LibraryComponents[item]; + if (!InternalData.LibraryComponents[item] && !CustomComponents[item]) return "div"; + + Internal.findModuleViaData(LibraryComponents, InternalData.LibraryComponents, item); + + if (CustomComponents[item]) LibraryComponents[item] = LibraryComponents[item] ? Object.assign({}, LibraryComponents[item], CustomComponents[item]) : CustomComponents[item]; + + const NativeComponent = LibraryComponents[item] && Internal.NativeSubComponents[item]; + if (NativeComponent && typeof NativeComponent != "string") { + for (let key in NativeComponent) if (key != "displayName" && key != "name" && (typeof NativeComponent[key] != "function" || key.charAt(0) == key.charAt(0).toUpperCase())) { + if (key == "defaultProps") LibraryComponents[item][key] = Object.assign({}, LibraryComponents[item][key], NativeComponent[key]); + else if (!LibraryComponents[item][key]) LibraryComponents[item][key] = NativeComponent[key]; + } + if (LibraryComponents[item].ObjectProperties) for (let key of LibraryComponents[item].ObjectProperties) if (!LibraryComponents[item][key]) LibraryComponents[item][key] = {}; + } + return LibraryComponents[item] ? LibraryComponents[item] : "div"; + } + }); + + const RealFilteredMenuItems = Object.keys(RealMenuItems).filter(type => typeof RealMenuItems[type] == "function" && RealMenuItems[type].toString().replace(/[\n\t\r]/g, "").endsWith("{return null}")); + for (let type of RealFilteredMenuItems) { + let children = BDFDB.ObjectUtils.get(BDFDB.ReactUtils.hookCall(Internal.LibraryComponents.Menu, {hideScroller: true, children: BDFDB.ReactUtils.createElement(RealMenuItems[type], {})}, true), "props.children.props.children.props.children"); + let menuItem = (BDFDB.ArrayUtils.is(children) ? children : []).flat(10).filter(n => n)[0]; + if (menuItem) { + let menuItemsProps = BDFDB.ReactUtils.findValue(menuItem, "menuItemProps"); + if (menuItemsProps && menuItemsProps.id == "undefined-empty") MappedMenuItems.MenuGroup = type; + else if (menuItemsProps && menuItemsProps.role) { + switch (menuItemsProps.role) { + case "menuitemcheckbox": MappedMenuItems.MenuCheckboxItem = type; break; + case "menuitemradio": MappedMenuItems.MenuRadioItem = type; break; + case "menuitem": { + if (Object.keys(menuItem.props).includes("children")) MappedMenuItems.MenuControlItem = type; + else if (Object.keys(menuItem.props).includes("hasSubmenu")) MappedMenuItems.MenuItem = type; + break; + } + } + } + else { + let key = BDFDB.ReactUtils.findValue(menuItem, "key"); + if (typeof key == "string" && key.startsWith("separator")) MappedMenuItems.MenuSeparator = type; + } + } + } + LibraryComponents.MenuItems = new Proxy(RealFilteredMenuItems.reduce((a, v) => ({ ...a, [v]: v}), {}) , { + get: function (_, item) { + if (CustomComponents.MenuItems[item]) return CustomComponents.MenuItems[item]; + if (RealMenuItems[item]) return RealMenuItems[item]; + if (MappedMenuItems[item] && RealMenuItems[MappedMenuItems[item]]) return RealMenuItems[MappedMenuItems[item]]; + return null; + } + }); + + BDFDB.LibraryComponents = Internal.LibraryComponents; + + const keyDownTimeouts = {}; + let unfocusedWindow = false; + BDFDB.ListenerUtils.add(BDFDB, document, "keydown.BDFDBPressedKeys", e => { + if (!pressedKeys.includes(e.which)) { + BDFDB.TimeUtils.clear(keyDownTimeouts[e.which]); + pressedKeys.push(e.which); + keyDownTimeouts[e.which] = BDFDB.TimeUtils.timeout(_ => { + BDFDB.ArrayUtils.remove(pressedKeys, e.which, true); + }, 60000); + } + }); + BDFDB.ListenerUtils.add(BDFDB, document, "keyup.BDFDBPressedKeys", e => { + BDFDB.TimeUtils.clear(keyDownTimeouts[e.which]); + BDFDB.ArrayUtils.remove(pressedKeys, e.which, true); + }); + BDFDB.ListenerUtils.add(BDFDB, window, "focus.BDFDBPressedKeysReset", e => { + if (unfocusedWindow) { + pressedKeys = []; + unfocusedWindow = false; + } + }); + BDFDB.ListenerUtils.add(BDFDB, window, "blur.BDFDBPressedKeysReset", e => { + if (!document.querySelector(":hover")) unfocusedWindow = true; + }); + BDFDB.ListenerUtils.add(BDFDB, document, "mousedown.BDFDBMousePosition", e => { + mousePosition = e; + }); + + Internal.modulePatches = { + before: [ + "BlobMask", + "EmojiPickerListRow", + "MemberListItem", + "Menu", + "MessageActionsContextMenu", + "MessageHeader", + "NameContainer", + "SearchBar" + ], + after: [ + "DiscordTag", + "UseCopyIdItem", + "UserPopoutAvatar" + ], + componentDidMount: [ + "Account", + "AnalyticsContext" + ], + componentDidUpdate: [ + "Account", + "AnalyticsContext" + ] + }; + + const BDFDB_Patrons = Object.assign({}, InternalData.BDFDB_Patrons), BDFDB_Patron_Tiers = Object.assign({}, InternalData.BDFDB_Patron_Tiers); + Internal._processAvatarMount = function (user, avatar, wrapper) { + if (!user) return; + if (Node.prototype.isPrototypeOf(avatar) && (avatar.className || "").indexOf(BDFDB.disCN.bdfdbbadgeavatar) == -1) { + let role = "", note = "", color, link, addBadge = Internal.settings.general.showSupportBadges; + if (BDFDB_Patrons[user.id] && BDFDB_Patrons[user.id].active) { + link = "https://www.patreon.com/MircoWittrien"; + role = BDFDB_Patrons[user.id].text || (BDFDB_Patron_Tiers[BDFDB_Patrons[user.id].tier] || {}).text; + note = BDFDB_Patrons[user.id].text && (BDFDB_Patron_Tiers[BDFDB_Patrons[user.id].tier] || {}).text; + color = BDFDB_Patrons[user.id].color; + avatar.className = BDFDB.DOMUtils.formatClassName(avatar.className, addBadge && BDFDB.disCN.bdfdbhasbadge, BDFDB.disCN.bdfdbbadgeavatar, BDFDB.disCN.bdfdbsupporter, BDFDB.disCN[`bdfdbsupporter${BDFDB_Patrons[user.id].tier}`]); + } + else if (user.id == InternalData.myId) { + addBadge = true; + role = `Theme ${BDFDB.LanguageUtils.LibraryStrings.developer}`; + avatar.className = BDFDB.DOMUtils.formatClassName(avatar.className, addBadge && BDFDB.disCN.bdfdbhasbadge, BDFDB.disCN.bdfdbbadgeavatar, BDFDB.disCN.bdfdbdev); + } + if (addBadge && role && !avatar.querySelector(BDFDB.dotCN.bdfdbbadge)) { + let badge = document.createElement("div"); + badge.className = BDFDB.disCN.bdfdbbadge; + badge.setAttribute("user-id", user.id); + if (link) badge.addEventListener("click", _ => BDFDB.DiscordUtils.openLink(link)); + badge.addEventListener("mouseenter", _ => BDFDB.TooltipUtils.create(badge, role, {position: "top", note: note, backgroundColor: color || ""})); + avatar.appendChild(badge); + } + } + }; + Internal._processAvatarRender = function (user, avatar, className) { + if (BDFDB.ReactUtils.isValidElement(avatar) && BDFDB.ObjectUtils.is(user) && (avatar.props.className || "").indexOf(BDFDB.disCN.bdfdbbadgeavatar) == -1) { + let role = "", note = "", color, link, addBadge = Internal.settings.general.showSupportBadges; + if (BDFDB_Patrons[user.id] && BDFDB_Patrons[user.id].active) { + link = "https://www.patreon.com/MircoWittrien"; + role = BDFDB_Patrons[user.id].text || (BDFDB_Patron_Tiers[BDFDB_Patrons[user.id].tier] || {}).text; + note = BDFDB_Patrons[user.id].text && (BDFDB_Patron_Tiers[BDFDB_Patrons[user.id].tier] || {}).text; + color = BDFDB_Patrons[user.id].color; + className = BDFDB.DOMUtils.formatClassName(avatar.props.className, className, addBadge && BDFDB.disCN.bdfdbhasbadge, BDFDB.disCN.bdfdbbadgeavatar, BDFDB.disCN.bdfdbsupporter, BDFDB.disCN[`bdfdbsupporter${BDFDB_Patrons[user.id].tier}`]); + } + else if (user.id == InternalData.myId) { + addBadge = true; + role = `Theme ${BDFDB.LanguageUtils.LibraryStrings.developer}`; + className = BDFDB.DOMUtils.formatClassName(avatar.props.className, className, BDFDB.disCN.bdfdbhasbadge, BDFDB.disCN.bdfdbbadgeavatar, BDFDB.disCN.bdfdbdev); + } + if (role) { + if (avatar.type == "img") avatar = BDFDB.ReactUtils.createElement(Internal.LibraryComponents.Avatars.Avatar, Object.assign({}, avatar.props, { + size: Internal.LibraryComponents.AvatarConstants.Sizes.SIZE_40 + })); + delete avatar.props.className; + let newProps = { + className: className, + children: [avatar] + }; + avatar = BDFDB.ReactUtils.createElement("div", newProps); + if (addBadge) avatar.props.children.push(BDFDB.ReactUtils.createElement(Internal.LibraryComponents.TooltipContainer, { + text: role, + note: note, + tooltipConfig: {backgroundColor: color || ""}, + onClick: link ? (_ => BDFDB.DiscordUtils.openLink(link)) : (_ => {}), + children: BDFDB.ReactUtils.createElement("div", { + className: BDFDB.disCN.bdfdbbadge, + "user-id": user.id + }) + })); + return avatar; + } + } + }; + + Internal.processAccount = function (e) { + Internal._processAvatarMount(e.instance.props.currentUser, e.node.querySelector(BDFDB.dotCN.avatarwrapper), e.node); + }; + Internal.processAnalyticsContext = function (e) { + if (e.instance.props.section != Internal.DiscordConstants.AnalyticsSections.PROFILE_MODAL && e.instance.props.section != Internal.DiscordConstants.AnalyticsSections.PROFILE_POPOUT) return; + const user = BDFDB.ReactUtils.findValue(e.instance, "user"); + if (!user) return; + const avatar = e.instance.props.section != Internal.DiscordConstants.AnalyticsSections.PROFILE_POPOUT && e.node.querySelector(BDFDB.dotCN.avatarwrapper); + const wrapper = e.node.querySelector(BDFDB.dotCNC.userpopoutouter + BDFDB.dotCN.userprofilemodal) || e.node; + if (avatar) Internal._processAvatarMount(user, avatar, wrapper); + }; + Internal.processBlobMask = function (e) { + if (!e.component.prototype || BDFDB.PatchUtils.isPatched(BDFDB, e.component.prototype, "render")) return; + + let newBadges = ["lowerLeftBadge", "upperLeftBadge"]; + let extraDefaultProps = {}; + for (let type of newBadges) extraDefaultProps[`${type}Width`] = 16; + + BDFDB.PatchUtils.patch(BDFDB, e.component.prototype, "render", { + before: e2 => { + e2.instance.props = Object.assign({}, e.component.defaultProps, extraDefaultProps, e2.instance.props); + for (let type of newBadges) if (!e2.instance.state[`${type}Mask`]) e2.instance.state[`${type}Mask`] = new Internal.LibraryComponents.Animations.Controller({spring: 0}); + }, + after: e2 => { + let [tChildren, tIndex] = BDFDB.ReactUtils.findParent(e2.returnValue, {name: "TransitionGroup"}); + if (tIndex > -1) { + tChildren[tIndex].props.children.push(!e2.instance.props.lowerLeftBadge ? null : BDFDB.ReactUtils.createElement(Internal.LibraryComponents.BadgeAnimationContainer, { + className: BDFDB.disCN.guildlowerleftbadge, + key: "lower-left-badge", + animatedStyle: _ => { + const spring = e2.instance.state.lowerLeftBadgeMask.springs.spring; + return { + opacity: spring.to([0, .5, 1], [0, 0, 1]), + transform: spring.to(value => "translate(" + -1 * (16 - 16 * value) + "px, " + (16 - 16 * value) + "px)") + }; + }, + children: e2.instance.props.lowerLeftBadge + })); + tChildren[tIndex].props.children.push(!e2.instance.props.upperLeftBadge ? null : BDFDB.ReactUtils.createElement(Internal.LibraryComponents.BadgeAnimationContainer, { + className: BDFDB.disCN.guildupperleftbadge, + key: "upper-left-badge", + animatedStyle: _ => { + const spring = e2.instance.state.upperLeftBadgeMask.springs.spring; + return { + opacity: spring.to([0, .5, 1], [0, 0, 1]), + transform: spring.to(value => "translate(" + -1 * (16 - 16 * value) + "px, " + -1 * (16 - 16 * value) + "px)") + }; + }, + children: e2.instance.props.upperLeftBadge + })); + } + let [mChildren, mIndex] = BDFDB.ReactUtils.findParent(e2.returnValue, {type: "mask"}); + if (mIndex > -1) { + mChildren[mIndex].props.children.push(BDFDB.ReactUtils.createElement(Internal.LibraryComponents.Animations.animated.rect, { + x: -4, + y: -4, + width: e2.instance.props.upperLeftBadgeWidth + 8, + height: 24, + rx: 12, + ry: 12, + transform: e2.instance.state.upperLeftBadgeMask.springs.spring.to([0, 1], [20, 0]).to(value => `translate(${value * -1} ${value * -1})`), + fill: "black" + })); + mChildren[mIndex].props.children.push(BDFDB.ReactUtils.createElement(Internal.LibraryComponents.Animations.animated.rect, { + x: -4, + y: 28, + width: e2.instance.props.lowerLeftBadgeWidth + 8, + height: 24, + rx: 12, + ry: 12, + transform: e2.instance.state.lowerLeftBadgeMask.springs.spring.to([0, 1], [20, 0]).to(value => `translate(${value * -1} ${value * 1})`), + fill: "black" + })); + } + } + }, {name: "BlobMask"}); + BDFDB.PatchUtils.patch(BDFDB, e.component.prototype, "componentDidMount", { + after: e2 => { + for (let type of newBadges) e2.instance.state[`${type}Mask`].update({ + spring: e2.instance.props[type] != null ? 1 : 0, + immediate: true + }).start(); + } + }, {name: "BlobMask"}); + BDFDB.PatchUtils.patch(BDFDB, e.component.prototype, "componentWillUnmount", { + after: e2 => { + for (let type of newBadges) if (e2.instance.state[`${type}Mask`]) e2.instance.state[`${type}Mask`].dispose(); + } + }); + BDFDB.PatchUtils.patch(BDFDB, e.component.prototype, "componentDidUpdate", { + after: e2 => { + for (let type of newBadges) if (e2.instance.props[type] != null && e2.methodArguments[0][type] == null) { + e2.instance.state[`${type}Mask`].update({ + spring: 1, + immediate: !document.hasFocus(), + config: {friction: 30, tension: 900, mass: 1} + }).start(); + } + else if (e2.instance.props[type] == null && e2.methodArguments[0][type] != null) { + e2.instance.state[`${type}Mask`].update({ + spring: 0, + immediate: !document.hasFocus(), + config: {duration: 150, friction: 10, tension: 100, mass: 1} + }).start(); + } + } + }, {name: "BlobMask"}); + }; + Internal.processDiscordTag = function (e) { + if (e.instance && e.instance.props && e.returnvalue && e.instance.props.user) e.returnvalue.props.user = e.instance.props.user; + }; + Internal.processEmojiPickerListRow = function (e) { + if (e.instance.props.emojiDescriptors && Internal.LibraryComponents.EmojiPickerButton.current && Internal.LibraryComponents.EmojiPickerButton.current.props && Internal.LibraryComponents.EmojiPickerButton.current.props.allowManagedEmojisUsage) for (let i in e.instance.props.emojiDescriptors) e.instance.props.emojiDescriptors[i] = Object.assign({}, e.instance.props.emojiDescriptors[i], {isDisabled: false}); + }; + var memberStore = {}; + Internal.processMemberListItem = function (e) { + if (!memberStore || !memberStore.channel || memberStore.channel.id != e.instance.props.channel.id) memberStore = {channel: e.instance.props.channel, members: {}}; + let src = BDFDB.UserUtils.getAvatar(e.instance.props.user.id); + if (!src) return; + memberStore.members[(src.split(".com")[1] || src).split("/").slice(0, 3).join("/").split(".")[0] + " " + e.instance.props.user.username] = e.instance.props.user; + }; + Internal.processNameContainer = function (e) { + if (e.instance.props.innerClassName != BDFDB.disCN.memberinner || !memberStore || !memberStore.members) return; + let avatar = BDFDB.ReactUtils.findChild(e.instance.props.avatar, {props: ["src"]}); + if (!avatar) return; + let src = avatar.props._originalSrc || avatar.props.src; + if (!src) return; + src = (src.split(".com")[1] || src).split("/").slice(0, 3).join("/").split(".")[0]; + let username = avatar.props["aria-label"]; + if (!memberStore.members[src + " " + username]) return; + e.instance.props.user = memberStore.members[src + " " + username]; + e.instance.props.channel = memberStore.channel; + e.instance.props.avatar = Internal._processAvatarRender(e.instance.props.user, e.instance.props.avatar) || e.instance.props.avatar; + }; + Internal.processMenu = function (e) { + if (e.instance.props && (!e.instance.props.children || BDFDB.ArrayUtils.is(e.instance.props.children) && !e.instance.props.children.length)) Internal.LibraryModules.ContextMenuUtils.closeContextMenu(); + }; + Internal.processMessageActionsContextMenu = function (e) { + e.instance.props.updatePosition = _ => {}; + }; + Internal.processMessageHeader = function (e) { + if (e.instance.props.message && e.instance.props.message.author) { + if (e.instance.props.avatar && e.instance.props.avatar.props && typeof e.instance.props.avatar.props.children == "function") { + let renderChildren = e.instance.props.avatar.props.children; + e.instance.props.avatar.props.children = BDFDB.TimeUtils.suppress((...args) => { + let renderedChildren = renderChildren(...args); + return Internal._processAvatarRender(e.instance.props.message.author, renderedChildren, BDFDB.disCN.messageavatar) || renderedChildren; + }, "Error in Avatar Render of MessageHeader!"); + } + } + }; + Internal.processSearchBar = function (e) { + if (typeof e.instance.props.query != "string") e.instance.props.query = ""; + }; + Internal.processUseCopyIdItem = function (e) { + if (!e.returnvalue) e.returnvalue = false; + }; + Internal.processUserPopoutAvatar = function (e) { + if (!e.instance.props.user) return; + let [children, index] = BDFDB.ReactUtils.findParent(e.returnvalue, {props: [["className", BDFDB.disCN.userpopoutavatarwrapper]]}); + if (index > -1) children[index] = Internal._processAvatarRender(e.instance.props.user, children[index], e.instance) || children[index]; + }; + + MyReact.instanceKey = Object.keys(document.querySelector(BDFDB.dotCN.app) || {}).some(n => n.startsWith("__reactInternalInstance")) ? "_reactInternalFiber" : "_reactInternals"; + + BDFDB.PluginUtils.load(BDFDB); + Internal.settings = BDFDB.DataUtils.get(Internal); + changeLogs = BDFDB.DataUtils.load(BDFDB, "changeLogs"); + BDFDB.PluginUtils.checkChangeLog(BDFDB); + + BDFDB.PatchUtils.unpatch(BDFDB); + Internal.addModulePatches(BDFDB); + Internal.addContextPatches(BDFDB); + + const possibleRenderPaths = ["render", "type", "type.render"]; + const createElementPatches = { + before: e => { + if (!e.methodArguments[0] || typeof e.methodArguments[0] == "string") return; + let renderFunction = null; + if (typeof e.methodArguments[0] == "function") renderFunction = e.methodArguments[0]; + else for (const path of possibleRenderPaths) { + const possibleRenderFuncion = BDFDB.ObjectUtils.get(e.methodArguments[0], path); + if (typeof possibleRenderFuncion == "function") { + renderFunction = possibleRenderFuncion; + break; + } + } + if (!renderFunction || typeof renderFunction != "function") return; + if (PluginStores.modulePatches.before) for (const type in PluginStores.modulePatches.before) if (Internal.isCorrectModule(renderFunction, type, true)) { + let hasArgumentChildren = false, children = [...e.methodArguments].slice(2); + if (children.length && e.methodArguments[1].children === undefined) { + hasArgumentChildren = true; + e.methodArguments[1].children = children; + } + for (let plugin of PluginStores.modulePatches.before[type].flat(10)) Internal.initiatePatch(plugin, type, { + arguments: e.methodArguments, + instance: {props: e.methodArguments[1]}, + returnvalue: e.returnValue, + component: e.methodArguments[0], + name: type, + methodname: "render", + patchtypes: ["before"] + }); + if (hasArgumentChildren) { + [].splice.call(e.methodArguments, 2); + for (let child of e.methodArguments[1].children) [].push.call(e.methodArguments, child); + delete e.methodArguments[1].children; + } + break; + } + if (PluginStores.modulePatches.after) { + let patchFunction = "", parentModule = e.methodArguments[0]; + if (parentModule.prototype && typeof parentModule.prototype.render == "function") parentModule = parentModule.prototype, patchFunction = "render"; + else if (typeof parentModule.render == "function") patchFunction = "render"; + else if (typeof parentModule.type == "function") patchFunction = "type"; + else if (parentModule.type && typeof parentModule.type.render == "function") parentModule = parentModule.type, patchFunction = "render"; + if (patchFunction) for (const type in PluginStores.modulePatches.after) if (Internal.isCorrectModule(renderFunction, type, true)) { + for (let plugin of PluginStores.modulePatches.after[type].flat(10)) if (!BDFDB.PatchUtils.isPatched(plugin, parentModule, patchFunction)) { + BDFDB.PatchUtils.patch(plugin, parentModule, patchFunction, {after: e2 => Internal.initiatePatch(plugin, type, { + arguments: e2.methodArguments, + instance: e2.instance, + returnvalue: e2.returnValue, + component: e.methodArguments[0], + name: type, + methodname: patchFunction, + patchtypes: ["after"] + })}, {name: type}); + } + break; + } + } + if (e.methodArguments[0].prototype) for (let patchType of ["componentDidMount", "componentDidUpdate", "componentWillUnmount"]) { + if (PluginStores.modulePatches[patchType]) for (const type in PluginStores.modulePatches[patchType]) if (Internal.isCorrectModule(renderFunction, type, true)) { + for (let plugin of PluginStores.modulePatches[patchType][type].flat(10)) if (!BDFDB.PatchUtils.isPatched(plugin, e.methodArguments[0].prototype, patchType)) { + BDFDB.PatchUtils.patch(plugin, e.methodArguments[0].prototype, patchType, {after: e2 => Internal.initiatePatch(plugin, type, { + arguments: e2.methodArguments, + instance: e2.instance, + returnvalue: e2.returnValue, + component: e.methodArguments[0], + name: type, + methodname: patchType, + patchtypes: ["after"] + })}, {name: type}); + } + break; + } + } + }, + after: e => { + if (!e.methodArguments[0] || typeof e.methodArguments[0] != "function" || (e.methodArguments[0].prototype && typeof e.methodArguments[0].prototype.render == "function") || !PluginStores.modulePatches.after) return; + else for (const type in PluginStores.modulePatches.after) if (Internal.isCorrectModule(e.methodArguments[0], type, true) && !Internal.isCorrectModuleButDontPatch(type)) { + for (let plugin of PluginStores.modulePatches.after[type].flat(10)) BDFDB.PatchUtils.patch(plugin, e.returnValue, "type", {after: e2 => Internal.initiatePatch(plugin, type, { + arguments: e2.methodArguments, + instance: e2.instance, + returnvalue: e2.returnValue, + component: e.methodArguments[0], + name: type, + methodname: "type", + patchtypes: ["after"] + })}, {name: type, noCache: true}); + break; + } + } + }; + BDFDB.PatchUtils.patch(BDFDB, LibraryModules.React, "createElement", createElementPatches); + if (Internal.LibraryModules.InternalReactUtils) for (let key in Internal.LibraryModules.InternalReactUtils) if (typeof Internal.LibraryModules.InternalReactUtils[key] == "function" && Internal.LibraryModules.InternalReactUtils[key].toString().indexOf("return{$$typeof:") > -1) BDFDB.PatchUtils.patch(BDFDB, Internal.LibraryModules.InternalReactUtils, key, createElementPatches); + + let languageChangeTimeout; + BDFDB.PatchUtils.patch(BDFDB, Internal.LibraryModules.AppearanceSettingsUtils, "updateLocale", {after: e => { + BDFDB.TimeUtils.clear(languageChangeTimeout); + languageChangeTimeout = BDFDB.TimeUtils.timeout(_ => { + for (let pluginName in PluginStores.loaded) if (PluginStores.loaded[pluginName].started) BDFDB.PluginUtils.translate(PluginStores.loaded[pluginName]); + }, 10000); + }}); + + Internal.onSettingsClosed = function () { + if (Internal.SettingsUpdated) { + delete Internal.SettingsUpdated; + Internal.forceUpdateAll(); + } + }; + + Internal.forceUpdateAll = function () { + BDFDB.MessageUtils.rerenderAll(); + BDFDB.PatchUtils.forceAllUpdates(BDFDB); + }; + + BDFDB.PatchUtils.patch(BDFDB, Internal.LibraryModules.EmojiStateUtils, "getEmojiUnavailableReason", {after: e => { + if (Internal.LibraryComponents.EmojiPickerButton.current && Internal.LibraryComponents.EmojiPickerButton.current.props && Internal.LibraryComponents.EmojiPickerButton.current.props.allowManagedEmojisUsage) return null; + }}); + + Internal.forceUpdateAll(); + + const pluginQueue = window.BDFDB_Global && BDFDB.ArrayUtils.is(window.BDFDB_Global.pluginQueue) ? window.BDFDB_Global.pluginQueue : []; + + if (BDFDB.UserUtils.me.id == InternalData.myId || BDFDB.UserUtils.me.id == "350635509275557888") { + BDFDB.DevUtils = {}; + BDFDB.DevUtils.generateClassId = Internal.generateClassId; + BDFDB.DevUtils.findByIndex = function (index) { + return BDFDB.DevUtils.req.c[index]; + }; + BDFDB.DevUtils.findPropAny = function (...strings) { + window.t = {"$filter":(prop => [...strings].flat(10).filter(n => typeof n == "string").every(string => prop.toLowerCase().indexOf(string.toLowerCase()) > -1))}; + for (let i in BDFDB.DevUtils.req.c) if (BDFDB.DevUtils.req.c.hasOwnProperty(i)) { + let m = BDFDB.DevUtils.req.c[i].exports; + if (m && typeof m == "object") for (let j in m) if (window.t.$filter(j)) window.t[j + "_" + i] = m; + if (m && typeof m == "object" && typeof m.default == "object") for (let j in m.default) if (window.t.$filter(j)) window.t[j + "_default_" + i] = m.default; + } + console.clear(); + console.log(window.t); + }; + BDFDB.DevUtils.findPropFunc = function (...strings) { + window.t = {"$filter":(prop => [...strings].flat(10).filter(n => typeof n == "string").every(string => prop.toLowerCase().indexOf(string.toLowerCase()) > -1))}; + for (let i in BDFDB.DevUtils.req.c) if (BDFDB.DevUtils.req.c.hasOwnProperty(i)) { + let m = BDFDB.DevUtils.req.c[i].exports; + if (m && typeof m == "object") for (let j in m) if (window.t.$filter(j) && typeof m[j] != "string") window.t[j + "_" + i] = m; + if (m && typeof m == "object" && typeof m.default == "object") for (let j in m.default) if (window.t.$filter(j) && typeof m.default[j] != "string") window.t[j + "_default_" + i] = m.default; + } + console.clear(); + console.log(window.t); + }; + BDFDB.DevUtils.findPropStringLib = function (...strings) { + window.t = {"$filter":(prop => [...strings].flat(10).filter(n => typeof n == "string").every(string => prop.toLowerCase().indexOf(string.toLowerCase()) > -1))}; + for (let i in BDFDB.DevUtils.req.c) if (BDFDB.DevUtils.req.c.hasOwnProperty(i)) { + let m = BDFDB.DevUtils.req.c[i].exports; + if (m && typeof m == "object") for (let j in m) if (window.t.$filter(j) && typeof m[j] == "string" && /^[A-z0-9]+\-[A-z0-9_-]{6}$/.test(m[j])) window.t[j + "_" + i] = m; + if (m && typeof m == "object" && typeof m.default == "object") for (let j in m.default) if (window.t.$filter(j) && typeof m.default[j] == "string" && /^[A-z0-9]+\-[A-z0-9_-]{6}$/.test(m.default[j])) window.t[j + "_default_" + i] = m.default; + } + console.clear(); + console.log(window.t); + }; + BDFDB.DevUtils.findNameAny = function (...strings) { + window.t = {"$filter":(m => [...strings].flat(10).filter(n => typeof n == "string").some(string => typeof m.displayName == "string" && m.displayName.toLowerCase().indexOf(string.toLowerCase()) > -1 || m.name == "string" && m.name.toLowerCase().indexOf(string.toLowerCase()) > -1))}; + for (let i in BDFDB.DevUtils.req.c) if (BDFDB.DevUtils.req.c.hasOwnProperty(i)) { + let m = BDFDB.DevUtils.req.c[i].exports; + if (m && (typeof m == "object" || typeof m == "function") && window.t.$filter(m)) window.t[(m.displayName || m.name) + "_" + i] = m; + if (m && (typeof m == "object" || typeof m == "function") && m.default && (typeof m.default == "object" || typeof m.default == "function") && window.t.$filter(m.default)) window.t[(m.default.displayName || m.default.name) + "_" + i] = m.default; + } + console.clear(); + console.log(window.t); + }; + BDFDB.DevUtils.findCodeAny = function (...strings) { + window.t = {"$filter":(m => Internal.checkModuleStrings(m, strings, {ignoreCase: true}))}; + for (let i in BDFDB.DevUtils.req.c) if (BDFDB.DevUtils.req.c.hasOwnProperty(i)) { + let m = BDFDB.DevUtils.req.c[i].exports; + if (m && typeof m == "function" && window.t.$filter(m)) window.t["module_" + i] = {string: m.toString(), func: m}; + if (m && m.__esModule) { + for (let j in m) if (m[j] && typeof m[j] == "function" && window.t.$filter(m[j])) window.t[j + "_module_" + i] = {string: m[j].toString(), func: m[j], module: m}; + if (m.default && (typeof m.default == "object" || typeof m.default == "function")) for (let j in m.default) if (m.default[j] && typeof m.default[j] == "function" && window.t.$filter(m.default[j])) window.t[j + "_module_" + i + "_default"] = {string: m.default[j].toString(), func: m.default[j], module: m}; + } + } + for (let i in BDFDB.DevUtils.req.m) if (typeof BDFDB.DevUtils.req.m[i] == "function" && window.t.$filter(BDFDB.DevUtils.req.m[i])) window.t["function_" + i] = {string: BDFDB.DevUtils.req.m[i].toString(), func: BDFDB.DevUtils.req.m[i]}; + console.clear(); + console.log(window.t); + }; + BDFDB.DevUtils.getAllModules = function () { + window.t = {}; + for (let i in BDFDB.DevUtils.req.c) if (BDFDB.DevUtils.req.c.hasOwnProperty(i)) { + let m = BDFDB.DevUtils.req.c[i].exports; + if (m && typeof m == "object") window.t[i] = m; + } + console.clear(); + console.log(window.t); + }; + BDFDB.DevUtils.getAllStringLibs = function () { + window.t = []; + for (let i in BDFDB.DevUtils.req.c) if (BDFDB.DevUtils.req.c.hasOwnProperty(i)) { + let m = BDFDB.DevUtils.req.c[i].exports; + if (m && typeof m == "object" && !BDFDB.ArrayUtils.is(m) && Object.keys(m).length) { + var string = true, stringlib = false; + for (let j in m) { + if (typeof m[j] != "string") string = false; + if (typeof m[j] == "string" && /^[A-z0-9]+\-[A-z0-9_-]{6}$/.test(m[j])) stringlib = true; + } + if (string && stringlib) window.t.push(m); + } + if (m && typeof m == "object" && m.default && typeof m.default == "object" && !BDFDB.ArrayUtils.is(m.default) && Object.keys(m.default).length) { + var string = true, stringlib = false; + for (let j in m.default) { + if (typeof m.default[j] != "string") string = false; + if (typeof m.default[j] == "string" && /^[A-z0-9]+\-[A-z0-9_-]{6}$/.test(m.default[j])) stringlib = true; + } + if (string && stringlib) window.t.push(m.default); + } + } + console.clear(); + console.log(window.t); + }; + BDFDB.DevUtils.listen = function (strings) { + strings = BDFDB.ArrayUtils.is(strings) ? strings : Array.from(arguments); + BDFDB.DevUtils.listenStop(); + BDFDB.DevUtils.listen.p = BDFDB.PatchUtils.patch("WebpackSearch", BDFDB.ModuleUtils.findByProperties(strings), strings[0], {after: e => { + console.log(e); + }}); + }; + BDFDB.DevUtils.listenStop = function () { + if (typeof BDFDB.DevUtils.listen.p == "function") BDFDB.DevUtils.listen.p(); + }; + BDFDB.DevUtils.generateLanguageStrings = function (strings, config = {}) { + const language = config.language || "en"; + const languages = BDFDB.ArrayUtils.removeCopies(BDFDB.ArrayUtils.is(config.languages) ? config.languages : ["en"].concat((Internal.LibraryModules.LanguageStore.languages || Internal.LibraryModules.LanguageStore._languages).filter(n => n.enabled).map(n => { + if (BDFDB.LanguageUtils.languages[n.code]) return n.code; + else { + const code = n.code.split("-")[0]; + if (BDFDB.LanguageUtils.languages[code]) return code; + } + })).filter(n => n && !n.startsWith("en-") && !n.startsWith("$") && n != language)).sort(); + let translations = {}; + strings = BDFDB.ObjectUtils.sort(strings); + const stringKeys = Object.keys(strings); + translations[language] = BDFDB.ObjectUtils.toArray(strings); + let text = Object.keys(translations[language]).map(k => translations[language][k]).join("\n\n"); + + let fails = 0, next = lang => { + if (!lang) { + let formatTranslation = (l, s, i) => { + l = l == "en" ? "default" : l; + return config.cached && config.cached[l] && config.cached[l][stringKeys[i]] || (translations[language][i][0] == translations[language][i][0].toUpperCase() ? BDFDB.StringUtils.upperCaseFirstChar(s) : s); + }; + let format = config.asObject ? ((l, isNotFirst) => { + return `${isNotFirst ? "," : ""}\n\t\t"${l == "en" ? "default" : l}": {${translations[l].map((s, i) => `\n\t\t\t"${stringKeys[i]}": "${formatTranslation(l, s, i)}"`).join(",")}\n\t\t}`; + }) : ((l, isNotFirst) => { + return `\n\t\t\t\t\t${l == "en" ? "default" : `case "${l}"`}:${l.length > 2 ? "\t" : "\t\t"}// ${BDFDB.LanguageUtils.languages[l].name}\n\t\t\t\t\t\treturn {${translations[l].map((s, i) => `\n\t\t\t\t\t\t\t${stringKeys[i]}:${"\t".repeat(10 - ((stringKeys[i].length + 2) / 4))}"${formatTranslation(l, s, i)}"`).join(",")}\n\t\t\t\t\t\t};`; + }); + let result = Object.keys(translations).filter(n => n != "en").sort().map((l, i) => format(l, i)).join(""); + if (translations.en) result += format("en", result ? 1 : 0); + BDFDB.NotificationUtils.toast("Translation copied to clipboard", { + type: "success" + }); + BDFDB.LibraryModules.WindowUtils.copy(result); + } + else { + const callback = translation => { + BDFDB.LogUtils.log(lang); + if (!translation) { + console.warn("No Translation"); + fails++; + if (fails > 10) console.error("Skipped Language"); + else languages.unshift(lang); + } + else { + fails = 0; + translations[lang] = translation.split("\n\n"); + } + next(languages.shift()); + }; + requestFunction(`https://translate.googleapis.com/translate_a/single?client=gtx&sl=${language}&tl=${lang}&dt=t&dj=1&source=input&q=${encodeURIComponent(text)}`, (error, response, result) => { + if (!error && result && response.statusCode == 200) { + try {callback(JSON.parse(result).sentences.map(n => n && n.trans).filter(n => n).join(""));} + catch (err) {callback("");} + } + else { + if (response.statusCode == 429) { + BDFDB.NotificationUtils.toast("Too many Requests", { + type: "danger" + }); + } + else { + BDFDB.NotificationUtils.toast("Failed to translate Text", { + type: "danger" + }); + callback(""); + } + } + }); + } + }; + if (stringKeys.length) next(languages.shift()); + }; + BDFDB.DevUtils.req = Internal.getWebModuleReq(); + } + + if (libraryCSS) BDFDB.DOMUtils.appendLocalStyle("BDFDB", libraryCSS.replace(/[\n\t\r]/g, "").replace(/\[REPLACE_CLASS_([A-z0-9_]+?)\]/g, (a, b) => BDFDB.dotCN[b])); + + BDFDB.LogUtils.log("Finished loading Library"); + + window.BDFDB_Global = Object.assign({ + started: true, + loaded: true, + PluginUtils: { + buildPlugin: BDFDB.PluginUtils.buildPlugin, + cleanUp: BDFDB.PluginUtils.cleanUp + } + }); + + while (PluginStores.delayed.loads.length) PluginStores.delayed.loads.shift().load(); + while (PluginStores.delayed.starts.length) PluginStores.delayed.starts.shift().start(); + while (pluginQueue.length) { + let pluginName = pluginQueue.shift(); + if (pluginName) BDFDB.TimeUtils.timeout(_ => BDFDB.BDUtils.reloadPlugin(pluginName)); + } + }; + requestLibraryHashes(true); + + this.loaded = true; + } + start () { + if (!this.loaded) this.load(); + } + stop () { + + } + + getSettingsPanel (collapseStates = {}) { + let settingsPanel; + let getString = (type, key, property) => { + return BDFDB.LanguageUtils.LibraryStringsCheck[`settings_${key}_${property}`] ? BDFDB.LanguageUtils.LibraryStringsFormat(`settings_${key}_${property}`, BDFDB.BDUtils.getSettingsProperty("name", BDFDB.BDUtils.settingsIds[key]) || BDFDB.StringUtils.upperCaseFirstChar(key.replace(/([A-Z])/g, " $1"))) : Internal.defaults[type][key][property]; + }; + return settingsPanel = BDFDB.PluginUtils.createSettingsPanel(BDFDB, { + collapseStates: collapseStates, + children: _ => { + let settingsItems = []; + + for (let key in Internal.settings.choices) settingsItems.push(BDFDB.ReactUtils.createElement(Internal.LibraryComponents.SettingsSaveItem, { + type: "Select", + plugin: Internal, + keys: ["choices", key], + label: getString("choices", key, "description"), + note: getString("choices", key, "note"), + basis: "50%", + value: Internal.settings.choices[key], + options: Object.keys(Internal.DiscordConstants[Internal.defaults.choices[key].items] || {}).map(p => ({ + value: p, + label: BDFDB.LanguageUtils.LibraryStrings[p] || p + })), + searchable: true + })); + for (let key in Internal.settings.general) { + let nativeSetting = BDFDB.BDUtils.settingsIds[key] && BDFDB.BDUtils.getSettings(BDFDB.BDUtils.settingsIds[key]); + let disabled = typeof Internal.defaults.general[key].isDisabled == "function" && Internal.defaults.general[key].isDisabled({ + value: Internal.settings.general[key], + nativeValue: nativeSetting + }); + let hidden = typeof Internal.defaults.general[key].isHidden == "function" && Internal.defaults.general[key].isHidden({ + value: Internal.settings.general[key], + nativeValue: nativeSetting + }); + if (!hidden) settingsItems.push(BDFDB.ReactUtils.createElement(Internal.LibraryComponents.SettingsSaveItem, { + type: "Switch", + plugin: Internal, + disabled: disabled, + keys: ["general", key], + label: getString("general", key, "description"), + note: (typeof Internal.defaults.general[key].hasNote == "function" ? Internal.defaults.general[key].hasNote({ + value: Internal.settings.general[key], + nativeValue: nativeSetting, + disabled: disabled + }) : Internal.defaults.general[key].hasNote) && getString("general", key, "note"), + value: (typeof Internal.defaults.general[key].getValue == "function" ? Internal.defaults.general[key].getValue({ + value: Internal.settings.general[key], + nativeValue: nativeSetting, + disabled: disabled + }) : true) && (Internal.settings.general[key] || nativeSetting), + onChange: typeof Internal.defaults.general[key].onChange == "function" ? Internal.defaults.general[key].onChange : (_ => {}) + })); + } + settingsItems.push(BDFDB.ReactUtils.createElement(Internal.LibraryComponents.SettingsItem, { + type: "Button", + label: BDFDB.LanguageUtils.LibraryStrings.update_check_info, + dividerTop: true, + basis: "20%", + children: BDFDB.LanguageUtils.LibraryStrings.check_for_updates, + labelChildren: BDFDB.ReactUtils.createElement(Internal.LibraryComponents.Clickable, { + children: BDFDB.ReactUtils.createElement(Internal.LibraryComponents.SvgIcon, { + name: Internal.LibraryComponents.SvgIcon.Names.QUESTIONMARK, + width: 20, + height: 20, + onClick: _ => BDFDB.ModalUtils.open(Internal, { + header: "Plugins", + subHeader: "", + contentClassName: BDFDB.disCN.marginbottom20, + text: BDFDB.ObjectUtils.toArray(Object.assign({}, window.PluginUpdates && window.PluginUpdates.plugins, PluginStores.updateData.plugins)).map(p => p.name).filter(n => n).sort().join(", ") + }) + }) + }), + onClick: _ => { + let toast = BDFDB.NotificationUtils.toast(`${BDFDB.LanguageUtils.LanguageStrings.CHECKING_FOR_UPDATES} - ${BDFDB.LanguageUtils.LibraryStrings.please_wait}`, { + type: "info", + timeout: 0, + ellipsis: true + }); + BDFDB.PluginUtils.checkAllUpdates().then(outdated => { + toast.close(); + if (outdated > 0) BDFDB.NotificationUtils.toast(BDFDB.LanguageUtils.LibraryStringsFormat("update_check_complete_outdated", outdated), { + type: "danger" + }); + else BDFDB.NotificationUtils.toast(BDFDB.LanguageUtils.LibraryStrings.update_check_complete, { + type: "success" + }); + }); + } + })); + + return settingsItems; + } + }); + } + } +})(); diff --git a/BetterDiscord/plugins/0BDFDB.raw.css b/BetterDiscord/plugins/0BDFDB.raw.css new file mode 100644 index 0000000..8c083e2 --- /dev/null +++ b/BetterDiscord/plugins/0BDFDB.raw.css @@ -0,0 +1,1647 @@ +@import url(https://mwittrien.github.io/BetterDiscordAddons/Themes/_res/SupporterBadge.css); + +:root { + --bdfdb-blurple: var(--brand-experiment, hsl(235, calc(var(--saturation-factor, 1) * 85.6%), 64.7%)); +} + +img:not([src]), img[src=""], img[src="null"] { + opacity: 0; +} + +[REPLACE_CLASS_appmount] > [REPLACE_CLASS_itemlayercontainer] { + position: fixed; + z-index: 1001; +} + +[REPLACE_CLASS_titlebarmac] { + z-index: 1000001; +} + +[REPLACE_CLASS_itemlayercontainerzindexdisabled] { + position: absolute !important; + z-index: unset !important; +} +[REPLACE_CLASS_itemlayercontainerzindexdisabled] > * { + z-index: 1002; +} +[REPLACE_CLASS_itemlayer] ~ [REPLACE_CLASS_itemlayer] [REPLACE_CLASS_menu] { + z-index: 1001; +} +[REPLACE_CLASS_menu] [REPLACE_CLASS_itemlayer] { + z-index: 1002; +} + +[REPLACE_CLASS_loadingiconwrapper] { + position: absolute; + bottom: 0; + right: 0; + z-index: 1000; + animation: loadingwrapper-fade 3s infinite ease; +} +[REPLACE_CLASS_loadingiconwrapper] [REPLACE_CLASS_loadingicon] { + margin: 0 5px; +} +@keyframes loadingwrapper-fade { + from {opacity: 0.1;} + 50% {opacity: 0.9;} + to {opacity: 0.1;} +} + +[REPLACE_CLASS_settingspanellistwrapper] { + margin-bottom: 8px; +} +[REPLACE_CLASS_settingspanellistwrapper][REPLACE_CLASS_settingspanellistwrappermini] { + margin-bottom: 4px; +} +[REPLACE_CLASS_settingspanellist] { + padding-left: 15px; +} +[REPLACE_CLASS_settingspanellistwrapper][REPLACE_CLASS_settingspanellistwrappermini] [REPLACE_CLASS_settingspanellist] { + padding-left: 10px; +} + +[REPLACE_CLASS_settingsrowcontainer][REPLACE_CLASS_marginreset] { + margin-bottom: 0; +} +[REPLACE_CLASS_settingsrowcontainer][REPLACE_CLASS_marginbottom4] { + margin-bottom: 4px; +} +[REPLACE_CLASS_settingsrowcontainer][REPLACE_CLASS_marginbottom8] { + margin-bottom: 8px; +} +[REPLACE_CLASS_settingsrowcontainer][REPLACE_CLASS_marginbottom20] { + margin-bottom: 20px; +} +[REPLACE_CLASS_settingsrowcontainer][REPLACE_CLASS_marginbottom40] { + margin-bottom: 40px; +} +[REPLACE_CLASS_settingsrowcontainer][REPLACE_CLASS_marginbottom60] { + margin-bottom: 60px; +} +[REPLACE_CLASS_flexdirectionrow] > [REPLACE_CLASS_settingsrowcontainer] + [REPLACE_CLASS_settingsrowcontainer] { + margin-left: 8px; +} +[REPLACE_CLASS_settingsrowcontainer] [REPLACE_CLASS_settingsrowlabel] { + align-items: center; +} +[REPLACE_CLASS_settingsrowcontainer] [REPLACE_CLASS_settingsrowcontrol] { + margin-left: 8px; +} +[REPLACE_CLASS_settingsrowcontainer] [REPLACE_CLASS_settingsrowtitlemini] { + line-height: 18px; + font-size: 12px; + font-weight: 400; +} +[REPLACE_CLASS_h5][REPLACE_CLASS_marginreset] { + flex: 1 1 auto; +} + +[REPLACE_CLASS_switch][REPLACE_CLASS_switchmini] { + width: 26px; + height: 16px; +} +[REPLACE_CLASS_switch][REPLACE_CLASS_switchmini] [REPLACE_CLASS_switchslider] { + width: 16px; + height: 14px; + margin: 1px; +} + +[REPLACE_CLASS_accountinfobutton][REPLACE_CLASS_accountinfobuttondisabled] { + opacity: .4; +} + +[REPLACE_CLASS_marginleft4] { + margin-left: 4px; +} +[REPLACE_CLASS_marginleft8] { + margin-left: 8px; +} + +[REPLACE_CLASS_collapsecontainer] { + margin-bottom: 20px; +} +[REPLACE_CLASS_collapsecontainermini] { + margin-bottom: 8px; +} +[REPLACE_CLASS_collapsecontainerheader] { + margin-bottom: 4px; +} +[REPLACE_CLASS_collapsecontainercollapsed] [REPLACE_CLASS_collapsecontainertitle] { + margin-bottom: 0; +} +[REPLACE_CLASS_collapsecontainertitle] { + flex: 1 1 auto; + display: flex; + justify-content: space-between; + align-items: center; + cursor: pointer; + order: 1; +} +[REPLACE_CLASS_collapsecontainertitle]:hover { + color: var(--text-normal); +} +[REPLACE_CLASS_collapsecontainertitle]::before { + content: ""; + flex: 1 1 auto; + background-color: currentColor; + height: 2px; + margin: 0 10px 0 15px; + opacity: 0.2; + order: 2; +} +[REPLACE_CLASS_collapsecontainertitle]::after { + content: ""; + -webkit-mask: url('data:image/svg+xml; utf8, ') center/cover no-repeat; + background-color: currentColor; + width: 20px; + height: 20px; + order: 3; + transition: transform .3s ease; + transform: rotate(0); +} +[REPLACE_CLASS_collapsecontainercollapsed] [REPLACE_CLASS_collapsecontainertitle]::after { + transform: rotate(90deg) +} +[REPLACE_CLASS_collapsecontainerinner] { + padding-left: 15px; +} + +[REPLACE_CLASS_settingsguild] { + flex: 0 0 auto; + border-radius: 50%; + border: 3px solid #43b581; + box-sizing: border-box; + cursor: pointer; + margin: 3px; + overflow: hidden; +} +[REPLACE_CLASS_settingsguilddisabled] { + border-color: #747f8d; + filter: grayscale(100%) brightness(50%); +} + +[REPLACE_CLASS_guildslabel] { + color: var(--text-muted); + text-align: center; + text-transform: uppercase; + font-size: 9px; + font-weight: 500; + line-height: 1.3; + width: 70px; + word-wrap: normal; + white-space: nowrap; +} +[REPLACE_CLASS_guildslabel]:hover { + color: var(--header-secondary); +} +[REPLACE_CLASS_guildslabel]:active { + color: var(--header-primary); +} + +[REPLACE_CLASS_searchbarwrapper] { + padding: 10px; +} +[REPLACE_CLASS_popoutwrapper] [REPLACE_CLASS_searchbarwrapper] { + padding: 0 0 5px 0; + border-bottom: 1px solid var(--background-modifier-accent); +} + +[REPLACE_CLASS_pagination] { + display: flex; + justify-content: center; + align-items: center; + width: 100%; +} +[REPLACE_CLASS_paginationtop] { + margin-bottom: 10px; +} +[REPLACE_CLASS_paginationbottom] { + margin-top: 10px; +} +[REPLACE_CLASS_pagination] [REPLACE_CLASS_paginationcontainer] { + width: unset; + margin: 0; +} +[REPLACE_CLASS_paginationmini][REPLACE_CLASS_paginationtop] { + margin-bottom: 5px; +} +[REPLACE_CLASS_paginationmini][REPLACE_CLASS_paginationbottom] { + margin-top: 5px; +} +[REPLACE_CLASS_paginationmini] [REPLACE_CLASS_paginationbutton], +[REPLACE_CLASS_paginationmini] [REPLACE_CLASS_paginationgap] { + font-size: 14px; + margin: 4px 2px; +} +[REPLACE_CLASS_paginationmini] [REPLACE_CLASS_paginationgap] { + width: 20px; +} +[REPLACE_CLASS_paginationmini] [REPLACE_CLASS_inputwrapper] { + min-width: 54px; +} + +[REPLACE_CLASS_paginationlist] { + height: 100%; +} +[REPLACE_CLASS_paginationlistalphabet] { + display: flex; + align-items: center; + justify-content: center; + font-size: 16px; + line-height: 20px; + margin-bottom: 10px; + color: var(--text-normal); +} +[REPLACE_CLASS_paginationlistalphabetchar] { + min-width: 12px; + text-align: center; + margin: 0 4px; +} +[REPLACE_CLASS_paginationlistalphabetchar]:not([REPLACE_CLASS_paginationlistalphabetchardisabled]):hover { + color: var(--header-primary); +} +[REPLACE_CLASS_paginationlistalphabetchar][REPLACE_CLASS_paginationlistalphabetchardisabled] { + color: var(--text-muted); +} +[REPLACE_CLASS_paginationlistmini] [REPLACE_CLASS_paginationlistalphabet] { + font-size: 14px; + line-height: 18px; + margin-bottom: 5px; +} +[REPLACE_CLASS_paginationlistmini] [REPLACE_CLASS_paginationlistalphabetchar] { + min-width: 10px; + margin: 0 3px; +} + +[REPLACE_CLASS_discoverycards] { + display: grid; + grid-gap: 16px; + grid-template-columns: repeat(auto-fill, minmax(294px, 1fr)); + padding: 10px 0; +} +[REPLACE_CLASS_discoverycard] { + display: flex; + flex-direction: column; + position: relative; + background-color: var(--activity-card-background); + border: 2px solid var(--activity-card-background); + border-radius: 8px; + box-shadow: var(--elevation-medium); + height: 370px; + width: 100%; + overflow: hidden; + transition: box-shadow .2s ease-out,transform .2s ease-out,background .2s ease-out,border .2s ease-out,opacity .2s ease-in; +} +[REPLACE_CLASS_themedark] [REPLACE_CLASS_discoverycard] { + background-color: var(--background-secondary-alt); + border-color: var(--background-secondary-alt); +} +[REPLACE_CLASS_discoverycard]:focus, +[REPLACE_CLASS_discoverycard]:hover { + background-color: var(--background-tertiary); + border-color: var(--background-tertiary); + box-shadow: var(--elevation-high); + transform: translateY(-1px); +} +[REPLACE_CLASS_discoverycardheader] { + display: block; + flex: 0 0 auto; + position: relative; + height: 143px; + overflow: visible; + margin-bottom: 28px; +} +[REPLACE_CLASS_discoverycardcoverwrapper] { + display: block; + position: absolute; + top: 0; + left: 0; + background: url(https://mwittrien.github.io/_res/imgs/cardplaceholder.svg) center/cover no-repeat; + transform: scale(1); + transition: transform .2s ease-out; +} +[REPLACE_CLASS_discoverycard]:focus [REPLACE_CLASS_discoverycardcoverwrapper], +[REPLACE_CLASS_discoverycard]:hover [REPLACE_CLASS_discoverycardcoverwrapper] { + transform: scale(1.01) translateZ(0); +} +[REPLACE_CLASS_discoverycardcoverwrapper], +[REPLACE_CLASS_discoverycardcover] { + width: 100%; + height: 100%; +} +[REPLACE_CLASS_discoverycardcover] { + object-fit: cover; + cursor: pointer; +} +[REPLACE_CLASS_discoverycardcoverbadge] { + position: absolute; + top: 6px; + right: 8px; +} +[REPLACE_CLASS_discoverycardiconwrapper] { + position: absolute; + bottom: -21px; + left: 12px; + background-color: var(--activity-card-background); + border-radius: 10px; + padding: 6px; + transition: box-shadow .2s ease-out, transform .2s ease-out, background .2s ease-out; +} +[REPLACE_CLASS_themedark] [REPLACE_CLASS_discoverycardiconwrapper] { + background-color: var(--background-secondary-alt); +} +[REPLACE_CLASS_discoverycard]:focus [REPLACE_CLASS_discoverycardiconwrapper], +[REPLACE_CLASS_discoverycard]:hover [REPLACE_CLASS_discoverycardiconwrapper] { + background-color: var(--background-tertiary); +} +[REPLACE_CLASS_discoverycardicon] { + color: var(--header-primary); + border-radius: 10px; + width: 32px; + height: 32px; +} +[REPLACE_CLASS_discoverycardiconloading] { + visibility: hidden; +} +[REPLACE_CLASS_discoverycardinfo] { + display: flex; + flex: 1 1 auto; + position: relative; + flex-direction: column; + align-content: stretch; + padding: 0 16px 16px; + overflow: hidden; +} +[REPLACE_CLASS_discoverycardtitle] { + display: flex; + align-items: center; + color: var(--header-primary); + width: 100%; + font-weight: 600; + font-size: 16px; + line-height: 20px; + flex: 0 0 auto; +} +[REPLACE_CLASS_discoverycardname] { + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; + flex: 1 1 auto; +} +[REPLACE_CLASS_discoverycardtitlebutton] { + display: flex; + justify-content: center; + align-items: center; + margin-left: 6px; +} +[REPLACE_CLASS_discoverycard] [REPLACE_CLASS_favbuttoncontainer] { + width: 20px; + height: 20px; + flex: 0 0 auto; + margin-left: 3px; +} +[REPLACE_CLASS_discoverycard] [REPLACE_CLASS_favbuttoncontainer] [REPLACE_CLASS_giffavoritesize] { + width: 20px; + height: 20px; +} +[REPLACE_CLASS_discoverycardauthor] { + color: var(--header-secondary); + margin: 3px 0 6px; + font-size: 14px; + flex: 0 0 auto; +} +[REPLACE_CLASS_discoverycarddescription] { + color: var(--header-secondary); + margin: 4px 0 12px; + font-size: 14px; + line-height: 18px; +} +[REPLACE_CLASS_discoverycardfooter] { + flex: 0 0 auto; +} +[REPLACE_CLASS_discoverycardtags] { + display: flex; + flex-wrap: wrap; + align-items: center; + margin-bottom: 4px; +} +[REPLACE_CLASS_discoverycardtag] { + margin-right: 6px; + margin-bottom: 6px; + font-weight: 500; +} +[REPLACE_CLASS_discoverycardcontrols] { + display: flex; + justify-content: space-between; + align-items: flex-end; + align-items: center; + flex: 0 0 auto; +} +[REPLACE_CLASS_discoverycardbutton] { + display: flex; + justify-content: center; + align-items: center; + border-radius: 3px; + color: var(--header-primary); + font-size: 14px; + font-weight: 500; + padding: 6px 8px; +} +[REPLACE_CLASS_discoverycardstats] { + display: flex; + justify-content: center; + align-items: center; + color: var(--header-secondary); + font-size: 14px; +} +[REPLACE_CLASS_discoverycardstat] { + display: flex; + justify-content: center; + align-items: center; + margin-right: 8px; +} +[REPLACE_CLASS_discoverycardstaticon] { + margin-right: 4px; +} + +[REPLACE_CLASS_overflowellipsis] { + overflow: hidden; + text-overflow: ellipsis; +} + + +[REPLACE_CLASS_userpopoutheaderbottag] { + margin-top: 4px !important; +} + +[REPLACE_CLASS_avatardisabled] { + filter: grayscale(100%) brightness(50%); +} + +[REPLACE_CLASS_messagebottag] { + top: .15rem; +} +[REPLACE_CLASS_messageusername] ~ [REPLACE_CLASS_messagebottagcompact] { + margin-right: 0; + margin-left: .25rem; +} + +[REPLACE_CLASS_messageavatar][REPLACE_CLASS_bdfdbbadgeavatar] > :not([REPLACE_CLASS_bdfdbbadge]), +[REPLACE_CLASS_messageavatar][REPLACE_CLASS_bdfdbbadgeavatar] [REPLACE_CLASS_avatarwrapper] { + width: inherit !important; + height: inherit !important; +} +[REPLACE_CLASS_messageavatar][REPLACE_CLASS_bdfdbbadgeavatar] [REPLACE_CLASS_messageavatar] { + position: static !important; + margin: unset !important; +} + +[REPLACE_CLASS_favbuttoncontainer] { + display: flex; + position: relative; + cursor: pointer; + width: 24px; + height: 24px; +} + +[REPLACE_CLASS_menuhint] { + width: 42px; + max-width: 42px; + margin-left: 8px; +} + +[REPLACE_CLASS_cursordefault] { + cursor: default !important; +} +[REPLACE_CLASS_cursorpointer] { + cursor: pointer !important; +} + +[REPLACE_CLASS_slidergrabber]:active [REPLACE_CLASS_sliderbubble], +[REPLACE_CLASS_slidergrabber]:hover [REPLACE_CLASS_sliderbubble] { + visibility: visible; +} +[REPLACE_CLASS_sliderbubble] { + background-color: var(--background-floating); + border-radius: 3px; + top: -32px; + height: 22px; + width: auto; + padding: 0 5px; + white-space: pre; + transform: translateX(-50%); + line-height: 22px; + text-align: center; + font-weight: 600; + font-size: 12px; + color: var(--header-primary); + visibility: hidden; +} +[REPLACE_CLASS_sliderbubble], +[REPLACE_CLASS_sliderbubble]::before { + position: absolute; + left: 50%; + pointer-events: none; +} +[REPLACE_CLASS_sliderbubble]::before { + border: 5px solid transparent; + border-top-color: var(--background-floating); + content: " "; + width: 0; + height: 0; + margin-left: -5px; + top: 100%; +} + +[REPLACE_CLASS_quickselectwrapper] { + margin-left: 12px; +} + +[REPLACE_CLASS_selectwrapper] { + display: flex; + align-items: center; + flex: 1 1 auto; +} +[REPLACE_CLASS_selectwrapper] [REPLACE_CLASS_select] { + flex: 1 1 auto; +} +[REPLACE_CLASS_selectwrapper] [REPLACE_CLASS_selectouter], +[REPLACE_CLASS_selectwrapper] [REPLACE_CLASS_selectsearchinput] { + width: 100%; +} +[REPLACE_CLASS_selectoption] [REPLACE_CLASS_giffavoritesize] { + width: 20px; + height: 20px; +} + +[REPLACE_CLASS_hotkeywrapper] { + min-width: 200px; +} +[REPLACE_CLASS_hotkeywrapper] [REPLACE_CLASS_hotkeycontainer] { + flex: 1 1 auto; +} +[REPLACE_CLASS_hotkeyresetbutton] { + cursor: pointer; + margin-left: 5px; +} +[REPLACE_CLASS_hotkeyresetbutton] [REPLACE_CLASS_svgicon]:hover { + color: #f04747; +} + +[REPLACE_CLASS_hovercardwrapper] { + position: relative; + display: flex; + flex-direction: column; + align-items: center; +} +[REPLACE_CLASS_hovercardhorizontal] { + flex-direction: row; +} +[REPLACE_CLASS_hovercardhorizontal] > [REPLACE_CLASS_flexchild] + [REPLACE_CLASS_flexchild] { + margin-left: 8px; +} +[REPLACE_CLASS_hovercarddisabled] { + opacity: 0.7; + filter: grayscale(0.2); +} +[REPLACE_CLASS_settingspanel] [REPLACE_CLASS_hovercardwrapper] { + width: calc(100% - 32px); +} +[REPLACE_CLASS_hovercardwrapper][REPLACE_CLASS_hovercard] { + padding: 10px 0; +} +[REPLACE_CLASS_hovercardwrapper][REPLACE_CLASS_hovercard] > * { + z-index: 1; +} +[REPLACE_CLASS_hovercardwrapper], [REPLACE_CLASS_hovercardinner] { + min-height: 28px; +} +[REPLACE_CLASS_hovercardinner] { + width: 100%; + padding-right: 5px; + display: flex; + align-items: center; + z-index: 1; +} +[REPLACE_CLASS_hovercardwrapper] [REPLACE_CLASS_hovercardbutton] { + position: absolute; + top: -6px; + right: -6px; + opacity: 0; +} +[REPLACE_CLASS_hovercardwrapper][REPLACE_CLASS_hovercard] [REPLACE_CLASS_hovercardbutton] { + right: -25px; +} +[REPLACE_CLASS_hovercardwrapper]:hover [REPLACE_CLASS_hovercardbutton] { + opacity: 1; +} + +[REPLACE_CLASS_textareabuttonwrapper] { + display: flex; + justify-content: center; + align-items: center; +} + +[REPLACE_CLASS_guildsummarycontainer] { + display: -webkit-box; + display: -ms-flexbox; + display: flex; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; +} +[REPLACE_CLASS_guildsummarysvgicon] { + -webkit-box-flex: 0; + -ms-flex: 0 0 auto; + flex: 0 0 auto; + color: var(--text-muted); + width: 20px; + height: 20px; + margin-right: 8px; +} +[REPLACE_CLASS_guildsummaryiconcontainer] { + width: 24px; + height: 24px; +} +[REPLACE_CLASS_guildsummaryiconcontainermasked] { + margin-right: -4px; + -webkit-mask: url('data:image/svg+xml; utf8, ') center/cover no-repeat; +} +[REPLACE_CLASS_guildsummaryclickableicon] { + cursor: pointer; +} +[REPLACE_CLASS_guildsummaryicon], +[REPLACE_CLASS_guildsummaryclickableicon], +[REPLACE_CLASS_guildsummaryemptyguild] { + width: 24px; + height: 24px; + border-radius: 50%; +} +[REPLACE_CLASS_guildsummaryemptyguild] { + background: var(--background-accent); +} +[REPLACE_CLASS_guildsummarymoreguilds] { + -webkit-box-sizing: border-box; + box-sizing: border-box; + background-color: var(--background-tertiary); + font-size: 12px; + line-height: 24px; + font-weight: 500; + text-align: center; + color: var(--text-normal); + height: 24px; + min-width: 24px; + border-radius: 12px; + padding: 0 8px; +} + +[REPLACE_CLASS_table] { + position: relative; + width: 100%; +} +[REPLACE_CLASS_table] [REPLACE_CLASS_tablerow]:first-child { + padding: 0px 12px 8px 0; + margin-bottom: 5px; + box-sizing: border-box; + background-color: var(--background-primary); + border-bottom: 1px solid var(--background-modifier-accent); +} +[REPLACE_CLASS_table] [REPLACE_CLASS_tablestickyheader] { + position: sticky; + width: 100%; +} +[REPLACE_CLASS_modalsub] [REPLACE_CLASS_table] [REPLACE_CLASS_tablestickyheader]:first-child { + padding-left: 20px; +} +[REPLACE_CLASS_tableheadercell] { + color: var(--interactive-normal); + font-size: 12px; + font-weight: 600; + text-transform: uppercase; +} +[REPLACE_CLASS_tableheadercell], +[REPLACE_CLASS_tablebodycell] { + border-left: 1px solid var(--background-modifier-accent); + box-sizing: border-box; + padding: 0 12px; +} +[REPLACE_CLASS_tableheadercell]:first-child, +[REPLACE_CLASS_tablebodycell]:first-child { + border-left: none; + padding-left: 0; +} +[REPLACE_CLASS_table] [REPLACE_CLASS_tablerow] { + position: relative; + display: flex; + margin-bottom: 5px; + align-items: center; + color: var(--header-secondary); +} +[REPLACE_CLASS_tablebodycell] { + font-size: 15px; +} + +[REPLACE_CLASS_settingstableheaders] { + display: flex; + align-items: center; + flex: 1 0 auto; + margin-right: 20px; + margin-left: 10px; +} +[REPLACE_CLASS_settingstableheaderoptions] { + display: flex; + align-items: center; + justify-content: space-between; + flex: 0 0 auto; +} +[REPLACE_CLASS_settingstablelist] [REPLACE_CLASS_settingstableheader] { + min-height: 10px; + text-transform: uppercase; +} +[REPLACE_CLASS_settingstablelist] [REPLACE_CLASS_settingstableheaderoption] { + width: unset; +} +[REPLACE_CLASS_settingstablelist] [REPLACE_CLASS_settingstableheaderoption][REPLACE_CLASS_settingstableheadervertical] { + width: 24px; +} +[REPLACE_CLASS_settingstableheadervertical] { + position: relative; +} +[REPLACE_CLASS_settingstableheadervertical] > span { + position: absolute; + bottom: 50%; + right: calc(50% - 4px); + margin-bottom: -5px; + writing-mode: vertical-rl; +} +[REPLACE_CLASS_settingstablecard] { + height: 60px; + padding: 0 10px; + margin-bottom: 10px; +} +[REPLACE_CLASS_settingstablecard][REPLACE_CLASS_cardprimaryoutline] { + border: 1px solid var(--background-tertiary); +} +[REPLACE_CLASS_settingstablecardlabel] { + display: flex; + align-items: center; + flex: 1 1 auto; + overflow: hidden; + color: var(--header-primary); +} +[REPLACE_CLASS_settingstablecardconfigs] { + display: flex; + align-items: center; + justify-content: space-between; + flex: 0 0 auto; +} +[REPLACE_CLASS_settingstablecard] [REPLACE_CLASS_settingstablecardlabel] { + padding-right: 10px; +} +[REPLACE_CLASS_settingstablecard] [REPLACE_CLASS_settingstablecardlabel], +[REPLACE_CLASS_settingstablecard] [REPLACE_CLASS_settingstablecardconfigs] { + margin: 0; +} +[REPLACE_CLASS_settingstablelist] [REPLACE_CLASS_checkboxcontainer]::before { + display: none; +} + +[REPLACE_CLASS_popoutwrapper] [REPLACE_CLASS_messagespopouttabbarheader] { + flex: 1 0 auto; + align-items: center; + height: unset; + min-height: 56px; +} +[REPLACE_CLASS_popoutwrapper] [REPLACE_CLASS_messagespopouttabbarheader] [REPLACE_CLASS_messagespopouttabbar] { + align-items: center; + flex: 1 1 auto; + min-height: 32px; +} + +[REPLACE_CLASS_charcounter] { + color: var(--channels-default); +} + +[REPLACE_CLASS_inputmulti] { + display: flex; + justify-content: space-between; + align-items: center; +} +[REPLACE_CLASS_inputmultifirst] { + flex-grow: 1; + padding: 0 8px; +} +[REPLACE_CLASS_inputmultilast]::before { + content: ""; + position: absolute; + border: 1px solid var(--header-primary); + width: 1px; + height: 30px; + margin-top: 5px; + opacity: .1; +} +[REPLACE_CLASS_inputmultilast] input { + width: 250px; + padding-left: 16px; +} +[REPLACE_CLASS_inputmultifield] { + border: none !important; +} + +[REPLACE_CLASS_inputlistitems] { + display: flex; + flex-direction: row; + justify-content: flex-start; + align-items: center; + flex-wrap: wrap; +} +[REPLACE_CLASS_inputlistitem] { + display: flex; + justify-content: center; + align-items: center; +} +[REPLACE_CLASS_inputlistitem]:not(:last-child) { + margin-right: 4px; +} +[REPLACE_CLASS_inputlistdelete] { + cursor: pointer; + margin-left: 6px; +} + +[REPLACE_CLASS_dateinputwrapper] { + display: flex; + align-items: center; + margin-bottom: 8px; +} +[REPLACE_CLASS_dateinputinner] { + flex: 1 1 auto; + color: var(--header-primary); + max-width: calc(100% - 150px); + margin-left: 8px; +} +[REPLACE_CLASS_dateinputcontrols] { + display: flex; + align-items: center; +} +[REPLACE_CLASS_dateinputpreview] { + display: flex; + align-items: center; + color: var(--header-primary); + margin-top: 8px; + font-weight: normal; +} +[REPLACE_CLASS_dateinputpreviewprefix], +[REPLACE_CLASS_dateinputpreviewsuffix] { + font-weight: 600; +} +[REPLACE_CLASS_dateinputpreviewprefix]:not(:empty) { + margin-right: 6px; +} +[REPLACE_CLASS_dateinputpreviewsuffix]:not(:empty) { + margin-left: 6px; +} +[REPLACE_CLASS_dateinputfield] { + flex: 1 1 auto; +} +[REPLACE_CLASS_dateinputbutton] { + margin-left: 8px; +} +[REPLACE_CLASS_dateinputbuttonselected] [REPLACE_CLASS_svgicon], +[REPLACE_CLASS_dateinputbuttonselected] [REPLACE_CLASS_svgicon]:hover { + color: var(--interactive-active); +} + +input[REPLACE_CLASS_input][REPLACE_CLASS_inputsuccess], +input[REPLACE_CLASS_input][REPLACE_CLASS_inputerror] { + border-style: solid; + border-width: 1px; +} +[REPLACE_CLASS_inputnumberwrapper] { + position: relative; +} +[REPLACE_CLASS_inputnumberbuttons]:hover + [REPLACE_CLASS_input]:not([REPLACE_CLASS_inputfocused]):not([REPLACE_CLASS_inputerror]):not([REPLACE_CLASS_inputsuccess]):not([REPLACE_CLASS_inputdisabled]):not(:focus) { + border-color: var(--deprecated-text-input-border-hover); +} +[REPLACE_CLASS_inputnumberwrapper] [REPLACE_CLASS_input] { + text-align: right; +} +[REPLACE_CLASS_inputnumberwrapperdefault] [REPLACE_CLASS_input] { + padding-right: 25px; +} +[REPLACE_CLASS_inputnumberwrappermini] [REPLACE_CLASS_input] { + padding-left: 6px; + padding-right: 17px; +} +[REPLACE_CLASS_inputnumberwrapper] [REPLACE_CLASS_input]::-webkit-inner-spin-button, +[REPLACE_CLASS_inputnumberwrapper] [REPLACE_CLASS_input]::-webkit-outer-spin-button{ + -webkit-appearance: none !important; +} +[REPLACE_CLASS_inputnumberbuttons] { + position: absolute; + display: flex; + flex-direction: column; + align-items: center; + justify-content: space-around; + height: 110%; + top: -2%; +} +[REPLACE_CLASS_inputnumberwrapperdefault] [REPLACE_CLASS_inputnumberbuttons] { + right: 8px; +} +[REPLACE_CLASS_inputnumberwrappermini] [REPLACE_CLASS_inputnumberbuttons] { + right: 4px; +} +[REPLACE_CLASS_inputnumberbutton] { + cursor: pointer; + border: transparent solid 5px; + border-top-width: 2.5px; + display: inline-block; +} +[REPLACE_CLASS_inputnumberbutton] { + border-bottom-color: var(--interactive-normal); +} +[REPLACE_CLASS_inputnumberbutton]:hover { + border-bottom-color: var(--interactive-active); +} +[REPLACE_CLASS_inputnumberbuttondown] { + transform: rotate(180deg); +} + +[REPLACE_CLASS_guildupperleftbadge] { + top: 0; +} +[REPLACE_CLASS_guildlowerleftbadge] { + bottom: 0; +} +[REPLACE_CLASS_guildlowerleftbadge], [REPLACE_CLASS_guildupperleftbadge] { + pointer-events: none; + position: absolute; + left: 0; +} + +[REPLACE_CLASS_svgiconwrapper] { + display: flex; + justify-content: center; + align-items: center; +} +[REPLACE_CLASS_svgicon] { + color: var(--interactive-normal); +} +[REPLACE_CLASS_svgicon]:hover { + color: var(--interactive-hover); +} +[REPLACE_CLASS_svgicon]:active { + color: var(--interactive-active); +} + +[REPLACE_CLASS_listrowwrapper] [REPLACE_CLASS_listavatar] { + display: flex; + justify-content: center; + align-items: center; +} + +[REPLACE_CLASS_sidebarlist] { + display: flex; + flex-direction: row; + flex: 1 1 auto; +} +[REPLACE_CLASS_sidebar] { + padding: 8px; + flex: 0 1 auto; +} +[REPLACE_CLASS_sidebarcontent] { + padding: 8px 0; + flex: 1 1 auto; +} + +[REPLACE_CLASS_modalwrapper] { + color: var(--header-secondary); +} +[REPLACE_CLASS_modalwrapper] [REPLACE_CLASS_modalheader] > [REPLACE_CLASS_headerwrapper] { + flex: 1 0 auto; +} +[REPLACE_CLASS_modalwrapper] [REPLACE_CLASS_modalfooter] [REPLACE_CLASS_button] { + margin-left: 8px; +} +[REPLACE_CLASS_modalwrapper] [REPLACE_CLASS_modalclose] [REPLACE_CLASS_buttoncontents] { + display: flex; + justify-content: center; + align-items: center; +} +[REPLACE_CLASS_modalsubinner] { + padding-left: 16px; + padding-right: 8px; +} +[REPLACE_CLASS_modaltextcontent] { + margin-bottom: 10px; +} +[REPLACE_CLASS_modalnoscroller] { + overflow: hidden; +} +[REPLACE_CLASS_modalcontent][REPLACE_CLASS_modalnoscroller] { + padding-bottom: 10px; +} +[REPLACE_CLASS_modaltabcontent] { + margin-top: 10px; +} +[REPLACE_CLASS_listscroller] [REPLACE_CLASS_modaltabcontent] { + margin-top: 0; +} +[REPLACE_CLASS_modalheaderhassibling] { + padding-bottom: 10px; +} +[REPLACE_CLASS_modalheadershade], +[REPLACE_CLASS_modalsidebar] { + background: rgba(0, 0, 0, 0.1); +} +[REPLACE_CLASS_themedark] [REPLACE_CLASS_modalheadershade], +[REPLACE_CLASS_themedark] [REPLACE_CLASS_modalsidebar] { + background: rgba(0, 0, 0, 0.2); +} +[REPLACE_CLASS_tabbarcontainer][REPLACE_CLASS_tabbarcontainerbottom] { + border-top: unset; + border-bottom: 1px solid hsla(0,0%,100%,.1); +} +[REPLACE_CLASS_modal] [REPLACE_CLASS_tabbarcontainer] { + background: rgba(0, 0, 0, 0.1); + border: none; + box-shadow: 0 2px 3px 0 rgba(0, 0, 0, 0.05); + flex: 1 1 auto; + margin: 0; + padding: 16px 16px 0 16px; +} +[REPLACE_CLASS_themedark] [REPLACE_CLASS_modal] [REPLACE_CLASS_tabbarcontainer] { + background: rgba(0, 0, 0, 0.2); + box-shadow: 0 2px 3px 0 rgba(0, 0, 0, 0.1); +} +[REPLACE_CLASS_modalchangelogmodal] [REPLACE_CLASS_changelogsociallink] { + margin-right: 12px; +} +[REPLACE_CLASS_changelogfooter] { + align-items: center; +} + +[REPLACE_CLASS_popoutwrapper] { + position: relative; +} +[REPLACE_CLASS_popoutarrow]::before { + content: ""; + display: block; + position: absolute; + border: 8px solid var(--background-secondary); + border-right-color: transparent; + border-left-color: transparent; + left: calc(50% - 8px); + z-index: 1; +} +[REPLACE_CLASS_popoutarrowtop] { + margin-bottom: 8px; +} +[REPLACE_CLASS_popoutarrowbottom] { + margin-top: 8px; +} +[REPLACE_CLASS_popoutarrowtop]::before { + border-bottom-color: transparent; + bottom: -16px; +} +[REPLACE_CLASS_popoutarrowbottom]::before { + border-top-color: transparent; + top: -16px; +} +[REPLACE_CLASS_popoutthemedpopout] { + background-color: var(--background-secondary); + border-radius: 5px; + -webkit-box-shadow: var(--elevation-stroke),var(--elevation-high); + box-shadow: var(--elevation-stroke),var(--elevation-high); + box-sizing: border-box; + display: -webkit-box; + display: -ms-flexbox; + display: flex; + -webkit-box-orient: vertical; + -webkit-box-direction: normal; + -ms-flex-direction: column; + flex-direction: column; +} + +[REPLACE_CLASS__repomodal] { + min-height: unset; +} +[REPLACE_CLASS__repomodalheader] { + pointer-events: none; +} +[REPLACE_CLASS__repomodalsettings] { + padding: 0 16px 16px 16px; +} +[REPLACE_CLASS__repochangelogbutton] { + position: fixed; + top: 14px; + right: 14px; +} + +[REPLACE_CLASS__repo] { + display: flex; + flex-direction: column; + flex: 1 1 auto; + width: 100%; + height: 100%; +} +[REPLACE_CLASS__repo] ~ [REPLACE_CLASS_settingswindowtoolscontainer] { + position: absolute; + right: 0; + margin-right: 21px; +} +[REPLACE_CLASS__repolistheader] { + display: flex; + flex-direction: column; + padding-right: 100px; + padding-bottom: 0; +} +[REPLACE_CLASS__repolistscroller] { + padding-top: 0; +} +[REPLACE_CLASS__repolistheader] [REPLACE_CLASS_tabbarcontainer] { + display: flex; + align-items: flex-start; +} +[REPLACE_CLASS__repolistheader] [REPLACE_CLASS_quickselectwrapper] { + line-height: 20px; +} +[REPLACE_CLASS__repoentry] [REPLACE_CLASS__repofooter] { + display: flex; + align-items: center; + justify-content: space-between; +} +[REPLACE_CLASS__repoentry] [REPLACE_CLASS__repofooter] > *:only-child { + margin-left: auto; +} +[REPLACE_CLASS__repoentry] [REPLACE_CLASS__repoheadercontrols], +[REPLACE_CLASS__repoentry] [REPLACE_CLASS__repofootercontrols] { + display: flex; + align-items: center; + justify-content: center; + flex: 0 0 auto; +} +[REPLACE_CLASS__repoentry] [REPLACE_CLASS__repoheadercontrols] > *, +[REPLACE_CLASS__repoentry] [REPLACE_CLASS__repocontrols] > * { + margin-left: 10px; +} +[REPLACE_CLASS__repoentry] [REPLACE_CLASS__repofootercontrols][REPLACE_CLASS__repocontrols] > * { + margin-left: 0; +} +[REPLACE_CLASS__repoentry] [REPLACE_CLASS__repocontrolsbutton] { + display: flex; + align-items: center; + justify-content: center; + cursor: pointer; +} +[REPLACE_CLASS__repoentry] [REPLACE_CLASS__repocontrols] > [REPLACE_CLASS__repocontrolsbutton]:only-child { + border-radius: 3px; +} + +[REPLACE_CLASS_noticewrapper] { + display: flex; + justify-content: center; + align-items: center; + height: 36px !important; + border-radius: 0 !important; + transition: height 0.5s ease !important; +} +[REPLACE_CLASS_noticeclosing] { + height: 0 !important; + overflow: hidden !important; +} +[REPLACE_CLASS_noticetext] { + display: inline-flex; + flex: 0 1 auto; + white-space: pre; + overflow: hidden; +} +[REPLACE_CLASS_noticewrapper] [REPLACE_CLASS_noticeplatformicon] { + display: inline-flex; + flex: 0 0 auto; + position: static; + margin: 0 10px 0 0; +} +[REPLACE_CLASS_noticewrapper] svg[REPLACE_CLASS_noticeplatformicon] { + max-height: 28px; + width: unset; +} +[REPLACE_CLASS_noticewrapper] [REPLACE_CLASS_noticebutton] { + display: inline-flex; + flex: 0 0 auto; + position: static; +} + +#pluginNotice .notice-message { + white-space: pre; +} +#pluginNotice #outdatedPlugins { + font-weight: 700; +} +#pluginNotice #outdatedPlugins span { + -webkit-app-region: no-drag; + color: #fff; + cursor: pointer; +} +#pluginNotice #outdatedPlugins span:hover { + text-decoration: underline; +} +[REPLACE_CLASS_noticeupdateentries] { + display: flex; + font-weight: 700; +} +[REPLACE_CLASS_noticeupdateentry] { + cursor: pointer; + -webkit-app-region: no-drag; +} +[REPLACE_CLASS_noticeupdateentry]:hover { + text-decoration: underline; +} +[REPLACE_CLASS_noticeupdateseparator] { + pointer-events: none; +} + +.platform-osx [REPLACE_CLASS_noticewrapper] ~ * [REPLACE_CLASS_guildswrapper] { + margin-top: 0; +} +.platform-osx [REPLACE_CLASS_noticewrapper] ~ * [REPLACE_CLASS_guildsscroller] { + padding-top: 4px; +} + +[REPLACE_CLASS_tooltiprow]:has([REPLACE_CLASS_usersummarycontainer]:empty) { + display: none; +} +[REPLACE_CLASS_tooltipnote] { + color: var(--text-muted); + font-size: 11px; + margin-top: 2px; +} +[REPLACE_CLASS_tooltip][REPLACE_CLASS_tooltipcustom], +[REPLACE_CLASS_tooltiplistitem][REPLACE_CLASS_tooltipcustom] { + color: #fff; +} +[REPLACE_CLASS_tooltip]:has([REPLACE_CLASS_tooltipcontent]:empty) { + display: none !important; +} +[REPLACE_CLASS_tooltip][REPLACE_CLASS_tooltipcustom] [REPLACE_CLASS_tooltipnote], +[REPLACE_CLASS_tooltiplistitem][REPLACE_CLASS_tooltipcustom] [REPLACE_CLASS_tooltipnote] { + color: #bbb; +} +[REPLACE_CLASS_tooltipcustom] [REPLACE_CLASS_tooltippointer] { + border-top-color: inherit !important; +} +[REPLACE_CLASS_tooltipcustom] [REPLACE_CLASS_tooltipguildnametext], +[REPLACE_CLASS_tooltipcustom] [REPLACE_CLASS_tooltipmutetext] { + color: inherit; +} +[REPLACE_CLASS_tooltipcustom] [REPLACE_CLASS_tooltipmutetext] { + opacity: .7; +} +[REPLACE_CLASS_guildvoicelist] ~ [REPLACE_CLASS_tooltipmutetext] { + margin-top: 8px; +} +[REPLACE_CLASS_tooltiprowextra]:empty { + display: none; +} +[REPLACE_CLASS_tooltiprowextra]:not(:empty) [REPLACE_CLASS_tooltipmutetext] { + margin-top: 8px; +} + +[REPLACE_CLASS_colorpickerswatches] > div:not([class]), +[REPLACE_CLASS_colorpickerswatches] [REPLACE_CLASS_colorpickerswatch] { + flex: 1 1 auto; +} +[REPLACE_CLASS_colorpickerswatchsinglewrapper] { + position: relative; + z-index: 1; +} +[REPLACE_CLASS_colorpickerswatchsingle] { + height: 30px; + width: 30px; +} +[REPLACE_CLASS_colorpickerswatchesdisabled] { + cursor: no-drop; + filter: grayscale(70%) brightness(50%); +} +[REPLACE_CLASS_colorpickerswatch][REPLACE_CLASS_colorpickerswatchnocolor] { + border-color: var(--text-muted); +} +[REPLACE_CLASS_colorpickerswatch][style*="background: rgb(255, 255, 255)"] { + border-color: var(--header-primary) !important; +} +[REPLACE_CLASS_colorpickerswatch][REPLACE_CLASS_colorpickerswatchnocolor] [REPLACE_CLASS_colorpickerswatchdropperfg] { + border-color: var(--text-muted); +} +[REPLACE_CLASS_colorpickerswatch]:not([REPLACE_CLASS_colorpickerswatchnocolor]):not([REPLACE_CLASS_colorpickerswatchdefault]):not([REPLACE_CLASS_colorpickerswatchdisabled]) { + overflow: hidden; +} +[REPLACE_CLASS_colorpickerswatch][REPLACE_CLASS_colorpickerswatchcustom][style*="background"] { + border: none; +} +[REPLACE_CLASS_colorpickerswatch]:not([REPLACE_CLASS_colorpickerswatchdefault])::after { + border-radius: 3px; +} +[REPLACE_CLASS_colorpickerswatch][REPLACE_CLASS_colorpickerswatchcustom]:not([REPLACE_CLASS_colorpickerswatchdefault])::after { + border-radius: 5px; +} +[REPLACE_CLASS_colorpickerswatch]:not([REPLACE_CLASS_colorpickerswatchnocolor]):not([REPLACE_CLASS_colorpickerswatchdefault]):not([REPLACE_CLASS_colorpickerswatchdisabled])::after { + content: ""; + background: url('data:image/svg+xml; utf8, ') center repeat; + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + z-index: -1; +} + +[REPLACE_CLASS_colorpickeralpha] { + position: relative; + height: 8px; + margin: 16px 0 8px; +} +[REPLACE_CLASS_colorpickergradient] { + position: relative; + height: 8px; + margin: 27px 2px 2px 2px; +} +[REPLACE_CLASS_colorpickeralpha] > div > div > div > div { + height: 16px !important; + width: 8px !important; + margin-top: -3px !important; + border-radius: 3px !important; +} +[REPLACE_CLASS_colorpickersaturation] > div > div > div > div { + box-shadow: var(--header-primary) 0px 0px 0px 1.5px, var(--background-floating) 0px 0px 1px 1px inset, var(--background-floating) 0px 0px 1px 2px !important; +} +[REPLACE_CLASS_colorpickerhue] > div > div > div > div, +[REPLACE_CLASS_colorpickeralpha] > div > div > div > div { + background: var(--header-primary) !important; + box-shadow: var(--background-floating) 0px 0px 2px !important; +} +[REPLACE_CLASS_colorpickeralpha] > div > div, +[REPLACE_CLASS_colorpickergradient] > div > div { + border-radius: 3px; +} +[REPLACE_CLASS_colorpickeralpha] [REPLACE_CLASS_colorpickeralphacheckered], +[REPLACE_CLASS_colorpickergradient] [REPLACE_CLASS_colorpickergradientcheckered], +[REPLACE_CLASS_colorpickergradient] [REPLACE_CLASS_colorpickergradientcursor] > div::after { + background: url('data:image/svg+xml; utf8, ') center repeat; +} +[REPLACE_CLASS_colorpickergradient] [REPLACE_CLASS_colorpickergradientcursor] > div { + height: 8px; + width: 8px; + margin-top: -15px; + border: 1px solid rgb(128, 128, 128); + border-radius: 3px; + transform: translateX(-5px); + transform-style: preserve-3d; +} +[REPLACE_CLASS_colorpickergradient] [REPLACE_CLASS_colorpickergradientcursor] > div::after { + content: ""; + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + z-index: -1; + border-radius: 3px; + transform: translateZ(-1px); +} +[REPLACE_CLASS_colorpickergradient] [REPLACE_CLASS_colorpickergradientcursor] > div::before { + content: ""; + position: absolute; + border: 3px solid transparent; + border-top-width: 5px; + border-top-color: rgb(128, 128, 128); + width: 0; + height: 0; + top: 100%; + left: -50%; + transform: translateX(5px); +} +[REPLACE_CLASS_colorpickergradient] [REPLACE_CLASS_colorpickergradientcursor][REPLACE_CLASS_colorpickergradientcursoredge] > div::before { + border-right-width: 0; + border-left-width: 5px; +} +[REPLACE_CLASS_colorpickergradient] [REPLACE_CLASS_colorpickergradientcursor][REPLACE_CLASS_colorpickergradientcursoredge] ~ [REPLACE_CLASS_colorpickergradientcursor][REPLACE_CLASS_colorpickergradientcursoredge] > div::before { + border-right-width: 5px; + border-left-width: 0; +} +[REPLACE_CLASS_colorpickergradient] [REPLACE_CLASS_colorpickergradientcursor][REPLACE_CLASS_colorpickergradientcursorselected] > div { + border-color: var(--header-secondary); + width: 10px; + height: 10px; + margin-top: -17px; + transform: translateX(-6px); +} +[REPLACE_CLASS_colorpickergradient] [REPLACE_CLASS_colorpickergradientcursor][REPLACE_CLASS_colorpickergradientcursorselected] > div::before { + border-top-color: var(--header-secondary); + transform: translateX(7px); +} +[REPLACE_CLASS_colorpickergradientbutton] { + color: var(--interactive-normal); + opacity: 0.6; + margin-left: 6px; + transition: color 200ms ease, opactity 200ms ease; +} +[REPLACE_CLASS_colorpickergradientbutton]:hover { + color: var(--interactive-hover); + opacity: 1; +} +[REPLACE_CLASS_colorpickergradientbutton][REPLACE_CLASS_colorpickergradientbuttonenabled], +[REPLACE_CLASS_colorpickergradientbutton][REPLACE_CLASS_colorpickergradientbuttonenabled]:hover { + color: var(--interactive-active); + opacity: 1; +} + +[REPLACE_CLASS_modallarge], [REPLACE_CLASS_modalsublarge] { + max-height: 95vh; +} +@media only screen and (max-height: 900px) { + [REPLACE_CLASS_modalmedium], [REPLACE_CLASS_modalsubmedium] { + max-height: 75vh; + } +} + +[REPLACE_CLASS_peoplesnowplayingmember] [REPLACE_CLASS_nametag] { + pointer-events: none; +} + +.platform-win [REPLACE_CLASS_toasts] { + padding-top: 22px; +} +[REPLACE_CLASS_toasts] { + position: absolute; + top: 10px; + right: 10px; + left: 10px; + display: flex; + flex-direction: column; + pointer-events: none; + z-index: 100001; +} +[REPLACE_CLASS_toasts][REPLACE_CLASS_toastsleft] { + bottom: 10px; + justify-content: flex-start; + align-items: flex-start; +} +[REPLACE_CLASS_toasts][REPLACE_CLASS_toastscenter] { + bottom: 80px; + justify-content: flex-end; + align-items: center; +} +[REPLACE_CLASS_toasts][REPLACE_CLASS_toastsright] { + bottom: 10px; + justify-content: flex-start; + align-items: flex-end; +} +[REPLACE_CLASS_toast] { + position: relative; + display: flex; + align-items: center; + backdrop-filter: blur(5px); + border-radius: 3px; + box-shadow: var(--elevation-medium); + margin-top: 10px; + padding: 10px 10px 12px 10px; + overflow: hidden; + max-width: 50vw; + min-height: 24px; + height: unset; + opacity: 1; + transform: scale(1); + transition: all 300ms ease; +} +[REPLACE_CLASS_toastclosable] { + cursor: pointer; + pointer-events: auto; +} +[REPLACE_CLASS_toasts][REPLACE_CLASS_toastsleft] [REPLACE_CLASS_toast] { + min-width: 200px; + transform-origin: left top; +} +[REPLACE_CLASS_toasts][REPLACE_CLASS_toastscenter] [REPLACE_CLASS_toast] { + min-width: 100px; + transform-origin: center bottom; +} +[REPLACE_CLASS_toasts][REPLACE_CLASS_toastsright] [REPLACE_CLASS_toast] { + min-width: 200px; + transform-origin: right top; +} +[REPLACE_CLASS_toastopening], +[REPLACE_CLASS_toastclosing] { + opacity: 0; + transform: scale(0); +} +[REPLACE_CLASS_toastclosing] { + min-height: 0; + height: 0; +} +[REPLACE_CLASS_toastbg] { + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + opacity: 0.9; + z-index: 1; +} +[REPLACE_CLASS_toastinner] { + position: relative; + display: flex; + align-items: center; + flex: 1 0 auto; + z-index: 2; +} +[REPLACE_CLASS_toasticon] { + display: flex; + justify-content: center; + align-items: center; + flex: 0 0 auto; + margin-right: 10px; +} +[REPLACE_CLASS_toastavatar] { + width: 24px; + height: 24px; +} +[REPLACE_CLASS_toasttext] { + max-width: calc(50vw - 50px); + flex: 1 0 auto; + font-size: 14px; + font-weight: 500; + white-space: pre-wrap; + word-wrap: break-word; +} +[REPLACE_CLASS_toastcloseicon] { + display: none; + margin-left: 6px; +} +[REPLACE_CLASS_toast]:hover [REPLACE_CLASS_toastcloseicon] { + display: block; +} +[REPLACE_CLASS_toastbar] { + position: absolute; + right: 0; + bottom: 0; + left: 0; + height: 4px; + background: black; + opacity: 0.3; + z-index: 3; + transition: all; +} +[REPLACE_CLASS_toast]:hover [REPLACE_CLASS_toastbar] { + animation-play-state: paused !important; +} +[REPLACE_CLASS_toastclosing] [REPLACE_CLASS_toastbar] { + animation: unset !important; + right: 0 !important; + transition: all !important; +} +@keyframes toast-bar { + from {right: 100%;} + to {right: 0;} +} +[REPLACE_CLASS_toastdefault] [REPLACE_CLASS_toastbar] { + background: var(--header-primary); +} +[REPLACE_CLASS_toastbar][REPLACE_CLASS_toastcustombar] { + opacity: 1; +} +[REPLACE_CLASS_toastdefault] [REPLACE_CLASS_toastbg] { + background-color: var(--background-floating); +} +[REPLACE_CLASS_toastdefault] [REPLACE_CLASS_toastinner] { + color: var(--header-primary); +} +[REPLACE_CLASS_toastbrand] [REPLACE_CLASS_toastbg] { + background-color: var(--bdfdb-blurple); +} +[REPLACE_CLASS_toastbrand] [REPLACE_CLASS_toastinner] { + color: #fff; +} +[REPLACE_CLASS_toastdanger] [REPLACE_CLASS_toastbg] { + background-color: var(--status-danger); +} +[REPLACE_CLASS_toastdanger] [REPLACE_CLASS_toastinner] { + color: var(--status-danger-text); +} +[REPLACE_CLASS_toastinfo] [REPLACE_CLASS_toastbg] { + background-color: #4A90E2; +} +[REPLACE_CLASS_toastinfo] [REPLACE_CLASS_toastinner] { + color: #fff; +} +[REPLACE_CLASS_toastsuccess] [REPLACE_CLASS_toastbg] { + background-color: var(--status-positive); +} +[REPLACE_CLASS_toastsuccess] [REPLACE_CLASS_toastinner] { + color: var(--status-positive-text); +} +[REPLACE_CLASS_toastwarning] [REPLACE_CLASS_toastbg] { + background-color: var(--status-warning); +} +[REPLACE_CLASS_toastwarning] [REPLACE_CLASS_toastinner] { + color: var(--status-warning-text); +} diff --git a/BetterDiscord/plugins/0PluginLibrary.plugin.js b/BetterDiscord/plugins/0PluginLibrary.plugin.js new file mode 100644 index 0000000..4a28948 --- /dev/null +++ b/BetterDiscord/plugins/0PluginLibrary.plugin.js @@ -0,0 +1,6325 @@ +/** + * @name ZeresPluginLibrary + * @description Gives other plugins utility functions. + * @version 2.0.19 + * @author Zerebos + * @source https://github.com/rauenzi/BDPluginLibrary + */ + +/*@cc_on +@if (@_jscript) + + // Offer to self-install for clueless users that try to run this directly. + var shell = WScript.CreateObject("WScript.Shell"); + var fs = new ActiveXObject("Scripting.FileSystemObject"); + var pathPlugins = shell.ExpandEnvironmentStrings("%APPDATA%\\BetterDiscord\\plugins"); + var pathSelf = WScript.ScriptFullName; + // Put the user at ease by addressing them in the first person + shell.Popup("It looks like you've mistakenly tried to run me directly. \n(Don't do that!)", 0, "I'm a plugin for BetterDiscord", 0x30); + if (fs.GetParentFolderName(pathSelf) === fs.GetAbsolutePathName(pathPlugins)) { + shell.Popup("I'm in the correct folder already.", 0, "I'm already installed", 0x40); + } else if (!fs.FolderExists(pathPlugins)) { + shell.Popup("I can't find the BetterDiscord plugins folder.\nAre you sure it's even installed?", 0, "Can't install myself", 0x10); + } else if (shell.Popup("Should I copy myself to BetterDiscord's plugins folder for you?", 0, "Do you need some help?", 0x34) === 6) { + fs.CopyFile(pathSelf, fs.BuildPath(pathPlugins, fs.GetFileName(pathSelf)), true); + // Show the user where to put plugins in the future + shell.Exec("explorer " + pathPlugins); + shell.Popup("I'm installed!", 0, "Successfully installed", 0x40); + } + WScript.Quit(); + +@else@*/ + +/******/ (() => { // webpackBootstrap +/******/ var __webpack_modules__ = ({ + +/***/ "./src/styles/settings.css": +/*!*********************************!*\ + !*** ./src/styles/settings.css ***! + \*********************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__) +/* harmony export */ }); +/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (".plugin-input-group {\r\n margin-top: 5px;\r\n}\r\n\r\n.plugin-input-group .button-collapse {\r\n background: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4NCjwhLS0gR2VuZXJhdG9yOiBBZG9iZSBJbGx1c3RyYXRvciAxOS4wLjAsIFNWRyBFeHBvcnQgUGx1Zy1JbiAuIFNWRyBWZXJzaW9uOiA2LjAwIEJ1aWxkIDApICAtLT4NCjxzdmcgdmVyc2lvbj0iMS4xIiBpZD0iQ2FscXVlXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4Ig0KCSB2aWV3Qm94PSItOTUwIDUzMiAxOCAxOCIgc3R5bGU9ImVuYWJsZS1iYWNrZ3JvdW5kOm5ldyAtOTUwIDUzMiAxOCAxODsiIHhtbDpzcGFjZT0icHJlc2VydmUiPg0KPHN0eWxlIHR5cGU9InRleHQvY3NzIj4NCgkuc3Qwe2ZpbGw6bm9uZTt9DQoJLnN0MXtmaWxsOm5vbmU7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjEuNTtzdHJva2UtbWl0ZXJsaW1pdDoxMDt9DQo8L3N0eWxlPg0KPHBhdGggY2xhc3M9InN0MCIgZD0iTS05MzIsNTMydjE4aC0xOHYtMThILTkzMnoiLz4NCjxwb2x5bGluZSBjbGFzcz0ic3QxIiBwb2ludHM9Ii05MzYuNiw1MzguOCAtOTQxLDU0My4yIC05NDUuNCw1MzguOCAiLz4NCjwvc3ZnPg0K);\r\n height: 16px;\r\n width: 16px;\r\n display: inline-block;\r\n vertical-align: bottom;\r\n transition: transform .3s ease;\r\n transform: rotate(0);\r\n}\r\n\r\n.plugin-input-group .button-collapse.collapsed {\r\n transition: transform .3s ease;\r\n transform: rotate(-90deg);\r\n}\r\n\r\n.plugin-input-group h2 {\r\n font-size: 14px;\r\n}\r\n\r\n.plugin-input-group .plugin-input-group h2 {\r\n margin-left: 16px;\r\n}\r\n\r\n.plugin-inputs {\r\n height: auto;\r\n overflow: hidden;\r\n transition: height 300ms cubic-bezier(0.47, 0, 0.745, 0.715);\r\n}\r\n\r\n.plugin-inputs.collapsed {\r\n height: 0px;\r\n}\r\n\r\n.file-input {\r\n color: var(--text-normal);\r\n background-color: var(--input-background);\r\n width: 100%;\r\n border-radius: 5px;\r\n padding: 10px;\r\n height: 40px;\r\n box-sizing: border-box;\r\n overflow: hidden;\r\n }\r\n \r\n .file-input::-webkit-file-upload-button {\r\n color: white;\r\n background: #7289DA;\r\n outline: 0;\r\n border: 0;\r\n padding: 12px!important;\r\n margin-top: -10px;\r\n margin-left: -10px;\r\n margin-right: 10px;\r\n bottom: 0;\r\n border-radius: 3px 0 0 3px;\r\n font-size: 14px;\r\n font-weight: 500;\r\n font-family: Whitney,Helvetica Neue,Helvetica,Arial,sans-serif;\r\n cursor: pointer;\r\n }\r\n\r\n.color-input {\r\n background: none;\r\n padding: 0;\r\n border: none;\r\n}\r\n\r\n.color-input:hover {\r\n opacity: 0.8;\r\n}\r\n\r\n\r\n.z-select {\r\n position: relative;\r\n cursor: pointer;\r\n color: var(--text-normal);\r\n font-size: 14px;\r\n display: flex;\r\n align-items: center;\r\n justify-content: space-between;\r\n background-color: var(--deprecated-text-input-bg);\r\n border: 1px solid var(--deprecated-text-input-border);\r\n border-radius: 3px;\r\n padding: 8px 8px 8px 12px;\r\n transition: 150ms ease border-color;\r\n }\r\n \r\n .z-select:hover,\r\n .z-select.menu-open {\r\n border-color: var(--background-tertiary);\r\n }\r\n \r\n .z-select.z-select-transparent {\r\n align-items: flex-start;\r\n background: none;\r\n border: none;\r\n padding: 0;\r\n }\r\n \r\n .z-select-icons {\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n }\r\n \r\n .z-select-clear,\r\n .z-select-arrow {\r\n margin-left: 5px;\r\n fill: var(--interactive-normal);\r\n }\r\n \r\n .z-select .z-select-options {\r\n position: absolute;\r\n background: var(--background-secondary);\r\n border-radius: 0 0 3px 3px;\r\n max-height: 125px;\r\n min-width: 100%;\r\n overflow-y: auto;\r\n box-shadow: rgba(0, 0, 0, 0.3) 0 1px 5px 0;\r\n border: 1px solid rgba(0, 0, 0, 0.3);\r\n border-top: 0;\r\n margin-top: -1px;\r\n margin-left: -13px;\r\n z-index: 2;\r\n top: 100%;\r\n }\r\n \r\n .z-select-transparent .z-select-options {\r\n border: 1px solid rgba(0, 0, 0, 0.3);\r\n margin-top: 3px;\r\n border-radius: 3px;\r\n }\r\n \r\n .z-select .z-select-option {\r\n padding: 8px 12px;\r\n cursor: pointer;\r\n white-space: pre;\r\n }\r\n \r\n .z-select .z-select-option:hover {\r\n background: rgba(0, 0, 0, 0.1);\r\n }\r\n \r\n .z-select .z-select-option.selected {\r\n background: rgba(0, 0, 0, 0.2);\r\n }\r\n\r\n\r\n\r\n .z-keybind-wrapper {\r\n display: flex;\r\n justify-content: space-between;\r\n align-items: center;\r\n }\r\n \r\n .z-keybind-wrapper > :first-child {\r\n flex-grow: 1;\r\n }\r\n \r\n .z-keybind-clear {\r\n margin-left: 5px;\r\n fill: var(--interactive-normal);\r\n cursor: pointer;\r\n }\r\n\r\n.plugin-input-container > .container-31PmuA {\r\n margin: 10px 0;\r\n}"); + +/***/ }), + +/***/ "./src/styles/toasts.css": +/*!*******************************!*\ + !*** ./src/styles/toasts.css ***! + \*******************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__) +/* harmony export */ }); +/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (".toasts {\r\n position: fixed;\r\n display: flex;\r\n top: 0;\r\n flex-direction: column;\r\n align-items: center;\r\n justify-content: flex-end;\r\n pointer-events: none;\r\n z-index: 4000;\r\n}\r\n\r\n@keyframes toast-up {\r\n from {\r\n transform: translateY(0);\r\n opacity: 0;\r\n }\r\n}\r\n\r\n.toast {\r\n animation: toast-up 300ms ease;\r\n transform: translateY(-10px);\r\n background: #36393F;\r\n padding: 10px;\r\n border-radius: 5px;\r\n box-shadow: 0 0 0 1px rgba(32,34,37,.6), 0 2px 10px 0 rgba(0,0,0,.2);\r\n font-weight: 500;\r\n color: #fff;\r\n user-select: text;\r\n font-size: 14px;\r\n opacity: 1;\r\n margin-top: 10px;\r\n display: flex;\r\n justify-content: center;\r\n align-items: center;\r\n}\r\n\r\n@keyframes toast-down {\r\n to {\r\n transform: translateY(0px);\r\n opacity: 0;\r\n }\r\n}\r\n\r\n.toast.closing {\r\n animation: toast-down 200ms ease;\r\n animation-fill-mode: forwards;\r\n opacity: 1;\r\n transform: translateY(-10px);\r\n}\r\n\r\n.toast.toast-info {\r\n background-color: #4a90e2;\r\n}\r\n\r\n.toast.toast-success {\r\n background-color: #43b581;\r\n}\r\n\r\n.toast.toast-danger,\r\n.toast.toast-error {\r\n background-color: #f04747;\r\n}\r\n\r\n.toast.toast-warning,\r\n.toast.toast-warn {\r\n background-color: #FFA600;\r\n}\r\n\r\n.toast-icon {\r\n margin-right: 5px;\r\n fill: white;\r\n border-radius: 50%;\r\n overflow: hidden;\r\n height: 20px;\r\n width: 20px;\r\n}\r\n\r\n.toast-text {\r\n line-height: 20px;\r\n}"); + +/***/ }), + +/***/ "./src/styles/updates.css": +/*!********************************!*\ + !*** ./src/styles/updates.css ***! + \********************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__) +/* harmony export */ }); +/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = ("#outdated-plugins {\r\n font-weight: 700;\r\n}\r\n#outdated-plugins > span {\r\n -webkit-app-region: no-drag;\r\n color: #fff;\r\n cursor: pointer;\r\n}\r\n#outdated-plugins > span:hover {\r\n text-decoration: underline;\r\n}"); + +/***/ }), + +/***/ "./src/config.js": +/*!***********************!*\ + !*** ./src/config.js ***! + \***********************/ +/***/ ((module) => { + +// Use non-ES6 so build script can require() +// Options: added, improved, fixed, progress. +module.exports = { + id: "9", + name: "ZeresPluginLibrary", + author: "Zerebos", + version: "2.0.19", + description: "Gives other plugins utility functions.", + source: "https://github.com/rauenzi/BDPluginLibrary", + github_raw: "https://raw.githubusercontent.com/rauenzi/BDPluginLibrary/master/release/0PluginLibrary.plugin.js", + changelog: [ + { + title: "Fixed", + type: "fixed", + items: [ + "Fixed the error on startup.", + "Fixed showing modals.", + "Fixed several module searches.", + "Does not fix showUserPopout." + ] + }, + ], + main: "index.js" +}; + + +/***/ }), + +/***/ "./src/modules/colorconverter.js": +/*!***************************************!*\ + !*** ./src/modules/colorconverter.js ***! + \***************************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "default": () => (/* binding */ ColorConverter) +/* harmony export */ }); +/** + * Helpful utilities for dealing with colors. + * @module ColorConverter + */ + + +const validHexRegex = /#([a-fA-F0-9]{1,2})([a-fA-F0-9]{1,2})([a-fA-F0-9]{1,2})/; + +class ColorConverter { + + static getDarkness(color) { + const [red, green, blue] = this.getRGB(color); + return 1 - (0.299 * red + 0.587 * green + 0.114 * blue) / 255; + } + + static hex2int(color) { + if (color.length === 4) color = `#${color[1]}${color[1]}${color[2]}${color[2]}${color[3]}${color[3]}`; + return parseInt(color.slice(1), 16); + } + + static hex2rgb(color) { + const [red, green, blue] = this.getRGB(color); + return `rgb(${red}, ${green}, ${blue})`; + } + + static int2hex(color) { + const red = color >> 16 & 255; + const green = color >> 8 & 255; + const blue = color & 255; + return `#${red.toString(16)}${green.toString(16)}${blue.toString(16)}`; + } + + static int2rgba(color, alpha) { + return `rgba(${color >> 16 & 255}, ${color >> 8 & 255}, ${color & 255}, ${alpha})`; + } + + static isValidHex(color) { + return color.match(validHexRegex) != null; + } + + /** + * Will get the red green and blue values of any color string. + * @param {string} color - the color to obtain the red, green and blue values of. Can be in any of these formats: #fff, #ffffff, rgb, rgba + * @returns {Array} - array containing the red, green, and blue values + */ + static getRGB(color) { + let result = /rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/.exec(color); + if (result) return [parseInt(result[1]), parseInt(result[2]), parseInt(result[3])]; + + result = /rgb\(\s*([0-9]+(?:\.[0-9]+)?)%\s*,\s*([0-9]+(?:\.[0-9]+)?)%\s*,\s*([0-9]+(?:\.[0-9]+)?)%\s*\)/.exec(color); + if (result) return [parseFloat(result[1]) * 2.55, parseFloat(result[2]) * 2.55, parseFloat(result[3]) * 2.55]; + + result = /#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec(color); + if (result) return [parseInt(result[1], 16), parseInt(result[2], 16), parseInt(result[3], 16)]; + + result = /#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/.exec(color); + if (result) return [parseInt(result[1] + result[1], 16), parseInt(result[2] + result[2], 16), parseInt(result[3] + result[3], 16)]; + } + + /** + * Will get the darken the color by a certain percent + * @param {string} color - Can be in any of these formats: #fff, #ffffff, rgb, rgba + * @param {number} percent - percent to darken the color by (0-100) + * @returns {string} - new color in rgb format + */ + static darkenColor(color, percent) { + const rgb = this.getRGB(color); + if (!rgb) return color; + for (let i = 0; i < rgb.length; i++) rgb[i] = Math.round(Math.max(0, rgb[i] - rgb[i] * (percent / 100))); + return "rgb(" + rgb[0] + "," + rgb[1] + "," + rgb[2] + ")"; + } + + /** + * Will get the lighten the color by a certain percent + * @param {string} color - Can be in any of these formats: #fff, #ffffff, rgb, rgba + * @param {number} percent - percent to lighten the color by (0-100) + * @returns {string} - new color in rgb format + */ + static lightenColor(color, percent) { + const rgb = this.getRGB(color); + if (!rgb) return color; + for (let i = 0; i < rgb.length; i++) rgb[i] = Math.round(Math.min(255, rgb[i] + rgb[i] * (percent / 100))); + return "rgb(" + rgb[0] + "," + rgb[1] + "," + rgb[2] + ")"; + } + + /** + * Converts a color to rgba format string + * @param {string} color - Can be in any of these formats: #fff, #ffffff, rgb, rgba + * @param {number} alpha - alpha level for the new color + * @returns {string} - new color in rgb format + */ + static rgbToAlpha(color, alpha) { + const rgb = this.getRGB(color); + if (!rgb) return color; + return "rgba(" + rgb[0] + "," + rgb[1] + "," + rgb[2] + "," + alpha + ")"; + } + +} + +/***/ }), + +/***/ "./src/modules/discordclasses.js": +/*!***************************************!*\ + !*** ./src/modules/discordclasses.js ***! + \***************************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__) +/* harmony export */ }); +/* harmony import */ var _discordclassmodules__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./discordclassmodules */ "./src/modules/discordclassmodules.js"); +/* harmony import */ var _domtools__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./domtools */ "./src/modules/domtools.js"); + + + +const getRaw = function(prop) { + if (!this.hasOwnProperty(prop)) return ""; + return this[prop]; +}; + +const getClass = function(prop) { + if (!this.hasOwnProperty(prop)) return ""; + return this[prop].split(" ")[0]; +}; + +/** + * Proxy for all the class packages, allows us to safely attempt + * to retrieve nested things without error. Also wraps the class in + * {@link module:DOMTools.ClassName} which adds features but can still + * be used in native function. + * + * For a list of all available class namespaces check out {@link module:DiscordClassModules}. + * + * @see module:DiscordClassModules + * @module DiscordClasses + */ +const DiscordModules = new Proxy(_discordclassmodules__WEBPACK_IMPORTED_MODULE_0__["default"], { + get: function(list, item) { + if (item == "getRaw" || item == "getClass") return (module, prop) => DiscordModules[module][item]([prop]); + if (list[item] === undefined) return new Proxy({}, {get: function() {return "";}}); + return new Proxy(list[item], { + get: function(obj, prop) { + if (prop == "getRaw") return getRaw.bind(obj); + if (prop == "getClass") return getClass.bind(obj); + if (!obj.hasOwnProperty(prop)) return ""; + return new _domtools__WEBPACK_IMPORTED_MODULE_1__["default"].ClassName(obj[prop]); + } + }); + } +}); +/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (DiscordModules); + +/***/ }), + +/***/ "./src/modules/discordclassmodules.js": +/*!********************************************!*\ + !*** ./src/modules/discordclassmodules.js ***! + \********************************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__) +/* harmony export */ }); +/* harmony import */ var _utilities__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./utilities */ "./src/modules/utilities.js"); +/* harmony import */ var _webpackmodules__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./webpackmodules */ "./src/modules/webpackmodules.js"); + + + +/** + * A large list of known and labelled classes in discord. + * Click the source link down below to view more info. Otherwise, if you + * have the library installed or have a plugin using this library, + * do `Object.keys(ZLibrary.DiscordClassModules)` in console for a list of modules. + * + * You can use this directly, however the preferred way of doing this is to use {@link module:DiscordClasses} or {@link module:DiscordSelectors} + * + * @see module:DiscordClasses + * @see module:DiscordSelectors + * @module DiscordClassModules + */ +/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (_utilities__WEBPACK_IMPORTED_MODULE_0__["default"].memoizeObject({ + get ContextMenu() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("menu", "item");}, + get Scrollers() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("thin", "scrollerBase", "content");}, + get AccountDetails() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("container", "avatar", "hasBuildOverride");}, + get Typing() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("typing", "text");}, + get UserPopout() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("userPopout");}, + get PopoutRoles() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("roleCircle");}, + get UserModal() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("profileBadge");}, + get Textarea() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("channelTextArea", "textArea");}, + get Popouts() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("popouts", "popout");}, // broken, popouts element has been removed. + get App() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("app", "mobileApp");}, + get Titles() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("defaultMarginh5");}, + get Notices() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("notice", "colorInfo");}, + get Backdrop() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("backdrop");}, + get Modals() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("hideOnFullscreen", "root");}, + get AuditLog() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("userHook");}, + get ChannelList() {return Object.assign({}, _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("containerDefault"), _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("name", "unread"), _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("sidebar", "hasNotice"));}, + get MemberList() {return Object.assign({}, _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("member", "memberInner"), _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("members", "membersWrap"));}, + get TitleWrap() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("titleWrapper");}, + get Titlebar() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("titleBar");}, + get Embeds() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("embed", "embedAuthor");}, + get Layers() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("layers", "layer");}, + get TooltipLayers() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("layerContainer", "layer");}, + get Margins() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getModule(m => !m.title && m.marginBottom40 && m.marginTop40);}, + get Dividers() {return Object.assign({}, _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("dividerDefault"), _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getModule(m => Object.keys(m).length == 1 && m.divider));}, + get Changelog() {return Object.assign({}, _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("container", "added"), _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("content", "modal", "size"));}, + get BasicInputs() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("inputDefault", "copyInput");}, + get Messages() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("message", "containerCozy");}, + get Guilds() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("guildsWrapper");}, + get EmojiPicker() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("emojiPicker", "emojiItem");}, + get Reactions() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("reaction", "reactionInner");}, + get Checkbox() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("checkbox", "checkboxInner");}, + get Tooltips() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("tooltip", "tooltipBlack");} +})); + + + +/***/ }), + +/***/ "./src/modules/discordmodules.js": +/*!***************************************!*\ + !*** ./src/modules/discordmodules.js ***! + \***************************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__) +/* harmony export */ }); +/* harmony import */ var _utilities__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./utilities */ "./src/modules/utilities.js"); +/* harmony import */ var _webpackmodules__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./webpackmodules */ "./src/modules/webpackmodules.js"); +/** + * A large list of known and useful webpack modules internal to Discord. + * Click the source link down below to view more info. Otherwise, if you + * have the library installed or have a plugin using this library, + * do `Object.keys(ZLibrary.DiscordModules)` in console for a list of modules. + * @module DiscordModules + */ + + + +/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (_utilities__WEBPACK_IMPORTED_MODULE_0__["default"].memoizeObject({ + get React() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("createElement", "cloneElement");}, + get ReactDOM() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("render", "findDOMNode");}, + get Events() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByPrototypes("setMaxListeners", "emit");}, + + /* Guild Info, Stores, and Utilities */ + get GuildStore() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("getGuild", "getGuildIds");}, + get SortedGuildStore() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("getSortedGuilds");}, + get SelectedGuildStore() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("getLastSelectedGuildId");}, + get GuildSync() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("getSyncedGuilds");}, + get GuildInfo() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("getAcronym");}, + get GuildChannelsStore() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("getChannels", "getDefaultChannel");}, + get GuildMemberStore() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("getMember");}, + get MemberCountStore() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("getMemberCounts");}, + get GuildEmojiStore() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("getEmojis");}, + get GuildActions() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("requestMembers");}, + get GuildPermissions() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("getGuildPermissions");}, + + /* Channel Store & Actions */ + get ChannelStore() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("getChannel", "getDMFromUserId");}, + get SelectedChannelStore() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("getLastSelectedChannelId");}, + get ChannelActions() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("selectChannel");}, + get PrivateChannelActions() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("openPrivateChannel");}, + + /* Current User Info, State and Settings */ + get UserInfoStore() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("getSessionId");}, + get UserSettingsStore() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("guildPositions");}, + get StreamerModeStore() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("hidePersonalInformation");}, + get UserSettingsUpdater() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("updateRemoteSettings");}, + get OnlineWatcher() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("isOnline");}, + get CurrentUserIdle() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("isIdle");}, + get RelationshipStore() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("isBlocked", "getFriendIDs");}, + get RelationshipManager() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("addRelationship");}, + get MentionStore() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("getMentions");}, + + /* User Stores and Utils */ + get UserStore() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("getCurrentUser", "getUser");}, + get UserStatusStore() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("getStatus", "getState");}, + get UserTypingStore() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("isTyping");}, + get UserActivityStore() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("getActivity");}, + get UserNameResolver() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("getName");}, + get UserNoteStore() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("getNote");}, + get UserNoteActions() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("updateNote");}, + + /* Emoji Store and Utils */ + get EmojiInfo() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("isEmojiDisabled");}, + get EmojiUtils() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("getGuildEmoji");}, + get EmojiStore() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("getByCategory", "EMOJI_NAME_RE");}, + + /* Invite Store and Utils */ + get InviteStore() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("getInvites");}, + get InviteResolver() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("resolveInvite");}, + get InviteActions() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("acceptInvite");}, + + /* Discord Objects & Utils */ + get DiscordConstants() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("Permissions", "ActivityTypes", "StatusTypes");}, + get DiscordPermissions() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getModule(m => m.ADD_REACTIONS, {searchExports: true});}, + get Permissions() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("computePermissions");}, + get ColorConverter() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getModule(m => Object.values(m).some(v => v?.toString().includes(`"rgba("`)));}, + get ColorShader() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("darken");}, + get TinyColor() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByPrototypes("toRgb");}, + get ClassResolver() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("getClass");}, + get ButtonData() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getModule(m => m.BorderColors, {searchExports: true});}, + get NavigationUtils() { + return { + transitionToGuild: _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("transitionToGuildSync")?.transitionToGuildSync, + transitionTo: _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getModule(m => m?.toString?.().includes(`"transitionTo - Transitioning to "`), {searchExports: true}), + replaceWith: _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getModule(m => m?.toString?.().includes(`"Replacing route with "`), {searchExports: true}) + }; + }, + get KeybindStore() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("keyToCode");}, + + /* Discord Messages */ + get MessageStore() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("getMessage", "getMessages");}, + get ReactionsStore() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("getReactions", "_dispatcher");}, + get MessageActions() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("jumpToMessage", "_sendMessage");}, + get MessageQueue() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("enqueue");}, + get MessageParser() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getModule(m => Object.keys(m).length && Object.keys(m).every(k => k === "parse" || k === "unparse"));}, + + /* Experiments */ + get ExperimentStore() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("getExperimentOverrides");}, + get ExperimentsManager() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("isDeveloper");}, + get CurrentExperiment() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("getExperimentId");}, + + /* Streams */ + get StreamStore() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("getAllActiveStreams", "getStreamForUser");}, + get StreamPreviewStore() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("getIsPreviewLoading", "getPreviewURL");}, + + /* Images, Avatars and Utils */ + get ImageResolver() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("getUserAvatarURL", "getGuildIconURL");}, + get ImageUtils() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("getSizedImageSrc");}, + get AvatarDefaults() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("getUserAvatarURL", "DEFAULT_AVATARS");}, + + /* Drag & Drop */ + get DNDSources() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("addTarget");}, + get DNDObjects() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("DragSource");}, + + /* Electron & Other Internals with Utils*/ + get ElectronModule() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("setBadge");}, + get Flux() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("Store", "connectStores");}, + get Dispatcher() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("dispatch", "subscribe");}, + get PathUtils() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("hasBasename");}, + get NotificationModule() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("showNotification");}, + get RouterModule() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("Router");}, + get APIModule() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("getAPIBaseURL");}, + get AnalyticEvents() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("AnalyticEventConfigs");}, + get KeyGenerator() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByRegex(/"binary"/);}, + get Buffers() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("Buffer", "kMaxLength");}, + get DeviceStore() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("getDevices");}, + get SoftwareInfo() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("os");}, + get i18n() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("Messages", "languages");}, + + /* Media Stuff (Audio/Video) */ + get MediaDeviceInfo() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("Codecs", "MediaEngineContextTypes");}, + get MediaInfo() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("getOutputVolume");}, + get MediaEngineInfo() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("determineMediaEngine");}, + get VoiceInfo() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("getEchoCancellation");}, + get SoundModule() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("playSound");}, + + /* Window, DOM, HTML */ + get WindowInfo() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("isFocused", "windowSize");}, + get DOMInfo() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("canUseDOM");}, + + /* Locale/Location and Time */ + get LocaleManager() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getModule(m => m.Messages && Object.keys(m.Messages).length);}, + get Moment() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("parseZone");}, + get LocationManager() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("createLocation");}, + get Timestamps() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("fromTimestamp");}, + + /* Strings and Utils */ + get Strings() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getModule(m => m.Messages && Object.keys(m.Messages).length && m.Messages.COPY_ID);}, + get StringFormats() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("a", "z");}, + get StringUtils() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("toASCII");}, + + /* URLs and Utils */ + get URLParser() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("Url", "parse");}, + get ExtraURLs() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("getArticleURL");}, + + /* Text Processing */ + get hljs() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("highlight", "highlightBlock");}, + get SimpleMarkdown() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("parseBlock", "parseInline", "defaultOutput");}, + + /* DOM/React Components */ + /* ==================== */ + get LayerManager() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("popLayer", "pushLayer");}, + get UserSettingsWindow() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("open", "updateAccount");}, + get ChannelSettingsWindow() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("open", "updateChannel");}, + get GuildSettingsWindow() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("open", "updateGuild");}, + + /* Modals */ + get ModalActions() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("openModal", "closeModal");}, + get ModalStack() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("push", "update", "pop", "popWithKey");}, + get UserProfileModals() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("fetchMutualFriends", "setSection");}, + get AlertModal() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByPrototypes("handleCancel", "handleSubmit");}, + get ConfirmationModal() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("ConfirmModal").ConfirmModal;}, + get ChangeNicknameModal() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("open", "changeNickname");}, + get CreateChannelModal() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("open", "createChannel");}, + get PruneMembersModal() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("open", "prune");}, + get NotificationSettingsModal() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("open", "updateNotificationSettings");}, + get PrivacySettingsModal() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getModule(m => m.open && m.open.toString().includes("PRIVACY_SETTINGS_MODAL"));}, + get Changelog() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getModule((m => m.defaultProps && m.defaultProps.selectable == false));}, + get ModalRoot() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getModule(m => m?.toString?.()?.includes("ENTERING") && m?.toString?.()?.includes("headerId"), {searchExports: true});}, + + /* Popouts */ + get PopoutStack() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("open", "close", "closeAll");}, + get PopoutOpener() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("openPopout");}, + get UserPopout() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getModule(m => m?.type?.toString?.().includes('Unexpected missing user'), {searchExports: true});}, + + /* Context Menus */ + get ContextMenuActions() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("openContextMenu");}, + get ContextMenuItemsGroup() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByRegex(/itemGroup/);}, + get ContextMenuItem() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByRegex(/\.label\b.*\.hint\b.*\.action\b/);}, + + /* Misc */ + get ExternalLink() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByRegex(/trusted/);}, + get TextElement() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getModule(m => m?.Sizes?.SIZE_32 && m.Colors);}, + get Anchor() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByDisplayName("Anchor");}, + get Flex() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByDisplayName("Flex");}, + get FlexChild() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("Child");}, + get Clickable() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByDisplayName("Clickable");}, + get Titles() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("Tags", "Sizes");}, + get HeaderBar() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByDisplayName("HeaderBar");}, + get TabBar() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByDisplayName("TabBar");}, + get Tooltip() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByPrototypes("renderTooltip");}, + get Spinner() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByDisplayName("Spinner");}, + + /* Forms */ + get FormTitle() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByDisplayName("FormTitle");}, + get FormSection() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByDisplayName("FormSection");}, + get FormNotice() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByDisplayName("FormNotice");}, + + /* Scrollers */ + get ScrollerThin() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("ScrollerThin").ScrollerThin;}, + get ScrollerAuto() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("ScrollerAuto").ScrollerAuto;}, + get AdvancedScrollerThin() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("AdvancedScrollerThin").AdvancedScrollerThin;}, + get AdvancedScrollerAuto() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("AdvancedScrollerAuto").AdvancedScrollerAuto;}, + get AdvancedScrollerNone() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("AdvancedScrollerNone").AdvancedScrollerNone;}, + + /* Settings */ + get SettingsWrapper() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getModule(m => m?.render?.toString?.().includes("required") && m?.render?.toString?.().includes("titleClassName"), {searchExports: true});}, + get SettingsNote() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getModule(m => m?.Types?.DESCRIPTION, {searchExports: true});}, + get SettingsDivider() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getModule(m => !m.defaultProps && m.prototype && m.prototype.render && m.prototype.render.toString().includes("default.divider"));}, + + get ColorPicker() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getModule(m => m?.displayName === "ColorPicker" && m?.defaultProps);}, + get Dropdown() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("SingleSelect").SingleSelect;}, + get Keybind() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByPrototypes("handleComboChange");}, + get RadioGroup() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getModule(m => m?.Sizes && m?.toString?.().includes("radioItemClassName"), {searchExports: true});}, + get Slider() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getModule(m => m?.defaultProps?.maxValue == 100 && m?.prototype?.renderMark, {searchExports: true});}, + get SwitchRow() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getModule(m => m?.toString?.().includes("tooltipNote"), {searchExports: true});}, + get Textbox() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getModule(m => m?.defaultProps && m?.defaultProps?.type == "text", {searchExports: true});}, +})); + + +/***/ }), + +/***/ "./src/modules/discordselectors.js": +/*!*****************************************!*\ + !*** ./src/modules/discordselectors.js ***! + \*****************************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__) +/* harmony export */ }); +/* harmony import */ var _discordclassmodules__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./discordclassmodules */ "./src/modules/discordclassmodules.js"); +/* harmony import */ var _domtools__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./domtools */ "./src/modules/domtools.js"); + + + +const getSelectorAll = function(prop) { + if (!this.hasOwnProperty(prop)) return ""; + return `.${this[prop].split(" ").join(".")}`; +}; + +const getSelector = function(prop) { + if (!this.hasOwnProperty(prop)) return ""; + return `.${this[prop].split(" ")[0]}`; +}; + +/** + * Gives us a way to retrieve the internal classes as selectors without + * needing to concatenate strings or use string templates. Wraps the + * selector in {@link module:DOMTools.Selector} which adds features but can + * still be used in native function. + * + * For a list of all available class namespaces check out {@link module:DiscordClassModules}. + * + * @see module:DiscordClassModules + * @module DiscordSelectors + */ +const DiscordSelectors = new Proxy(_discordclassmodules__WEBPACK_IMPORTED_MODULE_0__["default"], { + get: function(list, item) { + if (item == "getSelectorAll" || item == "getSelector") return (module, prop) => DiscordSelectors[module][item]([prop]); + if (list[item] === undefined) return new Proxy({}, {get: function() {return "";}}); + return new Proxy(list[item], { + get: function(obj, prop) { + if (prop == "getSelectorAll") return getSelectorAll.bind(obj); + if (prop == "getSelector") return getSelector.bind(obj); + if (!obj.hasOwnProperty(prop)) return ""; + return new _domtools__WEBPACK_IMPORTED_MODULE_1__["default"].Selector(obj[prop]); + } + }); + } +}); + +/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (DiscordSelectors); + +/***/ }), + +/***/ "./src/modules/domtools.js": +/*!*********************************!*\ + !*** ./src/modules/domtools.js ***! + \*********************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "default": () => (/* binding */ DOMTools) +/* harmony export */ }); +/* harmony import */ var structs__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! structs */ "./src/structs/structs.js"); +/** + * Helpful utilities for dealing with DOM operations. + * + * This module also extends `HTMLElement` to add a set of utility functions, + * the same as the ones available in the module itself, but with the `element` + * parameter bound to `this`. + * @module DOMTools + */ + + + +/** + * @interface + * @name Offset + * @property {number} top - Top offset of the target element. + * @property {number} right - Right offset of the target element. + * @property {number} bottom - Bottom offset of the target element. + * @property {number} left - Left offset of the target element. + * @property {number} height - Outer height of the target element. + * @property {number} width - Outer width of the target element. + */ + + /** + * Function that automatically removes added listener. + * @callback module:DOMTools~CancelListener + */ + +class DOMTools { + + static get Selector() {return structs__WEBPACK_IMPORTED_MODULE_0__.Selector;} + static get ClassName() {return structs__WEBPACK_IMPORTED_MODULE_0__.ClassName;} + static get DOMObserver() {return structs__WEBPACK_IMPORTED_MODULE_0__.DOMObserver;} + + /** + * Default DOMObserver for global usage. + * + * @see DOMObserver + */ + static get observer() { + return this._observer || (this._observer = new structs__WEBPACK_IMPORTED_MODULE_0__.DOMObserver()); + } + + /** Document/window width */ + static get screenWidth() {return Math.max(document.documentElement.clientWidth, window.innerWidth || 0);} + + /** Document/window height */ + static get screenHeight() {return Math.max(document.documentElement.clientHeight, window.innerHeight || 0);} + + static animate({timing = _ => _, update, duration}) { + // https://javascript.info/js-animation + const start = performance.now(); + + requestAnimationFrame(function renderFrame(time) { + // timeFraction goes from 0 to 1 + let timeFraction = (time - start) / duration; + if (timeFraction > 1) timeFraction = 1; + + // calculate the current animation state + const progress = timing(timeFraction); + + update(progress); // draw it + + if (timeFraction < 1) requestAnimationFrame(renderFrame); + }); + } + + /** + * Adds a style to the document. + * @param {string} id - identifier to use as the element id + * @param {string} css - css to add to the document + */ + static addStyle(id, css) { + document.head.append(DOMTools.createElement(``)); + } + + /** + * Removes a style from the document. + * @param {string} id - original identifier used + */ + static removeStyle(id) { + const element = document.getElementById(id); + if (element && element.tagName === "STYLE") element.remove(); + } + + /** + * Adds/requires a remote script to be loaded + * @param {string} id - identifier to use for this script + * @param {string} url - url from which to load the script + * @returns {Promise} promise that resolves when the script is loaded + */ + static addScript(id, url) { + return new Promise(resolve => { + const script = document.createElement("script"); + script.id = id; + script.src = url; + script.type = "text/javascript"; + script.onload = resolve; + document.head.append(script); + }); + } + + /** + * Removes a remote script from the document. + * @param {string} id - original identifier used + */ + static removeScript(id) { + const element = document.getElementById(id); + if (element && element.tagName === "SCRIPT") element.remove(); + } + + /** + * This is my shit version of not having to use `$` from jQuery. Meaning + * that you can pass a selector and it will automatically run {@link module:DOMTools.query}. + * It also means that you can pass a string of html and it will perform and return `parseHTML`. + * @see module:DOMTools.parseHTML + * @see module:DOMTools.query + * @param {string} selector - Selector to query or HTML to parse + * @returns {(DocumentFragment|NodeList|HTMLElement)} - Either the result of `parseHTML` or `query` + */ + static Q(selector) { + const element = this.parseHTML(selector); + const isHTML = element instanceof NodeList ? Array.from(element).some(n => n.nodeType === 1) : element.nodeType === 1; + if (isHTML) return element; + return this.query(selector); + } + + /** + * Essentially a shorthand for `document.querySelector`. If the `baseElement` is not provided + * `document` is used by default. + * @param {string} selector - Selector to query + * @param {Element} [baseElement] - Element to base the query from + * @returns {(Element|null)} - The found element or null if not found + */ + static query(selector, baseElement) { + if (!baseElement) baseElement = document; + return baseElement.querySelector(selector); + } + + /** + * Essentially a shorthand for `document.querySelectorAll`. If the `baseElement` is not provided + * `document` is used by default. + * @param {string} selector - Selector to query + * @param {Element} [baseElement] - Element to base the query from + * @returns {Array} - Array of all found elements + */ + static queryAll(selector, baseElement) { + if (!baseElement) baseElement = document; + return baseElement.querySelectorAll(selector); + } + + /** + * Parses a string of HTML and returns the results. If the second parameter is true, + * the parsed HTML will be returned as a document fragment {@see https://developer.mozilla.org/en-US/docs/Web/API/DocumentFragment}. + * This is extremely useful if you have a list of elements at the top level, they can then be appended all at once to another node. + * + * If the second parameter is false, then the return value will be the list of parsed + * nodes and there were multiple top level nodes, otherwise the single node is returned. + * @param {string} html - HTML to be parsed + * @param {boolean} [fragment=false] - Whether or not the return should be the raw `DocumentFragment` + * @returns {(DocumentFragment|NodeList|HTMLElement)} - The result of HTML parsing + */ + static parseHTML(html, fragment = false) { + const template = document.createElement("template"); + template.innerHTML = html; + const node = template.content.cloneNode(true); + if (fragment) return node; + return node.childNodes.length > 1 ? node.childNodes : node.childNodes[0]; + } + + /** Alternate name for {@link module:DOMTools.parseHTML} */ + static createElement(html, fragment = false) {return this.parseHTML(html, fragment);} + + /** + * Takes a string of html and escapes it using the brower's own escaping mechanism. + * @param {String} html - html to be escaped + */ + static escapeHTML(html) { + const textNode = document.createTextNode(""); + const spanElement = document.createElement("span"); + spanElement.append(textNode); + textNode.nodeValue = html; + return spanElement.innerHTML; + } + + /** + * Takes a string and escapes it for use as a DOM id. + * @param {String} id - string to be escaped + */ + static escapeID(id) { + return id.replace(/^[^a-z]+|[^\w-]+/gi, "-"); + } + + /** + * Adds a list of classes from the target element. + * @param {Element} element - Element to edit classes of + * @param {...string} classes - Names of classes to add + * @returns {Element} - `element` to allow for chaining + */ + static addClass(element, ...classes) { + classes = classes.flat().filter(c => c); + for (let c = 0; c < classes.length; c++) classes[c] = classes[c].toString().split(" "); + classes = classes.flat().filter(c => c); + element.classList.add(...classes); + return element; + } + + /** + * Removes a list of classes from the target element. + * @param {Element} element - Element to edit classes of + * @param {...string} classes - Names of classes to remove + * @returns {Element} - `element` to allow for chaining + */ + static removeClass(element, ...classes) { + for (let c = 0; c < classes.length; c++) classes[c] = classes[c].toString().split(" "); + classes = classes.flat().filter(c => c); + element.classList.remove(...classes); + return element; + } + + /** + * When only one argument is present: Toggle class value; + * i.e., if class exists then remove it and return false, if not, then add it and return true. + * When a second argument is present: + * If the second argument evaluates to true, add specified class value, and if it evaluates to false, remove it. + * @param {Element} element - Element to edit classes of + * @param {string} classname - Name of class to toggle + * @param {boolean} [indicator] - Optional indicator for if the class should be toggled + * @returns {Element} - `element` to allow for chaining + */ + static toggleClass(element, classname, indicator) { + classname = classname.toString().split(" ").filter(c => c); + if (typeof(indicator) !== "undefined") classname.forEach(c => element.classList.toggle(c, indicator)); + else classname.forEach(c => element.classList.toggle(c)); + return element; + } + + /** + * Checks if an element has a specific class + * @param {Element} element - Element to edit classes of + * @param {string} classname - Name of class to check + * @returns {boolean} - `true` if the element has the class, `false` otherwise. + */ + static hasClass(element, classname) { + return classname.toString().split(" ").filter(c => c).every(c => element.classList.contains(c)); + } + + /** + * Replaces one class with another + * @param {Element} element - Element to edit classes of + * @param {string} oldName - Name of class to replace + * @param {string} newName - New name for the class + * @returns {Element} - `element` to allow for chaining + */ + static replaceClass(element, oldName, newName) { + element.classList.replace(oldName, newName); + return element; + } + + /** + * Appends `thisNode` to `thatNode` + * @param {Node} thisNode - Node to be appended to another node + * @param {Node} thatNode - Node for `thisNode` to be appended to + * @returns {Node} - `thisNode` to allow for chaining + */ + static appendTo(thisNode, thatNode) { + if (typeof(thatNode) == "string") thatNode = this.query(thatNode); + if (!thatNode) return null; + thatNode.append(thisNode); + return thisNode; + } + + /** + * Prepends `thisNode` to `thatNode` + * @param {Node} thisNode - Node to be prepended to another node + * @param {Node} thatNode - Node for `thisNode` to be prepended to + * @returns {Node} - `thisNode` to allow for chaining + */ + static prependTo(thisNode, thatNode) { + if (typeof(thatNode) == "string") thatNode = this.query(thatNode); + if (!thatNode) return null; + thatNode.prepend(thisNode); + return thisNode; + } + + /** + * Insert after a specific element, similar to jQuery's `thisElement.insertAfter(otherElement)`. + * @param {Node} thisNode - The node to insert + * @param {Node} targetNode - Node to insert after in the tree + * @returns {Node} - `thisNode` to allow for chaining + */ + static insertAfter(thisNode, targetNode) { + targetNode.parentNode.insertBefore(thisNode, targetNode.nextSibling); + return thisNode; + } + + /** + * Insert after a specific element, similar to jQuery's `thisElement.after(newElement)`. + * @param {Node} thisNode - The node to insert + * @param {Node} newNode - Node to insert after in the tree + * @returns {Node} - `thisNode` to allow for chaining + */ + static after(thisNode, newNode) { + thisNode.parentNode.insertBefore(newNode, thisNode.nextSibling); + return thisNode; + } + + /** + * Gets the next sibling element that matches the selector. + * @param {Element} element - Element to get the next sibling of + * @param {string} [selector=""] - Optional selector + * @returns {Element} - The sibling element + */ + static next(element, selector = "") { + return selector ? element.querySelector("+ " + selector) : element.nextElementSibling; + } + + /** + * Gets all subsequent siblings. + * @param {Element} element - Element to get next siblings of + * @returns {NodeList} - The list of siblings + */ + static nextAll(element) { + return element.querySelectorAll("~ *"); + } + + /** + * Gets the subsequent siblings until an element matches the selector. + * @param {Element} element - Element to get the following siblings of + * @param {string} selector - Selector to stop at + * @returns {Array} - The list of siblings + */ + static nextUntil(element, selector) { + const next = []; + while (element.nextElementSibling && !element.nextElementSibling.matches(selector)) next.push(element = element.nextElementSibling); + return next; + } + + /** + * Gets the previous sibling element that matches the selector. + * @param {Element} element - Element to get the previous sibling of + * @param {string} [selector=""] - Optional selector + * @returns {Element} - The sibling element + */ + static previous(element, selector = "") { + const previous = element.previousElementSibling; + if (selector) return previous && previous.matches(selector) ? previous : null; + return previous; + } + + /** + * Gets all preceeding siblings. + * @param {Element} element - Element to get preceeding siblings of + * @returns {NodeList} - The list of siblings + */ + static previousAll(element) { + const previous = []; + while (element.previousElementSibling) previous.push(element = element.previousElementSibling); + return previous; + } + + /** + * Gets the preceeding siblings until an element matches the selector. + * @param {Element} element - Element to get the preceeding siblings of + * @param {string} selector - Selector to stop at + * @returns {Array} - The list of siblings + */ + static previousUntil(element, selector) { + const previous = []; + while (element.previousElementSibling && !element.previousElementSibling.matches(selector)) previous.push(element = element.previousElementSibling); + return previous; + } + + /** + * Find which index in children a certain node is. Similar to jQuery's `$.index()` + * @param {HTMLElement} node - The node to find its index in parent + * @returns {number} Index of the node + */ + static indexInParent(node) { + const children = node.parentNode.childNodes; + let num = 0; + for (let i = 0; i < children.length; i++) { + if (children[i] == node) return num; + if (children[i].nodeType == 1) num++; + } + return -1; + } + + /** Shorthand for {@link module:DOMTools.indexInParent} */ + static index(node) {return this.indexInParent(node);} + + /** + * Gets the parent of the element if it matches the selector, + * otherwise returns null. + * @param {Element} element - Element to get parent of + * @param {string} [selector=""] - Selector to match parent + * @returns {(Element|null)} - The sibling element or null + */ + static parent(element, selector = "") { + return !selector || element.parentElement.matches(selector) ? element.parentElement : null; + } + + /** + * Gets all children of Element that match the selector if provided. + * @param {Element} element - Element to get all children of + * @param {string} selector - Selector to match the children to + * @returns {Array} - The list of children + */ + static findChild(element, selector) { + return element.querySelector(":scope > " + selector); + } + + /** + * Gets all children of Element that match the selector if provided. + * @param {Element} element - Element to get all children of + * @param {string} selector - Selector to match the children to + * @returns {Array} - The list of children + */ + static findChildren(element, selector) { + return element.querySelectorAll(":scope > " + selector); + } + + /** + * Gets all ancestors of Element that match the selector if provided. + * @param {Element} element - Element to get all parents of + * @param {string} [selector=""] - Selector to match the parents to + * @returns {Array} - The list of parents + */ + static parents(element, selector = "") { + const parents = []; + if (selector) while (element.parentElement && element.parentElement.closest(selector)) parents.push(element = element.parentElement.closest(selector)); + else while (element.parentElement) parents.push(element = element.parentElement); + return parents; + } + + /** + * Gets the ancestors until an element matches the selector. + * @param {Element} element - Element to get the ancestors of + * @param {string} selector - Selector to stop at + * @returns {Array} - The list of parents + */ + static parentsUntil(element, selector) { + const parents = []; + while (element.parentElement && !element.parentElement.matches(selector)) parents.push(element = element.parentElement); + return parents; + } + + /** + * Gets all siblings of the element that match the selector. + * @param {Element} element - Element to get all siblings of + * @param {string} [selector="*"] - Selector to match the siblings to + * @returns {Array} - The list of siblings + */ + static siblings(element, selector = "*") { + return Array.from(element.parentElement.children).filter(e => e != element && e.matches(selector)); + } + + /** + * Sets or gets css styles for a specific element. If `value` is provided + * then it sets the style and returns the element to allow for chaining, + * otherwise returns the style. + * @param {Element} element - Element to set the CSS of + * @param {string} attribute - Attribute to get or set + * @param {string} [value] - Value to set for attribute + * @returns {Element|string} - When setting a value, element is returned for chaining, otherwise the value is returned. + */ + static css(element, attribute, value) { + if (typeof(value) == "undefined") return global.getComputedStyle(element)[attribute]; + element.style[attribute] = value; + return element; + } + + /** + * Sets or gets the width for a specific element. If `value` is provided + * then it sets the width and returns the element to allow for chaining, + * otherwise returns the width. + * @param {Element} element - Element to set the CSS of + * @param {string} [value] - Width to set + * @returns {Element|string} - When setting a value, element is returned for chaining, otherwise the value is returned. + */ + static width(element, value) { + if (typeof(value) == "undefined") return parseInt(getComputedStyle(element).width); + element.style.width = value; + return element; + } + + /** + * Sets or gets the height for a specific element. If `value` is provided + * then it sets the height and returns the element to allow for chaining, + * otherwise returns the height. + * @param {Element} element - Element to set the CSS of + * @param {string} [value] - Height to set + * @returns {Element|string} - When setting a value, element is returned for chaining, otherwise the value is returned. + */ + static height(element, value) { + if (typeof(value) == "undefined") return parseInt(getComputedStyle(element).height); + element.style.height = value; + return element; + } + + /** + * Sets the inner text of an element if given a value, otherwise returns it. + * @param {Element} element - Element to set the text of + * @param {string} [text] - Content to set + * @returns {string} - Either the string set by this call or the current text content of the node. + */ + static text(element, text) { + if (typeof(text) == "undefined") return element.textContent; + return element.textContent = text; + } + + /** + * Returns the innerWidth of the element. + * @param {Element} element - Element to retrieve inner width of + * @return {number} - The inner width of the element. + */ + static innerWidth(element) { + return element.clientWidth; + } + + /** + * Returns the innerHeight of the element. + * @param {Element} element - Element to retrieve inner height of + * @return {number} - The inner height of the element. + */ + static innerHeight(element) { + return element.clientHeight; + } + + /** + * Returns the outerWidth of the element. + * @param {Element} element - Element to retrieve outer width of + * @return {number} - The outer width of the element. + */ + static outerWidth(element) { + return element.offsetWidth; + } + + /** + * Returns the outerHeight of the element. + * @param {Element} element - Element to retrieve outer height of + * @return {number} - The outer height of the element. + */ + static outerHeight(element) { + return element.offsetHeight; + } + + /** + * Gets the offset of the element in the page. + * @param {Element} element - Element to get offset of + * @return {Offset} - The offset of the element + */ + static offset(element) { + return element.getBoundingClientRect(); + } + + static get listeners() {return this._listeners || (this._listeners = {});} + + /** + * This is similar to jQuery's `on` function and can *hopefully* be used in the same way. + * + * Rather than attempt to explain, I'll show some example usages. + * + * The following will add a click listener (in the `myPlugin` namespace) to `element`. + * `DOMTools.on(element, "click.myPlugin", () => {console.log("clicked!");});` + * + * The following will add a click listener (in the `myPlugin` namespace) to `element` that only fires when the target is a `.block` element. + * `DOMTools.on(element, "click.myPlugin", ".block", () => {console.log("clicked!");});` + * + * The following will add a click listener (without namespace) to `element`. + * `DOMTools.on(element, "click", () => {console.log("clicked!");});` + * + * The following will add a click listener (without namespace) to `element` that only fires once. + * `const cancel = DOMTools.on(element, "click", () => {console.log("fired!"); cancel();});` + * + * @param {Element} element - Element to add listener to + * @param {string} event - Event to listen to with option namespace (e.g. "event.namespace") + * @param {(string|callable)} delegate - Selector to run on element to listen to + * @param {callable} [callback] - Function to fire on event + * @returns {module:DOMTools~CancelListener} - A function that will undo the listener + */ + static on(element, event, delegate, callback) { + const [type, namespace] = event.split("."); + const hasDelegate = delegate && callback; + if (!callback) callback = delegate; + const eventFunc = !hasDelegate ? callback : function(ev) { + if (ev.target.matches(delegate)) { + callback(ev); + } + }; + + element.addEventListener(type, eventFunc); + const cancel = () => { + element.removeEventListener(type, eventFunc); + }; + if (namespace) { + if (!this.listeners[namespace]) this.listeners[namespace] = []; + const newCancel = () => { + cancel(); + this.listeners[namespace].splice(this.listeners[namespace].findIndex(l => l.event == type && l.element == element), 1); + }; + this.listeners[namespace].push({ + event: type, + element: element, + cancel: newCancel + }); + return newCancel; + } + return cancel; + } + + /** + * Functionality for this method matches {@link module:DOMTools.on} but automatically cancels itself + * and removes the listener upon the first firing of the desired event. + * + * @param {Element} element - Element to add listener to + * @param {string} event - Event to listen to with option namespace (e.g. "event.namespace") + * @param {(string|callable)} delegate - Selector to run on element to listen to + * @param {callable} [callback] - Function to fire on event + * @returns {module:DOMTools~CancelListener} - A function that will undo the listener + */ + static once(element, event, delegate, callback) { + const [type, namespace] = event.split("."); + const hasDelegate = delegate && callback; + if (!callback) callback = delegate; + const eventFunc = !hasDelegate ? function(ev) { + callback(ev); + element.removeEventListener(type, eventFunc); + } : function(ev) { + if (!ev.target.matches(delegate)) return; + callback(ev); + element.removeEventListener(type, eventFunc); + }; + + element.addEventListener(type, eventFunc); + const cancel = () => { + element.removeEventListener(type, eventFunc); + }; + if (namespace) { + if (!this.listeners[namespace]) this.listeners[namespace] = []; + const newCancel = () => { + cancel(); + this.listeners[namespace].splice(this.listeners[namespace].findIndex(l => l.event == type && l.element == element), 1); + }; + this.listeners[namespace].push({ + event: type, + element: element, + cancel: newCancel + }); + return newCancel; + } + return cancel; + } + + static __offAll(event, element) { + const [type, namespace] = event.split("."); + let matchFilter = listener => listener.event == type, defaultFilter = _ => _; + if (element) { + matchFilter = l => l.event == type && l.element == element; + defaultFilter = l => l.element == element; + } + const listeners = this.listeners[namespace] || []; + const list = type ? listeners.filter(matchFilter) : listeners.filter(defaultFilter); + for (let c = 0; c < list.length; c++) list[c].cancel(); + } + + /** + * This is similar to jQuery's `off` function and can *hopefully* be used in the same way. + * + * Rather than attempt to explain, I'll show some example usages. + * + * The following will remove a click listener called `onClick` (in the `myPlugin` namespace) from `element`. + * `DOMTools.off(element, "click.myPlugin", onClick);` + * + * The following will remove a click listener called `onClick` (in the `myPlugin` namespace) from `element` that only fired when the target is a `.block` element. + * `DOMTools.off(element, "click.myPlugin", ".block", onClick);` + * + * The following will remove a click listener (without namespace) from `element`. + * `DOMTools.off(element, "click", onClick);` + * + * The following will remove all listeners in namespace `myPlugin` from `element`. + * `DOMTools.off(element, ".myPlugin");` + * + * The following will remove all click listeners in namespace `myPlugin` from *all elements*. + * `DOMTools.off("click.myPlugin");` + * + * The following will remove all listeners in namespace `myPlugin` from *all elements*. + * `DOMTools.off(".myPlugin");` + * + * @param {(Element|string)} element - Element to remove listener from + * @param {string} [event] - Event to listen to with option namespace (e.g. "event.namespace") + * @param {(string|callable)} [delegate] - Selector to run on element to listen to + * @param {callable} [callback] - Function to fire on event + * @returns {Element} - The original element to allow for chaining + */ + static off(element, event, delegate, callback) { + if (typeof(element) == "string") return this.__offAll(element); + const [type, namespace] = event.split("."); + if (namespace) return this.__offAll(event, element); + + const hasDelegate = delegate && callback; + if (!callback) callback = delegate; + const eventFunc = !hasDelegate ? callback : function(ev) { + if (ev.target.matches(delegate)) { + callback(ev); + } + }; + + element.removeEventListener(type, eventFunc); + return element; + } + + /** + * Adds a listener for when the node is added/removed from the document body. + * The listener is automatically removed upon firing. + * @param {HTMLElement} node - node to wait for + * @param {callable} callback - function to be performed on event + * @param {boolean} onMount - determines if it should fire on Mount or on Unmount + */ + static onMountChange(node, callback, onMount = true) { + const wrappedCallback = () => { + this.observer.unsubscribe(wrappedCallback); + callback(); + }; + this.observer.subscribe(wrappedCallback, mutation => { + const nodes = Array.from(onMount ? mutation.addedNodes : mutation.removedNodes); + const directMatch = nodes.indexOf(node) > -1; + const parentMatch = nodes.some(parent => parent.contains(node)); + return directMatch || parentMatch; + }); + return node; + } + + /** Shorthand for {@link module:DOMTools.onMountChange} with third parameter `true` */ + static onMount(node, callback) {return this.onMountChange(node, callback);} + + /** Shorthand for {@link module:DOMTools.onMountChange} with third parameter `false` */ + static onUnmount(node, callback) {return this.onMountChange(node, callback, false);} + + /** Alias for {@link module:DOMTools.onMount} */ + static onAdded(node, callback) {return this.onMount(node, callback);} + + /** Alias for {@link module:DOMTools.onUnmount} */ + static onRemoved(node, callback) {return this.onUnmount(node, callback, false);} + + /** + * Helper function which combines multiple elements into one parent element + * @param {Array} elements - array of elements to put into a single parent + */ + static wrap(elements) { + const domWrapper = this.parseHTML(`
`); + for (let e = 0; e < elements.length; e++) domWrapper.appendChild(elements[e]); + return domWrapper; + } + + /** + * Resolves the node to an HTMLElement. This is mainly used by library modules. + * @param {(jQuery|Element)} node - node to resolve + */ + static resolveElement(node) { + try { + if (!(node instanceof window.jQuery) && !(node instanceof Element)) return undefined; + return node instanceof window.jQuery ? node[0] : node; + } + catch { + return node; + } + } +} + +/***/ }), + +/***/ "./src/modules/logger.js": +/*!*******************************!*\ + !*** ./src/modules/logger.js ***! + \*******************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ LogTypes: () => (/* binding */ LogTypes), +/* harmony export */ "default": () => (/* binding */ Logger) +/* harmony export */ }); +/** + * Simple logger for the lib and plugins. + * + * @module Logger + */ + +/* eslint-disable no-console */ + +/** + * List of logging types. + */ +const LogTypes = { + /** Alias for error */ + err: "error", + error: "error", + /** Alias for debug */ + dbg: "debug", + debug: "debug", + log: "log", + warn: "warn", + info: "info" +}; + +class Logger { + + /** + * Logs an error using a collapsed error group with stacktrace. + * + * @param {string} module - Name of the calling module. + * @param {string} message - Message or error to have logged. + * @param {Error} error - Error object to log with the message. + */ + static stacktrace(module, message, error) { + console.error(`%c[${module}]%c ${message}\n\n%c`, "color: #3a71c1; font-weight: 700;", "color: red; font-weight: 700;", "color: red;", error); + } + + /** + * Logs using error formatting. For logging an actual error object consider {@link module:Logger.stacktrace} + * + * @param {string} module - Name of the calling module. + * @param {string} message - Messages to have logged. + */ + static err(module, ...message) {Logger._log(module, message, "error");} + + /** + * Logs a warning message. + * + * @param {string} module - Name of the calling module. + * @param {...any} message - Messages to have logged. + */ + static warn(module, ...message) {Logger._log(module, message, "warn");} + + /** + * Logs an informational message. + * + * @param {string} module - Name of the calling module. + * @param {...any} message - Messages to have logged. + */ + static info(module, ...message) {Logger._log(module, message, "info");} + + /** + * Logs used for debugging purposes. + * + * @param {string} module - Name of the calling module. + * @param {...any} message - Messages to have logged. + */ + static debug(module, ...message) {Logger._log(module, message, "debug");} + + /** + * Logs used for basic loggin. + * + * @param {string} module - Name of the calling module. + * @param {...any} message - Messages to have logged. + */ + static log(module, ...message) {Logger._log(module, message);} + + /** + * Logs strings using different console levels and a module label. + * + * @param {string} module - Name of the calling module. + * @param {any|Array} message - Messages to have logged. + * @param {module:Logger.LogTypes} type - Type of log to use in console. + */ + static _log(module, message, type = "log") { + type = Logger.parseType(type); + if (!Array.isArray(message)) message = [message]; + console[type](`%c[${module}]%c`, "color: #3a71c1; font-weight: 700;", "", ...message); + } + + static parseType(type) { + return LogTypes.hasOwnProperty(type) ? LogTypes[type] : "log"; + } + +} + +/***/ }), + +/***/ "./src/modules/modules.js": +/*!********************************!*\ + !*** ./src/modules/modules.js ***! + \********************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ ColorConverter: () => (/* reexport safe */ _colorconverter__WEBPACK_IMPORTED_MODULE_3__["default"]), +/* harmony export */ DOMTools: () => (/* reexport safe */ _domtools__WEBPACK_IMPORTED_MODULE_4__["default"]), +/* harmony export */ DiscordClassModules: () => (/* reexport safe */ _discordclassmodules__WEBPACK_IMPORTED_MODULE_13__["default"]), +/* harmony export */ DiscordClasses: () => (/* reexport safe */ _discordclasses__WEBPACK_IMPORTED_MODULE_5__["default"]), +/* harmony export */ DiscordModules: () => (/* reexport safe */ _discordmodules__WEBPACK_IMPORTED_MODULE_2__["default"]), +/* harmony export */ DiscordSelectors: () => (/* reexport safe */ _discordselectors__WEBPACK_IMPORTED_MODULE_6__["default"]), +/* harmony export */ Filters: () => (/* reexport safe */ _webpackmodules__WEBPACK_IMPORTED_MODULE_1__.Filters), +/* harmony export */ Logger: () => (/* reexport safe */ _logger__WEBPACK_IMPORTED_MODULE_9__["default"]), +/* harmony export */ Patcher: () => (/* reexport safe */ _patcher__WEBPACK_IMPORTED_MODULE_10__["default"]), +/* harmony export */ PluginUpdater: () => (/* reexport safe */ _pluginupdater__WEBPACK_IMPORTED_MODULE_11__["default"]), +/* harmony export */ PluginUtilities: () => (/* reexport safe */ _pluginutilities__WEBPACK_IMPORTED_MODULE_12__["default"]), +/* harmony export */ ReactComponents: () => (/* reexport safe */ _reactcomponents__WEBPACK_IMPORTED_MODULE_8__["default"]), +/* harmony export */ ReactTools: () => (/* reexport safe */ _reacttools__WEBPACK_IMPORTED_MODULE_7__["default"]), +/* harmony export */ Structs: () => (/* reexport module object */ structs__WEBPACK_IMPORTED_MODULE_14__), +/* harmony export */ Utilities: () => (/* reexport safe */ _utilities__WEBPACK_IMPORTED_MODULE_0__["default"]), +/* harmony export */ WebpackModules: () => (/* reexport safe */ _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"]) +/* harmony export */ }); +/* harmony import */ var _utilities__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./utilities */ "./src/modules/utilities.js"); +/* harmony import */ var _webpackmodules__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./webpackmodules */ "./src/modules/webpackmodules.js"); +/* harmony import */ var _discordmodules__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./discordmodules */ "./src/modules/discordmodules.js"); +/* harmony import */ var _colorconverter__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./colorconverter */ "./src/modules/colorconverter.js"); +/* harmony import */ var _domtools__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./domtools */ "./src/modules/domtools.js"); +/* harmony import */ var _discordclasses__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./discordclasses */ "./src/modules/discordclasses.js"); +/* harmony import */ var _discordselectors__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./discordselectors */ "./src/modules/discordselectors.js"); +/* harmony import */ var _reacttools__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./reacttools */ "./src/modules/reacttools.js"); +/* harmony import */ var _reactcomponents__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ./reactcomponents */ "./src/modules/reactcomponents.js"); +/* harmony import */ var _logger__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ./logger */ "./src/modules/logger.js"); +/* harmony import */ var _patcher__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! ./patcher */ "./src/modules/patcher.js"); +/* harmony import */ var _pluginupdater__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(/*! ./pluginupdater */ "./src/modules/pluginupdater.js"); +/* harmony import */ var _pluginutilities__WEBPACK_IMPORTED_MODULE_12__ = __webpack_require__(/*! ./pluginutilities */ "./src/modules/pluginutilities.js"); +/* harmony import */ var _discordclassmodules__WEBPACK_IMPORTED_MODULE_13__ = __webpack_require__(/*! ./discordclassmodules */ "./src/modules/discordclassmodules.js"); +/* harmony import */ var structs__WEBPACK_IMPORTED_MODULE_14__ = __webpack_require__(/*! structs */ "./src/structs/structs.js"); + + + + + + + + + + + + + + + + + + + + +/***/ }), + +/***/ "./src/modules/patcher.js": +/*!********************************!*\ + !*** ./src/modules/patcher.js ***! + \********************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "default": () => (/* binding */ Patcher) +/* harmony export */ }); +/** + * Patcher that can patch other functions allowing you to run code before, after or + * instead of the original function. Can also alter arguments and return values. + * + * This is a modified version of what we have been working on in BDv2. {@link https://github.com/JsSucks/BetterDiscordApp/blob/master/client/src/modules/patcher.js} + * + * @module Patcher + */ + +class Patcher { + + // Use window._patches instead of local variables in case something tries to whack the lib + static get patches() {return [];} + + /** + * Returns all the patches done by a specific caller + * @param {string} name - Name of the patch caller + * @method + */ + static getPatchesByCaller(name) { + return BdApi.Patcher.getPatchesByCaller(name); + } + + /** + * Unpatches all patches passed, or when a string is passed unpatches all + * patches done by that specific caller. + * @param {Array|string} patches - Either an array of patches to unpatch or a caller name + */ + static unpatchAll(patches) { + BdApi.Patcher.unpatchAll(patches); + } + + /** + * Function with no arguments and no return value that may be called to revert changes made by {@link module:Patcher}, restoring (unpatching) original method. + * @callback module:Patcher~unpatch + */ + + /** + * A callback that modifies method logic. This callback is called on each call of the original method and is provided all data about original call. Any of the data can be modified if necessary, but do so wisely. + * + * The third argument for the callback will be `undefined` for `before` patches. `originalFunction` for `instead` patches and `returnValue` for `after` patches. + * + * @callback module:Patcher~patchCallback + * @param {object} thisObject - `this` in the context of the original function. + * @param {args} args - The original arguments of the original function. + * @param {(function|*)} extraValue - For `instead` patches, this is the original function from the module. For `after` patches, this is the return value of the function. + * @return {*} Makes sense only when using an `instead` or `after` patch. If something other than `undefined` is returned, the returned value replaces the value of `returnValue`. If used for `before` the return value is ignored. + */ + + /** + * This method patches onto another function, allowing your code to run beforehand. + * Using this, you are also able to modify the incoming arguments before the original method is run. + * + * @param {string} caller - Name of the caller of the patch function. Using this you can undo all patches with the same name using {@link module:Patcher.unpatchAll}. Use `""` if you don't care. + * @param {object} moduleToPatch - Object with the function to be patched. Can also patch an object's prototype. + * @param {string} functionName - Name of the method to be patched + * @param {module:Patcher~patchCallback} callback - Function to run before the original method + * @return {module:Patcher~unpatch} Function with no arguments and no return value that should be called to cancel (unpatch) this patch. You should save and run it when your plugin is stopped. + */ + static before(caller, moduleToPatch, functionName, callback) {return BdApi.Patcher.before(caller, moduleToPatch, functionName, callback);} + + /** + * This method patches onto another function, allowing your code to run after. + * Using this, you are also able to modify the return value, using the return of your code instead. + * + * @param {string} caller - Name of the caller of the patch function. Using this you can undo all patches with the same name using {@link module:Patcher.unpatchAll}. Use `""` if you don't care. + * @param {object} moduleToPatch - Object with the function to be patched. Can also patch an object's prototype. + * @param {string} functionName - Name of the method to be patched + * @param {module:Patcher~patchCallback} callback - Function to run instead of the original method + * @return {module:Patcher~unpatch} Function with no arguments and no return value that should be called to cancel (unpatch) this patch. You should save and run it when your plugin is stopped. + */ + static after(caller, moduleToPatch, functionName, callback) {return BdApi.Patcher.after(caller, moduleToPatch, functionName, callback);} + + /** + * This method patches onto another function, allowing your code to run instead. + * Using this, you are also able to modify the return value, using the return of your code instead. + * + * @param {string} caller - Name of the caller of the patch function. Using this you can undo all patches with the same name using {@link module:Patcher.unpatchAll}. Use `""` if you don't care. + * @param {object} moduleToPatch - Object with the function to be patched. Can also patch an object's prototype. + * @param {string} functionName - Name of the method to be patched + * @param {module:Patcher~patchCallback} callback - Function to run after the original method + * @return {module:Patcher~unpatch} Function with no arguments and no return value that should be called to cancel (unpatch) this patch. You should save and run it when your plugin is stopped. + */ + static instead(caller, moduleToPatch, functionName, callback) {return BdApi.Patcher.instead(caller, moduleToPatch, functionName, callback);} + +} + +/***/ }), + +/***/ "./src/modules/pluginupdater.js": +/*!**************************************!*\ + !*** ./src/modules/pluginupdater.js ***! + \**************************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "default": () => (/* binding */ PluginUpdater) +/* harmony export */ }); +/* harmony import */ var _domtools__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./domtools */ "./src/modules/domtools.js"); +/* harmony import */ var ui__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ui */ "./src/ui/ui.js"); +/* harmony import */ var _styles_updates_css__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../styles/updates.css */ "./src/styles/updates.css"); + + + + + +const fileSystem = require("fs"); +const path = require("path"); +const request = require("request"); + +/** + * Comparator that takes the current version and the remote version, + * then compares them returning `true` if there is an update and `false` otherwise. + * @param {string} currentVersion - the current version of the plugin + * @param {string} remoteVersion - the remote version of the plugin + * @returns {boolean} - whether the plugin has an update or not + * @callback module:PluginUpdater~comparator + */ + +const splitRegex = /[^\S\r\n]*?\r?(?:\r\n|\n)[^\S\r\n]*?\*[^\S\r\n]?/; +const escapedAtRegex = /^\\@/; +const HOUR_IN_MILLISECONDS = 1000 * 60 * 60; +const pluginId = name => name + "-update-notice"; +const pending = []; +const banner = {}; + + +/** + * Functions that check for and update existing plugins. + * @module PluginUpdater + * @deprecated It is recommended to go through the approval process instead. + */ +class PluginUpdater { + + static get CSS() {return _styles_updates_css__WEBPACK_IMPORTED_MODULE_2__["default"];} + static get state() {return window.__PLUGIN_UPDATES__;} + static getPlugin(link) {return this.state.plugins[link];} + static setPlugin(name, raw, version, comparator) {this.state.plugins[raw] = {name, raw, version, comparator};} + static clearPending() { + delete banner.close; + delete banner.notice; + pending.splice(0, pending.length); + } + + /** + * Checks for updates for the specified plugin at the specified link. The final + * parameter should link to the raw text of the plugin and will compare semantic + * versions. + * @param {string} pluginName - name of the plugin + * @param {string} currentVersion - current version (semantic versioning only) + * @param {string} updateURL - url to check for update + * @param {module:PluginUpdater~comparator} [comparator] - comparator that determines if there is an update. If not provided uses {@link module:PluginUpdater.defaultComparator}. + */ + static async checkForUpdate(pluginName, currentVersion, addonId, comparator) { + if (!pluginName || !currentVersion || !addonId) return; + let isUrl = false; + try { + // eslint-disable-next-line no-new + new URL(addonId); + isUrl = true; + } + catch { + isUrl = false; + } + let updateLink = `https://betterdiscord.app/gh-redirect?id=${addonId}`; + if (isUrl) updateLink = addonId; + if (typeof(comparator) != "function") comparator = this.defaultComparator; + this.setPlugin(pluginName, updateLink, currentVersion, comparator); + + const hasUpdate = await this.hasUpdate(updateLink); + if (!hasUpdate) return; + pending.push(updateLink); + this.showUpdateNotice(updateLink); + } + + static async checkAllPlugins() { + for (const link in this.state.plugins) { + const hasUpdate = await this.hasUpdate(link); + if (!hasUpdate) return; + pending.push(link); + this.showUpdateNotice(link); + } + } + + /** + * Will check for updates and automatically show or remove the update notice + * bar based on the internal result. Better not to call this directly and to + * instead use {@link module:PluginUpdater.checkForUpdate}. + * @param {string} pluginName - name of the plugin to check + * @param {string} updateLink - link to the raw text version of the plugin + */ + static async hasUpdate(updateLink) { + const doit = (resolve, result) => { + try { + const plugin = this.getPlugin(updateLink); + const meta = this.parseMeta(result); + plugin.remoteVersion = meta.version; + const hasUpdate = plugin.comparator(plugin.version, plugin.remoteVersion); + if (hasUpdate) plugin.remote = result; + resolve(hasUpdate); + } + catch (err) { + resolve(false); + } + }; + return new Promise(resolve => { + request(updateLink, (err, resp, result) => { + if (err) return resolve(false); + + // If a direct url was used + if (resp.statusCode === 200) return doit(resolve, result); + + // If an addon id and redirect was used + if (resp.statusCode === 302) { + request(resp.headers.location, (error, response, body) => { + if (error || response.statusCode !== 200) return resolve(false); + return doit(resolve, body); + }); + } + }); + }); + } + + /** + * @param {string} pluginName - name of the plugin to download + * @param {string} updateLink - link to the raw text version of the plugin + */ + static async updatePlugin(updateLink) { + const plugin = this.getPlugin(updateLink); + + let filename = updateLink.split("/"); + filename = filename[filename.length - 1]; + const file = path.join(BdApi.Plugins.folder, filename); + await new Promise(r => fileSystem.writeFile(file, plugin.remote, r)); + ui__WEBPACK_IMPORTED_MODULE_1__.Toasts.success(`${plugin.name} ${plugin.version} has been replaced by ${plugin.name} ${plugin.remoteVersion}`); + } + + /** + * Will show the update notice top bar seen in Discord. Better not to call + * this directly and to instead use {@link module:PluginUpdater.checkForUpdate}. + * @param {string} pluginName - name of the plugin + * @param {string} updateLink - link to the raw text version of the plugin + */ + static showUpdateNotice(updateLink) { + const plugin = this.getPlugin(updateLink); + const pluginNoticeID = pluginId(plugin.name); + if (document.getElementById(pluginNoticeID)) return; // This plugin already shown + if (!document.getElementById("plugin-update-notice-message")) { + banner.notice = _domtools__WEBPACK_IMPORTED_MODULE_0__["default"].parseHTML(`The following plugins have updates:  `); + banner.close = BdApi.showNotice(banner.notice, { + timeout: 0, + buttons: [{ + label: "Update All", + onClick: async () => { + for (const link of pending) await this.updatePlugin(link); + banner.close(); + } + }] + }); + _domtools__WEBPACK_IMPORTED_MODULE_0__["default"].onRemoved(banner.notice, this.clearPending); + } + + const outdatedPlugins = document.getElementById("outdated-plugins"); + const pluginNoticeElement = _domtools__WEBPACK_IMPORTED_MODULE_0__["default"].parseHTML(`${plugin.name}`); + pluginNoticeElement.addEventListener("click", async () => { + await this.updatePlugin(updateLink); + this.removeUpdateNotice(updateLink); + }); + if (outdatedPlugins.querySelectorAll("span").length) outdatedPlugins.append(_domtools__WEBPACK_IMPORTED_MODULE_0__["default"].createElement(", ")); + outdatedPlugins.append(pluginNoticeElement); + ui__WEBPACK_IMPORTED_MODULE_1__.Tooltip.create(pluginNoticeElement, "Click To Update!", {side: "bottom"}); + } + + /** + * Will remove the plugin from the update notice top bar seen in Discord. + * Better not to call this directly and to instead use {@link module:PluginUpdater.checkForUpdate}. + * @param {string} pluginName - name of the plugin + */ + static removeUpdateNotice(updateLink) { + const plugin = this.getPlugin(updateLink); + if (!document.getElementById("outdated-plugins")) return; + const notice = document.getElementById(pluginId(plugin.name)); + if (notice) { + if (notice.nextElementSibling && notice.nextElementSibling.matches(".separator")) notice.nextElementSibling.remove(); + else if (notice.previousElementSibling && notice.previousElementSibling.matches(".separator")) notice.previousElementSibling.remove(); + notice.remove(); + } + + if (!document.getElementById("outdated-plugins").querySelectorAll("span").length) { + banner?.close(); + } + } + + static parseMeta(fileContent) { + const block = fileContent.split("/**", 2)[1].split("*/", 1)[0]; + const out = {}; + let field = ""; + let accum = ""; + for (const line of block.split(splitRegex)) { + if (line.length === 0) continue; + if (line.charAt(0) === "@" && line.charAt(1) !== " ") { + out[field] = accum; + const l = line.indexOf(" "); + field = line.substring(1, l); + accum = line.substring(l + 1); + } + else { + accum += " " + line.replace("\\n", "\n").replace(escapedAtRegex, "@"); + } + } + out[field] = accum.trim(); + delete out[""]; + out.format = "jsdoc"; + return out; + } + + /** + * The default comparator used as {@link module:PluginUpdater~comparator} for {@link module:PluginUpdater.checkForUpdate}. + * This solely compares remote > local. You do not need to provide this as a comparator if your plugin adheres + * to this style as this will be used as default. + * @param {string} currentVersion + * @param {string} content + */ + static defaultComparator(currentVersion, remoteVersion) { + return remoteVersion > currentVersion; + } +} + +if (typeof(window.__PLUGIN_UPDATES__) === "undefined") window.__PLUGIN_UPDATES__ = {plugins: {}}; +if (window.__PLUGIN_UPDATES__.interval) clearInterval(window.__PLUGIN_UPDATES__.interval); + +window.__PLUGIN_UPDATES__.interval = setInterval(PluginUpdater.checkAllPlugins.bind(PluginUpdater), HOUR_IN_MILLISECONDS * 2); + +// Transition +if (window.PluginUpdates) { + if (window.PluginUpdates.interval) clearInterval(window.PluginUpdates.interval); + Object.assign(window.__PLUGIN_UPDATES__.plugins, window.PluginUpdates.plugins); + delete window.PluginUpdates; +} + +/***/ }), + +/***/ "./src/modules/pluginutilities.js": +/*!****************************************!*\ + !*** ./src/modules/pluginutilities.js ***! + \****************************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "default": () => (/* binding */ PluginUtilities) +/* harmony export */ }); +/* harmony import */ var _utilities__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./utilities */ "./src/modules/utilities.js"); +/* harmony import */ var _domtools__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./domtools */ "./src/modules/domtools.js"); + + + +/** + * A series of useful functions for BetterDiscord plugins. + * @module PluginUtilities + * @deprecated 1/21/22 Use Alternatives + */ + + + class PluginUtilities { + + /** + * Loads data through BetterDiscord's API. + * @param {string} name - name for the file (usually plugin name) + * @param {string} key - which key the data is saved under + * @param {object} defaultData - default data to populate the object with + * @returns {object} the combined saved and default data + * @deprecated 1/21/22 Use Utilities or BdApi directly + */ + static loadData(name, key, defaultData) {return _utilities__WEBPACK_IMPORTED_MODULE_0__["default"].loadData(name, key, defaultData);} + + /** + * Saves data through BetterDiscord's API. + * @param {string} name - name for the file (usually plugin name) + * @param {string} key - which key the data should be saved under + * @param {object} data - data to save + * @deprecated 1/21/22 Use Utilities or BdApi directly + */ + static saveData(name, key, data) {return _utilities__WEBPACK_IMPORTED_MODULE_0__["default"].saveData(name, key, data);} + + /** + * Loads settings through BetterDiscord's API. + * @param {string} name - name for the file (usually plugin name) + * @param {object} defaultData - default data to populate the object with + * @returns {object} the combined saved and default settings + * @deprecated 1/21/22 Use Utilities or BdApi directly + */ + static loadSettings(name, defaultSettings) {return _utilities__WEBPACK_IMPORTED_MODULE_0__["default"].loadSettings(name, defaultSettings);} + + /** + * Saves settings through BetterDiscord's API. + * @param {string} name - name for the file (usually plugin name) + * @param {object} data - settings to save + * @deprecated 1/21/22 Use Utilities or BdApi directly + */ + static saveSettings(name, data) {return _utilities__WEBPACK_IMPORTED_MODULE_0__["default"].saveSettings(name, data);} + + /** + * Get the full path to the BetterDiscord folder. + * @returns {string} full path to the BetterDiscord folder + * @deprecated 1/21/22 Use BdApi + */ + static getBDFolder(subtarget = "") { + const process = require("process"); + const path = require("path"); + if (process.env.injDir) return path.resolve(process.env.injDir, subtarget); + switch (process.platform) { + case "win32": + return path.resolve(process.env.APPDATA, "BetterDiscord/", subtarget); + case "darwin": + return path.resolve(process.env.HOME, "Library/Application Support/", "BetterDiscord/", subtarget); + default: + return path.resolve(process.env.XDG_CONFIG_HOME ? process.env.XDG_CONFIG_HOME : process.env.HOME + "/.config", "BetterDiscord/", subtarget); + } + } + + /** + * Get the full path to the plugins folder. + * @returns {string} full path to the plugins folder + * @deprecated 1/21/22 Use BdApi + */ + static getPluginsFolder() {return BdApi.Plugins.folder;} + + /** + * Get the full path to the themes folder. + * @returns {string} full path to the themes folder + * @deprecated 1/21/22 Use BdApi + */ + static getThemesFolder() {return BdApi.Themes.folder;} + + /** + * Adds a callback to a set of listeners for onSwitch. + * @param {callable} callback - basic callback to happen on channel switch + * @deprecated 1/21/22 Use onSwitch + */ + static addOnSwitchListener() {} + + /** + * Removes the listener added by {@link InternalUtilities.addOnSwitchListener}. + * @param {callable} callback - callback to remove from the listener list + * @deprecated 1/21/22 Use onSwitch + */ + static removeOnSwitchListener() {} + + /** + * Adds a style to the document. + * @param {string} id - identifier to use as the element id + * @param {string} css - css to add to the document + * @deprecated 1/21/22 Use DOMTools + */ + static addStyle(id, css) {return _domtools__WEBPACK_IMPORTED_MODULE_1__["default"].addStyle(id, css);} + + /** + * Removes a style from the document. + * @param {string} id - original identifier used + * @deprecated 1/21/22 Use DOMTools + */ + static removeStyle(id) {return _domtools__WEBPACK_IMPORTED_MODULE_1__["default"].removeStyle(id);} + + /** + * Adds/requires a remote script to be loaded + * @param {string} id - identifier to use for this script + * @param {string} url - url from which to load the script + * @returns {Promise} promise that resolves when the script is loaded + * @deprecated 1/21/22 Use DOMTools + */ + static addScript(id, url) {return _domtools__WEBPACK_IMPORTED_MODULE_1__["default"].addScript(id, url);} + + /** + * Removes a remote script from the document. + * @param {string} id - original identifier used + * @deprecated 1/21/22 Use DOMTools + */ + static removeScript(id) {return _domtools__WEBPACK_IMPORTED_MODULE_1__["default"].removeScript(id);} +} + + + + +/***/ }), + +/***/ "./src/modules/reactcomponents.js": +/*!****************************************!*\ + !*** ./src/modules/reactcomponents.js ***! + \****************************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "default": () => (/* binding */ ReactComponents) +/* harmony export */ }); +/* harmony import */ var _domtools__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./domtools */ "./src/modules/domtools.js"); +/* harmony import */ var _reacttools__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./reacttools */ "./src/modules/reacttools.js"); +/* harmony import */ var _utilities__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./utilities */ "./src/modules/utilities.js"); +/** + * BetterDiscord React Component Manipulations + * Original concept and some code by samogot - https://github.com/samogot / https://github.com/samogot/betterdiscord-plugins/tree/master/v2/1Lib%20Discord%20Internals + * + * Copyright (c) 2015-present JsSucks - https://github.com/JsSucks + * All rights reserved. + * https://github.com/JsSucks - https://betterdiscord.net + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. +*/ + + + + + +class ReactComponent { + constructor(id, component, selector, filter) { + this.id = id; + this.component = component; + this.selector = selector; + this.filter = filter; + } + + forceUpdateAll() { + if (!this.selector) return; + for (const e of document.querySelectorAll(this.selector)) { + const stateNode = _utilities__WEBPACK_IMPORTED_MODULE_2__["default"].findInTree(_reacttools__WEBPACK_IMPORTED_MODULE_1__["default"].getReactInstance(e), m => m && m.forceUpdate, {walkable: ["return", "stateNode"]}); + if (!stateNode) continue; + stateNode.forceUpdate(); + } + } +} + +/** + * Methods for obtaining and interacting with react components. + * @module ReactComponents + */ +class ReactComponents { + static get components() {return this._components || (this._components = new Map());} + static get unknownComponents() {return this._unknownComponents || (this._unknownComponents = new Set());} + static get listeners() {return this._listeners || (this._listeners = new Map());} + static get nameSetters() {return this._nameSetters || (this._nameSetters = new Set());} + + static get ReactComponent() {return ReactComponent;} + + static push(component, selector, filter) { + if (typeof(component) !== "function") return null; + const {displayName} = component; + if (!displayName) return this.processUnknown(component); + + const have = this.components.get(displayName); + if (have) { + if (!have.selector) have.selector = selector; + if (!have.filter) have.filter = filter; + return component; + } + + const c = new ReactComponent(displayName, component, selector, filter); + this.components.set(c.id, c); + + const listener = this.listeners.get(displayName); + if (listener) { + for (const l of listener.children) l(c); + this.listeners.delete(listener); + } + + return c; + } + + /** + * Finds a component from the components array or by waiting for it to be mounted. + * @param {String} name The component's name + * @param {Object} selector A selector to look for + * @return {Promise} + */ + static async getComponentByName(name, selector) { + return this.getComponent(name, selector, m => m.displayName == name); + } + + /** + * Finds a component from the components array or by waiting for it to be mounted. + * @param {String} name The component's name + * @param {Object} selector A selector to look for + * @param {Function} filter A function to filter components if a single element is rendered by multiple components + * @return {Promise} + */ + static async getComponent(name, selector, filter) { + const have = this.components.get(name); + if (have) { + if (!have.selector) have.selector = selector; + if (!have.filter) have.filter = filter; + return have; + } + + if (selector) { + const callback = () => { + if (this.components.get(name)) { + _domtools__WEBPACK_IMPORTED_MODULE_0__["default"].observer.unsubscribe(observerSubscription); + return; + } + + const elements = document.querySelectorAll(selector); + if (!elements.length) return; + + let component; + for (const element of elements) { + const componentsFound = _reacttools__WEBPACK_IMPORTED_MODULE_1__["default"].getComponents(element); + component = filter ? componentsFound.find(filter) : componentsFound[0]; + if (component) break; + } + + if (!component && filter) return; + + _domtools__WEBPACK_IMPORTED_MODULE_0__["default"].observer.unsubscribe(observerSubscription); + + if (!component) return; + + if (!component.displayName) component.displayName = name; + + this.push(component, selector, filter); + }; + + const observerSubscription = _domtools__WEBPACK_IMPORTED_MODULE_0__["default"].observer.subscribeToQuerySelector(callback, selector, null, true); + setTimeout(callback, 0); + } + + let listener = this.listeners.get(name); + if (!listener) { + listener = { + id: name, + children: [], + filter + }; + this.listeners.set(name, listener); + } + + + return new Promise(resolve => { + listener.children.push(resolve); + }); + } + + static setName(name, filter) { + const have = this.components.get(name); + if (have) return have; + + for (const component of this.unknownComponents.entries()) { + if (!filter(component)) continue; + component.displayName = name; + this.unknownComponents.delete(component); + return this.push(component); + } + return this.nameSetters.add({name, filter}); + } + + static processUnknown(component) { + const have = this.unknownComponents.has(component); + for (const setter of this.nameSetters.entries()) { + if (setter.filter.filter(component)) { + component.displayName = setter.name; + this.nameSetters.delete(setter); + return this.push(component); + } + } + if (have) return have; + this.unknownComponents.add(component); + return component; + } + + static *recursiveComponents(internalInstance = _reacttools__WEBPACK_IMPORTED_MODULE_1__["default"].rootInstance) { + if (internalInstance.stateNode) yield internalInstance.stateNode; + if (internalInstance.sibling) yield* this.recursiveComponents(internalInstance.sibling); + if (internalInstance.child) yield* this.recursiveComponents(internalInstance.child); + } +} + + +/***/ }), + +/***/ "./src/modules/reacttools.js": +/*!***********************************!*\ + !*** ./src/modules/reacttools.js ***! + \***********************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "default": () => (/* binding */ ReactTools) +/* harmony export */ }); +/* harmony import */ var _domtools__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./domtools */ "./src/modules/domtools.js"); +/* harmony import */ var _discordmodules__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./discordmodules */ "./src/modules/discordmodules.js"); +/* harmony import */ var _utilities__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./utilities */ "./src/modules/utilities.js"); +/** + * Helpful utilities for dealing with getting react information from DOM objects. + * @module ReactTools + */ + + + + + +class ReactTools { + + static get rootInstance() {return document.getElementById("app-mount")._reactRootContainer._internalRoot.current;} + + /** + * Grabs the react internal instance of a specific node. + * @param {(HTMLElement|jQuery)} node - node to obtain react instance of + * @return {object} the internal react instance + */ + static getReactInstance(node) { + const domNode = _domtools__WEBPACK_IMPORTED_MODULE_0__["default"].resolveElement(node); + if (!(domNode instanceof Element)) return undefined; + return domNode[Object.keys(domNode).find((key) => key.startsWith("__reactInternalInstance") || key.startsWith("__reactFiber"))]; + } + + /** + * Grabs a value from the react internal instance. Allows you to grab + * long depth values safely without accessing no longer valid properties. + * @param {(HTMLElement|jQuery)} node - node to obtain react instance of + * @param {string} path - path to the requested value + * @return {(*|undefined)} the value requested or undefined if not found. + */ + static getReactProperty(node, path) { + return _utilities__WEBPACK_IMPORTED_MODULE_2__["default"].getNestedProp(this.getReactInstance(node), path); + } + + /** + * Grabs a value from the react internal instance. Allows you to grab + * long depth values safely without accessing no longer valid properties. + * @param {(HTMLElement|jQuery)} node - node to obtain react instance of + * @param {object} options - options for the search + * @param {array} [options.include] - list of items to include from the search + * @param {array} [options.exclude=["Popout", "Tooltip", "Scroller", "BackgroundFlash"]] - list of items to exclude from the search + * @param {callable} [options.filter=_=>_] - filter to check the current instance with (should return a boolean) + * @return {(*|null)} the owner instance or undefined if not found. + */ + static getOwnerInstance(node, {include, exclude = ["Popout", "Tooltip", "Scroller", "BackgroundFlash"], filter = _ => _} = {}) { + if (node === undefined) return undefined; + const excluding = include === undefined; + const nameFilter = excluding ? exclude : include; + function getDisplayName(owner) { + const type = owner.type; + if (!type) return null; + return type.displayName || type.name || null; + } + function classFilter(owner) { + const name = getDisplayName(owner); + return (name !== null && !!(nameFilter.includes(name) ^ excluding)); + } + + let curr = this.getReactInstance(node); + for (curr = curr && curr.return; !_utilities__WEBPACK_IMPORTED_MODULE_2__["default"].isNil(curr); curr = curr.return) { + if (_utilities__WEBPACK_IMPORTED_MODULE_2__["default"].isNil(curr)) continue; + const owner = curr.stateNode; + if (!_utilities__WEBPACK_IMPORTED_MODULE_2__["default"].isNil(owner) && !(owner instanceof HTMLElement) && classFilter(curr) && filter(owner)) return owner; + } + + return null; + } + + /** + * Grabs the react internal state node trees of a specific node. + * @param {(HTMLElement|jQuery)} node - node to obtain state nodes of + * @return {Array} list of found state nodes + */ + static getStateNodes(node) { + const instance = this.getReactInstance(node); + const stateNodes = []; + let lastInstance = instance; + while (lastInstance && lastInstance.return) { + if (lastInstance.return.stateNode instanceof HTMLElement) break; + if (lastInstance.return.stateNode) stateNodes.push(lastInstance.return.stateNode); + lastInstance = lastInstance.return; + } + return stateNodes; + } + + /** + * Grabs the react internal component tree of a specific node. + * @param {(HTMLElement|jQuery)} node - node to obtain react components of + * @return {Array} list of found react components + */ + static getComponents(node) { + const instance = this.getReactInstance(node); + const components = []; + let lastInstance = instance; + while (lastInstance && lastInstance.return) { + if (typeof lastInstance.return.type === "string") break; + if (lastInstance.return.type) components.push(lastInstance.return.type); + lastInstance = lastInstance.return; + } + return components; + } + + /** + * Creates and renders a react element that wraps dom elements. + * @param {(HTMLElement|Array)} element - element or array of elements to wrap into a react element + * @returns {object} - rendered react element + */ + static createWrappedElement(element) { + if (Array.isArray(element)) element = _domtools__WEBPACK_IMPORTED_MODULE_0__["default"].wrap(element); + return _discordmodules__WEBPACK_IMPORTED_MODULE_1__["default"].React.createElement(this.wrapElement(element)); + } + + /** + * Creates an unrendered react component that wraps dom elements. + * @param {(HTMLElement|Array)} element - element or array of elements to wrap into a react component + * @returns {object} - unrendered react component + */ + static wrapElement(element) { + if (Array.isArray(element)) element = _domtools__WEBPACK_IMPORTED_MODULE_0__["default"].wrap(element); + return class ReactWrapper extends _discordmodules__WEBPACK_IMPORTED_MODULE_1__["default"].React.Component { + constructor(props) { + super(props); + this.element = element; + } + + componentDidMount() {this.refs.element.appendChild(this.element);} + render() {return _discordmodules__WEBPACK_IMPORTED_MODULE_1__["default"].React.createElement("div", {className: "react-wrapper", ref: "element"});} + }; + } +} + +/***/ }), + +/***/ "./src/modules/utilities.js": +/*!**********************************!*\ + !*** ./src/modules/utilities.js ***! + \**********************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "default": () => (/* binding */ Utilities) +/* harmony export */ }); +/* harmony import */ var _logger__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./logger */ "./src/modules/logger.js"); +/** + * Random set of utilities that didn't fit elsewhere. + * @module Utilities + */ + + + +class Utilities { + + /** + * Stably sorts arrays since `.sort()` has issues. + * @param {Array} list - array to sort + * @param {function} comparator - comparator to sort by + */ + static stableSort(list, comparator) { + const entries = Array(list.length); + + // wrap values with initial indices + for (let index = 0; index < list.length; index++) { + entries[index] = [index, list[index]]; + } + + // sort with fallback based on initial indices + entries.sort(function (a, b) { + const comparison = Number(this(a[1], b[1])); + return comparison || a[0] - b[0]; + }.bind(comparator)); + + // re-map original array to stable sorted values + for (let index = 0; index < list.length; index++) { + list[index] = entries[index][1]; + } + } + + /** + * Generates an automatically memoizing version of an object. + * @param {Object} object - object to memoize + * @returns {Proxy} the proxy to the object that memoizes properties + */ + static memoizeObject(object) { + const proxy = new Proxy(object, { + get: function(obj, mod) { + if (!obj.hasOwnProperty(mod)) return undefined; + if (Object.getOwnPropertyDescriptor(obj, mod).get) { + const value = obj[mod]; + delete obj[mod]; + obj[mod] = value; + } + return obj[mod]; + }, + set: function(obj, mod, value) { + if (obj.hasOwnProperty(mod)) return _logger__WEBPACK_IMPORTED_MODULE_0__["default"].err("MemoizedObject", "Trying to overwrite existing property"); + obj[mod] = value; + return obj[mod]; + } + }); + + Object.defineProperty(proxy, "hasOwnProperty", {value: function(prop) { + return this[prop] !== undefined; + }}); + + return proxy; + } + + /** + * Wraps the method in a `try..catch` block. + * @param {callable} method - method to wrap + * @param {string} description - description of method + * @returns {callable} wrapped version of method + */ + static suppressErrors(method, description) { + return (...params) => { + try {return method(...params);} + catch (e) {_logger__WEBPACK_IMPORTED_MODULE_0__["default"].err("Suppression", "Error occurred in " + description, e);} + }; + } + + /** + * This only exists because Samo relied on lodash being there... fuck lodash. + * @param {*} anything - whatever you want + */ + static isNil(anything) { + return anything === null; + } + + /** + * Format template strings with placeholders (`${placeholder}`) into full strings. + * Quick example: `Utilities.formatString("Hello, ${user}", {user: "Zerebos"})` + * would return "Hello, Zerebos". + * @param {string} string - string to format + * @param {object} values - object literal of placeholders to replacements + * @returns {string} the properly formatted string + */ + static formatTString(string, values) { + for (const val in values) { + let replacement = values[val]; + if (Array.isArray(replacement)) replacement = JSON.stringify(replacement); + if (typeof(replacement) === "object" && replacement !== null) replacement = replacement.toString(); + string = string.replace(new RegExp(`\\$\\{${val}\\}`, "g"), replacement); + } + return string; + } + + /** + * Format strings with placeholders (`{{placeholder}}`) into full strings. + * Quick example: `Utilities.formatString("Hello, {{user}}", {user: "Zerebos"})` + * would return "Hello, Zerebos". + * @param {string} string - string to format + * @param {object} values - object literal of placeholders to replacements + * @returns {string} the properly formatted string + */ + static formatString(string, values) { + for (const val in values) { + let replacement = values[val]; + if (Array.isArray(replacement)) replacement = JSON.stringify(replacement); + if (typeof(replacement) === "object" && replacement !== null) replacement = replacement.toString(); + string = string.replace(new RegExp(`{{${val}}}`, "g"), replacement); + } + return string; + } + + /** + * Finds a value, subobject, or array from a tree that matches a specific filter. Great for patching render functions. + * @param {object} tree React tree to look through. Can be a rendered object or an internal instance. + * @param {callable} searchFilter Filter function to check subobjects against. + */ + static findInReactTree(tree, searchFilter) { + return this.findInTree(tree, searchFilter, {walkable: ["props", "children", "child", "sibling"]}); + } + + /** + * Finds a value, subobject, or array from a tree that matches a specific filter. + * @param {object} tree Tree that should be walked + * @param {callable} searchFilter Filter to check against each object and subobject + * @param {object} options Additional options to customize the search + * @param {Array|null} [options.walkable=null] Array of strings to use as keys that are allowed to be walked on. Null value indicates all keys are walkable + * @param {Array} [options.ignore=[]] Array of strings to use as keys to exclude from the search, most helpful when `walkable = null`. + */ + static findInTree(tree, searchFilter, {walkable = null, ignore = []} = {}) { + if (typeof searchFilter === "string") { + if (tree.hasOwnProperty(searchFilter)) return tree[searchFilter]; + } + else if (searchFilter(tree)) { + return tree; + } + + if (typeof tree !== "object" || tree == null) return undefined; + + let tempReturn; + if (Array.isArray(tree)) { + for (const value of tree) { + tempReturn = this.findInTree(value, searchFilter, {walkable, ignore}); + if (typeof tempReturn != "undefined") return tempReturn; + } + } + else { + const toWalk = walkable == null ? Object.keys(tree) : walkable; + for (const key of toWalk) { + if (!tree.hasOwnProperty(key) || ignore.includes(key)) continue; + tempReturn = this.findInTree(tree[key], searchFilter, {walkable, ignore}); + if (typeof tempReturn != "undefined") return tempReturn; + } + } + return tempReturn; + } + + /** + * Gets a nested property (if it exists) safely. Path should be something like `prop.prop2.prop3`. + * Numbers can be used for arrays as well like `prop.prop2.array.0.id`. + * @param {Object} obj - object to get nested property of + * @param {string} path - representation of the property to obtain + */ + static getNestedProp(obj, path) { + return path.split(".").reduce(function(ob, prop) { + return ob && ob[prop]; + }, obj); + } + + /** + * Builds a classname string from any number of arguments. This includes arrays and objects. + * When given an array all values from the array are added to the list. + * When given an object they keys are added as the classnames if the value is truthy. + * Copyright (c) 2018 Jed Watson https://github.com/JedWatson/classnames MIT License + * @param {...Any} argument - anything that should be used to add classnames. + */ + static className() { + const classes = []; + const hasOwn = {}.hasOwnProperty; + + for (let i = 0; i < arguments.length; i++) { + const arg = arguments[i]; + if (!arg) continue; + + const argType = typeof arg; + + if (argType === "string" || argType === "number") { + classes.push(arg); + } + else if (Array.isArray(arg) && arg.length) { + const inner = this.classNames.apply(null, arg); + if (inner) { + classes.push(inner); + } + } + else if (argType === "object") { + for (const key in arg) { + if (hasOwn.call(arg, key) && arg[key]) { + classes.push(key); + } + } + } + } + + return classes.join(" "); + } + + /** + * Safely adds to the prototype of an existing object by checking if the + * property exists on the prototype. + * @param {object} object - Object whose prototype to extend + * @param {string} prop - Name of the prototype property to add + * @param {callable} func - Function to run + */ + static addToPrototype(object, prop, func) { + if (!object.prototype) return; + if (object.prototype[prop]) return; + return object.prototype[prop] = func; + } + + /** + * Deep extends an object with a set of other objects. Objects later in the list + * of `extenders` have priority, that is to say if one sets a key to be a primitive, + * it will be overwritten with the next one with the same key. If it is an object, + * and the keys match, the object is extended. This happens recursively. + * @param {object} extendee - Object to be extended + * @param {...object} extenders - Objects to extend with + * @returns {object} - A reference to `extendee` + */ + static extend(extendee, ...extenders) { + for (let i = 0; i < extenders.length; i++) { + for (const key in extenders[i]) { + if (extenders[i].hasOwnProperty(key)) { + if (Array.isArray(extendee[key]) && Array.isArray(extenders[i][key])) this.extend(extendee[key], extenders[i][key]); + else if (this.isNil(extenders[i][key])) extendee[key] = extenders[i][key]; + else if (typeof extendee[key] === "object" && typeof extenders[i][key] === "object") this.extend(extendee[key], extenders[i][key]); + else if (Array.isArray(extenders[i][key])) extendee[key] = [], this.extend(extendee[key], extenders[i][key]); // eslint-disable-line no-sequences + else if (typeof extenders[i][key] === "object") extendee[key] = {}, this.extend(extendee[key], extenders[i][key]); // eslint-disable-line no-sequences + else extendee[key] = extenders[i][key]; + } + } + } + return extendee; + } + + /* Code below comes from our work on BDv2: + * https://github.com/JsSucks/BetterDiscordApp/blob/master/common/modules/utils.js + */ + + /** + * Clones an object and all it's properties. + * @param {Any} value The value to clone + * @return {Any} The cloned value + */ + static deepclone(value) { + if (this.isNil(value)) return value; + if (typeof value === "object") { + if (Array.isArray(value)) return value.map(i => this.deepclone(i)); + + const clone = Object.assign({}, value); + + for (const key in clone) { + clone[key] = this.deepclone(clone[key]); + } + + return clone; + } + + return value; + } + + /** + * Freezes an object and all it's properties. + * @param {Any} object The object to freeze + * @param {Function} exclude A function to filter object that shouldn't be frozen + */ + static deepfreeze(object, exclude) { + if (exclude && exclude(object)) return; + + if (typeof object === "object" && object !== null) { + const properties = Object.getOwnPropertyNames(object); + + for (const property of properties) { + this.deepfreeze(object[property], exclude); + } + + Object.freeze(object); + } + + return object; + } + + /** + * Removes an item from an array. This differs from Array.prototype.filter as it mutates the original array instead of creating a new one. + * @param {Array} array The array to filter + * @param {Any} item The item to remove from the array + * @return {Array} + */ + static removeFromArray(array, item, filter) { + let index; + while ((index = filter ? array.findIndex(item) : array.indexOf(item)) > -1) array.splice(index, 1); + return array; + } + + /** + * Returns a function, that, as long as it continues to be invoked, will not + * be triggered. The function will be called after it stops being called for + * N milliseconds. + * + * Adapted from the version by David Walsh (https://davidwalsh.name/javascript-debounce-function) + * + * @param {function} executor + * @param {number} delay + */ + static debounce(executor, delay) { + let timeout; + return function(...args) { + const callback = () => { + timeout = null; + Reflect.apply(executor, null, args); + }; + clearTimeout(timeout); + timeout = setTimeout(callback, delay); + }; + } + + /** + * Loads data through BetterDiscord's API. + * @param {string} name - name for the file (usually plugin name) + * @param {string} key - which key the data is saved under + * @param {object} defaultData - default data to populate the object with + * @returns {object} the combined saved and default data + */ + static loadData(name, key, defaultData = {}) { + const defaults = this.deepclone(defaultData); + try { + const storedData = BdApi.getData(name, key); + if (typeof(defaults) === "object") return this.extend(defaults, storedData); + return this.isNil(storedData) || typeof(storedData) === "undefined" ? defaults : storedData; + } + catch (err) { + _logger__WEBPACK_IMPORTED_MODULE_0__["default"].err(name, "Unable to load data: ", err); + } + return defaults; + } + + /** + * Saves data through BetterDiscord's API. + * @param {string} name - name for the file (usually plugin name) + * @param {string} key - which key the data should be saved under + * @param {object} data - data to save + */ + static saveData(name, key, data) { + try {BdApi.setData(name, key, data);} + catch (err) {_logger__WEBPACK_IMPORTED_MODULE_0__["default"].err(name, "Unable to save data: ", err);} + } + + /** + * Loads settings through BetterDiscord's API. + * @param {string} name - name for the file (usually plugin name) + * @param {object} defaultData - default data to populate the object with + * @returns {object} the combined saved and default settings + */ + static loadSettings(name, defaultSettings) { + return this.loadData(name, "settings", defaultSettings); + } + + /** + * Saves settings through BetterDiscord's API. + * @param {string} name - name for the file (usually plugin name) + * @param {object} data - settings to save + */ + static saveSettings(name, data) { + this.saveData(name, "settings", data); + } + +} + +/***/ }), + +/***/ "./src/modules/webpackmodules.js": +/*!***************************************!*\ + !*** ./src/modules/webpackmodules.js ***! + \***************************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ Filters: () => (/* binding */ Filters), +/* harmony export */ "default": () => (/* binding */ WebpackModules) +/* harmony export */ }); +/* harmony import */ var _discordmodules__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./discordmodules */ "./src/modules/discordmodules.js"); +/* harmony import */ var _logger__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./logger */ "./src/modules/logger.js"); +/** + * Random set of utilities that didn't fit elsewhere. + * @module WebpackModules + */ + + + + /** + * Checks if a given module matches a set of parameters. + * @callback module:WebpackModules.Filters~filter + * @param {*} module - module to check + * @returns {boolean} - True if the module matches the filter, false otherwise + */ + +/** + * Filters for use with {@link module:WebpackModules} but may prove useful elsewhere. + */ +class Filters { + /** + * Generates a {@link module:WebpackModules.Filters~filter} that filters by a set of properties. + * @param {Array} props - Array of property names + * @param {module:WebpackModules.Filters~filter} filter - Additional filter + * @returns {module:WebpackModules.Filters~filter} - A filter that checks for a set of properties + */ + static byProperties(props, filter = m => m) { + return module => { + const component = filter(module); + if (!component) return false; + for (let p = 0; p < props.length; p++) { + if (module[props[p]] === undefined) return false; + } + return true; + }; + } + + /** + * Generates a {@link module:WebpackModules.Filters~filter} that filters by a set of properties on the object's prototype. + * @param {Array} fields - Array of property names + * @param {module:WebpackModules.Filters~filter} filter - Additional filter + * @returns {module:WebpackModules.Filters~filter} - A filter that checks for a set of properties on the object's prototype + */ + static byPrototypeFields(fields, filter = m => m) { + return module => { + const component = filter(module); + if (!component) return false; + if (!component.prototype) return false; + for (let f = 0; f < fields.length; f++) { + if (module.prototype[fields[f]] === undefined) return false; + } + return true; + }; + } + + /** + * Generates a {@link module:WebpackModules.Filters~filter} that filters by a regex. + * @param {RegExp} search - A RegExp to check on the module + * @param {module:WebpackModules.Filters~filter} filter - Additional filter + * @returns {module:WebpackModules.Filters~filter} - A filter that checks for a set of properties + */ + static byCode(search, filter = m => m) { + return module => { + const method = filter(module); + if (!method) return false; + let methodString = ""; + try {methodString = method.toString([]);} + catch (err) {methodString = method.toString();} + return methodString.search(search) !== -1; + }; + } + + /** + * Generates a {@link module:WebpackModules.Filters~filter} that filters by strings. + * @param {...String} search - A RegExp to check on the module + * @returns {module:WebpackModules.Filters~filter} - A filter that checks for a set of strings + */ + static byString(...strings) { + return module => { + let moduleString = ""; + try {moduleString = module.toString([]);} + catch (err) {moduleString = module.toString();} + for (const s of strings) { + if (!moduleString.includes(s)) return false; + } + return true; + }; + } + + /** + * Generates a {@link module:WebpackModules.Filters~filter} that filters by a set of properties. + * @param {string} name - Name the module should have + * @param {module:WebpackModules.Filters~filter} filter - Additional filter + * @returns {module:WebpackModules.Filters~filter} - A filter that checks for a set of properties + */ + static byDisplayName(name) { + return module => { + return module && module.displayName === name; + }; + } + + /** + * Generates a combined {@link module:WebpackModules.Filters~filter} from a list of filters. + * @param {...module:WebpackModules.Filters~filter} filters - A list of filters + * @returns {module:WebpackModules.Filters~filter} - Combinatory filter of all arguments + */ + static combine(...filters) { + return module => { + return filters.every(filter => filter(module)); + }; + } +} + +class WebpackModules { + + static find(filter, first = true) {return this.getModule(filter, first);} + static findAll(filter) {return this.getModule(filter, false);} + static findByUniqueProperties(props, first = true) {return first ? this.getByProps(...props) : this.getAllByProps(...props);} + static findByDisplayName(name) {return this.getByDisplayName(name);} + + /** + * Finds a module using a filter function. + * @param {Function} filter A function to use to filter modules + * @param {Boolean|object} first Whether to return only the first matching module or options object matching BD's options + * @return {Any} + */ + static getModule(filter, first = true) { + const options = typeof(first) === "object" ? first : {first}; + return BdApi.Webpack.getModule(filter, options); + } + + static getIndex() { + return null; + } + + static getIndexByModule() { + return null; + } + + /** + * Finds all modules matching a filter function. + * @param {Function} filter A function to use to filter modules + */ + static getModules(filter) {return this.getModule(filter, false);} + + /** + * Finds a module by its name. + * @param {String} name The name of the module + * @param {Function} fallback A function to use to filter modules if not finding a known module + * @return {Any} + */ + static getModuleByName(name, fallback) { + if (_discordmodules__WEBPACK_IMPORTED_MODULE_0__["default"].hasOwnProperty(name)) return _discordmodules__WEBPACK_IMPORTED_MODULE_0__["default"][name]; + if (!fallback) return undefined; + const module = this.getModule(fallback); + return module ? _discordmodules__WEBPACK_IMPORTED_MODULE_0__["default"][name] = module : undefined; + } + + /** + * Finds a module by its display name. + * @param {String} name The display name of the module + * @return {Any} + */ + static getByDisplayName(name) { + return this.getModule(Filters.byDisplayName(name)); + } + + /** + * Finds a module using its code. + * @param {RegEx} regex A regular expression to use to filter modules + * @param {Boolean} first Whether to return the only the first matching module + * @return {Any} + */ + static getByRegex(regex, first = true) { + return this.getModule(Filters.byCode(regex), first); + } + + /** + * Finds a single module using properties on its prototype. + * @param {...string} prototypes Properties to use to filter modules + * @return {Any} + */ + static getByPrototypes(...prototypes) { + return this.getModule(Filters.byPrototypeFields(prototypes), true); + } + + /** + * Finds all modules with a set of properties of its prototype. + * @param {...string} prototypes Properties to use to filter modules + * @return {Any} + */ + static getAllByPrototypes(...prototypes) { + return this.getModule(Filters.byPrototypeFields(prototypes), false); + } + + /** + * Finds a single module using its own properties. + * @param {...string} props Properties to use to filter modules + * @return {Any} + */ + static getByProps(...props) { + return this.getModule(Filters.byProperties(props), true); + } + + /** + * Finds all modules with a set of properties. + * @param {...string} props Properties to use to filter modules + * @return {Any} + */ + static getAllByProps(...props) { + return this.getModule(Filters.byProperties(props), false); + } + + /** + * Finds a single module using a set of strings. + * @param {...String} props Strings to use to filter modules + * @return {Any} + */ + static getByString(...strings) { + return this.getModule(Filters.byString(...strings), true); + } + + /** + * Finds all modules with a set of strings. + * @param {...String} strings Strings to use to filter modules + * @return {Any} + */ + static getAllByString(...strings) { + return this.getModule(Filters.byString(...strings), false); + } + + /** + * Gets a specific module by index of the webpack require cache. + * Best used in combination with getIndex in order to patch a + * specific function. + * + * Note: this gives the **raw** module, meaning the actual module + * is in returnValue.exports. This is done in order to be able + * to patch modules which export a single function directly. + * @param {Number} index Index into the webpack require cache + * @return {Any} + */ + static getByIndex(index) { + return WebpackModules.require.c[index].exports; + } + + /** + * Discord's __webpack_require__ function. + */ + static get require() { + if (this._require) return this._require; + const __nested_webpack_require_9242__ = window.webpackChunkdiscord_app.push([[Symbol()], {}, r=> r]); + window.webpackChunkdiscord_app.pop(); + return this._require = __nested_webpack_require_9242__; + } + + /** + * Returns all loaded modules. + * @return {Array} + */ + static getAllModules() { + return this.require.c; + } + + + + // Webpack Chunk Observing + static get chunkName() {return "webpackChunkdiscord_app";} + + static initialize() { + this.handlePush = this.handlePush.bind(this); + this.listeners = new Set(); + + this.__ORIGINAL_PUSH__ = window[this.chunkName].push; + Object.defineProperty(window[this.chunkName], "push", { + configurable: true, + get: () => this.handlePush, + set: (newPush) => { + this.__ORIGINAL_PUSH__ = newPush; + + Object.defineProperty(window[this.chunkName], "push", { + value: this.handlePush, + configurable: true, + writable: true + }); + } + }); + } + + /** + * Adds a listener for when discord loaded a chunk. Useful for subscribing to lazy loaded modules. + * @param {Function} listener - Function to subscribe for chunks + * @returns {Function} A cancelling function + */ + static addListener(listener) { + this.listeners.add(listener); + return this.removeListener.bind(this, listener); + } + + /** + * Removes a listener for when discord loaded a chunk. + * @param {Function} listener + * @returns {boolean} + */ + static removeListener(listener) {return this.listeners.delete(listener);} + + static handlePush(chunk) { + const [, modules] = chunk; + + for (const moduleId in modules) { + const originalModule = modules[moduleId]; + + modules[moduleId] = (module, exports, require) => { + try { + Reflect.apply(originalModule, null, [module, exports, require]); + + const listeners = [...this.listeners]; + for (let i = 0; i < listeners.length; i++) { + try {listeners[i](exports, originalModule, moduleId);} + catch (error) { + _logger__WEBPACK_IMPORTED_MODULE_1__["default"].err("WebpackModules", "Could not fire callback listener:", error); + } + } + } + catch (error) { + _logger__WEBPACK_IMPORTED_MODULE_1__["default"].stacktrace("WebpackModules", "Error patching chunked module push", error); + } + }; + + Object.assign(modules[moduleId], originalModule, { + toString: () => originalModule.toString() + }); + } + + return Reflect.apply(this.__ORIGINAL_PUSH__, window[this.chunkName], [chunk]); + } + +} + +WebpackModules.initialize(); + +/***/ }), + +/***/ "./src/structs/dom/classname.js": +/*!**************************************!*\ + !*** ./src/structs/dom/classname.js ***! + \**************************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__) +/* harmony export */ }); +/* harmony import */ var _selector__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./selector */ "./src/structs/dom/selector.js"); + + +/** + * Representation of a Class Name + * @memberof module:DOMTools + **/ +class ClassName { + /** + * + * @param {string} name - name of the class to represent + */ + constructor(name) { + this.value = name; + } + + /** + * Concatenates new class names to the current one using spaces. + * @param {string} classNames - list of class names to add to this class name + * @returns {ClassName} returns self to allow chaining + */ + add(...classNames) { + for (let i = 0; i < classNames.length; i++) this.value += " " + classNames[i]; + return this; + } + + /** + * Returns the raw class name, this is how native function get the value. + * @returns {string} raw class name. + */ + toString() { + return this.value; + } + + /** + * Returns the raw class name, this is how native function get the value. + * @returns {string} raw class name. + */ + valueOf() { + return this.value; + } + + /** + * Returns the classname represented as {@link module:DOMTools.Selector}. + * @returns {Selector} selector representation of this class name. + */ + get selector() { + return new _selector__WEBPACK_IMPORTED_MODULE_0__["default"](this.value); + } + + get single() { + return this.value.split(" ")[0]; + } + + get first() { + return this.value.split(" ")[0]; + } +} + +/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (ClassName); + +/***/ }), + +/***/ "./src/structs/dom/observer.js": +/*!*************************************!*\ + !*** ./src/structs/dom/observer.js ***! + \*************************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__) +/* harmony export */ }); +/* harmony import */ var modules__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! modules */ "./src/modules/modules.js"); +/** + * BetterDiscord Client DOM Module + * Copyright (c) 2015-present JsSucks - https://github.com/JsSucks + * All rights reserved. + * https://betterdiscord.net + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. +*/ + + + +/* eslint-disable operator-linebreak */ + +/** + * Representation of a MutationObserver but with helpful utilities. + * @memberof module:DOMTools + **/ +class DOMObserver { + constructor(root, options) { + this.observe = this.observe.bind(this); + this.subscribe = this.subscribe.bind(this); + this.observerCallback = this.observerCallback.bind(this); + + this.active = false; + this.root = root || document.getElementById("app-mount"); + this.options = options || {attributes: true, childList: true, subtree: true}; + + this.observer = new MutationObserver(this.observerCallback); + this.observe(); + } + + observerCallback(mutations) { + for (const sub of Array.from(this.subscriptions)) { + try { + const filteredMutations = sub.filter ? mutations.filter(sub.filter) : mutations; + + if (sub.group) { + if (!filteredMutations.length) continue; + sub.callback.call(sub.bind || sub, filteredMutations); + } + else { + for (const mutation of filteredMutations) sub.callback.call(sub.bind || sub, mutation); + } + } + catch (err) { + modules__WEBPACK_IMPORTED_MODULE_0__.Logger.stacktrace("DOMObserver", "Error in observer callback", err); + } + } + } + + /** + * Starts observing the element. This will be called when attaching a callback. + * You don't need to call this manually. + */ + observe() { + if (this.active) return; + this.observer.observe(this.root, this.options); + this.active = true; + } + + /** + * Disconnects this observer. This stops callbacks being called, but does not unbind them. + * You probably want to use observer.unsubscribeAll instead. + */ + disconnect() { + if (!this.active) return; + this.observer.disconnect(); + this.active = false; + } + + reconnect() { + if (this.active) { + this.disconnect(); + this.observe(); + } + } + + get root() {return this._root;} + set root(root) {this._root = root; this.reconnect();} + + get options() {return this._options;} + set options(options) {this._options = options; this.reconnect();} + + get subscriptions() { + return this._subscriptions || (this._subscriptions = []); + } + + /** + * Subscribes to mutations. + * @param {Function} callback A function to call when on a mutation + * @param {Function} filter A function to call to filter mutations + * @param {Any} bind Something to bind the callback to + * @param {Boolean} group Whether to call the callback with an array of mutations instead of a single mutation + * @return {Object} + */ + subscribe(callback, filter, bind, group) { + const subscription = {callback, filter, bind, group}; + this.subscriptions.push(subscription); + this.observe(); + return subscription; + } + + /** + * Removes a subscription and disconnect if there are none left. + * @param {Object} subscription A subscription object returned by observer.subscribe + */ + unsubscribe(subscription) { + if (!this.subscriptions.includes(subscription)) subscription = this.subscriptions.find(s => s.callback === subscription); + modules__WEBPACK_IMPORTED_MODULE_0__.Utilities.removeFromArray(this.subscriptions, subscription); + if (!this.subscriptions.length) this.disconnect(); + } + + unsubscribeAll() { + this.subscriptions.splice(0, this.subscriptions.length); + this.disconnect(); + } + + /** + * Subscribes to mutations that affect an element matching a selector. + * @param {Function} callback A function to call when on a mutation + * @param {Function} filter A function to call to filter mutations + * @param {Any} bind Something to bind the callback to + * @param {Boolean} group Whether to call the callback with an array of mutations instead of a single mutation + * @return {Object} + */ + subscribeToQuerySelector(callback, selector, bind, group) { + return this.subscribe(callback, mutation => { + return mutation.target.matches(selector) // If the target matches the selector + || Array.from(mutation.addedNodes).concat(Array.from(mutation.removedNodes)) // Or if either an added or removed node + .find(n => n instanceof Element && (n.matches(selector) || n.querySelector(selector))); // match or contain an element matching the selector + }, bind, group); + } +} + +/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (DOMObserver); + +/***/ }), + +/***/ "./src/structs/dom/selector.js": +/*!*************************************!*\ + !*** ./src/structs/dom/selector.js ***! + \*************************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__) +/* harmony export */ }); +/** + * Representation of a Selector + * @memberof module:DOMTools + **/ +class Selector { + /** + * + * @param {string} classname - class to create selector for + */ + constructor(className) { + this.value = " ." + className.split(" ").join("."); + } + + /** + * Returns the raw selector, this is how native function get the value. + * @returns {string} raw selector. + */ + toString() { + return this.value; + } + + /** + * Returns the raw selector, this is how native function get the value. + * @returns {string} raw selector. + */ + valueOf() { + return this.value; + } + + selector(symbol, other) { + this.value = `${this.toString()} ${symbol} ${other.toString()}`; + return this; + } + + /** + * Adds another selector as a direct child `>` to this one. + * @param {string|DOMTools.Selector} other - Selector to add as child + * @returns {DOMTools.Selector} returns self to allow chaining + */ + child(other) { + return this.selector(">", other); + } + + /** + * Adds another selector as a adjacent sibling `+` to this one. + * @param {string|DOMTools.Selector} other - Selector to add as adjacent sibling + * @returns {DOMTools.Selector} returns self to allow chaining + */ + adjacent(other) { + return this.selector("+", other); + } + + /** + * Adds another selector as a general sibling `~` to this one. + * @param {string|DOMTools.Selector} other - Selector to add as sibling + * @returns {DOMTools.Selector} returns self to allow chaining + */ + sibling(other) { + return this.selector("~", other); + } + + /** + * Adds another selector as a descendent `(space)` to this one. + * @param {string|DOMTools.Selector} other - Selector to add as descendent + * @returns {DOMTools.Selector} returns self to allow chaining + */ + descend(other) { + return this.selector(" ", other); + } + + /** + * Adds another selector to this one via `,`. + * @param {string|DOMTools.Selector} other - Selector to add + * @returns {DOMTools.Selector} returns self to allow chaining + */ + and(other) { + return this.selector(",", other); + } +} + +/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (Selector); + +/***/ }), + +/***/ "./src/structs/listenable.js": +/*!***********************************!*\ + !*** ./src/structs/listenable.js ***! + \***********************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__) +/* harmony export */ }); +/** + * Acts as an interface for anything that should be listenable. + */ +class Listenable { + + constructor() { + this.listeners = []; + } + + /** + * Adds a listener to the current object. + * @param {callable} callback - callback for when the event occurs + * @returns {callable} - a way to cancel the listener without needing to call `removeListener` + */ + addListener(callback) { + if (typeof(callback) !== "function") return; + this.listeners.push(callback); + return () => { + this.listeners.splice(this.listeners.indexOf(callback), 1); + }; + } + + /** + * Removes a listener from the current object. + * @param {callable} callback - callback that was originally registered + */ + removeListener(callback) { + if (typeof(callback) !== "function") return; + this.listeners.splice(this.listeners.indexOf(callback), 1); + } + + /** + * Alerts the listeners that an event occurred. Data passed is optional + * @param {*} [...data] - Any data desired to be passed to listeners + */ + alertListeners(...data) { + for (let l = 0; l < this.listeners.length; l++) this.listeners[l](...data); + } +} + +/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (Listenable); + +/***/ }), + +/***/ "./src/structs/plugin.js": +/*!*******************************!*\ + !*** ./src/structs/plugin.js ***! + \*******************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "default": () => (/* binding */ Plugin), +/* harmony export */ wrapPluginBase: () => (/* binding */ wrapPluginBase) +/* harmony export */ }); +/* harmony import */ var _modules_pluginupdater__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../modules/pluginupdater */ "./src/modules/pluginupdater.js"); +/* harmony import */ var _modules_logger__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../modules/logger */ "./src/modules/logger.js"); +/* harmony import */ var _modules_reacttools__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../modules/reacttools */ "./src/modules/reacttools.js"); +/* harmony import */ var _ui_modals__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../ui/modals */ "./src/ui/modals.js"); +/* harmony import */ var _modules_utilities__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../modules/utilities */ "./src/modules/utilities.js"); +/* harmony import */ var _modules_discordmodules__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../modules/discordmodules */ "./src/modules/discordmodules.js"); +/* harmony import */ var _ui_settings__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ../ui/settings */ "./src/ui/settings/index.js"); + + + + + + + + +class Plugin { + + get name() {return this._config.name ?? this._config.info.name.replace(" ", "");} + get description() {return this._config.description ?? this._config.info.description;} + get version() {return this._config.version ?? this._config.info.version;} + get author() {return this._config.author ?? this._config.info.authors.map(a => a.name).join(", ");} + getName() {return this._config.name ?? this._config.info.name.replace(" ", "");} + getDescription() {return this._config.description ?? this._config.info.description;} + getVersion() {return this._config.version ?? this._config.info.version;} + getAuthor() {return this._config.author ?? this._config.info.authors.map(a => a.name).join(", ");} + get isEnabled() {return this._enabled;} + + get strings() { + if (!this._config.strings) return {}; + const locale = _modules_discordmodules__WEBPACK_IMPORTED_MODULE_5__["default"].LocaleManager?.getLocale().split("-")[0] ?? "en"; + if (this._config.strings.hasOwnProperty(locale)) return this._config.strings[locale]; + if (this._config.strings.hasOwnProperty("en")) return this._config.strings.en; + return this._config.strings; + } + + set strings(strings) { + this._config.strings = strings; + } + + constructor(zplConfig) { + this._config = zplConfig; + this._enabled = false; + + // Build the settings model from the default if it exists + if (typeof(this._config.defaultConfig) !== "undefined") { + this.defaultSettings = {}; + for (let s = 0; s < this._config.defaultConfig.length; s++) { + const current = this._config.defaultConfig[s]; + if (current.type != "category") {this.defaultSettings[current.id] = current.value;} + else { + this.defaultSettings[current.id] = {}; + for (let si = 0; si < current.settings.length; si++) { + const subCurrent = current.settings[si]; + this.defaultSettings[current.id][subCurrent.id] = subCurrent.value; + } + } + } + + // Clone the default settings to the current ones + this.settings = _modules_utilities__WEBPACK_IMPORTED_MODULE_4__["default"].deepclone(this.defaultSettings); + } + + // Load previously stored info to check if changelog is needed then check for update + const currentVersionInfo = _modules_utilities__WEBPACK_IMPORTED_MODULE_4__["default"].loadData(this.name, "currentVersionInfo", {version: this.version, hasShownChangelog: false}); + if (currentVersionInfo.version != this.version || !currentVersionInfo.hasShownChangelog) { + this.showChangelog(); + _modules_utilities__WEBPACK_IMPORTED_MODULE_4__["default"].saveData(this.name, "currentVersionInfo", {version: this.version, hasShownChangelog: true}); + } + + // Do not check updates for self + if (this._config?.id === "9") return; + _modules_pluginupdater__WEBPACK_IMPORTED_MODULE_0__["default"].checkForUpdate(this.name, this.version, this._config.id ?? this._config.github_raw ?? this._config?.info.github_raw); + } + + async start() { + _modules_logger__WEBPACK_IMPORTED_MODULE_1__["default"].info(this.name, `version ${this.version} has started.`); + if (this.defaultSettings) this.settings = this.loadSettings(); + this._enabled = true; + if (typeof(this.onStart) == "function") this.onStart(); + } + + stop() { + _modules_logger__WEBPACK_IMPORTED_MODULE_1__["default"].info(this.name, `version ${this.version} has stopped.`); + this._enabled = false; + if (typeof(this.onStop) == "function") this.onStop(); + } + + showSettingsModal() { + if (typeof(this.getSettingsPanel) != "function") return; + _ui_modals__WEBPACK_IMPORTED_MODULE_3__["default"].showModal(this.name + " Settings", _modules_reacttools__WEBPACK_IMPORTED_MODULE_2__["default"].createWrappedElement(this.getSettingsPanel()), { + cancelText: "", + confirmText: "Done", + size: _ui_modals__WEBPACK_IMPORTED_MODULE_3__["default"].ModalSizes.MEDIUM + }); + } + + showChangelog(footer) { + if (typeof(this._config.changelog) == "undefined") return; + _ui_modals__WEBPACK_IMPORTED_MODULE_3__["default"].showChangelogModal(this.name + " Changelog", this.version, this._config.changelog, footer); + } + + saveSettings(settings) { + _modules_utilities__WEBPACK_IMPORTED_MODULE_4__["default"].saveSettings(this.name, this.settings ? this.settings : settings); + } + + loadSettings(defaultSettings) { + // loadSettings -> loadData -> defaultSettings gets deep cloned + return _modules_utilities__WEBPACK_IMPORTED_MODULE_4__["default"].loadSettings(this.name, this.defaultSettings ? this.defaultSettings : defaultSettings); + } + + buildSetting(data) { + const {name, note, type, value, onChange, id} = data; + let setting = null; + if (type == "color") setting = new _ui_settings__WEBPACK_IMPORTED_MODULE_6__.ColorPicker(name, note, value, onChange, {disabled: data.disabled, presetColors: data.presetColors}); + else if (type == "dropdown") setting = new _ui_settings__WEBPACK_IMPORTED_MODULE_6__.Dropdown(name, note, value, data.options, onChange); + else if (type == "file") setting = new _ui_settings__WEBPACK_IMPORTED_MODULE_6__.FilePicker(name, note, onChange); + else if (type == "keybind") setting = new _ui_settings__WEBPACK_IMPORTED_MODULE_6__.Keybind(name, note, value, onChange); + else if (type == "radio") setting = new _ui_settings__WEBPACK_IMPORTED_MODULE_6__.RadioGroup(name, note, value, data.options, onChange, {disabled: data.disabled}); + else if (type == "slider") setting = new _ui_settings__WEBPACK_IMPORTED_MODULE_6__.Slider(name, note, data.min, data.max, value, onChange, data); + else if (type == "switch") setting = new _ui_settings__WEBPACK_IMPORTED_MODULE_6__.Switch(name, note, value, onChange, {disabled: data.disabled}); + else if (type == "textbox") setting = new _ui_settings__WEBPACK_IMPORTED_MODULE_6__.Textbox(name, note, value, onChange, {placeholder: data.placeholder || ""}); + if (id) setting.id = id; + return setting; + } + + buildSettingsPanel() { + const config = this._config.defaultConfig; + const buildGroup = (group) => { + const {name, id, collapsible, shown, settings} = group; + // this.settings[id] = {}; + + const list = []; + for (let s = 0; s < settings.length; s++) { + const current = Object.assign({}, settings[s]); + current.value = this.settings[id][current.id]; + current.onChange = (value) => { + this.settings[id][current.id] = value; + }; + if (Object.keys(this.strings).length && this.strings.settings && this.strings.settings[id] && this.strings.settings[id][current.id]) { + const {settingName = name, note} = this.strings.settings[id][current.id]; + current.name = settingName; + current.note = note; + } + list.push(this.buildSetting(current)); + } + + const settingGroup = new _ui_settings__WEBPACK_IMPORTED_MODULE_6__.SettingGroup(name, {shown, collapsible}).append(...list); + settingGroup.id = id; + return settingGroup; + }; + const list = []; + for (let s = 0; s < config.length; s++) { + const current = Object.assign({}, config[s]); + if (current.type != "category") { + current.value = this.settings[current.id]; + current.onChange = (value) => { + this.settings[current.id] = value; + }; + if (Object.keys(this.strings).length && this.strings.settings && this.strings.settings[current.id]) { + const {name, note} = this.strings.settings[current.id]; + current.name = name; + current.note = note; + } + list.push(this.buildSetting(current)); + } + else { + list.push(buildGroup(current)); + } + } + + return new _ui_settings__WEBPACK_IMPORTED_MODULE_6__.SettingPanel(this.saveSettings.bind(this), ...list); + } +} + +const wrapPluginBase = (conf) => { + return class BoundPlugin extends Plugin { + constructor() { + super(conf); + } + }; +}; + +/***/ }), + +/***/ "./src/structs/screen.js": +/*!*******************************!*\ + !*** ./src/structs/screen.js ***! + \*******************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__) +/* harmony export */ }); +/** + * Representation of the screen such as width and height. + * @deprecated 1/21/22 Use DOMTools + */ +class Screen { + /** Document/window width */ + static get width() {return Math.max(document.documentElement.clientWidth, window.innerWidth || 0);} + /** Document/window height */ + static get height() {return Math.max(document.documentElement.clientHeight, window.innerHeight || 0);} +} + +/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (Screen); + +/***/ }), + +/***/ "./src/structs/structs.js": +/*!********************************!*\ + !*** ./src/structs/structs.js ***! + \********************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ ClassName: () => (/* reexport safe */ _dom_classname__WEBPACK_IMPORTED_MODULE_2__["default"]), +/* harmony export */ DOMObserver: () => (/* reexport safe */ _dom_observer__WEBPACK_IMPORTED_MODULE_3__["default"]), +/* harmony export */ Listenable: () => (/* reexport safe */ _listenable__WEBPACK_IMPORTED_MODULE_4__["default"]), +/* harmony export */ Plugin: () => (/* reexport safe */ _plugin__WEBPACK_IMPORTED_MODULE_5__["default"]), +/* harmony export */ Screen: () => (/* reexport safe */ _screen__WEBPACK_IMPORTED_MODULE_0__["default"]), +/* harmony export */ Selector: () => (/* reexport safe */ _dom_selector__WEBPACK_IMPORTED_MODULE_1__["default"]) +/* harmony export */ }); +/* harmony import */ var _screen__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./screen */ "./src/structs/screen.js"); +/* harmony import */ var _dom_selector__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./dom/selector */ "./src/structs/dom/selector.js"); +/* harmony import */ var _dom_classname__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./dom/classname */ "./src/structs/dom/classname.js"); +/* harmony import */ var _dom_observer__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./dom/observer */ "./src/structs/dom/observer.js"); +/* harmony import */ var _listenable__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./listenable */ "./src/structs/listenable.js"); +/* harmony import */ var _plugin__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./plugin */ "./src/structs/plugin.js"); + + + + + + + + +/***/ }), + +/***/ "./src/ui/colorpicker.js": +/*!*******************************!*\ + !*** ./src/ui/colorpicker.js ***! + \*******************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "default": () => (/* binding */ ColorPicker) +/* harmony export */ }); +/* harmony import */ var modules__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! modules */ "./src/modules/modules.js"); + + +const React = modules__WEBPACK_IMPORTED_MODULE_0__.DiscordModules.React; + +const Popout = modules__WEBPACK_IMPORTED_MODULE_0__.WebpackModules.getByDisplayName("Popout"); +const ColorPickerComponents = modules__WEBPACK_IMPORTED_MODULE_0__.WebpackModules.getByProps("CustomColorPicker"); +const Swatch = ColorPickerComponents?.CustomColorButton.prototype.render.call({props: {}}).type; +const Tooltip = modules__WEBPACK_IMPORTED_MODULE_0__.WebpackModules.getByPrototypes("renderTooltip"); +const LocaleManager = modules__WEBPACK_IMPORTED_MODULE_0__.DiscordModules.LocaleManager; + +class ColorPicker extends React.Component { + constructor(props) { + super(props); + + this.state = { + value: props.value || 0 + }; + + this.onChange = this.onChange.bind(this); + this.swatchRef = React.createRef(); + } + + get canCustom() {return this.props.acceptsCustom || true;} + + onChange(value) { + this.setState({value: value}, () => { + if (typeof(this.props.onChange) === "function") this.props.onChange(this.state.value); + }); + } + + render() { + const renderPopout = () => { + return React.createElement(ColorPickerComponents.CustomColorPicker, { + value: this.state.value, + onChange: this.onChange, + }); + }; + + return React.createElement(ColorPickerComponents.default, { + value: this.state.value, + onChange: this.onChange, + colors: this.props.colors, + renderDefaultButton: props => React.createElement(Tooltip, { + position: Tooltip.Positions.BOTTOM, + text: LocaleManager.Messages.DEFAULT + }, tooltipProps => React.createElement("div", Object.assign(tooltipProps, { + className: "defaultButtonWrapper", + }), React.createElement(ColorPickerComponents.DefaultColorButton, Object.assign(props, {color: this.props.defaultColor})))), + renderCustomButton: () => React.createElement(Popout, { + renderPopout: renderPopout, + animation: Popout.Animation.TRANSLATE, + align: Popout.Align.CENTER, + position: Popout.Positions.BOTTOM + }, props => React.createElement(Tooltip, { + position: Tooltip.Positions.BOTTOM, + text: LocaleManager.Messages.PICK_A_COLOR + }, tooltipProps => React.createElement("div", Object.assign({}, tooltipProps, props, { + className: "colorPickerButtonWrapper" + }), React.createElement(Swatch, { + isCustom: true, + color: this.state.value, + isSelected: !this.props.colors.includes(this.state.value) && this.props.defaultColor !== this.state.value, + disabled: !this.canCustom + })))) + }); + } +} + +/***/ }), + +/***/ "./src/ui/discordcontextmenu.js": +/*!**************************************!*\ + !*** ./src/ui/discordcontextmenu.js ***! + \**************************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "default": () => (/* binding */ DiscordContextMenu) +/* harmony export */ }); +/* harmony import */ var _modules_webpackmodules__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../modules/webpackmodules */ "./src/modules/webpackmodules.js"); +/* harmony import */ var _modules_reacttools__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../modules/reacttools */ "./src/modules/reacttools.js"); +/* harmony import */ var _modules_utilities__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../modules/utilities */ "./src/modules/utilities.js"); +/* harmony import */ var _modules_discordclasses__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../modules/discordclasses */ "./src/modules/discordclasses.js"); + + + + + +// d = e.label, +// f = e.icon, +// h = e.imageUrl, +// v = e.hint, +// m = e.subtext, +// g = e.hasSubmenu, +// y = e.disabled, +// E = e.isFocused, +// S = e.menuItemProps, +// T = e.action, +// b = e.onClose, + + +/** + * Fires when the item is clicked. + * @param {MouseEvent} event - The event generated on click + * @callback module:DiscordContextMenu~MenuItemOnClick + */ + +/** + * @interface + * @name module:DiscordContextMenu~MenuItem + * @description + * This is the generic context menu item component. It is very extensible and will adapt + * it's type depending on the props. + * + * Note: The item ID should be unique to this item across the entire menu. If no `id` is + * provided, the system will use the `label`. Plugins should ensure there are no `label` + * conflicts if they do not wish to provide `id`. `label` conflicts (when not using + * unique `id`s) can cause multiple items to be hovered at once. + * + * @param {object} props - props to pass to the react renderer + * @param {string} props.label - label to show on the menu item + * @param {string} [props.id] - specific id used for this item + * @param {string} [props.hint] - hint to show on the right hand side (usually keyboard combo) + * @param {string} [props.subtext] - description to show underneath + * @param {string} [props.image] - link to image to show on the side + * @param {function} [props.icon] - react component to render on the side + * @param {function} [props.render] - render function for custom rendering the menu item + * @param {module:DiscordContextMenu~MenuItemOnClick} [props.action] - function to perform on click + * @param {module:DiscordContextMenu~MenuItemOnClick} [props.onClick] - function to perform on click (alias of `action`) + * @param {function} [props.onClose] - function to run when this is closed + * @param {boolean} [props.danger=false] - should the item show as danger (red) + * @param {boolean} [props.disabled=false] - should the item be disabled/unclickable + * + * @param {object} [props.style] - allows you to add custom styles + * @param {boolean} [props.closeOnClick] - allows you to prevent closing on click + */ + +/** + * @interface + * @name module:DiscordContextMenu~MenuToggleItem + * @extends module:DiscordContextMenu~MenuItem + * @description + * This item is used for creating checkboxes in menus. Properties shown here are additional + * to those of the main MenuItem {@link module:DiscordContextMenu~MenuItem} + * + * + * @param {boolean} [props.checked=false] - should the checkbox be checked + * @param {boolean} [props.active=false] - alias of `checked` + */ + +/** + * @interface + * @name module:DiscordContextMenu~MenuRadioItem + * @extends module:DiscordContextMenu~MenuItem + * @description + * This item is used for creating radio selections in menus. Properties shown here are additional + * to those of the main MenuItem {@link module:DiscordContextMenu~MenuItem} + * + * Note: for the `forceUpdate` option... Without this enabled, you will manually need to + * manage the state for the functional component. If you do not the toggle will appear + * to not update. @see {@link https://reactjs.org/docs/hooks-reference.html#usestate} + * + * @param {boolean} [props.checked=false] - should the checkbox be checked + * @param {boolean} [props.active=false] - alias of `checked` + * @param {boolean} [props.forceUpdate=true] - should the menu be force-updated after click + */ + +/** + * @interface + * @name module:DiscordContextMenu~SubMenuItem + * @extends module:DiscordContextMenu~MenuItem + * @description + * This item is used for creating nested submenus. Properties shown here are additional + * to those of the main MenuItem {@link module:DiscordContextMenu~MenuItem} + * + * @param {Array} [props.render] - array of items to render in the submenu + * @param {Array} [props.items] - alias of `render` + * @param {Array} [props.children] - Already rendered elements + */ + +/** + * @interface + * @name module:DiscordContextMenu~MenuControlItem + * @extends module:DiscordContextMenu~MenuItem + * @description + * This item is used for adding custom controls like sliders to the context menu. + * Properties shown here are additional to those of the main MenuItem {@link module:DiscordContextMenu~MenuItem} + * + * @param {function} [props.control] - control function that renders the component + */ + + +/** + * A utility for building and rendering Discord's own menus. + * @module DiscordContextMenu + */ +class DiscordContextMenu { + + /** + * Builds a single menu item. The only prop shown here is the type, the rest should + * match the actual component being built. View those to see what options exist + * for each, they often have less in common than you might think. See {@link module:DiscordContextMenu.MenuItem} + * for the majority of props commonly available. Check the documentation for the + * rest of the components. + * + * @param {object} props - props used to build the item + * @param {string} [props.type="text"] - type of the item, options: text, submenu, toggle, radio, custom, separator + * @returns {object} the created component + * + * @see {@link module:DiscordContextMenu~MenuItem} + * @see {@link module:DiscordContextMenu~MenuToggleItem} + * @see {@link module:DiscordContextMenu~MenuRadioItem} + * @see {@link module:DiscordContextMenu~SubMenuItem} + * @see {@link module:DiscordContextMenu~MenuControlItem} + * + * @example + * // Creates a single menu item that prints "MENU ITEM" on click + * DiscordContextMenu.buildMenuItem({ + * label: "Menu Item", + * action: () => {console.log("MENU ITEM");} + * }); + * + * @example + * // Creates a single toggle item that starts unchecked + * // and print the new value on every toggle + * DiscordContextMenu.buildMenuItem({ + * type: "toggle", + * label: "Item Toggle", + * checked: false, + * action: (newValue) => {console.log(newValue);} + * }); + */ + static buildMenuItem(props) { + return window.BdApi.ContextMenu.buildItem(props); + } + + /** + * Creates the all the items **and groups** of a context menu recursively. + * There is no hard limit to the number of groups within groups or number + * of items in a menu. + * @param {Array} setup - array of item props used to build items. See {@link module:DiscordContextMenu.buildMenuItem} + * @returns {Array} array of the created component + * + * @example + * // Creates a single item group item with a toggle item + * DiscordContextMenu.buildMenuChildren([{ + * type: "group", + * items: [{ + * type: "toggle", + * label: "Item Toggle", + * active: false, + * action: (newValue) => {console.log(newValue);} + * }] + * }]); + * + * @example + * // Creates two item groups with a single toggle item each + * DiscordContextMenu.buildMenuChildren([{ + * type: "group", + * items: [{ + * type: "toggle", + * label: "Item Toggle", + * active: false, + * action: (newValue) => { + * console.log(newValue); + * } + * }] + * }, { + * type: "group", + * items: [{ + * type: "toggle", + * label: "Item Toggle", + * active: false, + * action: (newValue) => { + * console.log(newValue); + * } + * }] + * }]); + */ + static buildMenuChildren(setup) { + return window.BdApi.ContextMenu.buildMenuChildren(setup); + } + + /** + * Creates the menu *component* including the wrapping `ContextMenu`. + * Calls {@link module:DiscordContextMenu.buildMenuChildren} under the covers. + * Used to call in combination with {@link module:DiscordContextMenu.openContextMenu}. + * @param {Array} setup - array of item props used to build items. See {@link module:DiscordContextMenu.buildMenuChildren} + * @returns {function} the unique context menu component + */ + static buildMenu(setup) { + return window.BdApi.ContextMenu.buildMenu(setup); + } + + /** + * + * @param {MouseEvent} event - The context menu event. This can be emulated, requires target, and all X, Y locations. + * @param {function} menuComponent - Component to render. This can be any react component or output of {@link module:DiscordContextMenu.buildMenu} + * @param {object} config - configuration/props for the context menu + * @param {string} [config.position="right"] - default position for the menu, options: "left", "right" + * @param {string} [config.align="top"] - default alignment for the menu, options: "bottom", "top" + * @param {function} [config.onClose] - function to run when the menu is closed + * @param {boolean} [config.noBlurEvent=false] - No clue + */ + static openContextMenu(event, menuComponent, config) { + return window.BdApi.ContextMenu.open(event, menuComponent, config); + } + + /** + * Attempts to find and return a specific context menu type's module. Useful + * when patching the render of these menus. + * @param {string | Function} nameOrFilter - name of the context menu type + * @returns {Promise} the webpack module the menu was found in + * @deprecated + */ + static getDiscordMenu(nameOrFilter) { + if (typeof(nameOrFilter) !== "function") { + const displayName = nameOrFilter; + nameOrFilter = (m) => m && m.displayName === displayName; + } + + const directMatch = _modules_webpackmodules__WEBPACK_IMPORTED_MODULE_0__["default"].getModule(m => m.default && nameOrFilter(m.default)); + if (directMatch) return Promise.resolve(directMatch); + + return new Promise(resolve => { + const cancel = _modules_webpackmodules__WEBPACK_IMPORTED_MODULE_0__["default"].addListener(module => { + if (!module.default || !nameOrFilter(module.default)) return; + resolve(module); + cancel(); + }); + }); + } + + /** + * Calls `forceUpdate()` on all context menus it can find. Useful for + * after patching a menu. + */ + static forceUpdateMenus() { + const menus = document.querySelectorAll(`.${_modules_discordclasses__WEBPACK_IMPORTED_MODULE_3__["default"].ContextMenu.menu.first}`); + for (const menu of menus) { + const stateNode = _modules_utilities__WEBPACK_IMPORTED_MODULE_2__["default"].findInTree(_modules_reacttools__WEBPACK_IMPORTED_MODULE_1__["default"].getReactInstance(menu), m=>m && m.forceUpdate && m.updatePosition, {walkable: ["return", "stateNode"]}); + if (!stateNode) continue; + stateNode.forceUpdate(); + stateNode.updatePosition(); + } + } +} + +/***/ }), + +/***/ "./src/ui/errorboundary.js": +/*!*********************************!*\ + !*** ./src/ui/errorboundary.js ***! + \*********************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ WrapBoundary: () => (/* binding */ WrapBoundary), +/* harmony export */ "default": () => (/* binding */ ErrorBoundary) +/* harmony export */ }); +/* harmony import */ var _modules_discordmodules__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../modules/discordmodules */ "./src/modules/discordmodules.js"); + + +const React = _modules_discordmodules__WEBPACK_IMPORTED_MODULE_0__["default"].React; +const ce = React.createElement; + +class ErrorBoundary extends React.Component { + constructor(props) { + super(props); + this.state = {hasError: false}; + } + + componentDidCatch() { + this.setState({hasError: true}); + } + + render() { + if (this.state.hasError) return this.props.errorChildren ? this.props.errorChildren : ce("div", {className: "error"}, "Component Error"); + return this.props.children; + } +} + +function WrapBoundary(Original) { + return class ErrorBoundaryWrapper extends React.Component { + render() { + return ce(ErrorBoundary, null, ce(Original, this.props)); + } + }; +} + +/***/ }), + +/***/ "./src/ui/icons.js": +/*!*************************!*\ + !*** ./src/ui/icons.js ***! + \*************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ IconError: () => (/* reexport safe */ _icons_error__WEBPACK_IMPORTED_MODULE_0__["default"]), +/* harmony export */ IconInfo: () => (/* reexport safe */ _icons_info__WEBPACK_IMPORTED_MODULE_1__["default"]), +/* harmony export */ IconSuccess: () => (/* reexport safe */ _icons_success__WEBPACK_IMPORTED_MODULE_2__["default"]), +/* harmony export */ IconWarning: () => (/* reexport safe */ _icons_warning__WEBPACK_IMPORTED_MODULE_3__["default"]) +/* harmony export */ }); +/* harmony import */ var _icons_error__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./icons/error */ "./src/ui/icons/error.js"); +/* harmony import */ var _icons_info__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./icons/info */ "./src/ui/icons/info.js"); +/* harmony import */ var _icons_success__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./icons/success */ "./src/ui/icons/success.js"); +/* harmony import */ var _icons_warning__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./icons/warning */ "./src/ui/icons/warning.js"); + + + + + +/***/ }), + +/***/ "./src/ui/icons/error.js": +/*!*******************************!*\ + !*** ./src/ui/icons/error.js ***! + \*******************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "default": () => (/* export default binding */ __WEBPACK_DEFAULT_EXPORT__) +/* harmony export */ }); +/** + * Error Icon + * @param {number} size - Size of the icon. + */ +/* harmony default export */ function __WEBPACK_DEFAULT_EXPORT__(size) { + return ` + + `; +} + +/***/ }), + +/***/ "./src/ui/icons/info.js": +/*!******************************!*\ + !*** ./src/ui/icons/info.js ***! + \******************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "default": () => (/* export default binding */ __WEBPACK_DEFAULT_EXPORT__) +/* harmony export */ }); +/** + * Info Icon + * @param {number} size - Size of the icon. + */ +/* harmony default export */ function __WEBPACK_DEFAULT_EXPORT__(size) { + return ` + + + `; +} + +/***/ }), + +/***/ "./src/ui/icons/success.js": +/*!*********************************!*\ + !*** ./src/ui/icons/success.js ***! + \*********************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "default": () => (/* export default binding */ __WEBPACK_DEFAULT_EXPORT__) +/* harmony export */ }); +/** + * Success Icon + * @param {number} size - Size of the icon. + */ +/* harmony default export */ function __WEBPACK_DEFAULT_EXPORT__(size) { + return ` + + + `; +} + +/***/ }), + +/***/ "./src/ui/icons/warning.js": +/*!*********************************!*\ + !*** ./src/ui/icons/warning.js ***! + \*********************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "default": () => (/* export default binding */ __WEBPACK_DEFAULT_EXPORT__) +/* harmony export */ }); +/** + * Warning Icon + * @param {number} size - Size of the icon. + */ +/* harmony default export */ function __WEBPACK_DEFAULT_EXPORT__(size) { + return ` + + + `; +} + +/***/ }), + +/***/ "./src/ui/modals.js": +/*!**************************!*\ + !*** ./src/ui/modals.js ***! + \**************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "default": () => (/* binding */ Modals) +/* harmony export */ }); +/* harmony import */ var modules__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! modules */ "./src/modules/modules.js"); +/** + * Allows an easy way to create and show modals. + * @module Modals + */ + + + +const React = modules__WEBPACK_IMPORTED_MODULE_0__.DiscordModules.React; +const ce = React.createElement; +const Markdown = modules__WEBPACK_IMPORTED_MODULE_0__.WebpackModules.getModule(m => m.rules); +const MarkdownParser = modules__WEBPACK_IMPORTED_MODULE_0__.WebpackModules.getByProps("defaultRules", "parse"); + +class Modals { + + /** Sizes of modals. */ + static get ModalSizes() {return {};} + + /** + * Shows the user profile modal for a given user. + * @param {string} userId - id of the user to show profile for + */ + static showUserProfile(userId) { + return modules__WEBPACK_IMPORTED_MODULE_0__.DiscordModules.UserProfileModal.open(userId); + } + + /** + * Acts as a wrapper for {@link module:Modals.showModal} where the `children` is a text element. + * @param {string} title - title of the modal + * @param {string} content - text to show inside the modal. Can be markdown. + * @param {object} [options] - see {@link module:Modals.showModal} + * @see module:Modals.showModal + */ + static showConfirmationModal(title, content, options = {}) { + this.showModal(title, ce(Markdown, null, content), options); + } + + /** + * Shows a very simple alert modal that has title, content and an okay button. + * @param {string} title - title of the modal + * @param {string} body - text to show inside the modal + */ + static showAlertModal(title, body) { + this.showConfirmationModal(title, body, {cancelText: null}); + } + + /** + * Shows a generic but very customizable modal. + * @param {string} title - title of the modal + * @param {(ReactElement|Array)} children - a single or array of rendered react elements to act as children + * @param {object} [options] - options to modify the modal + * @param {boolean} [options.danger=false] - whether the main button should be red or not + * @param {string} [options.confirmText=Okay] - text for the confirmation/submit button + * @param {string} [options.cancelText=Cancel] - text for the cancel button + * @param {callable} [options.onConfirm=NOOP] - callback to occur when clicking the submit button + * @param {callable} [options.onCancel=NOOP] - callback to occur when clicking the cancel button + */ + static showModal(title, children, options = {}) { + const {danger = false, confirmText = "Okay", cancelText = "Cancel", onConfirm = () => {}, onCancel = () => {}} = options; + return modules__WEBPACK_IMPORTED_MODULE_0__.DiscordModules.ModalActions.openModal(props => { + return React.createElement(modules__WEBPACK_IMPORTED_MODULE_0__.DiscordModules.ConfirmationModal, Object.assign({ + header: title, + confirmButtonColor: danger ? modules__WEBPACK_IMPORTED_MODULE_0__.DiscordModules.ButtonData.Colors.RED : modules__WEBPACK_IMPORTED_MODULE_0__.DiscordModules.ButtonData.Colors.BRAND, + confirmText: confirmText, + cancelText: cancelText, + onConfirm: onConfirm, + onCancel: onCancel + }, props), children); + }); + } + + /** + * @interface + * @name module:Modals~Changelog + * @property {string} title - title of the changelog section + * @property {string} [type=added] - type information of the section. Options: added, improved, fixed, progress. + * @property {Array} items - itemized list of items to show in that section. Can use markdown. + */ + + /** + * Shows a changelog modal based on changelog data. + * @param {string} title - title of the modal + * @param {string} version - subtitle (usually version or date) of the modal + * @param {module:Modals~Changelog} changelog - changelog to show inside the modal + * @param {string} footer - either an html element or text to show in the footer of the modal. Can use markdown. + */ + static showChangelogModal(title, version, changelog, footer) { + const TextElement = modules__WEBPACK_IMPORTED_MODULE_0__.DiscordModules.TextElement; + const ChangelogModalClasses = modules__WEBPACK_IMPORTED_MODULE_0__.WebpackModules.getModule(m => typeof(m) === "object" && Object.keys(m).length === 2 && m.modal && m.content); + if (!TextElement || !ChangelogModalClasses || !modules__WEBPACK_IMPORTED_MODULE_0__.DiscordModules.FlexChild || !modules__WEBPACK_IMPORTED_MODULE_0__.DiscordModules.ModalRoot || !modules__WEBPACK_IMPORTED_MODULE_0__.DiscordModules.ModalActions) return modules__WEBPACK_IMPORTED_MODULE_0__.Logger.warn("Modals", "Unable to show changelog modal--missing modules"); + const changelogItems = []; + for (let c = 0; c < changelog.length; c++) { + const entry = changelog[c]; + const type = modules__WEBPACK_IMPORTED_MODULE_0__.DiscordClasses.Changelog[entry.type] ? modules__WEBPACK_IMPORTED_MODULE_0__.DiscordClasses.Changelog[entry.type] : modules__WEBPACK_IMPORTED_MODULE_0__.DiscordClasses.Changelog.added; + const margin = c == 0 ? modules__WEBPACK_IMPORTED_MODULE_0__.DiscordClasses.Changelog.marginTop : ""; + changelogItems.push(ce("h1", {className: `${type} ${margin}`,}, entry.title)); + const list = ce("ul", null, entry.items.map(i => ce("li", null, MarkdownParser.parse(i)))); + changelogItems.push(list); + } + const renderHeader = function() { + return ce(modules__WEBPACK_IMPORTED_MODULE_0__.DiscordModules.FlexChild, {className: modules__WEBPACK_IMPORTED_MODULE_0__.DiscordClasses.Modals.header.toString(), grow: 0, shrink: 0, direction: modules__WEBPACK_IMPORTED_MODULE_0__.DiscordModules.FlexChild.Direction.VERTICAL}, + ce(TextElement, {tag: "h1", size: TextElement.Sizes.SIZE_20, strong: true}, title), + ce(TextElement, {size: TextElement.Sizes.SIZE_12, color: TextElement.Colors.STANDARD, className: modules__WEBPACK_IMPORTED_MODULE_0__.DiscordClasses.Changelog.date.toString()}, "Version " + version) + ); + }; + const renderFooter = footer ? function() { + return ce(Markdown, null, footer); + } : null; + + const body = ce("div", { + className: `${modules__WEBPACK_IMPORTED_MODULE_0__.DiscordClasses.Modals.content} ${modules__WEBPACK_IMPORTED_MODULE_0__.DiscordClasses.Changelog.container} ${ChangelogModalClasses.content} ${modules__WEBPACK_IMPORTED_MODULE_0__.DiscordClasses.Scrollers.thin}` + }, changelogItems); + + // return DiscordModules.ModalActions.openModal(props => { + // return ce(WebpackModules.getModule(m => m?.toString()?.includes("confirmText")), Object.assign({ + // className: DiscordClasses.Changelog.container.toString(), + // selectable: true, + // onScroll: _ => _, + // onClose: _ => _, + // renderHeader: renderHeader, + // renderFooter: renderFooter, + // }, props), changelogItems); + // }); + return modules__WEBPACK_IMPORTED_MODULE_0__.DiscordModules.ModalActions.openModal(props => { + return React.createElement(modules__WEBPACK_IMPORTED_MODULE_0__.DiscordModules.ModalRoot, Object.assign({ + className: `bd-changelog-modal ${modules__WEBPACK_IMPORTED_MODULE_0__.DiscordClasses.Modals.root} ${modules__WEBPACK_IMPORTED_MODULE_0__.DiscordClasses.Modals.small} ${ChangelogModalClasses.modal}`, + selectable: true, + onScroll: _ => _, + onClose: _ => _ + }, props), [renderHeader(), body, renderFooter?.()]); + }); + // return Modals.showModal(`${title} v${version}`, [renderHeader(), changelogItems, renderFooter?.()], {cancelText: null}); + } +} + +/***/ }), + +/***/ "./src/ui/popouts.js": +/*!***************************!*\ + !*** ./src/ui/popouts.js ***! + \***************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "default": () => (/* binding */ Popouts) +/* harmony export */ }); +/* harmony import */ var modules__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! modules */ "./src/modules/modules.js"); +/** + * Allows an easy way to create and show popouts. + * @module Popouts + */ + + + +const {React, ReactDOM} = modules__WEBPACK_IMPORTED_MODULE_0__.DiscordModules; +const {useReducer, useEffect, useRef} = React; +const AppLayer = modules__WEBPACK_IMPORTED_MODULE_0__.WebpackModules.getModule(m => Object.values(m).some(m => m?.displayName === "AppLayer")); +const ReferencePositionLayer = modules__WEBPACK_IMPORTED_MODULE_0__.WebpackModules.getModule(m => m?.prototype?.calculatePositionStyle, {searchExports: true}); +// const PopoutCSSAnimator = WebpackModules.getByDisplayName("PopoutCSSAnimator"); +const LayerProvider = Object.values(AppLayer).find(m => m.displayName === "AppLayerProvider")?.().props.layerContext.Provider; // eslint-disable-line new-cap +const ComponentDispatch = modules__WEBPACK_IMPORTED_MODULE_0__.WebpackModules.getModule(m => m.toString?.().includes("useContext") && m.toString?.().includes(".windowDispatch"), {searchExports: true}); +const ComponentActions = modules__WEBPACK_IMPORTED_MODULE_0__.WebpackModules.getModule(m => m.POPOUT_SHOW, {searchExports: true}); +const Popout = modules__WEBPACK_IMPORTED_MODULE_0__.WebpackModules.getModule(m => m?.defaultProps && m?.Animation, {searchExports: true}); +const ThemeContext = modules__WEBPACK_IMPORTED_MODULE_0__.WebpackModules.getModule(m => m?.toString?.().includes(".DARK") && m?.toString?.().includes("primaryColor") && m?.toString?.().includes("Provider"), {searchExports: true}); +const Hooks = modules__WEBPACK_IMPORTED_MODULE_0__.WebpackModules.getModule(m => m.useStateFromStores); +const ThemeStore = modules__WEBPACK_IMPORTED_MODULE_0__.WebpackModules.getModule(m => m.theme); + +const createStore = state => { + const listeners = new Set(); + + const setState = function (getter = _ => _) { + const partial = getter(state); + if (partial === state) return; + + state = partial; + + [...listeners].forEach(e => e()); + }; + + setState.getState = () => state; + + function storeListener(getter = _ => _) { + const [, forceUpdate] = useReducer(n => !n, true); + + useEffect(() => { + const dispatch = () => {forceUpdate();}; + + listeners.add(dispatch); + + return () => {listeners.delete(dispatch);}; + }); + + return getter(state); + } + + return [ + setState, + storeListener + ]; +}; + +const [setPopouts, usePopouts] = createStore([]); + +// const AnimationTypes = {FADE: 3, SCALE: 2, TRANSLATE: 1}; + +class Popouts { + + // static get AnimationTypes() {return AnimationTypes;} + + static initialize() { + // return; + this.dispose(); + this.popouts = 0; + + this.container = Object.assign(document.createElement("div"), { + className: "ZeresPluginLibraryPopoutsRenderer", + style: "display: none;" + }); + + this.layerContainer = Object.assign(document.createElement("div"), { + id: "ZeresPluginLibraryPopouts", + className: modules__WEBPACK_IMPORTED_MODULE_0__.DiscordClassModules.TooltipLayers.layerContainer + }); + + document.body.append(this.container, this.layerContainer); + ReactDOM.render(React.createElement(PopoutsContainer), this.layerContainer); + } + + /** + * Shows the user popout for a user relative to a target element + * @param {HTMLElement} target - Element to show the popout in relation to + * @param {object} user - Discord User object for the user to show + * @param {object} [options] - Options to modify the request + * @param {string} [options.guild="currentGuildId"] - Id of the guild (uses current if not specified) + * @param {string} [options.channel="currentChannelId"] - Id of the channel (uses current if not specified) + * @param {string} [options.position="right"] - Positioning relative to element + * @param {string} [options.align="top"] - Positioning relative to element + */ + static showUserPopout(target, user, options = {}) { + const {position = "right", align = "top", guild = modules__WEBPACK_IMPORTED_MODULE_0__.DiscordModules.SelectedGuildStore.getGuildId(), channel = modules__WEBPACK_IMPORTED_MODULE_0__.DiscordModules.SelectedChannelStore.getChannelId()} = options; + target = modules__WEBPACK_IMPORTED_MODULE_0__.DOMTools.resolveElement(target); + // if (target.getBoundingClientRect().right + 250 >= DOMTools.screenWidth && options.autoInvert) position = "left"; + // if (target.getBoundingClientRect().bottom + 400 >= DOMTools.screenHeight && options.autoInvert) align = "bottom"; + // if (target.getBoundingClientRect().top - 400 >= DOMTools.screenHeight && options.autoInvert) align = "top"; + this.openPopout(target, { + position: position, + align: align, + // animation: options.animation || Popouts.AnimationTypes.TRANSLATE, + autoInvert: options.autoInvert, + nudgeAlignIntoViewport: options.nudgeAlignIntoViewport, + spacing: options.spacing, + render: (props) => { + return modules__WEBPACK_IMPORTED_MODULE_0__.DiscordModules.React.createElement(modules__WEBPACK_IMPORTED_MODULE_0__.DiscordModules.UserPopout, Object.assign({}, props, { + userId: user.id, + guildId: guild, + channelId: channel, + closePopout: () => this.closePopout(props.popoutId) + })); + } + }); + } + + /** + * Shows a react popout relative to a target element + * @param {HTMLElement} target - Element to show the popout in relation to + * @param {object} [options] - Options to modify the request + * @param {string} [options.position="right"] - General position relative to element + * @param {string} [options.align="top"] - Alignment relative to element + * @param {boolean} [options.autoInvert=true] - Try to automatically adjust the position if it overflows the screen + * @param {boolean} [options.nudgeAlignIntoViewport=true] - Try to automatically adjust the alignment if it overflows the screen + * @param {number} [options.spacing=8] - Spacing between target and popout + */ + static openPopout(target, options) { + const id = this.popouts++; + + setPopouts(popouts => popouts.concat({ + id: id, + element: React.createElement(PopoutWrapper, Object.assign({}, Popout.defaultProps, { + reference: {current: target}, + popoutId: id, + key: "popout_" + id, + spacing: 50 + }, options)) + })); + + return id; + } + + static closePopout(id) { + const popout = setPopouts.getState().find(e => e.id === id); + + if (!popout) return null; + + setPopouts(popouts => { + const clone = [...popouts]; + clone.splice(clone.indexOf(popout), 1); + return clone; + }); + } + + static dispose() { + modules__WEBPACK_IMPORTED_MODULE_0__.Patcher.unpatchAll("Popouts"); + const container = document.querySelector(".ZeresPluginLibraryPopoutsRenderer"); + const layerContainer = document.querySelector("#ZeresPluginLibraryPopouts"); + if (container) ReactDOM.unmountComponentAtNode(container); + if (container) container.remove(); + if (layerContainer) layerContainer.remove(); + } +} + +function DiscordProviders({children, container}) { + const theme = Hooks.useStateFromStores([ThemeStore], () => ThemeStore.theme); + + return React.createElement(LayerProvider, {value: [container]}, + React.createElement(ThemeContext, {theme}, children) + ); +} + +function PopoutsContainer() { + const popouts = usePopouts(); + + return React.createElement(DiscordProviders, + {container: Popouts.layerContainer}, + popouts.map((popout) => popout.element) + ); +} + +function PopoutWrapper({render, popoutId, ...props}) { + const popoutRef = useRef(); + + useEffect(() => { + if (!popoutRef.current) return; + + const node = ReactDOM.findDOMNode(popoutRef.current); + + const handleClick = ({target}) => { + if (target === node || node.contains(target)) return; + + Popouts.closePopout(popoutId); + }; + + const target = modules__WEBPACK_IMPORTED_MODULE_0__.Utilities.findInTree(node.__reactFiber$, m => m?.stateNode?.updatePosition, {walkable: ["return"]}); + setTimeout(() => target?.stateNode?.updatePosition(), 1); + + document.addEventListener("click", handleClick); + + return () => { + document.removeEventListener("click", handleClick); + }; + }, [popoutRef]); + + // switch (animation) { + // case PopoutCSSAnimator.Types.FADE: + // case PopoutCSSAnimator.Types.SCALE: + // case PopoutCSSAnimator.Types.TRANSLATE: { + // const renderPopout = render; + // render = (renderProps) => { + // return React.createElement(PopoutCSSAnimator, { + // position: renderProps.position, + // type: animation + // }, renderPopout(renderProps)); + // }; + // } + // } + + // eslint-disable-next-line new-cap + const ComponentDispatcher = ComponentDispatch(); + + return React.createElement(ReferencePositionLayer, Object.assign(props, { + ref: popoutRef, + positionKey: "0", + autoInvert: true, + nudgeAlignIntoViewport: true, + id: "popout_" + popoutId, + animation: 2, + onMount() { + ComponentDispatcher.dispatch(ComponentActions.POPOUT_SHOW); + }, + onUnmount() { + ComponentDispatcher.dispatch(ComponentActions.POPOUT_HIDE); + }, + children: (props, ...p) => React.createElement( + "div", + { + style: {transform: "translateZ(0)"}, // for z-index to work properly for sub-popouts + }, + render({popoutId, ...props}, ...p) + ) + })); +} + + + + +/***/ }), + +/***/ "./src/ui/settings/index.js": +/*!**********************************!*\ + !*** ./src/ui/settings/index.js ***! + \**********************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ CSS: () => (/* reexport safe */ _styles_settings_css__WEBPACK_IMPORTED_MODULE_0__["default"]), +/* harmony export */ ColorPicker: () => (/* reexport safe */ _types_color__WEBPACK_IMPORTED_MODULE_5__["default"]), +/* harmony export */ Dropdown: () => (/* reexport safe */ _types_dropdown__WEBPACK_IMPORTED_MODULE_9__["default"]), +/* harmony export */ FilePicker: () => (/* reexport safe */ _types_file__WEBPACK_IMPORTED_MODULE_6__["default"]), +/* harmony export */ Keybind: () => (/* reexport safe */ _types_keybind__WEBPACK_IMPORTED_MODULE_10__["default"]), +/* harmony export */ RadioGroup: () => (/* reexport safe */ _types_radiogroup__WEBPACK_IMPORTED_MODULE_11__["default"]), +/* harmony export */ ReactSetting: () => (/* reexport safe */ _settingfield__WEBPACK_IMPORTED_MODULE_1__.ReactSetting), +/* harmony export */ SettingField: () => (/* reexport safe */ _settingfield__WEBPACK_IMPORTED_MODULE_1__["default"]), +/* harmony export */ SettingGroup: () => (/* reexport safe */ _settinggroup__WEBPACK_IMPORTED_MODULE_2__["default"]), +/* harmony export */ SettingPanel: () => (/* reexport safe */ _settingpanel__WEBPACK_IMPORTED_MODULE_3__["default"]), +/* harmony export */ Slider: () => (/* reexport safe */ _types_slider__WEBPACK_IMPORTED_MODULE_7__["default"]), +/* harmony export */ Switch: () => (/* reexport safe */ _types_switch__WEBPACK_IMPORTED_MODULE_8__["default"]), +/* harmony export */ Textbox: () => (/* reexport safe */ _types_textbox__WEBPACK_IMPORTED_MODULE_4__["default"]) +/* harmony export */ }); +/* harmony import */ var _styles_settings_css__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../styles/settings.css */ "./src/styles/settings.css"); +/* harmony import */ var _settingfield__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./settingfield */ "./src/ui/settings/settingfield.js"); +/* harmony import */ var _settinggroup__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./settinggroup */ "./src/ui/settings/settinggroup.js"); +/* harmony import */ var _settingpanel__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./settingpanel */ "./src/ui/settings/settingpanel.js"); +/* harmony import */ var _types_textbox__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./types/textbox */ "./src/ui/settings/types/textbox.js"); +/* harmony import */ var _types_color__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./types/color */ "./src/ui/settings/types/color.js"); +/* harmony import */ var _types_file__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./types/file */ "./src/ui/settings/types/file.js"); +/* harmony import */ var _types_slider__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./types/slider */ "./src/ui/settings/types/slider.js"); +/* harmony import */ var _types_switch__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ./types/switch */ "./src/ui/settings/types/switch.js"); +/* harmony import */ var _types_dropdown__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ./types/dropdown */ "./src/ui/settings/types/dropdown.js"); +/* harmony import */ var _types_keybind__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! ./types/keybind */ "./src/ui/settings/types/keybind.js"); +/* harmony import */ var _types_radiogroup__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(/*! ./types/radiogroup */ "./src/ui/settings/types/radiogroup.js"); +/** + * An object that makes generating settings panel 10x easier. + * @module Settings + */ + + + + + + + + + + + + + + + + +/***/ }), + +/***/ "./src/ui/settings/settingfield.js": +/*!*****************************************!*\ + !*** ./src/ui/settings/settingfield.js ***! + \*****************************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ ReactSetting: () => (/* binding */ ReactSetting), +/* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__) +/* harmony export */ }); +/* harmony import */ var _structs_listenable__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../structs/listenable */ "./src/structs/listenable.js"); +/* harmony import */ var modules__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! modules */ "./src/modules/modules.js"); + + + + +/** + * Setting field to extend to create new settings + * @memberof module:Settings + */ +class SettingField extends _structs_listenable__WEBPACK_IMPORTED_MODULE_0__["default"] { + /** + * @param {string} name - name label of the setting + * @param {string} note - help/note to show underneath or above the setting + * @param {callable} onChange - callback to perform on setting change + * @param {(ReactComponent|HTMLElement)} settingtype - actual setting to render + * @param {object} [props] - object of props to give to the setting and the settingtype + * @param {boolean} [props.noteOnTop=false] - determines if the note should be shown above the element or not. + */ + constructor(name, note, onChange, settingtype, props = {}) { + super(); + this.name = name; + this.note = note; + if (typeof(onChange) == "function") this.addListener(onChange); + this.inputWrapper = modules__WEBPACK_IMPORTED_MODULE_1__.DOMTools.parseHTML(`
`); + this.type = typeof(settingtype) == "function" ? settingtype : modules__WEBPACK_IMPORTED_MODULE_1__.ReactTools.wrapElement(settingtype); + this.props = props; + modules__WEBPACK_IMPORTED_MODULE_1__.DOMTools.onAdded(this.getElement(), () => {this.onAdded();}); + modules__WEBPACK_IMPORTED_MODULE_1__.DOMTools.onRemoved(this.getElement(), () => {this.onRemoved();}); + } + + /** @returns {HTMLElement} - root element for setting */ + getElement() {return this.inputWrapper;} + + /** Fires onchange to listeners */ + onChange() { + this.alertListeners(...arguments); + } + + /** Fired when root node added to DOM */ + onAdded() { + const reactElement = modules__WEBPACK_IMPORTED_MODULE_1__.DiscordModules.ReactDOM.render(modules__WEBPACK_IMPORTED_MODULE_1__.DiscordModules.React.createElement(ReactSetting, Object.assign({ + title: this.name, + type: this.type, + note: this.note, + }, this.props)), this.getElement()); + + if (this.props.onChange) reactElement.props.onChange = this.props.onChange(reactElement); + reactElement.forceUpdate(); + } + + /** Fired when root node removed from DOM */ + onRemoved() { + modules__WEBPACK_IMPORTED_MODULE_1__.DiscordModules.ReactDOM.unmountComponentAtNode(this.getElement()); + } +} + +/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (SettingField); + +class ReactSetting extends modules__WEBPACK_IMPORTED_MODULE_1__.DiscordModules.React.Component { + get noteElement() { + const className = this.props.noteOnTop ? modules__WEBPACK_IMPORTED_MODULE_1__.DiscordClasses.Margins.marginBottom8 : modules__WEBPACK_IMPORTED_MODULE_1__.DiscordClasses.Margins.marginTop8; + return modules__WEBPACK_IMPORTED_MODULE_1__.DiscordModules.React.createElement(modules__WEBPACK_IMPORTED_MODULE_1__.DiscordModules.SettingsNote, {children: this.props.note, type: "description", className: className.toString()}); + } + + get dividerElement() {return modules__WEBPACK_IMPORTED_MODULE_1__.DiscordModules.React.createElement("div", {className: modules__WEBPACK_IMPORTED_MODULE_1__.DiscordClasses.Dividers.divider.add(modules__WEBPACK_IMPORTED_MODULE_1__.DiscordClasses.Margins.marginTop20).toString()});} + + render() { + const ce = modules__WEBPACK_IMPORTED_MODULE_1__.DiscordModules.React.createElement; + const SettingElement = ce(this.props.type, this.props); + if (this.props.inline) { + const Flex = modules__WEBPACK_IMPORTED_MODULE_1__.DiscordModules.FlexChild; + const titleDefault = modules__WEBPACK_IMPORTED_MODULE_1__.WebpackModules.getByProps("titleDefault") ? modules__WEBPACK_IMPORTED_MODULE_1__.WebpackModules.getByProps("titleDefault").title : "titleDefault-a8-ZSr title-31JmR4"; + return ce(Flex, {direction: Flex.Direction.VERTICAL, className: modules__WEBPACK_IMPORTED_MODULE_1__.DiscordClasses.Margins.marginTop20.toString()}, + ce(Flex, {align: Flex.Align.START}, + ce(Flex.Child, {wrap: !0}, + ce("div", {className: titleDefault}, this.props.title) + ), + ce(Flex.Child, {grow: 0, shrink: 0}, SettingElement) + ), + this.noteElement, + this.dividerElement + ); + } + + return ce(modules__WEBPACK_IMPORTED_MODULE_1__.DiscordModules.SettingsWrapper, { + className: modules__WEBPACK_IMPORTED_MODULE_1__.DiscordClasses.Margins.marginTop20.toString(), + title: this.props.title, + children: [ + this.props.noteOnTop ? this.noteElement : SettingElement, + this.props.noteOnTop ? SettingElement : this.noteElement, + this.dividerElement + ] + }); + } +} + + + +/***/ }), + +/***/ "./src/ui/settings/settinggroup.js": +/*!*****************************************!*\ + !*** ./src/ui/settings/settinggroup.js ***! + \*****************************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__) +/* harmony export */ }); +/* harmony import */ var _structs_listenable__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../structs/listenable */ "./src/structs/listenable.js"); +/* harmony import */ var modules__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! modules */ "./src/modules/modules.js"); +/* harmony import */ var _settingfield__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./settingfield */ "./src/ui/settings/settingfield.js"); + + + + +/** + * Grouping of controls for easier management in settings panels. + * @memberof module:Settings + */ +class SettingGroup extends _structs_listenable__WEBPACK_IMPORTED_MODULE_0__["default"] { + /** + * @param {string} groupName - title for the group of settings + * @param {object} [options] - additional options for the group + * @param {callback} [options.callback] - callback called on settings changed + * @param {boolean} [options.collapsible=true] - determines if the group should be collapsible + * @param {boolean} [options.shown=false] - determines if the group should be expanded by default + */ + constructor(groupName, options = {}) { + super(); + const {collapsible = true, shown = false, callback = () => {}} = options; + this.addListener(callback); + this.onChange = this.onChange.bind(this); + + const collapsed = shown || !collapsible ? "" : "collapsed"; + const group = modules__WEBPACK_IMPORTED_MODULE_1__.DOMTools.parseHTML(`
+

+ ${groupName} +

+
+
`); + const label = group.querySelector("h2"); + const controls = group.querySelector(".plugin-inputs"); + + this.group = group; + this.label = label; + this.controls = controls; + + if (!collapsible) return; + label.addEventListener("click", async () => { + const button = label.querySelector(".button-collapse"); + const wasCollapsed = button.classList.contains("collapsed"); + group.parentElement.querySelectorAll(":scope > .plugin-input-group > .collapsible:not(.collapsed)").forEach((element) => { + element.style.setProperty("height", element.scrollHeight + "px"); + element.classList.add("collapsed"); + setImmediate(() => {element.style.setProperty("height", "");}); + }); + group.parentElement.querySelectorAll(":scope > .plugin-input-group > h2 > .button-collapse").forEach(e => e.classList.add("collapsed")); + if (!wasCollapsed) return; + controls.style.setProperty("height", controls.scrollHeight + "px"); + controls.classList.remove("collapsed"); + button.classList.remove("collapsed"); + await new Promise(resolve => setTimeout(resolve, 300)); + controls.style.setProperty("height", ""); + }); + } + + /** @returns {HTMLElement} - root node for the group. */ + getElement() {return this.group;} + + /** + * Adds multiple nodes to this group. + * @param {(...HTMLElement|...jQuery|...module:Settings.SettingField|...module:Settings.SettingGroup)} nodes - list of nodes to add to the group container + * @returns {module:Settings.SettingGroup} - returns self for chaining + */ + append(...nodes) { + for (let i = 0; i < nodes.length; i++) { + if (modules__WEBPACK_IMPORTED_MODULE_1__.DOMTools.resolveElement(nodes[i]) instanceof Element) this.controls.append(nodes[i]); + else if (nodes[i] instanceof _settingfield__WEBPACK_IMPORTED_MODULE_2__["default"] || nodes[i] instanceof SettingGroup) this.controls.append(nodes[i].getElement()); + if (nodes[i] instanceof _settingfield__WEBPACK_IMPORTED_MODULE_2__["default"]) { + nodes[i].addListener(((node) => (value) => { + this.onChange(node.id || node.name, value); + })(nodes[i])); + } + else if (nodes[i] instanceof SettingGroup) { + nodes[i].addListener(((node) => (settingId, value) => { + this.onChange(node.id || node.name, settingId, value); + })(nodes[i])); + } + } + return this; + } + + /** + * Appends this node to another + * @param {HTMLElement} node - node to attach the group to. + * @returns {module:Settings.SettingGroup} - returns self for chaining + */ + appendTo(node) { + node.append(this.group); + return this; + } + + /** Fires onchange to listeners */ + onChange() { + this.alertListeners(...arguments); + } +} + +/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (SettingGroup); + +/***/ }), + +/***/ "./src/ui/settings/settingpanel.js": +/*!*****************************************!*\ + !*** ./src/ui/settings/settingpanel.js ***! + \*****************************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__) +/* harmony export */ }); +/* harmony import */ var _structs_listenable__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../structs/listenable */ "./src/structs/listenable.js"); +/* harmony import */ var modules__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! modules */ "./src/modules/modules.js"); +/* harmony import */ var _settingfield__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./settingfield */ "./src/ui/settings/settingfield.js"); +/* harmony import */ var _settinggroup__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./settinggroup */ "./src/ui/settings/settinggroup.js"); + + + + + +/** + * Grouping of controls for easier management in settings panels. + * @memberof module:Settings + */ +class SettingPanel extends _structs_listenable__WEBPACK_IMPORTED_MODULE_0__["default"] { + + /** + * Creates a new settings panel + * @param {callable} onChange - callback to fire when settings change + * @param {(...HTMLElement|...jQuery|...module:Settings.SettingField|...module:Settings.SettingGroup)} nodes - list of nodes to add to the panel container + */ + constructor(onChange, ...nodes) { + super(); + this.element = modules__WEBPACK_IMPORTED_MODULE_1__.DOMTools.parseHTML(`
`); + if (typeof(onChange) == "function") this.addListener(onChange); + this.onChange = this.onChange.bind(this); + this.append(...nodes); + } + + /** + * Creates a new settings panel + * @param {callable} onChange - callback to fire when settings change + * @param {(...HTMLElement|...jQuery|...module:Settings.SettingField|...module:Settings.SettingGroup)} nodes - list of nodes to add to the panel container + * @returns {HTMLElement} - root node for the panel. + */ + static build(onChange, ...nodes) { + return (new SettingPanel(onChange, ...nodes)).getElement(); + } + + /** @returns {HTMLElement} - root node for the panel. */ + getElement() {return this.element;} + + /** + * Adds multiple nodes to this panel. + * @param {(...HTMLElement|...jQuery|...SettingField|...SettingGroup)} nodes - list of nodes to add to the panel container + * @returns {module:Settings.SettingPanel} - returns self for chaining + */ + append(...nodes) { + for (let i = 0; i < nodes.length; i++) { + if (modules__WEBPACK_IMPORTED_MODULE_1__.DOMTools.resolveElement(nodes[i]) instanceof Element) this.element.append(nodes[i]); + else if (nodes[i] instanceof _settingfield__WEBPACK_IMPORTED_MODULE_2__["default"] || nodes[i] instanceof _settinggroup__WEBPACK_IMPORTED_MODULE_3__["default"]) this.element.append(nodes[i].getElement()); + if (nodes[i] instanceof _settingfield__WEBPACK_IMPORTED_MODULE_2__["default"]) { + nodes[i].addListener(((node) => (value) => { + this.onChange(node.id || node.name, value); + })(nodes[i])); + } + else if (nodes[i] instanceof _settinggroup__WEBPACK_IMPORTED_MODULE_3__["default"]) { + nodes[i].addListener(((node) => (settingId, value) => { + this.onChange(node.id || node.name, settingId, value); + })(nodes[i])); + } + } + return this; + } + + /** Fires onchange to listeners */ + onChange() { + this.alertListeners(...arguments); + } +} + +/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (SettingPanel); + +/***/ }), + +/***/ "./src/ui/settings/types/color.js": +/*!****************************************!*\ + !*** ./src/ui/settings/types/color.js ***! + \****************************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__) +/* harmony export */ }); +/* harmony import */ var _settingfield__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../settingfield */ "./src/ui/settings/settingfield.js"); +/* harmony import */ var modules__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! modules */ "./src/modules/modules.js"); +/* harmony import */ var _colorpicker__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../../colorpicker */ "./src/ui/colorpicker.js"); + + + + + + +const presetColors = [1752220, 3066993, 3447003, 10181046, 15277667, 15844367, 15105570, 15158332, 9807270, 6323595, 1146986, 2067276, 2123412, 7419530, 11342935, 12745742, 11027200, 10038562, 9936031, 5533306]; + +/** + * Creates a color picker using Discord's built in color picker + * as a base. Input and output using hex strings. + * @memberof module:Settings + * @extends module:Settings.SettingField + */ +class ColorPicker extends _settingfield__WEBPACK_IMPORTED_MODULE_0__["default"] { + /** + * @param {string} name - name label of the setting + * @param {string} note - help/note to show underneath or above the setting + * @param {string} value - current hex color + * @param {callable} onChange - callback to perform on setting change, callback receives hex string + * @param {object} [options] - object of options to give to the setting + * @param {boolean} [options.disabled=false] - should the setting be disabled + * @param {string} [options.defaultColor] - default color to show as large option + * @param {Array} [options.colors] - preset colors to show in swatch + */ + constructor(name, note, value, onChange, options = {}) { + const ColorPickerComponents = modules__WEBPACK_IMPORTED_MODULE_1__.WebpackModules.getByProps("CustomColorPicker"); + if (ColorPickerComponents) { + const defaultColor = options.defaultColor; + super(name, note, onChange, _colorpicker__WEBPACK_IMPORTED_MODULE_2__["default"], { + disabled: !!options.disabled, + onChange: reactElement => color => { + reactElement.props.value = color; + reactElement.forceUpdate(); + this.onChange(modules__WEBPACK_IMPORTED_MODULE_1__.ColorConverter.int2hex(color)); + }, + colors: Array.isArray(options.colors) ? options.colors : presetColors, + defaultColor: defaultColor && typeof(defaultColor) !== "number" ? modules__WEBPACK_IMPORTED_MODULE_1__.ColorConverter.hex2int(defaultColor) : defaultColor, + value: typeof(value) == "number" ? value : modules__WEBPACK_IMPORTED_MODULE_1__.ColorConverter.hex2int(value), + customPickerPosition: "right" + }); + } + else { + const classes = ["color-input"]; + if (options.disabled) classes.push(modules__WEBPACK_IMPORTED_MODULE_1__.DiscordClasses.BasicInputs.disabled); + const ReactColorPicker = modules__WEBPACK_IMPORTED_MODULE_1__.DOMTools.parseHTML(``); + if (options.disabled) ReactColorPicker.setAttribute("disabled", ""); + if (value) ReactColorPicker.setAttribute("value", value); + ReactColorPicker.addEventListener("change", (event) => { + this.onChange(event.target.value); + }); + super(name, note, onChange, ReactColorPicker, {inline: true}); + } + } + + /** Default colors for ColorPicker */ + static get presetColors() {return presetColors;} +} + + + +/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (ColorPicker); + +/***/ }), + +/***/ "./src/ui/settings/types/dropdown.js": +/*!*******************************************!*\ + !*** ./src/ui/settings/types/dropdown.js ***! + \*******************************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__) +/* harmony export */ }); +/* harmony import */ var _settingfield__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../settingfield */ "./src/ui/settings/settingfield.js"); +/* harmony import */ var modules__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! modules */ "./src/modules/modules.js"); + + + +/** + * @interface + * @name module:Settings~DropdownItem + * @property {string|ReactElement} label - label to show in the dropdown + * @property {*} value - actual value represented by label (this is passed via onChange) + */ + + const React = modules__WEBPACK_IMPORTED_MODULE_1__.DiscordModules.React; + + class CloseButton extends React.Component { + render() { + const size = this.props.size || "14px"; + return React.createElement("svg", {className: this.props.className || "", fill: "currentColor", viewBox: "0 0 24 24", style: {width: size, height: size}, onClick: this.props.onClick}, + React.createElement("path", {d: "M18.4 4L12 10.4L5.6 4L4 5.6L10.4 12L4 18.4L5.6 20L12 13.6L18.4 20L20 18.4L13.6 12L20 5.6L18.4 4Z"}) + ); + } +} + + class DownArrow extends React.Component { + render() { + const size = this.props.size || "16px"; + return React.createElement("svg", {className: this.props.className || "", fill: "currentColor", viewBox: "0 0 24 24", style: {width: size, height: size}, onClick: this.props.onClick}, + React.createElement("path", {d: "M8.12 9.29L12 13.17l3.88-3.88c.39-.39 1.02-.39 1.41 0 .39.39.39 1.02 0 1.41l-4.59 4.59c-.39.39-1.02.39-1.41 0L6.7 10.7c-.39-.39-.39-1.02 0-1.41.39-.38 1.03-.39 1.42 0z"}) + ); + } +} + +// + +class Select extends React.Component { + constructor(props) { + super(props); + this.state = {open: false, value: this.props.value}; + this.dropdown = React.createRef(); + this.onChange = this.onChange.bind(this); + this.showMenu = this.showMenu.bind(this); + this.hideMenu = this.hideMenu.bind(this); + this.clear = this.clear.bind(this); + } + + showMenu(event) { + event.preventDefault(); + event.stopPropagation(); + + this.setState((state) => ({open: !state.open}), () => { + if (!this.state.open) return; + + document.addEventListener("click", this.hideMenu); + }); + } + + hideMenu() { + this.setState({open: false}, () => { + document.removeEventListener("click", this.hideMenu); + }); + } + + onChange(value) { + this.setState({value}); + if (this.props.onChange) this.props.onChange(value); + } + + get selected() {return this.props.options.find(o => o.value == this.state.value);} + + get options() { + const selected = this.selected; + return React.createElement("div", {className: "z-select-options"}, + this.props.options.map(opt => + React.createElement("div", {className: `z-select-option${selected?.value == opt.value ? " selected" : ""}`, onClick: this.onChange.bind(this, opt.value)}, opt.label) + ) + ); + } + + clear(event) { + event.stopPropagation(); + this.onChange(null); + } + + render() { + const style = this.props.style == "transparent" ? " z-select-transparent" : ""; + const isOpen = this.state.open ? " menu-open" : ""; + return React.createElement("div", {className: `z-select${style}${isOpen}`, ref: this.dropdown, onClick: this.showMenu}, [ + React.createElement("div", {className: "z-select-value"}, this?.selected?.label ?? this.props.placeholder), + React.createElement("div", {className: "z-select-icons"}, + this.props.clearable && this.selected && React.createElement(CloseButton, {className: "z-select-clear", onClick: this.clear}), + React.createElement(DownArrow, {className: "z-select-arrow"}), + ), + this.state.open && this.options + ]); + } +} + +/** + * Creates a dropdown using discord's built in dropdown. + * @memberof module:Settings + * @extends module:Settings.SettingField + */ +class Dropdown extends _settingfield__WEBPACK_IMPORTED_MODULE_0__["default"] { + /** + * @param {string} name - name label of the setting + * @param {string} note - help/note to show underneath or above the setting + * @param {*} defaultValue - currently selected value + * @param {Array} values - array of all options available + * @param {callable} onChange - callback to perform on setting change, callback item value + * @param {object} [options] - object of options to give to the setting + * @param {boolean} [options.clearable=false] - should be able to empty the field value + * @param {string} [options.placeholder=""] - Placeholder to show when no option is selected, useful when clearable + * @param {boolean} [options.disabled=false] - should the setting be disabled + */ + constructor(name, note, defaultValue, values, onChange, options = {}) { + const {clearable = false, disabled = false, placeholder = ""} = options; + super(name, note, onChange, Select, { + placeholder: placeholder, + clearable: clearable, + disabled: disabled, + options: values, + onChange: dropdown => value => { + dropdown.props.value = value; + dropdown.forceUpdate(); + this.onChange(value); + }, + value: defaultValue + }); + } +} + +/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (Dropdown); + +/***/ }), + +/***/ "./src/ui/settings/types/file.js": +/*!***************************************!*\ + !*** ./src/ui/settings/types/file.js ***! + \***************************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__) +/* harmony export */ }); +/* harmony import */ var _settingfield__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../settingfield */ "./src/ui/settings/settingfield.js"); +/* harmony import */ var modules__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! modules */ "./src/modules/modules.js"); + + + +/** + * Creates a file picker using chromium's default. + * @memberof module:Settings + * @extends module:Settings.SettingField + */ +class FilePicker extends _settingfield__WEBPACK_IMPORTED_MODULE_0__["default"] { + /** + * @param {string} name - name label of the setting + * @param {string} note - help/note to show underneath or above the setting + * @param {callable} onChange - callback to perform on setting change, callback receives File object + * @param {object} [options] - object of options to give to the setting + * @param {boolean} [options.disabled=false] - should the setting be disabled + * @param {Array|string} [options.accept] - what file types should be accepted + * @param {boolean} [options.multiple=false] - should multiple files be accepted + */ + constructor(name, note, onChange, options = {}) { + const classes = modules__WEBPACK_IMPORTED_MODULE_1__.DiscordClasses.BasicInputs.inputDefault.add("file-input"); + if (options.disabled) classes.add(modules__WEBPACK_IMPORTED_MODULE_1__.DiscordClasses.BasicInputs.disabled); + const ReactFilePicker = modules__WEBPACK_IMPORTED_MODULE_1__.DOMTools.parseHTML(``); + if (options.disabled) ReactFilePicker.setAttribute("disabled", ""); + if (options.multiple) ReactFilePicker.setAttribute("multiple", ""); + if (options.accept) ReactFilePicker.setAttribute("accept", Array.isArray(options.accept) ? options.accept.join(",") : options.accept); + ReactFilePicker.addEventListener("change", (event) => { + this.onChange(event.target.files[0]); + }); + super(name, note, onChange, ReactFilePicker); + } +} + +/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (FilePicker); + +/***/ }), + +/***/ "./src/ui/settings/types/keybind.js": +/*!******************************************!*\ + !*** ./src/ui/settings/types/keybind.js ***! + \******************************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__) +/* harmony export */ }); +/* harmony import */ var _settingfield__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../settingfield */ "./src/ui/settings/settingfield.js"); +/* harmony import */ var modules__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! modules */ "./src/modules/modules.js"); + + + +const React = modules__WEBPACK_IMPORTED_MODULE_1__.DiscordModules.React; + +class CloseButton extends React.Component { + render() { + const size = this.props.size || "16px"; + return React.createElement("svg", {className: this.props.className || "", fill: "currentColor", viewBox: "0 0 24 24", style: {width: size, height: size}, onClick: this.props.onClick}, + React.createElement("path", {d: "M18.4 4L12 10.4L5.6 4L4 5.6L10.4 12L4 18.4L5.6 20L12 13.6L18.4 20L20 18.4L13.6 12L20 5.6L18.4 4Z"}) + ); + } +} + +const toCombo = modules__WEBPACK_IMPORTED_MODULE_1__.WebpackModules.getModule(m => m?.toString?.()?.includes("numpad plus"), {searchExports: true}) ?? (() => [[0, 0], [0, 0]]); +const toEvent = modules__WEBPACK_IMPORTED_MODULE_1__.WebpackModules.getModule(m => m?.toString?.()?.includes("keyCode") && m?.toString?.()?.includes("BROWSER"), {searchExports: true}) ?? (() => ({})); + +class ClearableKeybind extends React.Component { + constructor(props) { + super(props); + + this.state = {value: this.props.defaultValue}; + this.clear = this.clear.bind(this); + } + + clear() { + this.setState({value: []}); + this.props.onChange([]); + } + + render() { + return React.createElement("div", {className: "z-keybind-wrapper"}, + React.createElement(modules__WEBPACK_IMPORTED_MODULE_1__.DiscordModules.Keybind, { + disabled: this.props.disabled, + defaultValue: this.state.value, + onChange: this.props.onChange + }), + React.createElement(CloseButton, {className: "z-keybind-clear", onClick: this.clear}) + ); + } +} + +/** + * Creates a keybind setting using discord's built in keybind recorder. + * @memberof module:Settings= + * @extends module:Settings.SettingField + */ +class Keybind extends _settingfield__WEBPACK_IMPORTED_MODULE_0__["default"] { + /** + * @param {string} name - name label of the setting + * @param {string} note - help/note to show underneath or above the setting + * @param {Array} value - array of key names + * @param {callable} onChange - callback to perform on setting change, callback receives array of keycodes + * @param {object} [options] - object of options to give to the setting + * @param {boolean} [options.disabled=false] - should the setting be disabled + */ + constructor(label, help, value, onChange, options = {}) { + const {disabled = false} = options; + if (!Array.isArray(value) || value.some(v => typeof(v) !== "string")) value = []; // if non-strings present, not a valid combo + super(label, help, onChange, ClearableKeybind, { + disabled: disabled, + defaultValue: toCombo(value.join("+")) ?? [], + onChange: element => val => { + if (!Array.isArray(val)) return; + element.props.value = val; + this.onChange(toEvent(val)); + } + }); + } +} + +/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (Keybind); + +/***/ }), + +/***/ "./src/ui/settings/types/radiogroup.js": +/*!*********************************************!*\ + !*** ./src/ui/settings/types/radiogroup.js ***! + \*********************************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__) +/* harmony export */ }); +/* harmony import */ var _settingfield__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../settingfield */ "./src/ui/settings/settingfield.js"); +/* harmony import */ var modules__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! modules */ "./src/modules/modules.js"); + + + +/** + * @interface + * @name module:Settings~RadioItem + * @property {string} name - label to show in the dropdown + * @property {*} value - actual value represented by label (this is passed via onChange) + * @property {string} desc - description/help text to show below name + * @property {string} color - hex string to color the item + */ + +/** + * Creates a radio group using discord's built in radios. + * @memberof module:Settings + * @extends module:Settings.SettingField + */ +class RadioGroup extends _settingfield__WEBPACK_IMPORTED_MODULE_0__["default"] { + /** + * @param {string} name - name label of the setting + * @param {string} note - help/note to show underneath or above the setting + * @param {*} defaultValue - currently selected value + * @param {Array} values - array of all options available + * @param {callable} onChange - callback to perform on setting change, callback item value + * @param {object} [options] - object of options to give to the setting + * @param {boolean} [options.disabled=false] - should the setting be disabled + */ + constructor(name, note, defaultValue, values, onChange, options = {}) { + super(name, note, onChange, modules__WEBPACK_IMPORTED_MODULE_1__.DiscordModules.RadioGroup, { + noteOnTop: true, + disabled: !!options.disabled, + options: values, + onChange: reactElement => option => { + reactElement.props.value = option.value; + reactElement.forceUpdate(); + this.onChange(option.value); + }, + value: defaultValue + }); + } +} + +/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (RadioGroup); + + + +/***/ }), + +/***/ "./src/ui/settings/types/slider.js": +/*!*****************************************!*\ + !*** ./src/ui/settings/types/slider.js ***! + \*****************************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__) +/* harmony export */ }); +/* harmony import */ var _settingfield__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../settingfield */ "./src/ui/settings/settingfield.js"); +/* harmony import */ var modules__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! modules */ "./src/modules/modules.js"); + + + +/** + * Used to render the marker. + * @param {Number} value - The value to render + * @returns {string} the text to show in the marker + * @callback module:Settings~SliderMarkerValue + */ + +/** + * Used to render the grabber tooltip. + * @param {Number} value - The value to render + * @returns {string} the text to show in the tooltip + * @callback module:Settings~SliderRenderValue + */ + +/** + * Creates a slider/range using discord's built in slider. + * @memberof module:Settings + * @extends module:Settings.SettingField + */ +class Slider extends _settingfield__WEBPACK_IMPORTED_MODULE_0__["default"] { + /** + * + * @param {string} name - name label of the setting + * @param {string} note - help/note to show underneath or above the setting + * @param {number} min - minimum value allowed + * @param {number} max - maximum value allowed + * @param {number} value - currently selected value + * @param {callable} onChange - callback to fire when setting is changed, callback receives number + * @param {object} [options] - object of options to give to the setting + * @param {boolean} [options.disabled=false] - should the setting be disabled + * @param {object} [options.fillStyles] - object of css styles to add to active slider + * @param {number} [options.defaultValue] - value highlighted as default + * @param {number} [options.keyboardStep] - step moved when using arrow keys + * @param {Array} [options.markers] - array of vertical markers to show on the slider + * @param {boolean} [options.stickToMarkers] - should the slider be forced to use markers + * @param {boolean} [options.equidistant] - should the markers be scaled to be equidistant + * @param {module:Settings~SliderMarkerValue} [options.onMarkerRender] - function to call to render the value in the marker + * @param {module:Settings~SliderMarkerValue} [options.renderMarker] - alias of `onMarkerRender` + * @param {module:Settings~SliderRenderValue} [options.onValueRender] - function to call to render the value in the tooltip + * @param {module:Settings~SliderRenderValue} [options.renderValue] - alias of `onValueRender` + * @param {string} [options.units] - can be used in place of `onValueRender` will use this string and render Math.round(value) + units + */ + constructor(name, note, min, max, value, onChange, options = {}) { + const props = { + onChange: _ => _, + initialValue: value, + disabled: !!options.disabled, + minValue: min, + maxValue: max, + handleSize: 10 + }; + if (options.fillStyles) props.fillStyles = options.fillStyles; + if (typeof(options.defaultValue) !== "undefined") props.defaultValue = options.defaultValue; + if (options.keyboardStep) props.keyboardStep = options.keyboardStep; + if (options.markers) props.markers = options.markers; + if (options.stickToMarkers) props.stickToMarkers = options.stickToMarkers; + if (typeof(options.equidistant) != "undefined") props.equidistant = options.equidistant; + if (options.units) { + const renderValueLabel = (val) => `${Math.round(val)}${options.units}`; + props.onMarkerRender = renderValueLabel; + props.onValueRender = renderValueLabel; + } + if (options.onMarkerRender || options.renderMarker) props.onMarkerRender = options.onMarkerRender || options.renderMarker; + if (options.onValueRender || options.renderValue) props.onValueRender = options.onValueRender || options.renderValue; + super(name, note, onChange, modules__WEBPACK_IMPORTED_MODULE_1__.DiscordModules.Slider, Object.assign(props, {onValueChange: v => this.onChange(v)})); + } +} + +/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (Slider); + +/***/ }), + +/***/ "./src/ui/settings/types/switch.js": +/*!*****************************************!*\ + !*** ./src/ui/settings/types/switch.js ***! + \*****************************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__) +/* harmony export */ }); +/* harmony import */ var _settingfield__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../settingfield */ "./src/ui/settings/settingfield.js"); +/* harmony import */ var modules__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! modules */ "./src/modules/modules.js"); + + + + +const {useCallback, useState, createElement} = modules__WEBPACK_IMPORTED_MODULE_1__.DiscordModules.React; + +function SwitchComponent({id, checked: initialValue, disabled, onChange}) { + const [checked, setChecked] = useState(initialValue); + const change = useCallback(() => { + onChange?.(!checked); + setChecked(!checked); + }, [checked, onChange]); + + const enabledClass = disabled ? " bd-switch-disabled" : ""; + const checkedClass = checked ? " bd-switch-checked" : ""; + return createElement("div", {className: `bd-switch` + enabledClass + checkedClass}, + createElement("input", {id: id, type: "checkbox", disabled: disabled, checked: checked, onChange: change}), + createElement("div", {className: "bd-switch-body"}, + createElement("svg", {className: "bd-switch-slider", viewBox: "0 0 28 20", preserveAspectRatio: "xMinYMid meet"}, + createElement("rect", {className: "bd-switch-handle", fill: "white", x: "4", y: "0", height: "20", width: "20", rx: "10"}), + createElement("svg", {className: "bd-switch-symbol", viewBox: "0 0 20 20", fill: "none"}, + createElement("path"), + createElement("path") + ) + ) + ) + ); +} + +/** + * Creates a switch using discord's built in switch. + * @memberof module:Settings + * @extends module:Settings.SettingField + */ +class Switch extends _settingfield__WEBPACK_IMPORTED_MODULE_0__["default"] { + /** + * @param {string} name - name label of the setting + * @param {string} note - help/note to show underneath or above the setting + * @param {boolean} isChecked - should switch be checked + * @param {callable} onChange - callback to perform on setting change, callback receives boolean + * @param {object} [options] - object of options to give to the setting + * @param {boolean} [options.disabled=false] - should the setting be disabled + */ + constructor(name, note, isChecked, onChange, options = {}) { + const props = { + disabled: !!options.disabled, + checked: !!isChecked, + onChange: () => value => this.onChange(value), + inline: true + }; + + super(name, note, onChange, SwitchComponent, props); + } +} + +/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (Switch); + + +/***/ }), + +/***/ "./src/ui/settings/types/textbox.js": +/*!******************************************!*\ + !*** ./src/ui/settings/types/textbox.js ***! + \******************************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__) +/* harmony export */ }); +/* harmony import */ var _settingfield__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../settingfield */ "./src/ui/settings/settingfield.js"); +/* harmony import */ var modules__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! modules */ "./src/modules/modules.js"); + + + +/** + * Creates a textbox using discord's built in textbox. + * @memberof module:Settings + * @extends module:Settings.SettingField + */ +class Textbox extends _settingfield__WEBPACK_IMPORTED_MODULE_0__["default"] { + /** + * @param {string} name - name label of the setting + * @param {string} note - help/note to show underneath or above the setting + * @param {string} value - current text in box + * @param {callable} onChange - callback to perform on setting change, callback receives text + * @param {object} [options] - object of options to give to the setting + * @param {string} [options.placeholder=""] - placeholder for when textbox is empty + * @param {boolean} [options.disabled=false] - should the setting be disabled + */ + constructor(name, note, value, onChange, options = {}) { + const {placeholder = "", disabled = false} = options; + super(name, note, onChange, modules__WEBPACK_IMPORTED_MODULE_1__.DiscordModules.Textbox, { + onChange: textbox => val => { + textbox.props.value = val; + textbox.forceUpdate(); + this.onChange(val); + }, + value: value, + disabled: disabled, + placeholder: placeholder || "" + }); + } +} + +/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (Textbox); + +/***/ }), + +/***/ "./src/ui/toasts.js": +/*!**************************!*\ + !*** ./src/ui/toasts.js ***! + \**************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "default": () => (/* binding */ Toasts) +/* harmony export */ }); +/* harmony import */ var modules__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! modules */ "./src/modules/modules.js"); +/* harmony import */ var ui__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ui */ "./src/ui/ui.js"); +/* harmony import */ var _styles_toasts_css__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../styles/toasts.css */ "./src/styles/toasts.css"); +/** + * Toast maker similar to Android. + * + * @module Toasts + */ + + + + + +class Toasts { + + static get CSS() {return _styles_toasts_css__WEBPACK_IMPORTED_MODULE_2__["default"];} + + /** Shorthand for `type = "success"` for {@link module:Toasts.show} */ + static async success(content, options = {}) {return this.show(content, Object.assign(options, {type: "success"}));} + + /** Shorthand for `type = "info"` for {@link module:Toasts.show} */ + static async info(content, options = {}) {return this.show(content, Object.assign(options, {type: "info"}));} + + /** Shorthand for `type = "warning"` for {@link module:Toasts.show} */ + static async warning(content, options = {}) {return this.show(content, Object.assign(options, {type: "warning"}));} + + /** Shorthand for `type = "error"` for {@link module:Toasts.show} */ + static async error(content, options = {}) {return this.show(content, Object.assign(options, {type: "error"}));} + + /** Shorthand for `type = "default"` for {@link module:Toasts.show} */ + static async default(content, options = {}) {return this.show(content, Object.assign(options, {type: "default"}));} + + + /** + * Shows a simple toast, similar to Android, centered over + * the textarea if it exists, and center screen otherwise. + * Vertically it shows towards the bottom like in Android. + * @param {string} content - The string to show in the toast. + * @param {object} options - additional options for the toast + * @param {string} [options.type] - Changes the type of the toast stylistically and semantically. {@link module:Toasts.ToastTypes} + * @param {string} [options.icon] - URL to an optional icon + * @param {number} [options.timeout=3000] - Adjusts the time (in ms) the toast should be shown for before disappearing automatically + * @returns {Promise} - Promise that resolves when the toast is removed from the DOM + */ + static async show(content, options = {}) { + const {type = "", icon = "", timeout = 3000} = options; + this.ensureContainer(); + const toast = modules__WEBPACK_IMPORTED_MODULE_0__.DOMTools.parseHTML(this.buildToast(content, this.parseType(type), icon)); + document.querySelector(".toasts").appendChild(toast); + await new Promise(resolve => setTimeout(resolve, timeout)); + toast.classList.add("closing"); + await new Promise(resolve => setTimeout(resolve, 300)); + toast.remove(); + if (!document.querySelectorAll(".toasts .toast").length) document.querySelector(".toasts").remove(); + } + + static buildToast(message, type, icon) { + const hasIcon = type || icon; + const className = `toast ${hasIcon ? "toast-has-icon" : ""} ${type && type != "default" ? `toast-${type}` : ""}`; + if (!icon && type) icon = type; + return modules__WEBPACK_IMPORTED_MODULE_0__.Utilities.formatString(`
{{icon}}
{{message}}
`, { + className: className, + icon: hasIcon ? this.getIcon(icon) : "", + message: message + }); + } + + static getIcon(icon) { + let iconInner = ``; + switch (icon) { + case "success": iconInner = ui__WEBPACK_IMPORTED_MODULE_1__.Icons.IconSuccess(20); break; // eslint-disable-line new-cap + case "warning": iconInner = ui__WEBPACK_IMPORTED_MODULE_1__.Icons.IconWarning(20); break; // eslint-disable-line new-cap + case "info": iconInner = ui__WEBPACK_IMPORTED_MODULE_1__.Icons.IconInfo(20); break; // eslint-disable-line new-cap + case "error": iconInner = ui__WEBPACK_IMPORTED_MODULE_1__.Icons.IconError(20); // eslint-disable-line new-cap + } + return modules__WEBPACK_IMPORTED_MODULE_0__.Utilities.formatString(`
{{icon}}
`, {icon: iconInner}); + } + + static ensureContainer() { + if (document.querySelector(".toasts")) return; + const channelClass = modules__WEBPACK_IMPORTED_MODULE_0__.DiscordSelectors.ChannelList.sidebar; + const container = channelClass ? document.querySelector(`${channelClass} ~ div:not([style])`) : null; + const memberlist = container ? container.querySelector(modules__WEBPACK_IMPORTED_MODULE_0__.DiscordSelectors.MemberList.membersWrap) : null; + const form = container ? container.querySelector("form") : null; + const left = container ? container.getBoundingClientRect().left : 310; + const right = memberlist ? memberlist.getBoundingClientRect().left : 0; + const width = right ? right - container.getBoundingClientRect().left : (container?.offsetWidth ?? document.body.offsetWidth / 2); + const bottom = form ? form.offsetHeight : 80; + const toastWrapper = document.createElement("div"); + toastWrapper.classList.add("toasts"); + toastWrapper.style.setProperty("left", left + "px"); + toastWrapper.style.setProperty("width", width + "px"); + toastWrapper.style.setProperty("bottom", bottom + "px"); + document.querySelector("#app-mount").appendChild(toastWrapper); + } + + static parseType(type) { + return this.ToastTypes.hasOwnProperty(type) ? this.ToastTypes[type] : ""; + } + + /** + * Enumeration of accepted types. + */ + static get ToastTypes() { + return { + "default": "", + "error": "error", + "success": "success", + "warning": "warning", + "info": "info" + }; + } +} + +/***/ }), + +/***/ "./src/ui/tooltip.js": +/*!***************************!*\ + !*** ./src/ui/tooltip.js ***! + \***************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "default": () => (/* binding */ Tooltip) +/* harmony export */ }); +/* harmony import */ var modules__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! modules */ "./src/modules/modules.js"); +/** + * Tooltip that automatically show and hide themselves on mouseenter and mouseleave events. + * Will also remove themselves if the node to watch is removed from DOM through + * a MutationObserver. + * + * Note this is not using Discord's internals but normal DOM manipulation and emulates + * Discord's own tooltips as closely as possible. + * + * @module Tooltip + */ + + + +const getClass = function(sideOrColor) { + const upperCase = sideOrColor[0].toUpperCase() + sideOrColor.slice(1); + const tooltipClass = modules__WEBPACK_IMPORTED_MODULE_0__.DiscordClasses.Tooltips[`tooltip${upperCase}`]; + if (tooltipClass) return tooltipClass.value; + return null; +}; + +const classExists = function(sideOrColor) { + return !!getClass(sideOrColor); +}; + +const toPx = function(value) { + return `${value}px`; +}; + +/*
+
+
+ User Settings +
+
*/ + +class Tooltip { + /** + * + * @constructor + * @param {(HTMLElement|jQuery)} node - DOM node to monitor and show the tooltip on + * @param {string} tip - string to show in the tooltip + * @param {object} options - additional options for the tooltip + * @param {string} [options.style=black] - correlates to the discord styling/colors (black, brand, green, grey, red, yellow) + * @param {string} [options.side=top] - can be any of top, right, bottom, left + * @param {boolean} [options.preventFlip=false] - prevents moving the tooltip to the opposite side if it is too big or goes offscreen + * @param {boolean} [options.isTimestamp=false] - adds the timestampTooltip class (disables text wrapping) + * @param {boolean} [options.disablePointerEvents=false] - disables pointer events + * @param {boolean} [options.disabled=false] - whether the tooltip should be disabled from showing on hover + */ + constructor(node, text, options = {}) { + const {style = "black", side = "top", preventFlip = false, isTimestamp = false, disablePointerEvents = false, disabled = false} = options; + this.node = modules__WEBPACK_IMPORTED_MODULE_0__.DOMTools.resolveElement(node); + this.label = text; + this.style = style.toLowerCase(); + this.side = side.toLowerCase(); + this.preventFlip = preventFlip; + this.isTimestamp = isTimestamp; + this.disablePointerEvents = disablePointerEvents; + this.disabled = disabled; + this.active = false; + + if (!classExists(this.side)) return modules__WEBPACK_IMPORTED_MODULE_0__.Logger.err("Tooltip", `Side ${this.side} does not exist.`); + if (!classExists(this.style)) return modules__WEBPACK_IMPORTED_MODULE_0__.Logger.err("Tooltip", `Style ${this.style} does not exist.`); + + this.element = modules__WEBPACK_IMPORTED_MODULE_0__.DOMTools.createElement(`
`); + this.tooltipElement = modules__WEBPACK_IMPORTED_MODULE_0__.DOMTools.createElement(`
${this.label}
`); + this.labelElement = this.tooltipElement.childNodes[1]; + this.element.append(this.tooltipElement); + + if (this.disablePointerEvents) { + this.element.classList.add(modules__WEBPACK_IMPORTED_MODULE_0__.DiscordClasses.TooltipLayers.disabledPointerEvents); + this.tooltipElement.classList.add(modules__WEBPACK_IMPORTED_MODULE_0__.DiscordClasses.Tooltips.tooltipDisablePointerEvents); + } + if (this.isTimestamp) this.tooltipElement.classList.add(modules__WEBPACK_IMPORTED_MODULE_0__.WebpackModules.getByProps("timestampTooltip").timestampTooltip); + + + this.node.addEventListener("mouseenter", () => { + if (this.disabled) return; + this.show(); + }); + + this.node.addEventListener("mouseleave", () => { + this.hide(); + }); + } + + /** Alias for the constructor */ + static create(node, text, options = {}) {return new Tooltip(node, text, options);} + + /** Container where the tooltip will be appended. */ + get container() {return document.querySelector(modules__WEBPACK_IMPORTED_MODULE_0__.DiscordSelectors.App.app.sibling(modules__WEBPACK_IMPORTED_MODULE_0__.DiscordSelectors.TooltipLayers.layerContainer));} + /** Boolean representing if the tooltip will fit on screen above the element */ + get canShowAbove() {return this.node.getBoundingClientRect().top - this.element.offsetHeight >= 0;} + /** Boolean representing if the tooltip will fit on screen below the element */ + get canShowBelow() {return this.node.getBoundingClientRect().top + this.node.offsetHeight + this.element.offsetHeight <= modules__WEBPACK_IMPORTED_MODULE_0__.DOMTools.screenHeight;} + /** Boolean representing if the tooltip will fit on screen to the left of the element */ + get canShowLeft() {return this.node.getBoundingClientRect().left - this.element.offsetWidth >= 0;} + /** Boolean representing if the tooltip will fit on screen to the right of the element */ + get canShowRight() {return this.node.getBoundingClientRect().left + this.node.offsetWidth + this.element.offsetWidth <= modules__WEBPACK_IMPORTED_MODULE_0__.DOMTools.screenWidth;} + + /** Hides the tooltip. Automatically called on mouseleave. */ + hide() { + /** Don't rehide if already inactive */ + if (!this.active) return; + this.active = false; + this.element.remove(); + this.tooltipElement.className = this._className; + } + + /** Shows the tooltip. Automatically called on mouseenter. Will attempt to flip if position was wrong. */ + show() { + /** Don't reshow if already active */ + if (this.active) return; + this.active = true; + this.tooltipElement.className = `${modules__WEBPACK_IMPORTED_MODULE_0__.DiscordClasses.Tooltips.tooltip} ${getClass(this.style)}`; + if (this.disablePointerEvents) this.tooltipElement.classList.add(modules__WEBPACK_IMPORTED_MODULE_0__.DiscordClasses.Tooltips.tooltipDisablePointerEvents); + if (this.isTimestamp) this.tooltipElement.classList.add(modules__WEBPACK_IMPORTED_MODULE_0__.WebpackModules.getByProps("timestampTooltip").timestampTooltip); + this.labelElement.textContent = this.label; + this.container.append(this.element); + + if (this.side == "top") { + if (this.canShowAbove || (!this.canShowAbove && this.preventFlip)) this.showAbove(); + else this.showBelow(); + } + + if (this.side == "bottom") { + if (this.canShowBelow || (!this.canShowBelow && this.preventFlip)) this.showBelow(); + else this.showAbove(); + } + + if (this.side == "left") { + if (this.canShowLeft || (!this.canShowLeft && this.preventFlip)) this.showLeft(); + else this.showRight(); + } + + if (this.side == "right") { + if (this.canShowRight || (!this.canShowRight && this.preventFlip)) this.showRight(); + else this.showLeft(); + } + + /** Do not create a new observer each time if one already exists! */ + if (this.observer) return; + /** Use an observer in show otherwise you'll cause unclosable tooltips */ + this.observer = new MutationObserver((mutations) => { + mutations.forEach((mutation) => { + const nodes = Array.from(mutation.removedNodes); + const directMatch = nodes.indexOf(this.node) > -1; + const parentMatch = nodes.some(parent => parent.contains(this.node)); + if (directMatch || parentMatch) { + this.hide(); + this.observer.disconnect(); + } + }); + }); + + this.observer.observe(document.body, {subtree: true, childList: true}); + } + + /** Force showing the tooltip above the node. */ + showAbove() { + this.tooltipElement.classList.add(getClass("top")); + this.element.style.setProperty("top", toPx(this.node.getBoundingClientRect().top - this.element.offsetHeight - 10)); + this.centerHorizontally(); + } + + /** Force showing the tooltip below the node. */ + showBelow() { + this.tooltipElement.classList.add(getClass("bottom")); + this.element.style.setProperty("top", toPx(this.node.getBoundingClientRect().top + this.node.offsetHeight + 10)); + this.centerHorizontally(); + } + + /** Force showing the tooltip to the left of the node. */ + showLeft() { + this.tooltipElement.classList.add(getClass("left")); + this.element.style.setProperty("left", toPx(this.node.getBoundingClientRect().left - this.element.offsetWidth - 10)); + this.centerVertically(); + } + + /** Force showing the tooltip to the right of the node. */ + showRight() { + this.tooltipElement.classList.add(getClass("right")); + this.element.style.setProperty("left", toPx(this.node.getBoundingClientRect().left + this.node.offsetWidth + 10)); + this.centerVertically(); + } + + centerHorizontally() { + const nodecenter = this.node.getBoundingClientRect().left + (this.node.offsetWidth / 2); + this.element.style.setProperty("left", toPx(nodecenter - (this.element.offsetWidth / 2))); + } + + centerVertically() { + const nodecenter = this.node.getBoundingClientRect().top + (this.node.offsetHeight / 2); + this.element.style.setProperty("top", toPx(nodecenter - (this.element.offsetHeight / 2))); + } +} + +/***/ }), + +/***/ "./src/ui/ui.js": +/*!**********************!*\ + !*** ./src/ui/ui.js ***! + \**********************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ ColorPicker: () => (/* reexport safe */ _colorpicker__WEBPACK_IMPORTED_MODULE_8__["default"]), +/* harmony export */ DiscordContextMenu: () => (/* reexport safe */ _discordcontextmenu__WEBPACK_IMPORTED_MODULE_6__["default"]), +/* harmony export */ ErrorBoundary: () => (/* reexport safe */ _errorboundary__WEBPACK_IMPORTED_MODULE_7__["default"]), +/* harmony export */ Icons: () => (/* reexport module object */ _icons__WEBPACK_IMPORTED_MODULE_1__), +/* harmony export */ Modals: () => (/* reexport safe */ _modals__WEBPACK_IMPORTED_MODULE_5__["default"]), +/* harmony export */ Popouts: () => (/* reexport safe */ _popouts__WEBPACK_IMPORTED_MODULE_4__["default"]), +/* harmony export */ Settings: () => (/* reexport module object */ _settings__WEBPACK_IMPORTED_MODULE_0__), +/* harmony export */ Toasts: () => (/* reexport safe */ _toasts__WEBPACK_IMPORTED_MODULE_3__["default"]), +/* harmony export */ Tooltip: () => (/* reexport safe */ _tooltip__WEBPACK_IMPORTED_MODULE_2__["default"]) +/* harmony export */ }); +/* harmony import */ var _settings__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./settings */ "./src/ui/settings/index.js"); +/* harmony import */ var _icons__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./icons */ "./src/ui/icons.js"); +/* harmony import */ var _tooltip__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./tooltip */ "./src/ui/tooltip.js"); +/* harmony import */ var _toasts__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./toasts */ "./src/ui/toasts.js"); +/* harmony import */ var _popouts__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./popouts */ "./src/ui/popouts.js"); +/* harmony import */ var _modals__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./modals */ "./src/ui/modals.js"); +/* harmony import */ var _discordcontextmenu__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./discordcontextmenu */ "./src/ui/discordcontextmenu.js"); +/* harmony import */ var _errorboundary__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./errorboundary */ "./src/ui/errorboundary.js"); +/* harmony import */ var _colorpicker__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ./colorpicker */ "./src/ui/colorpicker.js"); + + + + + + + + + + + + +/***/ }) + +/******/ }); +/************************************************************************/ +/******/ // The module cache +/******/ var __webpack_module_cache__ = {}; +/******/ +/******/ // The require function +/******/ function __webpack_require__(moduleId) { +/******/ // Check if module is in cache +/******/ var cachedModule = __webpack_module_cache__[moduleId]; +/******/ if (cachedModule !== undefined) { +/******/ return cachedModule.exports; +/******/ } +/******/ // Create a new module (and put it into the cache) +/******/ var module = __webpack_module_cache__[moduleId] = { +/******/ // no module.id needed +/******/ // no module.loaded needed +/******/ exports: {} +/******/ }; +/******/ +/******/ // Execute the module function +/******/ __webpack_modules__[moduleId](module, module.exports, __webpack_require__); +/******/ +/******/ // Return the exports of the module +/******/ return module.exports; +/******/ } +/******/ +/************************************************************************/ +/******/ /* webpack/runtime/define property getters */ +/******/ (() => { +/******/ // define getter functions for harmony exports +/******/ __webpack_require__.d = (exports, definition) => { +/******/ for(var key in definition) { +/******/ if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) { +/******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] }); +/******/ } +/******/ } +/******/ }; +/******/ })(); +/******/ +/******/ /* webpack/runtime/hasOwnProperty shorthand */ +/******/ (() => { +/******/ __webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop)) +/******/ })(); +/******/ +/******/ /* webpack/runtime/make namespace object */ +/******/ (() => { +/******/ // define __esModule on exports +/******/ __webpack_require__.r = (exports) => { +/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { +/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); +/******/ } +/******/ Object.defineProperty(exports, '__esModule', { value: true }); +/******/ }; +/******/ })(); +/******/ +/************************************************************************/ +var __webpack_exports__ = {}; +// This entry need to be wrapped in an IIFE because it need to be in strict mode. +(() => { +"use strict"; +/*!**********************!*\ + !*** ./src/index.js ***! + \**********************/ +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__) +/* harmony export */ }); +/* harmony import */ var modules__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! modules */ "./src/modules/modules.js"); +/* harmony import */ var ui__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ui */ "./src/ui/ui.js"); +/* harmony import */ var _structs_plugin__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./structs/plugin */ "./src/structs/plugin.js"); + + + + +const LibraryConfig = __webpack_require__(/*! ./config */ "./src/config.js"); // Use cjs require to prevent polyfill + +const Library = {}; +Library.DCM = ui__WEBPACK_IMPORTED_MODULE_1__.DiscordContextMenu; +Library.ContextMenu = ui__WEBPACK_IMPORTED_MODULE_1__.DiscordContextMenu; +Library.Tooltip = ui__WEBPACK_IMPORTED_MODULE_1__.Tooltip; +Library.Toasts = ui__WEBPACK_IMPORTED_MODULE_1__.Toasts; +Library.Settings = ui__WEBPACK_IMPORTED_MODULE_1__.Settings; +Library.Popouts = ui__WEBPACK_IMPORTED_MODULE_1__.Popouts; +Library.Modals = ui__WEBPACK_IMPORTED_MODULE_1__.Modals; +for (const mod in modules__WEBPACK_IMPORTED_MODULE_0__) Library[mod] = modules__WEBPACK_IMPORTED_MODULE_0__[mod]; + +Library.Components = {ErrorBoundary: ui__WEBPACK_IMPORTED_MODULE_1__.ErrorBoundary, ColorPicker: ui__WEBPACK_IMPORTED_MODULE_1__.ColorPicker}; + +// export default LibraryPlugin(Library.Structs.Plugin, Library); // eslint-disable-line new-cap + +class PluginLibrary extends _structs_plugin__WEBPACK_IMPORTED_MODULE_2__["default"] { + get Library() {return Library;} + + constructor() { + super(LibraryConfig); + + const wasLibLoaded = !!document.getElementById("ZLibraryCSS"); + const isBDLoading = document.getElementById("bd-loading-icon"); + modules__WEBPACK_IMPORTED_MODULE_0__.DOMTools.removeStyle("ZLibraryCSS"); + modules__WEBPACK_IMPORTED_MODULE_0__.DOMTools.addStyle("ZLibraryCSS", ui__WEBPACK_IMPORTED_MODULE_1__.Settings.CSS + ui__WEBPACK_IMPORTED_MODULE_1__.Toasts.CSS + modules__WEBPACK_IMPORTED_MODULE_0__.PluginUpdater.CSS); + ui__WEBPACK_IMPORTED_MODULE_1__.Popouts.initialize(); + + /** + * Checking if this is the library first being loaded during init + * This means that subsequent loads will cause dependents to reload + * This also means first load when installing for the first time + * will automatically reload the dependent plugins. This is needed + * for those plugins that prompt to download and install the lib. + */ + + if (!wasLibLoaded && isBDLoading) return; // If the this is the lib's first load AND this is BD's initialization + + /** + * Now we can go ahead and reload any dependent plugins by checking + * for any with instance._config. Both plugins using buildPlugin() + * and plugin skeletons that prompt for download should have this + * instance property. + */ + + // Temporarily disable toasts so people don't get bombarded + const wasEnabled = BdApi.isSettingEnabled("settings", "general", "showToasts"); + if (wasEnabled) BdApi.disableSetting("settings", "general", "showToasts"); + this._reloadPlugins(); + if (wasEnabled) BdApi.enableSetting("settings", "general", "showToasts"); + } + + _reloadPlugins() { + const list = BdApi.Plugins.getAll().reduce((acc, val) => { + if (!val.instance || !val.instance._config) return acc; + const name = val.id || val.instance?.getName(); + if (name === "ZeresPluginLibrary") return acc; + acc.push(name); + return acc; + }, []); + for (let p = 0; p < list.length; p++) BdApi.Plugins.reload(list[p]); + } + + static bindLibrary(name) { + const BoundAPI = { + Logger: { + stacktrace: (message, error) => modules__WEBPACK_IMPORTED_MODULE_0__.Logger.stacktrace(name, message, error), + log: (...message) => modules__WEBPACK_IMPORTED_MODULE_0__.Logger.log(name, ...message), + error: (...message) => modules__WEBPACK_IMPORTED_MODULE_0__.Logger.err(name, ...message), + err: (...message) => modules__WEBPACK_IMPORTED_MODULE_0__.Logger.err(name, ...message), + warn: (...message) => modules__WEBPACK_IMPORTED_MODULE_0__.Logger.warn(name, ...message), + info: (...message) => modules__WEBPACK_IMPORTED_MODULE_0__.Logger.info(name, ...message), + debug: (...message) => modules__WEBPACK_IMPORTED_MODULE_0__.Logger.debug(name, ...message) + }, + Patcher: { + getPatchesByCaller: () => {return modules__WEBPACK_IMPORTED_MODULE_0__.Patcher.getPatchesByCaller(name);}, + unpatchAll: () => {return modules__WEBPACK_IMPORTED_MODULE_0__.Patcher.unpatchAll(name);}, + before: (moduleToPatch, functionName, callback, options = {}) => {return modules__WEBPACK_IMPORTED_MODULE_0__.Patcher.before(name, moduleToPatch, functionName, callback, options);}, + instead: (moduleToPatch, functionName, callback, options = {}) => {return modules__WEBPACK_IMPORTED_MODULE_0__.Patcher.instead(name, moduleToPatch, functionName, callback, options);}, + after: (moduleToPatch, functionName, callback, options = {}) => {return modules__WEBPACK_IMPORTED_MODULE_0__.Patcher.after(name, moduleToPatch, functionName, callback, options);} + } + }; + const BoundLib = Object.assign({}, Library); + BoundLib.Logger = BoundAPI.Logger; + BoundLib.Patcher = BoundAPI.Patcher; + return BoundLib; + } + + static buildPlugin(config) { + return [(0,_structs_plugin__WEBPACK_IMPORTED_MODULE_2__.wrapPluginBase)(config), this.bindLibrary(config.name ?? config.info.name)]; // eslint-disable-line new-cap + } +} + +Object.assign(PluginLibrary, Library); +Library.bindLibrary = PluginLibrary.bindLibrary; +Library.buildPlugin = PluginLibrary.buildPlugin; +window.ZLibrary = Library; +window.ZeresPluginLibrary = PluginLibrary; +/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (PluginLibrary); + +})(); + +module.exports.ZeresPluginLibrary = __webpack_exports__["default"]; +/******/ })() +; +/*@end@*/ \ No newline at end of file diff --git a/BetterDiscord/plugins/ActivityIcons.config.json b/BetterDiscord/plugins/ActivityIcons.config.json new file mode 100644 index 0000000..e1a799f --- /dev/null +++ b/BetterDiscord/plugins/ActivityIcons.config.json @@ -0,0 +1,9 @@ +{ + "settings": { + "normalIconBehavior": 0 + }, + "currentVersionInfo": { + "version": "1.2.11", + "hasShownChangelog": true + } +} \ No newline at end of file diff --git a/BetterDiscord/plugins/AutoIdleOnAFK.config.json b/BetterDiscord/plugins/AutoIdleOnAFK.config.json new file mode 100644 index 0000000..b2a696e --- /dev/null +++ b/BetterDiscord/plugins/AutoIdleOnAFK.config.json @@ -0,0 +1,6 @@ +{ + "currentVersionInfo": { + "version": "0.4.0", + "hasShownChangelog": true + } +} \ No newline at end of file diff --git a/BetterDiscord/plugins/AutoIdleOnAFK.plugin.js b/BetterDiscord/plugins/AutoIdleOnAFK.plugin.js new file mode 100644 index 0000000..8ffa30d --- /dev/null +++ b/BetterDiscord/plugins/AutoIdleOnAFK.plugin.js @@ -0,0 +1,442 @@ +/** + * @name AutoIdleOnAFK + * @description Automatically updates your discord status to 'idle' when you haven't opened your discord client for more than 5 minutes. +Plugin only works when your status is 'online' and you are not in a voice channel. + +For Bugs or Feature Requests open an issue on my Github + * @version 0.4.0 + * @author RoguedBear + * @authorLink https://github.com/RoguedBear + * @website https://github.com/RoguedBear/BetterDiscordPlugin-AutoIdleOnAFK + * @source https://raw.githubusercontent.com/RoguedBear/BetterDiscordPlugin-AutoIdleOnAFK/main/release/AutoIdleOnAFK.plugin.js + */ +/*@cc_on +@if (@_jscript) + + // Offer to self-install for clueless users that try to run this directly. + var shell = WScript.CreateObject("WScript.Shell"); + var fs = new ActiveXObject("Scripting.FileSystemObject"); + var pathPlugins = shell.ExpandEnvironmentStrings("%APPDATA%\\BetterDiscord\\plugins"); + var pathSelf = WScript.ScriptFullName; + // Put the user at ease by addressing them in the first person + shell.Popup("It looks like you've mistakenly tried to run me directly. \n(Don't do that!)", 0, "I'm a plugin for BetterDiscord", 0x30); + if (fs.GetParentFolderName(pathSelf) === fs.GetAbsolutePathName(pathPlugins)) { + shell.Popup("I'm in the correct folder already.", 0, "I'm already installed", 0x40); + } else if (!fs.FolderExists(pathPlugins)) { + shell.Popup("I can't find the BetterDiscord plugins folder.\nAre you sure it's even installed?", 0, "Can't install myself", 0x10); + } else if (shell.Popup("Should I copy myself to BetterDiscord's plugins folder for you?", 0, "Do you need some help?", 0x34) === 6) { + fs.CopyFile(pathSelf, fs.BuildPath(pathPlugins, fs.GetFileName(pathSelf)), true); + // Show the user where to put plugins in the future + shell.Exec("explorer " + pathPlugins); + shell.Popup("I'm installed!", 0, "Successfully installed", 0x40); + } + WScript.Quit(); + +@else@*/ +const config = { + info: { + name: "AutoIdleOnAFK", + authors: [ + { + name: "RoguedBear", + github_username: "RoguedBear", + link: "https://github.com/RoguedBear" + } + ], + version: "0.4.0", + description: "Automatically updates your discord status to 'idle' when you haven't opened your discord client for more than 5 minutes.\nPlugin only works when your status is 'online' and you are not in a voice channel. \n\nFor Bugs or Feature Requests open an issue on my Github", + github: "https://github.com/RoguedBear/BetterDiscordPlugin-AutoIdleOnAFK", + github_raw: "https://raw.githubusercontent.com/RoguedBear/BetterDiscordPlugin-AutoIdleOnAFK/main/release/AutoIdleOnAFK.plugin.js" + }, + changelog: [ + { + type: "added", + title: "Added a setting to optionally ignore VC state when going AFK", + items: [ + "Earlier if you were in a VC you couldn't go in AFK mode. Now you can toggle a setting to ignore whether you are in VC or not before going AFK.", + "default value: off" + ] + } + ], + main: "index.js", + DEBUG: false, + DEBUG_ActuallyChangeStatus: false, + defaultConfig: [ + { + type: "radio", + name: "Change Status To:", + note: "the status selected will be switched to when AFK. default: Idle", + id: "afkStatus", + value: "idle", + options: [ + { + name: "Idle", + value: "idle" + }, + { + name: "Invisible", + value: "invisible" + }, + { + name: "Do Not Disturb", + value: "dnd" + } + ] + }, + { + type: "slider", + name: "AFK Timeout (minutes)", + note: "minutes to wait before changing your status to Idle/Invisible", + id: "afkTimeout", + value: 5, + defaultValue: 5, + min: 5, + max: 30, + units: "min", + markers: [ + 5, + 10, + 15, + 20, + 25, + 30 + ] + }, + { + type: "slider", + name: "Back To Online Delay (Grace/Cooldown Period)", + note: "seconds to wait before changing your status back to Online. if you close or unfocus discord window, status will not be changed", + id: "backToOnlineDelay", + defaultValue: 10, + value: 10, + min: 5, + max: 120, + units: "s", + markers: [ + 5, + 10, + 30, + 60, + 90, + 120 + ] + }, + { + type: "switch", + name: "Ignore VC state when going AFK", + note: "Allows going to Idle mode even when you are in a VC. default: off", + id: "ignoreVCState", + value: false, + defaultValue: false + }, + { + type: "switch", + name: "Always Revert To Online", + note: "Come back online even if this plugin didn't change your status. Useful for multiple devices.", + id: "alwaysOnline", + value: false, + defaultValue: false + }, + { + type: "switch", + name: "Show toast messages?", + note: "toggles the visibility of \"Changing status back to online\" toast message", + id: "showToasts", + defaultValue: true, + value: true + } + ] +}; +class Dummy { + constructor() {this._config = config;} + start() {} + stop() {} +} + +if (!global.ZeresPluginLibrary) { + BdApi.showConfirmationModal("Library Missing", `The library plugin needed for ${config.name ?? config.info.name} is missing. Please click Download Now to install it.`, { + confirmText: "Download Now", + cancelText: "Cancel", + onConfirm: () => { + require("request").get("https://betterdiscord.app/gh-redirect?id=9", async (err, resp, body) => { + if (err) return require("electron").shell.openExternal("https://betterdiscord.app/Download?id=9"); + if (resp.statusCode === 302) { + require("request").get(resp.headers.location, async (error, response, content) => { + if (error) return require("electron").shell.openExternal("https://betterdiscord.app/Download?id=9"); + await new Promise(r => require("fs").writeFile(require("path").join(BdApi.Plugins.folder, "0PluginLibrary.plugin.js"), content, r)); + }); + } + else { + await new Promise(r => require("fs").writeFile(require("path").join(BdApi.Plugins.folder, "0PluginLibrary.plugin.js"), body, r)); + } + }); + } + }); +} + +module.exports = !global.ZeresPluginLibrary ? Dummy : (([Plugin, Api]) => { + const plugin = (Plugin, Library) => { + const { Logger, DiscordModules } = Library; + const { + SelectedChannelStore: { getVoiceChannelId }, + } = DiscordModules; + + // Logger.info("VC: " + getVoiceChannelId()); + + const { + Webpack, + Webpack: { Filters }, + } = BdApi; + + const UserSettingsProtoStore = BdApi.Webpack.getModule( + (m) => + m && + typeof m.getName == "function" && + m.getName() == "UserSettingsProtoStore" && + m, + { first: true, searchExports: true } + ); + + const UserSettingsProtoUtils = BdApi.Webpack.getModule( + (m) => + m.ProtoClass && + m.ProtoClass.typeName.endsWith(".PreloadedUserSettings"), + { first: true, searchExports: true } + ); + + var DEBUG = false; + function log_debug(module, ...message) { + if (DEBUG !== true) { + return; + } else { + Logger.debug(module, ...message); + } + } + + return class AutoIdleOnAFK extends Plugin { + constructor() { + super(); + + this.getSettingsPanel = () => { + return this.buildSettingsPanel().getElement(); + }; + + // Some instance variables + this.afkTimeoutID = undefined; // timeout set by onBlur + this.backFromAFKTimeoutID = undefined; // timeout set by onFocus + // key used as a flag to detect if afk was set by the plugin or manually by user + this.keyIdleSetByPlugin = "IdleSetByPlugin"; + + this.OnBlurBounded = this.onBlurFunction.bind(this); + this.boundOnFocusBounded = this.onFocusFunction.bind(this); + } + + onStart() { + if (this._config.DEBUG === true) { + DEBUG = true; + log_debug(this); + log_debug("Current status: " + this.currentStatus()); + log_debug("In Voice Channel: " + this.inVoiceChannel()); + log_debug( + "onlineStatusAndNotInVC: " + this.onlineStatusAndNotInVC() + ); + } + window.addEventListener("blur", this.OnBlurBounded); + window.addEventListener("focus", this.boundOnFocusBounded); + } + + onStop() { + clearTimeout(this.afkTimeoutID); + clearTimeout(this.backFromAFKTimeoutID); + window.removeEventListener("blur", this.OnBlurBounded); + window.removeEventListener("focus", this.boundOnFocusBounded); + } + + onBlurFunction() { + // TODO: remove this + log_debug("Focus lost from discord window"); + + // if user closes discord before the backToOnlineTimeout, clear the + // pending timeout (if it even exists) + this.backFromAFKTimeoutID = this.cancelTimeout( + this.backFromAFKTimeoutID + ); + + var __afkSetByPlugin = BdApi.loadData( + this._config.info.name, + this.keyIdleSetByPlugin + ); + var inVoiceChannelAndIdleSetByPlugin = + this.inVoiceChannel() && __afkSetByPlugin; + + var inVoiceChannelAndAlwaysOnlineEnabled = + this.inVoiceChannel() && this.settings.alwaysOnline; + + if (this.onlineStatusAndNotInVC()) { + var _timeout_ms = + this.settings.afkTimeout * (DEBUG ? 2 : 60) * 1000; + log_debug("setting timeout of " + _timeout_ms + "ms"); + this.afkTimeoutID = setTimeout(() => { + if (this.onlineStatusAndNotInVC()) { + this.updateStatus(this.settings.afkStatus); + BdApi.saveData( + this._config.info.name, + this.keyIdleSetByPlugin, + true + ); + } + // If DEBUG is enabled then keep a shorter duration than 60s + }, _timeout_ms); // converting min to ms + } else if ( + // if the user is in a VC, idle was set by plugin & + // their current status == afkStatus + (inVoiceChannelAndIdleSetByPlugin || + // OR, is user in VC and always online is enabled + inVoiceChannelAndAlwaysOnlineEnabled) && + this.currentStatus() == this.settings.afkStatus + ) { + this.updateStatus("online"); + this.showToast("Changing status back to online, You are in VC"); + BdApi.saveData( + this._config.info.name, + this.keyIdleSetByPlugin, + false + ); + } + } + + onFocusFunction() { + // TODO: remove this + log_debug("Discord window in focus now"); + + // if user opens discord before the afkTimeout, clear the pending + // timeout (if it even exists) + this.afkTimeoutID = this.cancelTimeout(this.afkTimeoutID); + + log_debug( + "Setting timeout of " + + this.settings.backToOnlineDelay * 1000 + + " ms" + ); + this.backFromAFKTimeoutID = setTimeout(() => { + // TODO: Refactor/comment out/test more this part + var __afkSetByPlugin = BdApi.loadData( + this._config.info.name, + this.keyIdleSetByPlugin + ); + var statusIsAFKAndWasSetByPlugin = + this.currentStatus() === this.settings.afkStatus && + __afkSetByPlugin === true; + + var statusIsAFKAndAlwaysOnlineIsTrue = + this.currentStatus() === this.settings.afkStatus && + this.settings.alwaysOnline === true; + + if (statusIsAFKAndWasSetByPlugin) { + this.showToast("Changing status back to online"); + this.updateStatus("online"); + BdApi.saveData( + this._config.info.name, + this.keyIdleSetByPlugin, + false + ); + } else if (statusIsAFKAndAlwaysOnlineIsTrue) { + BdApi.showToast("Changing status back to online"); + this.updateStatus("online"); + BdApi.saveData( + this._config.info.name, + this.keyIdleSetByPlugin, + false + ); + } else if (__afkSetByPlugin == undefined) { + return; + } else { + BdApi.deleteData( + this._config.info.name, + this.keyIdleSetByPlugin + ); + Logger.info( + "User overrode the status. leaving status unchanged..." + ); + } + }, this.settings.backToOnlineDelay * 1000); + } + + /** + * Cancles the given timeout id + * @param {number} timeoutId + * @returns {undefined} + */ + cancelTimeout(timeoutId) { + if (timeoutId != undefined) { + log_debug("Cancelling timeout " + timeoutId); // TODO: remove this + clearTimeout(timeoutId); + return undefined; + } + } + + /** + * @returns {string} the current user status + */ + currentStatus() { + return UserSettingsProtoStore.settings.status.status.value; + } + /** + * @returns {boolean} if user is in a VC + */ + inVoiceChannel() { + return ( + this.settings.ignoreVCState === false && + getVoiceChannelId() !== null + ); + } + + /** + * Returns if the current status is 'online' and the user is not in a + * voice channel + * @returns + */ + onlineStatusAndNotInVC() { + return ( + this.currentStatus() === "online" && + this.inVoiceChannel() === false + ); + } + + /** + * Updates the remote status to the param `toStatus` + * @param {('online'|'idle'|'invisible'|'dnd')} toStatus + */ + updateStatus(toStatus) { + if ( + this._config.DEBUG === true && + this._config.DEBUG_ActuallyChangeStatus === false + ) { + log_debug("Changing (but not changing) status to: " + toStatus); + return; + } + log_debug("Actually changing status to: " + toStatus); + UserSettingsProtoUtils.updateAsync( + "status", + (statusSetting) => { + log_debug(statusSetting); + statusSetting.status.value = toStatus; + }, + 0 + ); + } + + /** + * shows toast message based on showToast settings + * @param {string} msg + */ + showToast(msg) { + if (this.settings.showToasts) { + BdApi.showToast(msg); + } + } + }; +}; + return plugin(Plugin, Api); +})(global.ZeresPluginLibrary.buildPlugin(config)); +/*@end@*/ \ No newline at end of file diff --git a/BetterDiscord/plugins/BetterAnimations.config.json b/BetterDiscord/plugins/BetterAnimations.config.json new file mode 100644 index 0000000..4bb14ab --- /dev/null +++ b/BetterDiscord/plugins/BetterAnimations.config.json @@ -0,0 +1,45 @@ +{ + "currentVersionInfo": { + "version": "1.1.12", + "hasShownChangelog": true + }, + "settings": { + "guild": { + "enabled": true, + "type": "flipForwards", + "easing": "easeInOutCubic", + "duration": 700 + }, + "channel": { + "enabled": true, + "type": "slideUp", + "easing": "easeInOutCubic", + "duration": 500 + }, + "settings": { + "enabled": false, + "type": "slipUp", + "easing": "easeInOutCubic", + "duration": 500 + }, + "messages": { + "enabled": true, + "type": "slip", + "easing": "easeOutQuart", + "position": "bottom", + "duration": 200 + }, + "popouts": { + "enabled": true, + "type": "flip", + "easing": "easeInOutBack", + "duration": 300 + }, + "expressionPicker": { + "enabled": true, + "type": "scaleChange", + "easing": "easeInOutBack", + "duration": 500 + } + } +} \ No newline at end of file diff --git a/BetterDiscord/plugins/BetterAudioPlayer.config.json b/BetterDiscord/plugins/BetterAudioPlayer.config.json new file mode 100644 index 0000000..54b34aa --- /dev/null +++ b/BetterDiscord/plugins/BetterAudioPlayer.config.json @@ -0,0 +1,6 @@ +{ + "currentVersionInfo": { + "version": "1.0.2", + "hasShownChangelog": true + } +} \ No newline at end of file diff --git a/BetterDiscord/plugins/BetterBotTags.config.json b/BetterDiscord/plugins/BetterBotTags.config.json new file mode 100644 index 0000000..156cfc8 --- /dev/null +++ b/BetterDiscord/plugins/BetterBotTags.config.json @@ -0,0 +1,6 @@ +{ + "currentVersionInfo": { + "version": "1.1.0", + "hasShownChangelog": true + } +} \ No newline at end of file diff --git a/BetterDiscord/plugins/BetterInvites.config.json b/BetterDiscord/plugins/BetterInvites.config.json new file mode 100644 index 0000000..2abe9e1 --- /dev/null +++ b/BetterDiscord/plugins/BetterInvites.config.json @@ -0,0 +1,6 @@ +{ + "currentVersionInfo": { + "version": "1.6.3", + "hasShownChangelog": true + } +} \ No newline at end of file diff --git a/BetterDiscord/plugins/BetterRoleColors.config.json b/BetterDiscord/plugins/BetterRoleColors.config.json new file mode 100644 index 0000000..a50418b --- /dev/null +++ b/BetterDiscord/plugins/BetterRoleColors.config.json @@ -0,0 +1,37 @@ +{ + "currentVersionInfo": { + "version": "0.9.1", + "hasShownChangelog": true + }, + "settings": { + "global": { + "important": false + }, + "modules": { + "typing": true, + "voice": true, + "mentions": true, + "chat": false, + "botTags": true, + "memberList": true + }, + "popouts": { + "username": false, + "discriminator": false, + "nickname": true, + "fallback": true + }, + "modals": { + "username": true, + "discriminator": false + }, + "auditLog": { + "username": true, + "discriminator": false + }, + "account": { + "username": true, + "discriminator": false + } + } +} \ No newline at end of file diff --git a/BetterDiscord/plugins/CallTimeCounter.config.json b/BetterDiscord/plugins/CallTimeCounter.config.json new file mode 100644 index 0000000..8c0dc45 --- /dev/null +++ b/BetterDiscord/plugins/CallTimeCounter.config.json @@ -0,0 +1,6 @@ +{ + "currentVersionInfo": { + "version": "0.0.6", + "hasShownChangelog": true + } +} \ No newline at end of file diff --git a/BetterDiscord/plugins/CallTimeCounter.plugin.js b/BetterDiscord/plugins/CallTimeCounter.plugin.js new file mode 100644 index 0000000..9499673 --- /dev/null +++ b/BetterDiscord/plugins/CallTimeCounter.plugin.js @@ -0,0 +1,157 @@ +/** + * @name CallTimeCounter + * @source https://github.com/QWERTxD/BetterDiscordPlugins/blob/main/CallTimeCounter/CallTimeCounter.plugin.js + * @description Shows how much time you are in a voice chat. + * @updateUrl https://raw.githubusercontent.com/QWERTxD/BetterDiscordPlugins/main/CallTimeCounter/CallTimeCounter.plugin.js + * @website https://github.com/QWERTxD/BetterDiscordPlugins/tree/main/CallTimeCounter + * @version 0.0.6 + */ + +const request = require("request"); +const fs = require("fs"); +const path = require("path"); + +const config = { + info: { + name: "CallTimeCounter", + authors: [ + { + name: "QWERT" + } + ], + version: "0.0.6", + description: "Shows how much time you are in a voice chat.", + github_raw: "https://raw.githubusercontent.com/QWERTxD/BetterDiscordPlugins/main/CallTimeCounter/CallTimeCounter.plugin.js", + }, + changelog: [ + { + title: "Fixes", + type: "fixed", + items: [ + "Fixed for BetterDiscord 1.8 update." + ] + } + ], + defaultConfig: [] +}; + +module.exports = !global.ZeresPluginLibrary ? class { + constructor() { + this._config = config; + } + + load() { + BdApi.showConfirmationModal("Library plugin is needed", + `The library plugin needed for AQWERT'sPluginBuilder is missing. Please click Download Now to install it.`, { + confirmText: "Download", + cancelText: "Cancel", + onConfirm: () => { + request.get("https://rauenzi.github.io/BDPluginLibrary/release/0PluginLibrary.plugin.js", (error, response, body) => { + if (error) + return electron.shell.openExternal("https://betterdiscord.net/ghdl?url=https://raw.githubusercontent.com/rauenzi/BDPluginLibrary/master/release/0PluginLibrary.plugin.js"); + + fs.writeFileSync(path.join(BdApi.Plugins.folder, "0PluginLibrary.plugin.js"), body); + }); + } + }); + } + + start() { } + + stop() { } +} : (([Plugin, Library]) => { + const { DiscordModules, WebpackModules, Patcher, PluginUtilities } = Library; + const { React, SelectedChannelStore: {getVoiceChannelId} } = DiscordModules; + const PanelSubtext = WebpackModules.find(m => m?.$$typeof?.toString() === "Symbol(react.forward_ref)" + && m.render?.toString().includes("createHref"), {searchExports: true}); + let lastVoice, lastState; + const Dispatcher = WebpackModules.getByProps('dispatch', 'register'); + + class Timer extends React.Component { + constructor(props) { + super(props); + this.connected = this.connected.bind(this); + this.state = { + startTime: 0, + delta: 0 + }; + } + + connected(e) { + if (e.state && e.state === 'RTC_DISCONNECTED' && !e.hasOwnProperty('streamKey')) { + this.setState((prev) => ( + prev.startTime = Date.now())); + } + } + + componentDidMount() { + if(lastVoice === getVoiceChannelId()) { + Dispatcher.subscribe('RTC_CONNECTION_STATE', this.connected); + this.setState(lastState); + this.interval = setInterval(() => { + this.setState((prev) => (prev.delta = Math.round((Date.now() - prev.startTime) / 1000) * 1000)); + this.setState((prev) => prev.lastVoice = getVoiceChannelId()); + }, 1000); + }else{ + this.setState((prev) => ( + prev.startTime = Date.now())); + Dispatcher.subscribe('RTC_CONNECTION_STATE', this.connected); + this.interval = setInterval(() => { + this.setState((prev) => (prev.delta = Math.round((Date.now() - prev.startTime) / 1000) * 1000)); + this.setState((prev) => prev.lastVoice = getVoiceChannelId()); + }, 1000); + } + } + + componentWillUnmount() { + Dispatcher.unsubscribe('RTC_CONNECTION_STATE', this.connected); + lastVoice = this.state.lastVoice; + lastState = this.state; + setTimeout(() => { + lastVoice = null; + lastState = {}; + }, 1000) + clearInterval(this.interval); + } + + render() { + return React.createElement("div", { className: "voiceTimer" }, `Time elapsed: ${new Date(this.state.delta).toISOString().substr(11, 8)}`); + } + }; + + class plugin extends Plugin { + constructor() { + super(); + } + + + onStart() { + this.patch(); + + PluginUtilities.addStyle("voicetimer", ` + .voiceTimer { + text-decoration: none !important; + margin-top: 8px; + } + `) + } + + onStop() { + Patcher.unpatchAll(); + PluginUtilities.removeStyle("voicetimer"); + } + + patch() { + Patcher.before(PanelSubtext, "render", (_, [props], ret) => { + if (!props?.children?.props?.className?.includes("channel")) return; + props.children.props.children = [ + props.children.props.children, + React.createElement(Timer, { className: "voiceTimer" }) + ] + }); + } + + } + + return plugin; +})(global.ZeresPluginLibrary.buildPlugin(config)); diff --git a/BetterDiscord/plugins/CharCounter.config.json b/BetterDiscord/plugins/CharCounter.config.json new file mode 100644 index 0000000..5977a26 --- /dev/null +++ b/BetterDiscord/plugins/CharCounter.config.json @@ -0,0 +1,7 @@ +{ + "all": { + "sliders": { + "showPercentage": 0 + } + } +} \ No newline at end of file diff --git a/BetterDiscord/plugins/HideDisabledEmojis.config.json b/BetterDiscord/plugins/HideDisabledEmojis.config.json new file mode 100644 index 0000000..f394323 --- /dev/null +++ b/BetterDiscord/plugins/HideDisabledEmojis.config.json @@ -0,0 +1,6 @@ +{ + "currentVersionInfo": { + "version": "0.0.9", + "hasShownChangelog": true + } +} \ No newline at end of file diff --git a/BetterDiscord/plugins/HideDisabledEmojis.plugin.js b/BetterDiscord/plugins/HideDisabledEmojis.plugin.js new file mode 100644 index 0000000..6816ea0 --- /dev/null +++ b/BetterDiscord/plugins/HideDisabledEmojis.plugin.js @@ -0,0 +1,187 @@ +/** + * @name HideDisabledEmojis + * @description Hides disabled emojis from the emoji picker. + * @version 0.0.9 + * @author Zerebos + * @authorId 249746236008169473 + * @website https://github.com/rauenzi/BetterDiscordAddons/tree/master/Plugins/HideDisabledEmojis + * @source https://github.com/rauenzi/BetterDiscordAddons/blob/master/Plugins/HideDisabledEmojis/HideDisabledEmojis.plugin.js + */ +/*@cc_on +@if (@_jscript) + + // Offer to self-install for clueless users that try to run this directly. + var shell = WScript.CreateObject("WScript.Shell"); + var fs = new ActiveXObject("Scripting.FileSystemObject"); + var pathPlugins = shell.ExpandEnvironmentStrings("%APPDATA%\\BetterDiscord\\plugins"); + var pathSelf = WScript.ScriptFullName; + // Put the user at ease by addressing them in the first person + shell.Popup("It looks like you've mistakenly tried to run me directly. \n(Don't do that!)", 0, "I'm a plugin for BetterDiscord", 0x30); + if (fs.GetParentFolderName(pathSelf) === fs.GetAbsolutePathName(pathPlugins)) { + shell.Popup("I'm in the correct folder already.", 0, "I'm already installed", 0x40); + } else if (!fs.FolderExists(pathPlugins)) { + shell.Popup("I can't find the BetterDiscord plugins folder.\nAre you sure it's even installed?", 0, "Can't install myself", 0x10); + } else if (shell.Popup("Should I copy myself to BetterDiscord's plugins folder for you?", 0, "Do you need some help?", 0x34) === 6) { + fs.CopyFile(pathSelf, fs.BuildPath(pathPlugins, fs.GetFileName(pathSelf)), true); + // Show the user where to put plugins in the future + shell.Exec("explorer " + pathPlugins); + shell.Popup("I'm installed!", 0, "Successfully installed", 0x40); + } + WScript.Quit(); + +@else@*/ +const config = { + info: { + name: "HideDisabledEmojis", + authors: [ + { + name: "Zerebos", + discord_id: "249746236008169473", + github_username: "rauenzi", + twitter_username: "ZackRauen" + } + ], + version: "0.0.9", + description: "Hides disabled emojis from the emoji picker.", + github: "https://github.com/rauenzi/BetterDiscordAddons/tree/master/Plugins/HideDisabledEmojis", + github_raw: "https://github.com/rauenzi/BetterDiscordAddons/blob/master/Plugins/HideDisabledEmojis/HideDisabledEmojis.plugin.js" + }, + changelog: [ + { + title: "Bugs Squashed", + type: "fixed", + items: [ + "Hides emojis in normal picker once again!", + "Hides emojis in the status emoji picker.", + "Hides extra unrelated buttons in status emoji picker." + ] + } + ], + main: "index.js" +}; +class Dummy { + constructor() {this._config = config;} + start() {} + stop() {} +} + +if (!global.ZeresPluginLibrary) { + BdApi.showConfirmationModal("Library Missing", `The library plugin needed for ${config.name ?? config.info.name} is missing. Please click Download Now to install it.`, { + confirmText: "Download Now", + cancelText: "Cancel", + onConfirm: () => { + require("request").get("https://betterdiscord.app/gh-redirect?id=9", async (err, resp, body) => { + if (err) return require("electron").shell.openExternal("https://betterdiscord.app/Download?id=9"); + if (resp.statusCode === 302) { + require("request").get(resp.headers.location, async (error, response, content) => { + if (error) return require("electron").shell.openExternal("https://betterdiscord.app/Download?id=9"); + await new Promise(r => require("fs").writeFile(require("path").join(BdApi.Plugins.folder, "0PluginLibrary.plugin.js"), content, r)); + }); + } + else { + await new Promise(r => require("fs").writeFile(require("path").join(BdApi.Plugins.folder, "0PluginLibrary.plugin.js"), body, r)); + } + }); + } + }); +} + +module.exports = !global.ZeresPluginLibrary ? Dummy : (([Plugin, Api]) => { + const plugin = (Plugin, Api) => { + const {Patcher, DiscordModules, WebpackModules, Utilities} = Api; + return class HideDisabledEmojis extends Plugin { + async onStart() { + Patcher.after(DiscordModules.EmojiInfo, "isEmojiFiltered", (thisObject, methodArguments, returnValue) => { + return returnValue || DiscordModules.EmojiInfo.isEmojiDisabled(methodArguments[0], methodArguments[1]); + }); + + // BdApi.Webpack.Filters.byStrings("topEmojis", "getDisambiguatedEmojiContext") + let key = ""; + const categoryMemo = WebpackModules.getModule(m => { + if (typeof(m) !== "object") return false; + const keys = Object.keys(m); + if (!keys.length) return false; + for (let k = 0; k < keys.length; k++) { + const val = m[keys[k]]; + if (typeof(val?.toString) !== "function") continue; + const first = val.toString().includes("topEmojis"); + const second = val.toString().includes("getDisambiguatedEmojiContext"); + if (first && second) { + key = keys[k]; + return true; + } + } + return false; + }); + + if (key && categoryMemo) { + Patcher.before(categoryMemo, key, (_, args) => { + // arg 0 = picker intention + // arg 1 = channel object + // arg 2 = guild id + // Create a fake channel object with a null guild id + // This fake object forces the categories to check isEmojiFiltered + if (args[1] == null) { + args[1] = { + getGuildId: () => null + }; + } + }); + } + + const doFiltering = props => { + props.rowCountBySection = props.rowCountBySection.filter((c, i) => c || props.collapsedSections.has(props.sectionDescriptors[i].sectionId)); + props.sectionDescriptors = props.sectionDescriptors.filter(s => s.count || props.collapsedSections.has(s.sectionId)); + + const wasFiltered = props.emojiGrid.filtered; + props.emojiGrid = props.emojiGrid.filter(r => r.length); + if (wasFiltered) props.emojiGrid.filtered = true; // Reassign + }; + + const PickerWrapMemo = WebpackModules.getModule(m => m?.type?.render?.toString?.()?.includes("EMOJI_PICKER_POPOUT")); + Patcher.after(PickerWrapMemo.type, "render", (_, [inputProps], ret) => { + const pickerChild = Utilities.findInTree(ret, m => m?.props?.emojiGrid, {walkable: ["props", "children"]}); + if (!pickerChild?.type?.type) return; + ret.props.children.props.page = "DM Channel"; + if (pickerChild.type.type.__patched) return; + Patcher.before(pickerChild.type, "type", (_, [props]) => { + if (!props.rowCountBySection) return; + if (props.emojiGrid.filtered) return doFiltering(props); + props.emojiGrid.filtered = true; + let row = 0; + for (let s = 0; s < props.sectionDescriptors.length; s++) { + const section = props.sectionDescriptors[s]; + const rowCount = props.rowCountBySection[s]; + const rowEnd = row + rowCount - 1; + let countLeft = 0; + let rowsLeft = 0; + for (let r = row; r <= rowEnd; r++) { + // If it's not disabled or if it's the upload button in status picker + props.emojiGrid[r] = props.emojiGrid[r].filter(e => !e.isDisabled && (e.type !== 1 || inputProps?.pickerIntention !== 1)); + const remaining = props.emojiGrid[r].length; + if (remaining) { + rowsLeft = rowsLeft + 1; + countLeft = countLeft + remaining; + } + } + section.count = countLeft; + props.rowCountBySection[s] = rowsLeft; + + row = rowEnd + 1; + } + + doFiltering(props); + }); + pickerChild.type.type.__patched = true; + }); + } + + onStop() { + Patcher.unpatchAll(); + } + + }; +}; + return plugin(Plugin, Api); +})(global.ZeresPluginLibrary.buildPlugin(config)); +/*@end@*/ \ No newline at end of file diff --git a/BetterDiscord/plugins/PluginRepo.config.json b/BetterDiscord/plugins/PluginRepo.config.json new file mode 100644 index 0000000..fb29be0 --- /dev/null +++ b/BetterDiscord/plugins/PluginRepo.config.json @@ -0,0 +1,16 @@ +{ + "all": { + "cached": "2 7 8 9 11 14 15 17 28 29 30 59 60 61 62 63 64 65 66 67 70 71 73 74 75 76 77 80 81 82 83 85 86 87 88 89 91 92 93 94 95 96 97 98 99 100 101 102 104 105 106 108 109 110 111 120 126 127 131 132 134 137 138 139 146 157 158 159 160 162 171 173 176 179 181 182 183 184 185 186 188 189 190 193 195 196 197 200 201 228 237 238 240 245 262 274 278 287 291 292 293 295 306 312 314 317 318 323 331 344 350 351 352 353 354 356 366 368 377 379 381 382 383 390 395 401 404 420 421 429 438 442 476 479 489 509 518 520 523 525 535 539 547 554 577 579 589 592 593 598 599 606 608 611 614 620 621 627 638 644 645 670 671 679 686 692 693 694 699 708 760 762 772 798 802 805 806 807 819 827 843 845 850 856 859 867 878 881 882 883 930 936 938 953 954 970 971 979 989 990 996 1000 1005 1010 1016 1023", + "filters": { + "updated": true, + "outdated": true, + "downloadable": true + }, + "general": { + "notifyNewEntries": true, + "notifyOutdated": true, + "startDownloaded": true, + "startUpdated": true + } + } +} \ No newline at end of file diff --git a/BetterDiscord/plugins/PluginRepo.plugin.js b/BetterDiscord/plugins/PluginRepo.plugin.js new file mode 100644 index 0000000..09da84f --- /dev/null +++ b/BetterDiscord/plugins/PluginRepo.plugin.js @@ -0,0 +1,1055 @@ +/** + * @name PluginRepo + * @author DevilBro + * @authorId 278543574059057154 + * @version 2.5.4 + * @description Allows you to download all Plugins from BD's Website within Discord + * @invite Jx3TjNS + * @donate https://www.paypal.me/MircoWittrien + * @patreon https://www.patreon.com/MircoWittrien + * @website https://mwittrien.github.io/ + * @source https://github.com/mwittrien/BetterDiscordAddons/tree/master/Plugins/PluginRepo/ + * @updateUrl https://mwittrien.github.io/BetterDiscordAddons/Plugins/PluginRepo/PluginRepo.plugin.js + */ + +module.exports = (_ => { + const changeLog = { + + }; + + return !window.BDFDB_Global || (!window.BDFDB_Global.loaded && !window.BDFDB_Global.started) ? class { + constructor (meta) {for (let key in meta) this[key] = meta[key];} + getName () {return this.name;} + getAuthor () {return this.author;} + getVersion () {return this.version;} + getDescription () {return `The Library Plugin needed for ${this.name} is missing. Open the Plugin Settings to download it. \n\n${this.description}`;} + + downloadLibrary () { + require("request").get("https://mwittrien.github.io/BetterDiscordAddons/Library/0BDFDB.plugin.js", (e, r, b) => { + if (!e && b && r.statusCode == 200) require("fs").writeFile(require("path").join(BdApi.Plugins.folder, "0BDFDB.plugin.js"), b, _ => BdApi.showToast("Finished downloading BDFDB Library", {type: "success"})); + else BdApi.alert("Error", "Could not download BDFDB Library Plugin. Try again later or download it manually from GitHub: https://mwittrien.github.io/downloader/?library"); + }); + } + + load () { + if (!window.BDFDB_Global || !Array.isArray(window.BDFDB_Global.pluginQueue)) window.BDFDB_Global = Object.assign({}, window.BDFDB_Global, {pluginQueue: []}); + if (!window.BDFDB_Global.downloadModal) { + window.BDFDB_Global.downloadModal = true; + BdApi.showConfirmationModal("Library Missing", `The Library Plugin needed for ${this.name} is missing. Please click "Download Now" to install it.`, { + confirmText: "Download Now", + cancelText: "Cancel", + onCancel: _ => {delete window.BDFDB_Global.downloadModal;}, + onConfirm: _ => { + delete window.BDFDB_Global.downloadModal; + this.downloadLibrary(); + } + }); + } + if (!window.BDFDB_Global.pluginQueue.includes(this.name)) window.BDFDB_Global.pluginQueue.push(this.name); + } + start () {this.load();} + stop () {} + getSettingsPanel () { + let template = document.createElement("template"); + template.innerHTML = `
The Library Plugin needed for ${this.name} is missing.\nPlease click Download Now to install it.
`; + template.content.firstElementChild.querySelector("a").addEventListener("click", this.downloadLibrary); + return template.content.firstElementChild; + } + } : (([Plugin, BDFDB]) => { + var _this; + + var list; + + var loading, cachedPlugins, grabbedPlugins, updateInterval; + var searchString, searchTimeout, forcedSort, forcedOrder, showOnlyOutdated; + + var favorites = []; + + const pluginStates = { + INSTALLED: 0, + OUTDATED: 1, + DOWNLOADABLE: 2 + }; + const buttonData = { + INSTALLED: { + backgroundColor: "var(--status-positive)", + icon: "CHECKMARK", + text: "installed" + }, + OUTDATED: { + backgroundColor: "var(--status-danger)", + icon: "CLOSE", + text: "outdated" + }, + DOWNLOADABLE: { + backgroundColor: "var(--bdfdb-blurple)", + icon: "DOWNLOAD", + text: "download" + } + }; + const reverseSorts = [ + "RELEASEDATE", "DOWNLOADS", "LIKES", "FAV" + ]; + const sortKeys = { + NAME: "Name", + AUTHORNAME: "Author", + VERSION: "Version", + DESCRIPTION: "Description", + RELEASEDATE: "Release Date", + STATE: "Update State", + DOWNLOADS: "Downloads", + LIKES: "Likes", + FAV: "Favorites" + }; + const orderKeys = { + ASC: "ascending", + DESC: "descending" + }; + + const pluginRepoIcon = ``; + + const RepoListComponent = class PluginList extends BdApi.React.Component { + componentDidMount() { + list = this; + BDFDB.TimeUtils.timeout(_ => { + forcedSort = null; + forcedOrder = null; + showOnlyOutdated = false; + }, 5000); + } + componentWillUnmount() { + list = null; + } + filterPlugins() { + let plugins = grabbedPlugins.map(plugin => { + if (plugin.failed) return; + const installedPlugin = _this.getInstalledPlugin(plugin); + const state = installedPlugin ? (plugin.version && _this.compareVersions(plugin.version, _this.getString(installedPlugin.plugin && installedPlugin.plugin.version || installedPlugin.version)) ? pluginStates.OUTDATED : pluginStates.INSTALLED) : pluginStates.DOWNLOADABLE; + return Object.assign(plugin, { + search: [plugin.name, plugin.version, plugin.authorname, plugin.description, plugin.tags].flat(10).filter(n => typeof n == "string").join(" ").toUpperCase(), + description: plugin.description || "No Description found", + fav: favorites.includes(plugin.id) && 1, + new: state == pluginStates.DOWNLOADABLE && !cachedPlugins.includes(plugin.id) && 1, + state: state + }); + }).filter(n => n); + if (!this.props.updated) plugins = plugins.filter(plugin => plugin.state != pluginStates.INSTALLED); + if (!this.props.outdated) plugins = plugins.filter(plugin => plugin.state != pluginStates.OUTDATED); + if (!this.props.downloadable) plugins = plugins.filter(plugin => plugin.state != pluginStates.DOWNLOADABLE); + if (searchString) { + let usedSearchString = searchString.toUpperCase(); + let spacelessUsedSearchString = usedSearchString.replace(/\s/g, ""); + plugins = plugins.filter(plugin => plugin.search.indexOf(usedSearchString) > -1 || plugin.search.indexOf(spacelessUsedSearchString) > -1); + } + + BDFDB.ArrayUtils.keySort(plugins, this.props.sortKey.toLowerCase()); + if (this.props.orderKey == "DESC") plugins.reverse(); + if (reverseSorts.includes(this.props.sortKey)) plugins.reverse(); + return plugins; + } + render() { + if (!this.props.tab) this.props.tab = "Plugins"; + + const entries = (!loading.is && grabbedPlugins.length ? this.filterPlugins() : []); + + return BDFDB.ReactUtils.createElement("div", { + className: BDFDB.disCN._repo, + children: [ + BDFDB.ReactUtils.createElement("div", { + className: BDFDB.disCNS._repolistheader + BDFDB.disCN.settingswindowcontentcolumndefault, + children: [ + BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.Flex, { + className: BDFDB.disCN.marginbottom4, + align: BDFDB.LibraryComponents.Flex.Align.CENTER, + children: [ + BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.Flex.Child, { + grow: 1, + shrink: 0, + children: BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.FormComponents.FormTitle, { + tag: BDFDB.LibraryComponents.FormComponents.FormTags.H1, + className: BDFDB.disCN.marginreset, + children: `Plugin Repo — ${loading.is ? 0 : entries.length || 0}/${loading.is ? 0 : grabbedPlugins.length}` + }) + }), + BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.Flex.Child, { + children: BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.SearchBar, { + autoFocus: true, + query: searchString, + onChange: value => { + if (loading.is) return; + BDFDB.TimeUtils.clear(searchTimeout); + searchTimeout = BDFDB.TimeUtils.timeout(_ => { + searchString = value.replace(/[<|>]/g, ""); + BDFDB.ReactUtils.forceUpdate(this); + }, 1000); + }, + onClear: _ => { + if (loading.is) return; + searchString = ""; + BDFDB.ReactUtils.forceUpdate(this); + } + }) + }), + BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.Button, { + size: BDFDB.LibraryComponents.Button.Sizes.TINY, + children: BDFDB.LanguageUtils.LibraryStrings.check_for_updates, + onClick: _ => { + if (loading.is) return; + loading = {is: false, timeout: null, amount: 0}; + _this.loadPlugins(true); + } + }) + ] + }), + BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.Flex, { + className: BDFDB.disCNS.tabbarcontainer + BDFDB.disCN.tabbarcontainerbottom, + align: BDFDB.LibraryComponents.Flex.Align.CENTER, + children: [ + BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.Flex.Child, { + children: BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.TabBar, { + className: BDFDB.disCN.tabbar, + itemClassName: BDFDB.disCN.tabbaritem, + type: BDFDB.LibraryComponents.TabBar.Types.TOP, + selectedItem: this.props.tab, + items: [{value: "Plugins"}, {value: BDFDB.LanguageUtils.LanguageStrings.SETTINGS}], + onItemSelect: value => { + this.props.tab = value; + BDFDB.ReactUtils.forceUpdate(this); + } + }) + }), + BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.Flex.Child, { + children: BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.QuickSelect, { + label: BDFDB.LanguageUtils.LibraryStrings.sort_by + ":", + value: { + label: sortKeys[this.props.sortKey], + value: this.props.sortKey + }, + options: Object.keys(sortKeys).map(key => ({ + label: sortKeys[key], + value: key + })), + onChange: key => { + this.props.sortKey = key; + BDFDB.ReactUtils.forceUpdate(this); + } + }) + }), + BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.Flex.Child, { + children: BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.QuickSelect, { + label: BDFDB.LanguageUtils.LibraryStrings.order + ":", + value: { + label: BDFDB.LanguageUtils.LibraryStrings[orderKeys[this.props.orderKey]], + value: this.props.orderKey + }, + options: Object.keys(orderKeys).map(key => ({ + label: BDFDB.LanguageUtils.LibraryStrings[orderKeys[key]], + value: key + })), + onChange: key => { + this.props.orderKey = key; + BDFDB.ReactUtils.forceUpdate(this); + } + }) + }) + ] + }) + ] + }), + BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.Scrollers.Auto, { + className: BDFDB.disCNS._repolistscroller + BDFDB.disCN.settingswindowcontentcolumndefault, + children: [ + BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.ModalComponents.ModalTabContent, { + tab: "Plugins", + open: this.props.tab == "Plugins", + render: false, + children: loading.is ? BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.Flex, { + direction: BDFDB.LibraryComponents.Flex.Direction.VERTICAL, + justify: BDFDB.LibraryComponents.Flex.Justify.CENTER, + style: {marginTop: "50%"}, + children: [ + BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.SpinnerComponents.Spinner, { + type: BDFDB.LibraryComponents.SpinnerComponents.Types.WANDERING_CUBES + }), + BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.TextElement, { + className: BDFDB.disCN.margintop20, + style: {textAlign: "center"}, + children: `${BDFDB.LanguageUtils.LibraryStringsFormat("loading", "Plugin Repo")} - ${BDFDB.LanguageUtils.LibraryStrings.please_wait}` + }) + ] + }) : BDFDB.ReactUtils.createElement("div", { + className: BDFDB.disCN.discoverycards, + children: entries.map(plugin => BDFDB.ReactUtils.createElement(RepoCardComponent, { + data: plugin + })).filter(n => n) + }) + }), + BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.ModalComponents.ModalTabContent, { + tab: BDFDB.LanguageUtils.LanguageStrings.SETTINGS, + open: this.props.tab == BDFDB.LanguageUtils.LanguageStrings.SETTINGS, + render: false, + children: [ + BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.SettingsPanelList, { + title: "Show following Plugins", + children: Object.keys(_this.defaults.filters).map(key => BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.SettingsSaveItem, { + type: "Switch", + plugin: _this, + keys: ["filters", key], + label: _this.defaults.filters[key].description, + value: _this.settings.filters[key], + onChange: value => { + this.props[key] = _this.settings.filters[key] = value; + BDFDB.ReactUtils.forceUpdate(this); + } + })) + }), + Object.keys(_this.defaults.general).map(key => BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.SettingsSaveItem, { + type: "Switch", + plugin: _this, + keys: ["general", key], + label: _this.defaults.general[key].description, + value: _this.settings.general[key], + onChange: value => { + _this.settings.general[key] = value; + BDFDB.ReactUtils.forceUpdate(this); + } + })) + ].flat(10).filter(n => n) + }) + ] + }) + ] + }); + } + }; + + const RepoCardComponent = class PluginCard extends BdApi.React.Component { + render() { + return BDFDB.ReactUtils.createElement("div", { + className: BDFDB.disCN.discoverycard, + onMouseEnter: _ => { + this.props.hovered = true; + BDFDB.ReactUtils.forceUpdate(this); + }, + children: [ + BDFDB.ReactUtils.createElement("div", { + className: BDFDB.disCN.discoverycardheader, + children: [ + BDFDB.ReactUtils.createElement("div", { + className: BDFDB.disCN.discoverycardcoverwrapper, + children: [ + this.props.data.thumbnailUrl && this.props.hovered && BDFDB.ReactUtils.createElement("img", { + className: BDFDB.disCN.discoverycardcover, + src: this.props.data.thumbnailUrl, + onClick: _ => { + const url = this.props.data.thumbnailUrl; + const img = document.createElement("img"); + img.addEventListener("load", function() { + BDFDB.LibraryModules.ModalUtils.openModal(modalData => BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.ModalComponents.ModalRoot, Object.assign({ + className: BDFDB.disCN.imagemodal + }, modalData, { + size: BDFDB.LibraryComponents.ModalComponents.ModalSize.DYNAMIC, + "aria-label": BDFDB.LanguageUtils.LanguageStrings.IMAGE, + children: BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.ImageModal, { + animated: false, + src: url, + original: url, + width: this.width, + height: this.height, + className: BDFDB.disCN.imagemodalimage, + shouldAnimate: true, + renderLinkComponent: props => BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.Anchor, props) + }) + }), true)); + }); + img.src = url; + } + }), + this.props.data.new && BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.Badges.TextBadge, { + className: BDFDB.disCN.discoverycardcoverbadge, + style: { + borderRadius: 3, + textTransform: "uppercase", + background: BDFDB.DiscordConstants.Colors.YELLOW + }, + text: BDFDB.LanguageUtils.LanguageStrings.NEW + }) + ] + }), + BDFDB.ReactUtils.createElement("div", { + className: BDFDB.disCN.discoverycardiconwrapper, + children: BDFDB.ReactUtils.createElement("div", { + className: BDFDB.disCN.discoverycardicon, + children: BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.SvgIcon, { + nativeClass: true, + iconSVG: `` + }) + }) + }) + ] + }), + BDFDB.ReactUtils.createElement("div", { + className: BDFDB.disCN.discoverycardinfo, + children: [ + BDFDB.ReactUtils.createElement("div", { + className: BDFDB.disCN.discoverycardtitle, + children: [ + BDFDB.ReactUtils.createElement("div", { + className: BDFDB.disCN.discoverycardname, + children: this.props.data.name + }), + this.props.data.latestSourceUrl && + BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.TooltipContainer, { + text: BDFDB.LanguageUtils.LanguageStrings.SCREENSHARE_SOURCE, + children: BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.Clickable, { + className: BDFDB.disCN.discoverycardtitlebutton, + children: BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.SvgIcon, { + nativeClass: true, + width: 16, + height: 16, + name: BDFDB.LibraryComponents.SvgIcon.Names.GITHUB + }) + }), + onClick: _ => BDFDB.DiscordUtils.openLink(this.props.data.latestSourceUrl) + }), + BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.FavButton, { + className: BDFDB.disCN.discoverycardtitlebutton, + isFavorite: this.props.data.fav, + onClick: value => { + this.props.data.fav = value && 1; + if (value) favorites.push(this.props.data.id); + else BDFDB.ArrayUtils.remove(favorites, this.props.data.id, true); + BDFDB.DataUtils.save(BDFDB.ArrayUtils.numSort(favorites).join(" "), _this, "favorites"); + } + }) + ] + }), + BDFDB.ReactUtils.createElement("div", { + className: BDFDB.disCN.discoverycardauthor, + children: `by ${this.props.data.authorname}` + }), + BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.Scrollers.Thin, { + className: BDFDB.disCN.discoverycarddescription, + children: this.props.data.description + }), + BDFDB.ReactUtils.createElement("div", { + className: BDFDB.disCN.discoverycardfooter, + children: [ + BDFDB.ArrayUtils.is(this.props.data.tags) && this.props.data.tags.length && BDFDB.ReactUtils.createElement("div", { + className: BDFDB.disCN.discoverycardtags, + children: this.props.data.tags.map(tag => BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.Badges.TextBadge, { + className: BDFDB.disCN.discoverycardtag, + style: {background: "var(--background-accent)"}, + text: tag + })) + }), + BDFDB.ReactUtils.createElement("div", { + className: BDFDB.disCN.discoverycardcontrols, + children: [ + BDFDB.ReactUtils.createElement("div", { + className: BDFDB.disCN.discoverycardstats, + children: [ + BDFDB.ReactUtils.createElement("div", { + className: BDFDB.disCN.discoverycardstat, + children: [ + BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.SvgIcon, { + className: BDFDB.disCN.discoverycardstaticon, + width: 16, + height: 16, + name: BDFDB.LibraryComponents.SvgIcon.Names.DOWNLOAD + }), + this.props.data.downloads + ] + }), + BDFDB.ReactUtils.createElement("div", { + className: BDFDB.disCN.discoverycardstat, + children: [ + BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.SvgIcon, { + className: BDFDB.disCN.discoverycardstaticon, + width: 16, + height: 16, + name: BDFDB.LibraryComponents.SvgIcon.Names.HEART + }), + this.props.data.likes + ] + }) + ] + }), + BDFDB.ReactUtils.createElement(RepoCardDownloadButtonComponent, { + ...buttonData[(Object.entries(pluginStates).find(n => n[1] == this.props.data.state) || [])[0]], + installed: this.props.data.state == pluginStates.INSTALLED, + outdated: this.props.data.state == pluginStates.OUTDATED, + onDownload: _ => { + if (this.props.downloading) return; + this.props.downloading = true; + let loadingToast = BDFDB.NotificationUtils.toast(`${BDFDB.LanguageUtils.LibraryStringsFormat("loading", this.props.data.name)} - ${BDFDB.LanguageUtils.LibraryStrings.please_wait}`, {timeout: 0, ellipsis: true}); + let autoloadKey = this.props.data.state == pluginStates.OUTDATED ? "startUpdated" : "startDownloaded"; + BDFDB.LibraryRequires.request(this.props.data.rawSourceUrl, (error, response, body) => { + if (error || !body) { + delete this.props.downloading; + loadingToast.close(); + BDFDB.NotificationUtils.toast(BDFDB.LanguageUtils.LibraryStringsFormat("download_fail", `Plugin "${this.props.data.name}"`), {type: "danger"}); + } + else BDFDB.LibraryRequires.fs.writeFile(BDFDB.LibraryRequires.path.join(BDFDB.BDUtils.getPluginsFolder(), this.props.data.rawSourceUrl.split("/").pop()), body, error2 => { + delete this.props.downloading; + loadingToast.close(); + if (error2) BDFDB.NotificationUtils.toast(BDFDB.LanguageUtils.LibraryStringsFormat("save_fail", `Plugin "${this.props.data.name}"`), {type: "danger"}); + else { + BDFDB.NotificationUtils.toast(BDFDB.LanguageUtils.LibraryStringsFormat("save_success", `Plugin "${this.props.data.name}"`), {type: "success"}); + if (_this.settings.general[autoloadKey]) BDFDB.TimeUtils.timeout(_ => { + if (this.props.data.state == pluginStates.INSTALLED && BDFDB.BDUtils.isPluginEnabled(this.props.data.name) == false) { + BDFDB.BDUtils.enablePlugin(this.props.data.name, false); + BDFDB.LogUtils.log(BDFDB.LanguageUtils.LibraryStringsFormat("toast_plugin_started", this.props.data.name), _this); + } + }, 3000); + this.props.data.state = pluginStates.INSTALLED; + BDFDB.ReactUtils.forceUpdate(this); + } + }); + }); + }, + onDelete: _ => { + if (this.props.deleting) return; + this.props.deleting = true; + BDFDB.LibraryRequires.fs.unlinkSync(BDFDB.LibraryRequires.path.join(BDFDB.BDUtils.getPluginsFolder(), this.props.data.rawSourceUrl.split("/").pop())); + delete this.props.deleting; + BDFDB.NotificationUtils.toast(BDFDB.LanguageUtils.LibraryStringsFormat("delete_success", `Plugin "${this.props.data.name}"`)); + this.props.data.state = pluginStates.DOWNLOADABLE; + BDFDB.ReactUtils.forceUpdate(this); + } + }) + ] + }) + ] + }) + ] + }) + ] + }); + } + }; + + const RepoCardDownloadButtonComponent = class PluginCardDownloadButton extends BdApi.React.Component { + render() { + const backgroundColor = this.props.doDelete ? buttonData.OUTDATED.backgroundColor : this.props.doUpdate ? buttonData.INSTALLED.backgroundColor : this.props.backgroundColor; + return BDFDB.ReactUtils.createElement("button", { + className: BDFDB.disCN.discoverycardbutton, + style: {backgroundColor: BDFDB.DiscordConstants.Colors[backgroundColor] || backgroundColor}, + children: [ + BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.SvgIcon, { + className: BDFDB.disCN.discoverycardstaticon, + width: 16, + height: 16, + name: this.props.doDelete ? BDFDB.LibraryComponents.SvgIcon.Names.TRASH : this.props.doUpdate ? BDFDB.LibraryComponents.SvgIcon.Names.DOWNLOAD : BDFDB.LibraryComponents.SvgIcon.Names[this.props.icon] + }), + this.props.doDelete ? BDFDB.LanguageUtils.LanguageStrings.APPLICATION_CONTEXT_MENU_UNINSTALL : this.props.doUpdate ? BDFDB.LanguageUtils.LanguageStrings.GAME_ACTION_BUTTON_UPDATE : (BDFDB.LanguageUtils.LibraryStringsCheck[this.props.text] ? BDFDB.LanguageUtils.LibraryStrings[this.props.text] : BDFDB.LanguageUtils.LanguageStrings[this.props.text]) + ], + onClick: _ => { + if (this.props.doDelete) typeof this.props.onDelete == "function" && this.props.onDelete(); + else if (!this.props.installed) typeof this.props.onDownload == "function" && this.props.onDownload(); + }, + onMouseEnter: this.props.installed ? (_ => { + this.props.doDelete = true; + BDFDB.ReactUtils.forceUpdate(this); + }) : this.props.outdated ? (_ => { + this.props.doUpdate = true; + BDFDB.ReactUtils.forceUpdate(this); + }) : (_ => {}), + onMouseLeave: this.props.installed ? (_ => { + this.props.doDelete = false; + BDFDB.ReactUtils.forceUpdate(this); + }) : this.props.outdated ? (_ => { + this.props.doUpdate = false; + BDFDB.ReactUtils.forceUpdate(this); + }) : (_ => {}) + }); + } + }; + + return class PluginRepo extends Plugin { + onLoad () { + _this = this; + + loading = {is: false, timeout: null, amount: 0}; + + cachedPlugins = []; + grabbedPlugins = []; + searchString = ""; + + this.defaults = { + general: { + notifyOutdated: {value: true, autoload: false, description: "Shows a Notification when one of your Plugins is outdated"}, + notifyNewEntries: {value: true, autoload: false, description: "Shows a Notification when there are new Entries in the Repo"}, + startDownloaded: {value: false, autoload: true, description: "Starts new Plugins after Download"}, + startUpdated: {value: false, autoload: true, description: "Starts updated Plugins after Download"} + }, + filters: { + updated: {value: true, description: "Updated"}, + outdated: {value: true, description: "Outdated"}, + downloadable: {value: true, description: "Downloadable"}, + } + }; + + this.modulePatches = { + before: [ + "SettingsView", + "StandardSidebarView" + ], + componentWillUnmount: [ + "SettingsView" + ] + }; + + } + + onStart () { + this.forceUpdateAll(); + + this.loadPlugins(); + + updateInterval = BDFDB.TimeUtils.interval(_ => this.checkForNewPlugins(), 1000*60*30); + } + + onStop () { + BDFDB.TimeUtils.clear(updateInterval); + BDFDB.TimeUtils.clear(loading.timeout); + + this.forceUpdateAll(); + + BDFDB.DOMUtils.remove(BDFDB.dotCN._pluginreponotice, BDFDB.dotCN._pluginrepoloadingicon); + } + + onSettingsClosed () { + if (this.SettingsUpdated) { + delete this.SettingsUpdated; + this.forceUpdateAll(); + } + } + + forceUpdateAll () { + favorites = BDFDB.DataUtils.load(this, "favorites"); + favorites = (typeof favorites == "string" ? favorites.split(" ") : []).map(n => parseInt(n)).filter(n => !isNaN(n)); + + BDFDB.PatchUtils.forceAllUpdates(this); + } + + onUserSettingsCogContextMenu (e) { + BDFDB.TimeUtils.timeout(_ => { + let [children, index] = BDFDB.ReactUtils.findParent(e.returnvalue, {props: [["label", ["BandagedBD", "BetterDiscord"]]]}); + if (index > -1 && BDFDB.ArrayUtils.is(children[index].props.children)) { + let item = BDFDB.ContextMenuUtils.createItem(BDFDB.LibraryComponents.MenuItems.MenuItem, { + label: "Plugin Repo", + id: BDFDB.ContextMenuUtils.createItemId(this.name, "repo"), + action: _ => BDFDB.LibraryModules.UserSettingsUtils.open("pluginrepo") + }); + if (children[index].props.children.find(n => n && n.props && n.props.id == "themerepo-repo")) children[index].props.children.splice(children[index].props.children.length-1, 0, item); + else children[index].props.children.push(item); + } + }); + } + + processSettingsView (e) { + if (e.node) searchString = ""; + else if (e.component.prototype && !BDFDB.PatchUtils.isPatched(this, e.component.prototype, "getPredicateSections")) { + BDFDB.PatchUtils.patch(this, e.component.prototype, "getPredicateSections", {after: e2 => { + if (BDFDB.ArrayUtils.is(e2.returnValue) && e2.returnValue.findIndex(n => n.section && (n.section.toLowerCase() == "changelog" || n.section == BDFDB.DiscordConstants.UserSettingsSections.CHANGE_LOG || n.section.toLowerCase() == "logout" || n.section == BDFDB.DiscordConstants.UserSettingsSections.LOGOUT))) { + e2.returnValue = e2.returnValue.filter(n => n.section != "pluginrepo"); + let index = e2.returnValue.indexOf(e2.returnValue.find(n => n.section == "themes") || e2.returnValue.find(n => n.section == BDFDB.DiscordConstants.UserSettingsSections.DEVELOPER_OPTIONS) || e2.returnValue.find(n => n.section == BDFDB.DiscordConstants.UserSettingsSections.HYPESQUAD_ONLINE)); + if (index > -1) { + e2.returnValue.splice(index + 1, 0, { + className: "pluginrepo-tab", + section: "pluginrepo", + label: "Plugin Repo", + element: _ => { + let options = Object.assign({}, this.settings.filters); + options.updated = options.updated && !showOnlyOutdated; + options.outdated = options.outdated || showOnlyOutdated; + options.downloadable = options.downloadable && !showOnlyOutdated; + options.sortKey = forcedSort || Object.keys(sortKeys)[0]; + options.orderKey = forcedOrder || Object.keys(orderKeys)[0]; + + return BDFDB.ReactUtils.createElement(RepoListComponent, options, true); + } + }); + if (!e2.returnValue.find(n => n.section == "plugins")) e2.returnValue.splice(index + 1, 0, {section: "DIVIDER"}); + } + } + }}); + } + } + + processStandardSidebarView (e) { + if (e.instance.props && e.instance.props.section == "pluginrepo") e.instance.props.contentType = "custom"; + } + + loadPlugins (forceBanner) { + BDFDB.DOMUtils.remove(BDFDB.dotCN._pluginrepoloadingicon); + cachedPlugins = BDFDB.DataUtils.load(this, "cached"); + cachedPlugins = (typeof cachedPlugins == "string" ? cachedPlugins.split(" ") : []).map(n => parseInt(n)).filter(n => !isNaN(n)); + + let loadingIcon; + let newEntries = 0, outdatedEntries = 0, checkIndex = 0, checksRunning = 0, callbackCalled = false; + + const checkPlugin = _ => { + if (checksRunning > 20) return; + else if (grabbedPlugins.every(p => p.loaded || (!p.latestSourceUrl && !p.latest_source_url)) || !this.started || !loading.is) { + if (!callbackCalled) { + callbackCalled = true; + if (!this.started) return BDFDB.TimeUtils.clear(loading.timeout); + BDFDB.TimeUtils.clear(loading.timeout); + BDFDB.DOMUtils.remove(loadingIcon, BDFDB.dotCN._pluginrepoloadingicon); + loading = {is: false, timeout: null, amount: loading.amount}; + + BDFDB.LogUtils.log("Finished fetching Plugins", this); + BDFDB.ReactUtils.forceUpdate(list); + + if ((this.settings.general.notifyOutdated || forceBanner) && outdatedEntries > 0) { + let notice = document.querySelector(BDFDB.dotCN._pluginrepooutdatednotice); + if (notice) notice.close(); + BDFDB.NotificationUtils.notice(this.labels.notice_outdated_plugins.replace("{{var0}}", outdatedEntries), { + type: "danger", + className: BDFDB.disCNS._pluginreponotice + BDFDB.disCN._pluginrepooutdatednotice, + customIcon: pluginRepoIcon.replace(/COLOR_[0-9]+/gi, "currentColor"), + buttons: [{ + contents: BDFDB.LanguageUtils.LanguageStrings.OPEN, + close: true, + onClick: _ => { + showOnlyOutdated = true; + BDFDB.LibraryModules.UserSettingsUtils.open("pluginrepo"); + } + }] + }); + } + + if (this.settings.general.notifyNewEntries && newEntries > 0) { + let notice = document.querySelector(BDFDB.dotCN._pluginreponewentriesnotice); + if (notice) notice.close(); + BDFDB.NotificationUtils.notice(this.labels.notice_new_plugins.replace("{{var0}}", newEntries), { + type: "success", + className: BDFDB.disCNS._pluginreponotice + BDFDB.disCN._pluginreponewentriesnotice, + customIcon: pluginRepoIcon.replace(/COLOR_[0-9]+/gi, "currentColor"), + buttons: [{ + contents: BDFDB.LanguageUtils.LanguageStrings.OPEN, + close: true, + onClick: _ => { + forcedSort = "RELEASEDATE"; + forcedOrder = "ASC"; + BDFDB.LibraryModules.UserSettingsUtils.open("pluginrepo"); + } + }] + }); + } + } + return; + } + else if (checkIndex > grabbedPlugins.length) return; + + const plugin = grabbedPlugins[checkIndex++]; + if (!plugin || (!plugin.latestSourceUrl && !plugin.latest_source_url)) checkPlugin(); + else { + checksRunning++; + plugin.releasedate = new Date(plugin.releaseDate || plugin.release_date || 0).getTime(); + plugin.latestSourceUrl = plugin.latestSourceUrl || plugin.latest_source_url; + plugin.rawSourceUrl = plugin.latestSourceUrl.replace("https://github.com/", "https://raw.githubusercontent.com/").replace(/\/blob\/(.{32,})/i, "/$1"); + plugin.thumbnailUrl = plugin.thumbnailUrl || plugin.thumbnail_url; + plugin.thumbnailUrl = plugin.thumbnailUrl ? (plugin.thumbnailUrl.startsWith("https://") ? plugin.thumbnailUrl : `https://betterdiscord.app${plugin.thumbnailUrl}`) : ""; + delete plugin.release_date; + delete plugin.latest_source_url; + delete plugin.thumbnail_url; + BDFDB.LibraryRequires.request(plugin.rawSourceUrl, (error, response, body) => { + if (error || !body || body.indexOf("404: Not Found") == 0) plugin.failed = true; + else { + const META = body.split("*/")[0]; + plugin.name = BDFDB.StringUtils.upperCaseFirstChar((/@name\s+([^\t^\r^\n]+)|\/\/\**META.*["']name["']\s*:\s*["'](.+?)["']/i.exec(META) || []).filter(n => n)[1] || plugin.name || ""); + plugin.authorname = (/@author\s+(.+)|\/\/\**META.*["']author["']\s*:\s*["'](.+?)["']/i.exec(META) || []).filter(n => n)[1] || plugin.author.display_name || plugin.author; + const version = (/@version\s+(.+)|\/\/\**META.*["']version["']\s*:\s*["'](.+?)["']/i.exec(META) || []).filter(n => n)[1]; + if (version) { + plugin.version = version; + const installedPlugin = this.getInstalledPlugin(plugin); + if (installedPlugin && this.compareVersions(version, this.getString(installedPlugin.plugin && installedPlugin.plugin.version || installedPlugin.version))) outdatedEntries++; + } + if (!cachedPlugins.includes(plugin.id)) newEntries++; + } + + plugin.loaded = true; + + let loadingTooltip = document.querySelector(BDFDB.dotCN._pluginrepoloadingtooltip); + if (loadingTooltip) loadingTooltip.update(this.getLoadingTooltipText()); + + checksRunning--; + checkPlugin(); + }); + } + }; + + BDFDB.TimeUtils.timeout(_ => BDFDB.LibraryRequires.request("https://api.betterdiscord.app/v1/store/plugins", {bdVersion: true}, (error, response, body) => { + if (!error && body && response.statusCode == 200) try { + grabbedPlugins = BDFDB.ArrayUtils.keySort(JSON.parse(body).filter(n => n), "name"); + BDFDB.DataUtils.save(BDFDB.ArrayUtils.numSort(grabbedPlugins.map(n => n.id)).join(" "), this, "cached"); + + loading = {is: true, timeout: BDFDB.TimeUtils.timeout(_ => { + BDFDB.TimeUtils.clear(loading.timeout); + if (this.started) { + if (loading.is && loading.amount < 4) BDFDB.TimeUtils.timeout(_ => this.loadPlugins(), 10000); + loading = {is: false, timeout: null, amount: loading.amount}; + } + }, 1200000), amount: loading.amount + 1}; + + loadingIcon = BDFDB.DOMUtils.create(pluginRepoIcon.replace(/COLOR_1/gi, "var(--bdfdb-blurple)").replace(/COLOR_2/gi, "#72767d")); + BDFDB.DOMUtils.addClass(loadingIcon, BDFDB.disCN._pluginrepoloadingicon); + loadingIcon.addEventListener("mouseenter", _ => { + BDFDB.TooltipUtils.create(loadingIcon, this.getLoadingTooltipText(), { + type: "left", + className: BDFDB.disCN._pluginrepoloadingtooltip, + delay: 500, + style: "max-width: unset;" + }); + }); + BDFDB.PluginUtils.addLoadingIcon(loadingIcon); + + BDFDB.ReactUtils.forceUpdate(list); + + for (let i = 0; i <= 20; i++) checkPlugin(); + } + catch (err) {BDFDB.NotificationUtils.toast("Failed to load Plugin Store", {type: "danger"});} + if (response && response.statusCode == 403) BDFDB.NotificationUtils.toast("Failed to fetch Plugin Store from the Website Api due to DDoS Protection", {type: "danger"}); + else if (response && response.statusCode == 404) BDFDB.NotificationUtils.toast("Failed to fetch Plugin Store from the Website Api due to Connection Issue", {type: "danger"}); + }), 10000); + } + + getLoadingTooltipText () { + return BDFDB.LanguageUtils.LibraryStringsFormat("loading", `Plugin Repo - [${grabbedPlugins.filter(n => n.loaded).length}/${grabbedPlugins.length}]`); + } + + getString (obj) { + let string = ""; + if (typeof obj == "string") string = obj; + else if (obj && obj.props) { + if (typeof obj.props.children == "string") string = obj.props.children; + else if (Array.isArray(obj.props.children)) for (let c of obj.props.children) string += typeof c == "string" ? c : this.getString(c); + } + return string; + } + + compareVersions (v1, v2) { + return !(v1 == v2 || !BDFDB.NumberUtils.compareVersions(v1, v2)); + } + + getInstalledPlugin (plugin) { + if (!plugin || typeof plugin.authorname != "string") return; + const iPlugin = BDFDB.BDUtils.getPlugin(plugin.name, false, true); + if (iPlugin && (plugin.authorname.toUpperCase().indexOf(this.getString(iPlugin.author).toUpperCase()) > -1 || this.getString(iPlugin.author).toUpperCase().indexOf(plugin.authorname.toUpperCase()) > -1)) return iPlugin; + else if (plugin.rawSourceUrl && window.BdApi && BdApi.Plugins && typeof BdApi.Plugins.getAll == "function") { + const filename = plugin.rawSourceUrl.split("/").pop(); + for (let p of BdApi.Plugins.getAll()) if (p.filename == filename && (plugin.authorname.toUpperCase().indexOf(this.getString(p.author).toUpperCase()) > -1 || this.getString(p.author).toUpperCase().indexOf(plugin.authorname.toUpperCase()) > -1)) return p; + } + } + + checkForNewPlugins () { + BDFDB.LibraryRequires.request("https://api.betterdiscord.app/v1/store/plugins", {bdVersion: true}, (error, response, body) => { + if (!error && body) try { + if (JSON.parse(body).filter(n => n).length != grabbedPlugins.length) { + loading = {is: false, timeout: null, amount: 0}; + this.loadPlugins(); + } + } + catch (err) {BDFDB.NotificationUtils.toast("Failed to load Plugin Store", {type: "danger"});} + }); + } + + setLabelsByLanguage () { + switch (BDFDB.LanguageUtils.getLanguage().id) { + case "bg": // Bulgarian + return { + list: "Списък", + notice_failed_plugins: "Някои Plugins [{{var0}}] не можаха да бъдат заредени", + notice_new_plugins: "Новите Plugins [{{var0}}] бяха добавени към Plugin Repo", + notice_outdated_plugins: "Някои Plugins [{{var0}}] са остарели" + }; + case "da": // Danish + return { + list: "Liste", + notice_failed_plugins: "Nogle Plugins [{{var0}}] kunne ikke indlæses", + notice_new_plugins: "Nye Plugins [{{var0}}] er blevet føjet til Plugin Repo", + notice_outdated_plugins: "Nogle Plugins [{{var0}}] er forældede" + }; + case "de": // German + return { + list: "Liste", + notice_failed_plugins: "Einige Plugins [{{var0}}] konnten nicht geladen werden", + notice_new_plugins: "Neue Plugins [{{var0}}] wurden zur Plugin Repo hinzugefügt", + notice_outdated_plugins: "Einige Plugins [{{var0}}] sind veraltet" + }; + case "el": // Greek + return { + list: "Λίστα", + notice_failed_plugins: "Δεν ήταν δυνατή η φόρτωση ορισμένων Πρόσθετων [{{var0}}] ", + notice_new_plugins: "Προστέθηκαν νέα Πρόσθετα [{{var0}}] στο Αποθετήριο Προσθέτων", + notice_outdated_plugins: "Ορισμένα Πρόσθετα [{{var0}}] είναι παλαιά" + }; + case "es": // Spanish + return { + list: "Lista", + notice_failed_plugins: "Algunos Plugins [{{var0}}] no se pudieron cargar", + notice_new_plugins: "Se han agregado nuevos Plugins [{{var0}}] a Plugin Repo", + notice_outdated_plugins: "Algunas Plugins [{{var0}}] están desactualizadas" + }; + case "fi": // Finnish + return { + list: "Lista", + notice_failed_plugins: "Joitain kohdetta Plugins [{{var0}}] ei voitu ladata", + notice_new_plugins: "Uusi Plugins [{{var0}}] on lisätty Plugin Repo", + notice_outdated_plugins: "Jotkut Plugins [{{var0}}] ovat vanhentuneita" + }; + case "fr": // French + return { + list: "Liste", + notice_failed_plugins: "Certains Plugins [{{var0}}] n'ont pas pu être chargés", + notice_new_plugins: "De nouveaux Plugins [{{var0}}] ont été ajoutés à Plugin Repo", + notice_outdated_plugins: "Certains Plugins [{{var0}}] sont obsolètes" + }; + case "hr": // Croatian + return { + list: "Popis", + notice_failed_plugins: "Neke datoteke Plugins [{{var0}}] nije moguće učitati", + notice_new_plugins: "Novi Plugins [{{var0}}] dodani su u Plugin Repo", + notice_outdated_plugins: "Neki su Plugins [{{var0}}] zastarjeli" + }; + case "hu": // Hungarian + return { + list: "Lista", + notice_failed_plugins: "Néhány Plugins [{{var0}}] nem sikerült betölteni", + notice_new_plugins: "Új Plugins [{{var0}}] hozzáadva a következőhöz: Plugin Repo", + notice_outdated_plugins: "Néhány Plugins [{{var0}}] elavult" + }; + case "it": // Italian + return { + list: "Elenco", + notice_failed_plugins: "Impossibile caricare alcuni Plugins [{{var0}}] ", + notice_new_plugins: "Il nuovo Plugins [{{var0}}] è stato aggiunto a Plugin Repo", + notice_outdated_plugins: "Alcuni Plugins [{{var0}}] non sono aggiornati" + }; + case "ja": // Japanese + return { + list: "リスト", + notice_failed_plugins: "一部の Plugins [{{var0}}] を読み込めませんでした", + notice_new_plugins: "新しい Plugins [{{var0}}] が Plugin Repo に追加されました", + notice_outdated_plugins: "一部の Plugins [{{var0}}] は古くなっています" + }; + case "ko": // Korean + return { + list: "명부", + notice_failed_plugins: "일부 Plugins [{{var0}}] 을 (를)로드 할 수 없습니다.", + notice_new_plugins: "새 Plugins [{{var0}}] 이 Plugin Repo 에 추가되었습니다.", + notice_outdated_plugins: "일부 Plugins [{{var0}}] 이 오래되었습니다." + }; + case "lt": // Lithuanian + return { + list: "Sąrašas", + notice_failed_plugins: "Kai kurių Plugins [{{var0}}] nepavyko įkelti", + notice_new_plugins: "Naujas Plugins [{{var0}}] pridėtas prie Plugin Repo", + notice_outdated_plugins: "Kai kurie Plugins [{{var0}}] yra pasenę" + }; + case "nl": // Dutch + return { + list: "Lijst", + notice_failed_plugins: "Sommige Plugins [{{var0}}] konden niet worden geladen", + notice_new_plugins: "Nieuwe Plugins [{{var0}}] zijn toegevoegd aan de Plugin Repo", + notice_outdated_plugins: "Sommige Plugins [{{var0}}] zijn verouderd" + }; + case "no": // Norwegian + return { + list: "Liste", + notice_failed_plugins: "Noen Plugins [{{var0}}] kunne ikke lastes inn", + notice_new_plugins: "Nye Plugins [{{var0}}] er lagt til i Plugin Repo", + notice_outdated_plugins: "Noen Plugins [{{var0}}] er utdaterte" + }; + case "pl": // Polish + return { + list: "Lista", + notice_failed_plugins: "Nie można załadować niektórych Plugins [{{var0}}] ", + notice_new_plugins: "Nowe Plugins [{{var0}}] zostały dodane do Plugin Repo", + notice_outdated_plugins: "Niektóre Plugins [{{var0}}] są nieaktualne" + }; + case "pt-BR": // Portuguese (Brazil) + return { + list: "Lista", + notice_failed_plugins: "Algum Plugins [{{var0}}] não pôde ser carregado", + notice_new_plugins: "Novo Plugins [{{var0}}] foi adicionado ao Plugin Repo", + notice_outdated_plugins: "Alguns Plugins [{{var0}}] estão desatualizados" + }; + case "ro": // Romanian + return { + list: "Listă", + notice_failed_plugins: "Unele Plugins [{{var0}}] nu au putut fi încărcate", + notice_new_plugins: "Plugins [{{var0}}] nou au fost adăugate la Plugin Repo", + notice_outdated_plugins: "Unele Plugins [{{var0}}] sunt învechite" + }; + case "ru": // Russian + return { + list: "Список", + notice_failed_plugins: "Не удалось загрузить некоторые Plugins [{{var0}}] ", + notice_new_plugins: "Новые Plugins [{{var0}}] добавлены в Plugin Repo", + notice_outdated_plugins: "Некоторые Plugins [{{var0}}] устарели" + }; + case "sv": // Swedish + return { + list: "Lista", + notice_failed_plugins: "Vissa Plugins [{{var0}}] kunde inte laddas", + notice_new_plugins: "Nya Plugins [{{var0}}] har lagts till i Plugin Repo", + notice_outdated_plugins: "Vissa Plugins [{{var0}}] är föråldrade" + }; + case "th": // Thai + return { + list: "รายการ", + notice_failed_plugins: "ไม่สามารถโหลด Plugins [{{var0}}] บางรายการได้", + notice_new_plugins: "เพิ่ม Plugins [{{var0}}] ใหม่ใน Plugin Repo แล้ว", + notice_outdated_plugins: "Plugins [{{var0}}] บางรายการล้าสมัย" + }; + case "tr": // Turkish + return { + list: "Liste", + notice_failed_plugins: "Bazı Plugins [{{var0}}] yüklenemedi", + notice_new_plugins: "Yeni Plugins [{{var0}}], Plugin Repo 'ye eklendi", + notice_outdated_plugins: "Bazı Plugins [{{var0}}] güncel değil" + }; + case "uk": // Ukrainian + return { + list: "Список", + notice_failed_plugins: "Деякі Plugins [{{var0}}] не вдалося завантажити", + notice_new_plugins: "Нові Plugins [{{var0}}] були додані до Plugin Repo", + notice_outdated_plugins: "Деякі Plugins [{{var0}}] застарілі" + }; + case "vi": // Vietnamese + return { + list: "Danh sách", + notice_failed_plugins: "Không thể tải một số Plugins [{{var0}}] ", + notice_new_plugins: "Plugins [{{var0}}] mới đã được thêm vào Plugin Repo", + notice_outdated_plugins: "Một số Plugins [{{var0}}] đã lỗi thời" + }; + case "zh-CN": // Chinese (China) + return { + list: "清单", + notice_failed_plugins: "某些 Plugins [{{var0}}] 无法加载", + notice_new_plugins: "新的 Plugins [{{var0}}] 已添加到 Plugin Repo", + notice_outdated_plugins: "一些 Plugins [{{var0}}] 已过时" + }; + case "zh-TW": // Chinese (Taiwan) + return { + list: "清單", + notice_failed_plugins: "某些 Plugins [{{var0}}] 無法加載", + notice_new_plugins: "新的 Plugins [{{var0}}] 已添加到 Plugin Repo", + notice_outdated_plugins: "一些 Plugins [{{var0}}] 已過時" + }; + default: // English + return { + list: "List", + notice_failed_plugins: "Some Plugins [{{var0}}] could not be loaded", + notice_new_plugins: "New Plugins [{{var0}}] have been added to the Plugin Repo", + notice_outdated_plugins: "Some Plugins [{{var0}}] are outdated" + }; + } + } + }; + })(window.BDFDB_Global.PluginUtils.buildPlugin(changeLog)); +})(); diff --git a/BetterDiscord/plugins/SplitLargeMessages.config.json b/BetterDiscord/plugins/SplitLargeMessages.config.json new file mode 100644 index 0000000..c2bf06b --- /dev/null +++ b/BetterDiscord/plugins/SplitLargeMessages.config.json @@ -0,0 +1,10 @@ +{ + "all": { + "amounts": { + "splitCounter": 0 + }, + "general": { + "byNewlines": false + } + } +} \ No newline at end of file diff --git a/BetterDiscord/plugins/TeX.plugin.js b/BetterDiscord/plugins/TeX.plugin.js new file mode 100644 index 0000000..47015d9 --- /dev/null +++ b/BetterDiscord/plugins/TeX.plugin.js @@ -0,0 +1,21609 @@ +/** + * @name TeX + * @version 1.0.1 + * + * @author Lucario ☉ ∝ x²#7902 + * @authorId 438469378418409483 + * @description Create and send TeX math equations. Uses the KaTeX rendering engine. + * Required dependency: ZeresPluginLibrary + * + * DM the author or create an issue for support. + * + * @updateUrl https://raw.githubusercontent.com/Mega-Mewthree/BetterDiscordPlugins/master/Plugins/TeX/TeX.plugin.js + * @invite ZYND2Xd + * @authorLink https://github.com/Mega-Mewthree/BetterDiscordPlugins/tree/master/Plugins/TeX + * @source https://github.com/Mega-Mewthree/BetterDiscordPlugins/tree/master/Plugins/TeX + * @website https://github.com/Mega-Mewthree/BetterDiscordPlugins/tree/master/Plugins/TeX + * @donate https://www.buymeacoffee.com/lucariodev + */ +/* +MIT License + +Copyright (c) 2021-2022 Mega-Mewthree + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +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. +*/ + +// Updated January 26th, 2022. + +// Thanks https://github.com/Viibrant for finding a better way to upload the +// image that is more resistant to breaking with updates. + +(function webpackUniversalModuleDefinition(root, factory) { + if(typeof exports === 'object' && typeof module === 'object') + module.exports = factory(); + else if(typeof define === 'function' && define.amd) + define([], factory); + else if(typeof exports === 'object') + exports["katex"] = factory(); + else + root["katex"] = factory(); +})((typeof self !== 'undefined' ? self : this), function() { +return /******/ (function() { // webpackBootstrap +/******/ "use strict"; +/******/ // The require scope +/******/ var __webpack_require__ = {}; +/******/ +/************************************************************************/ +/******/ /* webpack/runtime/define property getters */ +/******/ !function() { +/******/ // define getter functions for harmony exports +/******/ __webpack_require__.d = function(exports, definition) { +/******/ for(var key in definition) { +/******/ if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) { +/******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] }); +/******/ } +/******/ } +/******/ }; +/******/ }(); +/******/ +/******/ /* webpack/runtime/hasOwnProperty shorthand */ +/******/ !function() { +/******/ __webpack_require__.o = function(obj, prop) { return Object.prototype.hasOwnProperty.call(obj, prop); } +/******/ }(); +/******/ +/************************************************************************/ +var __webpack_exports__ = {}; + +// EXPORTS +__webpack_require__.d(__webpack_exports__, { + "default": function() { return /* binding */ katex_webpack; } +}); + +;// CONCATENATED MODULE: ./src/ParseError.js + + +/** + * This is the ParseError class, which is the main error thrown by KaTeX + * functions when something has gone wrong. This is used to distinguish internal + * errors from errors in the expression that the user provided. + * + * If possible, a caller should provide a Token or ParseNode with information + * about where in the source string the problem occurred. + */ +var ParseError = // Error position based on passed-in Token or ParseNode. +function ParseError(message, // The error message +token) { + this.position = void 0; + var error = "KaTeX parse error: " + message; + var start; + var loc = token && token.loc; + + if (loc && loc.start <= loc.end) { + // If we have the input and a position, make the error a bit fancier + // Get the input + var input = loc.lexer.input; // Prepend some information + + start = loc.start; + var end = loc.end; + + if (start === input.length) { + error += " at end of input: "; + } else { + error += " at position " + (start + 1) + ": "; + } // Underline token in question using combining underscores + + + var underlined = input.slice(start, end).replace(/[^]/g, "$&\u0332"); // Extract some context from the input and add it to the error + + var left; + + if (start > 15) { + left = "…" + input.slice(start - 15, start); + } else { + left = input.slice(0, start); + } + + var right; + + if (end + 15 < input.length) { + right = input.slice(end, end + 15) + "…"; + } else { + right = input.slice(end); + } + + error += left + underlined + right; + } // Some hackery to make ParseError a prototype of Error + // See http://stackoverflow.com/a/8460753 + + + var self = new Error(error); + self.name = "ParseError"; // $FlowFixMe + + self.__proto__ = ParseError.prototype; // $FlowFixMe + + self.position = start; + return self; +}; // $FlowFixMe More hackery + + +ParseError.prototype.__proto__ = Error.prototype; +/* harmony default export */ var src_ParseError = (ParseError); +;// CONCATENATED MODULE: ./src/utils.js +/** + * This file contains a list of utility functions which are useful in other + * files. + */ + +/** + * Return whether an element is contained in a list + */ +var contains = function contains(list, elem) { + return list.indexOf(elem) !== -1; +}; +/** + * Provide a default value if a setting is undefined + * NOTE: Couldn't use `T` as the output type due to facebook/flow#5022. + */ + + +var deflt = function deflt(setting, defaultIfUndefined) { + return setting === undefined ? defaultIfUndefined : setting; +}; // hyphenate and escape adapted from Facebook's React under Apache 2 license + + +var uppercase = /([A-Z])/g; + +var hyphenate = function hyphenate(str) { + return str.replace(uppercase, "-$1").toLowerCase(); +}; + +var ESCAPE_LOOKUP = { + "&": "&", + ">": ">", + "<": "<", + "\"": """, + "'": "'" +}; +var ESCAPE_REGEX = /[&><"']/g; +/** + * Escapes text to prevent scripting attacks. + */ + +function utils_escape(text) { + return String(text).replace(ESCAPE_REGEX, function (match) { + return ESCAPE_LOOKUP[match]; + }); +} +/** + * Sometimes we want to pull out the innermost element of a group. In most + * cases, this will just be the group itself, but when ordgroups and colors have + * a single element, we want to pull that out. + */ + + +var getBaseElem = function getBaseElem(group) { + if (group.type === "ordgroup") { + if (group.body.length === 1) { + return getBaseElem(group.body[0]); + } else { + return group; + } + } else if (group.type === "color") { + if (group.body.length === 1) { + return getBaseElem(group.body[0]); + } else { + return group; + } + } else if (group.type === "font") { + return getBaseElem(group.body); + } else { + return group; + } +}; +/** + * TeXbook algorithms often reference "character boxes", which are simply groups + * with a single character in them. To decide if something is a character box, + * we find its innermost group, and see if it is a single character. + */ + + +var isCharacterBox = function isCharacterBox(group) { + var baseElem = getBaseElem(group); // These are all they types of groups which hold single characters + + return baseElem.type === "mathord" || baseElem.type === "textord" || baseElem.type === "atom"; +}; + +var assert = function assert(value) { + if (!value) { + throw new Error('Expected non-null, but got ' + String(value)); + } + + return value; +}; +/** + * Return the protocol of a URL, or "_relative" if the URL does not specify a + * protocol (and thus is relative). + */ + +var protocolFromUrl = function protocolFromUrl(url) { + var protocol = /^\s*([^\\/#]*?)(?::|�*58|�*3a)/i.exec(url); + return protocol != null ? protocol[1] : "_relative"; +}; +/* harmony default export */ var utils = ({ + contains: contains, + deflt: deflt, + escape: utils_escape, + hyphenate: hyphenate, + getBaseElem: getBaseElem, + isCharacterBox: isCharacterBox, + protocolFromUrl: protocolFromUrl +}); +;// CONCATENATED MODULE: ./src/Settings.js +/* eslint no-console:0 */ + +/** + * This is a module for storing settings passed into KaTeX. It correctly handles + * default settings. + */ + + + + +/** + * The main Settings object + * + * The current options stored are: + * - displayMode: Whether the expression should be typeset as inline math + * (false, the default), meaning that the math starts in + * \textstyle and is placed in an inline-block); or as display + * math (true), meaning that the math starts in \displaystyle + * and is placed in a block with vertical margin. + */ +var Settings = /*#__PURE__*/function () { + function Settings(options) { + this.displayMode = void 0; + this.output = void 0; + this.leqno = void 0; + this.fleqn = void 0; + this.throwOnError = void 0; + this.errorColor = void 0; + this.macros = void 0; + this.minRuleThickness = void 0; + this.colorIsTextColor = void 0; + this.strict = void 0; + this.trust = void 0; + this.maxSize = void 0; + this.maxExpand = void 0; + this.globalGroup = void 0; + // allow null options + options = options || {}; + this.displayMode = utils.deflt(options.displayMode, false); + this.output = utils.deflt(options.output, "htmlAndMathml"); + this.leqno = utils.deflt(options.leqno, false); + this.fleqn = utils.deflt(options.fleqn, false); + this.throwOnError = utils.deflt(options.throwOnError, true); + this.errorColor = utils.deflt(options.errorColor, "#cc0000"); + this.macros = options.macros || {}; + this.minRuleThickness = Math.max(0, utils.deflt(options.minRuleThickness, 0)); + this.colorIsTextColor = utils.deflt(options.colorIsTextColor, false); + this.strict = utils.deflt(options.strict, "warn"); + this.trust = utils.deflt(options.trust, false); + this.maxSize = Math.max(0, utils.deflt(options.maxSize, Infinity)); + this.maxExpand = Math.max(0, utils.deflt(options.maxExpand, 1000)); + this.globalGroup = utils.deflt(options.globalGroup, false); + } + /** + * Report nonstrict (non-LaTeX-compatible) input. + * Can safely not be called if `this.strict` is false in JavaScript. + */ + + + var _proto = Settings.prototype; + + _proto.reportNonstrict = function reportNonstrict(errorCode, errorMsg, token) { + var strict = this.strict; + + if (typeof strict === "function") { + // Allow return value of strict function to be boolean or string + // (or null/undefined, meaning no further processing). + strict = strict(errorCode, errorMsg, token); + } + + if (!strict || strict === "ignore") { + return; + } else if (strict === true || strict === "error") { + throw new src_ParseError("LaTeX-incompatible input and strict mode is set to 'error': " + (errorMsg + " [" + errorCode + "]"), token); + } else if (strict === "warn") { + typeof console !== "undefined" && console.warn("LaTeX-incompatible input and strict mode is set to 'warn': " + (errorMsg + " [" + errorCode + "]")); + } else { + // won't happen in type-safe code + typeof console !== "undefined" && console.warn("LaTeX-incompatible input and strict mode is set to " + ("unrecognized '" + strict + "': " + errorMsg + " [" + errorCode + "]")); + } + } + /** + * Check whether to apply strict (LaTeX-adhering) behavior for unusual + * input (like `\\`). Unlike `nonstrict`, will not throw an error; + * instead, "error" translates to a return value of `true`, while "ignore" + * translates to a return value of `false`. May still print a warning: + * "warn" prints a warning and returns `false`. + * This is for the second category of `errorCode`s listed in the README. + */ + ; + + _proto.useStrictBehavior = function useStrictBehavior(errorCode, errorMsg, token) { + var strict = this.strict; + + if (typeof strict === "function") { + // Allow return value of strict function to be boolean or string + // (or null/undefined, meaning no further processing). + // But catch any exceptions thrown by function, treating them + // like "error". + try { + strict = strict(errorCode, errorMsg, token); + } catch (error) { + strict = "error"; + } + } + + if (!strict || strict === "ignore") { + return false; + } else if (strict === true || strict === "error") { + return true; + } else if (strict === "warn") { + typeof console !== "undefined" && console.warn("LaTeX-incompatible input and strict mode is set to 'warn': " + (errorMsg + " [" + errorCode + "]")); + return false; + } else { + // won't happen in type-safe code + typeof console !== "undefined" && console.warn("LaTeX-incompatible input and strict mode is set to " + ("unrecognized '" + strict + "': " + errorMsg + " [" + errorCode + "]")); + return false; + } + } + /** + * Check whether to test potentially dangerous input, and return + * `true` (trusted) or `false` (untrusted). The sole argument `context` + * should be an object with `command` field specifying the relevant LaTeX + * command (as a string starting with `\`), and any other arguments, etc. + * If `context` has a `url` field, a `protocol` field will automatically + * get added by this function (changing the specified object). + */ + ; + + _proto.isTrusted = function isTrusted(context) { + if (context.url && !context.protocol) { + context.protocol = utils.protocolFromUrl(context.url); + } + + var trust = typeof this.trust === "function" ? this.trust(context) : this.trust; + return Boolean(trust); + }; + + return Settings; +}(); + + +;// CONCATENATED MODULE: ./src/Style.js +/** + * This file contains information and classes for the various kinds of styles + * used in TeX. It provides a generic `Style` class, which holds information + * about a specific style. It then provides instances of all the different kinds + * of styles possible, and provides functions to move between them and get + * information about them. + */ + +/** + * The main style class. Contains a unique id for the style, a size (which is + * the same for cramped and uncramped version of a style), and a cramped flag. + */ +var Style = /*#__PURE__*/function () { + function Style(id, size, cramped) { + this.id = void 0; + this.size = void 0; + this.cramped = void 0; + this.id = id; + this.size = size; + this.cramped = cramped; + } + /** + * Get the style of a superscript given a base in the current style. + */ + + + var _proto = Style.prototype; + + _proto.sup = function sup() { + return styles[_sup[this.id]]; + } + /** + * Get the style of a subscript given a base in the current style. + */ + ; + + _proto.sub = function sub() { + return styles[_sub[this.id]]; + } + /** + * Get the style of a fraction numerator given the fraction in the current + * style. + */ + ; + + _proto.fracNum = function fracNum() { + return styles[_fracNum[this.id]]; + } + /** + * Get the style of a fraction denominator given the fraction in the current + * style. + */ + ; + + _proto.fracDen = function fracDen() { + return styles[_fracDen[this.id]]; + } + /** + * Get the cramped version of a style (in particular, cramping a cramped style + * doesn't change the style). + */ + ; + + _proto.cramp = function cramp() { + return styles[_cramp[this.id]]; + } + /** + * Get a text or display version of this style. + */ + ; + + _proto.text = function text() { + return styles[_text[this.id]]; + } + /** + * Return true if this style is tightly spaced (scriptstyle/scriptscriptstyle) + */ + ; + + _proto.isTight = function isTight() { + return this.size >= 2; + }; + + return Style; +}(); // Export an interface for type checking, but don't expose the implementation. +// This way, no more styles can be generated. + + +// IDs of the different styles +var D = 0; +var Dc = 1; +var T = 2; +var Tc = 3; +var S = 4; +var Sc = 5; +var SS = 6; +var SSc = 7; // Instances of the different styles + +var styles = [new Style(D, 0, false), new Style(Dc, 0, true), new Style(T, 1, false), new Style(Tc, 1, true), new Style(S, 2, false), new Style(Sc, 2, true), new Style(SS, 3, false), new Style(SSc, 3, true)]; // Lookup tables for switching from one style to another + +var _sup = [S, Sc, S, Sc, SS, SSc, SS, SSc]; +var _sub = [Sc, Sc, Sc, Sc, SSc, SSc, SSc, SSc]; +var _fracNum = [T, Tc, S, Sc, SS, SSc, SS, SSc]; +var _fracDen = [Tc, Tc, Sc, Sc, SSc, SSc, SSc, SSc]; +var _cramp = [Dc, Dc, Tc, Tc, Sc, Sc, SSc, SSc]; +var _text = [D, Dc, T, Tc, T, Tc, T, Tc]; // We only export some of the styles. + +/* harmony default export */ var src_Style = ({ + DISPLAY: styles[D], + TEXT: styles[T], + SCRIPT: styles[S], + SCRIPTSCRIPT: styles[SS] +}); +;// CONCATENATED MODULE: ./src/unicodeScripts.js +/* + * This file defines the Unicode scripts and script families that we + * support. To add new scripts or families, just add a new entry to the + * scriptData array below. Adding scripts to the scriptData array allows + * characters from that script to appear in \text{} environments. + */ + +/** + * Each script or script family has a name and an array of blocks. + * Each block is an array of two numbers which specify the start and + * end points (inclusive) of a block of Unicode codepoints. + */ + +/** + * Unicode block data for the families of scripts we support in \text{}. + * Scripts only need to appear here if they do not have font metrics. + */ +var scriptData = [{ + // Latin characters beyond the Latin-1 characters we have metrics for. + // Needed for Czech, Hungarian and Turkish text, for example. + name: 'latin', + blocks: [[0x0100, 0x024f], // Latin Extended-A and Latin Extended-B + [0x0300, 0x036f] // Combining Diacritical marks + ] +}, { + // The Cyrillic script used by Russian and related languages. + // A Cyrillic subset used to be supported as explicitly defined + // symbols in symbols.js + name: 'cyrillic', + blocks: [[0x0400, 0x04ff]] +}, { + // Armenian + name: 'armenian', + blocks: [[0x0530, 0x058F]] +}, { + // The Brahmic scripts of South and Southeast Asia + // Devanagari (0900–097F) + // Bengali (0980–09FF) + // Gurmukhi (0A00–0A7F) + // Gujarati (0A80–0AFF) + // Oriya (0B00–0B7F) + // Tamil (0B80–0BFF) + // Telugu (0C00–0C7F) + // Kannada (0C80–0CFF) + // Malayalam (0D00–0D7F) + // Sinhala (0D80–0DFF) + // Thai (0E00–0E7F) + // Lao (0E80–0EFF) + // Tibetan (0F00–0FFF) + // Myanmar (1000–109F) + name: 'brahmic', + blocks: [[0x0900, 0x109F]] +}, { + name: 'georgian', + blocks: [[0x10A0, 0x10ff]] +}, { + // Chinese and Japanese. + // The "k" in cjk is for Korean, but we've separated Korean out + name: "cjk", + blocks: [[0x3000, 0x30FF], // CJK symbols and punctuation, Hiragana, Katakana + [0x4E00, 0x9FAF], // CJK ideograms + [0xFF00, 0xFF60] // Fullwidth punctuation + // TODO: add halfwidth Katakana and Romanji glyphs + ] +}, { + // Korean + name: 'hangul', + blocks: [[0xAC00, 0xD7AF]] +}]; +/** + * Given a codepoint, return the name of the script or script family + * it is from, or null if it is not part of a known block + */ + +function scriptFromCodepoint(codepoint) { + for (var i = 0; i < scriptData.length; i++) { + var script = scriptData[i]; + + for (var _i = 0; _i < script.blocks.length; _i++) { + var block = script.blocks[_i]; + + if (codepoint >= block[0] && codepoint <= block[1]) { + return script.name; + } + } + } + + return null; +} +/** + * A flattened version of all the supported blocks in a single array. + * This is an optimization to make supportedCodepoint() fast. + */ + +var allBlocks = []; +scriptData.forEach(function (s) { + return s.blocks.forEach(function (b) { + return allBlocks.push.apply(allBlocks, b); + }); +}); +/** + * Given a codepoint, return true if it falls within one of the + * scripts or script families defined above and false otherwise. + * + * Micro benchmarks shows that this is faster than + * /[\u3000-\u30FF\u4E00-\u9FAF\uFF00-\uFF60\uAC00-\uD7AF\u0900-\u109F]/.test() + * in Firefox, Chrome and Node. + */ + +function supportedCodepoint(codepoint) { + for (var i = 0; i < allBlocks.length; i += 2) { + if (codepoint >= allBlocks[i] && codepoint <= allBlocks[i + 1]) { + return true; + } + } + + return false; +} +;// CONCATENATED MODULE: ./src/svgGeometry.js +/** + * This file provides support to domTree.js and delimiter.js. + * It's a storehouse of path geometry for SVG images. + */ +// In all paths below, the viewBox-to-em scale is 1000:1. +var hLinePad = 80; // padding above a sqrt viniculum. Prevents image cropping. +// The viniculum of a \sqrt can be made thicker by a KaTeX rendering option. +// Think of variable extraViniculum as two detours in the SVG path. +// The detour begins at the lower left of the area labeled extraViniculum below. +// The detour proceeds one extraViniculum distance up and slightly to the right, +// displacing the radiused corner between surd and viniculum. The radius is +// traversed as usual, then the detour resumes. It goes right, to the end of +// the very long viniculumn, then down one extraViniculum distance, +// after which it resumes regular path geometry for the radical. + +/* viniculum + / + /▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒←extraViniculum + / █████████████████████←0.04em (40 unit) std viniculum thickness + / / + / / + / /\ + / / surd +*/ + +var sqrtMain = function sqrtMain(extraViniculum, hLinePad) { + // sqrtMain path geometry is from glyph U221A in the font KaTeX Main + return "M95," + (622 + extraViniculum + hLinePad) + "\nc-2.7,0,-7.17,-2.7,-13.5,-8c-5.8,-5.3,-9.5,-10,-9.5,-14\nc0,-2,0.3,-3.3,1,-4c1.3,-2.7,23.83,-20.7,67.5,-54\nc44.2,-33.3,65.8,-50.3,66.5,-51c1.3,-1.3,3,-2,5,-2c4.7,0,8.7,3.3,12,10\ns173,378,173,378c0.7,0,35.3,-71,104,-213c68.7,-142,137.5,-285,206.5,-429\nc69,-144,104.5,-217.7,106.5,-221\nl" + extraViniculum / 2.075 + " -" + extraViniculum + "\nc5.3,-9.3,12,-14,20,-14\nH400000v" + (40 + extraViniculum) + "H845.2724\ns-225.272,467,-225.272,467s-235,486,-235,486c-2.7,4.7,-9,7,-19,7\nc-6,0,-10,-1,-12,-3s-194,-422,-194,-422s-65,47,-65,47z\nM" + (834 + extraViniculum) + " " + hLinePad + "h400000v" + (40 + extraViniculum) + "h-400000z"; +}; + +var sqrtSize1 = function sqrtSize1(extraViniculum, hLinePad) { + // size1 is from glyph U221A in the font KaTeX_Size1-Regular + return "M263," + (601 + extraViniculum + hLinePad) + "c0.7,0,18,39.7,52,119\nc34,79.3,68.167,158.7,102.5,238c34.3,79.3,51.8,119.3,52.5,120\nc340,-704.7,510.7,-1060.3,512,-1067\nl" + extraViniculum / 2.084 + " -" + extraViniculum + "\nc4.7,-7.3,11,-11,19,-11\nH40000v" + (40 + extraViniculum) + "H1012.3\ns-271.3,567,-271.3,567c-38.7,80.7,-84,175,-136,283c-52,108,-89.167,185.3,-111.5,232\nc-22.3,46.7,-33.8,70.3,-34.5,71c-4.7,4.7,-12.3,7,-23,7s-12,-1,-12,-1\ns-109,-253,-109,-253c-72.7,-168,-109.3,-252,-110,-252c-10.7,8,-22,16.7,-34,26\nc-22,17.3,-33.3,26,-34,26s-26,-26,-26,-26s76,-59,76,-59s76,-60,76,-60z\nM" + (1001 + extraViniculum) + " " + hLinePad + "h400000v" + (40 + extraViniculum) + "h-400000z"; +}; + +var sqrtSize2 = function sqrtSize2(extraViniculum, hLinePad) { + // size2 is from glyph U221A in the font KaTeX_Size2-Regular + return "M983 " + (10 + extraViniculum + hLinePad) + "\nl" + extraViniculum / 3.13 + " -" + extraViniculum + "\nc4,-6.7,10,-10,18,-10 H400000v" + (40 + extraViniculum) + "\nH1013.1s-83.4,268,-264.1,840c-180.7,572,-277,876.3,-289,913c-4.7,4.7,-12.7,7,-24,7\ns-12,0,-12,0c-1.3,-3.3,-3.7,-11.7,-7,-25c-35.3,-125.3,-106.7,-373.3,-214,-744\nc-10,12,-21,25,-33,39s-32,39,-32,39c-6,-5.3,-15,-14,-27,-26s25,-30,25,-30\nc26.7,-32.7,52,-63,76,-91s52,-60,52,-60s208,722,208,722\nc56,-175.3,126.3,-397.3,211,-666c84.7,-268.7,153.8,-488.2,207.5,-658.5\nc53.7,-170.3,84.5,-266.8,92.5,-289.5z\nM" + (1001 + extraViniculum) + " " + hLinePad + "h400000v" + (40 + extraViniculum) + "h-400000z"; +}; + +var sqrtSize3 = function sqrtSize3(extraViniculum, hLinePad) { + // size3 is from glyph U221A in the font KaTeX_Size3-Regular + return "M424," + (2398 + extraViniculum + hLinePad) + "\nc-1.3,-0.7,-38.5,-172,-111.5,-514c-73,-342,-109.8,-513.3,-110.5,-514\nc0,-2,-10.7,14.3,-32,49c-4.7,7.3,-9.8,15.7,-15.5,25c-5.7,9.3,-9.8,16,-12.5,20\ns-5,7,-5,7c-4,-3.3,-8.3,-7.7,-13,-13s-13,-13,-13,-13s76,-122,76,-122s77,-121,77,-121\ns209,968,209,968c0,-2,84.7,-361.7,254,-1079c169.3,-717.3,254.7,-1077.7,256,-1081\nl" + extraViniculum / 4.223 + " -" + extraViniculum + "c4,-6.7,10,-10,18,-10 H400000\nv" + (40 + extraViniculum) + "H1014.6\ns-87.3,378.7,-272.6,1166c-185.3,787.3,-279.3,1182.3,-282,1185\nc-2,6,-10,9,-24,9\nc-8,0,-12,-0.7,-12,-2z M" + (1001 + extraViniculum) + " " + hLinePad + "\nh400000v" + (40 + extraViniculum) + "h-400000z"; +}; + +var sqrtSize4 = function sqrtSize4(extraViniculum, hLinePad) { + // size4 is from glyph U221A in the font KaTeX_Size4-Regular + return "M473," + (2713 + extraViniculum + hLinePad) + "\nc339.3,-1799.3,509.3,-2700,510,-2702 l" + extraViniculum / 5.298 + " -" + extraViniculum + "\nc3.3,-7.3,9.3,-11,18,-11 H400000v" + (40 + extraViniculum) + "H1017.7\ns-90.5,478,-276.2,1466c-185.7,988,-279.5,1483,-281.5,1485c-2,6,-10,9,-24,9\nc-8,0,-12,-0.7,-12,-2c0,-1.3,-5.3,-32,-16,-92c-50.7,-293.3,-119.7,-693.3,-207,-1200\nc0,-1.3,-5.3,8.7,-16,30c-10.7,21.3,-21.3,42.7,-32,64s-16,33,-16,33s-26,-26,-26,-26\ns76,-153,76,-153s77,-151,77,-151c0.7,0.7,35.7,202,105,604c67.3,400.7,102,602.7,104,\n606zM" + (1001 + extraViniculum) + " " + hLinePad + "h400000v" + (40 + extraViniculum) + "H1017.7z"; +}; + +var phasePath = function phasePath(y) { + var x = y / 2; // x coordinate at top of angle + + return "M400000 " + y + " H0 L" + x + " 0 l65 45 L145 " + (y - 80) + " H400000z"; +}; + +var sqrtTall = function sqrtTall(extraViniculum, hLinePad, viewBoxHeight) { + // sqrtTall is from glyph U23B7 in the font KaTeX_Size4-Regular + // One path edge has a variable length. It runs vertically from the viniculumn + // to a point near (14 units) the bottom of the surd. The viniculum + // is normally 40 units thick. So the length of the line in question is: + var vertSegment = viewBoxHeight - 54 - hLinePad - extraViniculum; + return "M702 " + (extraViniculum + hLinePad) + "H400000" + (40 + extraViniculum) + "\nH742v" + vertSegment + "l-4 4-4 4c-.667.7 -2 1.5-4 2.5s-4.167 1.833-6.5 2.5-5.5 1-9.5 1\nh-12l-28-84c-16.667-52-96.667 -294.333-240-727l-212 -643 -85 170\nc-4-3.333-8.333-7.667-13 -13l-13-13l77-155 77-156c66 199.333 139 419.667\n219 661 l218 661zM702 " + hLinePad + "H400000v" + (40 + extraViniculum) + "H742z"; +}; + +var sqrtPath = function sqrtPath(size, extraViniculum, viewBoxHeight) { + extraViniculum = 1000 * extraViniculum; // Convert from document ems to viewBox. + + var path = ""; + + switch (size) { + case "sqrtMain": + path = sqrtMain(extraViniculum, hLinePad); + break; + + case "sqrtSize1": + path = sqrtSize1(extraViniculum, hLinePad); + break; + + case "sqrtSize2": + path = sqrtSize2(extraViniculum, hLinePad); + break; + + case "sqrtSize3": + path = sqrtSize3(extraViniculum, hLinePad); + break; + + case "sqrtSize4": + path = sqrtSize4(extraViniculum, hLinePad); + break; + + case "sqrtTall": + path = sqrtTall(extraViniculum, hLinePad, viewBoxHeight); + } + + return path; +}; +var innerPath = function innerPath(name, height) { + // The inner part of stretchy tall delimiters + switch (name) { + case "\u239C": + return "M291 0 H417 V" + height + " H291z M291 0 H417 V" + height + " H291z"; + + case "\u2223": + return "M145 0 H188 V" + height + " H145z M145 0 H188 V" + height + " H145z"; + + case "\u2225": + return "M145 0 H188 V" + height + " H145z M145 0 H188 V" + height + " H145z" + ("M367 0 H410 V" + height + " H367z M367 0 H410 V" + height + " H367z"); + + case "\u239F": + return "M457 0 H583 V" + height + " H457z M457 0 H583 V" + height + " H457z"; + + case "\u23A2": + return "M319 0 H403 V" + height + " H319z M319 0 H403 V" + height + " H319z"; + + case "\u23A5": + return "M263 0 H347 V" + height + " H263z M263 0 H347 V" + height + " H263z"; + + case "\u23AA": + return "M384 0 H504 V" + height + " H384z M384 0 H504 V" + height + " H384z"; + + case "\u23D0": + return "M312 0 H355 V" + height + " H312z M312 0 H355 V" + height + " H312z"; + + case "\u2016": + return "M257 0 H300 V" + height + " H257z M257 0 H300 V" + height + " H257z" + ("M478 0 H521 V" + height + " H478z M478 0 H521 V" + height + " H478z"); + + default: + return ""; + } +}; +var path = { + // The doubleleftarrow geometry is from glyph U+21D0 in the font KaTeX Main + doubleleftarrow: "M262 157\nl10-10c34-36 62.7-77 86-123 3.3-8 5-13.3 5-16 0-5.3-6.7-8-20-8-7.3\n 0-12.2.5-14.5 1.5-2.3 1-4.8 4.5-7.5 10.5-49.3 97.3-121.7 169.3-217 216-28\n 14-57.3 25-88 33-6.7 2-11 3.8-13 5.5-2 1.7-3 4.2-3 7.5s1 5.8 3 7.5\nc2 1.7 6.3 3.5 13 5.5 68 17.3 128.2 47.8 180.5 91.5 52.3 43.7 93.8 96.2 124.5\n 157.5 9.3 8 15.3 12.3 18 13h6c12-.7 18-4 18-10 0-2-1.7-7-5-15-23.3-46-52-87\n-86-123l-10-10h399738v-40H218c328 0 0 0 0 0l-10-8c-26.7-20-65.7-43-117-69 2.7\n-2 6-3.7 10-5 36.7-16 72.3-37.3 107-64l10-8h399782v-40z\nm8 0v40h399730v-40zm0 194v40h399730v-40z", + // doublerightarrow is from glyph U+21D2 in font KaTeX Main + doublerightarrow: "M399738 392l\n-10 10c-34 36-62.7 77-86 123-3.3 8-5 13.3-5 16 0 5.3 6.7 8 20 8 7.3 0 12.2-.5\n 14.5-1.5 2.3-1 4.8-4.5 7.5-10.5 49.3-97.3 121.7-169.3 217-216 28-14 57.3-25 88\n-33 6.7-2 11-3.8 13-5.5 2-1.7 3-4.2 3-7.5s-1-5.8-3-7.5c-2-1.7-6.3-3.5-13-5.5-68\n-17.3-128.2-47.8-180.5-91.5-52.3-43.7-93.8-96.2-124.5-157.5-9.3-8-15.3-12.3-18\n-13h-6c-12 .7-18 4-18 10 0 2 1.7 7 5 15 23.3 46 52 87 86 123l10 10H0v40h399782\nc-328 0 0 0 0 0l10 8c26.7 20 65.7 43 117 69-2.7 2-6 3.7-10 5-36.7 16-72.3 37.3\n-107 64l-10 8H0v40zM0 157v40h399730v-40zm0 194v40h399730v-40z", + // leftarrow is from glyph U+2190 in font KaTeX Main + leftarrow: "M400000 241H110l3-3c68.7-52.7 113.7-120\n 135-202 4-14.7 6-23 6-25 0-7.3-7-11-21-11-8 0-13.2.8-15.5 2.5-2.3 1.7-4.2 5.8\n-5.5 12.5-1.3 4.7-2.7 10.3-4 17-12 48.7-34.8 92-68.5 130S65.3 228.3 18 247\nc-10 4-16 7.7-18 11 0 8.7 6 14.3 18 17 47.3 18.7 87.8 47 121.5 85S196 441.3 208\n 490c.7 2 1.3 5 2 9s1.2 6.7 1.5 8c.3 1.3 1 3.3 2 6s2.2 4.5 3.5 5.5c1.3 1 3.3\n 1.8 6 2.5s6 1 10 1c14 0 21-3.7 21-11 0-2-2-10.3-6-25-20-79.3-65-146.7-135-202\n l-3-3h399890zM100 241v40h399900v-40z", + // overbrace is from glyphs U+23A9/23A8/23A7 in font KaTeX_Size4-Regular + leftbrace: "M6 548l-6-6v-35l6-11c56-104 135.3-181.3 238-232 57.3-28.7 117\n-45 179-50h399577v120H403c-43.3 7-81 15-113 26-100.7 33-179.7 91-237 174-2.7\n 5-6 9-10 13-.7 1-7.3 1-20 1H6z", + leftbraceunder: "M0 6l6-6h17c12.688 0 19.313.3 20 1 4 4 7.313 8.3 10 13\n 35.313 51.3 80.813 93.8 136.5 127.5 55.688 33.7 117.188 55.8 184.5 66.5.688\n 0 2 .3 4 1 18.688 2.7 76 4.3 172 5h399450v120H429l-6-1c-124.688-8-235-61.7\n-331-161C60.687 138.7 32.312 99.3 7 54L0 41V6z", + // overgroup is from the MnSymbol package (public domain) + leftgroup: "M400000 80\nH435C64 80 168.3 229.4 21 260c-5.9 1.2-18 0-18 0-2 0-3-1-3-3v-38C76 61 257 0\n 435 0h399565z", + leftgroupunder: "M400000 262\nH435C64 262 168.3 112.6 21 82c-5.9-1.2-18 0-18 0-2 0-3 1-3 3v38c76 158 257 219\n 435 219h399565z", + // Harpoons are from glyph U+21BD in font KaTeX Main + leftharpoon: "M0 267c.7 5.3 3 10 7 14h399993v-40H93c3.3\n-3.3 10.2-9.5 20.5-18.5s17.8-15.8 22.5-20.5c50.7-52 88-110.3 112-175 4-11.3 5\n-18.3 3-21-1.3-4-7.3-6-18-6-8 0-13 .7-15 2s-4.7 6.7-8 16c-42 98.7-107.3 174.7\n-196 228-6.7 4.7-10.7 8-12 10-1.3 2-2 5.7-2 11zm100-26v40h399900v-40z", + leftharpoonplus: "M0 267c.7 5.3 3 10 7 14h399993v-40H93c3.3-3.3 10.2-9.5\n 20.5-18.5s17.8-15.8 22.5-20.5c50.7-52 88-110.3 112-175 4-11.3 5-18.3 3-21-1.3\n-4-7.3-6-18-6-8 0-13 .7-15 2s-4.7 6.7-8 16c-42 98.7-107.3 174.7-196 228-6.7 4.7\n-10.7 8-12 10-1.3 2-2 5.7-2 11zm100-26v40h399900v-40zM0 435v40h400000v-40z\nm0 0v40h400000v-40z", + leftharpoondown: "M7 241c-4 4-6.333 8.667-7 14 0 5.333.667 9 2 11s5.333\n 5.333 12 10c90.667 54 156 130 196 228 3.333 10.667 6.333 16.333 9 17 2 .667 5\n 1 9 1h5c10.667 0 16.667-2 18-6 2-2.667 1-9.667-3-21-32-87.333-82.667-157.667\n-152-211l-3-3h399907v-40zM93 281 H400000 v-40L7 241z", + leftharpoondownplus: "M7 435c-4 4-6.3 8.7-7 14 0 5.3.7 9 2 11s5.3 5.3 12\n 10c90.7 54 156 130 196 228 3.3 10.7 6.3 16.3 9 17 2 .7 5 1 9 1h5c10.7 0 16.7\n-2 18-6 2-2.7 1-9.7-3-21-32-87.3-82.7-157.7-152-211l-3-3h399907v-40H7zm93 0\nv40h399900v-40zM0 241v40h399900v-40zm0 0v40h399900v-40z", + // hook is from glyph U+21A9 in font KaTeX Main + lefthook: "M400000 281 H103s-33-11.2-61-33.5S0 197.3 0 164s14.2-61.2 42.5\n-83.5C70.8 58.2 104 47 142 47 c16.7 0 25 6.7 25 20 0 12-8.7 18.7-26 20-40 3.3\n-68.7 15.7-86 37-10 12-15 25.3-15 40 0 22.7 9.8 40.7 29.5 54 19.7 13.3 43.5 21\n 71.5 23h399859zM103 281v-40h399897v40z", + leftlinesegment: "M40 281 V428 H0 V94 H40 V241 H400000 v40z\nM40 281 V428 H0 V94 H40 V241 H400000 v40z", + leftmapsto: "M40 281 V448H0V74H40V241H400000v40z\nM40 281 V448H0V74H40V241H400000v40z", + // tofrom is from glyph U+21C4 in font KaTeX AMS Regular + leftToFrom: "M0 147h400000v40H0zm0 214c68 40 115.7 95.7 143 167h22c15.3 0 23\n-.3 23-1 0-1.3-5.3-13.7-16-37-18-35.3-41.3-69-70-101l-7-8h399905v-40H95l7-8\nc28.7-32 52-65.7 70-101 10.7-23.3 16-35.7 16-37 0-.7-7.7-1-23-1h-22C115.7 265.3\n 68 321 0 361zm0-174v-40h399900v40zm100 154v40h399900v-40z", + longequal: "M0 50 h400000 v40H0z m0 194h40000v40H0z\nM0 50 h400000 v40H0z m0 194h40000v40H0z", + midbrace: "M200428 334\nc-100.7-8.3-195.3-44-280-108-55.3-42-101.7-93-139-153l-9-14c-2.7 4-5.7 8.7-9 14\n-53.3 86.7-123.7 153-211 199-66.7 36-137.3 56.3-212 62H0V214h199568c178.3-11.7\n 311.7-78.3 403-201 6-8 9.7-12 11-12 .7-.7 6.7-1 18-1s17.3.3 18 1c1.3 0 5 4 11\n 12 44.7 59.3 101.3 106.3 170 141s145.3 54.3 229 60h199572v120z", + midbraceunder: "M199572 214\nc100.7 8.3 195.3 44 280 108 55.3 42 101.7 93 139 153l9 14c2.7-4 5.7-8.7 9-14\n 53.3-86.7 123.7-153 211-199 66.7-36 137.3-56.3 212-62h199568v120H200432c-178.3\n 11.7-311.7 78.3-403 201-6 8-9.7 12-11 12-.7.7-6.7 1-18 1s-17.3-.3-18-1c-1.3 0\n-5-4-11-12-44.7-59.3-101.3-106.3-170-141s-145.3-54.3-229-60H0V214z", + oiintSize1: "M512.6 71.6c272.6 0 320.3 106.8 320.3 178.2 0 70.8-47.7 177.6\n-320.3 177.6S193.1 320.6 193.1 249.8c0-71.4 46.9-178.2 319.5-178.2z\nm368.1 178.2c0-86.4-60.9-215.4-368.1-215.4-306.4 0-367.3 129-367.3 215.4 0 85.8\n60.9 214.8 367.3 214.8 307.2 0 368.1-129 368.1-214.8z", + oiintSize2: "M757.8 100.1c384.7 0 451.1 137.6 451.1 230 0 91.3-66.4 228.8\n-451.1 228.8-386.3 0-452.7-137.5-452.7-228.8 0-92.4 66.4-230 452.7-230z\nm502.4 230c0-111.2-82.4-277.2-502.4-277.2s-504 166-504 277.2\nc0 110 84 276 504 276s502.4-166 502.4-276z", + oiiintSize1: "M681.4 71.6c408.9 0 480.5 106.8 480.5 178.2 0 70.8-71.6 177.6\n-480.5 177.6S202.1 320.6 202.1 249.8c0-71.4 70.5-178.2 479.3-178.2z\nm525.8 178.2c0-86.4-86.8-215.4-525.7-215.4-437.9 0-524.7 129-524.7 215.4 0\n85.8 86.8 214.8 524.7 214.8 438.9 0 525.7-129 525.7-214.8z", + oiiintSize2: "M1021.2 53c603.6 0 707.8 165.8 707.8 277.2 0 110-104.2 275.8\n-707.8 275.8-606 0-710.2-165.8-710.2-275.8C311 218.8 415.2 53 1021.2 53z\nm770.4 277.1c0-131.2-126.4-327.6-770.5-327.6S248.4 198.9 248.4 330.1\nc0 130 128.8 326.4 772.7 326.4s770.5-196.4 770.5-326.4z", + rightarrow: "M0 241v40h399891c-47.3 35.3-84 78-110 128\n-16.7 32-27.7 63.7-33 95 0 1.3-.2 2.7-.5 4-.3 1.3-.5 2.3-.5 3 0 7.3 6.7 11 20\n 11 8 0 13.2-.8 15.5-2.5 2.3-1.7 4.2-5.5 5.5-11.5 2-13.3 5.7-27 11-41 14.7-44.7\n 39-84.5 73-119.5s73.7-60.2 119-75.5c6-2 9-5.7 9-11s-3-9-9-11c-45.3-15.3-85\n-40.5-119-75.5s-58.3-74.8-73-119.5c-4.7-14-8.3-27.3-11-40-1.3-6.7-3.2-10.8-5.5\n-12.5-2.3-1.7-7.5-2.5-15.5-2.5-14 0-21 3.7-21 11 0 2 2 10.3 6 25 20.7 83.3 67\n 151.7 139 205zm0 0v40h399900v-40z", + rightbrace: "M400000 542l\n-6 6h-17c-12.7 0-19.3-.3-20-1-4-4-7.3-8.3-10-13-35.3-51.3-80.8-93.8-136.5-127.5\ns-117.2-55.8-184.5-66.5c-.7 0-2-.3-4-1-18.7-2.7-76-4.3-172-5H0V214h399571l6 1\nc124.7 8 235 61.7 331 161 31.3 33.3 59.7 72.7 85 118l7 13v35z", + rightbraceunder: "M399994 0l6 6v35l-6 11c-56 104-135.3 181.3-238 232-57.3\n 28.7-117 45-179 50H-300V214h399897c43.3-7 81-15 113-26 100.7-33 179.7-91 237\n-174 2.7-5 6-9 10-13 .7-1 7.3-1 20-1h17z", + rightgroup: "M0 80h399565c371 0 266.7 149.4 414 180 5.9 1.2 18 0 18 0 2 0\n 3-1 3-3v-38c-76-158-257-219-435-219H0z", + rightgroupunder: "M0 262h399565c371 0 266.7-149.4 414-180 5.9-1.2 18 0 18\n 0 2 0 3 1 3 3v38c-76 158-257 219-435 219H0z", + rightharpoon: "M0 241v40h399993c4.7-4.7 7-9.3 7-14 0-9.3\n-3.7-15.3-11-18-92.7-56.7-159-133.7-199-231-3.3-9.3-6-14.7-8-16-2-1.3-7-2-15-2\n-10.7 0-16.7 2-18 6-2 2.7-1 9.7 3 21 15.3 42 36.7 81.8 64 119.5 27.3 37.7 58\n 69.2 92 94.5zm0 0v40h399900v-40z", + rightharpoonplus: "M0 241v40h399993c4.7-4.7 7-9.3 7-14 0-9.3-3.7-15.3-11\n-18-92.7-56.7-159-133.7-199-231-3.3-9.3-6-14.7-8-16-2-1.3-7-2-15-2-10.7 0-16.7\n 2-18 6-2 2.7-1 9.7 3 21 15.3 42 36.7 81.8 64 119.5 27.3 37.7 58 69.2 92 94.5z\nm0 0v40h399900v-40z m100 194v40h399900v-40zm0 0v40h399900v-40z", + rightharpoondown: "M399747 511c0 7.3 6.7 11 20 11 8 0 13-.8 15-2.5s4.7-6.8\n 8-15.5c40-94 99.3-166.3 178-217 13.3-8 20.3-12.3 21-13 5.3-3.3 8.5-5.8 9.5\n-7.5 1-1.7 1.5-5.2 1.5-10.5s-2.3-10.3-7-15H0v40h399908c-34 25.3-64.7 57-92 95\n-27.3 38-48.7 77.7-64 119-3.3 8.7-5 14-5 16zM0 241v40h399900v-40z", + rightharpoondownplus: "M399747 705c0 7.3 6.7 11 20 11 8 0 13-.8\n 15-2.5s4.7-6.8 8-15.5c40-94 99.3-166.3 178-217 13.3-8 20.3-12.3 21-13 5.3-3.3\n 8.5-5.8 9.5-7.5 1-1.7 1.5-5.2 1.5-10.5s-2.3-10.3-7-15H0v40h399908c-34 25.3\n-64.7 57-92 95-27.3 38-48.7 77.7-64 119-3.3 8.7-5 14-5 16zM0 435v40h399900v-40z\nm0-194v40h400000v-40zm0 0v40h400000v-40z", + righthook: "M399859 241c-764 0 0 0 0 0 40-3.3 68.7-15.7 86-37 10-12 15-25.3\n 15-40 0-22.7-9.8-40.7-29.5-54-19.7-13.3-43.5-21-71.5-23-17.3-1.3-26-8-26-20 0\n-13.3 8.7-20 26-20 38 0 71 11.2 99 33.5 0 0 7 5.6 21 16.7 14 11.2 21 33.5 21\n 66.8s-14 61.2-42 83.5c-28 22.3-61 33.5-99 33.5L0 241z M0 281v-40h399859v40z", + rightlinesegment: "M399960 241 V94 h40 V428 h-40 V281 H0 v-40z\nM399960 241 V94 h40 V428 h-40 V281 H0 v-40z", + rightToFrom: "M400000 167c-70.7-42-118-97.7-142-167h-23c-15.3 0-23 .3-23\n 1 0 1.3 5.3 13.7 16 37 18 35.3 41.3 69 70 101l7 8H0v40h399905l-7 8c-28.7 32\n-52 65.7-70 101-10.7 23.3-16 35.7-16 37 0 .7 7.7 1 23 1h23c24-69.3 71.3-125 142\n-167z M100 147v40h399900v-40zM0 341v40h399900v-40z", + // twoheadleftarrow is from glyph U+219E in font KaTeX AMS Regular + twoheadleftarrow: "M0 167c68 40\n 115.7 95.7 143 167h22c15.3 0 23-.3 23-1 0-1.3-5.3-13.7-16-37-18-35.3-41.3-69\n-70-101l-7-8h125l9 7c50.7 39.3 85 86 103 140h46c0-4.7-6.3-18.7-19-42-18-35.3\n-40-67.3-66-96l-9-9h399716v-40H284l9-9c26-28.7 48-60.7 66-96 12.7-23.333 19\n-37.333 19-42h-46c-18 54-52.3 100.7-103 140l-9 7H95l7-8c28.7-32 52-65.7 70-101\n 10.7-23.333 16-35.7 16-37 0-.7-7.7-1-23-1h-22C115.7 71.3 68 127 0 167z", + twoheadrightarrow: "M400000 167\nc-68-40-115.7-95.7-143-167h-22c-15.3 0-23 .3-23 1 0 1.3 5.3 13.7 16 37 18 35.3\n 41.3 69 70 101l7 8h-125l-9-7c-50.7-39.3-85-86-103-140h-46c0 4.7 6.3 18.7 19 42\n 18 35.3 40 67.3 66 96l9 9H0v40h399716l-9 9c-26 28.7-48 60.7-66 96-12.7 23.333\n-19 37.333-19 42h46c18-54 52.3-100.7 103-140l9-7h125l-7 8c-28.7 32-52 65.7-70\n 101-10.7 23.333-16 35.7-16 37 0 .7 7.7 1 23 1h22c27.3-71.3 75-127 143-167z", + // tilde1 is a modified version of a glyph from the MnSymbol package + tilde1: "M200 55.538c-77 0-168 73.953-177 73.953-3 0-7\n-2.175-9-5.437L2 97c-1-2-2-4-2-6 0-4 2-7 5-9l20-12C116 12 171 0 207 0c86 0\n 114 68 191 68 78 0 168-68 177-68 4 0 7 2 9 5l12 19c1 2.175 2 4.35 2 6.525 0\n 4.35-2 7.613-5 9.788l-19 13.05c-92 63.077-116.937 75.308-183 76.128\n-68.267.847-113-73.952-191-73.952z", + // ditto tilde2, tilde3, & tilde4 + tilde2: "M344 55.266c-142 0-300.638 81.316-311.5 86.418\n-8.01 3.762-22.5 10.91-23.5 5.562L1 120c-1-2-1-3-1-4 0-5 3-9 8-10l18.4-9C160.9\n 31.9 283 0 358 0c148 0 188 122 331 122s314-97 326-97c4 0 8 2 10 7l7 21.114\nc1 2.14 1 3.21 1 4.28 0 5.347-3 9.626-7 10.696l-22.3 12.622C852.6 158.372 751\n 181.476 676 181.476c-149 0-189-126.21-332-126.21z", + tilde3: "M786 59C457 59 32 175.242 13 175.242c-6 0-10-3.457\n-11-10.37L.15 138c-1-7 3-12 10-13l19.2-6.4C378.4 40.7 634.3 0 804.3 0c337 0\n 411.8 157 746.8 157 328 0 754-112 773-112 5 0 10 3 11 9l1 14.075c1 8.066-.697\n 16.595-6.697 17.492l-21.052 7.31c-367.9 98.146-609.15 122.696-778.15 122.696\n -338 0-409-156.573-744-156.573z", + tilde4: "M786 58C457 58 32 177.487 13 177.487c-6 0-10-3.345\n-11-10.035L.15 143c-1-7 3-12 10-13l22-6.7C381.2 35 637.15 0 807.15 0c337 0 409\n 177 744 177 328 0 754-127 773-127 5 0 10 3 11 9l1 14.794c1 7.805-3 13.38-9\n 14.495l-20.7 5.574c-366.85 99.79-607.3 139.372-776.3 139.372-338 0-409\n -175.236-744-175.236z", + // vec is from glyph U+20D7 in font KaTeX Main + vec: "M377 20c0-5.333 1.833-10 5.5-14S391 0 397 0c4.667 0 8.667 1.667 12 5\n3.333 2.667 6.667 9 10 19 6.667 24.667 20.333 43.667 41 57 7.333 4.667 11\n10.667 11 18 0 6-1 10-3 12s-6.667 5-14 9c-28.667 14.667-53.667 35.667-75 63\n-1.333 1.333-3.167 3.5-5.5 6.5s-4 4.833-5 5.5c-1 .667-2.5 1.333-4.5 2s-4.333 1\n-7 1c-4.667 0-9.167-1.833-13.5-5.5S337 184 337 178c0-12.667 15.667-32.333 47-59\nH213l-171-1c-8.667-6-13-12.333-13-19 0-4.667 4.333-11.333 13-20h359\nc-16-25.333-24-45-24-59z", + // widehat1 is a modified version of a glyph from the MnSymbol package + widehat1: "M529 0h5l519 115c5 1 9 5 9 10 0 1-1 2-1 3l-4 22\nc-1 5-5 9-11 9h-2L532 67 19 159h-2c-5 0-9-4-11-9l-5-22c-1-6 2-12 8-13z", + // ditto widehat2, widehat3, & widehat4 + widehat2: "M1181 0h2l1171 176c6 0 10 5 10 11l-2 23c-1 6-5 10\n-11 10h-1L1182 67 15 220h-1c-6 0-10-4-11-10l-2-23c-1-6 4-11 10-11z", + widehat3: "M1181 0h2l1171 236c6 0 10 5 10 11l-2 23c-1 6-5 10\n-11 10h-1L1182 67 15 280h-1c-6 0-10-4-11-10l-2-23c-1-6 4-11 10-11z", + widehat4: "M1181 0h2l1171 296c6 0 10 5 10 11l-2 23c-1 6-5 10\n-11 10h-1L1182 67 15 340h-1c-6 0-10-4-11-10l-2-23c-1-6 4-11 10-11z", + // widecheck paths are all inverted versions of widehat + widecheck1: "M529,159h5l519,-115c5,-1,9,-5,9,-10c0,-1,-1,-2,-1,-3l-4,-22c-1,\n-5,-5,-9,-11,-9h-2l-512,92l-513,-92h-2c-5,0,-9,4,-11,9l-5,22c-1,6,2,12,8,13z", + widecheck2: "M1181,220h2l1171,-176c6,0,10,-5,10,-11l-2,-23c-1,-6,-5,-10,\n-11,-10h-1l-1168,153l-1167,-153h-1c-6,0,-10,4,-11,10l-2,23c-1,6,4,11,10,11z", + widecheck3: "M1181,280h2l1171,-236c6,0,10,-5,10,-11l-2,-23c-1,-6,-5,-10,\n-11,-10h-1l-1168,213l-1167,-213h-1c-6,0,-10,4,-11,10l-2,23c-1,6,4,11,10,11z", + widecheck4: "M1181,340h2l1171,-296c6,0,10,-5,10,-11l-2,-23c-1,-6,-5,-10,\n-11,-10h-1l-1168,273l-1167,-273h-1c-6,0,-10,4,-11,10l-2,23c-1,6,4,11,10,11z", + // The next ten paths support reaction arrows from the mhchem package. + // Arrows for \ce{<-->} are offset from xAxis by 0.22ex, per mhchem in LaTeX + // baraboveleftarrow is mostly from from glyph U+2190 in font KaTeX Main + baraboveleftarrow: "M400000 620h-399890l3 -3c68.7 -52.7 113.7 -120 135 -202\nc4 -14.7 6 -23 6 -25c0 -7.3 -7 -11 -21 -11c-8 0 -13.2 0.8 -15.5 2.5\nc-2.3 1.7 -4.2 5.8 -5.5 12.5c-1.3 4.7 -2.7 10.3 -4 17c-12 48.7 -34.8 92 -68.5 130\ns-74.2 66.3 -121.5 85c-10 4 -16 7.7 -18 11c0 8.7 6 14.3 18 17c47.3 18.7 87.8 47\n121.5 85s56.5 81.3 68.5 130c0.7 2 1.3 5 2 9s1.2 6.7 1.5 8c0.3 1.3 1 3.3 2 6\ns2.2 4.5 3.5 5.5c1.3 1 3.3 1.8 6 2.5s6 1 10 1c14 0 21 -3.7 21 -11\nc0 -2 -2 -10.3 -6 -25c-20 -79.3 -65 -146.7 -135 -202l-3 -3h399890z\nM100 620v40h399900v-40z M0 241v40h399900v-40zM0 241v40h399900v-40z", + // rightarrowabovebar is mostly from glyph U+2192, KaTeX Main + rightarrowabovebar: "M0 241v40h399891c-47.3 35.3-84 78-110 128-16.7 32\n-27.7 63.7-33 95 0 1.3-.2 2.7-.5 4-.3 1.3-.5 2.3-.5 3 0 7.3 6.7 11 20 11 8 0\n13.2-.8 15.5-2.5 2.3-1.7 4.2-5.5 5.5-11.5 2-13.3 5.7-27 11-41 14.7-44.7 39\n-84.5 73-119.5s73.7-60.2 119-75.5c6-2 9-5.7 9-11s-3-9-9-11c-45.3-15.3-85-40.5\n-119-75.5s-58.3-74.8-73-119.5c-4.7-14-8.3-27.3-11-40-1.3-6.7-3.2-10.8-5.5\n-12.5-2.3-1.7-7.5-2.5-15.5-2.5-14 0-21 3.7-21 11 0 2 2 10.3 6 25 20.7 83.3 67\n151.7 139 205zm96 379h399894v40H0zm0 0h399904v40H0z", + // The short left harpoon has 0.5em (i.e. 500 units) kern on the left end. + // Ref from mhchem.sty: \rlap{\raisebox{-.22ex}{$\kern0.5em + baraboveshortleftharpoon: "M507,435c-4,4,-6.3,8.7,-7,14c0,5.3,0.7,9,2,11\nc1.3,2,5.3,5.3,12,10c90.7,54,156,130,196,228c3.3,10.7,6.3,16.3,9,17\nc2,0.7,5,1,9,1c0,0,5,0,5,0c10.7,0,16.7,-2,18,-6c2,-2.7,1,-9.7,-3,-21\nc-32,-87.3,-82.7,-157.7,-152,-211c0,0,-3,-3,-3,-3l399351,0l0,-40\nc-398570,0,-399437,0,-399437,0z M593 435 v40 H399500 v-40z\nM0 281 v-40 H399908 v40z M0 281 v-40 H399908 v40z", + rightharpoonaboveshortbar: "M0,241 l0,40c399126,0,399993,0,399993,0\nc4.7,-4.7,7,-9.3,7,-14c0,-9.3,-3.7,-15.3,-11,-18c-92.7,-56.7,-159,-133.7,-199,\n-231c-3.3,-9.3,-6,-14.7,-8,-16c-2,-1.3,-7,-2,-15,-2c-10.7,0,-16.7,2,-18,6\nc-2,2.7,-1,9.7,3,21c15.3,42,36.7,81.8,64,119.5c27.3,37.7,58,69.2,92,94.5z\nM0 241 v40 H399908 v-40z M0 475 v-40 H399500 v40z M0 475 v-40 H399500 v40z", + shortbaraboveleftharpoon: "M7,435c-4,4,-6.3,8.7,-7,14c0,5.3,0.7,9,2,11\nc1.3,2,5.3,5.3,12,10c90.7,54,156,130,196,228c3.3,10.7,6.3,16.3,9,17c2,0.7,5,1,9,\n1c0,0,5,0,5,0c10.7,0,16.7,-2,18,-6c2,-2.7,1,-9.7,-3,-21c-32,-87.3,-82.7,-157.7,\n-152,-211c0,0,-3,-3,-3,-3l399907,0l0,-40c-399126,0,-399993,0,-399993,0z\nM93 435 v40 H400000 v-40z M500 241 v40 H400000 v-40z M500 241 v40 H400000 v-40z", + shortrightharpoonabovebar: "M53,241l0,40c398570,0,399437,0,399437,0\nc4.7,-4.7,7,-9.3,7,-14c0,-9.3,-3.7,-15.3,-11,-18c-92.7,-56.7,-159,-133.7,-199,\n-231c-3.3,-9.3,-6,-14.7,-8,-16c-2,-1.3,-7,-2,-15,-2c-10.7,0,-16.7,2,-18,6\nc-2,2.7,-1,9.7,3,21c15.3,42,36.7,81.8,64,119.5c27.3,37.7,58,69.2,92,94.5z\nM500 241 v40 H399408 v-40z M500 435 v40 H400000 v-40z" +}; +;// CONCATENATED MODULE: ./src/tree.js + + +/** + * This node represents a document fragment, which contains elements, but when + * placed into the DOM doesn't have any representation itself. It only contains + * children and doesn't have any DOM node properties. + */ +var DocumentFragment = /*#__PURE__*/function () { + // HtmlDomNode + // Never used; needed for satisfying interface. + function DocumentFragment(children) { + this.children = void 0; + this.classes = void 0; + this.height = void 0; + this.depth = void 0; + this.maxFontSize = void 0; + this.style = void 0; + this.children = children; + this.classes = []; + this.height = 0; + this.depth = 0; + this.maxFontSize = 0; + this.style = {}; + } + + var _proto = DocumentFragment.prototype; + + _proto.hasClass = function hasClass(className) { + return utils.contains(this.classes, className); + } + /** Convert the fragment into a node. */ + ; + + _proto.toNode = function toNode() { + var frag = document.createDocumentFragment(); + + for (var i = 0; i < this.children.length; i++) { + frag.appendChild(this.children[i].toNode()); + } + + return frag; + } + /** Convert the fragment into HTML markup. */ + ; + + _proto.toMarkup = function toMarkup() { + var markup = ""; // Simply concatenate the markup for the children together. + + for (var i = 0; i < this.children.length; i++) { + markup += this.children[i].toMarkup(); + } + + return markup; + } + /** + * Converts the math node into a string, similar to innerText. Applies to + * MathDomNode's only. + */ + ; + + _proto.toText = function toText() { + // To avoid this, we would subclass documentFragment separately for + // MathML, but polyfills for subclassing is expensive per PR 1469. + // $FlowFixMe: Only works for ChildType = MathDomNode. + var toText = function toText(child) { + return child.toText(); + }; + + return this.children.map(toText).join(""); + }; + + return DocumentFragment; +}(); +;// CONCATENATED MODULE: ./src/domTree.js +/** + * These objects store the data about the DOM nodes we create, as well as some + * extra data. They can then be transformed into real DOM nodes with the + * `toNode` function or HTML markup using `toMarkup`. They are useful for both + * storing extra properties on the nodes, as well as providing a way to easily + * work with the DOM. + * + * Similar functions for working with MathML nodes exist in mathMLTree.js. + * + * TODO: refactor `span` and `anchor` into common superclass when + * target environments support class inheritance + */ + + + + + +/** + * Create an HTML className based on a list of classes. In addition to joining + * with spaces, we also remove empty classes. + */ +var createClass = function createClass(classes) { + return classes.filter(function (cls) { + return cls; + }).join(" "); +}; + +var initNode = function initNode(classes, options, style) { + this.classes = classes || []; + this.attributes = {}; + this.height = 0; + this.depth = 0; + this.maxFontSize = 0; + this.style = style || {}; + + if (options) { + if (options.style.isTight()) { + this.classes.push("mtight"); + } + + var color = options.getColor(); + + if (color) { + this.style.color = color; + } + } +}; +/** + * Convert into an HTML node + */ + + +var _toNode = function toNode(tagName) { + var node = document.createElement(tagName); // Apply the class + + node.className = createClass(this.classes); // Apply inline styles + + for (var style in this.style) { + if (this.style.hasOwnProperty(style)) { + // $FlowFixMe Flow doesn't seem to understand span.style's type. + node.style[style] = this.style[style]; + } + } // Apply attributes + + + for (var attr in this.attributes) { + if (this.attributes.hasOwnProperty(attr)) { + node.setAttribute(attr, this.attributes[attr]); + } + } // Append the children, also as HTML nodes + + + for (var i = 0; i < this.children.length; i++) { + node.appendChild(this.children[i].toNode()); + } + + return node; +}; +/** + * Convert into an HTML markup string + */ + + +var _toMarkup = function toMarkup(tagName) { + var markup = "<" + tagName; // Add the class + + if (this.classes.length) { + markup += " class=\"" + utils.escape(createClass(this.classes)) + "\""; + } + + var styles = ""; // Add the styles, after hyphenation + + for (var style in this.style) { + if (this.style.hasOwnProperty(style)) { + styles += utils.hyphenate(style) + ":" + this.style[style] + ";"; + } + } + + if (styles) { + markup += " style=\"" + utils.escape(styles) + "\""; + } // Add the attributes + + + for (var attr in this.attributes) { + if (this.attributes.hasOwnProperty(attr)) { + markup += " " + attr + "=\"" + utils.escape(this.attributes[attr]) + "\""; + } + } + + markup += ">"; // Add the markup of the children, also as markup + + for (var i = 0; i < this.children.length; i++) { + markup += this.children[i].toMarkup(); + } + + markup += ""; + return markup; +}; // Making the type below exact with all optional fields doesn't work due to +// - https://github.com/facebook/flow/issues/4582 +// - https://github.com/facebook/flow/issues/5688 +// However, since *all* fields are optional, $Shape<> works as suggested in 5688 +// above. +// This type does not include all CSS properties. Additional properties should +// be added as needed. + + +/** + * This node represents a span node, with a className, a list of children, and + * an inline style. It also contains information about its height, depth, and + * maxFontSize. + * + * Represents two types with different uses: SvgSpan to wrap an SVG and DomSpan + * otherwise. This typesafety is important when HTML builders access a span's + * children. + */ +var Span = /*#__PURE__*/function () { + function Span(classes, children, options, style) { + this.children = void 0; + this.attributes = void 0; + this.classes = void 0; + this.height = void 0; + this.depth = void 0; + this.width = void 0; + this.maxFontSize = void 0; + this.style = void 0; + initNode.call(this, classes, options, style); + this.children = children || []; + } + /** + * Sets an arbitrary attribute on the span. Warning: use this wisely. Not + * all browsers support attributes the same, and having too many custom + * attributes is probably bad. + */ + + + var _proto = Span.prototype; + + _proto.setAttribute = function setAttribute(attribute, value) { + this.attributes[attribute] = value; + }; + + _proto.hasClass = function hasClass(className) { + return utils.contains(this.classes, className); + }; + + _proto.toNode = function toNode() { + return _toNode.call(this, "span"); + }; + + _proto.toMarkup = function toMarkup() { + return _toMarkup.call(this, "span"); + }; + + return Span; +}(); +/** + * This node represents an anchor () element with a hyperlink. See `span` + * for further details. + */ + +var Anchor = /*#__PURE__*/function () { + function Anchor(href, classes, children, options) { + this.children = void 0; + this.attributes = void 0; + this.classes = void 0; + this.height = void 0; + this.depth = void 0; + this.maxFontSize = void 0; + this.style = void 0; + initNode.call(this, classes, options); + this.children = children || []; + this.setAttribute('href', href); + } + + var _proto2 = Anchor.prototype; + + _proto2.setAttribute = function setAttribute(attribute, value) { + this.attributes[attribute] = value; + }; + + _proto2.hasClass = function hasClass(className) { + return utils.contains(this.classes, className); + }; + + _proto2.toNode = function toNode() { + return _toNode.call(this, "a"); + }; + + _proto2.toMarkup = function toMarkup() { + return _toMarkup.call(this, "a"); + }; + + return Anchor; +}(); +/** + * This node represents an image embed () element. + */ + +var Img = /*#__PURE__*/function () { + function Img(src, alt, style) { + this.src = void 0; + this.alt = void 0; + this.classes = void 0; + this.height = void 0; + this.depth = void 0; + this.maxFontSize = void 0; + this.style = void 0; + this.alt = alt; + this.src = src; + this.classes = ["mord"]; + this.style = style; + } + + var _proto3 = Img.prototype; + + _proto3.hasClass = function hasClass(className) { + return utils.contains(this.classes, className); + }; + + _proto3.toNode = function toNode() { + var node = document.createElement("img"); + node.src = this.src; + node.alt = this.alt; + node.className = "mord"; // Apply inline styles + + for (var style in this.style) { + if (this.style.hasOwnProperty(style)) { + // $FlowFixMe + node.style[style] = this.style[style]; + } + } + + return node; + }; + + _proto3.toMarkup = function toMarkup() { + var markup = "" + this.alt + " 0) { + span = document.createElement("span"); + span.style.marginRight = this.italic + "em"; + } + + if (this.classes.length > 0) { + span = span || document.createElement("span"); + span.className = createClass(this.classes); + } + + for (var style in this.style) { + if (this.style.hasOwnProperty(style)) { + span = span || document.createElement("span"); // $FlowFixMe Flow doesn't seem to understand span.style's type. + + span.style[style] = this.style[style]; + } + } + + if (span) { + span.appendChild(node); + return span; + } else { + return node; + } + } + /** + * Creates markup for a symbol node. + */ + ; + + _proto4.toMarkup = function toMarkup() { + // TODO(alpert): More duplication than I'd like from + // span.prototype.toMarkup and symbolNode.prototype.toNode... + var needsSpan = false; + var markup = " 0) { + styles += "margin-right:" + this.italic + "em;"; + } + + for (var style in this.style) { + if (this.style.hasOwnProperty(style)) { + styles += utils.hyphenate(style) + ":" + this.style[style] + ";"; + } + } + + if (styles) { + needsSpan = true; + markup += " style=\"" + utils.escape(styles) + "\""; + } + + var escaped = utils.escape(this.text); + + if (needsSpan) { + markup += ">"; + markup += escaped; + markup += ""; + return markup; + } else { + return escaped; + } + }; + + return SymbolNode; +}(); +/** + * SVG nodes are used to render stretchy wide elements. + */ + +var SvgNode = /*#__PURE__*/function () { + function SvgNode(children, attributes) { + this.children = void 0; + this.attributes = void 0; + this.children = children || []; + this.attributes = attributes || {}; + } + + var _proto5 = SvgNode.prototype; + + _proto5.toNode = function toNode() { + var svgNS = "http://www.w3.org/2000/svg"; + var node = document.createElementNS(svgNS, "svg"); // Apply attributes + + for (var attr in this.attributes) { + if (Object.prototype.hasOwnProperty.call(this.attributes, attr)) { + node.setAttribute(attr, this.attributes[attr]); + } + } + + for (var i = 0; i < this.children.length; i++) { + node.appendChild(this.children[i].toNode()); + } + + return node; + }; + + _proto5.toMarkup = function toMarkup() { + var markup = ""; + } else { + return ""; + } + }; + + return PathNode; +}(); +var LineNode = /*#__PURE__*/function () { + function LineNode(attributes) { + this.attributes = void 0; + this.attributes = attributes || {}; + } + + var _proto7 = LineNode.prototype; + + _proto7.toNode = function toNode() { + var svgNS = "http://www.w3.org/2000/svg"; + var node = document.createElementNS(svgNS, "line"); // Apply attributes + + for (var attr in this.attributes) { + if (Object.prototype.hasOwnProperty.call(this.attributes, attr)) { + node.setAttribute(attr, this.attributes[attr]); + } + } + + return node; + }; + + _proto7.toMarkup = function toMarkup() { + var markup = " but got " + String(group) + "."); + } +} +;// CONCATENATED MODULE: ./src/fontMetricsData.js +// This file is GENERATED by buildMetrics.sh. DO NOT MODIFY. +/* harmony default export */ var fontMetricsData = ({ + "AMS-Regular": { + "32": [0, 0, 0, 0, 0.25], + "65": [0, 0.68889, 0, 0, 0.72222], + "66": [0, 0.68889, 0, 0, 0.66667], + "67": [0, 0.68889, 0, 0, 0.72222], + "68": [0, 0.68889, 0, 0, 0.72222], + "69": [0, 0.68889, 0, 0, 0.66667], + "70": [0, 0.68889, 0, 0, 0.61111], + "71": [0, 0.68889, 0, 0, 0.77778], + "72": [0, 0.68889, 0, 0, 0.77778], + "73": [0, 0.68889, 0, 0, 0.38889], + "74": [0.16667, 0.68889, 0, 0, 0.5], + "75": [0, 0.68889, 0, 0, 0.77778], + "76": [0, 0.68889, 0, 0, 0.66667], + "77": [0, 0.68889, 0, 0, 0.94445], + "78": [0, 0.68889, 0, 0, 0.72222], + "79": [0.16667, 0.68889, 0, 0, 0.77778], + "80": [0, 0.68889, 0, 0, 0.61111], + "81": [0.16667, 0.68889, 0, 0, 0.77778], + "82": [0, 0.68889, 0, 0, 0.72222], + "83": [0, 0.68889, 0, 0, 0.55556], + "84": [0, 0.68889, 0, 0, 0.66667], + "85": [0, 0.68889, 0, 0, 0.72222], + "86": [0, 0.68889, 0, 0, 0.72222], + "87": [0, 0.68889, 0, 0, 1.0], + "88": [0, 0.68889, 0, 0, 0.72222], + "89": [0, 0.68889, 0, 0, 0.72222], + "90": [0, 0.68889, 0, 0, 0.66667], + "107": [0, 0.68889, 0, 0, 0.55556], + "160": [0, 0, 0, 0, 0.25], + "165": [0, 0.675, 0.025, 0, 0.75], + "174": [0.15559, 0.69224, 0, 0, 0.94666], + "240": [0, 0.68889, 0, 0, 0.55556], + "295": [0, 0.68889, 0, 0, 0.54028], + "710": [0, 0.825, 0, 0, 2.33334], + "732": [0, 0.9, 0, 0, 2.33334], + "770": [0, 0.825, 0, 0, 2.33334], + "771": [0, 0.9, 0, 0, 2.33334], + "989": [0.08167, 0.58167, 0, 0, 0.77778], + "1008": [0, 0.43056, 0.04028, 0, 0.66667], + "8245": [0, 0.54986, 0, 0, 0.275], + "8463": [0, 0.68889, 0, 0, 0.54028], + "8487": [0, 0.68889, 0, 0, 0.72222], + "8498": [0, 0.68889, 0, 0, 0.55556], + "8502": [0, 0.68889, 0, 0, 0.66667], + "8503": [0, 0.68889, 0, 0, 0.44445], + "8504": [0, 0.68889, 0, 0, 0.66667], + "8513": [0, 0.68889, 0, 0, 0.63889], + "8592": [-0.03598, 0.46402, 0, 0, 0.5], + "8594": [-0.03598, 0.46402, 0, 0, 0.5], + "8602": [-0.13313, 0.36687, 0, 0, 1.0], + "8603": [-0.13313, 0.36687, 0, 0, 1.0], + "8606": [0.01354, 0.52239, 0, 0, 1.0], + "8608": [0.01354, 0.52239, 0, 0, 1.0], + "8610": [0.01354, 0.52239, 0, 0, 1.11111], + "8611": [0.01354, 0.52239, 0, 0, 1.11111], + "8619": [0, 0.54986, 0, 0, 1.0], + "8620": [0, 0.54986, 0, 0, 1.0], + "8621": [-0.13313, 0.37788, 0, 0, 1.38889], + "8622": [-0.13313, 0.36687, 0, 0, 1.0], + "8624": [0, 0.69224, 0, 0, 0.5], + "8625": [0, 0.69224, 0, 0, 0.5], + "8630": [0, 0.43056, 0, 0, 1.0], + "8631": [0, 0.43056, 0, 0, 1.0], + "8634": [0.08198, 0.58198, 0, 0, 0.77778], + "8635": [0.08198, 0.58198, 0, 0, 0.77778], + "8638": [0.19444, 0.69224, 0, 0, 0.41667], + "8639": [0.19444, 0.69224, 0, 0, 0.41667], + "8642": [0.19444, 0.69224, 0, 0, 0.41667], + "8643": [0.19444, 0.69224, 0, 0, 0.41667], + "8644": [0.1808, 0.675, 0, 0, 1.0], + "8646": [0.1808, 0.675, 0, 0, 1.0], + "8647": [0.1808, 0.675, 0, 0, 1.0], + "8648": [0.19444, 0.69224, 0, 0, 0.83334], + "8649": [0.1808, 0.675, 0, 0, 1.0], + "8650": [0.19444, 0.69224, 0, 0, 0.83334], + "8651": [0.01354, 0.52239, 0, 0, 1.0], + "8652": [0.01354, 0.52239, 0, 0, 1.0], + "8653": [-0.13313, 0.36687, 0, 0, 1.0], + "8654": [-0.13313, 0.36687, 0, 0, 1.0], + "8655": [-0.13313, 0.36687, 0, 0, 1.0], + "8666": [0.13667, 0.63667, 0, 0, 1.0], + "8667": [0.13667, 0.63667, 0, 0, 1.0], + "8669": [-0.13313, 0.37788, 0, 0, 1.0], + "8672": [-0.064, 0.437, 0, 0, 1.334], + "8674": [-0.064, 0.437, 0, 0, 1.334], + "8705": [0, 0.825, 0, 0, 0.5], + "8708": [0, 0.68889, 0, 0, 0.55556], + "8709": [0.08167, 0.58167, 0, 0, 0.77778], + "8717": [0, 0.43056, 0, 0, 0.42917], + "8722": [-0.03598, 0.46402, 0, 0, 0.5], + "8724": [0.08198, 0.69224, 0, 0, 0.77778], + "8726": [0.08167, 0.58167, 0, 0, 0.77778], + "8733": [0, 0.69224, 0, 0, 0.77778], + "8736": [0, 0.69224, 0, 0, 0.72222], + "8737": [0, 0.69224, 0, 0, 0.72222], + "8738": [0.03517, 0.52239, 0, 0, 0.72222], + "8739": [0.08167, 0.58167, 0, 0, 0.22222], + "8740": [0.25142, 0.74111, 0, 0, 0.27778], + "8741": [0.08167, 0.58167, 0, 0, 0.38889], + "8742": [0.25142, 0.74111, 0, 0, 0.5], + "8756": [0, 0.69224, 0, 0, 0.66667], + "8757": [0, 0.69224, 0, 0, 0.66667], + "8764": [-0.13313, 0.36687, 0, 0, 0.77778], + "8765": [-0.13313, 0.37788, 0, 0, 0.77778], + "8769": [-0.13313, 0.36687, 0, 0, 0.77778], + "8770": [-0.03625, 0.46375, 0, 0, 0.77778], + "8774": [0.30274, 0.79383, 0, 0, 0.77778], + "8776": [-0.01688, 0.48312, 0, 0, 0.77778], + "8778": [0.08167, 0.58167, 0, 0, 0.77778], + "8782": [0.06062, 0.54986, 0, 0, 0.77778], + "8783": [0.06062, 0.54986, 0, 0, 0.77778], + "8785": [0.08198, 0.58198, 0, 0, 0.77778], + "8786": [0.08198, 0.58198, 0, 0, 0.77778], + "8787": [0.08198, 0.58198, 0, 0, 0.77778], + "8790": [0, 0.69224, 0, 0, 0.77778], + "8791": [0.22958, 0.72958, 0, 0, 0.77778], + "8796": [0.08198, 0.91667, 0, 0, 0.77778], + "8806": [0.25583, 0.75583, 0, 0, 0.77778], + "8807": [0.25583, 0.75583, 0, 0, 0.77778], + "8808": [0.25142, 0.75726, 0, 0, 0.77778], + "8809": [0.25142, 0.75726, 0, 0, 0.77778], + "8812": [0.25583, 0.75583, 0, 0, 0.5], + "8814": [0.20576, 0.70576, 0, 0, 0.77778], + "8815": [0.20576, 0.70576, 0, 0, 0.77778], + "8816": [0.30274, 0.79383, 0, 0, 0.77778], + "8817": [0.30274, 0.79383, 0, 0, 0.77778], + "8818": [0.22958, 0.72958, 0, 0, 0.77778], + "8819": [0.22958, 0.72958, 0, 0, 0.77778], + "8822": [0.1808, 0.675, 0, 0, 0.77778], + "8823": [0.1808, 0.675, 0, 0, 0.77778], + "8828": [0.13667, 0.63667, 0, 0, 0.77778], + "8829": [0.13667, 0.63667, 0, 0, 0.77778], + "8830": [0.22958, 0.72958, 0, 0, 0.77778], + "8831": [0.22958, 0.72958, 0, 0, 0.77778], + "8832": [0.20576, 0.70576, 0, 0, 0.77778], + "8833": [0.20576, 0.70576, 0, 0, 0.77778], + "8840": [0.30274, 0.79383, 0, 0, 0.77778], + "8841": [0.30274, 0.79383, 0, 0, 0.77778], + "8842": [0.13597, 0.63597, 0, 0, 0.77778], + "8843": [0.13597, 0.63597, 0, 0, 0.77778], + "8847": [0.03517, 0.54986, 0, 0, 0.77778], + "8848": [0.03517, 0.54986, 0, 0, 0.77778], + "8858": [0.08198, 0.58198, 0, 0, 0.77778], + "8859": [0.08198, 0.58198, 0, 0, 0.77778], + "8861": [0.08198, 0.58198, 0, 0, 0.77778], + "8862": [0, 0.675, 0, 0, 0.77778], + "8863": [0, 0.675, 0, 0, 0.77778], + "8864": [0, 0.675, 0, 0, 0.77778], + "8865": [0, 0.675, 0, 0, 0.77778], + "8872": [0, 0.69224, 0, 0, 0.61111], + "8873": [0, 0.69224, 0, 0, 0.72222], + "8874": [0, 0.69224, 0, 0, 0.88889], + "8876": [0, 0.68889, 0, 0, 0.61111], + "8877": [0, 0.68889, 0, 0, 0.61111], + "8878": [0, 0.68889, 0, 0, 0.72222], + "8879": [0, 0.68889, 0, 0, 0.72222], + "8882": [0.03517, 0.54986, 0, 0, 0.77778], + "8883": [0.03517, 0.54986, 0, 0, 0.77778], + "8884": [0.13667, 0.63667, 0, 0, 0.77778], + "8885": [0.13667, 0.63667, 0, 0, 0.77778], + "8888": [0, 0.54986, 0, 0, 1.11111], + "8890": [0.19444, 0.43056, 0, 0, 0.55556], + "8891": [0.19444, 0.69224, 0, 0, 0.61111], + "8892": [0.19444, 0.69224, 0, 0, 0.61111], + "8901": [0, 0.54986, 0, 0, 0.27778], + "8903": [0.08167, 0.58167, 0, 0, 0.77778], + "8905": [0.08167, 0.58167, 0, 0, 0.77778], + "8906": [0.08167, 0.58167, 0, 0, 0.77778], + "8907": [0, 0.69224, 0, 0, 0.77778], + "8908": [0, 0.69224, 0, 0, 0.77778], + "8909": [-0.03598, 0.46402, 0, 0, 0.77778], + "8910": [0, 0.54986, 0, 0, 0.76042], + "8911": [0, 0.54986, 0, 0, 0.76042], + "8912": [0.03517, 0.54986, 0, 0, 0.77778], + "8913": [0.03517, 0.54986, 0, 0, 0.77778], + "8914": [0, 0.54986, 0, 0, 0.66667], + "8915": [0, 0.54986, 0, 0, 0.66667], + "8916": [0, 0.69224, 0, 0, 0.66667], + "8918": [0.0391, 0.5391, 0, 0, 0.77778], + "8919": [0.0391, 0.5391, 0, 0, 0.77778], + "8920": [0.03517, 0.54986, 0, 0, 1.33334], + "8921": [0.03517, 0.54986, 0, 0, 1.33334], + "8922": [0.38569, 0.88569, 0, 0, 0.77778], + "8923": [0.38569, 0.88569, 0, 0, 0.77778], + "8926": [0.13667, 0.63667, 0, 0, 0.77778], + "8927": [0.13667, 0.63667, 0, 0, 0.77778], + "8928": [0.30274, 0.79383, 0, 0, 0.77778], + "8929": [0.30274, 0.79383, 0, 0, 0.77778], + "8934": [0.23222, 0.74111, 0, 0, 0.77778], + "8935": [0.23222, 0.74111, 0, 0, 0.77778], + "8936": [0.23222, 0.74111, 0, 0, 0.77778], + "8937": [0.23222, 0.74111, 0, 0, 0.77778], + "8938": [0.20576, 0.70576, 0, 0, 0.77778], + "8939": [0.20576, 0.70576, 0, 0, 0.77778], + "8940": [0.30274, 0.79383, 0, 0, 0.77778], + "8941": [0.30274, 0.79383, 0, 0, 0.77778], + "8994": [0.19444, 0.69224, 0, 0, 0.77778], + "8995": [0.19444, 0.69224, 0, 0, 0.77778], + "9416": [0.15559, 0.69224, 0, 0, 0.90222], + "9484": [0, 0.69224, 0, 0, 0.5], + "9488": [0, 0.69224, 0, 0, 0.5], + "9492": [0, 0.37788, 0, 0, 0.5], + "9496": [0, 0.37788, 0, 0, 0.5], + "9585": [0.19444, 0.68889, 0, 0, 0.88889], + "9586": [0.19444, 0.74111, 0, 0, 0.88889], + "9632": [0, 0.675, 0, 0, 0.77778], + "9633": [0, 0.675, 0, 0, 0.77778], + "9650": [0, 0.54986, 0, 0, 0.72222], + "9651": [0, 0.54986, 0, 0, 0.72222], + "9654": [0.03517, 0.54986, 0, 0, 0.77778], + "9660": [0, 0.54986, 0, 0, 0.72222], + "9661": [0, 0.54986, 0, 0, 0.72222], + "9664": [0.03517, 0.54986, 0, 0, 0.77778], + "9674": [0.11111, 0.69224, 0, 0, 0.66667], + "9733": [0.19444, 0.69224, 0, 0, 0.94445], + "10003": [0, 0.69224, 0, 0, 0.83334], + "10016": [0, 0.69224, 0, 0, 0.83334], + "10731": [0.11111, 0.69224, 0, 0, 0.66667], + "10846": [0.19444, 0.75583, 0, 0, 0.61111], + "10877": [0.13667, 0.63667, 0, 0, 0.77778], + "10878": [0.13667, 0.63667, 0, 0, 0.77778], + "10885": [0.25583, 0.75583, 0, 0, 0.77778], + "10886": [0.25583, 0.75583, 0, 0, 0.77778], + "10887": [0.13597, 0.63597, 0, 0, 0.77778], + "10888": [0.13597, 0.63597, 0, 0, 0.77778], + "10889": [0.26167, 0.75726, 0, 0, 0.77778], + "10890": [0.26167, 0.75726, 0, 0, 0.77778], + "10891": [0.48256, 0.98256, 0, 0, 0.77778], + "10892": [0.48256, 0.98256, 0, 0, 0.77778], + "10901": [0.13667, 0.63667, 0, 0, 0.77778], + "10902": [0.13667, 0.63667, 0, 0, 0.77778], + "10933": [0.25142, 0.75726, 0, 0, 0.77778], + "10934": [0.25142, 0.75726, 0, 0, 0.77778], + "10935": [0.26167, 0.75726, 0, 0, 0.77778], + "10936": [0.26167, 0.75726, 0, 0, 0.77778], + "10937": [0.26167, 0.75726, 0, 0, 0.77778], + "10938": [0.26167, 0.75726, 0, 0, 0.77778], + "10949": [0.25583, 0.75583, 0, 0, 0.77778], + "10950": [0.25583, 0.75583, 0, 0, 0.77778], + "10955": [0.28481, 0.79383, 0, 0, 0.77778], + "10956": [0.28481, 0.79383, 0, 0, 0.77778], + "57350": [0.08167, 0.58167, 0, 0, 0.22222], + "57351": [0.08167, 0.58167, 0, 0, 0.38889], + "57352": [0.08167, 0.58167, 0, 0, 0.77778], + "57353": [0, 0.43056, 0.04028, 0, 0.66667], + "57356": [0.25142, 0.75726, 0, 0, 0.77778], + "57357": [0.25142, 0.75726, 0, 0, 0.77778], + "57358": [0.41951, 0.91951, 0, 0, 0.77778], + "57359": [0.30274, 0.79383, 0, 0, 0.77778], + "57360": [0.30274, 0.79383, 0, 0, 0.77778], + "57361": [0.41951, 0.91951, 0, 0, 0.77778], + "57366": [0.25142, 0.75726, 0, 0, 0.77778], + "57367": [0.25142, 0.75726, 0, 0, 0.77778], + "57368": [0.25142, 0.75726, 0, 0, 0.77778], + "57369": [0.25142, 0.75726, 0, 0, 0.77778], + "57370": [0.13597, 0.63597, 0, 0, 0.77778], + "57371": [0.13597, 0.63597, 0, 0, 0.77778] + }, + "Caligraphic-Regular": { + "32": [0, 0, 0, 0, 0.25], + "65": [0, 0.68333, 0, 0.19445, 0.79847], + "66": [0, 0.68333, 0.03041, 0.13889, 0.65681], + "67": [0, 0.68333, 0.05834, 0.13889, 0.52653], + "68": [0, 0.68333, 0.02778, 0.08334, 0.77139], + "69": [0, 0.68333, 0.08944, 0.11111, 0.52778], + "70": [0, 0.68333, 0.09931, 0.11111, 0.71875], + "71": [0.09722, 0.68333, 0.0593, 0.11111, 0.59487], + "72": [0, 0.68333, 0.00965, 0.11111, 0.84452], + "73": [0, 0.68333, 0.07382, 0, 0.54452], + "74": [0.09722, 0.68333, 0.18472, 0.16667, 0.67778], + "75": [0, 0.68333, 0.01445, 0.05556, 0.76195], + "76": [0, 0.68333, 0, 0.13889, 0.68972], + "77": [0, 0.68333, 0, 0.13889, 1.2009], + "78": [0, 0.68333, 0.14736, 0.08334, 0.82049], + "79": [0, 0.68333, 0.02778, 0.11111, 0.79611], + "80": [0, 0.68333, 0.08222, 0.08334, 0.69556], + "81": [0.09722, 0.68333, 0, 0.11111, 0.81667], + "82": [0, 0.68333, 0, 0.08334, 0.8475], + "83": [0, 0.68333, 0.075, 0.13889, 0.60556], + "84": [0, 0.68333, 0.25417, 0, 0.54464], + "85": [0, 0.68333, 0.09931, 0.08334, 0.62583], + "86": [0, 0.68333, 0.08222, 0, 0.61278], + "87": [0, 0.68333, 0.08222, 0.08334, 0.98778], + "88": [0, 0.68333, 0.14643, 0.13889, 0.7133], + "89": [0.09722, 0.68333, 0.08222, 0.08334, 0.66834], + "90": [0, 0.68333, 0.07944, 0.13889, 0.72473], + "160": [0, 0, 0, 0, 0.25] + }, + "Fraktur-Regular": { + "32": [0, 0, 0, 0, 0.25], + "33": [0, 0.69141, 0, 0, 0.29574], + "34": [0, 0.69141, 0, 0, 0.21471], + "38": [0, 0.69141, 0, 0, 0.73786], + "39": [0, 0.69141, 0, 0, 0.21201], + "40": [0.24982, 0.74947, 0, 0, 0.38865], + "41": [0.24982, 0.74947, 0, 0, 0.38865], + "42": [0, 0.62119, 0, 0, 0.27764], + "43": [0.08319, 0.58283, 0, 0, 0.75623], + "44": [0, 0.10803, 0, 0, 0.27764], + "45": [0.08319, 0.58283, 0, 0, 0.75623], + "46": [0, 0.10803, 0, 0, 0.27764], + "47": [0.24982, 0.74947, 0, 0, 0.50181], + "48": [0, 0.47534, 0, 0, 0.50181], + "49": [0, 0.47534, 0, 0, 0.50181], + "50": [0, 0.47534, 0, 0, 0.50181], + "51": [0.18906, 0.47534, 0, 0, 0.50181], + "52": [0.18906, 0.47534, 0, 0, 0.50181], + "53": [0.18906, 0.47534, 0, 0, 0.50181], + "54": [0, 0.69141, 0, 0, 0.50181], + "55": [0.18906, 0.47534, 0, 0, 0.50181], + "56": [0, 0.69141, 0, 0, 0.50181], + "57": [0.18906, 0.47534, 0, 0, 0.50181], + "58": [0, 0.47534, 0, 0, 0.21606], + "59": [0.12604, 0.47534, 0, 0, 0.21606], + "61": [-0.13099, 0.36866, 0, 0, 0.75623], + "63": [0, 0.69141, 0, 0, 0.36245], + "65": [0, 0.69141, 0, 0, 0.7176], + "66": [0, 0.69141, 0, 0, 0.88397], + "67": [0, 0.69141, 0, 0, 0.61254], + "68": [0, 0.69141, 0, 0, 0.83158], + "69": [0, 0.69141, 0, 0, 0.66278], + "70": [0.12604, 0.69141, 0, 0, 0.61119], + "71": [0, 0.69141, 0, 0, 0.78539], + "72": [0.06302, 0.69141, 0, 0, 0.7203], + "73": [0, 0.69141, 0, 0, 0.55448], + "74": [0.12604, 0.69141, 0, 0, 0.55231], + "75": [0, 0.69141, 0, 0, 0.66845], + "76": [0, 0.69141, 0, 0, 0.66602], + "77": [0, 0.69141, 0, 0, 1.04953], + "78": [0, 0.69141, 0, 0, 0.83212], + "79": [0, 0.69141, 0, 0, 0.82699], + "80": [0.18906, 0.69141, 0, 0, 0.82753], + "81": [0.03781, 0.69141, 0, 0, 0.82699], + "82": [0, 0.69141, 0, 0, 0.82807], + "83": [0, 0.69141, 0, 0, 0.82861], + "84": [0, 0.69141, 0, 0, 0.66899], + "85": [0, 0.69141, 0, 0, 0.64576], + "86": [0, 0.69141, 0, 0, 0.83131], + "87": [0, 0.69141, 0, 0, 1.04602], + "88": [0, 0.69141, 0, 0, 0.71922], + "89": [0.18906, 0.69141, 0, 0, 0.83293], + "90": [0.12604, 0.69141, 0, 0, 0.60201], + "91": [0.24982, 0.74947, 0, 0, 0.27764], + "93": [0.24982, 0.74947, 0, 0, 0.27764], + "94": [0, 0.69141, 0, 0, 0.49965], + "97": [0, 0.47534, 0, 0, 0.50046], + "98": [0, 0.69141, 0, 0, 0.51315], + "99": [0, 0.47534, 0, 0, 0.38946], + "100": [0, 0.62119, 0, 0, 0.49857], + "101": [0, 0.47534, 0, 0, 0.40053], + "102": [0.18906, 0.69141, 0, 0, 0.32626], + "103": [0.18906, 0.47534, 0, 0, 0.5037], + "104": [0.18906, 0.69141, 0, 0, 0.52126], + "105": [0, 0.69141, 0, 0, 0.27899], + "106": [0, 0.69141, 0, 0, 0.28088], + "107": [0, 0.69141, 0, 0, 0.38946], + "108": [0, 0.69141, 0, 0, 0.27953], + "109": [0, 0.47534, 0, 0, 0.76676], + "110": [0, 0.47534, 0, 0, 0.52666], + "111": [0, 0.47534, 0, 0, 0.48885], + "112": [0.18906, 0.52396, 0, 0, 0.50046], + "113": [0.18906, 0.47534, 0, 0, 0.48912], + "114": [0, 0.47534, 0, 0, 0.38919], + "115": [0, 0.47534, 0, 0, 0.44266], + "116": [0, 0.62119, 0, 0, 0.33301], + "117": [0, 0.47534, 0, 0, 0.5172], + "118": [0, 0.52396, 0, 0, 0.5118], + "119": [0, 0.52396, 0, 0, 0.77351], + "120": [0.18906, 0.47534, 0, 0, 0.38865], + "121": [0.18906, 0.47534, 0, 0, 0.49884], + "122": [0.18906, 0.47534, 0, 0, 0.39054], + "160": [0, 0, 0, 0, 0.25], + "8216": [0, 0.69141, 0, 0, 0.21471], + "8217": [0, 0.69141, 0, 0, 0.21471], + "58112": [0, 0.62119, 0, 0, 0.49749], + "58113": [0, 0.62119, 0, 0, 0.4983], + "58114": [0.18906, 0.69141, 0, 0, 0.33328], + "58115": [0.18906, 0.69141, 0, 0, 0.32923], + "58116": [0.18906, 0.47534, 0, 0, 0.50343], + "58117": [0, 0.69141, 0, 0, 0.33301], + "58118": [0, 0.62119, 0, 0, 0.33409], + "58119": [0, 0.47534, 0, 0, 0.50073] + }, + "Main-Bold": { + "32": [0, 0, 0, 0, 0.25], + "33": [0, 0.69444, 0, 0, 0.35], + "34": [0, 0.69444, 0, 0, 0.60278], + "35": [0.19444, 0.69444, 0, 0, 0.95833], + "36": [0.05556, 0.75, 0, 0, 0.575], + "37": [0.05556, 0.75, 0, 0, 0.95833], + "38": [0, 0.69444, 0, 0, 0.89444], + "39": [0, 0.69444, 0, 0, 0.31944], + "40": [0.25, 0.75, 0, 0, 0.44722], + "41": [0.25, 0.75, 0, 0, 0.44722], + "42": [0, 0.75, 0, 0, 0.575], + "43": [0.13333, 0.63333, 0, 0, 0.89444], + "44": [0.19444, 0.15556, 0, 0, 0.31944], + "45": [0, 0.44444, 0, 0, 0.38333], + "46": [0, 0.15556, 0, 0, 0.31944], + "47": [0.25, 0.75, 0, 0, 0.575], + "48": [0, 0.64444, 0, 0, 0.575], + "49": [0, 0.64444, 0, 0, 0.575], + "50": [0, 0.64444, 0, 0, 0.575], + "51": [0, 0.64444, 0, 0, 0.575], + "52": [0, 0.64444, 0, 0, 0.575], + "53": [0, 0.64444, 0, 0, 0.575], + "54": [0, 0.64444, 0, 0, 0.575], + "55": [0, 0.64444, 0, 0, 0.575], + "56": [0, 0.64444, 0, 0, 0.575], + "57": [0, 0.64444, 0, 0, 0.575], + "58": [0, 0.44444, 0, 0, 0.31944], + "59": [0.19444, 0.44444, 0, 0, 0.31944], + "60": [0.08556, 0.58556, 0, 0, 0.89444], + "61": [-0.10889, 0.39111, 0, 0, 0.89444], + "62": [0.08556, 0.58556, 0, 0, 0.89444], + "63": [0, 0.69444, 0, 0, 0.54305], + "64": [0, 0.69444, 0, 0, 0.89444], + "65": [0, 0.68611, 0, 0, 0.86944], + "66": [0, 0.68611, 0, 0, 0.81805], + "67": [0, 0.68611, 0, 0, 0.83055], + "68": [0, 0.68611, 0, 0, 0.88194], + "69": [0, 0.68611, 0, 0, 0.75555], + "70": [0, 0.68611, 0, 0, 0.72361], + "71": [0, 0.68611, 0, 0, 0.90416], + "72": [0, 0.68611, 0, 0, 0.9], + "73": [0, 0.68611, 0, 0, 0.43611], + "74": [0, 0.68611, 0, 0, 0.59444], + "75": [0, 0.68611, 0, 0, 0.90138], + "76": [0, 0.68611, 0, 0, 0.69166], + "77": [0, 0.68611, 0, 0, 1.09166], + "78": [0, 0.68611, 0, 0, 0.9], + "79": [0, 0.68611, 0, 0, 0.86388], + "80": [0, 0.68611, 0, 0, 0.78611], + "81": [0.19444, 0.68611, 0, 0, 0.86388], + "82": [0, 0.68611, 0, 0, 0.8625], + "83": [0, 0.68611, 0, 0, 0.63889], + "84": [0, 0.68611, 0, 0, 0.8], + "85": [0, 0.68611, 0, 0, 0.88472], + "86": [0, 0.68611, 0.01597, 0, 0.86944], + "87": [0, 0.68611, 0.01597, 0, 1.18888], + "88": [0, 0.68611, 0, 0, 0.86944], + "89": [0, 0.68611, 0.02875, 0, 0.86944], + "90": [0, 0.68611, 0, 0, 0.70277], + "91": [0.25, 0.75, 0, 0, 0.31944], + "92": [0.25, 0.75, 0, 0, 0.575], + "93": [0.25, 0.75, 0, 0, 0.31944], + "94": [0, 0.69444, 0, 0, 0.575], + "95": [0.31, 0.13444, 0.03194, 0, 0.575], + "97": [0, 0.44444, 0, 0, 0.55902], + "98": [0, 0.69444, 0, 0, 0.63889], + "99": [0, 0.44444, 0, 0, 0.51111], + "100": [0, 0.69444, 0, 0, 0.63889], + "101": [0, 0.44444, 0, 0, 0.52708], + "102": [0, 0.69444, 0.10903, 0, 0.35139], + "103": [0.19444, 0.44444, 0.01597, 0, 0.575], + "104": [0, 0.69444, 0, 0, 0.63889], + "105": [0, 0.69444, 0, 0, 0.31944], + "106": [0.19444, 0.69444, 0, 0, 0.35139], + "107": [0, 0.69444, 0, 0, 0.60694], + "108": [0, 0.69444, 0, 0, 0.31944], + "109": [0, 0.44444, 0, 0, 0.95833], + "110": [0, 0.44444, 0, 0, 0.63889], + "111": [0, 0.44444, 0, 0, 0.575], + "112": [0.19444, 0.44444, 0, 0, 0.63889], + "113": [0.19444, 0.44444, 0, 0, 0.60694], + "114": [0, 0.44444, 0, 0, 0.47361], + "115": [0, 0.44444, 0, 0, 0.45361], + "116": [0, 0.63492, 0, 0, 0.44722], + "117": [0, 0.44444, 0, 0, 0.63889], + "118": [0, 0.44444, 0.01597, 0, 0.60694], + "119": [0, 0.44444, 0.01597, 0, 0.83055], + "120": [0, 0.44444, 0, 0, 0.60694], + "121": [0.19444, 0.44444, 0.01597, 0, 0.60694], + "122": [0, 0.44444, 0, 0, 0.51111], + "123": [0.25, 0.75, 0, 0, 0.575], + "124": [0.25, 0.75, 0, 0, 0.31944], + "125": [0.25, 0.75, 0, 0, 0.575], + "126": [0.35, 0.34444, 0, 0, 0.575], + "160": [0, 0, 0, 0, 0.25], + "163": [0, 0.69444, 0, 0, 0.86853], + "168": [0, 0.69444, 0, 0, 0.575], + "172": [0, 0.44444, 0, 0, 0.76666], + "176": [0, 0.69444, 0, 0, 0.86944], + "177": [0.13333, 0.63333, 0, 0, 0.89444], + "184": [0.17014, 0, 0, 0, 0.51111], + "198": [0, 0.68611, 0, 0, 1.04166], + "215": [0.13333, 0.63333, 0, 0, 0.89444], + "216": [0.04861, 0.73472, 0, 0, 0.89444], + "223": [0, 0.69444, 0, 0, 0.59722], + "230": [0, 0.44444, 0, 0, 0.83055], + "247": [0.13333, 0.63333, 0, 0, 0.89444], + "248": [0.09722, 0.54167, 0, 0, 0.575], + "305": [0, 0.44444, 0, 0, 0.31944], + "338": [0, 0.68611, 0, 0, 1.16944], + "339": [0, 0.44444, 0, 0, 0.89444], + "567": [0.19444, 0.44444, 0, 0, 0.35139], + "710": [0, 0.69444, 0, 0, 0.575], + "711": [0, 0.63194, 0, 0, 0.575], + "713": [0, 0.59611, 0, 0, 0.575], + "714": [0, 0.69444, 0, 0, 0.575], + "715": [0, 0.69444, 0, 0, 0.575], + "728": [0, 0.69444, 0, 0, 0.575], + "729": [0, 0.69444, 0, 0, 0.31944], + "730": [0, 0.69444, 0, 0, 0.86944], + "732": [0, 0.69444, 0, 0, 0.575], + "733": [0, 0.69444, 0, 0, 0.575], + "915": [0, 0.68611, 0, 0, 0.69166], + "916": [0, 0.68611, 0, 0, 0.95833], + "920": [0, 0.68611, 0, 0, 0.89444], + "923": [0, 0.68611, 0, 0, 0.80555], + "926": [0, 0.68611, 0, 0, 0.76666], + "928": [0, 0.68611, 0, 0, 0.9], + "931": [0, 0.68611, 0, 0, 0.83055], + "933": [0, 0.68611, 0, 0, 0.89444], + "934": [0, 0.68611, 0, 0, 0.83055], + "936": [0, 0.68611, 0, 0, 0.89444], + "937": [0, 0.68611, 0, 0, 0.83055], + "8211": [0, 0.44444, 0.03194, 0, 0.575], + "8212": [0, 0.44444, 0.03194, 0, 1.14999], + "8216": [0, 0.69444, 0, 0, 0.31944], + "8217": [0, 0.69444, 0, 0, 0.31944], + "8220": [0, 0.69444, 0, 0, 0.60278], + "8221": [0, 0.69444, 0, 0, 0.60278], + "8224": [0.19444, 0.69444, 0, 0, 0.51111], + "8225": [0.19444, 0.69444, 0, 0, 0.51111], + "8242": [0, 0.55556, 0, 0, 0.34444], + "8407": [0, 0.72444, 0.15486, 0, 0.575], + "8463": [0, 0.69444, 0, 0, 0.66759], + "8465": [0, 0.69444, 0, 0, 0.83055], + "8467": [0, 0.69444, 0, 0, 0.47361], + "8472": [0.19444, 0.44444, 0, 0, 0.74027], + "8476": [0, 0.69444, 0, 0, 0.83055], + "8501": [0, 0.69444, 0, 0, 0.70277], + "8592": [-0.10889, 0.39111, 0, 0, 1.14999], + "8593": [0.19444, 0.69444, 0, 0, 0.575], + "8594": [-0.10889, 0.39111, 0, 0, 1.14999], + "8595": [0.19444, 0.69444, 0, 0, 0.575], + "8596": [-0.10889, 0.39111, 0, 0, 1.14999], + "8597": [0.25, 0.75, 0, 0, 0.575], + "8598": [0.19444, 0.69444, 0, 0, 1.14999], + "8599": [0.19444, 0.69444, 0, 0, 1.14999], + "8600": [0.19444, 0.69444, 0, 0, 1.14999], + "8601": [0.19444, 0.69444, 0, 0, 1.14999], + "8636": [-0.10889, 0.39111, 0, 0, 1.14999], + "8637": [-0.10889, 0.39111, 0, 0, 1.14999], + "8640": [-0.10889, 0.39111, 0, 0, 1.14999], + "8641": [-0.10889, 0.39111, 0, 0, 1.14999], + "8656": [-0.10889, 0.39111, 0, 0, 1.14999], + "8657": [0.19444, 0.69444, 0, 0, 0.70277], + "8658": [-0.10889, 0.39111, 0, 0, 1.14999], + "8659": [0.19444, 0.69444, 0, 0, 0.70277], + "8660": [-0.10889, 0.39111, 0, 0, 1.14999], + "8661": [0.25, 0.75, 0, 0, 0.70277], + "8704": [0, 0.69444, 0, 0, 0.63889], + "8706": [0, 0.69444, 0.06389, 0, 0.62847], + "8707": [0, 0.69444, 0, 0, 0.63889], + "8709": [0.05556, 0.75, 0, 0, 0.575], + "8711": [0, 0.68611, 0, 0, 0.95833], + "8712": [0.08556, 0.58556, 0, 0, 0.76666], + "8715": [0.08556, 0.58556, 0, 0, 0.76666], + "8722": [0.13333, 0.63333, 0, 0, 0.89444], + "8723": [0.13333, 0.63333, 0, 0, 0.89444], + "8725": [0.25, 0.75, 0, 0, 0.575], + "8726": [0.25, 0.75, 0, 0, 0.575], + "8727": [-0.02778, 0.47222, 0, 0, 0.575], + "8728": [-0.02639, 0.47361, 0, 0, 0.575], + "8729": [-0.02639, 0.47361, 0, 0, 0.575], + "8730": [0.18, 0.82, 0, 0, 0.95833], + "8733": [0, 0.44444, 0, 0, 0.89444], + "8734": [0, 0.44444, 0, 0, 1.14999], + "8736": [0, 0.69224, 0, 0, 0.72222], + "8739": [0.25, 0.75, 0, 0, 0.31944], + "8741": [0.25, 0.75, 0, 0, 0.575], + "8743": [0, 0.55556, 0, 0, 0.76666], + "8744": [0, 0.55556, 0, 0, 0.76666], + "8745": [0, 0.55556, 0, 0, 0.76666], + "8746": [0, 0.55556, 0, 0, 0.76666], + "8747": [0.19444, 0.69444, 0.12778, 0, 0.56875], + "8764": [-0.10889, 0.39111, 0, 0, 0.89444], + "8768": [0.19444, 0.69444, 0, 0, 0.31944], + "8771": [0.00222, 0.50222, 0, 0, 0.89444], + "8776": [0.02444, 0.52444, 0, 0, 0.89444], + "8781": [0.00222, 0.50222, 0, 0, 0.89444], + "8801": [0.00222, 0.50222, 0, 0, 0.89444], + "8804": [0.19667, 0.69667, 0, 0, 0.89444], + "8805": [0.19667, 0.69667, 0, 0, 0.89444], + "8810": [0.08556, 0.58556, 0, 0, 1.14999], + "8811": [0.08556, 0.58556, 0, 0, 1.14999], + "8826": [0.08556, 0.58556, 0, 0, 0.89444], + "8827": [0.08556, 0.58556, 0, 0, 0.89444], + "8834": [0.08556, 0.58556, 0, 0, 0.89444], + "8835": [0.08556, 0.58556, 0, 0, 0.89444], + "8838": [0.19667, 0.69667, 0, 0, 0.89444], + "8839": [0.19667, 0.69667, 0, 0, 0.89444], + "8846": [0, 0.55556, 0, 0, 0.76666], + "8849": [0.19667, 0.69667, 0, 0, 0.89444], + "8850": [0.19667, 0.69667, 0, 0, 0.89444], + "8851": [0, 0.55556, 0, 0, 0.76666], + "8852": [0, 0.55556, 0, 0, 0.76666], + "8853": [0.13333, 0.63333, 0, 0, 0.89444], + "8854": [0.13333, 0.63333, 0, 0, 0.89444], + "8855": [0.13333, 0.63333, 0, 0, 0.89444], + "8856": [0.13333, 0.63333, 0, 0, 0.89444], + "8857": [0.13333, 0.63333, 0, 0, 0.89444], + "8866": [0, 0.69444, 0, 0, 0.70277], + "8867": [0, 0.69444, 0, 0, 0.70277], + "8868": [0, 0.69444, 0, 0, 0.89444], + "8869": [0, 0.69444, 0, 0, 0.89444], + "8900": [-0.02639, 0.47361, 0, 0, 0.575], + "8901": [-0.02639, 0.47361, 0, 0, 0.31944], + "8902": [-0.02778, 0.47222, 0, 0, 0.575], + "8968": [0.25, 0.75, 0, 0, 0.51111], + "8969": [0.25, 0.75, 0, 0, 0.51111], + "8970": [0.25, 0.75, 0, 0, 0.51111], + "8971": [0.25, 0.75, 0, 0, 0.51111], + "8994": [-0.13889, 0.36111, 0, 0, 1.14999], + "8995": [-0.13889, 0.36111, 0, 0, 1.14999], + "9651": [0.19444, 0.69444, 0, 0, 1.02222], + "9657": [-0.02778, 0.47222, 0, 0, 0.575], + "9661": [0.19444, 0.69444, 0, 0, 1.02222], + "9667": [-0.02778, 0.47222, 0, 0, 0.575], + "9711": [0.19444, 0.69444, 0, 0, 1.14999], + "9824": [0.12963, 0.69444, 0, 0, 0.89444], + "9825": [0.12963, 0.69444, 0, 0, 0.89444], + "9826": [0.12963, 0.69444, 0, 0, 0.89444], + "9827": [0.12963, 0.69444, 0, 0, 0.89444], + "9837": [0, 0.75, 0, 0, 0.44722], + "9838": [0.19444, 0.69444, 0, 0, 0.44722], + "9839": [0.19444, 0.69444, 0, 0, 0.44722], + "10216": [0.25, 0.75, 0, 0, 0.44722], + "10217": [0.25, 0.75, 0, 0, 0.44722], + "10815": [0, 0.68611, 0, 0, 0.9], + "10927": [0.19667, 0.69667, 0, 0, 0.89444], + "10928": [0.19667, 0.69667, 0, 0, 0.89444], + "57376": [0.19444, 0.69444, 0, 0, 0] + }, + "Main-BoldItalic": { + "32": [0, 0, 0, 0, 0.25], + "33": [0, 0.69444, 0.11417, 0, 0.38611], + "34": [0, 0.69444, 0.07939, 0, 0.62055], + "35": [0.19444, 0.69444, 0.06833, 0, 0.94444], + "37": [0.05556, 0.75, 0.12861, 0, 0.94444], + "38": [0, 0.69444, 0.08528, 0, 0.88555], + "39": [0, 0.69444, 0.12945, 0, 0.35555], + "40": [0.25, 0.75, 0.15806, 0, 0.47333], + "41": [0.25, 0.75, 0.03306, 0, 0.47333], + "42": [0, 0.75, 0.14333, 0, 0.59111], + "43": [0.10333, 0.60333, 0.03306, 0, 0.88555], + "44": [0.19444, 0.14722, 0, 0, 0.35555], + "45": [0, 0.44444, 0.02611, 0, 0.41444], + "46": [0, 0.14722, 0, 0, 0.35555], + "47": [0.25, 0.75, 0.15806, 0, 0.59111], + "48": [0, 0.64444, 0.13167, 0, 0.59111], + "49": [0, 0.64444, 0.13167, 0, 0.59111], + "50": [0, 0.64444, 0.13167, 0, 0.59111], + "51": [0, 0.64444, 0.13167, 0, 0.59111], + "52": [0.19444, 0.64444, 0.13167, 0, 0.59111], + "53": [0, 0.64444, 0.13167, 0, 0.59111], + "54": [0, 0.64444, 0.13167, 0, 0.59111], + "55": [0.19444, 0.64444, 0.13167, 0, 0.59111], + "56": [0, 0.64444, 0.13167, 0, 0.59111], + "57": [0, 0.64444, 0.13167, 0, 0.59111], + "58": [0, 0.44444, 0.06695, 0, 0.35555], + "59": [0.19444, 0.44444, 0.06695, 0, 0.35555], + "61": [-0.10889, 0.39111, 0.06833, 0, 0.88555], + "63": [0, 0.69444, 0.11472, 0, 0.59111], + "64": [0, 0.69444, 0.09208, 0, 0.88555], + "65": [0, 0.68611, 0, 0, 0.86555], + "66": [0, 0.68611, 0.0992, 0, 0.81666], + "67": [0, 0.68611, 0.14208, 0, 0.82666], + "68": [0, 0.68611, 0.09062, 0, 0.87555], + "69": [0, 0.68611, 0.11431, 0, 0.75666], + "70": [0, 0.68611, 0.12903, 0, 0.72722], + "71": [0, 0.68611, 0.07347, 0, 0.89527], + "72": [0, 0.68611, 0.17208, 0, 0.8961], + "73": [0, 0.68611, 0.15681, 0, 0.47166], + "74": [0, 0.68611, 0.145, 0, 0.61055], + "75": [0, 0.68611, 0.14208, 0, 0.89499], + "76": [0, 0.68611, 0, 0, 0.69777], + "77": [0, 0.68611, 0.17208, 0, 1.07277], + "78": [0, 0.68611, 0.17208, 0, 0.8961], + "79": [0, 0.68611, 0.09062, 0, 0.85499], + "80": [0, 0.68611, 0.0992, 0, 0.78721], + "81": [0.19444, 0.68611, 0.09062, 0, 0.85499], + "82": [0, 0.68611, 0.02559, 0, 0.85944], + "83": [0, 0.68611, 0.11264, 0, 0.64999], + "84": [0, 0.68611, 0.12903, 0, 0.7961], + "85": [0, 0.68611, 0.17208, 0, 0.88083], + "86": [0, 0.68611, 0.18625, 0, 0.86555], + "87": [0, 0.68611, 0.18625, 0, 1.15999], + "88": [0, 0.68611, 0.15681, 0, 0.86555], + "89": [0, 0.68611, 0.19803, 0, 0.86555], + "90": [0, 0.68611, 0.14208, 0, 0.70888], + "91": [0.25, 0.75, 0.1875, 0, 0.35611], + "93": [0.25, 0.75, 0.09972, 0, 0.35611], + "94": [0, 0.69444, 0.06709, 0, 0.59111], + "95": [0.31, 0.13444, 0.09811, 0, 0.59111], + "97": [0, 0.44444, 0.09426, 0, 0.59111], + "98": [0, 0.69444, 0.07861, 0, 0.53222], + "99": [0, 0.44444, 0.05222, 0, 0.53222], + "100": [0, 0.69444, 0.10861, 0, 0.59111], + "101": [0, 0.44444, 0.085, 0, 0.53222], + "102": [0.19444, 0.69444, 0.21778, 0, 0.4], + "103": [0.19444, 0.44444, 0.105, 0, 0.53222], + "104": [0, 0.69444, 0.09426, 0, 0.59111], + "105": [0, 0.69326, 0.11387, 0, 0.35555], + "106": [0.19444, 0.69326, 0.1672, 0, 0.35555], + "107": [0, 0.69444, 0.11111, 0, 0.53222], + "108": [0, 0.69444, 0.10861, 0, 0.29666], + "109": [0, 0.44444, 0.09426, 0, 0.94444], + "110": [0, 0.44444, 0.09426, 0, 0.64999], + "111": [0, 0.44444, 0.07861, 0, 0.59111], + "112": [0.19444, 0.44444, 0.07861, 0, 0.59111], + "113": [0.19444, 0.44444, 0.105, 0, 0.53222], + "114": [0, 0.44444, 0.11111, 0, 0.50167], + "115": [0, 0.44444, 0.08167, 0, 0.48694], + "116": [0, 0.63492, 0.09639, 0, 0.385], + "117": [0, 0.44444, 0.09426, 0, 0.62055], + "118": [0, 0.44444, 0.11111, 0, 0.53222], + "119": [0, 0.44444, 0.11111, 0, 0.76777], + "120": [0, 0.44444, 0.12583, 0, 0.56055], + "121": [0.19444, 0.44444, 0.105, 0, 0.56166], + "122": [0, 0.44444, 0.13889, 0, 0.49055], + "126": [0.35, 0.34444, 0.11472, 0, 0.59111], + "160": [0, 0, 0, 0, 0.25], + "168": [0, 0.69444, 0.11473, 0, 0.59111], + "176": [0, 0.69444, 0, 0, 0.94888], + "184": [0.17014, 0, 0, 0, 0.53222], + "198": [0, 0.68611, 0.11431, 0, 1.02277], + "216": [0.04861, 0.73472, 0.09062, 0, 0.88555], + "223": [0.19444, 0.69444, 0.09736, 0, 0.665], + "230": [0, 0.44444, 0.085, 0, 0.82666], + "248": [0.09722, 0.54167, 0.09458, 0, 0.59111], + "305": [0, 0.44444, 0.09426, 0, 0.35555], + "338": [0, 0.68611, 0.11431, 0, 1.14054], + "339": [0, 0.44444, 0.085, 0, 0.82666], + "567": [0.19444, 0.44444, 0.04611, 0, 0.385], + "710": [0, 0.69444, 0.06709, 0, 0.59111], + "711": [0, 0.63194, 0.08271, 0, 0.59111], + "713": [0, 0.59444, 0.10444, 0, 0.59111], + "714": [0, 0.69444, 0.08528, 0, 0.59111], + "715": [0, 0.69444, 0, 0, 0.59111], + "728": [0, 0.69444, 0.10333, 0, 0.59111], + "729": [0, 0.69444, 0.12945, 0, 0.35555], + "730": [0, 0.69444, 0, 0, 0.94888], + "732": [0, 0.69444, 0.11472, 0, 0.59111], + "733": [0, 0.69444, 0.11472, 0, 0.59111], + "915": [0, 0.68611, 0.12903, 0, 0.69777], + "916": [0, 0.68611, 0, 0, 0.94444], + "920": [0, 0.68611, 0.09062, 0, 0.88555], + "923": [0, 0.68611, 0, 0, 0.80666], + "926": [0, 0.68611, 0.15092, 0, 0.76777], + "928": [0, 0.68611, 0.17208, 0, 0.8961], + "931": [0, 0.68611, 0.11431, 0, 0.82666], + "933": [0, 0.68611, 0.10778, 0, 0.88555], + "934": [0, 0.68611, 0.05632, 0, 0.82666], + "936": [0, 0.68611, 0.10778, 0, 0.88555], + "937": [0, 0.68611, 0.0992, 0, 0.82666], + "8211": [0, 0.44444, 0.09811, 0, 0.59111], + "8212": [0, 0.44444, 0.09811, 0, 1.18221], + "8216": [0, 0.69444, 0.12945, 0, 0.35555], + "8217": [0, 0.69444, 0.12945, 0, 0.35555], + "8220": [0, 0.69444, 0.16772, 0, 0.62055], + "8221": [0, 0.69444, 0.07939, 0, 0.62055] + }, + "Main-Italic": { + "32": [0, 0, 0, 0, 0.25], + "33": [0, 0.69444, 0.12417, 0, 0.30667], + "34": [0, 0.69444, 0.06961, 0, 0.51444], + "35": [0.19444, 0.69444, 0.06616, 0, 0.81777], + "37": [0.05556, 0.75, 0.13639, 0, 0.81777], + "38": [0, 0.69444, 0.09694, 0, 0.76666], + "39": [0, 0.69444, 0.12417, 0, 0.30667], + "40": [0.25, 0.75, 0.16194, 0, 0.40889], + "41": [0.25, 0.75, 0.03694, 0, 0.40889], + "42": [0, 0.75, 0.14917, 0, 0.51111], + "43": [0.05667, 0.56167, 0.03694, 0, 0.76666], + "44": [0.19444, 0.10556, 0, 0, 0.30667], + "45": [0, 0.43056, 0.02826, 0, 0.35778], + "46": [0, 0.10556, 0, 0, 0.30667], + "47": [0.25, 0.75, 0.16194, 0, 0.51111], + "48": [0, 0.64444, 0.13556, 0, 0.51111], + "49": [0, 0.64444, 0.13556, 0, 0.51111], + "50": [0, 0.64444, 0.13556, 0, 0.51111], + "51": [0, 0.64444, 0.13556, 0, 0.51111], + "52": [0.19444, 0.64444, 0.13556, 0, 0.51111], + "53": [0, 0.64444, 0.13556, 0, 0.51111], + "54": [0, 0.64444, 0.13556, 0, 0.51111], + "55": [0.19444, 0.64444, 0.13556, 0, 0.51111], + "56": [0, 0.64444, 0.13556, 0, 0.51111], + "57": [0, 0.64444, 0.13556, 0, 0.51111], + "58": [0, 0.43056, 0.0582, 0, 0.30667], + "59": [0.19444, 0.43056, 0.0582, 0, 0.30667], + "61": [-0.13313, 0.36687, 0.06616, 0, 0.76666], + "63": [0, 0.69444, 0.1225, 0, 0.51111], + "64": [0, 0.69444, 0.09597, 0, 0.76666], + "65": [0, 0.68333, 0, 0, 0.74333], + "66": [0, 0.68333, 0.10257, 0, 0.70389], + "67": [0, 0.68333, 0.14528, 0, 0.71555], + "68": [0, 0.68333, 0.09403, 0, 0.755], + "69": [0, 0.68333, 0.12028, 0, 0.67833], + "70": [0, 0.68333, 0.13305, 0, 0.65277], + "71": [0, 0.68333, 0.08722, 0, 0.77361], + "72": [0, 0.68333, 0.16389, 0, 0.74333], + "73": [0, 0.68333, 0.15806, 0, 0.38555], + "74": [0, 0.68333, 0.14028, 0, 0.525], + "75": [0, 0.68333, 0.14528, 0, 0.76888], + "76": [0, 0.68333, 0, 0, 0.62722], + "77": [0, 0.68333, 0.16389, 0, 0.89666], + "78": [0, 0.68333, 0.16389, 0, 0.74333], + "79": [0, 0.68333, 0.09403, 0, 0.76666], + "80": [0, 0.68333, 0.10257, 0, 0.67833], + "81": [0.19444, 0.68333, 0.09403, 0, 0.76666], + "82": [0, 0.68333, 0.03868, 0, 0.72944], + "83": [0, 0.68333, 0.11972, 0, 0.56222], + "84": [0, 0.68333, 0.13305, 0, 0.71555], + "85": [0, 0.68333, 0.16389, 0, 0.74333], + "86": [0, 0.68333, 0.18361, 0, 0.74333], + "87": [0, 0.68333, 0.18361, 0, 0.99888], + "88": [0, 0.68333, 0.15806, 0, 0.74333], + "89": [0, 0.68333, 0.19383, 0, 0.74333], + "90": [0, 0.68333, 0.14528, 0, 0.61333], + "91": [0.25, 0.75, 0.1875, 0, 0.30667], + "93": [0.25, 0.75, 0.10528, 0, 0.30667], + "94": [0, 0.69444, 0.06646, 0, 0.51111], + "95": [0.31, 0.12056, 0.09208, 0, 0.51111], + "97": [0, 0.43056, 0.07671, 0, 0.51111], + "98": [0, 0.69444, 0.06312, 0, 0.46], + "99": [0, 0.43056, 0.05653, 0, 0.46], + "100": [0, 0.69444, 0.10333, 0, 0.51111], + "101": [0, 0.43056, 0.07514, 0, 0.46], + "102": [0.19444, 0.69444, 0.21194, 0, 0.30667], + "103": [0.19444, 0.43056, 0.08847, 0, 0.46], + "104": [0, 0.69444, 0.07671, 0, 0.51111], + "105": [0, 0.65536, 0.1019, 0, 0.30667], + "106": [0.19444, 0.65536, 0.14467, 0, 0.30667], + "107": [0, 0.69444, 0.10764, 0, 0.46], + "108": [0, 0.69444, 0.10333, 0, 0.25555], + "109": [0, 0.43056, 0.07671, 0, 0.81777], + "110": [0, 0.43056, 0.07671, 0, 0.56222], + "111": [0, 0.43056, 0.06312, 0, 0.51111], + "112": [0.19444, 0.43056, 0.06312, 0, 0.51111], + "113": [0.19444, 0.43056, 0.08847, 0, 0.46], + "114": [0, 0.43056, 0.10764, 0, 0.42166], + "115": [0, 0.43056, 0.08208, 0, 0.40889], + "116": [0, 0.61508, 0.09486, 0, 0.33222], + "117": [0, 0.43056, 0.07671, 0, 0.53666], + "118": [0, 0.43056, 0.10764, 0, 0.46], + "119": [0, 0.43056, 0.10764, 0, 0.66444], + "120": [0, 0.43056, 0.12042, 0, 0.46389], + "121": [0.19444, 0.43056, 0.08847, 0, 0.48555], + "122": [0, 0.43056, 0.12292, 0, 0.40889], + "126": [0.35, 0.31786, 0.11585, 0, 0.51111], + "160": [0, 0, 0, 0, 0.25], + "168": [0, 0.66786, 0.10474, 0, 0.51111], + "176": [0, 0.69444, 0, 0, 0.83129], + "184": [0.17014, 0, 0, 0, 0.46], + "198": [0, 0.68333, 0.12028, 0, 0.88277], + "216": [0.04861, 0.73194, 0.09403, 0, 0.76666], + "223": [0.19444, 0.69444, 0.10514, 0, 0.53666], + "230": [0, 0.43056, 0.07514, 0, 0.71555], + "248": [0.09722, 0.52778, 0.09194, 0, 0.51111], + "338": [0, 0.68333, 0.12028, 0, 0.98499], + "339": [0, 0.43056, 0.07514, 0, 0.71555], + "710": [0, 0.69444, 0.06646, 0, 0.51111], + "711": [0, 0.62847, 0.08295, 0, 0.51111], + "713": [0, 0.56167, 0.10333, 0, 0.51111], + "714": [0, 0.69444, 0.09694, 0, 0.51111], + "715": [0, 0.69444, 0, 0, 0.51111], + "728": [0, 0.69444, 0.10806, 0, 0.51111], + "729": [0, 0.66786, 0.11752, 0, 0.30667], + "730": [0, 0.69444, 0, 0, 0.83129], + "732": [0, 0.66786, 0.11585, 0, 0.51111], + "733": [0, 0.69444, 0.1225, 0, 0.51111], + "915": [0, 0.68333, 0.13305, 0, 0.62722], + "916": [0, 0.68333, 0, 0, 0.81777], + "920": [0, 0.68333, 0.09403, 0, 0.76666], + "923": [0, 0.68333, 0, 0, 0.69222], + "926": [0, 0.68333, 0.15294, 0, 0.66444], + "928": [0, 0.68333, 0.16389, 0, 0.74333], + "931": [0, 0.68333, 0.12028, 0, 0.71555], + "933": [0, 0.68333, 0.11111, 0, 0.76666], + "934": [0, 0.68333, 0.05986, 0, 0.71555], + "936": [0, 0.68333, 0.11111, 0, 0.76666], + "937": [0, 0.68333, 0.10257, 0, 0.71555], + "8211": [0, 0.43056, 0.09208, 0, 0.51111], + "8212": [0, 0.43056, 0.09208, 0, 1.02222], + "8216": [0, 0.69444, 0.12417, 0, 0.30667], + "8217": [0, 0.69444, 0.12417, 0, 0.30667], + "8220": [0, 0.69444, 0.1685, 0, 0.51444], + "8221": [0, 0.69444, 0.06961, 0, 0.51444], + "8463": [0, 0.68889, 0, 0, 0.54028] + }, + "Main-Regular": { + "32": [0, 0, 0, 0, 0.25], + "33": [0, 0.69444, 0, 0, 0.27778], + "34": [0, 0.69444, 0, 0, 0.5], + "35": [0.19444, 0.69444, 0, 0, 0.83334], + "36": [0.05556, 0.75, 0, 0, 0.5], + "37": [0.05556, 0.75, 0, 0, 0.83334], + "38": [0, 0.69444, 0, 0, 0.77778], + "39": [0, 0.69444, 0, 0, 0.27778], + "40": [0.25, 0.75, 0, 0, 0.38889], + "41": [0.25, 0.75, 0, 0, 0.38889], + "42": [0, 0.75, 0, 0, 0.5], + "43": [0.08333, 0.58333, 0, 0, 0.77778], + "44": [0.19444, 0.10556, 0, 0, 0.27778], + "45": [0, 0.43056, 0, 0, 0.33333], + "46": [0, 0.10556, 0, 0, 0.27778], + "47": [0.25, 0.75, 0, 0, 0.5], + "48": [0, 0.64444, 0, 0, 0.5], + "49": [0, 0.64444, 0, 0, 0.5], + "50": [0, 0.64444, 0, 0, 0.5], + "51": [0, 0.64444, 0, 0, 0.5], + "52": [0, 0.64444, 0, 0, 0.5], + "53": [0, 0.64444, 0, 0, 0.5], + "54": [0, 0.64444, 0, 0, 0.5], + "55": [0, 0.64444, 0, 0, 0.5], + "56": [0, 0.64444, 0, 0, 0.5], + "57": [0, 0.64444, 0, 0, 0.5], + "58": [0, 0.43056, 0, 0, 0.27778], + "59": [0.19444, 0.43056, 0, 0, 0.27778], + "60": [0.0391, 0.5391, 0, 0, 0.77778], + "61": [-0.13313, 0.36687, 0, 0, 0.77778], + "62": [0.0391, 0.5391, 0, 0, 0.77778], + "63": [0, 0.69444, 0, 0, 0.47222], + "64": [0, 0.69444, 0, 0, 0.77778], + "65": [0, 0.68333, 0, 0, 0.75], + "66": [0, 0.68333, 0, 0, 0.70834], + "67": [0, 0.68333, 0, 0, 0.72222], + "68": [0, 0.68333, 0, 0, 0.76389], + "69": [0, 0.68333, 0, 0, 0.68056], + "70": [0, 0.68333, 0, 0, 0.65278], + "71": [0, 0.68333, 0, 0, 0.78472], + "72": [0, 0.68333, 0, 0, 0.75], + "73": [0, 0.68333, 0, 0, 0.36111], + "74": [0, 0.68333, 0, 0, 0.51389], + "75": [0, 0.68333, 0, 0, 0.77778], + "76": [0, 0.68333, 0, 0, 0.625], + "77": [0, 0.68333, 0, 0, 0.91667], + "78": [0, 0.68333, 0, 0, 0.75], + "79": [0, 0.68333, 0, 0, 0.77778], + "80": [0, 0.68333, 0, 0, 0.68056], + "81": [0.19444, 0.68333, 0, 0, 0.77778], + "82": [0, 0.68333, 0, 0, 0.73611], + "83": [0, 0.68333, 0, 0, 0.55556], + "84": [0, 0.68333, 0, 0, 0.72222], + "85": [0, 0.68333, 0, 0, 0.75], + "86": [0, 0.68333, 0.01389, 0, 0.75], + "87": [0, 0.68333, 0.01389, 0, 1.02778], + "88": [0, 0.68333, 0, 0, 0.75], + "89": [0, 0.68333, 0.025, 0, 0.75], + "90": [0, 0.68333, 0, 0, 0.61111], + "91": [0.25, 0.75, 0, 0, 0.27778], + "92": [0.25, 0.75, 0, 0, 0.5], + "93": [0.25, 0.75, 0, 0, 0.27778], + "94": [0, 0.69444, 0, 0, 0.5], + "95": [0.31, 0.12056, 0.02778, 0, 0.5], + "97": [0, 0.43056, 0, 0, 0.5], + "98": [0, 0.69444, 0, 0, 0.55556], + "99": [0, 0.43056, 0, 0, 0.44445], + "100": [0, 0.69444, 0, 0, 0.55556], + "101": [0, 0.43056, 0, 0, 0.44445], + "102": [0, 0.69444, 0.07778, 0, 0.30556], + "103": [0.19444, 0.43056, 0.01389, 0, 0.5], + "104": [0, 0.69444, 0, 0, 0.55556], + "105": [0, 0.66786, 0, 0, 0.27778], + "106": [0.19444, 0.66786, 0, 0, 0.30556], + "107": [0, 0.69444, 0, 0, 0.52778], + "108": [0, 0.69444, 0, 0, 0.27778], + "109": [0, 0.43056, 0, 0, 0.83334], + "110": [0, 0.43056, 0, 0, 0.55556], + "111": [0, 0.43056, 0, 0, 0.5], + "112": [0.19444, 0.43056, 0, 0, 0.55556], + "113": [0.19444, 0.43056, 0, 0, 0.52778], + "114": [0, 0.43056, 0, 0, 0.39167], + "115": [0, 0.43056, 0, 0, 0.39445], + "116": [0, 0.61508, 0, 0, 0.38889], + "117": [0, 0.43056, 0, 0, 0.55556], + "118": [0, 0.43056, 0.01389, 0, 0.52778], + "119": [0, 0.43056, 0.01389, 0, 0.72222], + "120": [0, 0.43056, 0, 0, 0.52778], + "121": [0.19444, 0.43056, 0.01389, 0, 0.52778], + "122": [0, 0.43056, 0, 0, 0.44445], + "123": [0.25, 0.75, 0, 0, 0.5], + "124": [0.25, 0.75, 0, 0, 0.27778], + "125": [0.25, 0.75, 0, 0, 0.5], + "126": [0.35, 0.31786, 0, 0, 0.5], + "160": [0, 0, 0, 0, 0.25], + "163": [0, 0.69444, 0, 0, 0.76909], + "167": [0.19444, 0.69444, 0, 0, 0.44445], + "168": [0, 0.66786, 0, 0, 0.5], + "172": [0, 0.43056, 0, 0, 0.66667], + "176": [0, 0.69444, 0, 0, 0.75], + "177": [0.08333, 0.58333, 0, 0, 0.77778], + "182": [0.19444, 0.69444, 0, 0, 0.61111], + "184": [0.17014, 0, 0, 0, 0.44445], + "198": [0, 0.68333, 0, 0, 0.90278], + "215": [0.08333, 0.58333, 0, 0, 0.77778], + "216": [0.04861, 0.73194, 0, 0, 0.77778], + "223": [0, 0.69444, 0, 0, 0.5], + "230": [0, 0.43056, 0, 0, 0.72222], + "247": [0.08333, 0.58333, 0, 0, 0.77778], + "248": [0.09722, 0.52778, 0, 0, 0.5], + "305": [0, 0.43056, 0, 0, 0.27778], + "338": [0, 0.68333, 0, 0, 1.01389], + "339": [0, 0.43056, 0, 0, 0.77778], + "567": [0.19444, 0.43056, 0, 0, 0.30556], + "710": [0, 0.69444, 0, 0, 0.5], + "711": [0, 0.62847, 0, 0, 0.5], + "713": [0, 0.56778, 0, 0, 0.5], + "714": [0, 0.69444, 0, 0, 0.5], + "715": [0, 0.69444, 0, 0, 0.5], + "728": [0, 0.69444, 0, 0, 0.5], + "729": [0, 0.66786, 0, 0, 0.27778], + "730": [0, 0.69444, 0, 0, 0.75], + "732": [0, 0.66786, 0, 0, 0.5], + "733": [0, 0.69444, 0, 0, 0.5], + "915": [0, 0.68333, 0, 0, 0.625], + "916": [0, 0.68333, 0, 0, 0.83334], + "920": [0, 0.68333, 0, 0, 0.77778], + "923": [0, 0.68333, 0, 0, 0.69445], + "926": [0, 0.68333, 0, 0, 0.66667], + "928": [0, 0.68333, 0, 0, 0.75], + "931": [0, 0.68333, 0, 0, 0.72222], + "933": [0, 0.68333, 0, 0, 0.77778], + "934": [0, 0.68333, 0, 0, 0.72222], + "936": [0, 0.68333, 0, 0, 0.77778], + "937": [0, 0.68333, 0, 0, 0.72222], + "8211": [0, 0.43056, 0.02778, 0, 0.5], + "8212": [0, 0.43056, 0.02778, 0, 1.0], + "8216": [0, 0.69444, 0, 0, 0.27778], + "8217": [0, 0.69444, 0, 0, 0.27778], + "8220": [0, 0.69444, 0, 0, 0.5], + "8221": [0, 0.69444, 0, 0, 0.5], + "8224": [0.19444, 0.69444, 0, 0, 0.44445], + "8225": [0.19444, 0.69444, 0, 0, 0.44445], + "8230": [0, 0.12, 0, 0, 1.172], + "8242": [0, 0.55556, 0, 0, 0.275], + "8407": [0, 0.71444, 0.15382, 0, 0.5], + "8463": [0, 0.68889, 0, 0, 0.54028], + "8465": [0, 0.69444, 0, 0, 0.72222], + "8467": [0, 0.69444, 0, 0.11111, 0.41667], + "8472": [0.19444, 0.43056, 0, 0.11111, 0.63646], + "8476": [0, 0.69444, 0, 0, 0.72222], + "8501": [0, 0.69444, 0, 0, 0.61111], + "8592": [-0.13313, 0.36687, 0, 0, 1.0], + "8593": [0.19444, 0.69444, 0, 0, 0.5], + "8594": [-0.13313, 0.36687, 0, 0, 1.0], + "8595": [0.19444, 0.69444, 0, 0, 0.5], + "8596": [-0.13313, 0.36687, 0, 0, 1.0], + "8597": [0.25, 0.75, 0, 0, 0.5], + "8598": [0.19444, 0.69444, 0, 0, 1.0], + "8599": [0.19444, 0.69444, 0, 0, 1.0], + "8600": [0.19444, 0.69444, 0, 0, 1.0], + "8601": [0.19444, 0.69444, 0, 0, 1.0], + "8614": [0.011, 0.511, 0, 0, 1.0], + "8617": [0.011, 0.511, 0, 0, 1.126], + "8618": [0.011, 0.511, 0, 0, 1.126], + "8636": [-0.13313, 0.36687, 0, 0, 1.0], + "8637": [-0.13313, 0.36687, 0, 0, 1.0], + "8640": [-0.13313, 0.36687, 0, 0, 1.0], + "8641": [-0.13313, 0.36687, 0, 0, 1.0], + "8652": [0.011, 0.671, 0, 0, 1.0], + "8656": [-0.13313, 0.36687, 0, 0, 1.0], + "8657": [0.19444, 0.69444, 0, 0, 0.61111], + "8658": [-0.13313, 0.36687, 0, 0, 1.0], + "8659": [0.19444, 0.69444, 0, 0, 0.61111], + "8660": [-0.13313, 0.36687, 0, 0, 1.0], + "8661": [0.25, 0.75, 0, 0, 0.61111], + "8704": [0, 0.69444, 0, 0, 0.55556], + "8706": [0, 0.69444, 0.05556, 0.08334, 0.5309], + "8707": [0, 0.69444, 0, 0, 0.55556], + "8709": [0.05556, 0.75, 0, 0, 0.5], + "8711": [0, 0.68333, 0, 0, 0.83334], + "8712": [0.0391, 0.5391, 0, 0, 0.66667], + "8715": [0.0391, 0.5391, 0, 0, 0.66667], + "8722": [0.08333, 0.58333, 0, 0, 0.77778], + "8723": [0.08333, 0.58333, 0, 0, 0.77778], + "8725": [0.25, 0.75, 0, 0, 0.5], + "8726": [0.25, 0.75, 0, 0, 0.5], + "8727": [-0.03472, 0.46528, 0, 0, 0.5], + "8728": [-0.05555, 0.44445, 0, 0, 0.5], + "8729": [-0.05555, 0.44445, 0, 0, 0.5], + "8730": [0.2, 0.8, 0, 0, 0.83334], + "8733": [0, 0.43056, 0, 0, 0.77778], + "8734": [0, 0.43056, 0, 0, 1.0], + "8736": [0, 0.69224, 0, 0, 0.72222], + "8739": [0.25, 0.75, 0, 0, 0.27778], + "8741": [0.25, 0.75, 0, 0, 0.5], + "8743": [0, 0.55556, 0, 0, 0.66667], + "8744": [0, 0.55556, 0, 0, 0.66667], + "8745": [0, 0.55556, 0, 0, 0.66667], + "8746": [0, 0.55556, 0, 0, 0.66667], + "8747": [0.19444, 0.69444, 0.11111, 0, 0.41667], + "8764": [-0.13313, 0.36687, 0, 0, 0.77778], + "8768": [0.19444, 0.69444, 0, 0, 0.27778], + "8771": [-0.03625, 0.46375, 0, 0, 0.77778], + "8773": [-0.022, 0.589, 0, 0, 1.0], + "8776": [-0.01688, 0.48312, 0, 0, 0.77778], + "8781": [-0.03625, 0.46375, 0, 0, 0.77778], + "8784": [-0.133, 0.67, 0, 0, 0.778], + "8801": [-0.03625, 0.46375, 0, 0, 0.77778], + "8804": [0.13597, 0.63597, 0, 0, 0.77778], + "8805": [0.13597, 0.63597, 0, 0, 0.77778], + "8810": [0.0391, 0.5391, 0, 0, 1.0], + "8811": [0.0391, 0.5391, 0, 0, 1.0], + "8826": [0.0391, 0.5391, 0, 0, 0.77778], + "8827": [0.0391, 0.5391, 0, 0, 0.77778], + "8834": [0.0391, 0.5391, 0, 0, 0.77778], + "8835": [0.0391, 0.5391, 0, 0, 0.77778], + "8838": [0.13597, 0.63597, 0, 0, 0.77778], + "8839": [0.13597, 0.63597, 0, 0, 0.77778], + "8846": [0, 0.55556, 0, 0, 0.66667], + "8849": [0.13597, 0.63597, 0, 0, 0.77778], + "8850": [0.13597, 0.63597, 0, 0, 0.77778], + "8851": [0, 0.55556, 0, 0, 0.66667], + "8852": [0, 0.55556, 0, 0, 0.66667], + "8853": [0.08333, 0.58333, 0, 0, 0.77778], + "8854": [0.08333, 0.58333, 0, 0, 0.77778], + "8855": [0.08333, 0.58333, 0, 0, 0.77778], + "8856": [0.08333, 0.58333, 0, 0, 0.77778], + "8857": [0.08333, 0.58333, 0, 0, 0.77778], + "8866": [0, 0.69444, 0, 0, 0.61111], + "8867": [0, 0.69444, 0, 0, 0.61111], + "8868": [0, 0.69444, 0, 0, 0.77778], + "8869": [0, 0.69444, 0, 0, 0.77778], + "8872": [0.249, 0.75, 0, 0, 0.867], + "8900": [-0.05555, 0.44445, 0, 0, 0.5], + "8901": [-0.05555, 0.44445, 0, 0, 0.27778], + "8902": [-0.03472, 0.46528, 0, 0, 0.5], + "8904": [0.005, 0.505, 0, 0, 0.9], + "8942": [0.03, 0.9, 0, 0, 0.278], + "8943": [-0.19, 0.31, 0, 0, 1.172], + "8945": [-0.1, 0.82, 0, 0, 1.282], + "8968": [0.25, 0.75, 0, 0, 0.44445], + "8969": [0.25, 0.75, 0, 0, 0.44445], + "8970": [0.25, 0.75, 0, 0, 0.44445], + "8971": [0.25, 0.75, 0, 0, 0.44445], + "8994": [-0.14236, 0.35764, 0, 0, 1.0], + "8995": [-0.14236, 0.35764, 0, 0, 1.0], + "9136": [0.244, 0.744, 0, 0, 0.412], + "9137": [0.244, 0.744, 0, 0, 0.412], + "9651": [0.19444, 0.69444, 0, 0, 0.88889], + "9657": [-0.03472, 0.46528, 0, 0, 0.5], + "9661": [0.19444, 0.69444, 0, 0, 0.88889], + "9667": [-0.03472, 0.46528, 0, 0, 0.5], + "9711": [0.19444, 0.69444, 0, 0, 1.0], + "9824": [0.12963, 0.69444, 0, 0, 0.77778], + "9825": [0.12963, 0.69444, 0, 0, 0.77778], + "9826": [0.12963, 0.69444, 0, 0, 0.77778], + "9827": [0.12963, 0.69444, 0, 0, 0.77778], + "9837": [0, 0.75, 0, 0, 0.38889], + "9838": [0.19444, 0.69444, 0, 0, 0.38889], + "9839": [0.19444, 0.69444, 0, 0, 0.38889], + "10216": [0.25, 0.75, 0, 0, 0.38889], + "10217": [0.25, 0.75, 0, 0, 0.38889], + "10222": [0.244, 0.744, 0, 0, 0.412], + "10223": [0.244, 0.744, 0, 0, 0.412], + "10229": [0.011, 0.511, 0, 0, 1.609], + "10230": [0.011, 0.511, 0, 0, 1.638], + "10231": [0.011, 0.511, 0, 0, 1.859], + "10232": [0.024, 0.525, 0, 0, 1.609], + "10233": [0.024, 0.525, 0, 0, 1.638], + "10234": [0.024, 0.525, 0, 0, 1.858], + "10236": [0.011, 0.511, 0, 0, 1.638], + "10815": [0, 0.68333, 0, 0, 0.75], + "10927": [0.13597, 0.63597, 0, 0, 0.77778], + "10928": [0.13597, 0.63597, 0, 0, 0.77778], + "57376": [0.19444, 0.69444, 0, 0, 0] + }, + "Math-BoldItalic": { + "32": [0, 0, 0, 0, 0.25], + "48": [0, 0.44444, 0, 0, 0.575], + "49": [0, 0.44444, 0, 0, 0.575], + "50": [0, 0.44444, 0, 0, 0.575], + "51": [0.19444, 0.44444, 0, 0, 0.575], + "52": [0.19444, 0.44444, 0, 0, 0.575], + "53": [0.19444, 0.44444, 0, 0, 0.575], + "54": [0, 0.64444, 0, 0, 0.575], + "55": [0.19444, 0.44444, 0, 0, 0.575], + "56": [0, 0.64444, 0, 0, 0.575], + "57": [0.19444, 0.44444, 0, 0, 0.575], + "65": [0, 0.68611, 0, 0, 0.86944], + "66": [0, 0.68611, 0.04835, 0, 0.8664], + "67": [0, 0.68611, 0.06979, 0, 0.81694], + "68": [0, 0.68611, 0.03194, 0, 0.93812], + "69": [0, 0.68611, 0.05451, 0, 0.81007], + "70": [0, 0.68611, 0.15972, 0, 0.68889], + "71": [0, 0.68611, 0, 0, 0.88673], + "72": [0, 0.68611, 0.08229, 0, 0.98229], + "73": [0, 0.68611, 0.07778, 0, 0.51111], + "74": [0, 0.68611, 0.10069, 0, 0.63125], + "75": [0, 0.68611, 0.06979, 0, 0.97118], + "76": [0, 0.68611, 0, 0, 0.75555], + "77": [0, 0.68611, 0.11424, 0, 1.14201], + "78": [0, 0.68611, 0.11424, 0, 0.95034], + "79": [0, 0.68611, 0.03194, 0, 0.83666], + "80": [0, 0.68611, 0.15972, 0, 0.72309], + "81": [0.19444, 0.68611, 0, 0, 0.86861], + "82": [0, 0.68611, 0.00421, 0, 0.87235], + "83": [0, 0.68611, 0.05382, 0, 0.69271], + "84": [0, 0.68611, 0.15972, 0, 0.63663], + "85": [0, 0.68611, 0.11424, 0, 0.80027], + "86": [0, 0.68611, 0.25555, 0, 0.67778], + "87": [0, 0.68611, 0.15972, 0, 1.09305], + "88": [0, 0.68611, 0.07778, 0, 0.94722], + "89": [0, 0.68611, 0.25555, 0, 0.67458], + "90": [0, 0.68611, 0.06979, 0, 0.77257], + "97": [0, 0.44444, 0, 0, 0.63287], + "98": [0, 0.69444, 0, 0, 0.52083], + "99": [0, 0.44444, 0, 0, 0.51342], + "100": [0, 0.69444, 0, 0, 0.60972], + "101": [0, 0.44444, 0, 0, 0.55361], + "102": [0.19444, 0.69444, 0.11042, 0, 0.56806], + "103": [0.19444, 0.44444, 0.03704, 0, 0.5449], + "104": [0, 0.69444, 0, 0, 0.66759], + "105": [0, 0.69326, 0, 0, 0.4048], + "106": [0.19444, 0.69326, 0.0622, 0, 0.47083], + "107": [0, 0.69444, 0.01852, 0, 0.6037], + "108": [0, 0.69444, 0.0088, 0, 0.34815], + "109": [0, 0.44444, 0, 0, 1.0324], + "110": [0, 0.44444, 0, 0, 0.71296], + "111": [0, 0.44444, 0, 0, 0.58472], + "112": [0.19444, 0.44444, 0, 0, 0.60092], + "113": [0.19444, 0.44444, 0.03704, 0, 0.54213], + "114": [0, 0.44444, 0.03194, 0, 0.5287], + "115": [0, 0.44444, 0, 0, 0.53125], + "116": [0, 0.63492, 0, 0, 0.41528], + "117": [0, 0.44444, 0, 0, 0.68102], + "118": [0, 0.44444, 0.03704, 0, 0.56666], + "119": [0, 0.44444, 0.02778, 0, 0.83148], + "120": [0, 0.44444, 0, 0, 0.65903], + "121": [0.19444, 0.44444, 0.03704, 0, 0.59028], + "122": [0, 0.44444, 0.04213, 0, 0.55509], + "160": [0, 0, 0, 0, 0.25], + "915": [0, 0.68611, 0.15972, 0, 0.65694], + "916": [0, 0.68611, 0, 0, 0.95833], + "920": [0, 0.68611, 0.03194, 0, 0.86722], + "923": [0, 0.68611, 0, 0, 0.80555], + "926": [0, 0.68611, 0.07458, 0, 0.84125], + "928": [0, 0.68611, 0.08229, 0, 0.98229], + "931": [0, 0.68611, 0.05451, 0, 0.88507], + "933": [0, 0.68611, 0.15972, 0, 0.67083], + "934": [0, 0.68611, 0, 0, 0.76666], + "936": [0, 0.68611, 0.11653, 0, 0.71402], + "937": [0, 0.68611, 0.04835, 0, 0.8789], + "945": [0, 0.44444, 0, 0, 0.76064], + "946": [0.19444, 0.69444, 0.03403, 0, 0.65972], + "947": [0.19444, 0.44444, 0.06389, 0, 0.59003], + "948": [0, 0.69444, 0.03819, 0, 0.52222], + "949": [0, 0.44444, 0, 0, 0.52882], + "950": [0.19444, 0.69444, 0.06215, 0, 0.50833], + "951": [0.19444, 0.44444, 0.03704, 0, 0.6], + "952": [0, 0.69444, 0.03194, 0, 0.5618], + "953": [0, 0.44444, 0, 0, 0.41204], + "954": [0, 0.44444, 0, 0, 0.66759], + "955": [0, 0.69444, 0, 0, 0.67083], + "956": [0.19444, 0.44444, 0, 0, 0.70787], + "957": [0, 0.44444, 0.06898, 0, 0.57685], + "958": [0.19444, 0.69444, 0.03021, 0, 0.50833], + "959": [0, 0.44444, 0, 0, 0.58472], + "960": [0, 0.44444, 0.03704, 0, 0.68241], + "961": [0.19444, 0.44444, 0, 0, 0.6118], + "962": [0.09722, 0.44444, 0.07917, 0, 0.42361], + "963": [0, 0.44444, 0.03704, 0, 0.68588], + "964": [0, 0.44444, 0.13472, 0, 0.52083], + "965": [0, 0.44444, 0.03704, 0, 0.63055], + "966": [0.19444, 0.44444, 0, 0, 0.74722], + "967": [0.19444, 0.44444, 0, 0, 0.71805], + "968": [0.19444, 0.69444, 0.03704, 0, 0.75833], + "969": [0, 0.44444, 0.03704, 0, 0.71782], + "977": [0, 0.69444, 0, 0, 0.69155], + "981": [0.19444, 0.69444, 0, 0, 0.7125], + "982": [0, 0.44444, 0.03194, 0, 0.975], + "1009": [0.19444, 0.44444, 0, 0, 0.6118], + "1013": [0, 0.44444, 0, 0, 0.48333], + "57649": [0, 0.44444, 0, 0, 0.39352], + "57911": [0.19444, 0.44444, 0, 0, 0.43889] + }, + "Math-Italic": { + "32": [0, 0, 0, 0, 0.25], + "48": [0, 0.43056, 0, 0, 0.5], + "49": [0, 0.43056, 0, 0, 0.5], + "50": [0, 0.43056, 0, 0, 0.5], + "51": [0.19444, 0.43056, 0, 0, 0.5], + "52": [0.19444, 0.43056, 0, 0, 0.5], + "53": [0.19444, 0.43056, 0, 0, 0.5], + "54": [0, 0.64444, 0, 0, 0.5], + "55": [0.19444, 0.43056, 0, 0, 0.5], + "56": [0, 0.64444, 0, 0, 0.5], + "57": [0.19444, 0.43056, 0, 0, 0.5], + "65": [0, 0.68333, 0, 0.13889, 0.75], + "66": [0, 0.68333, 0.05017, 0.08334, 0.75851], + "67": [0, 0.68333, 0.07153, 0.08334, 0.71472], + "68": [0, 0.68333, 0.02778, 0.05556, 0.82792], + "69": [0, 0.68333, 0.05764, 0.08334, 0.7382], + "70": [0, 0.68333, 0.13889, 0.08334, 0.64306], + "71": [0, 0.68333, 0, 0.08334, 0.78625], + "72": [0, 0.68333, 0.08125, 0.05556, 0.83125], + "73": [0, 0.68333, 0.07847, 0.11111, 0.43958], + "74": [0, 0.68333, 0.09618, 0.16667, 0.55451], + "75": [0, 0.68333, 0.07153, 0.05556, 0.84931], + "76": [0, 0.68333, 0, 0.02778, 0.68056], + "77": [0, 0.68333, 0.10903, 0.08334, 0.97014], + "78": [0, 0.68333, 0.10903, 0.08334, 0.80347], + "79": [0, 0.68333, 0.02778, 0.08334, 0.76278], + "80": [0, 0.68333, 0.13889, 0.08334, 0.64201], + "81": [0.19444, 0.68333, 0, 0.08334, 0.79056], + "82": [0, 0.68333, 0.00773, 0.08334, 0.75929], + "83": [0, 0.68333, 0.05764, 0.08334, 0.6132], + "84": [0, 0.68333, 0.13889, 0.08334, 0.58438], + "85": [0, 0.68333, 0.10903, 0.02778, 0.68278], + "86": [0, 0.68333, 0.22222, 0, 0.58333], + "87": [0, 0.68333, 0.13889, 0, 0.94445], + "88": [0, 0.68333, 0.07847, 0.08334, 0.82847], + "89": [0, 0.68333, 0.22222, 0, 0.58056], + "90": [0, 0.68333, 0.07153, 0.08334, 0.68264], + "97": [0, 0.43056, 0, 0, 0.52859], + "98": [0, 0.69444, 0, 0, 0.42917], + "99": [0, 0.43056, 0, 0.05556, 0.43276], + "100": [0, 0.69444, 0, 0.16667, 0.52049], + "101": [0, 0.43056, 0, 0.05556, 0.46563], + "102": [0.19444, 0.69444, 0.10764, 0.16667, 0.48959], + "103": [0.19444, 0.43056, 0.03588, 0.02778, 0.47697], + "104": [0, 0.69444, 0, 0, 0.57616], + "105": [0, 0.65952, 0, 0, 0.34451], + "106": [0.19444, 0.65952, 0.05724, 0, 0.41181], + "107": [0, 0.69444, 0.03148, 0, 0.5206], + "108": [0, 0.69444, 0.01968, 0.08334, 0.29838], + "109": [0, 0.43056, 0, 0, 0.87801], + "110": [0, 0.43056, 0, 0, 0.60023], + "111": [0, 0.43056, 0, 0.05556, 0.48472], + "112": [0.19444, 0.43056, 0, 0.08334, 0.50313], + "113": [0.19444, 0.43056, 0.03588, 0.08334, 0.44641], + "114": [0, 0.43056, 0.02778, 0.05556, 0.45116], + "115": [0, 0.43056, 0, 0.05556, 0.46875], + "116": [0, 0.61508, 0, 0.08334, 0.36111], + "117": [0, 0.43056, 0, 0.02778, 0.57246], + "118": [0, 0.43056, 0.03588, 0.02778, 0.48472], + "119": [0, 0.43056, 0.02691, 0.08334, 0.71592], + "120": [0, 0.43056, 0, 0.02778, 0.57153], + "121": [0.19444, 0.43056, 0.03588, 0.05556, 0.49028], + "122": [0, 0.43056, 0.04398, 0.05556, 0.46505], + "160": [0, 0, 0, 0, 0.25], + "915": [0, 0.68333, 0.13889, 0.08334, 0.61528], + "916": [0, 0.68333, 0, 0.16667, 0.83334], + "920": [0, 0.68333, 0.02778, 0.08334, 0.76278], + "923": [0, 0.68333, 0, 0.16667, 0.69445], + "926": [0, 0.68333, 0.07569, 0.08334, 0.74236], + "928": [0, 0.68333, 0.08125, 0.05556, 0.83125], + "931": [0, 0.68333, 0.05764, 0.08334, 0.77986], + "933": [0, 0.68333, 0.13889, 0.05556, 0.58333], + "934": [0, 0.68333, 0, 0.08334, 0.66667], + "936": [0, 0.68333, 0.11, 0.05556, 0.61222], + "937": [0, 0.68333, 0.05017, 0.08334, 0.7724], + "945": [0, 0.43056, 0.0037, 0.02778, 0.6397], + "946": [0.19444, 0.69444, 0.05278, 0.08334, 0.56563], + "947": [0.19444, 0.43056, 0.05556, 0, 0.51773], + "948": [0, 0.69444, 0.03785, 0.05556, 0.44444], + "949": [0, 0.43056, 0, 0.08334, 0.46632], + "950": [0.19444, 0.69444, 0.07378, 0.08334, 0.4375], + "951": [0.19444, 0.43056, 0.03588, 0.05556, 0.49653], + "952": [0, 0.69444, 0.02778, 0.08334, 0.46944], + "953": [0, 0.43056, 0, 0.05556, 0.35394], + "954": [0, 0.43056, 0, 0, 0.57616], + "955": [0, 0.69444, 0, 0, 0.58334], + "956": [0.19444, 0.43056, 0, 0.02778, 0.60255], + "957": [0, 0.43056, 0.06366, 0.02778, 0.49398], + "958": [0.19444, 0.69444, 0.04601, 0.11111, 0.4375], + "959": [0, 0.43056, 0, 0.05556, 0.48472], + "960": [0, 0.43056, 0.03588, 0, 0.57003], + "961": [0.19444, 0.43056, 0, 0.08334, 0.51702], + "962": [0.09722, 0.43056, 0.07986, 0.08334, 0.36285], + "963": [0, 0.43056, 0.03588, 0, 0.57141], + "964": [0, 0.43056, 0.1132, 0.02778, 0.43715], + "965": [0, 0.43056, 0.03588, 0.02778, 0.54028], + "966": [0.19444, 0.43056, 0, 0.08334, 0.65417], + "967": [0.19444, 0.43056, 0, 0.05556, 0.62569], + "968": [0.19444, 0.69444, 0.03588, 0.11111, 0.65139], + "969": [0, 0.43056, 0.03588, 0, 0.62245], + "977": [0, 0.69444, 0, 0.08334, 0.59144], + "981": [0.19444, 0.69444, 0, 0.08334, 0.59583], + "982": [0, 0.43056, 0.02778, 0, 0.82813], + "1009": [0.19444, 0.43056, 0, 0.08334, 0.51702], + "1013": [0, 0.43056, 0, 0.05556, 0.4059], + "57649": [0, 0.43056, 0, 0.02778, 0.32246], + "57911": [0.19444, 0.43056, 0, 0.08334, 0.38403] + }, + "SansSerif-Bold": { + "32": [0, 0, 0, 0, 0.25], + "33": [0, 0.69444, 0, 0, 0.36667], + "34": [0, 0.69444, 0, 0, 0.55834], + "35": [0.19444, 0.69444, 0, 0, 0.91667], + "36": [0.05556, 0.75, 0, 0, 0.55], + "37": [0.05556, 0.75, 0, 0, 1.02912], + "38": [0, 0.69444, 0, 0, 0.83056], + "39": [0, 0.69444, 0, 0, 0.30556], + "40": [0.25, 0.75, 0, 0, 0.42778], + "41": [0.25, 0.75, 0, 0, 0.42778], + "42": [0, 0.75, 0, 0, 0.55], + "43": [0.11667, 0.61667, 0, 0, 0.85556], + "44": [0.10556, 0.13056, 0, 0, 0.30556], + "45": [0, 0.45833, 0, 0, 0.36667], + "46": [0, 0.13056, 0, 0, 0.30556], + "47": [0.25, 0.75, 0, 0, 0.55], + "48": [0, 0.69444, 0, 0, 0.55], + "49": [0, 0.69444, 0, 0, 0.55], + "50": [0, 0.69444, 0, 0, 0.55], + "51": [0, 0.69444, 0, 0, 0.55], + "52": [0, 0.69444, 0, 0, 0.55], + "53": [0, 0.69444, 0, 0, 0.55], + "54": [0, 0.69444, 0, 0, 0.55], + "55": [0, 0.69444, 0, 0, 0.55], + "56": [0, 0.69444, 0, 0, 0.55], + "57": [0, 0.69444, 0, 0, 0.55], + "58": [0, 0.45833, 0, 0, 0.30556], + "59": [0.10556, 0.45833, 0, 0, 0.30556], + "61": [-0.09375, 0.40625, 0, 0, 0.85556], + "63": [0, 0.69444, 0, 0, 0.51945], + "64": [0, 0.69444, 0, 0, 0.73334], + "65": [0, 0.69444, 0, 0, 0.73334], + "66": [0, 0.69444, 0, 0, 0.73334], + "67": [0, 0.69444, 0, 0, 0.70278], + "68": [0, 0.69444, 0, 0, 0.79445], + "69": [0, 0.69444, 0, 0, 0.64167], + "70": [0, 0.69444, 0, 0, 0.61111], + "71": [0, 0.69444, 0, 0, 0.73334], + "72": [0, 0.69444, 0, 0, 0.79445], + "73": [0, 0.69444, 0, 0, 0.33056], + "74": [0, 0.69444, 0, 0, 0.51945], + "75": [0, 0.69444, 0, 0, 0.76389], + "76": [0, 0.69444, 0, 0, 0.58056], + "77": [0, 0.69444, 0, 0, 0.97778], + "78": [0, 0.69444, 0, 0, 0.79445], + "79": [0, 0.69444, 0, 0, 0.79445], + "80": [0, 0.69444, 0, 0, 0.70278], + "81": [0.10556, 0.69444, 0, 0, 0.79445], + "82": [0, 0.69444, 0, 0, 0.70278], + "83": [0, 0.69444, 0, 0, 0.61111], + "84": [0, 0.69444, 0, 0, 0.73334], + "85": [0, 0.69444, 0, 0, 0.76389], + "86": [0, 0.69444, 0.01528, 0, 0.73334], + "87": [0, 0.69444, 0.01528, 0, 1.03889], + "88": [0, 0.69444, 0, 0, 0.73334], + "89": [0, 0.69444, 0.0275, 0, 0.73334], + "90": [0, 0.69444, 0, 0, 0.67223], + "91": [0.25, 0.75, 0, 0, 0.34306], + "93": [0.25, 0.75, 0, 0, 0.34306], + "94": [0, 0.69444, 0, 0, 0.55], + "95": [0.35, 0.10833, 0.03056, 0, 0.55], + "97": [0, 0.45833, 0, 0, 0.525], + "98": [0, 0.69444, 0, 0, 0.56111], + "99": [0, 0.45833, 0, 0, 0.48889], + "100": [0, 0.69444, 0, 0, 0.56111], + "101": [0, 0.45833, 0, 0, 0.51111], + "102": [0, 0.69444, 0.07639, 0, 0.33611], + "103": [0.19444, 0.45833, 0.01528, 0, 0.55], + "104": [0, 0.69444, 0, 0, 0.56111], + "105": [0, 0.69444, 0, 0, 0.25556], + "106": [0.19444, 0.69444, 0, 0, 0.28611], + "107": [0, 0.69444, 0, 0, 0.53056], + "108": [0, 0.69444, 0, 0, 0.25556], + "109": [0, 0.45833, 0, 0, 0.86667], + "110": [0, 0.45833, 0, 0, 0.56111], + "111": [0, 0.45833, 0, 0, 0.55], + "112": [0.19444, 0.45833, 0, 0, 0.56111], + "113": [0.19444, 0.45833, 0, 0, 0.56111], + "114": [0, 0.45833, 0.01528, 0, 0.37222], + "115": [0, 0.45833, 0, 0, 0.42167], + "116": [0, 0.58929, 0, 0, 0.40417], + "117": [0, 0.45833, 0, 0, 0.56111], + "118": [0, 0.45833, 0.01528, 0, 0.5], + "119": [0, 0.45833, 0.01528, 0, 0.74445], + "120": [0, 0.45833, 0, 0, 0.5], + "121": [0.19444, 0.45833, 0.01528, 0, 0.5], + "122": [0, 0.45833, 0, 0, 0.47639], + "126": [0.35, 0.34444, 0, 0, 0.55], + "160": [0, 0, 0, 0, 0.25], + "168": [0, 0.69444, 0, 0, 0.55], + "176": [0, 0.69444, 0, 0, 0.73334], + "180": [0, 0.69444, 0, 0, 0.55], + "184": [0.17014, 0, 0, 0, 0.48889], + "305": [0, 0.45833, 0, 0, 0.25556], + "567": [0.19444, 0.45833, 0, 0, 0.28611], + "710": [0, 0.69444, 0, 0, 0.55], + "711": [0, 0.63542, 0, 0, 0.55], + "713": [0, 0.63778, 0, 0, 0.55], + "728": [0, 0.69444, 0, 0, 0.55], + "729": [0, 0.69444, 0, 0, 0.30556], + "730": [0, 0.69444, 0, 0, 0.73334], + "732": [0, 0.69444, 0, 0, 0.55], + "733": [0, 0.69444, 0, 0, 0.55], + "915": [0, 0.69444, 0, 0, 0.58056], + "916": [0, 0.69444, 0, 0, 0.91667], + "920": [0, 0.69444, 0, 0, 0.85556], + "923": [0, 0.69444, 0, 0, 0.67223], + "926": [0, 0.69444, 0, 0, 0.73334], + "928": [0, 0.69444, 0, 0, 0.79445], + "931": [0, 0.69444, 0, 0, 0.79445], + "933": [0, 0.69444, 0, 0, 0.85556], + "934": [0, 0.69444, 0, 0, 0.79445], + "936": [0, 0.69444, 0, 0, 0.85556], + "937": [0, 0.69444, 0, 0, 0.79445], + "8211": [0, 0.45833, 0.03056, 0, 0.55], + "8212": [0, 0.45833, 0.03056, 0, 1.10001], + "8216": [0, 0.69444, 0, 0, 0.30556], + "8217": [0, 0.69444, 0, 0, 0.30556], + "8220": [0, 0.69444, 0, 0, 0.55834], + "8221": [0, 0.69444, 0, 0, 0.55834] + }, + "SansSerif-Italic": { + "32": [0, 0, 0, 0, 0.25], + "33": [0, 0.69444, 0.05733, 0, 0.31945], + "34": [0, 0.69444, 0.00316, 0, 0.5], + "35": [0.19444, 0.69444, 0.05087, 0, 0.83334], + "36": [0.05556, 0.75, 0.11156, 0, 0.5], + "37": [0.05556, 0.75, 0.03126, 0, 0.83334], + "38": [0, 0.69444, 0.03058, 0, 0.75834], + "39": [0, 0.69444, 0.07816, 0, 0.27778], + "40": [0.25, 0.75, 0.13164, 0, 0.38889], + "41": [0.25, 0.75, 0.02536, 0, 0.38889], + "42": [0, 0.75, 0.11775, 0, 0.5], + "43": [0.08333, 0.58333, 0.02536, 0, 0.77778], + "44": [0.125, 0.08333, 0, 0, 0.27778], + "45": [0, 0.44444, 0.01946, 0, 0.33333], + "46": [0, 0.08333, 0, 0, 0.27778], + "47": [0.25, 0.75, 0.13164, 0, 0.5], + "48": [0, 0.65556, 0.11156, 0, 0.5], + "49": [0, 0.65556, 0.11156, 0, 0.5], + "50": [0, 0.65556, 0.11156, 0, 0.5], + "51": [0, 0.65556, 0.11156, 0, 0.5], + "52": [0, 0.65556, 0.11156, 0, 0.5], + "53": [0, 0.65556, 0.11156, 0, 0.5], + "54": [0, 0.65556, 0.11156, 0, 0.5], + "55": [0, 0.65556, 0.11156, 0, 0.5], + "56": [0, 0.65556, 0.11156, 0, 0.5], + "57": [0, 0.65556, 0.11156, 0, 0.5], + "58": [0, 0.44444, 0.02502, 0, 0.27778], + "59": [0.125, 0.44444, 0.02502, 0, 0.27778], + "61": [-0.13, 0.37, 0.05087, 0, 0.77778], + "63": [0, 0.69444, 0.11809, 0, 0.47222], + "64": [0, 0.69444, 0.07555, 0, 0.66667], + "65": [0, 0.69444, 0, 0, 0.66667], + "66": [0, 0.69444, 0.08293, 0, 0.66667], + "67": [0, 0.69444, 0.11983, 0, 0.63889], + "68": [0, 0.69444, 0.07555, 0, 0.72223], + "69": [0, 0.69444, 0.11983, 0, 0.59722], + "70": [0, 0.69444, 0.13372, 0, 0.56945], + "71": [0, 0.69444, 0.11983, 0, 0.66667], + "72": [0, 0.69444, 0.08094, 0, 0.70834], + "73": [0, 0.69444, 0.13372, 0, 0.27778], + "74": [0, 0.69444, 0.08094, 0, 0.47222], + "75": [0, 0.69444, 0.11983, 0, 0.69445], + "76": [0, 0.69444, 0, 0, 0.54167], + "77": [0, 0.69444, 0.08094, 0, 0.875], + "78": [0, 0.69444, 0.08094, 0, 0.70834], + "79": [0, 0.69444, 0.07555, 0, 0.73611], + "80": [0, 0.69444, 0.08293, 0, 0.63889], + "81": [0.125, 0.69444, 0.07555, 0, 0.73611], + "82": [0, 0.69444, 0.08293, 0, 0.64584], + "83": [0, 0.69444, 0.09205, 0, 0.55556], + "84": [0, 0.69444, 0.13372, 0, 0.68056], + "85": [0, 0.69444, 0.08094, 0, 0.6875], + "86": [0, 0.69444, 0.1615, 0, 0.66667], + "87": [0, 0.69444, 0.1615, 0, 0.94445], + "88": [0, 0.69444, 0.13372, 0, 0.66667], + "89": [0, 0.69444, 0.17261, 0, 0.66667], + "90": [0, 0.69444, 0.11983, 0, 0.61111], + "91": [0.25, 0.75, 0.15942, 0, 0.28889], + "93": [0.25, 0.75, 0.08719, 0, 0.28889], + "94": [0, 0.69444, 0.0799, 0, 0.5], + "95": [0.35, 0.09444, 0.08616, 0, 0.5], + "97": [0, 0.44444, 0.00981, 0, 0.48056], + "98": [0, 0.69444, 0.03057, 0, 0.51667], + "99": [0, 0.44444, 0.08336, 0, 0.44445], + "100": [0, 0.69444, 0.09483, 0, 0.51667], + "101": [0, 0.44444, 0.06778, 0, 0.44445], + "102": [0, 0.69444, 0.21705, 0, 0.30556], + "103": [0.19444, 0.44444, 0.10836, 0, 0.5], + "104": [0, 0.69444, 0.01778, 0, 0.51667], + "105": [0, 0.67937, 0.09718, 0, 0.23889], + "106": [0.19444, 0.67937, 0.09162, 0, 0.26667], + "107": [0, 0.69444, 0.08336, 0, 0.48889], + "108": [0, 0.69444, 0.09483, 0, 0.23889], + "109": [0, 0.44444, 0.01778, 0, 0.79445], + "110": [0, 0.44444, 0.01778, 0, 0.51667], + "111": [0, 0.44444, 0.06613, 0, 0.5], + "112": [0.19444, 0.44444, 0.0389, 0, 0.51667], + "113": [0.19444, 0.44444, 0.04169, 0, 0.51667], + "114": [0, 0.44444, 0.10836, 0, 0.34167], + "115": [0, 0.44444, 0.0778, 0, 0.38333], + "116": [0, 0.57143, 0.07225, 0, 0.36111], + "117": [0, 0.44444, 0.04169, 0, 0.51667], + "118": [0, 0.44444, 0.10836, 0, 0.46111], + "119": [0, 0.44444, 0.10836, 0, 0.68334], + "120": [0, 0.44444, 0.09169, 0, 0.46111], + "121": [0.19444, 0.44444, 0.10836, 0, 0.46111], + "122": [0, 0.44444, 0.08752, 0, 0.43472], + "126": [0.35, 0.32659, 0.08826, 0, 0.5], + "160": [0, 0, 0, 0, 0.25], + "168": [0, 0.67937, 0.06385, 0, 0.5], + "176": [0, 0.69444, 0, 0, 0.73752], + "184": [0.17014, 0, 0, 0, 0.44445], + "305": [0, 0.44444, 0.04169, 0, 0.23889], + "567": [0.19444, 0.44444, 0.04169, 0, 0.26667], + "710": [0, 0.69444, 0.0799, 0, 0.5], + "711": [0, 0.63194, 0.08432, 0, 0.5], + "713": [0, 0.60889, 0.08776, 0, 0.5], + "714": [0, 0.69444, 0.09205, 0, 0.5], + "715": [0, 0.69444, 0, 0, 0.5], + "728": [0, 0.69444, 0.09483, 0, 0.5], + "729": [0, 0.67937, 0.07774, 0, 0.27778], + "730": [0, 0.69444, 0, 0, 0.73752], + "732": [0, 0.67659, 0.08826, 0, 0.5], + "733": [0, 0.69444, 0.09205, 0, 0.5], + "915": [0, 0.69444, 0.13372, 0, 0.54167], + "916": [0, 0.69444, 0, 0, 0.83334], + "920": [0, 0.69444, 0.07555, 0, 0.77778], + "923": [0, 0.69444, 0, 0, 0.61111], + "926": [0, 0.69444, 0.12816, 0, 0.66667], + "928": [0, 0.69444, 0.08094, 0, 0.70834], + "931": [0, 0.69444, 0.11983, 0, 0.72222], + "933": [0, 0.69444, 0.09031, 0, 0.77778], + "934": [0, 0.69444, 0.04603, 0, 0.72222], + "936": [0, 0.69444, 0.09031, 0, 0.77778], + "937": [0, 0.69444, 0.08293, 0, 0.72222], + "8211": [0, 0.44444, 0.08616, 0, 0.5], + "8212": [0, 0.44444, 0.08616, 0, 1.0], + "8216": [0, 0.69444, 0.07816, 0, 0.27778], + "8217": [0, 0.69444, 0.07816, 0, 0.27778], + "8220": [0, 0.69444, 0.14205, 0, 0.5], + "8221": [0, 0.69444, 0.00316, 0, 0.5] + }, + "SansSerif-Regular": { + "32": [0, 0, 0, 0, 0.25], + "33": [0, 0.69444, 0, 0, 0.31945], + "34": [0, 0.69444, 0, 0, 0.5], + "35": [0.19444, 0.69444, 0, 0, 0.83334], + "36": [0.05556, 0.75, 0, 0, 0.5], + "37": [0.05556, 0.75, 0, 0, 0.83334], + "38": [0, 0.69444, 0, 0, 0.75834], + "39": [0, 0.69444, 0, 0, 0.27778], + "40": [0.25, 0.75, 0, 0, 0.38889], + "41": [0.25, 0.75, 0, 0, 0.38889], + "42": [0, 0.75, 0, 0, 0.5], + "43": [0.08333, 0.58333, 0, 0, 0.77778], + "44": [0.125, 0.08333, 0, 0, 0.27778], + "45": [0, 0.44444, 0, 0, 0.33333], + "46": [0, 0.08333, 0, 0, 0.27778], + "47": [0.25, 0.75, 0, 0, 0.5], + "48": [0, 0.65556, 0, 0, 0.5], + "49": [0, 0.65556, 0, 0, 0.5], + "50": [0, 0.65556, 0, 0, 0.5], + "51": [0, 0.65556, 0, 0, 0.5], + "52": [0, 0.65556, 0, 0, 0.5], + "53": [0, 0.65556, 0, 0, 0.5], + "54": [0, 0.65556, 0, 0, 0.5], + "55": [0, 0.65556, 0, 0, 0.5], + "56": [0, 0.65556, 0, 0, 0.5], + "57": [0, 0.65556, 0, 0, 0.5], + "58": [0, 0.44444, 0, 0, 0.27778], + "59": [0.125, 0.44444, 0, 0, 0.27778], + "61": [-0.13, 0.37, 0, 0, 0.77778], + "63": [0, 0.69444, 0, 0, 0.47222], + "64": [0, 0.69444, 0, 0, 0.66667], + "65": [0, 0.69444, 0, 0, 0.66667], + "66": [0, 0.69444, 0, 0, 0.66667], + "67": [0, 0.69444, 0, 0, 0.63889], + "68": [0, 0.69444, 0, 0, 0.72223], + "69": [0, 0.69444, 0, 0, 0.59722], + "70": [0, 0.69444, 0, 0, 0.56945], + "71": [0, 0.69444, 0, 0, 0.66667], + "72": [0, 0.69444, 0, 0, 0.70834], + "73": [0, 0.69444, 0, 0, 0.27778], + "74": [0, 0.69444, 0, 0, 0.47222], + "75": [0, 0.69444, 0, 0, 0.69445], + "76": [0, 0.69444, 0, 0, 0.54167], + "77": [0, 0.69444, 0, 0, 0.875], + "78": [0, 0.69444, 0, 0, 0.70834], + "79": [0, 0.69444, 0, 0, 0.73611], + "80": [0, 0.69444, 0, 0, 0.63889], + "81": [0.125, 0.69444, 0, 0, 0.73611], + "82": [0, 0.69444, 0, 0, 0.64584], + "83": [0, 0.69444, 0, 0, 0.55556], + "84": [0, 0.69444, 0, 0, 0.68056], + "85": [0, 0.69444, 0, 0, 0.6875], + "86": [0, 0.69444, 0.01389, 0, 0.66667], + "87": [0, 0.69444, 0.01389, 0, 0.94445], + "88": [0, 0.69444, 0, 0, 0.66667], + "89": [0, 0.69444, 0.025, 0, 0.66667], + "90": [0, 0.69444, 0, 0, 0.61111], + "91": [0.25, 0.75, 0, 0, 0.28889], + "93": [0.25, 0.75, 0, 0, 0.28889], + "94": [0, 0.69444, 0, 0, 0.5], + "95": [0.35, 0.09444, 0.02778, 0, 0.5], + "97": [0, 0.44444, 0, 0, 0.48056], + "98": [0, 0.69444, 0, 0, 0.51667], + "99": [0, 0.44444, 0, 0, 0.44445], + "100": [0, 0.69444, 0, 0, 0.51667], + "101": [0, 0.44444, 0, 0, 0.44445], + "102": [0, 0.69444, 0.06944, 0, 0.30556], + "103": [0.19444, 0.44444, 0.01389, 0, 0.5], + "104": [0, 0.69444, 0, 0, 0.51667], + "105": [0, 0.67937, 0, 0, 0.23889], + "106": [0.19444, 0.67937, 0, 0, 0.26667], + "107": [0, 0.69444, 0, 0, 0.48889], + "108": [0, 0.69444, 0, 0, 0.23889], + "109": [0, 0.44444, 0, 0, 0.79445], + "110": [0, 0.44444, 0, 0, 0.51667], + "111": [0, 0.44444, 0, 0, 0.5], + "112": [0.19444, 0.44444, 0, 0, 0.51667], + "113": [0.19444, 0.44444, 0, 0, 0.51667], + "114": [0, 0.44444, 0.01389, 0, 0.34167], + "115": [0, 0.44444, 0, 0, 0.38333], + "116": [0, 0.57143, 0, 0, 0.36111], + "117": [0, 0.44444, 0, 0, 0.51667], + "118": [0, 0.44444, 0.01389, 0, 0.46111], + "119": [0, 0.44444, 0.01389, 0, 0.68334], + "120": [0, 0.44444, 0, 0, 0.46111], + "121": [0.19444, 0.44444, 0.01389, 0, 0.46111], + "122": [0, 0.44444, 0, 0, 0.43472], + "126": [0.35, 0.32659, 0, 0, 0.5], + "160": [0, 0, 0, 0, 0.25], + "168": [0, 0.67937, 0, 0, 0.5], + "176": [0, 0.69444, 0, 0, 0.66667], + "184": [0.17014, 0, 0, 0, 0.44445], + "305": [0, 0.44444, 0, 0, 0.23889], + "567": [0.19444, 0.44444, 0, 0, 0.26667], + "710": [0, 0.69444, 0, 0, 0.5], + "711": [0, 0.63194, 0, 0, 0.5], + "713": [0, 0.60889, 0, 0, 0.5], + "714": [0, 0.69444, 0, 0, 0.5], + "715": [0, 0.69444, 0, 0, 0.5], + "728": [0, 0.69444, 0, 0, 0.5], + "729": [0, 0.67937, 0, 0, 0.27778], + "730": [0, 0.69444, 0, 0, 0.66667], + "732": [0, 0.67659, 0, 0, 0.5], + "733": [0, 0.69444, 0, 0, 0.5], + "915": [0, 0.69444, 0, 0, 0.54167], + "916": [0, 0.69444, 0, 0, 0.83334], + "920": [0, 0.69444, 0, 0, 0.77778], + "923": [0, 0.69444, 0, 0, 0.61111], + "926": [0, 0.69444, 0, 0, 0.66667], + "928": [0, 0.69444, 0, 0, 0.70834], + "931": [0, 0.69444, 0, 0, 0.72222], + "933": [0, 0.69444, 0, 0, 0.77778], + "934": [0, 0.69444, 0, 0, 0.72222], + "936": [0, 0.69444, 0, 0, 0.77778], + "937": [0, 0.69444, 0, 0, 0.72222], + "8211": [0, 0.44444, 0.02778, 0, 0.5], + "8212": [0, 0.44444, 0.02778, 0, 1.0], + "8216": [0, 0.69444, 0, 0, 0.27778], + "8217": [0, 0.69444, 0, 0, 0.27778], + "8220": [0, 0.69444, 0, 0, 0.5], + "8221": [0, 0.69444, 0, 0, 0.5] + }, + "Script-Regular": { + "32": [0, 0, 0, 0, 0.25], + "65": [0, 0.7, 0.22925, 0, 0.80253], + "66": [0, 0.7, 0.04087, 0, 0.90757], + "67": [0, 0.7, 0.1689, 0, 0.66619], + "68": [0, 0.7, 0.09371, 0, 0.77443], + "69": [0, 0.7, 0.18583, 0, 0.56162], + "70": [0, 0.7, 0.13634, 0, 0.89544], + "71": [0, 0.7, 0.17322, 0, 0.60961], + "72": [0, 0.7, 0.29694, 0, 0.96919], + "73": [0, 0.7, 0.19189, 0, 0.80907], + "74": [0.27778, 0.7, 0.19189, 0, 1.05159], + "75": [0, 0.7, 0.31259, 0, 0.91364], + "76": [0, 0.7, 0.19189, 0, 0.87373], + "77": [0, 0.7, 0.15981, 0, 1.08031], + "78": [0, 0.7, 0.3525, 0, 0.9015], + "79": [0, 0.7, 0.08078, 0, 0.73787], + "80": [0, 0.7, 0.08078, 0, 1.01262], + "81": [0, 0.7, 0.03305, 0, 0.88282], + "82": [0, 0.7, 0.06259, 0, 0.85], + "83": [0, 0.7, 0.19189, 0, 0.86767], + "84": [0, 0.7, 0.29087, 0, 0.74697], + "85": [0, 0.7, 0.25815, 0, 0.79996], + "86": [0, 0.7, 0.27523, 0, 0.62204], + "87": [0, 0.7, 0.27523, 0, 0.80532], + "88": [0, 0.7, 0.26006, 0, 0.94445], + "89": [0, 0.7, 0.2939, 0, 0.70961], + "90": [0, 0.7, 0.24037, 0, 0.8212], + "160": [0, 0, 0, 0, 0.25] + }, + "Size1-Regular": { + "32": [0, 0, 0, 0, 0.25], + "40": [0.35001, 0.85, 0, 0, 0.45834], + "41": [0.35001, 0.85, 0, 0, 0.45834], + "47": [0.35001, 0.85, 0, 0, 0.57778], + "91": [0.35001, 0.85, 0, 0, 0.41667], + "92": [0.35001, 0.85, 0, 0, 0.57778], + "93": [0.35001, 0.85, 0, 0, 0.41667], + "123": [0.35001, 0.85, 0, 0, 0.58334], + "125": [0.35001, 0.85, 0, 0, 0.58334], + "160": [0, 0, 0, 0, 0.25], + "710": [0, 0.72222, 0, 0, 0.55556], + "732": [0, 0.72222, 0, 0, 0.55556], + "770": [0, 0.72222, 0, 0, 0.55556], + "771": [0, 0.72222, 0, 0, 0.55556], + "8214": [-0.00099, 0.601, 0, 0, 0.77778], + "8593": [1e-05, 0.6, 0, 0, 0.66667], + "8595": [1e-05, 0.6, 0, 0, 0.66667], + "8657": [1e-05, 0.6, 0, 0, 0.77778], + "8659": [1e-05, 0.6, 0, 0, 0.77778], + "8719": [0.25001, 0.75, 0, 0, 0.94445], + "8720": [0.25001, 0.75, 0, 0, 0.94445], + "8721": [0.25001, 0.75, 0, 0, 1.05556], + "8730": [0.35001, 0.85, 0, 0, 1.0], + "8739": [-0.00599, 0.606, 0, 0, 0.33333], + "8741": [-0.00599, 0.606, 0, 0, 0.55556], + "8747": [0.30612, 0.805, 0.19445, 0, 0.47222], + "8748": [0.306, 0.805, 0.19445, 0, 0.47222], + "8749": [0.306, 0.805, 0.19445, 0, 0.47222], + "8750": [0.30612, 0.805, 0.19445, 0, 0.47222], + "8896": [0.25001, 0.75, 0, 0, 0.83334], + "8897": [0.25001, 0.75, 0, 0, 0.83334], + "8898": [0.25001, 0.75, 0, 0, 0.83334], + "8899": [0.25001, 0.75, 0, 0, 0.83334], + "8968": [0.35001, 0.85, 0, 0, 0.47222], + "8969": [0.35001, 0.85, 0, 0, 0.47222], + "8970": [0.35001, 0.85, 0, 0, 0.47222], + "8971": [0.35001, 0.85, 0, 0, 0.47222], + "9168": [-0.00099, 0.601, 0, 0, 0.66667], + "10216": [0.35001, 0.85, 0, 0, 0.47222], + "10217": [0.35001, 0.85, 0, 0, 0.47222], + "10752": [0.25001, 0.75, 0, 0, 1.11111], + "10753": [0.25001, 0.75, 0, 0, 1.11111], + "10754": [0.25001, 0.75, 0, 0, 1.11111], + "10756": [0.25001, 0.75, 0, 0, 0.83334], + "10758": [0.25001, 0.75, 0, 0, 0.83334] + }, + "Size2-Regular": { + "32": [0, 0, 0, 0, 0.25], + "40": [0.65002, 1.15, 0, 0, 0.59722], + "41": [0.65002, 1.15, 0, 0, 0.59722], + "47": [0.65002, 1.15, 0, 0, 0.81111], + "91": [0.65002, 1.15, 0, 0, 0.47222], + "92": [0.65002, 1.15, 0, 0, 0.81111], + "93": [0.65002, 1.15, 0, 0, 0.47222], + "123": [0.65002, 1.15, 0, 0, 0.66667], + "125": [0.65002, 1.15, 0, 0, 0.66667], + "160": [0, 0, 0, 0, 0.25], + "710": [0, 0.75, 0, 0, 1.0], + "732": [0, 0.75, 0, 0, 1.0], + "770": [0, 0.75, 0, 0, 1.0], + "771": [0, 0.75, 0, 0, 1.0], + "8719": [0.55001, 1.05, 0, 0, 1.27778], + "8720": [0.55001, 1.05, 0, 0, 1.27778], + "8721": [0.55001, 1.05, 0, 0, 1.44445], + "8730": [0.65002, 1.15, 0, 0, 1.0], + "8747": [0.86225, 1.36, 0.44445, 0, 0.55556], + "8748": [0.862, 1.36, 0.44445, 0, 0.55556], + "8749": [0.862, 1.36, 0.44445, 0, 0.55556], + "8750": [0.86225, 1.36, 0.44445, 0, 0.55556], + "8896": [0.55001, 1.05, 0, 0, 1.11111], + "8897": [0.55001, 1.05, 0, 0, 1.11111], + "8898": [0.55001, 1.05, 0, 0, 1.11111], + "8899": [0.55001, 1.05, 0, 0, 1.11111], + "8968": [0.65002, 1.15, 0, 0, 0.52778], + "8969": [0.65002, 1.15, 0, 0, 0.52778], + "8970": [0.65002, 1.15, 0, 0, 0.52778], + "8971": [0.65002, 1.15, 0, 0, 0.52778], + "10216": [0.65002, 1.15, 0, 0, 0.61111], + "10217": [0.65002, 1.15, 0, 0, 0.61111], + "10752": [0.55001, 1.05, 0, 0, 1.51112], + "10753": [0.55001, 1.05, 0, 0, 1.51112], + "10754": [0.55001, 1.05, 0, 0, 1.51112], + "10756": [0.55001, 1.05, 0, 0, 1.11111], + "10758": [0.55001, 1.05, 0, 0, 1.11111] + }, + "Size3-Regular": { + "32": [0, 0, 0, 0, 0.25], + "40": [0.95003, 1.45, 0, 0, 0.73611], + "41": [0.95003, 1.45, 0, 0, 0.73611], + "47": [0.95003, 1.45, 0, 0, 1.04445], + "91": [0.95003, 1.45, 0, 0, 0.52778], + "92": [0.95003, 1.45, 0, 0, 1.04445], + "93": [0.95003, 1.45, 0, 0, 0.52778], + "123": [0.95003, 1.45, 0, 0, 0.75], + "125": [0.95003, 1.45, 0, 0, 0.75], + "160": [0, 0, 0, 0, 0.25], + "710": [0, 0.75, 0, 0, 1.44445], + "732": [0, 0.75, 0, 0, 1.44445], + "770": [0, 0.75, 0, 0, 1.44445], + "771": [0, 0.75, 0, 0, 1.44445], + "8730": [0.95003, 1.45, 0, 0, 1.0], + "8968": [0.95003, 1.45, 0, 0, 0.58334], + "8969": [0.95003, 1.45, 0, 0, 0.58334], + "8970": [0.95003, 1.45, 0, 0, 0.58334], + "8971": [0.95003, 1.45, 0, 0, 0.58334], + "10216": [0.95003, 1.45, 0, 0, 0.75], + "10217": [0.95003, 1.45, 0, 0, 0.75] + }, + "Size4-Regular": { + "32": [0, 0, 0, 0, 0.25], + "40": [1.25003, 1.75, 0, 0, 0.79167], + "41": [1.25003, 1.75, 0, 0, 0.79167], + "47": [1.25003, 1.75, 0, 0, 1.27778], + "91": [1.25003, 1.75, 0, 0, 0.58334], + "92": [1.25003, 1.75, 0, 0, 1.27778], + "93": [1.25003, 1.75, 0, 0, 0.58334], + "123": [1.25003, 1.75, 0, 0, 0.80556], + "125": [1.25003, 1.75, 0, 0, 0.80556], + "160": [0, 0, 0, 0, 0.25], + "710": [0, 0.825, 0, 0, 1.8889], + "732": [0, 0.825, 0, 0, 1.8889], + "770": [0, 0.825, 0, 0, 1.8889], + "771": [0, 0.825, 0, 0, 1.8889], + "8730": [1.25003, 1.75, 0, 0, 1.0], + "8968": [1.25003, 1.75, 0, 0, 0.63889], + "8969": [1.25003, 1.75, 0, 0, 0.63889], + "8970": [1.25003, 1.75, 0, 0, 0.63889], + "8971": [1.25003, 1.75, 0, 0, 0.63889], + "9115": [0.64502, 1.155, 0, 0, 0.875], + "9116": [1e-05, 0.6, 0, 0, 0.875], + "9117": [0.64502, 1.155, 0, 0, 0.875], + "9118": [0.64502, 1.155, 0, 0, 0.875], + "9119": [1e-05, 0.6, 0, 0, 0.875], + "9120": [0.64502, 1.155, 0, 0, 0.875], + "9121": [0.64502, 1.155, 0, 0, 0.66667], + "9122": [-0.00099, 0.601, 0, 0, 0.66667], + "9123": [0.64502, 1.155, 0, 0, 0.66667], + "9124": [0.64502, 1.155, 0, 0, 0.66667], + "9125": [-0.00099, 0.601, 0, 0, 0.66667], + "9126": [0.64502, 1.155, 0, 0, 0.66667], + "9127": [1e-05, 0.9, 0, 0, 0.88889], + "9128": [0.65002, 1.15, 0, 0, 0.88889], + "9129": [0.90001, 0, 0, 0, 0.88889], + "9130": [0, 0.3, 0, 0, 0.88889], + "9131": [1e-05, 0.9, 0, 0, 0.88889], + "9132": [0.65002, 1.15, 0, 0, 0.88889], + "9133": [0.90001, 0, 0, 0, 0.88889], + "9143": [0.88502, 0.915, 0, 0, 1.05556], + "10216": [1.25003, 1.75, 0, 0, 0.80556], + "10217": [1.25003, 1.75, 0, 0, 0.80556], + "57344": [-0.00499, 0.605, 0, 0, 1.05556], + "57345": [-0.00499, 0.605, 0, 0, 1.05556], + "57680": [0, 0.12, 0, 0, 0.45], + "57681": [0, 0.12, 0, 0, 0.45], + "57682": [0, 0.12, 0, 0, 0.45], + "57683": [0, 0.12, 0, 0, 0.45] + }, + "Typewriter-Regular": { + "32": [0, 0, 0, 0, 0.525], + "33": [0, 0.61111, 0, 0, 0.525], + "34": [0, 0.61111, 0, 0, 0.525], + "35": [0, 0.61111, 0, 0, 0.525], + "36": [0.08333, 0.69444, 0, 0, 0.525], + "37": [0.08333, 0.69444, 0, 0, 0.525], + "38": [0, 0.61111, 0, 0, 0.525], + "39": [0, 0.61111, 0, 0, 0.525], + "40": [0.08333, 0.69444, 0, 0, 0.525], + "41": [0.08333, 0.69444, 0, 0, 0.525], + "42": [0, 0.52083, 0, 0, 0.525], + "43": [-0.08056, 0.53055, 0, 0, 0.525], + "44": [0.13889, 0.125, 0, 0, 0.525], + "45": [-0.08056, 0.53055, 0, 0, 0.525], + "46": [0, 0.125, 0, 0, 0.525], + "47": [0.08333, 0.69444, 0, 0, 0.525], + "48": [0, 0.61111, 0, 0, 0.525], + "49": [0, 0.61111, 0, 0, 0.525], + "50": [0, 0.61111, 0, 0, 0.525], + "51": [0, 0.61111, 0, 0, 0.525], + "52": [0, 0.61111, 0, 0, 0.525], + "53": [0, 0.61111, 0, 0, 0.525], + "54": [0, 0.61111, 0, 0, 0.525], + "55": [0, 0.61111, 0, 0, 0.525], + "56": [0, 0.61111, 0, 0, 0.525], + "57": [0, 0.61111, 0, 0, 0.525], + "58": [0, 0.43056, 0, 0, 0.525], + "59": [0.13889, 0.43056, 0, 0, 0.525], + "60": [-0.05556, 0.55556, 0, 0, 0.525], + "61": [-0.19549, 0.41562, 0, 0, 0.525], + "62": [-0.05556, 0.55556, 0, 0, 0.525], + "63": [0, 0.61111, 0, 0, 0.525], + "64": [0, 0.61111, 0, 0, 0.525], + "65": [0, 0.61111, 0, 0, 0.525], + "66": [0, 0.61111, 0, 0, 0.525], + "67": [0, 0.61111, 0, 0, 0.525], + "68": [0, 0.61111, 0, 0, 0.525], + "69": [0, 0.61111, 0, 0, 0.525], + "70": [0, 0.61111, 0, 0, 0.525], + "71": [0, 0.61111, 0, 0, 0.525], + "72": [0, 0.61111, 0, 0, 0.525], + "73": [0, 0.61111, 0, 0, 0.525], + "74": [0, 0.61111, 0, 0, 0.525], + "75": [0, 0.61111, 0, 0, 0.525], + "76": [0, 0.61111, 0, 0, 0.525], + "77": [0, 0.61111, 0, 0, 0.525], + "78": [0, 0.61111, 0, 0, 0.525], + "79": [0, 0.61111, 0, 0, 0.525], + "80": [0, 0.61111, 0, 0, 0.525], + "81": [0.13889, 0.61111, 0, 0, 0.525], + "82": [0, 0.61111, 0, 0, 0.525], + "83": [0, 0.61111, 0, 0, 0.525], + "84": [0, 0.61111, 0, 0, 0.525], + "85": [0, 0.61111, 0, 0, 0.525], + "86": [0, 0.61111, 0, 0, 0.525], + "87": [0, 0.61111, 0, 0, 0.525], + "88": [0, 0.61111, 0, 0, 0.525], + "89": [0, 0.61111, 0, 0, 0.525], + "90": [0, 0.61111, 0, 0, 0.525], + "91": [0.08333, 0.69444, 0, 0, 0.525], + "92": [0.08333, 0.69444, 0, 0, 0.525], + "93": [0.08333, 0.69444, 0, 0, 0.525], + "94": [0, 0.61111, 0, 0, 0.525], + "95": [0.09514, 0, 0, 0, 0.525], + "96": [0, 0.61111, 0, 0, 0.525], + "97": [0, 0.43056, 0, 0, 0.525], + "98": [0, 0.61111, 0, 0, 0.525], + "99": [0, 0.43056, 0, 0, 0.525], + "100": [0, 0.61111, 0, 0, 0.525], + "101": [0, 0.43056, 0, 0, 0.525], + "102": [0, 0.61111, 0, 0, 0.525], + "103": [0.22222, 0.43056, 0, 0, 0.525], + "104": [0, 0.61111, 0, 0, 0.525], + "105": [0, 0.61111, 0, 0, 0.525], + "106": [0.22222, 0.61111, 0, 0, 0.525], + "107": [0, 0.61111, 0, 0, 0.525], + "108": [0, 0.61111, 0, 0, 0.525], + "109": [0, 0.43056, 0, 0, 0.525], + "110": [0, 0.43056, 0, 0, 0.525], + "111": [0, 0.43056, 0, 0, 0.525], + "112": [0.22222, 0.43056, 0, 0, 0.525], + "113": [0.22222, 0.43056, 0, 0, 0.525], + "114": [0, 0.43056, 0, 0, 0.525], + "115": [0, 0.43056, 0, 0, 0.525], + "116": [0, 0.55358, 0, 0, 0.525], + "117": [0, 0.43056, 0, 0, 0.525], + "118": [0, 0.43056, 0, 0, 0.525], + "119": [0, 0.43056, 0, 0, 0.525], + "120": [0, 0.43056, 0, 0, 0.525], + "121": [0.22222, 0.43056, 0, 0, 0.525], + "122": [0, 0.43056, 0, 0, 0.525], + "123": [0.08333, 0.69444, 0, 0, 0.525], + "124": [0.08333, 0.69444, 0, 0, 0.525], + "125": [0.08333, 0.69444, 0, 0, 0.525], + "126": [0, 0.61111, 0, 0, 0.525], + "127": [0, 0.61111, 0, 0, 0.525], + "160": [0, 0, 0, 0, 0.525], + "176": [0, 0.61111, 0, 0, 0.525], + "184": [0.19445, 0, 0, 0, 0.525], + "305": [0, 0.43056, 0, 0, 0.525], + "567": [0.22222, 0.43056, 0, 0, 0.525], + "711": [0, 0.56597, 0, 0, 0.525], + "713": [0, 0.56555, 0, 0, 0.525], + "714": [0, 0.61111, 0, 0, 0.525], + "715": [0, 0.61111, 0, 0, 0.525], + "728": [0, 0.61111, 0, 0, 0.525], + "730": [0, 0.61111, 0, 0, 0.525], + "770": [0, 0.61111, 0, 0, 0.525], + "771": [0, 0.61111, 0, 0, 0.525], + "776": [0, 0.61111, 0, 0, 0.525], + "915": [0, 0.61111, 0, 0, 0.525], + "916": [0, 0.61111, 0, 0, 0.525], + "920": [0, 0.61111, 0, 0, 0.525], + "923": [0, 0.61111, 0, 0, 0.525], + "926": [0, 0.61111, 0, 0, 0.525], + "928": [0, 0.61111, 0, 0, 0.525], + "931": [0, 0.61111, 0, 0, 0.525], + "933": [0, 0.61111, 0, 0, 0.525], + "934": [0, 0.61111, 0, 0, 0.525], + "936": [0, 0.61111, 0, 0, 0.525], + "937": [0, 0.61111, 0, 0, 0.525], + "8216": [0, 0.61111, 0, 0, 0.525], + "8217": [0, 0.61111, 0, 0, 0.525], + "8242": [0, 0.61111, 0, 0, 0.525], + "9251": [0.11111, 0.21944, 0, 0, 0.525] + } +}); +;// CONCATENATED MODULE: ./src/fontMetrics.js + + +/** + * This file contains metrics regarding fonts and individual symbols. The sigma + * and xi variables, as well as the metricMap map contain data extracted from + * TeX, TeX font metrics, and the TTF files. These data are then exposed via the + * `metrics` variable and the getCharacterMetrics function. + */ +// In TeX, there are actually three sets of dimensions, one for each of +// textstyle (size index 5 and higher: >=9pt), scriptstyle (size index 3 and 4: +// 7-8pt), and scriptscriptstyle (size index 1 and 2: 5-6pt). These are +// provided in the the arrays below, in that order. +// +// The font metrics are stored in fonts cmsy10, cmsy7, and cmsy5 respsectively. +// This was determined by running the following script: +// +// latex -interaction=nonstopmode \ +// '\documentclass{article}\usepackage{amsmath}\begin{document}' \ +// '$a$ \expandafter\show\the\textfont2' \ +// '\expandafter\show\the\scriptfont2' \ +// '\expandafter\show\the\scriptscriptfont2' \ +// '\stop' +// +// The metrics themselves were retreived using the following commands: +// +// tftopl cmsy10 +// tftopl cmsy7 +// tftopl cmsy5 +// +// The output of each of these commands is quite lengthy. The only part we +// care about is the FONTDIMEN section. Each value is measured in EMs. +var sigmasAndXis = { + slant: [0.250, 0.250, 0.250], + // sigma1 + space: [0.000, 0.000, 0.000], + // sigma2 + stretch: [0.000, 0.000, 0.000], + // sigma3 + shrink: [0.000, 0.000, 0.000], + // sigma4 + xHeight: [0.431, 0.431, 0.431], + // sigma5 + quad: [1.000, 1.171, 1.472], + // sigma6 + extraSpace: [0.000, 0.000, 0.000], + // sigma7 + num1: [0.677, 0.732, 0.925], + // sigma8 + num2: [0.394, 0.384, 0.387], + // sigma9 + num3: [0.444, 0.471, 0.504], + // sigma10 + denom1: [0.686, 0.752, 1.025], + // sigma11 + denom2: [0.345, 0.344, 0.532], + // sigma12 + sup1: [0.413, 0.503, 0.504], + // sigma13 + sup2: [0.363, 0.431, 0.404], + // sigma14 + sup3: [0.289, 0.286, 0.294], + // sigma15 + sub1: [0.150, 0.143, 0.200], + // sigma16 + sub2: [0.247, 0.286, 0.400], + // sigma17 + supDrop: [0.386, 0.353, 0.494], + // sigma18 + subDrop: [0.050, 0.071, 0.100], + // sigma19 + delim1: [2.390, 1.700, 1.980], + // sigma20 + delim2: [1.010, 1.157, 1.420], + // sigma21 + axisHeight: [0.250, 0.250, 0.250], + // sigma22 + // These font metrics are extracted from TeX by using tftopl on cmex10.tfm; + // they correspond to the font parameters of the extension fonts (family 3). + // See the TeXbook, page 441. In AMSTeX, the extension fonts scale; to + // match cmex7, we'd use cmex7.tfm values for script and scriptscript + // values. + defaultRuleThickness: [0.04, 0.049, 0.049], + // xi8; cmex7: 0.049 + bigOpSpacing1: [0.111, 0.111, 0.111], + // xi9 + bigOpSpacing2: [0.166, 0.166, 0.166], + // xi10 + bigOpSpacing3: [0.2, 0.2, 0.2], + // xi11 + bigOpSpacing4: [0.6, 0.611, 0.611], + // xi12; cmex7: 0.611 + bigOpSpacing5: [0.1, 0.143, 0.143], + // xi13; cmex7: 0.143 + // The \sqrt rule width is taken from the height of the surd character. + // Since we use the same font at all sizes, this thickness doesn't scale. + sqrtRuleThickness: [0.04, 0.04, 0.04], + // This value determines how large a pt is, for metrics which are defined + // in terms of pts. + // This value is also used in katex.less; if you change it make sure the + // values match. + ptPerEm: [10.0, 10.0, 10.0], + // The space between adjacent `|` columns in an array definition. From + // `\showthe\doublerulesep` in LaTeX. Equals 2.0 / ptPerEm. + doubleRuleSep: [0.2, 0.2, 0.2], + // The width of separator lines in {array} environments. From + // `\showthe\arrayrulewidth` in LaTeX. Equals 0.4 / ptPerEm. + arrayRuleWidth: [0.04, 0.04, 0.04], + // Two values from LaTeX source2e: + fboxsep: [0.3, 0.3, 0.3], + // 3 pt / ptPerEm + fboxrule: [0.04, 0.04, 0.04] // 0.4 pt / ptPerEm + +}; // This map contains a mapping from font name and character code to character +// metrics, including height, depth, italic correction, and skew (kern from the +// character to the corresponding \skewchar) +// This map is generated via `make metrics`. It should not be changed manually. + + // These are very rough approximations. We default to Times New Roman which +// should have Latin-1 and Cyrillic characters, but may not depending on the +// operating system. The metrics do not account for extra height from the +// accents. In the case of Cyrillic characters which have both ascenders and +// descenders we prefer approximations with ascenders, primarily to prevent +// the fraction bar or root line from intersecting the glyph. +// TODO(kevinb) allow union of multiple glyph metrics for better accuracy. + +var extraCharacterMap = { + // Latin-1 + 'Å': 'A', + 'Ç': 'C', + 'Ð': 'D', + 'Þ': 'o', + 'å': 'a', + 'ç': 'c', + 'ð': 'd', + 'þ': 'o', + // Cyrillic + 'А': 'A', + 'Б': 'B', + 'В': 'B', + 'Г': 'F', + 'Д': 'A', + 'Е': 'E', + 'Ж': 'K', + 'З': '3', + 'И': 'N', + 'Й': 'N', + 'К': 'K', + 'Л': 'N', + 'М': 'M', + 'Н': 'H', + 'О': 'O', + 'П': 'N', + 'Р': 'P', + 'С': 'C', + 'Т': 'T', + 'У': 'y', + 'Ф': 'O', + 'Х': 'X', + 'Ц': 'U', + 'Ч': 'h', + 'Ш': 'W', + 'Щ': 'W', + 'Ъ': 'B', + 'Ы': 'X', + 'Ь': 'B', + 'Э': '3', + 'Ю': 'X', + 'Я': 'R', + 'а': 'a', + 'б': 'b', + 'в': 'a', + 'г': 'r', + 'д': 'y', + 'е': 'e', + 'ж': 'm', + 'з': 'e', + 'и': 'n', + 'й': 'n', + 'к': 'n', + 'л': 'n', + 'м': 'm', + 'н': 'n', + 'о': 'o', + 'п': 'n', + 'р': 'p', + 'с': 'c', + 'т': 'o', + 'у': 'y', + 'ф': 'b', + 'х': 'x', + 'ц': 'n', + 'ч': 'n', + 'ш': 'w', + 'щ': 'w', + 'ъ': 'a', + 'ы': 'm', + 'ь': 'a', + 'э': 'e', + 'ю': 'm', + 'я': 'r' +}; + +/** + * This function adds new font metrics to default metricMap + * It can also override existing metrics + */ +function setFontMetrics(fontName, metrics) { + fontMetricsData[fontName] = metrics; +} +/** + * This function is a convenience function for looking up information in the + * metricMap table. It takes a character as a string, and a font. + * + * Note: the `width` property may be undefined if fontMetricsData.js wasn't + * built using `Make extended_metrics`. + */ + +function getCharacterMetrics(character, font, mode) { + if (!fontMetricsData[font]) { + throw new Error("Font metrics not found for font: " + font + "."); + } + + var ch = character.charCodeAt(0); + var metrics = fontMetricsData[font][ch]; + + if (!metrics && character[0] in extraCharacterMap) { + ch = extraCharacterMap[character[0]].charCodeAt(0); + metrics = fontMetricsData[font][ch]; + } + + if (!metrics && mode === 'text') { + // We don't typically have font metrics for Asian scripts. + // But since we support them in text mode, we need to return + // some sort of metrics. + // So if the character is in a script we support but we + // don't have metrics for it, just use the metrics for + // the Latin capital letter M. This is close enough because + // we (currently) only care about the height of the glpyh + // not its width. + if (supportedCodepoint(ch)) { + metrics = fontMetricsData[font][77]; // 77 is the charcode for 'M' + } + } + + if (metrics) { + return { + depth: metrics[0], + height: metrics[1], + italic: metrics[2], + skew: metrics[3], + width: metrics[4] + }; + } +} +var fontMetricsBySizeIndex = {}; +/** + * Get the font metrics for a given size. + */ + +function getGlobalMetrics(size) { + var sizeIndex; + + if (size >= 5) { + sizeIndex = 0; + } else if (size >= 3) { + sizeIndex = 1; + } else { + sizeIndex = 2; + } + + if (!fontMetricsBySizeIndex[sizeIndex]) { + var metrics = fontMetricsBySizeIndex[sizeIndex] = { + cssEmPerMu: sigmasAndXis.quad[sizeIndex] / 18 + }; + + for (var key in sigmasAndXis) { + if (sigmasAndXis.hasOwnProperty(key)) { + metrics[key] = sigmasAndXis[key][sizeIndex]; + } + } + } + + return fontMetricsBySizeIndex[sizeIndex]; +} +;// CONCATENATED MODULE: ./src/symbols.js +/** + * This file holds a list of all no-argument functions and single-character + * symbols (like 'a' or ';'). + * + * For each of the symbols, there are three properties they can have: + * - font (required): the font to be used for this symbol. Either "main" (the + normal font), or "ams" (the ams fonts). + * - group (required): the ParseNode group type the symbol should have (i.e. + "textord", "mathord", etc). + See https://github.com/KaTeX/KaTeX/wiki/Examining-TeX#group-types + * - replace: the character that this symbol or function should be + * replaced with (i.e. "\phi" has a replace value of "\u03d5", the phi + * character in the main font). + * + * The outermost map in the table indicates what mode the symbols should be + * accepted in (e.g. "math" or "text"). + */ +// Some of these have a "-token" suffix since these are also used as `ParseNode` +// types for raw text tokens, and we want to avoid conflicts with higher-level +// `ParseNode` types. These `ParseNode`s are constructed within `Parser` by +// looking up the `symbols` map. +var ATOMS = { + "bin": 1, + "close": 1, + "inner": 1, + "open": 1, + "punct": 1, + "rel": 1 +}; +var NON_ATOMS = { + "accent-token": 1, + "mathord": 1, + "op-token": 1, + "spacing": 1, + "textord": 1 +}; +var symbols = { + "math": {}, + "text": {} +}; +/* harmony default export */ var src_symbols = (symbols); +/** `acceptUnicodeChar = true` is only applicable if `replace` is set. */ + +function defineSymbol(mode, font, group, replace, name, acceptUnicodeChar) { + symbols[mode][name] = { + font: font, + group: group, + replace: replace + }; + + if (acceptUnicodeChar && replace) { + symbols[mode][replace] = symbols[mode][name]; + } +} // Some abbreviations for commonly used strings. +// This helps minify the code, and also spotting typos using jshint. +// modes: + +var math = "math"; +var symbols_text = "text"; // fonts: + +var main = "main"; +var ams = "ams"; // groups: + +var accent = "accent-token"; +var bin = "bin"; +var symbols_close = "close"; +var inner = "inner"; +var mathord = "mathord"; +var op = "op-token"; +var symbols_open = "open"; +var punct = "punct"; +var rel = "rel"; +var spacing = "spacing"; +var textord = "textord"; // Now comes the symbol table +// Relation Symbols + +defineSymbol(math, main, rel, "\u2261", "\\equiv", true); +defineSymbol(math, main, rel, "\u227A", "\\prec", true); +defineSymbol(math, main, rel, "\u227B", "\\succ", true); +defineSymbol(math, main, rel, "\u223C", "\\sim", true); +defineSymbol(math, main, rel, "\u22A5", "\\perp"); +defineSymbol(math, main, rel, "\u2AAF", "\\preceq", true); +defineSymbol(math, main, rel, "\u2AB0", "\\succeq", true); +defineSymbol(math, main, rel, "\u2243", "\\simeq", true); +defineSymbol(math, main, rel, "\u2223", "\\mid", true); +defineSymbol(math, main, rel, "\u226A", "\\ll", true); +defineSymbol(math, main, rel, "\u226B", "\\gg", true); +defineSymbol(math, main, rel, "\u224D", "\\asymp", true); +defineSymbol(math, main, rel, "\u2225", "\\parallel"); +defineSymbol(math, main, rel, "\u22C8", "\\bowtie", true); +defineSymbol(math, main, rel, "\u2323", "\\smile", true); +defineSymbol(math, main, rel, "\u2291", "\\sqsubseteq", true); +defineSymbol(math, main, rel, "\u2292", "\\sqsupseteq", true); +defineSymbol(math, main, rel, "\u2250", "\\doteq", true); +defineSymbol(math, main, rel, "\u2322", "\\frown", true); +defineSymbol(math, main, rel, "\u220B", "\\ni", true); +defineSymbol(math, main, rel, "\u221D", "\\propto", true); +defineSymbol(math, main, rel, "\u22A2", "\\vdash", true); +defineSymbol(math, main, rel, "\u22A3", "\\dashv", true); +defineSymbol(math, main, rel, "\u220B", "\\owns"); // Punctuation + +defineSymbol(math, main, punct, ".", "\\ldotp"); +defineSymbol(math, main, punct, "\u22C5", "\\cdotp"); // Misc Symbols + +defineSymbol(math, main, textord, "#", "\\#"); +defineSymbol(symbols_text, main, textord, "#", "\\#"); +defineSymbol(math, main, textord, "&", "\\&"); +defineSymbol(symbols_text, main, textord, "&", "\\&"); +defineSymbol(math, main, textord, "\u2135", "\\aleph", true); +defineSymbol(math, main, textord, "\u2200", "\\forall", true); +defineSymbol(math, main, textord, "\u210F", "\\hbar", true); +defineSymbol(math, main, textord, "\u2203", "\\exists", true); +defineSymbol(math, main, textord, "\u2207", "\\nabla", true); +defineSymbol(math, main, textord, "\u266D", "\\flat", true); +defineSymbol(math, main, textord, "\u2113", "\\ell", true); +defineSymbol(math, main, textord, "\u266E", "\\natural", true); +defineSymbol(math, main, textord, "\u2663", "\\clubsuit", true); +defineSymbol(math, main, textord, "\u2118", "\\wp", true); +defineSymbol(math, main, textord, "\u266F", "\\sharp", true); +defineSymbol(math, main, textord, "\u2662", "\\diamondsuit", true); +defineSymbol(math, main, textord, "\u211C", "\\Re", true); +defineSymbol(math, main, textord, "\u2661", "\\heartsuit", true); +defineSymbol(math, main, textord, "\u2111", "\\Im", true); +defineSymbol(math, main, textord, "\u2660", "\\spadesuit", true); +defineSymbol(math, main, textord, "\xA7", "\\S", true); +defineSymbol(symbols_text, main, textord, "\xA7", "\\S"); +defineSymbol(math, main, textord, "\xB6", "\\P", true); +defineSymbol(symbols_text, main, textord, "\xB6", "\\P"); // Math and Text + +defineSymbol(math, main, textord, "\u2020", "\\dag"); +defineSymbol(symbols_text, main, textord, "\u2020", "\\dag"); +defineSymbol(symbols_text, main, textord, "\u2020", "\\textdagger"); +defineSymbol(math, main, textord, "\u2021", "\\ddag"); +defineSymbol(symbols_text, main, textord, "\u2021", "\\ddag"); +defineSymbol(symbols_text, main, textord, "\u2021", "\\textdaggerdbl"); // Large Delimiters + +defineSymbol(math, main, symbols_close, "\u23B1", "\\rmoustache", true); +defineSymbol(math, main, symbols_open, "\u23B0", "\\lmoustache", true); +defineSymbol(math, main, symbols_close, "\u27EF", "\\rgroup", true); +defineSymbol(math, main, symbols_open, "\u27EE", "\\lgroup", true); // Binary Operators + +defineSymbol(math, main, bin, "\u2213", "\\mp", true); +defineSymbol(math, main, bin, "\u2296", "\\ominus", true); +defineSymbol(math, main, bin, "\u228E", "\\uplus", true); +defineSymbol(math, main, bin, "\u2293", "\\sqcap", true); +defineSymbol(math, main, bin, "\u2217", "\\ast"); +defineSymbol(math, main, bin, "\u2294", "\\sqcup", true); +defineSymbol(math, main, bin, "\u25EF", "\\bigcirc", true); +defineSymbol(math, main, bin, "\u2219", "\\bullet"); +defineSymbol(math, main, bin, "\u2021", "\\ddagger"); +defineSymbol(math, main, bin, "\u2240", "\\wr", true); +defineSymbol(math, main, bin, "\u2A3F", "\\amalg"); +defineSymbol(math, main, bin, "&", "\\And"); // from amsmath +// Arrow Symbols + +defineSymbol(math, main, rel, "\u27F5", "\\longleftarrow", true); +defineSymbol(math, main, rel, "\u21D0", "\\Leftarrow", true); +defineSymbol(math, main, rel, "\u27F8", "\\Longleftarrow", true); +defineSymbol(math, main, rel, "\u27F6", "\\longrightarrow", true); +defineSymbol(math, main, rel, "\u21D2", "\\Rightarrow", true); +defineSymbol(math, main, rel, "\u27F9", "\\Longrightarrow", true); +defineSymbol(math, main, rel, "\u2194", "\\leftrightarrow", true); +defineSymbol(math, main, rel, "\u27F7", "\\longleftrightarrow", true); +defineSymbol(math, main, rel, "\u21D4", "\\Leftrightarrow", true); +defineSymbol(math, main, rel, "\u27FA", "\\Longleftrightarrow", true); +defineSymbol(math, main, rel, "\u21A6", "\\mapsto", true); +defineSymbol(math, main, rel, "\u27FC", "\\longmapsto", true); +defineSymbol(math, main, rel, "\u2197", "\\nearrow", true); +defineSymbol(math, main, rel, "\u21A9", "\\hookleftarrow", true); +defineSymbol(math, main, rel, "\u21AA", "\\hookrightarrow", true); +defineSymbol(math, main, rel, "\u2198", "\\searrow", true); +defineSymbol(math, main, rel, "\u21BC", "\\leftharpoonup", true); +defineSymbol(math, main, rel, "\u21C0", "\\rightharpoonup", true); +defineSymbol(math, main, rel, "\u2199", "\\swarrow", true); +defineSymbol(math, main, rel, "\u21BD", "\\leftharpoondown", true); +defineSymbol(math, main, rel, "\u21C1", "\\rightharpoondown", true); +defineSymbol(math, main, rel, "\u2196", "\\nwarrow", true); +defineSymbol(math, main, rel, "\u21CC", "\\rightleftharpoons", true); // AMS Negated Binary Relations + +defineSymbol(math, ams, rel, "\u226E", "\\nless", true); // Symbol names preceeded by "@" each have a corresponding macro. + +defineSymbol(math, ams, rel, "\uE010", "\\@nleqslant"); +defineSymbol(math, ams, rel, "\uE011", "\\@nleqq"); +defineSymbol(math, ams, rel, "\u2A87", "\\lneq", true); +defineSymbol(math, ams, rel, "\u2268", "\\lneqq", true); +defineSymbol(math, ams, rel, "\uE00C", "\\@lvertneqq"); +defineSymbol(math, ams, rel, "\u22E6", "\\lnsim", true); +defineSymbol(math, ams, rel, "\u2A89", "\\lnapprox", true); +defineSymbol(math, ams, rel, "\u2280", "\\nprec", true); // unicode-math maps \u22e0 to \npreccurlyeq. We'll use the AMS synonym. + +defineSymbol(math, ams, rel, "\u22E0", "\\npreceq", true); +defineSymbol(math, ams, rel, "\u22E8", "\\precnsim", true); +defineSymbol(math, ams, rel, "\u2AB9", "\\precnapprox", true); +defineSymbol(math, ams, rel, "\u2241", "\\nsim", true); +defineSymbol(math, ams, rel, "\uE006", "\\@nshortmid"); +defineSymbol(math, ams, rel, "\u2224", "\\nmid", true); +defineSymbol(math, ams, rel, "\u22AC", "\\nvdash", true); +defineSymbol(math, ams, rel, "\u22AD", "\\nvDash", true); +defineSymbol(math, ams, rel, "\u22EA", "\\ntriangleleft"); +defineSymbol(math, ams, rel, "\u22EC", "\\ntrianglelefteq", true); +defineSymbol(math, ams, rel, "\u228A", "\\subsetneq", true); +defineSymbol(math, ams, rel, "\uE01A", "\\@varsubsetneq"); +defineSymbol(math, ams, rel, "\u2ACB", "\\subsetneqq", true); +defineSymbol(math, ams, rel, "\uE017", "\\@varsubsetneqq"); +defineSymbol(math, ams, rel, "\u226F", "\\ngtr", true); +defineSymbol(math, ams, rel, "\uE00F", "\\@ngeqslant"); +defineSymbol(math, ams, rel, "\uE00E", "\\@ngeqq"); +defineSymbol(math, ams, rel, "\u2A88", "\\gneq", true); +defineSymbol(math, ams, rel, "\u2269", "\\gneqq", true); +defineSymbol(math, ams, rel, "\uE00D", "\\@gvertneqq"); +defineSymbol(math, ams, rel, "\u22E7", "\\gnsim", true); +defineSymbol(math, ams, rel, "\u2A8A", "\\gnapprox", true); +defineSymbol(math, ams, rel, "\u2281", "\\nsucc", true); // unicode-math maps \u22e1 to \nsucccurlyeq. We'll use the AMS synonym. + +defineSymbol(math, ams, rel, "\u22E1", "\\nsucceq", true); +defineSymbol(math, ams, rel, "\u22E9", "\\succnsim", true); +defineSymbol(math, ams, rel, "\u2ABA", "\\succnapprox", true); // unicode-math maps \u2246 to \simneqq. We'll use the AMS synonym. + +defineSymbol(math, ams, rel, "\u2246", "\\ncong", true); +defineSymbol(math, ams, rel, "\uE007", "\\@nshortparallel"); +defineSymbol(math, ams, rel, "\u2226", "\\nparallel", true); +defineSymbol(math, ams, rel, "\u22AF", "\\nVDash", true); +defineSymbol(math, ams, rel, "\u22EB", "\\ntriangleright"); +defineSymbol(math, ams, rel, "\u22ED", "\\ntrianglerighteq", true); +defineSymbol(math, ams, rel, "\uE018", "\\@nsupseteqq"); +defineSymbol(math, ams, rel, "\u228B", "\\supsetneq", true); +defineSymbol(math, ams, rel, "\uE01B", "\\@varsupsetneq"); +defineSymbol(math, ams, rel, "\u2ACC", "\\supsetneqq", true); +defineSymbol(math, ams, rel, "\uE019", "\\@varsupsetneqq"); +defineSymbol(math, ams, rel, "\u22AE", "\\nVdash", true); +defineSymbol(math, ams, rel, "\u2AB5", "\\precneqq", true); +defineSymbol(math, ams, rel, "\u2AB6", "\\succneqq", true); +defineSymbol(math, ams, rel, "\uE016", "\\@nsubseteqq"); +defineSymbol(math, ams, bin, "\u22B4", "\\unlhd"); +defineSymbol(math, ams, bin, "\u22B5", "\\unrhd"); // AMS Negated Arrows + +defineSymbol(math, ams, rel, "\u219A", "\\nleftarrow", true); +defineSymbol(math, ams, rel, "\u219B", "\\nrightarrow", true); +defineSymbol(math, ams, rel, "\u21CD", "\\nLeftarrow", true); +defineSymbol(math, ams, rel, "\u21CF", "\\nRightarrow", true); +defineSymbol(math, ams, rel, "\u21AE", "\\nleftrightarrow", true); +defineSymbol(math, ams, rel, "\u21CE", "\\nLeftrightarrow", true); // AMS Misc + +defineSymbol(math, ams, rel, "\u25B3", "\\vartriangle"); +defineSymbol(math, ams, textord, "\u210F", "\\hslash"); +defineSymbol(math, ams, textord, "\u25BD", "\\triangledown"); +defineSymbol(math, ams, textord, "\u25CA", "\\lozenge"); +defineSymbol(math, ams, textord, "\u24C8", "\\circledS"); +defineSymbol(math, ams, textord, "\xAE", "\\circledR"); +defineSymbol(symbols_text, ams, textord, "\xAE", "\\circledR"); +defineSymbol(math, ams, textord, "\u2221", "\\measuredangle", true); +defineSymbol(math, ams, textord, "\u2204", "\\nexists"); +defineSymbol(math, ams, textord, "\u2127", "\\mho"); +defineSymbol(math, ams, textord, "\u2132", "\\Finv", true); +defineSymbol(math, ams, textord, "\u2141", "\\Game", true); +defineSymbol(math, ams, textord, "\u2035", "\\backprime"); +defineSymbol(math, ams, textord, "\u25B2", "\\blacktriangle"); +defineSymbol(math, ams, textord, "\u25BC", "\\blacktriangledown"); +defineSymbol(math, ams, textord, "\u25A0", "\\blacksquare"); +defineSymbol(math, ams, textord, "\u29EB", "\\blacklozenge"); +defineSymbol(math, ams, textord, "\u2605", "\\bigstar"); +defineSymbol(math, ams, textord, "\u2222", "\\sphericalangle", true); +defineSymbol(math, ams, textord, "\u2201", "\\complement", true); // unicode-math maps U+F0 to \matheth. We map to AMS function \eth + +defineSymbol(math, ams, textord, "\xF0", "\\eth", true); +defineSymbol(symbols_text, main, textord, "\xF0", "\xF0"); +defineSymbol(math, ams, textord, "\u2571", "\\diagup"); +defineSymbol(math, ams, textord, "\u2572", "\\diagdown"); +defineSymbol(math, ams, textord, "\u25A1", "\\square"); +defineSymbol(math, ams, textord, "\u25A1", "\\Box"); +defineSymbol(math, ams, textord, "\u25CA", "\\Diamond"); // unicode-math maps U+A5 to \mathyen. We map to AMS function \yen + +defineSymbol(math, ams, textord, "\xA5", "\\yen", true); +defineSymbol(symbols_text, ams, textord, "\xA5", "\\yen", true); +defineSymbol(math, ams, textord, "\u2713", "\\checkmark", true); +defineSymbol(symbols_text, ams, textord, "\u2713", "\\checkmark"); // AMS Hebrew + +defineSymbol(math, ams, textord, "\u2136", "\\beth", true); +defineSymbol(math, ams, textord, "\u2138", "\\daleth", true); +defineSymbol(math, ams, textord, "\u2137", "\\gimel", true); // AMS Greek + +defineSymbol(math, ams, textord, "\u03DD", "\\digamma", true); +defineSymbol(math, ams, textord, "\u03F0", "\\varkappa"); // AMS Delimiters + +defineSymbol(math, ams, symbols_open, "\u250C", "\\@ulcorner", true); +defineSymbol(math, ams, symbols_close, "\u2510", "\\@urcorner", true); +defineSymbol(math, ams, symbols_open, "\u2514", "\\@llcorner", true); +defineSymbol(math, ams, symbols_close, "\u2518", "\\@lrcorner", true); // AMS Binary Relations + +defineSymbol(math, ams, rel, "\u2266", "\\leqq", true); +defineSymbol(math, ams, rel, "\u2A7D", "\\leqslant", true); +defineSymbol(math, ams, rel, "\u2A95", "\\eqslantless", true); +defineSymbol(math, ams, rel, "\u2272", "\\lesssim", true); +defineSymbol(math, ams, rel, "\u2A85", "\\lessapprox", true); +defineSymbol(math, ams, rel, "\u224A", "\\approxeq", true); +defineSymbol(math, ams, bin, "\u22D6", "\\lessdot"); +defineSymbol(math, ams, rel, "\u22D8", "\\lll", true); +defineSymbol(math, ams, rel, "\u2276", "\\lessgtr", true); +defineSymbol(math, ams, rel, "\u22DA", "\\lesseqgtr", true); +defineSymbol(math, ams, rel, "\u2A8B", "\\lesseqqgtr", true); +defineSymbol(math, ams, rel, "\u2251", "\\doteqdot"); +defineSymbol(math, ams, rel, "\u2253", "\\risingdotseq", true); +defineSymbol(math, ams, rel, "\u2252", "\\fallingdotseq", true); +defineSymbol(math, ams, rel, "\u223D", "\\backsim", true); +defineSymbol(math, ams, rel, "\u22CD", "\\backsimeq", true); +defineSymbol(math, ams, rel, "\u2AC5", "\\subseteqq", true); +defineSymbol(math, ams, rel, "\u22D0", "\\Subset", true); +defineSymbol(math, ams, rel, "\u228F", "\\sqsubset", true); +defineSymbol(math, ams, rel, "\u227C", "\\preccurlyeq", true); +defineSymbol(math, ams, rel, "\u22DE", "\\curlyeqprec", true); +defineSymbol(math, ams, rel, "\u227E", "\\precsim", true); +defineSymbol(math, ams, rel, "\u2AB7", "\\precapprox", true); +defineSymbol(math, ams, rel, "\u22B2", "\\vartriangleleft"); +defineSymbol(math, ams, rel, "\u22B4", "\\trianglelefteq"); +defineSymbol(math, ams, rel, "\u22A8", "\\vDash", true); +defineSymbol(math, ams, rel, "\u22AA", "\\Vvdash", true); +defineSymbol(math, ams, rel, "\u2323", "\\smallsmile"); +defineSymbol(math, ams, rel, "\u2322", "\\smallfrown"); +defineSymbol(math, ams, rel, "\u224F", "\\bumpeq", true); +defineSymbol(math, ams, rel, "\u224E", "\\Bumpeq", true); +defineSymbol(math, ams, rel, "\u2267", "\\geqq", true); +defineSymbol(math, ams, rel, "\u2A7E", "\\geqslant", true); +defineSymbol(math, ams, rel, "\u2A96", "\\eqslantgtr", true); +defineSymbol(math, ams, rel, "\u2273", "\\gtrsim", true); +defineSymbol(math, ams, rel, "\u2A86", "\\gtrapprox", true); +defineSymbol(math, ams, bin, "\u22D7", "\\gtrdot"); +defineSymbol(math, ams, rel, "\u22D9", "\\ggg", true); +defineSymbol(math, ams, rel, "\u2277", "\\gtrless", true); +defineSymbol(math, ams, rel, "\u22DB", "\\gtreqless", true); +defineSymbol(math, ams, rel, "\u2A8C", "\\gtreqqless", true); +defineSymbol(math, ams, rel, "\u2256", "\\eqcirc", true); +defineSymbol(math, ams, rel, "\u2257", "\\circeq", true); +defineSymbol(math, ams, rel, "\u225C", "\\triangleq", true); +defineSymbol(math, ams, rel, "\u223C", "\\thicksim"); +defineSymbol(math, ams, rel, "\u2248", "\\thickapprox"); +defineSymbol(math, ams, rel, "\u2AC6", "\\supseteqq", true); +defineSymbol(math, ams, rel, "\u22D1", "\\Supset", true); +defineSymbol(math, ams, rel, "\u2290", "\\sqsupset", true); +defineSymbol(math, ams, rel, "\u227D", "\\succcurlyeq", true); +defineSymbol(math, ams, rel, "\u22DF", "\\curlyeqsucc", true); +defineSymbol(math, ams, rel, "\u227F", "\\succsim", true); +defineSymbol(math, ams, rel, "\u2AB8", "\\succapprox", true); +defineSymbol(math, ams, rel, "\u22B3", "\\vartriangleright"); +defineSymbol(math, ams, rel, "\u22B5", "\\trianglerighteq"); +defineSymbol(math, ams, rel, "\u22A9", "\\Vdash", true); +defineSymbol(math, ams, rel, "\u2223", "\\shortmid"); +defineSymbol(math, ams, rel, "\u2225", "\\shortparallel"); +defineSymbol(math, ams, rel, "\u226C", "\\between", true); +defineSymbol(math, ams, rel, "\u22D4", "\\pitchfork", true); +defineSymbol(math, ams, rel, "\u221D", "\\varpropto"); +defineSymbol(math, ams, rel, "\u25C0", "\\blacktriangleleft"); // unicode-math says that \therefore is a mathord atom. +// We kept the amssymb atom type, which is rel. + +defineSymbol(math, ams, rel, "\u2234", "\\therefore", true); +defineSymbol(math, ams, rel, "\u220D", "\\backepsilon"); +defineSymbol(math, ams, rel, "\u25B6", "\\blacktriangleright"); // unicode-math says that \because is a mathord atom. +// We kept the amssymb atom type, which is rel. + +defineSymbol(math, ams, rel, "\u2235", "\\because", true); +defineSymbol(math, ams, rel, "\u22D8", "\\llless"); +defineSymbol(math, ams, rel, "\u22D9", "\\gggtr"); +defineSymbol(math, ams, bin, "\u22B2", "\\lhd"); +defineSymbol(math, ams, bin, "\u22B3", "\\rhd"); +defineSymbol(math, ams, rel, "\u2242", "\\eqsim", true); +defineSymbol(math, main, rel, "\u22C8", "\\Join"); +defineSymbol(math, ams, rel, "\u2251", "\\Doteq", true); // AMS Binary Operators + +defineSymbol(math, ams, bin, "\u2214", "\\dotplus", true); +defineSymbol(math, ams, bin, "\u2216", "\\smallsetminus"); +defineSymbol(math, ams, bin, "\u22D2", "\\Cap", true); +defineSymbol(math, ams, bin, "\u22D3", "\\Cup", true); +defineSymbol(math, ams, bin, "\u2A5E", "\\doublebarwedge", true); +defineSymbol(math, ams, bin, "\u229F", "\\boxminus", true); +defineSymbol(math, ams, bin, "\u229E", "\\boxplus", true); +defineSymbol(math, ams, bin, "\u22C7", "\\divideontimes", true); +defineSymbol(math, ams, bin, "\u22C9", "\\ltimes", true); +defineSymbol(math, ams, bin, "\u22CA", "\\rtimes", true); +defineSymbol(math, ams, bin, "\u22CB", "\\leftthreetimes", true); +defineSymbol(math, ams, bin, "\u22CC", "\\rightthreetimes", true); +defineSymbol(math, ams, bin, "\u22CF", "\\curlywedge", true); +defineSymbol(math, ams, bin, "\u22CE", "\\curlyvee", true); +defineSymbol(math, ams, bin, "\u229D", "\\circleddash", true); +defineSymbol(math, ams, bin, "\u229B", "\\circledast", true); +defineSymbol(math, ams, bin, "\u22C5", "\\centerdot"); +defineSymbol(math, ams, bin, "\u22BA", "\\intercal", true); +defineSymbol(math, ams, bin, "\u22D2", "\\doublecap"); +defineSymbol(math, ams, bin, "\u22D3", "\\doublecup"); +defineSymbol(math, ams, bin, "\u22A0", "\\boxtimes", true); // AMS Arrows +// Note: unicode-math maps \u21e2 to their own function \rightdasharrow. +// We'll map it to AMS function \dashrightarrow. It produces the same atom. + +defineSymbol(math, ams, rel, "\u21E2", "\\dashrightarrow", true); // unicode-math maps \u21e0 to \leftdasharrow. We'll use the AMS synonym. + +defineSymbol(math, ams, rel, "\u21E0", "\\dashleftarrow", true); +defineSymbol(math, ams, rel, "\u21C7", "\\leftleftarrows", true); +defineSymbol(math, ams, rel, "\u21C6", "\\leftrightarrows", true); +defineSymbol(math, ams, rel, "\u21DA", "\\Lleftarrow", true); +defineSymbol(math, ams, rel, "\u219E", "\\twoheadleftarrow", true); +defineSymbol(math, ams, rel, "\u21A2", "\\leftarrowtail", true); +defineSymbol(math, ams, rel, "\u21AB", "\\looparrowleft", true); +defineSymbol(math, ams, rel, "\u21CB", "\\leftrightharpoons", true); +defineSymbol(math, ams, rel, "\u21B6", "\\curvearrowleft", true); // unicode-math maps \u21ba to \acwopencirclearrow. We'll use the AMS synonym. + +defineSymbol(math, ams, rel, "\u21BA", "\\circlearrowleft", true); +defineSymbol(math, ams, rel, "\u21B0", "\\Lsh", true); +defineSymbol(math, ams, rel, "\u21C8", "\\upuparrows", true); +defineSymbol(math, ams, rel, "\u21BF", "\\upharpoonleft", true); +defineSymbol(math, ams, rel, "\u21C3", "\\downharpoonleft", true); +defineSymbol(math, main, rel, "\u22B6", "\\origof", true); // not in font + +defineSymbol(math, main, rel, "\u22B7", "\\imageof", true); // not in font + +defineSymbol(math, ams, rel, "\u22B8", "\\multimap", true); +defineSymbol(math, ams, rel, "\u21AD", "\\leftrightsquigarrow", true); +defineSymbol(math, ams, rel, "\u21C9", "\\rightrightarrows", true); +defineSymbol(math, ams, rel, "\u21C4", "\\rightleftarrows", true); +defineSymbol(math, ams, rel, "\u21A0", "\\twoheadrightarrow", true); +defineSymbol(math, ams, rel, "\u21A3", "\\rightarrowtail", true); +defineSymbol(math, ams, rel, "\u21AC", "\\looparrowright", true); +defineSymbol(math, ams, rel, "\u21B7", "\\curvearrowright", true); // unicode-math maps \u21bb to \cwopencirclearrow. We'll use the AMS synonym. + +defineSymbol(math, ams, rel, "\u21BB", "\\circlearrowright", true); +defineSymbol(math, ams, rel, "\u21B1", "\\Rsh", true); +defineSymbol(math, ams, rel, "\u21CA", "\\downdownarrows", true); +defineSymbol(math, ams, rel, "\u21BE", "\\upharpoonright", true); +defineSymbol(math, ams, rel, "\u21C2", "\\downharpoonright", true); +defineSymbol(math, ams, rel, "\u21DD", "\\rightsquigarrow", true); +defineSymbol(math, ams, rel, "\u21DD", "\\leadsto"); +defineSymbol(math, ams, rel, "\u21DB", "\\Rrightarrow", true); +defineSymbol(math, ams, rel, "\u21BE", "\\restriction"); +defineSymbol(math, main, textord, "\u2018", "`"); +defineSymbol(math, main, textord, "$", "\\$"); +defineSymbol(symbols_text, main, textord, "$", "\\$"); +defineSymbol(symbols_text, main, textord, "$", "\\textdollar"); +defineSymbol(math, main, textord, "%", "\\%"); +defineSymbol(symbols_text, main, textord, "%", "\\%"); +defineSymbol(math, main, textord, "_", "\\_"); +defineSymbol(symbols_text, main, textord, "_", "\\_"); +defineSymbol(symbols_text, main, textord, "_", "\\textunderscore"); +defineSymbol(math, main, textord, "\u2220", "\\angle", true); +defineSymbol(math, main, textord, "\u221E", "\\infty", true); +defineSymbol(math, main, textord, "\u2032", "\\prime"); +defineSymbol(math, main, textord, "\u25B3", "\\triangle"); +defineSymbol(math, main, textord, "\u0393", "\\Gamma", true); +defineSymbol(math, main, textord, "\u0394", "\\Delta", true); +defineSymbol(math, main, textord, "\u0398", "\\Theta", true); +defineSymbol(math, main, textord, "\u039B", "\\Lambda", true); +defineSymbol(math, main, textord, "\u039E", "\\Xi", true); +defineSymbol(math, main, textord, "\u03A0", "\\Pi", true); +defineSymbol(math, main, textord, "\u03A3", "\\Sigma", true); +defineSymbol(math, main, textord, "\u03A5", "\\Upsilon", true); +defineSymbol(math, main, textord, "\u03A6", "\\Phi", true); +defineSymbol(math, main, textord, "\u03A8", "\\Psi", true); +defineSymbol(math, main, textord, "\u03A9", "\\Omega", true); +defineSymbol(math, main, textord, "A", "\u0391"); +defineSymbol(math, main, textord, "B", "\u0392"); +defineSymbol(math, main, textord, "E", "\u0395"); +defineSymbol(math, main, textord, "Z", "\u0396"); +defineSymbol(math, main, textord, "H", "\u0397"); +defineSymbol(math, main, textord, "I", "\u0399"); +defineSymbol(math, main, textord, "K", "\u039A"); +defineSymbol(math, main, textord, "M", "\u039C"); +defineSymbol(math, main, textord, "N", "\u039D"); +defineSymbol(math, main, textord, "O", "\u039F"); +defineSymbol(math, main, textord, "P", "\u03A1"); +defineSymbol(math, main, textord, "T", "\u03A4"); +defineSymbol(math, main, textord, "X", "\u03A7"); +defineSymbol(math, main, textord, "\xAC", "\\neg", true); +defineSymbol(math, main, textord, "\xAC", "\\lnot"); +defineSymbol(math, main, textord, "\u22A4", "\\top"); +defineSymbol(math, main, textord, "\u22A5", "\\bot"); +defineSymbol(math, main, textord, "\u2205", "\\emptyset"); +defineSymbol(math, ams, textord, "\u2205", "\\varnothing"); +defineSymbol(math, main, mathord, "\u03B1", "\\alpha", true); +defineSymbol(math, main, mathord, "\u03B2", "\\beta", true); +defineSymbol(math, main, mathord, "\u03B3", "\\gamma", true); +defineSymbol(math, main, mathord, "\u03B4", "\\delta", true); +defineSymbol(math, main, mathord, "\u03F5", "\\epsilon", true); +defineSymbol(math, main, mathord, "\u03B6", "\\zeta", true); +defineSymbol(math, main, mathord, "\u03B7", "\\eta", true); +defineSymbol(math, main, mathord, "\u03B8", "\\theta", true); +defineSymbol(math, main, mathord, "\u03B9", "\\iota", true); +defineSymbol(math, main, mathord, "\u03BA", "\\kappa", true); +defineSymbol(math, main, mathord, "\u03BB", "\\lambda", true); +defineSymbol(math, main, mathord, "\u03BC", "\\mu", true); +defineSymbol(math, main, mathord, "\u03BD", "\\nu", true); +defineSymbol(math, main, mathord, "\u03BE", "\\xi", true); +defineSymbol(math, main, mathord, "\u03BF", "\\omicron", true); +defineSymbol(math, main, mathord, "\u03C0", "\\pi", true); +defineSymbol(math, main, mathord, "\u03C1", "\\rho", true); +defineSymbol(math, main, mathord, "\u03C3", "\\sigma", true); +defineSymbol(math, main, mathord, "\u03C4", "\\tau", true); +defineSymbol(math, main, mathord, "\u03C5", "\\upsilon", true); +defineSymbol(math, main, mathord, "\u03D5", "\\phi", true); +defineSymbol(math, main, mathord, "\u03C7", "\\chi", true); +defineSymbol(math, main, mathord, "\u03C8", "\\psi", true); +defineSymbol(math, main, mathord, "\u03C9", "\\omega", true); +defineSymbol(math, main, mathord, "\u03B5", "\\varepsilon", true); +defineSymbol(math, main, mathord, "\u03D1", "\\vartheta", true); +defineSymbol(math, main, mathord, "\u03D6", "\\varpi", true); +defineSymbol(math, main, mathord, "\u03F1", "\\varrho", true); +defineSymbol(math, main, mathord, "\u03C2", "\\varsigma", true); +defineSymbol(math, main, mathord, "\u03C6", "\\varphi", true); +defineSymbol(math, main, bin, "\u2217", "*"); +defineSymbol(math, main, bin, "+", "+"); +defineSymbol(math, main, bin, "\u2212", "-"); +defineSymbol(math, main, bin, "\u22C5", "\\cdot", true); +defineSymbol(math, main, bin, "\u2218", "\\circ"); +defineSymbol(math, main, bin, "\xF7", "\\div", true); +defineSymbol(math, main, bin, "\xB1", "\\pm", true); +defineSymbol(math, main, bin, "\xD7", "\\times", true); +defineSymbol(math, main, bin, "\u2229", "\\cap", true); +defineSymbol(math, main, bin, "\u222A", "\\cup", true); +defineSymbol(math, main, bin, "\u2216", "\\setminus"); +defineSymbol(math, main, bin, "\u2227", "\\land"); +defineSymbol(math, main, bin, "\u2228", "\\lor"); +defineSymbol(math, main, bin, "\u2227", "\\wedge", true); +defineSymbol(math, main, bin, "\u2228", "\\vee", true); +defineSymbol(math, main, textord, "\u221A", "\\surd"); +defineSymbol(math, main, symbols_open, "\u27E8", "\\langle", true); +defineSymbol(math, main, symbols_open, "\u2223", "\\lvert"); +defineSymbol(math, main, symbols_open, "\u2225", "\\lVert"); +defineSymbol(math, main, symbols_close, "?", "?"); +defineSymbol(math, main, symbols_close, "!", "!"); +defineSymbol(math, main, symbols_close, "\u27E9", "\\rangle", true); +defineSymbol(math, main, symbols_close, "\u2223", "\\rvert"); +defineSymbol(math, main, symbols_close, "\u2225", "\\rVert"); +defineSymbol(math, main, rel, "=", "="); +defineSymbol(math, main, rel, ":", ":"); +defineSymbol(math, main, rel, "\u2248", "\\approx", true); +defineSymbol(math, main, rel, "\u2245", "\\cong", true); +defineSymbol(math, main, rel, "\u2265", "\\ge"); +defineSymbol(math, main, rel, "\u2265", "\\geq", true); +defineSymbol(math, main, rel, "\u2190", "\\gets"); +defineSymbol(math, main, rel, ">", "\\gt", true); +defineSymbol(math, main, rel, "\u2208", "\\in", true); +defineSymbol(math, main, rel, "\uE020", "\\@not"); +defineSymbol(math, main, rel, "\u2282", "\\subset", true); +defineSymbol(math, main, rel, "\u2283", "\\supset", true); +defineSymbol(math, main, rel, "\u2286", "\\subseteq", true); +defineSymbol(math, main, rel, "\u2287", "\\supseteq", true); +defineSymbol(math, ams, rel, "\u2288", "\\nsubseteq", true); +defineSymbol(math, ams, rel, "\u2289", "\\nsupseteq", true); +defineSymbol(math, main, rel, "\u22A8", "\\models"); +defineSymbol(math, main, rel, "\u2190", "\\leftarrow", true); +defineSymbol(math, main, rel, "\u2264", "\\le"); +defineSymbol(math, main, rel, "\u2264", "\\leq", true); +defineSymbol(math, main, rel, "<", "\\lt", true); +defineSymbol(math, main, rel, "\u2192", "\\rightarrow", true); +defineSymbol(math, main, rel, "\u2192", "\\to"); +defineSymbol(math, ams, rel, "\u2271", "\\ngeq", true); +defineSymbol(math, ams, rel, "\u2270", "\\nleq", true); +defineSymbol(math, main, spacing, "\xA0", "\\ "); +defineSymbol(math, main, spacing, "\xA0", "\\space"); // Ref: LaTeX Source 2e: \DeclareRobustCommand{\nobreakspace}{% + +defineSymbol(math, main, spacing, "\xA0", "\\nobreakspace"); +defineSymbol(symbols_text, main, spacing, "\xA0", "\\ "); +defineSymbol(symbols_text, main, spacing, "\xA0", " "); +defineSymbol(symbols_text, main, spacing, "\xA0", "\\space"); +defineSymbol(symbols_text, main, spacing, "\xA0", "\\nobreakspace"); +defineSymbol(math, main, spacing, null, "\\nobreak"); +defineSymbol(math, main, spacing, null, "\\allowbreak"); +defineSymbol(math, main, punct, ",", ","); +defineSymbol(math, main, punct, ";", ";"); +defineSymbol(math, ams, bin, "\u22BC", "\\barwedge", true); +defineSymbol(math, ams, bin, "\u22BB", "\\veebar", true); +defineSymbol(math, main, bin, "\u2299", "\\odot", true); +defineSymbol(math, main, bin, "\u2295", "\\oplus", true); +defineSymbol(math, main, bin, "\u2297", "\\otimes", true); +defineSymbol(math, main, textord, "\u2202", "\\partial", true); +defineSymbol(math, main, bin, "\u2298", "\\oslash", true); +defineSymbol(math, ams, bin, "\u229A", "\\circledcirc", true); +defineSymbol(math, ams, bin, "\u22A1", "\\boxdot", true); +defineSymbol(math, main, bin, "\u25B3", "\\bigtriangleup"); +defineSymbol(math, main, bin, "\u25BD", "\\bigtriangledown"); +defineSymbol(math, main, bin, "\u2020", "\\dagger"); +defineSymbol(math, main, bin, "\u22C4", "\\diamond"); +defineSymbol(math, main, bin, "\u22C6", "\\star"); +defineSymbol(math, main, bin, "\u25C3", "\\triangleleft"); +defineSymbol(math, main, bin, "\u25B9", "\\triangleright"); +defineSymbol(math, main, symbols_open, "{", "\\{"); +defineSymbol(symbols_text, main, textord, "{", "\\{"); +defineSymbol(symbols_text, main, textord, "{", "\\textbraceleft"); +defineSymbol(math, main, symbols_close, "}", "\\}"); +defineSymbol(symbols_text, main, textord, "}", "\\}"); +defineSymbol(symbols_text, main, textord, "}", "\\textbraceright"); +defineSymbol(math, main, symbols_open, "{", "\\lbrace"); +defineSymbol(math, main, symbols_close, "}", "\\rbrace"); +defineSymbol(math, main, symbols_open, "[", "\\lbrack", true); +defineSymbol(symbols_text, main, textord, "[", "\\lbrack", true); +defineSymbol(math, main, symbols_close, "]", "\\rbrack", true); +defineSymbol(symbols_text, main, textord, "]", "\\rbrack", true); +defineSymbol(math, main, symbols_open, "(", "\\lparen", true); +defineSymbol(math, main, symbols_close, ")", "\\rparen", true); +defineSymbol(symbols_text, main, textord, "<", "\\textless", true); // in T1 fontenc + +defineSymbol(symbols_text, main, textord, ">", "\\textgreater", true); // in T1 fontenc + +defineSymbol(math, main, symbols_open, "\u230A", "\\lfloor", true); +defineSymbol(math, main, symbols_close, "\u230B", "\\rfloor", true); +defineSymbol(math, main, symbols_open, "\u2308", "\\lceil", true); +defineSymbol(math, main, symbols_close, "\u2309", "\\rceil", true); +defineSymbol(math, main, textord, "\\", "\\backslash"); +defineSymbol(math, main, textord, "\u2223", "|"); +defineSymbol(math, main, textord, "\u2223", "\\vert"); +defineSymbol(symbols_text, main, textord, "|", "\\textbar", true); // in T1 fontenc + +defineSymbol(math, main, textord, "\u2225", "\\|"); +defineSymbol(math, main, textord, "\u2225", "\\Vert"); +defineSymbol(symbols_text, main, textord, "\u2225", "\\textbardbl"); +defineSymbol(symbols_text, main, textord, "~", "\\textasciitilde"); +defineSymbol(symbols_text, main, textord, "\\", "\\textbackslash"); +defineSymbol(symbols_text, main, textord, "^", "\\textasciicircum"); +defineSymbol(math, main, rel, "\u2191", "\\uparrow", true); +defineSymbol(math, main, rel, "\u21D1", "\\Uparrow", true); +defineSymbol(math, main, rel, "\u2193", "\\downarrow", true); +defineSymbol(math, main, rel, "\u21D3", "\\Downarrow", true); +defineSymbol(math, main, rel, "\u2195", "\\updownarrow", true); +defineSymbol(math, main, rel, "\u21D5", "\\Updownarrow", true); +defineSymbol(math, main, op, "\u2210", "\\coprod"); +defineSymbol(math, main, op, "\u22C1", "\\bigvee"); +defineSymbol(math, main, op, "\u22C0", "\\bigwedge"); +defineSymbol(math, main, op, "\u2A04", "\\biguplus"); +defineSymbol(math, main, op, "\u22C2", "\\bigcap"); +defineSymbol(math, main, op, "\u22C3", "\\bigcup"); +defineSymbol(math, main, op, "\u222B", "\\int"); +defineSymbol(math, main, op, "\u222B", "\\intop"); +defineSymbol(math, main, op, "\u222C", "\\iint"); +defineSymbol(math, main, op, "\u222D", "\\iiint"); +defineSymbol(math, main, op, "\u220F", "\\prod"); +defineSymbol(math, main, op, "\u2211", "\\sum"); +defineSymbol(math, main, op, "\u2A02", "\\bigotimes"); +defineSymbol(math, main, op, "\u2A01", "\\bigoplus"); +defineSymbol(math, main, op, "\u2A00", "\\bigodot"); +defineSymbol(math, main, op, "\u222E", "\\oint"); +defineSymbol(math, main, op, "\u222F", "\\oiint"); +defineSymbol(math, main, op, "\u2230", "\\oiiint"); +defineSymbol(math, main, op, "\u2A06", "\\bigsqcup"); +defineSymbol(math, main, op, "\u222B", "\\smallint"); +defineSymbol(symbols_text, main, inner, "\u2026", "\\textellipsis"); +defineSymbol(math, main, inner, "\u2026", "\\mathellipsis"); +defineSymbol(symbols_text, main, inner, "\u2026", "\\ldots", true); +defineSymbol(math, main, inner, "\u2026", "\\ldots", true); +defineSymbol(math, main, inner, "\u22EF", "\\@cdots", true); +defineSymbol(math, main, inner, "\u22F1", "\\ddots", true); +defineSymbol(math, main, textord, "\u22EE", "\\varvdots"); // \vdots is a macro + +defineSymbol(math, main, accent, "\u02CA", "\\acute"); +defineSymbol(math, main, accent, "\u02CB", "\\grave"); +defineSymbol(math, main, accent, "\xA8", "\\ddot"); +defineSymbol(math, main, accent, "~", "\\tilde"); +defineSymbol(math, main, accent, "\u02C9", "\\bar"); +defineSymbol(math, main, accent, "\u02D8", "\\breve"); +defineSymbol(math, main, accent, "\u02C7", "\\check"); +defineSymbol(math, main, accent, "^", "\\hat"); +defineSymbol(math, main, accent, "\u20D7", "\\vec"); +defineSymbol(math, main, accent, "\u02D9", "\\dot"); +defineSymbol(math, main, accent, "\u02DA", "\\mathring"); // \imath and \jmath should be invariant to \mathrm, \mathbf, etc., so use PUA + +defineSymbol(math, main, mathord, "\uE131", "\\@imath"); +defineSymbol(math, main, mathord, "\uE237", "\\@jmath"); +defineSymbol(math, main, textord, "\u0131", "\u0131"); +defineSymbol(math, main, textord, "\u0237", "\u0237"); +defineSymbol(symbols_text, main, textord, "\u0131", "\\i", true); +defineSymbol(symbols_text, main, textord, "\u0237", "\\j", true); +defineSymbol(symbols_text, main, textord, "\xDF", "\\ss", true); +defineSymbol(symbols_text, main, textord, "\xE6", "\\ae", true); +defineSymbol(symbols_text, main, textord, "\u0153", "\\oe", true); +defineSymbol(symbols_text, main, textord, "\xF8", "\\o", true); +defineSymbol(symbols_text, main, textord, "\xC6", "\\AE", true); +defineSymbol(symbols_text, main, textord, "\u0152", "\\OE", true); +defineSymbol(symbols_text, main, textord, "\xD8", "\\O", true); +defineSymbol(symbols_text, main, accent, "\u02CA", "\\'"); // acute + +defineSymbol(symbols_text, main, accent, "\u02CB", "\\`"); // grave + +defineSymbol(symbols_text, main, accent, "\u02C6", "\\^"); // circumflex + +defineSymbol(symbols_text, main, accent, "\u02DC", "\\~"); // tilde + +defineSymbol(symbols_text, main, accent, "\u02C9", "\\="); // macron + +defineSymbol(symbols_text, main, accent, "\u02D8", "\\u"); // breve + +defineSymbol(symbols_text, main, accent, "\u02D9", "\\."); // dot above + +defineSymbol(symbols_text, main, accent, "\u02DA", "\\r"); // ring above + +defineSymbol(symbols_text, main, accent, "\u02C7", "\\v"); // caron + +defineSymbol(symbols_text, main, accent, "\xA8", '\\"'); // diaresis + +defineSymbol(symbols_text, main, accent, "\u02DD", "\\H"); // double acute + +defineSymbol(symbols_text, main, accent, "\u25EF", "\\textcircled"); // \bigcirc glyph +// These ligatures are detected and created in Parser.js's `formLigatures`. + +var ligatures = { + "--": true, + "---": true, + "``": true, + "''": true +}; +defineSymbol(symbols_text, main, textord, "\u2013", "--", true); +defineSymbol(symbols_text, main, textord, "\u2013", "\\textendash"); +defineSymbol(symbols_text, main, textord, "\u2014", "---", true); +defineSymbol(symbols_text, main, textord, "\u2014", "\\textemdash"); +defineSymbol(symbols_text, main, textord, "\u2018", "`", true); +defineSymbol(symbols_text, main, textord, "\u2018", "\\textquoteleft"); +defineSymbol(symbols_text, main, textord, "\u2019", "'", true); +defineSymbol(symbols_text, main, textord, "\u2019", "\\textquoteright"); +defineSymbol(symbols_text, main, textord, "\u201C", "``", true); +defineSymbol(symbols_text, main, textord, "\u201C", "\\textquotedblleft"); +defineSymbol(symbols_text, main, textord, "\u201D", "''", true); +defineSymbol(symbols_text, main, textord, "\u201D", "\\textquotedblright"); // \degree from gensymb package + +defineSymbol(math, main, textord, "\xB0", "\\degree", true); +defineSymbol(symbols_text, main, textord, "\xB0", "\\degree"); // \textdegree from inputenc package + +defineSymbol(symbols_text, main, textord, "\xB0", "\\textdegree", true); // TODO: In LaTeX, \pounds can generate a different character in text and math +// mode, but among our fonts, only Main-Regular defines this character "163". + +defineSymbol(math, main, textord, "\xA3", "\\pounds"); +defineSymbol(math, main, textord, "\xA3", "\\mathsterling", true); +defineSymbol(symbols_text, main, textord, "\xA3", "\\pounds"); +defineSymbol(symbols_text, main, textord, "\xA3", "\\textsterling", true); +defineSymbol(math, ams, textord, "\u2720", "\\maltese"); +defineSymbol(symbols_text, ams, textord, "\u2720", "\\maltese"); // There are lots of symbols which are the same, so we add them in afterwards. +// All of these are textords in math mode + +var mathTextSymbols = "0123456789/@.\""; + +for (var i = 0; i < mathTextSymbols.length; i++) { + var ch = mathTextSymbols.charAt(i); + defineSymbol(math, main, textord, ch, ch); +} // All of these are textords in text mode + + +var textSymbols = "0123456789!@*()-=+\";:?/.,"; + +for (var _i = 0; _i < textSymbols.length; _i++) { + var _ch = textSymbols.charAt(_i); + + defineSymbol(symbols_text, main, textord, _ch, _ch); +} // All of these are textords in text mode, and mathords in math mode + + +var letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; + +for (var _i2 = 0; _i2 < letters.length; _i2++) { + var _ch2 = letters.charAt(_i2); + + defineSymbol(math, main, mathord, _ch2, _ch2); + defineSymbol(symbols_text, main, textord, _ch2, _ch2); +} // Blackboard bold and script letters in Unicode range + + +defineSymbol(math, ams, textord, "C", "\u2102"); // blackboard bold + +defineSymbol(symbols_text, ams, textord, "C", "\u2102"); +defineSymbol(math, ams, textord, "H", "\u210D"); +defineSymbol(symbols_text, ams, textord, "H", "\u210D"); +defineSymbol(math, ams, textord, "N", "\u2115"); +defineSymbol(symbols_text, ams, textord, "N", "\u2115"); +defineSymbol(math, ams, textord, "P", "\u2119"); +defineSymbol(symbols_text, ams, textord, "P", "\u2119"); +defineSymbol(math, ams, textord, "Q", "\u211A"); +defineSymbol(symbols_text, ams, textord, "Q", "\u211A"); +defineSymbol(math, ams, textord, "R", "\u211D"); +defineSymbol(symbols_text, ams, textord, "R", "\u211D"); +defineSymbol(math, ams, textord, "Z", "\u2124"); +defineSymbol(symbols_text, ams, textord, "Z", "\u2124"); +defineSymbol(math, main, mathord, "h", "\u210E"); // italic h, Planck constant + +defineSymbol(symbols_text, main, mathord, "h", "\u210E"); // The next loop loads wide (surrogate pair) characters. +// We support some letters in the Unicode range U+1D400 to U+1D7FF, +// Mathematical Alphanumeric Symbols. +// Some editors do not deal well with wide characters. So don't write the +// string into this file. Instead, create the string from the surrogate pair. + +var wideChar = ""; + +for (var _i3 = 0; _i3 < letters.length; _i3++) { + var _ch3 = letters.charAt(_i3); // The hex numbers in the next line are a surrogate pair. + // 0xD835 is the high surrogate for all letters in the range we support. + // 0xDC00 is the low surrogate for bold A. + + + wideChar = String.fromCharCode(0xD835, 0xDC00 + _i3); // A-Z a-z bold + + defineSymbol(math, main, mathord, _ch3, wideChar); + defineSymbol(symbols_text, main, textord, _ch3, wideChar); + wideChar = String.fromCharCode(0xD835, 0xDC34 + _i3); // A-Z a-z italic + + defineSymbol(math, main, mathord, _ch3, wideChar); + defineSymbol(symbols_text, main, textord, _ch3, wideChar); + wideChar = String.fromCharCode(0xD835, 0xDC68 + _i3); // A-Z a-z bold italic + + defineSymbol(math, main, mathord, _ch3, wideChar); + defineSymbol(symbols_text, main, textord, _ch3, wideChar); + wideChar = String.fromCharCode(0xD835, 0xDD04 + _i3); // A-Z a-z Fractur + + defineSymbol(math, main, mathord, _ch3, wideChar); + defineSymbol(symbols_text, main, textord, _ch3, wideChar); + wideChar = String.fromCharCode(0xD835, 0xDDA0 + _i3); // A-Z a-z sans-serif + + defineSymbol(math, main, mathord, _ch3, wideChar); + defineSymbol(symbols_text, main, textord, _ch3, wideChar); + wideChar = String.fromCharCode(0xD835, 0xDDD4 + _i3); // A-Z a-z sans bold + + defineSymbol(math, main, mathord, _ch3, wideChar); + defineSymbol(symbols_text, main, textord, _ch3, wideChar); + wideChar = String.fromCharCode(0xD835, 0xDE08 + _i3); // A-Z a-z sans italic + + defineSymbol(math, main, mathord, _ch3, wideChar); + defineSymbol(symbols_text, main, textord, _ch3, wideChar); + wideChar = String.fromCharCode(0xD835, 0xDE70 + _i3); // A-Z a-z monospace + + defineSymbol(math, main, mathord, _ch3, wideChar); + defineSymbol(symbols_text, main, textord, _ch3, wideChar); + + if (_i3 < 26) { + // KaTeX fonts have only capital letters for blackboard bold and script. + // See exception for k below. + wideChar = String.fromCharCode(0xD835, 0xDD38 + _i3); // A-Z double struck + + defineSymbol(math, main, mathord, _ch3, wideChar); + defineSymbol(symbols_text, main, textord, _ch3, wideChar); + wideChar = String.fromCharCode(0xD835, 0xDC9C + _i3); // A-Z script + + defineSymbol(math, main, mathord, _ch3, wideChar); + defineSymbol(symbols_text, main, textord, _ch3, wideChar); + } // TODO: Add bold script when it is supported by a KaTeX font. + +} // "k" is the only double struck lower case letter in the KaTeX fonts. + + +wideChar = String.fromCharCode(0xD835, 0xDD5C); // k double struck + +defineSymbol(math, main, mathord, "k", wideChar); +defineSymbol(symbols_text, main, textord, "k", wideChar); // Next, some wide character numerals + +for (var _i4 = 0; _i4 < 10; _i4++) { + var _ch4 = _i4.toString(); + + wideChar = String.fromCharCode(0xD835, 0xDFCE + _i4); // 0-9 bold + + defineSymbol(math, main, mathord, _ch4, wideChar); + defineSymbol(symbols_text, main, textord, _ch4, wideChar); + wideChar = String.fromCharCode(0xD835, 0xDFE2 + _i4); // 0-9 sans serif + + defineSymbol(math, main, mathord, _ch4, wideChar); + defineSymbol(symbols_text, main, textord, _ch4, wideChar); + wideChar = String.fromCharCode(0xD835, 0xDFEC + _i4); // 0-9 bold sans + + defineSymbol(math, main, mathord, _ch4, wideChar); + defineSymbol(symbols_text, main, textord, _ch4, wideChar); + wideChar = String.fromCharCode(0xD835, 0xDFF6 + _i4); // 0-9 monospace + + defineSymbol(math, main, mathord, _ch4, wideChar); + defineSymbol(symbols_text, main, textord, _ch4, wideChar); +} // We add these Latin-1 letters as symbols for backwards-compatibility, +// but they are not actually in the font, nor are they supported by the +// Unicode accent mechanism, so they fall back to Times font and look ugly. +// TODO(edemaine): Fix this. + + +var extraLatin = "\xC7\xD0\xDE\xE7\xFE"; + +for (var _i5 = 0; _i5 < extraLatin.length; _i5++) { + var _ch5 = extraLatin.charAt(_i5); + + defineSymbol(math, main, mathord, _ch5, _ch5); + defineSymbol(symbols_text, main, textord, _ch5, _ch5); +} +;// CONCATENATED MODULE: ./src/wide-character.js +/** + * This file provides support for Unicode range U+1D400 to U+1D7FF, + * Mathematical Alphanumeric Symbols. + * + * Function wideCharacterFont takes a wide character as input and returns + * the font information necessary to render it properly. + */ + +/** + * Data below is from https://www.unicode.org/charts/PDF/U1D400.pdf + * That document sorts characters into groups by font type, say bold or italic. + * + * In the arrays below, each subarray consists three elements: + * * The CSS class of that group when in math mode. + * * The CSS class of that group when in text mode. + * * The font name, so that KaTeX can get font metrics. + */ + +var wideLatinLetterData = [["mathbf", "textbf", "Main-Bold"], // A-Z bold upright +["mathbf", "textbf", "Main-Bold"], // a-z bold upright +["mathnormal", "textit", "Math-Italic"], // A-Z italic +["mathnormal", "textit", "Math-Italic"], // a-z italic +["boldsymbol", "boldsymbol", "Main-BoldItalic"], // A-Z bold italic +["boldsymbol", "boldsymbol", "Main-BoldItalic"], // a-z bold italic +// Map fancy A-Z letters to script, not calligraphic. +// This aligns with unicode-math and math fonts (except Cambria Math). +["mathscr", "textscr", "Script-Regular"], // A-Z script +["", "", ""], // a-z script. No font +["", "", ""], // A-Z bold script. No font +["", "", ""], // a-z bold script. No font +["mathfrak", "textfrak", "Fraktur-Regular"], // A-Z Fraktur +["mathfrak", "textfrak", "Fraktur-Regular"], // a-z Fraktur +["mathbb", "textbb", "AMS-Regular"], // A-Z double-struck +["mathbb", "textbb", "AMS-Regular"], // k double-struck +["", "", ""], // A-Z bold Fraktur No font metrics +["", "", ""], // a-z bold Fraktur. No font. +["mathsf", "textsf", "SansSerif-Regular"], // A-Z sans-serif +["mathsf", "textsf", "SansSerif-Regular"], // a-z sans-serif +["mathboldsf", "textboldsf", "SansSerif-Bold"], // A-Z bold sans-serif +["mathboldsf", "textboldsf", "SansSerif-Bold"], // a-z bold sans-serif +["mathitsf", "textitsf", "SansSerif-Italic"], // A-Z italic sans-serif +["mathitsf", "textitsf", "SansSerif-Italic"], // a-z italic sans-serif +["", "", ""], // A-Z bold italic sans. No font +["", "", ""], // a-z bold italic sans. No font +["mathtt", "texttt", "Typewriter-Regular"], // A-Z monospace +["mathtt", "texttt", "Typewriter-Regular"] // a-z monospace +]; +var wideNumeralData = [["mathbf", "textbf", "Main-Bold"], // 0-9 bold +["", "", ""], // 0-9 double-struck. No KaTeX font. +["mathsf", "textsf", "SansSerif-Regular"], // 0-9 sans-serif +["mathboldsf", "textboldsf", "SansSerif-Bold"], // 0-9 bold sans-serif +["mathtt", "texttt", "Typewriter-Regular"] // 0-9 monospace +]; +var wideCharacterFont = function wideCharacterFont(wideChar, mode) { + // IE doesn't support codePointAt(). So work with the surrogate pair. + var H = wideChar.charCodeAt(0); // high surrogate + + var L = wideChar.charCodeAt(1); // low surrogate + + var codePoint = (H - 0xD800) * 0x400 + (L - 0xDC00) + 0x10000; + var j = mode === "math" ? 0 : 1; // column index for CSS class. + + if (0x1D400 <= codePoint && codePoint < 0x1D6A4) { + // wideLatinLetterData contains exactly 26 chars on each row. + // So we can calculate the relevant row. No traverse necessary. + var i = Math.floor((codePoint - 0x1D400) / 26); + return [wideLatinLetterData[i][2], wideLatinLetterData[i][j]]; + } else if (0x1D7CE <= codePoint && codePoint <= 0x1D7FF) { + // Numerals, ten per row. + var _i = Math.floor((codePoint - 0x1D7CE) / 10); + + return [wideNumeralData[_i][2], wideNumeralData[_i][j]]; + } else if (codePoint === 0x1D6A5 || codePoint === 0x1D6A6) { + // dotless i or j + return [wideLatinLetterData[0][2], wideLatinLetterData[0][j]]; + } else if (0x1D6A6 < codePoint && codePoint < 0x1D7CE) { + // Greek letters. Not supported, yet. + return ["", ""]; + } else { + // We don't support any wide characters outside 1D400–1D7FF. + throw new src_ParseError("Unsupported character: " + wideChar); + } +}; +;// CONCATENATED MODULE: ./src/Options.js +/** + * This file contains information about the options that the Parser carries + * around with it while parsing. Data is held in an `Options` object, and when + * recursing, a new `Options` object can be created with the `.with*` and + * `.reset` functions. + */ + +var sizeStyleMap = [// Each element contains [textsize, scriptsize, scriptscriptsize]. +// The size mappings are taken from TeX with \normalsize=10pt. +[1, 1, 1], // size1: [5, 5, 5] \tiny +[2, 1, 1], // size2: [6, 5, 5] +[3, 1, 1], // size3: [7, 5, 5] \scriptsize +[4, 2, 1], // size4: [8, 6, 5] \footnotesize +[5, 2, 1], // size5: [9, 6, 5] \small +[6, 3, 1], // size6: [10, 7, 5] \normalsize +[7, 4, 2], // size7: [12, 8, 6] \large +[8, 6, 3], // size8: [14.4, 10, 7] \Large +[9, 7, 6], // size9: [17.28, 12, 10] \LARGE +[10, 8, 7], // size10: [20.74, 14.4, 12] \huge +[11, 10, 9] // size11: [24.88, 20.74, 17.28] \HUGE +]; +var sizeMultipliers = [// fontMetrics.js:getGlobalMetrics also uses size indexes, so if +// you change size indexes, change that function. +0.5, 0.6, 0.7, 0.8, 0.9, 1.0, 1.2, 1.44, 1.728, 2.074, 2.488]; + +var sizeAtStyle = function sizeAtStyle(size, style) { + return style.size < 2 ? size : sizeStyleMap[size - 1][style.size - 1]; +}; // In these types, "" (empty string) means "no change". + + +/** + * This is the main options class. It contains the current style, size, color, + * and font. + * + * Options objects should not be modified. To create a new Options with + * different properties, call a `.having*` method. + */ +var Options = /*#__PURE__*/function () { + // A font family applies to a group of fonts (i.e. SansSerif), while a font + // represents a specific font (i.e. SansSerif Bold). + // See: https://tex.stackexchange.com/questions/22350/difference-between-textrm-and-mathrm + + /** + * The base size index. + */ + function Options(data) { + this.style = void 0; + this.color = void 0; + this.size = void 0; + this.textSize = void 0; + this.phantom = void 0; + this.font = void 0; + this.fontFamily = void 0; + this.fontWeight = void 0; + this.fontShape = void 0; + this.sizeMultiplier = void 0; + this.maxSize = void 0; + this.minRuleThickness = void 0; + this._fontMetrics = void 0; + this.style = data.style; + this.color = data.color; + this.size = data.size || Options.BASESIZE; + this.textSize = data.textSize || this.size; + this.phantom = !!data.phantom; + this.font = data.font || ""; + this.fontFamily = data.fontFamily || ""; + this.fontWeight = data.fontWeight || ''; + this.fontShape = data.fontShape || ''; + this.sizeMultiplier = sizeMultipliers[this.size - 1]; + this.maxSize = data.maxSize; + this.minRuleThickness = data.minRuleThickness; + this._fontMetrics = undefined; + } + /** + * Returns a new options object with the same properties as "this". Properties + * from "extension" will be copied to the new options object. + */ + + + var _proto = Options.prototype; + + _proto.extend = function extend(extension) { + var data = { + style: this.style, + size: this.size, + textSize: this.textSize, + color: this.color, + phantom: this.phantom, + font: this.font, + fontFamily: this.fontFamily, + fontWeight: this.fontWeight, + fontShape: this.fontShape, + maxSize: this.maxSize, + minRuleThickness: this.minRuleThickness + }; + + for (var key in extension) { + if (extension.hasOwnProperty(key)) { + data[key] = extension[key]; + } + } + + return new Options(data); + } + /** + * Return an options object with the given style. If `this.style === style`, + * returns `this`. + */ + ; + + _proto.havingStyle = function havingStyle(style) { + if (this.style === style) { + return this; + } else { + return this.extend({ + style: style, + size: sizeAtStyle(this.textSize, style) + }); + } + } + /** + * Return an options object with a cramped version of the current style. If + * the current style is cramped, returns `this`. + */ + ; + + _proto.havingCrampedStyle = function havingCrampedStyle() { + return this.havingStyle(this.style.cramp()); + } + /** + * Return an options object with the given size and in at least `\textstyle`. + * Returns `this` if appropriate. + */ + ; + + _proto.havingSize = function havingSize(size) { + if (this.size === size && this.textSize === size) { + return this; + } else { + return this.extend({ + style: this.style.text(), + size: size, + textSize: size, + sizeMultiplier: sizeMultipliers[size - 1] + }); + } + } + /** + * Like `this.havingSize(BASESIZE).havingStyle(style)`. If `style` is omitted, + * changes to at least `\textstyle`. + */ + ; + + _proto.havingBaseStyle = function havingBaseStyle(style) { + style = style || this.style.text(); + var wantSize = sizeAtStyle(Options.BASESIZE, style); + + if (this.size === wantSize && this.textSize === Options.BASESIZE && this.style === style) { + return this; + } else { + return this.extend({ + style: style, + size: wantSize + }); + } + } + /** + * Remove the effect of sizing changes such as \Huge. + * Keep the effect of the current style, such as \scriptstyle. + */ + ; + + _proto.havingBaseSizing = function havingBaseSizing() { + var size; + + switch (this.style.id) { + case 4: + case 5: + size = 3; // normalsize in scriptstyle + + break; + + case 6: + case 7: + size = 1; // normalsize in scriptscriptstyle + + break; + + default: + size = 6; + // normalsize in textstyle or displaystyle + } + + return this.extend({ + style: this.style.text(), + size: size + }); + } + /** + * Create a new options object with the given color. + */ + ; + + _proto.withColor = function withColor(color) { + return this.extend({ + color: color + }); + } + /** + * Create a new options object with "phantom" set to true. + */ + ; + + _proto.withPhantom = function withPhantom() { + return this.extend({ + phantom: true + }); + } + /** + * Creates a new options object with the given math font or old text font. + * @type {[type]} + */ + ; + + _proto.withFont = function withFont(font) { + return this.extend({ + font: font + }); + } + /** + * Create a new options objects with the given fontFamily. + */ + ; + + _proto.withTextFontFamily = function withTextFontFamily(fontFamily) { + return this.extend({ + fontFamily: fontFamily, + font: "" + }); + } + /** + * Creates a new options object with the given font weight + */ + ; + + _proto.withTextFontWeight = function withTextFontWeight(fontWeight) { + return this.extend({ + fontWeight: fontWeight, + font: "" + }); + } + /** + * Creates a new options object with the given font weight + */ + ; + + _proto.withTextFontShape = function withTextFontShape(fontShape) { + return this.extend({ + fontShape: fontShape, + font: "" + }); + } + /** + * Return the CSS sizing classes required to switch from enclosing options + * `oldOptions` to `this`. Returns an array of classes. + */ + ; + + _proto.sizingClasses = function sizingClasses(oldOptions) { + if (oldOptions.size !== this.size) { + return ["sizing", "reset-size" + oldOptions.size, "size" + this.size]; + } else { + return []; + } + } + /** + * Return the CSS sizing classes required to switch to the base size. Like + * `this.havingSize(BASESIZE).sizingClasses(this)`. + */ + ; + + _proto.baseSizingClasses = function baseSizingClasses() { + if (this.size !== Options.BASESIZE) { + return ["sizing", "reset-size" + this.size, "size" + Options.BASESIZE]; + } else { + return []; + } + } + /** + * Return the font metrics for this size. + */ + ; + + _proto.fontMetrics = function fontMetrics() { + if (!this._fontMetrics) { + this._fontMetrics = getGlobalMetrics(this.size); + } + + return this._fontMetrics; + } + /** + * Gets the CSS color of the current options object + */ + ; + + _proto.getColor = function getColor() { + if (this.phantom) { + return "transparent"; + } else { + return this.color; + } + }; + + return Options; +}(); + +Options.BASESIZE = 6; +/* harmony default export */ var src_Options = (Options); +;// CONCATENATED MODULE: ./src/units.js +/** + * This file does conversion between units. In particular, it provides + * calculateSize to convert other units into ems. + */ + + // This table gives the number of TeX pts in one of each *absolute* TeX unit. +// Thus, multiplying a length by this number converts the length from units +// into pts. Dividing the result by ptPerEm gives the number of ems +// *assuming* a font size of ptPerEm (normal size, normal style). + +var ptPerUnit = { + // https://en.wikibooks.org/wiki/LaTeX/Lengths and + // https://tex.stackexchange.com/a/8263 + "pt": 1, + // TeX point + "mm": 7227 / 2540, + // millimeter + "cm": 7227 / 254, + // centimeter + "in": 72.27, + // inch + "bp": 803 / 800, + // big (PostScript) points + "pc": 12, + // pica + "dd": 1238 / 1157, + // didot + "cc": 14856 / 1157, + // cicero (12 didot) + "nd": 685 / 642, + // new didot + "nc": 1370 / 107, + // new cicero (12 new didot) + "sp": 1 / 65536, + // scaled point (TeX's internal smallest unit) + // https://tex.stackexchange.com/a/41371 + "px": 803 / 800 // \pdfpxdimen defaults to 1 bp in pdfTeX and LuaTeX + +}; // Dictionary of relative units, for fast validity testing. + +var relativeUnit = { + "ex": true, + "em": true, + "mu": true +}; + +/** + * Determine whether the specified unit (either a string defining the unit + * or a "size" parse node containing a unit field) is valid. + */ +var validUnit = function validUnit(unit) { + if (typeof unit !== "string") { + unit = unit.unit; + } + + return unit in ptPerUnit || unit in relativeUnit || unit === "ex"; +}; +/* + * Convert a "size" parse node (with numeric "number" and string "unit" fields, + * as parsed by functions.js argType "size") into a CSS em value for the + * current style/scale. `options` gives the current options. + */ + +var calculateSize = function calculateSize(sizeValue, options) { + var scale; + + if (sizeValue.unit in ptPerUnit) { + // Absolute units + scale = ptPerUnit[sizeValue.unit] // Convert unit to pt + / options.fontMetrics().ptPerEm // Convert pt to CSS em + / options.sizeMultiplier; // Unscale to make absolute units + } else if (sizeValue.unit === "mu") { + // `mu` units scale with scriptstyle/scriptscriptstyle. + scale = options.fontMetrics().cssEmPerMu; + } else { + // Other relative units always refer to the *textstyle* font + // in the current size. + var unitOptions; + + if (options.style.isTight()) { + // isTight() means current style is script/scriptscript. + unitOptions = options.havingStyle(options.style.text()); + } else { + unitOptions = options; + } // TODO: In TeX these units are relative to the quad of the current + // *text* font, e.g. cmr10. KaTeX instead uses values from the + // comparably-sized *Computer Modern symbol* font. At 10pt, these + // match. At 7pt and 5pt, they differ: cmr7=1.138894, cmsy7=1.170641; + // cmr5=1.361133, cmsy5=1.472241. Consider $\scriptsize a\kern1emb$. + // TeX \showlists shows a kern of 1.13889 * fontsize; + // KaTeX shows a kern of 1.171 * fontsize. + + + if (sizeValue.unit === "ex") { + scale = unitOptions.fontMetrics().xHeight; + } else if (sizeValue.unit === "em") { + scale = unitOptions.fontMetrics().quad; + } else { + throw new src_ParseError("Invalid unit: '" + sizeValue.unit + "'"); + } + + if (unitOptions !== options) { + scale *= unitOptions.sizeMultiplier / options.sizeMultiplier; + } + } + + return Math.min(sizeValue.number * scale, options.maxSize); +}; +;// CONCATENATED MODULE: ./src/buildCommon.js +/* eslint no-console:0 */ + +/** + * This module contains general functions that can be used for building + * different kinds of domTree nodes in a consistent manner. + */ + + + + + + + +/** + * Looks up the given symbol in fontMetrics, after applying any symbol + * replacements defined in symbol.js + */ +var lookupSymbol = function lookupSymbol(value, // TODO(#963): Use a union type for this. +fontName, mode) { + // Replace the value with its replaced value from symbol.js + if (src_symbols[mode][value] && src_symbols[mode][value].replace) { + value = src_symbols[mode][value].replace; + } + + return { + value: value, + metrics: getCharacterMetrics(value, fontName, mode) + }; +}; +/** + * Makes a symbolNode after translation via the list of symbols in symbols.js. + * Correctly pulls out metrics for the character, and optionally takes a list of + * classes to be attached to the node. + * + * TODO: make argument order closer to makeSpan + * TODO: add a separate argument for math class (e.g. `mop`, `mbin`), which + * should if present come first in `classes`. + * TODO(#953): Make `options` mandatory and always pass it in. + */ + + +var makeSymbol = function makeSymbol(value, fontName, mode, options, classes) { + var lookup = lookupSymbol(value, fontName, mode); + var metrics = lookup.metrics; + value = lookup.value; + var symbolNode; + + if (metrics) { + var italic = metrics.italic; + + if (mode === "text" || options && options.font === "mathit") { + italic = 0; + } + + symbolNode = new SymbolNode(value, metrics.height, metrics.depth, italic, metrics.skew, metrics.width, classes); + } else { + // TODO(emily): Figure out a good way to only print this in development + typeof console !== "undefined" && console.warn("No character metrics " + ("for '" + value + "' in style '" + fontName + "' and mode '" + mode + "'")); + symbolNode = new SymbolNode(value, 0, 0, 0, 0, 0, classes); + } + + if (options) { + symbolNode.maxFontSize = options.sizeMultiplier; + + if (options.style.isTight()) { + symbolNode.classes.push("mtight"); + } + + var color = options.getColor(); + + if (color) { + symbolNode.style.color = color; + } + } + + return symbolNode; +}; +/** + * Makes a symbol in Main-Regular or AMS-Regular. + * Used for rel, bin, open, close, inner, and punct. + */ + + +var mathsym = function mathsym(value, mode, options, classes) { + if (classes === void 0) { + classes = []; + } + + // Decide what font to render the symbol in by its entry in the symbols + // table. + // Have a special case for when the value = \ because the \ is used as a + // textord in unsupported command errors but cannot be parsed as a regular + // text ordinal and is therefore not present as a symbol in the symbols + // table for text, as well as a special case for boldsymbol because it + // can be used for bold + and - + if (options.font === "boldsymbol" && lookupSymbol(value, "Main-Bold", mode).metrics) { + return makeSymbol(value, "Main-Bold", mode, options, classes.concat(["mathbf"])); + } else if (value === "\\" || src_symbols[mode][value].font === "main") { + return makeSymbol(value, "Main-Regular", mode, options, classes); + } else { + return makeSymbol(value, "AMS-Regular", mode, options, classes.concat(["amsrm"])); + } +}; +/** + * Determines which of the two font names (Main-Bold and Math-BoldItalic) and + * corresponding style tags (mathbf or boldsymbol) to use for font "boldsymbol", + * depending on the symbol. Use this function instead of fontMap for font + * "boldsymbol". + */ + + +var boldsymbol = function boldsymbol(value, mode, options, classes, type) { + if (type !== "textord" && lookupSymbol(value, "Math-BoldItalic", mode).metrics) { + return { + fontName: "Math-BoldItalic", + fontClass: "boldsymbol" + }; + } else { + // Some glyphs do not exist in Math-BoldItalic so we need to use + // Main-Bold instead. + return { + fontName: "Main-Bold", + fontClass: "mathbf" + }; + } +}; +/** + * Makes either a mathord or textord in the correct font and color. + */ + + +var makeOrd = function makeOrd(group, options, type) { + var mode = group.mode; + var text = group.text; + var classes = ["mord"]; // Math mode or Old font (i.e. \rm) + + var isFont = mode === "math" || mode === "text" && options.font; + var fontOrFamily = isFont ? options.font : options.fontFamily; + + if (text.charCodeAt(0) === 0xD835) { + // surrogate pairs get special treatment + var _wideCharacterFont = wideCharacterFont(text, mode), + wideFontName = _wideCharacterFont[0], + wideFontClass = _wideCharacterFont[1]; + + return makeSymbol(text, wideFontName, mode, options, classes.concat(wideFontClass)); + } else if (fontOrFamily) { + var fontName; + var fontClasses; + + if (fontOrFamily === "boldsymbol") { + var fontData = boldsymbol(text, mode, options, classes, type); + fontName = fontData.fontName; + fontClasses = [fontData.fontClass]; + } else if (isFont) { + fontName = fontMap[fontOrFamily].fontName; + fontClasses = [fontOrFamily]; + } else { + fontName = retrieveTextFontName(fontOrFamily, options.fontWeight, options.fontShape); + fontClasses = [fontOrFamily, options.fontWeight, options.fontShape]; + } + + if (lookupSymbol(text, fontName, mode).metrics) { + return makeSymbol(text, fontName, mode, options, classes.concat(fontClasses)); + } else if (ligatures.hasOwnProperty(text) && fontName.substr(0, 10) === "Typewriter") { + // Deconstruct ligatures in monospace fonts (\texttt, \tt). + var parts = []; + + for (var i = 0; i < text.length; i++) { + parts.push(makeSymbol(text[i], fontName, mode, options, classes.concat(fontClasses))); + } + + return makeFragment(parts); + } + } // Makes a symbol in the default font for mathords and textords. + + + if (type === "mathord") { + return makeSymbol(text, "Math-Italic", mode, options, classes.concat(["mathnormal"])); + } else if (type === "textord") { + var font = src_symbols[mode][text] && src_symbols[mode][text].font; + + if (font === "ams") { + var _fontName = retrieveTextFontName("amsrm", options.fontWeight, options.fontShape); + + return makeSymbol(text, _fontName, mode, options, classes.concat("amsrm", options.fontWeight, options.fontShape)); + } else if (font === "main" || !font) { + var _fontName2 = retrieveTextFontName("textrm", options.fontWeight, options.fontShape); + + return makeSymbol(text, _fontName2, mode, options, classes.concat(options.fontWeight, options.fontShape)); + } else { + // fonts added by plugins + var _fontName3 = retrieveTextFontName(font, options.fontWeight, options.fontShape); // We add font name as a css class + + + return makeSymbol(text, _fontName3, mode, options, classes.concat(_fontName3, options.fontWeight, options.fontShape)); + } + } else { + throw new Error("unexpected type: " + type + " in makeOrd"); + } +}; +/** + * Returns true if subsequent symbolNodes have the same classes, skew, maxFont, + * and styles. + */ + + +var canCombine = function canCombine(prev, next) { + if (createClass(prev.classes) !== createClass(next.classes) || prev.skew !== next.skew || prev.maxFontSize !== next.maxFontSize) { + return false; + } // If prev and next both are just "mbin"s or "mord"s we don't combine them + // so that the proper spacing can be preserved. + + + if (prev.classes.length === 1) { + var cls = prev.classes[0]; + + if (cls === "mbin" || cls === "mord") { + return false; + } + } + + for (var style in prev.style) { + if (prev.style.hasOwnProperty(style) && prev.style[style] !== next.style[style]) { + return false; + } + } + + for (var _style in next.style) { + if (next.style.hasOwnProperty(_style) && prev.style[_style] !== next.style[_style]) { + return false; + } + } + + return true; +}; +/** + * Combine consecutive domTree.symbolNodes into a single symbolNode. + * Note: this function mutates the argument. + */ + + +var tryCombineChars = function tryCombineChars(chars) { + for (var i = 0; i < chars.length - 1; i++) { + var prev = chars[i]; + var next = chars[i + 1]; + + if (prev instanceof SymbolNode && next instanceof SymbolNode && canCombine(prev, next)) { + prev.text += next.text; + prev.height = Math.max(prev.height, next.height); + prev.depth = Math.max(prev.depth, next.depth); // Use the last character's italic correction since we use + // it to add padding to the right of the span created from + // the combined characters. + + prev.italic = next.italic; + chars.splice(i + 1, 1); + i--; + } + } + + return chars; +}; +/** + * Calculate the height, depth, and maxFontSize of an element based on its + * children. + */ + + +var sizeElementFromChildren = function sizeElementFromChildren(elem) { + var height = 0; + var depth = 0; + var maxFontSize = 0; + + for (var i = 0; i < elem.children.length; i++) { + var child = elem.children[i]; + + if (child.height > height) { + height = child.height; + } + + if (child.depth > depth) { + depth = child.depth; + } + + if (child.maxFontSize > maxFontSize) { + maxFontSize = child.maxFontSize; + } + } + + elem.height = height; + elem.depth = depth; + elem.maxFontSize = maxFontSize; +}; +/** + * Makes a span with the given list of classes, list of children, and options. + * + * TODO(#953): Ensure that `options` is always provided (currently some call + * sites don't pass it) and make the type below mandatory. + * TODO: add a separate argument for math class (e.g. `mop`, `mbin`), which + * should if present come first in `classes`. + */ + + +var makeSpan = function makeSpan(classes, children, options, style) { + var span = new Span(classes, children, options, style); + sizeElementFromChildren(span); + return span; +}; // SVG one is simpler -- doesn't require height, depth, max-font setting. +// This is also a separate method for typesafety. + + +var makeSvgSpan = function makeSvgSpan(classes, children, options, style) { + return new Span(classes, children, options, style); +}; + +var makeLineSpan = function makeLineSpan(className, options, thickness) { + var line = makeSpan([className], [], options); + line.height = Math.max(thickness || options.fontMetrics().defaultRuleThickness, options.minRuleThickness); + line.style.borderBottomWidth = line.height + "em"; + line.maxFontSize = 1.0; + return line; +}; +/** + * Makes an anchor with the given href, list of classes, list of children, + * and options. + */ + + +var makeAnchor = function makeAnchor(href, classes, children, options) { + var anchor = new Anchor(href, classes, children, options); + sizeElementFromChildren(anchor); + return anchor; +}; +/** + * Makes a document fragment with the given list of children. + */ + + +var makeFragment = function makeFragment(children) { + var fragment = new DocumentFragment(children); + sizeElementFromChildren(fragment); + return fragment; +}; +/** + * Wraps group in a span if it's a document fragment, allowing to apply classes + * and styles + */ + + +var wrapFragment = function wrapFragment(group, options) { + if (group instanceof DocumentFragment) { + return makeSpan([], [group], options); + } + + return group; +}; // These are exact object types to catch typos in the names of the optional fields. + + +// Computes the updated `children` list and the overall depth. +// +// This helper function for makeVList makes it easier to enforce type safety by +// allowing early exits (returns) in the logic. +var getVListChildrenAndDepth = function getVListChildrenAndDepth(params) { + if (params.positionType === "individualShift") { + var oldChildren = params.children; + var children = [oldChildren[0]]; // Add in kerns to the list of params.children to get each element to be + // shifted to the correct specified shift + + var _depth = -oldChildren[0].shift - oldChildren[0].elem.depth; + + var currPos = _depth; + + for (var i = 1; i < oldChildren.length; i++) { + var diff = -oldChildren[i].shift - currPos - oldChildren[i].elem.depth; + var size = diff - (oldChildren[i - 1].elem.height + oldChildren[i - 1].elem.depth); + currPos = currPos + diff; + children.push({ + type: "kern", + size: size + }); + children.push(oldChildren[i]); + } + + return { + children: children, + depth: _depth + }; + } + + var depth; + + if (params.positionType === "top") { + // We always start at the bottom, so calculate the bottom by adding up + // all the sizes + var bottom = params.positionData; + + for (var _i = 0; _i < params.children.length; _i++) { + var child = params.children[_i]; + bottom -= child.type === "kern" ? child.size : child.elem.height + child.elem.depth; + } + + depth = bottom; + } else if (params.positionType === "bottom") { + depth = -params.positionData; + } else { + var firstChild = params.children[0]; + + if (firstChild.type !== "elem") { + throw new Error('First child must have type "elem".'); + } + + if (params.positionType === "shift") { + depth = -firstChild.elem.depth - params.positionData; + } else if (params.positionType === "firstBaseline") { + depth = -firstChild.elem.depth; + } else { + throw new Error("Invalid positionType " + params.positionType + "."); + } + } + + return { + children: params.children, + depth: depth + }; +}; +/** + * Makes a vertical list by stacking elements and kerns on top of each other. + * Allows for many different ways of specifying the positioning method. + * + * See VListParam documentation above. + */ + + +var makeVList = function makeVList(params, options) { + var _getVListChildrenAndD = getVListChildrenAndDepth(params), + children = _getVListChildrenAndD.children, + depth = _getVListChildrenAndD.depth; // Create a strut that is taller than any list item. The strut is added to + // each item, where it will determine the item's baseline. Since it has + // `overflow:hidden`, the strut's top edge will sit on the item's line box's + // top edge and the strut's bottom edge will sit on the item's baseline, + // with no additional line-height spacing. This allows the item baseline to + // be positioned precisely without worrying about font ascent and + // line-height. + + + var pstrutSize = 0; + + for (var i = 0; i < children.length; i++) { + var child = children[i]; + + if (child.type === "elem") { + var elem = child.elem; + pstrutSize = Math.max(pstrutSize, elem.maxFontSize, elem.height); + } + } + + pstrutSize += 2; + var pstrut = makeSpan(["pstrut"], []); + pstrut.style.height = pstrutSize + "em"; // Create a new list of actual children at the correct offsets + + var realChildren = []; + var minPos = depth; + var maxPos = depth; + var currPos = depth; + + for (var _i2 = 0; _i2 < children.length; _i2++) { + var _child = children[_i2]; + + if (_child.type === "kern") { + currPos += _child.size; + } else { + var _elem = _child.elem; + var classes = _child.wrapperClasses || []; + var style = _child.wrapperStyle || {}; + var childWrap = makeSpan(classes, [pstrut, _elem], undefined, style); + childWrap.style.top = -pstrutSize - currPos - _elem.depth + "em"; + + if (_child.marginLeft) { + childWrap.style.marginLeft = _child.marginLeft; + } + + if (_child.marginRight) { + childWrap.style.marginRight = _child.marginRight; + } + + realChildren.push(childWrap); + currPos += _elem.height + _elem.depth; + } + + minPos = Math.min(minPos, currPos); + maxPos = Math.max(maxPos, currPos); + } // The vlist contents go in a table-cell with `vertical-align:bottom`. + // This cell's bottom edge will determine the containing table's baseline + // without overly expanding the containing line-box. + + + var vlist = makeSpan(["vlist"], realChildren); + vlist.style.height = maxPos + "em"; // A second row is used if necessary to represent the vlist's depth. + + var rows; + + if (minPos < 0) { + // We will define depth in an empty span with display: table-cell. + // It should render with the height that we define. But Chrome, in + // contenteditable mode only, treats that span as if it contains some + // text content. And that min-height over-rides our desired height. + // So we put another empty span inside the depth strut span. + var emptySpan = makeSpan([], []); + var depthStrut = makeSpan(["vlist"], [emptySpan]); + depthStrut.style.height = -minPos + "em"; // Safari wants the first row to have inline content; otherwise it + // puts the bottom of the *second* row on the baseline. + + var topStrut = makeSpan(["vlist-s"], [new SymbolNode("\u200B")]); + rows = [makeSpan(["vlist-r"], [vlist, topStrut]), makeSpan(["vlist-r"], [depthStrut])]; + } else { + rows = [makeSpan(["vlist-r"], [vlist])]; + } + + var vtable = makeSpan(["vlist-t"], rows); + + if (rows.length === 2) { + vtable.classes.push("vlist-t2"); + } + + vtable.height = maxPos; + vtable.depth = -minPos; + return vtable; +}; // Glue is a concept from TeX which is a flexible space between elements in +// either a vertical or horizontal list. In KaTeX, at least for now, it's +// static space between elements in a horizontal layout. + + +var makeGlue = function makeGlue(measurement, options) { + // Make an empty span for the space + var rule = makeSpan(["mspace"], [], options); + var size = calculateSize(measurement, options); + rule.style.marginRight = size + "em"; + return rule; +}; // Takes font options, and returns the appropriate fontLookup name + + +var retrieveTextFontName = function retrieveTextFontName(fontFamily, fontWeight, fontShape) { + var baseFontName = ""; + + switch (fontFamily) { + case "amsrm": + baseFontName = "AMS"; + break; + + case "textrm": + baseFontName = "Main"; + break; + + case "textsf": + baseFontName = "SansSerif"; + break; + + case "texttt": + baseFontName = "Typewriter"; + break; + + default: + baseFontName = fontFamily; + // use fonts added by a plugin + } + + var fontStylesName; + + if (fontWeight === "textbf" && fontShape === "textit") { + fontStylesName = "BoldItalic"; + } else if (fontWeight === "textbf") { + fontStylesName = "Bold"; + } else if (fontWeight === "textit") { + fontStylesName = "Italic"; + } else { + fontStylesName = "Regular"; + } + + return baseFontName + "-" + fontStylesName; +}; +/** + * Maps TeX font commands to objects containing: + * - variant: string used for "mathvariant" attribute in buildMathML.js + * - fontName: the "style" parameter to fontMetrics.getCharacterMetrics + */ +// A map between tex font commands an MathML mathvariant attribute values + + +var fontMap = { + // styles + "mathbf": { + variant: "bold", + fontName: "Main-Bold" + }, + "mathrm": { + variant: "normal", + fontName: "Main-Regular" + }, + "textit": { + variant: "italic", + fontName: "Main-Italic" + }, + "mathit": { + variant: "italic", + fontName: "Main-Italic" + }, + "mathnormal": { + variant: "italic", + fontName: "Math-Italic" + }, + // "boldsymbol" is missing because they require the use of multiple fonts: + // Math-BoldItalic and Main-Bold. This is handled by a special case in + // makeOrd which ends up calling boldsymbol. + // families + "mathbb": { + variant: "double-struck", + fontName: "AMS-Regular" + }, + "mathcal": { + variant: "script", + fontName: "Caligraphic-Regular" + }, + "mathfrak": { + variant: "fraktur", + fontName: "Fraktur-Regular" + }, + "mathscr": { + variant: "script", + fontName: "Script-Regular" + }, + "mathsf": { + variant: "sans-serif", + fontName: "SansSerif-Regular" + }, + "mathtt": { + variant: "monospace", + fontName: "Typewriter-Regular" + } +}; +var svgData = { + // path, width, height + vec: ["vec", 0.471, 0.714], + // values from the font glyph + oiintSize1: ["oiintSize1", 0.957, 0.499], + // oval to overlay the integrand + oiintSize2: ["oiintSize2", 1.472, 0.659], + oiiintSize1: ["oiiintSize1", 1.304, 0.499], + oiiintSize2: ["oiiintSize2", 1.98, 0.659] +}; + +var staticSvg = function staticSvg(value, options) { + // Create a span with inline SVG for the element. + var _svgData$value = svgData[value], + pathName = _svgData$value[0], + width = _svgData$value[1], + height = _svgData$value[2]; + var path = new PathNode(pathName); + var svgNode = new SvgNode([path], { + "width": width + "em", + "height": height + "em", + // Override CSS rule `.katex svg { width: 100% }` + "style": "width:" + width + "em", + "viewBox": "0 0 " + 1000 * width + " " + 1000 * height, + "preserveAspectRatio": "xMinYMin" + }); + var span = makeSvgSpan(["overlay"], [svgNode], options); + span.height = height; + span.style.height = height + "em"; + span.style.width = width + "em"; + return span; +}; + +/* harmony default export */ var buildCommon = ({ + fontMap: fontMap, + makeSymbol: makeSymbol, + mathsym: mathsym, + makeSpan: makeSpan, + makeSvgSpan: makeSvgSpan, + makeLineSpan: makeLineSpan, + makeAnchor: makeAnchor, + makeFragment: makeFragment, + wrapFragment: wrapFragment, + makeVList: makeVList, + makeOrd: makeOrd, + makeGlue: makeGlue, + staticSvg: staticSvg, + svgData: svgData, + tryCombineChars: tryCombineChars +}); +;// CONCATENATED MODULE: ./src/spacingData.js +/** + * Describes spaces between different classes of atoms. + */ +var thinspace = { + number: 3, + unit: "mu" +}; +var mediumspace = { + number: 4, + unit: "mu" +}; +var thickspace = { + number: 5, + unit: "mu" +}; // Making the type below exact with all optional fields doesn't work due to +// - https://github.com/facebook/flow/issues/4582 +// - https://github.com/facebook/flow/issues/5688 +// However, since *all* fields are optional, $Shape<> works as suggested in 5688 +// above. + +// Spacing relationships for display and text styles +var spacings = { + mord: { + mop: thinspace, + mbin: mediumspace, + mrel: thickspace, + minner: thinspace + }, + mop: { + mord: thinspace, + mop: thinspace, + mrel: thickspace, + minner: thinspace + }, + mbin: { + mord: mediumspace, + mop: mediumspace, + mopen: mediumspace, + minner: mediumspace + }, + mrel: { + mord: thickspace, + mop: thickspace, + mopen: thickspace, + minner: thickspace + }, + mopen: {}, + mclose: { + mop: thinspace, + mbin: mediumspace, + mrel: thickspace, + minner: thinspace + }, + mpunct: { + mord: thinspace, + mop: thinspace, + mrel: thickspace, + mopen: thinspace, + mclose: thinspace, + mpunct: thinspace, + minner: thinspace + }, + minner: { + mord: thinspace, + mop: thinspace, + mbin: mediumspace, + mrel: thickspace, + mopen: thinspace, + mpunct: thinspace, + minner: thinspace + } +}; // Spacing relationships for script and scriptscript styles + +var tightSpacings = { + mord: { + mop: thinspace + }, + mop: { + mord: thinspace, + mop: thinspace + }, + mbin: {}, + mrel: {}, + mopen: {}, + mclose: { + mop: thinspace + }, + mpunct: {}, + minner: { + mop: thinspace + } +}; +;// CONCATENATED MODULE: ./src/defineFunction.js +/** Context provided to function handlers for error messages. */ +// Note: reverse the order of the return type union will cause a flow error. +// See https://github.com/facebook/flow/issues/3663. +// More general version of `HtmlBuilder` for nodes (e.g. \sum, accent types) +// whose presence impacts super/subscripting. In this case, ParseNode<"supsub"> +// delegates its HTML building to the HtmlBuilder corresponding to these nodes. + +/** + * Final function spec for use at parse time. + * This is almost identical to `FunctionPropSpec`, except it + * 1. includes the function handler, and + * 2. requires all arguments except argTypes. + * It is generated by `defineFunction()` below. + */ + +/** + * All registered functions. + * `functions.js` just exports this same dictionary again and makes it public. + * `Parser.js` requires this dictionary. + */ +var _functions = {}; +/** + * All HTML builders. Should be only used in the `define*` and the `build*ML` + * functions. + */ + +var _htmlGroupBuilders = {}; +/** + * All MathML builders. Should be only used in the `define*` and the `build*ML` + * functions. + */ + +var _mathmlGroupBuilders = {}; +function defineFunction(_ref) { + var type = _ref.type, + names = _ref.names, + props = _ref.props, + handler = _ref.handler, + htmlBuilder = _ref.htmlBuilder, + mathmlBuilder = _ref.mathmlBuilder; + // Set default values of functions + var data = { + type: type, + numArgs: props.numArgs, + argTypes: props.argTypes, + allowedInArgument: !!props.allowedInArgument, + allowedInText: !!props.allowedInText, + allowedInMath: props.allowedInMath === undefined ? true : props.allowedInMath, + numOptionalArgs: props.numOptionalArgs || 0, + infix: !!props.infix, + primitive: !!props.primitive, + handler: handler + }; + + for (var i = 0; i < names.length; ++i) { + _functions[names[i]] = data; + } + + if (type) { + if (htmlBuilder) { + _htmlGroupBuilders[type] = htmlBuilder; + } + + if (mathmlBuilder) { + _mathmlGroupBuilders[type] = mathmlBuilder; + } + } +} +/** + * Use this to register only the HTML and MathML builders for a function (e.g. + * if the function's ParseNode is generated in Parser.js rather than via a + * stand-alone handler provided to `defineFunction`). + */ + +function defineFunctionBuilders(_ref2) { + var type = _ref2.type, + htmlBuilder = _ref2.htmlBuilder, + mathmlBuilder = _ref2.mathmlBuilder; + defineFunction({ + type: type, + names: [], + props: { + numArgs: 0 + }, + handler: function handler() { + throw new Error('Should never be called.'); + }, + htmlBuilder: htmlBuilder, + mathmlBuilder: mathmlBuilder + }); +} +var normalizeArgument = function normalizeArgument(arg) { + return arg.type === "ordgroup" && arg.body.length === 1 ? arg.body[0] : arg; +}; // Since the corresponding buildHTML/buildMathML function expects a +// list of elements, we normalize for different kinds of arguments + +var ordargument = function ordargument(arg) { + return arg.type === "ordgroup" ? arg.body : [arg]; +}; +;// CONCATENATED MODULE: ./src/buildHTML.js +/** + * This file does the main work of building a domTree structure from a parse + * tree. The entry point is the `buildHTML` function, which takes a parse tree. + * Then, the buildExpression, buildGroup, and various groupBuilders functions + * are called, to produce a final HTML tree. + */ + + + + + + + + +var buildHTML_makeSpan = buildCommon.makeSpan; // Binary atoms (first class `mbin`) change into ordinary atoms (`mord`) +// depending on their surroundings. See TeXbook pg. 442-446, Rules 5 and 6, +// and the text before Rule 19. + +var binLeftCanceller = ["leftmost", "mbin", "mopen", "mrel", "mop", "mpunct"]; +var binRightCanceller = ["rightmost", "mrel", "mclose", "mpunct"]; +var styleMap = { + "display": src_Style.DISPLAY, + "text": src_Style.TEXT, + "script": src_Style.SCRIPT, + "scriptscript": src_Style.SCRIPTSCRIPT +}; +var DomEnum = { + mord: "mord", + mop: "mop", + mbin: "mbin", + mrel: "mrel", + mopen: "mopen", + mclose: "mclose", + mpunct: "mpunct", + minner: "minner" +}; + +/** + * Take a list of nodes, build them in order, and return a list of the built + * nodes. documentFragments are flattened into their contents, so the + * returned list contains no fragments. `isRealGroup` is true if `expression` + * is a real group (no atoms will be added on either side), as opposed to + * a partial group (e.g. one created by \color). `surrounding` is an array + * consisting type of nodes that will be added to the left and right. + */ +var buildExpression = function buildExpression(expression, options, isRealGroup, surrounding) { + if (surrounding === void 0) { + surrounding = [null, null]; + } + + // Parse expressions into `groups`. + var groups = []; + + for (var i = 0; i < expression.length; i++) { + var output = buildGroup(expression[i], options); + + if (output instanceof DocumentFragment) { + var children = output.children; + groups.push.apply(groups, children); + } else { + groups.push(output); + } + } // Combine consecutive domTree.symbolNodes into a single symbolNode. + + + buildCommon.tryCombineChars(groups); // If `expression` is a partial group, let the parent handle spacings + // to avoid processing groups multiple times. + + if (!isRealGroup) { + return groups; + } + + var glueOptions = options; + + if (expression.length === 1) { + var node = expression[0]; + + if (node.type === "sizing") { + glueOptions = options.havingSize(node.size); + } else if (node.type === "styling") { + glueOptions = options.havingStyle(styleMap[node.style]); + } + } // Dummy spans for determining spacings between surrounding atoms. + // If `expression` has no atoms on the left or right, class "leftmost" + // or "rightmost", respectively, is used to indicate it. + + + var dummyPrev = buildHTML_makeSpan([surrounding[0] || "leftmost"], [], options); + var dummyNext = buildHTML_makeSpan([surrounding[1] || "rightmost"], [], options); // TODO: These code assumes that a node's math class is the first element + // of its `classes` array. A later cleanup should ensure this, for + // instance by changing the signature of `makeSpan`. + // Before determining what spaces to insert, perform bin cancellation. + // Binary operators change to ordinary symbols in some contexts. + + var isRoot = isRealGroup === "root"; + traverseNonSpaceNodes(groups, function (node, prev) { + var prevType = prev.classes[0]; + var type = node.classes[0]; + + if (prevType === "mbin" && utils.contains(binRightCanceller, type)) { + prev.classes[0] = "mord"; + } else if (type === "mbin" && utils.contains(binLeftCanceller, prevType)) { + node.classes[0] = "mord"; + } + }, { + node: dummyPrev + }, dummyNext, isRoot); + traverseNonSpaceNodes(groups, function (node, prev) { + var prevType = getTypeOfDomTree(prev); + var type = getTypeOfDomTree(node); // 'mtight' indicates that the node is script or scriptscript style. + + var space = prevType && type ? node.hasClass("mtight") ? tightSpacings[prevType][type] : spacings[prevType][type] : null; + + if (space) { + // Insert glue (spacing) after the `prev`. + return buildCommon.makeGlue(space, glueOptions); + } + }, { + node: dummyPrev + }, dummyNext, isRoot); + return groups; +}; // Depth-first traverse non-space `nodes`, calling `callback` with the current and +// previous node as arguments, optionally returning a node to insert after the +// previous node. `prev` is an object with the previous node and `insertAfter` +// function to insert after it. `next` is a node that will be added to the right. +// Used for bin cancellation and inserting spacings. + +var traverseNonSpaceNodes = function traverseNonSpaceNodes(nodes, callback, prev, next, isRoot) { + if (next) { + // temporarily append the right node, if exists + nodes.push(next); + } + + var i = 0; + + for (; i < nodes.length; i++) { + var node = nodes[i]; + var partialGroup = checkPartialGroup(node); + + if (partialGroup) { + // Recursive DFS + // $FlowFixMe: make nodes a $ReadOnlyArray by returning a new array + traverseNonSpaceNodes(partialGroup.children, callback, prev, null, isRoot); + continue; + } // Ignore explicit spaces (e.g., \;, \,) when determining what implicit + // spacing should go between atoms of different classes + + + var nonspace = !node.hasClass("mspace"); + + if (nonspace) { + var result = callback(node, prev.node); + + if (result) { + if (prev.insertAfter) { + prev.insertAfter(result); + } else { + // insert at front + nodes.unshift(result); + i++; + } + } + } + + if (nonspace) { + prev.node = node; + } else if (isRoot && node.hasClass("newline")) { + prev.node = buildHTML_makeSpan(["leftmost"]); // treat like beginning of line + } + + prev.insertAfter = function (index) { + return function (n) { + nodes.splice(index + 1, 0, n); + i++; + }; + }(i); + } + + if (next) { + nodes.pop(); + } +}; // Check if given node is a partial group, i.e., does not affect spacing around. + + +var checkPartialGroup = function checkPartialGroup(node) { + if (node instanceof DocumentFragment || node instanceof Anchor || node instanceof Span && node.hasClass("enclosing")) { + return node; + } + + return null; +}; // Return the outermost node of a domTree. + + +var getOutermostNode = function getOutermostNode(node, side) { + var partialGroup = checkPartialGroup(node); + + if (partialGroup) { + var children = partialGroup.children; + + if (children.length) { + if (side === "right") { + return getOutermostNode(children[children.length - 1], "right"); + } else if (side === "left") { + return getOutermostNode(children[0], "left"); + } + } + } + + return node; +}; // Return math atom class (mclass) of a domTree. +// If `side` is given, it will get the type of the outermost node at given side. + + +var getTypeOfDomTree = function getTypeOfDomTree(node, side) { + if (!node) { + return null; + } + + if (side) { + node = getOutermostNode(node, side); + } // This makes a lot of assumptions as to where the type of atom + // appears. We should do a better job of enforcing this. + + + return DomEnum[node.classes[0]] || null; +}; +var makeNullDelimiter = function makeNullDelimiter(options, classes) { + var moreClasses = ["nulldelimiter"].concat(options.baseSizingClasses()); + return buildHTML_makeSpan(classes.concat(moreClasses)); +}; +/** + * buildGroup is the function that takes a group and calls the correct groupType + * function for it. It also handles the interaction of size and style changes + * between parents and children. + */ + +var buildGroup = function buildGroup(group, options, baseOptions) { + if (!group) { + return buildHTML_makeSpan(); + } + + if (_htmlGroupBuilders[group.type]) { + // Call the groupBuilders function + // $FlowFixMe + var groupNode = _htmlGroupBuilders[group.type](group, options); // If the size changed between the parent and the current group, account + // for that size difference. + + if (baseOptions && options.size !== baseOptions.size) { + groupNode = buildHTML_makeSpan(options.sizingClasses(baseOptions), [groupNode], options); + var multiplier = options.sizeMultiplier / baseOptions.sizeMultiplier; + groupNode.height *= multiplier; + groupNode.depth *= multiplier; + } + + return groupNode; + } else { + throw new src_ParseError("Got group of unknown type: '" + group.type + "'"); + } +}; +/** + * Combine an array of HTML DOM nodes (e.g., the output of `buildExpression`) + * into an unbreakable HTML node of class .base, with proper struts to + * guarantee correct vertical extent. `buildHTML` calls this repeatedly to + * make up the entire expression as a sequence of unbreakable units. + */ + +function buildHTMLUnbreakable(children, options) { + // Compute height and depth of this chunk. + var body = buildHTML_makeSpan(["base"], children, options); // Add strut, which ensures that the top of the HTML element falls at + // the height of the expression, and the bottom of the HTML element + // falls at the depth of the expression. + + var strut = buildHTML_makeSpan(["strut"]); + strut.style.height = body.height + body.depth + "em"; + strut.style.verticalAlign = -body.depth + "em"; + body.children.unshift(strut); + return body; +} +/** + * Take an entire parse tree, and build it into an appropriate set of HTML + * nodes. + */ + + +function buildHTML(tree, options) { + // Strip off outer tag wrapper for processing below. + var tag = null; + + if (tree.length === 1 && tree[0].type === "tag") { + tag = tree[0].tag; + tree = tree[0].body; + } // Build the expression contained in the tree + + + var expression = buildExpression(tree, options, "root"); + var eqnNum; + + if (expression.length === 2 && expression[1].hasClass("tag")) { + // An environment with automatic equation numbers, e.g. {gather}. + eqnNum = expression.pop(); + } + + var children = []; // Create one base node for each chunk between potential line breaks. + // The TeXBook [p.173] says "A formula will be broken only after a + // relation symbol like $=$ or $<$ or $\rightarrow$, or after a binary + // operation symbol like $+$ or $-$ or $\times$, where the relation or + // binary operation is on the ``outer level'' of the formula (i.e., not + // enclosed in {...} and not part of an \over construction)." + + var parts = []; + + for (var i = 0; i < expression.length; i++) { + parts.push(expression[i]); + + if (expression[i].hasClass("mbin") || expression[i].hasClass("mrel") || expression[i].hasClass("allowbreak")) { + // Put any post-operator glue on same line as operator. + // Watch for \nobreak along the way, and stop at \newline. + var nobreak = false; + + while (i < expression.length - 1 && expression[i + 1].hasClass("mspace") && !expression[i + 1].hasClass("newline")) { + i++; + parts.push(expression[i]); + + if (expression[i].hasClass("nobreak")) { + nobreak = true; + } + } // Don't allow break if \nobreak among the post-operator glue. + + + if (!nobreak) { + children.push(buildHTMLUnbreakable(parts, options)); + parts = []; + } + } else if (expression[i].hasClass("newline")) { + // Write the line except the newline + parts.pop(); + + if (parts.length > 0) { + children.push(buildHTMLUnbreakable(parts, options)); + parts = []; + } // Put the newline at the top level + + + children.push(expression[i]); + } + } + + if (parts.length > 0) { + children.push(buildHTMLUnbreakable(parts, options)); + } // Now, if there was a tag, build it too and append it as a final child. + + + var tagChild; + + if (tag) { + tagChild = buildHTMLUnbreakable(buildExpression(tag, options, true)); + tagChild.classes = ["tag"]; + children.push(tagChild); + } else if (eqnNum) { + children.push(eqnNum); + } + + var htmlNode = buildHTML_makeSpan(["katex-html"], children); + htmlNode.setAttribute("aria-hidden", "true"); // Adjust the strut of the tag to be the maximum height of all children + // (the height of the enclosing htmlNode) for proper vertical alignment. + + if (tagChild) { + var strut = tagChild.children[0]; + strut.style.height = htmlNode.height + htmlNode.depth + "em"; + strut.style.verticalAlign = -htmlNode.depth + "em"; + } + + return htmlNode; +} +;// CONCATENATED MODULE: ./src/mathMLTree.js +/** + * These objects store data about MathML nodes. This is the MathML equivalent + * of the types in domTree.js. Since MathML handles its own rendering, and + * since we're mainly using MathML to improve accessibility, we don't manage + * any of the styling state that the plain DOM nodes do. + * + * The `toNode` and `toMarkup` functions work simlarly to how they do in + * domTree.js, creating namespaced DOM nodes and HTML text markup respectively. + */ + + + +function newDocumentFragment(children) { + return new DocumentFragment(children); +} +/** + * This node represents a general purpose MathML node of any type. The + * constructor requires the type of node to create (for example, `"mo"` or + * `"mspace"`, corresponding to `` and `` tags). + */ + +var MathNode = /*#__PURE__*/function () { + function MathNode(type, children, classes) { + this.type = void 0; + this.attributes = void 0; + this.children = void 0; + this.classes = void 0; + this.type = type; + this.attributes = {}; + this.children = children || []; + this.classes = classes || []; + } + /** + * Sets an attribute on a MathML node. MathML depends on attributes to convey a + * semantic content, so this is used heavily. + */ + + + var _proto = MathNode.prototype; + + _proto.setAttribute = function setAttribute(name, value) { + this.attributes[name] = value; + } + /** + * Gets an attribute on a MathML node. + */ + ; + + _proto.getAttribute = function getAttribute(name) { + return this.attributes[name]; + } + /** + * Converts the math node into a MathML-namespaced DOM element. + */ + ; + + _proto.toNode = function toNode() { + var node = document.createElementNS("http://www.w3.org/1998/Math/MathML", this.type); + + for (var attr in this.attributes) { + if (Object.prototype.hasOwnProperty.call(this.attributes, attr)) { + node.setAttribute(attr, this.attributes[attr]); + } + } + + if (this.classes.length > 0) { + node.className = createClass(this.classes); + } + + for (var i = 0; i < this.children.length; i++) { + node.appendChild(this.children[i].toNode()); + } + + return node; + } + /** + * Converts the math node into an HTML markup string. + */ + ; + + _proto.toMarkup = function toMarkup() { + var markup = "<" + this.type; // Add the attributes + + for (var attr in this.attributes) { + if (Object.prototype.hasOwnProperty.call(this.attributes, attr)) { + markup += " " + attr + "=\""; + markup += utils.escape(this.attributes[attr]); + markup += "\""; + } + } + + if (this.classes.length > 0) { + markup += " class =\"" + utils.escape(createClass(this.classes)) + "\""; + } + + markup += ">"; + + for (var i = 0; i < this.children.length; i++) { + markup += this.children[i].toMarkup(); + } + + markup += ""; + return markup; + } + /** + * Converts the math node into a string, similar to innerText, but escaped. + */ + ; + + _proto.toText = function toText() { + return this.children.map(function (child) { + return child.toText(); + }).join(""); + }; + + return MathNode; +}(); +/** + * This node represents a piece of text. + */ + +var TextNode = /*#__PURE__*/function () { + function TextNode(text) { + this.text = void 0; + this.text = text; + } + /** + * Converts the text node into a DOM text node. + */ + + + var _proto2 = TextNode.prototype; + + _proto2.toNode = function toNode() { + return document.createTextNode(this.text); + } + /** + * Converts the text node into escaped HTML markup + * (representing the text itself). + */ + ; + + _proto2.toMarkup = function toMarkup() { + return utils.escape(this.toText()); + } + /** + * Converts the text node into a string + * (representing the text iteself). + */ + ; + + _proto2.toText = function toText() { + return this.text; + }; + + return TextNode; +}(); +/** + * This node represents a space, but may render as or as text, + * depending on the width. + */ + +var SpaceNode = /*#__PURE__*/function () { + /** + * Create a Space node with width given in CSS ems. + */ + function SpaceNode(width) { + this.width = void 0; + this.character = void 0; + this.width = width; // See https://www.w3.org/TR/2000/WD-MathML2-20000328/chapter6.html + // for a table of space-like characters. We use Unicode + // representations instead of &LongNames; as it's not clear how to + // make the latter via document.createTextNode. + + if (width >= 0.05555 && width <= 0.05556) { + this.character = "\u200A"; //   + } else if (width >= 0.1666 && width <= 0.1667) { + this.character = "\u2009"; //   + } else if (width >= 0.2222 && width <= 0.2223) { + this.character = "\u2005"; //   + } else if (width >= 0.2777 && width <= 0.2778) { + this.character = "\u2005\u200A"; //    + } else if (width >= -0.05556 && width <= -0.05555) { + this.character = "\u200A\u2063"; // ​ + } else if (width >= -0.1667 && width <= -0.1666) { + this.character = "\u2009\u2063"; // ​ + } else if (width >= -0.2223 && width <= -0.2222) { + this.character = "\u205F\u2063"; // ​ + } else if (width >= -0.2778 && width <= -0.2777) { + this.character = "\u2005\u2063"; // ​ + } else { + this.character = null; + } + } + /** + * Converts the math node into a MathML-namespaced DOM element. + */ + + + var _proto3 = SpaceNode.prototype; + + _proto3.toNode = function toNode() { + if (this.character) { + return document.createTextNode(this.character); + } else { + var node = document.createElementNS("http://www.w3.org/1998/Math/MathML", "mspace"); + node.setAttribute("width", this.width + "em"); + return node; + } + } + /** + * Converts the math node into an HTML markup string. + */ + ; + + _proto3.toMarkup = function toMarkup() { + if (this.character) { + return "" + this.character + ""; + } else { + return ""; + } + } + /** + * Converts the math node into a string, similar to innerText. + */ + ; + + _proto3.toText = function toText() { + if (this.character) { + return this.character; + } else { + return " "; + } + }; + + return SpaceNode; +}(); + +/* harmony default export */ var mathMLTree = ({ + MathNode: MathNode, + TextNode: TextNode, + SpaceNode: SpaceNode, + newDocumentFragment: newDocumentFragment +}); +;// CONCATENATED MODULE: ./src/buildMathML.js +/** + * This file converts a parse tree into a cooresponding MathML tree. The main + * entry point is the `buildMathML` function, which takes a parse tree from the + * parser. + */ + + + + + + + + + +/** + * Takes a symbol and converts it into a MathML text node after performing + * optional replacement from symbols.js. + */ +var makeText = function makeText(text, mode, options) { + if (src_symbols[mode][text] && src_symbols[mode][text].replace && text.charCodeAt(0) !== 0xD835 && !(ligatures.hasOwnProperty(text) && options && (options.fontFamily && options.fontFamily.substr(4, 2) === "tt" || options.font && options.font.substr(4, 2) === "tt"))) { + text = src_symbols[mode][text].replace; + } + + return new mathMLTree.TextNode(text); +}; +/** + * Wrap the given array of nodes in an node if needed, i.e., + * unless the array has length 1. Always returns a single node. + */ + +var makeRow = function makeRow(body) { + if (body.length === 1) { + return body[0]; + } else { + return new mathMLTree.MathNode("mrow", body); + } +}; +/** + * Returns the math variant as a string or null if none is required. + */ + +var getVariant = function getVariant(group, options) { + // Handle \text... font specifiers as best we can. + // MathML has a limited list of allowable mathvariant specifiers; see + // https://www.w3.org/TR/MathML3/chapter3.html#presm.commatt + if (options.fontFamily === "texttt") { + return "monospace"; + } else if (options.fontFamily === "textsf") { + if (options.fontShape === "textit" && options.fontWeight === "textbf") { + return "sans-serif-bold-italic"; + } else if (options.fontShape === "textit") { + return "sans-serif-italic"; + } else if (options.fontWeight === "textbf") { + return "bold-sans-serif"; + } else { + return "sans-serif"; + } + } else if (options.fontShape === "textit" && options.fontWeight === "textbf") { + return "bold-italic"; + } else if (options.fontShape === "textit") { + return "italic"; + } else if (options.fontWeight === "textbf") { + return "bold"; + } + + var font = options.font; + + if (!font || font === "mathnormal") { + return null; + } + + var mode = group.mode; + + if (font === "mathit") { + return "italic"; + } else if (font === "boldsymbol") { + return group.type === "textord" ? "bold" : "bold-italic"; + } else if (font === "mathbf") { + return "bold"; + } else if (font === "mathbb") { + return "double-struck"; + } else if (font === "mathfrak") { + return "fraktur"; + } else if (font === "mathscr" || font === "mathcal") { + // MathML makes no distinction between script and caligrahpic + return "script"; + } else if (font === "mathsf") { + return "sans-serif"; + } else if (font === "mathtt") { + return "monospace"; + } + + var text = group.text; + + if (utils.contains(["\\imath", "\\jmath"], text)) { + return null; + } + + if (src_symbols[mode][text] && src_symbols[mode][text].replace) { + text = src_symbols[mode][text].replace; + } + + var fontName = buildCommon.fontMap[font].fontName; + + if (getCharacterMetrics(text, fontName, mode)) { + return buildCommon.fontMap[font].variant; + } + + return null; +}; +/** + * Takes a list of nodes, builds them, and returns a list of the generated + * MathML nodes. Also combine consecutive outputs into a single + * tag. + */ + +var buildMathML_buildExpression = function buildExpression(expression, options, isOrdgroup) { + if (expression.length === 1) { + var group = buildMathML_buildGroup(expression[0], options); + + if (isOrdgroup && group instanceof MathNode && group.type === "mo") { + // When TeX writers want to suppress spacing on an operator, + // they often put the operator by itself inside braces. + group.setAttribute("lspace", "0em"); + group.setAttribute("rspace", "0em"); + } + + return [group]; + } + + var groups = []; + var lastGroup; + + for (var i = 0; i < expression.length; i++) { + var _group = buildMathML_buildGroup(expression[i], options); + + if (_group instanceof MathNode && lastGroup instanceof MathNode) { + // Concatenate adjacent s + if (_group.type === 'mtext' && lastGroup.type === 'mtext' && _group.getAttribute('mathvariant') === lastGroup.getAttribute('mathvariant')) { + var _lastGroup$children; + + (_lastGroup$children = lastGroup.children).push.apply(_lastGroup$children, _group.children); + + continue; // Concatenate adjacent s + } else if (_group.type === 'mn' && lastGroup.type === 'mn') { + var _lastGroup$children2; + + (_lastGroup$children2 = lastGroup.children).push.apply(_lastGroup$children2, _group.children); + + continue; // Concatenate ... followed by . + } else if (_group.type === 'mi' && _group.children.length === 1 && lastGroup.type === 'mn') { + var child = _group.children[0]; + + if (child instanceof TextNode && child.text === '.') { + var _lastGroup$children3; + + (_lastGroup$children3 = lastGroup.children).push.apply(_lastGroup$children3, _group.children); + + continue; + } + } else if (lastGroup.type === 'mi' && lastGroup.children.length === 1) { + var lastChild = lastGroup.children[0]; + + if (lastChild instanceof TextNode && lastChild.text === "\u0338" && (_group.type === 'mo' || _group.type === 'mi' || _group.type === 'mn')) { + var _child = _group.children[0]; + + if (_child instanceof TextNode && _child.text.length > 0) { + // Overlay with combining character long solidus + _child.text = _child.text.slice(0, 1) + "\u0338" + _child.text.slice(1); + groups.pop(); + } + } + } + } + + groups.push(_group); + lastGroup = _group; + } + + return groups; +}; +/** + * Equivalent to buildExpression, but wraps the elements in an + * if there's more than one. Returns a single node instead of an array. + */ + +var buildExpressionRow = function buildExpressionRow(expression, options, isOrdgroup) { + return makeRow(buildMathML_buildExpression(expression, options, isOrdgroup)); +}; +/** + * Takes a group from the parser and calls the appropriate groupBuilders function + * on it to produce a MathML node. + */ + +var buildMathML_buildGroup = function buildGroup(group, options) { + if (!group) { + return new mathMLTree.MathNode("mrow"); + } + + if (_mathmlGroupBuilders[group.type]) { + // Call the groupBuilders function + // $FlowFixMe + var result = _mathmlGroupBuilders[group.type](group, options); // $FlowFixMe + + return result; + } else { + throw new src_ParseError("Got group of unknown type: '" + group.type + "'"); + } +}; +/** + * Takes a full parse tree and settings and builds a MathML representation of + * it. In particular, we put the elements from building the parse tree into a + * tag so we can also include that TeX source as an annotation. + * + * Note that we actually return a domTree element with a `` inside it so + * we can do appropriate styling. + */ + +function buildMathML(tree, texExpression, options, isDisplayMode, forMathmlOnly) { + var expression = buildMathML_buildExpression(tree, options); // TODO: Make a pass thru the MathML similar to buildHTML.traverseNonSpaceNodes + // and add spacing nodes. This is necessary only adjacent to math operators + // like \sin or \lim or to subsup elements that contain math operators. + // MathML takes care of the other spacing issues. + // Wrap up the expression in an mrow so it is presented in the semantics + // tag correctly, unless it's a single or . + + var wrapper; + + if (expression.length === 1 && expression[0] instanceof MathNode && utils.contains(["mrow", "mtable"], expression[0].type)) { + wrapper = expression[0]; + } else { + wrapper = new mathMLTree.MathNode("mrow", expression); + } // Build a TeX annotation of the source + + + var annotation = new mathMLTree.MathNode("annotation", [new mathMLTree.TextNode(texExpression)]); + annotation.setAttribute("encoding", "application/x-tex"); + var semantics = new mathMLTree.MathNode("semantics", [wrapper, annotation]); + var math = new mathMLTree.MathNode("math", [semantics]); + math.setAttribute("xmlns", "http://www.w3.org/1998/Math/MathML"); + + if (isDisplayMode) { + math.setAttribute("display", "block"); + } // You can't style nodes, so we wrap the node in a span. + // NOTE: The span class is not typed to have nodes as children, and + // we don't want to make the children type more generic since the children + // of span are expected to have more fields in `buildHtml` contexts. + + + var wrapperClass = forMathmlOnly ? "katex" : "katex-mathml"; // $FlowFixMe + + return buildCommon.makeSpan([wrapperClass], [math]); +} +;// CONCATENATED MODULE: ./src/buildTree.js + + + + + + + +var optionsFromSettings = function optionsFromSettings(settings) { + return new src_Options({ + style: settings.displayMode ? src_Style.DISPLAY : src_Style.TEXT, + maxSize: settings.maxSize, + minRuleThickness: settings.minRuleThickness + }); +}; + +var displayWrap = function displayWrap(node, settings) { + if (settings.displayMode) { + var classes = ["katex-display"]; + + if (settings.leqno) { + classes.push("leqno"); + } + + if (settings.fleqn) { + classes.push("fleqn"); + } + + node = buildCommon.makeSpan(classes, [node]); + } + + return node; +}; + +var buildTree = function buildTree(tree, expression, settings) { + var options = optionsFromSettings(settings); + var katexNode; + + if (settings.output === "mathml") { + return buildMathML(tree, expression, options, settings.displayMode, true); + } else if (settings.output === "html") { + var htmlNode = buildHTML(tree, options); + katexNode = buildCommon.makeSpan(["katex"], [htmlNode]); + } else { + var mathMLNode = buildMathML(tree, expression, options, settings.displayMode, false); + + var _htmlNode = buildHTML(tree, options); + + katexNode = buildCommon.makeSpan(["katex"], [mathMLNode, _htmlNode]); + } + + return displayWrap(katexNode, settings); +}; +var buildHTMLTree = function buildHTMLTree(tree, expression, settings) { + var options = optionsFromSettings(settings); + var htmlNode = buildHTML(tree, options); + var katexNode = buildCommon.makeSpan(["katex"], [htmlNode]); + return displayWrap(katexNode, settings); +}; +/* harmony default export */ var src_buildTree = ((/* unused pure expression or super */ null && (buildTree))); +;// CONCATENATED MODULE: ./src/stretchy.js +/** + * This file provides support to buildMathML.js and buildHTML.js + * for stretchy wide elements rendered from SVG files + * and other CSS trickery. + */ + + + + +var stretchyCodePoint = { + widehat: "^", + widecheck: "ˇ", + widetilde: "~", + utilde: "~", + overleftarrow: "\u2190", + underleftarrow: "\u2190", + xleftarrow: "\u2190", + overrightarrow: "\u2192", + underrightarrow: "\u2192", + xrightarrow: "\u2192", + underbrace: "\u23DF", + overbrace: "\u23DE", + overgroup: "\u23E0", + undergroup: "\u23E1", + overleftrightarrow: "\u2194", + underleftrightarrow: "\u2194", + xleftrightarrow: "\u2194", + Overrightarrow: "\u21D2", + xRightarrow: "\u21D2", + overleftharpoon: "\u21BC", + xleftharpoonup: "\u21BC", + overrightharpoon: "\u21C0", + xrightharpoonup: "\u21C0", + xLeftarrow: "\u21D0", + xLeftrightarrow: "\u21D4", + xhookleftarrow: "\u21A9", + xhookrightarrow: "\u21AA", + xmapsto: "\u21A6", + xrightharpoondown: "\u21C1", + xleftharpoondown: "\u21BD", + xrightleftharpoons: "\u21CC", + xleftrightharpoons: "\u21CB", + xtwoheadleftarrow: "\u219E", + xtwoheadrightarrow: "\u21A0", + xlongequal: "=", + xtofrom: "\u21C4", + xrightleftarrows: "\u21C4", + xrightequilibrium: "\u21CC", + // Not a perfect match. + xleftequilibrium: "\u21CB", + // None better available. + "\\cdrightarrow": "\u2192", + "\\cdleftarrow": "\u2190", + "\\cdlongequal": "=" +}; + +var mathMLnode = function mathMLnode(label) { + var node = new mathMLTree.MathNode("mo", [new mathMLTree.TextNode(stretchyCodePoint[label.replace(/^\\/, '')])]); + node.setAttribute("stretchy", "true"); + return node; +}; // Many of the KaTeX SVG images have been adapted from glyphs in KaTeX fonts. +// Copyright (c) 2009-2010, Design Science, Inc. () +// Copyright (c) 2014-2017 Khan Academy () +// Licensed under the SIL Open Font License, Version 1.1. +// See \nhttp://scripts.sil.org/OFL +// Very Long SVGs +// Many of the KaTeX stretchy wide elements use a long SVG image and an +// overflow: hidden tactic to achieve a stretchy image while avoiding +// distortion of arrowheads or brace corners. +// The SVG typically contains a very long (400 em) arrow. +// The SVG is in a container span that has overflow: hidden, so the span +// acts like a window that exposes only part of the SVG. +// The SVG always has a longer, thinner aspect ratio than the container span. +// After the SVG fills 100% of the height of the container span, +// there is a long arrow shaft left over. That left-over shaft is not shown. +// Instead, it is sliced off because the span's CSS has overflow: hidden. +// Thus, the reader sees an arrow that matches the subject matter width +// without distortion. +// Some functions, such as \cancel, need to vary their aspect ratio. These +// functions do not get the overflow SVG treatment. +// Second Brush Stroke +// Low resolution monitors struggle to display images in fine detail. +// So browsers apply anti-aliasing. A long straight arrow shaft therefore +// will sometimes appear as if it has a blurred edge. +// To mitigate this, these SVG files contain a second "brush-stroke" on the +// arrow shafts. That is, a second long thin rectangular SVG path has been +// written directly on top of each arrow shaft. This reinforcement causes +// some of the screen pixels to display as black instead of the anti-aliased +// gray pixel that a single path would generate. So we get arrow shafts +// whose edges appear to be sharper. +// In the katexImagesData object just below, the dimensions all +// correspond to path geometry inside the relevant SVG. +// For example, \overrightarrow uses the same arrowhead as glyph U+2192 +// from the KaTeX Main font. The scaling factor is 1000. +// That is, inside the font, that arrowhead is 522 units tall, which +// corresponds to 0.522 em inside the document. + + +var katexImagesData = { + // path(s), minWidth, height, align + overrightarrow: [["rightarrow"], 0.888, 522, "xMaxYMin"], + overleftarrow: [["leftarrow"], 0.888, 522, "xMinYMin"], + underrightarrow: [["rightarrow"], 0.888, 522, "xMaxYMin"], + underleftarrow: [["leftarrow"], 0.888, 522, "xMinYMin"], + xrightarrow: [["rightarrow"], 1.469, 522, "xMaxYMin"], + "\\cdrightarrow": [["rightarrow"], 3.0, 522, "xMaxYMin"], + // CD minwwidth2.5pc + xleftarrow: [["leftarrow"], 1.469, 522, "xMinYMin"], + "\\cdleftarrow": [["leftarrow"], 3.0, 522, "xMinYMin"], + Overrightarrow: [["doublerightarrow"], 0.888, 560, "xMaxYMin"], + xRightarrow: [["doublerightarrow"], 1.526, 560, "xMaxYMin"], + xLeftarrow: [["doubleleftarrow"], 1.526, 560, "xMinYMin"], + overleftharpoon: [["leftharpoon"], 0.888, 522, "xMinYMin"], + xleftharpoonup: [["leftharpoon"], 0.888, 522, "xMinYMin"], + xleftharpoondown: [["leftharpoondown"], 0.888, 522, "xMinYMin"], + overrightharpoon: [["rightharpoon"], 0.888, 522, "xMaxYMin"], + xrightharpoonup: [["rightharpoon"], 0.888, 522, "xMaxYMin"], + xrightharpoondown: [["rightharpoondown"], 0.888, 522, "xMaxYMin"], + xlongequal: [["longequal"], 0.888, 334, "xMinYMin"], + "\\cdlongequal": [["longequal"], 3.0, 334, "xMinYMin"], + xtwoheadleftarrow: [["twoheadleftarrow"], 0.888, 334, "xMinYMin"], + xtwoheadrightarrow: [["twoheadrightarrow"], 0.888, 334, "xMaxYMin"], + overleftrightarrow: [["leftarrow", "rightarrow"], 0.888, 522], + overbrace: [["leftbrace", "midbrace", "rightbrace"], 1.6, 548], + underbrace: [["leftbraceunder", "midbraceunder", "rightbraceunder"], 1.6, 548], + underleftrightarrow: [["leftarrow", "rightarrow"], 0.888, 522], + xleftrightarrow: [["leftarrow", "rightarrow"], 1.75, 522], + xLeftrightarrow: [["doubleleftarrow", "doublerightarrow"], 1.75, 560], + xrightleftharpoons: [["leftharpoondownplus", "rightharpoonplus"], 1.75, 716], + xleftrightharpoons: [["leftharpoonplus", "rightharpoondownplus"], 1.75, 716], + xhookleftarrow: [["leftarrow", "righthook"], 1.08, 522], + xhookrightarrow: [["lefthook", "rightarrow"], 1.08, 522], + overlinesegment: [["leftlinesegment", "rightlinesegment"], 0.888, 522], + underlinesegment: [["leftlinesegment", "rightlinesegment"], 0.888, 522], + overgroup: [["leftgroup", "rightgroup"], 0.888, 342], + undergroup: [["leftgroupunder", "rightgroupunder"], 0.888, 342], + xmapsto: [["leftmapsto", "rightarrow"], 1.5, 522], + xtofrom: [["leftToFrom", "rightToFrom"], 1.75, 528], + // The next three arrows are from the mhchem package. + // In mhchem.sty, min-length is 2.0em. But these arrows might appear in the + // document as \xrightarrow or \xrightleftharpoons. Those have + // min-length = 1.75em, so we set min-length on these next three to match. + xrightleftarrows: [["baraboveleftarrow", "rightarrowabovebar"], 1.75, 901], + xrightequilibrium: [["baraboveshortleftharpoon", "rightharpoonaboveshortbar"], 1.75, 716], + xleftequilibrium: [["shortbaraboveleftharpoon", "shortrightharpoonabovebar"], 1.75, 716] +}; + +var groupLength = function groupLength(arg) { + if (arg.type === "ordgroup") { + return arg.body.length; + } else { + return 1; + } +}; + +var svgSpan = function svgSpan(group, options) { + // Create a span with inline SVG for the element. + function buildSvgSpan_() { + var viewBoxWidth = 400000; // default + + var label = group.label.substr(1); + + if (utils.contains(["widehat", "widecheck", "widetilde", "utilde"], label)) { + // Each type in the `if` statement corresponds to one of the ParseNode + // types below. This narrowing is required to access `grp.base`. + // $FlowFixMe + var grp = group; // There are four SVG images available for each function. + // Choose a taller image when there are more characters. + + var numChars = groupLength(grp.base); + var viewBoxHeight; + var pathName; + + var _height; + + if (numChars > 5) { + if (label === "widehat" || label === "widecheck") { + viewBoxHeight = 420; + viewBoxWidth = 2364; + _height = 0.42; + pathName = label + "4"; + } else { + viewBoxHeight = 312; + viewBoxWidth = 2340; + _height = 0.34; + pathName = "tilde4"; + } + } else { + var imgIndex = [1, 1, 2, 2, 3, 3][numChars]; + + if (label === "widehat" || label === "widecheck") { + viewBoxWidth = [0, 1062, 2364, 2364, 2364][imgIndex]; + viewBoxHeight = [0, 239, 300, 360, 420][imgIndex]; + _height = [0, 0.24, 0.3, 0.3, 0.36, 0.42][imgIndex]; + pathName = label + imgIndex; + } else { + viewBoxWidth = [0, 600, 1033, 2339, 2340][imgIndex]; + viewBoxHeight = [0, 260, 286, 306, 312][imgIndex]; + _height = [0, 0.26, 0.286, 0.3, 0.306, 0.34][imgIndex]; + pathName = "tilde" + imgIndex; + } + } + + var path = new PathNode(pathName); + var svgNode = new SvgNode([path], { + "width": "100%", + "height": _height + "em", + "viewBox": "0 0 " + viewBoxWidth + " " + viewBoxHeight, + "preserveAspectRatio": "none" + }); + return { + span: buildCommon.makeSvgSpan([], [svgNode], options), + minWidth: 0, + height: _height + }; + } else { + var spans = []; + var data = katexImagesData[label]; + var paths = data[0], + _minWidth = data[1], + _viewBoxHeight = data[2]; + + var _height2 = _viewBoxHeight / 1000; + + var numSvgChildren = paths.length; + var widthClasses; + var aligns; + + if (numSvgChildren === 1) { + // $FlowFixMe: All these cases must be of the 4-tuple type. + var align1 = data[3]; + widthClasses = ["hide-tail"]; + aligns = [align1]; + } else if (numSvgChildren === 2) { + widthClasses = ["halfarrow-left", "halfarrow-right"]; + aligns = ["xMinYMin", "xMaxYMin"]; + } else if (numSvgChildren === 3) { + widthClasses = ["brace-left", "brace-center", "brace-right"]; + aligns = ["xMinYMin", "xMidYMin", "xMaxYMin"]; + } else { + throw new Error("Correct katexImagesData or update code here to support\n " + numSvgChildren + " children."); + } + + for (var i = 0; i < numSvgChildren; i++) { + var _path = new PathNode(paths[i]); + + var _svgNode = new SvgNode([_path], { + "width": "400em", + "height": _height2 + "em", + "viewBox": "0 0 " + viewBoxWidth + " " + _viewBoxHeight, + "preserveAspectRatio": aligns[i] + " slice" + }); + + var _span = buildCommon.makeSvgSpan([widthClasses[i]], [_svgNode], options); + + if (numSvgChildren === 1) { + return { + span: _span, + minWidth: _minWidth, + height: _height2 + }; + } else { + _span.style.height = _height2 + "em"; + spans.push(_span); + } + } + + return { + span: buildCommon.makeSpan(["stretchy"], spans, options), + minWidth: _minWidth, + height: _height2 + }; + } + } // buildSvgSpan_() + + + var _buildSvgSpan_ = buildSvgSpan_(), + span = _buildSvgSpan_.span, + minWidth = _buildSvgSpan_.minWidth, + height = _buildSvgSpan_.height; // Note that we are returning span.depth = 0. + // Any adjustments relative to the baseline must be done in buildHTML. + + + span.height = height; + span.style.height = height + "em"; + + if (minWidth > 0) { + span.style.minWidth = minWidth + "em"; + } + + return span; +}; + +var encloseSpan = function encloseSpan(inner, label, topPad, bottomPad, options) { + // Return an image span for \cancel, \bcancel, \xcancel, \fbox, or \angl + var img; + var totalHeight = inner.height + inner.depth + topPad + bottomPad; + + if (/fbox|color|angl/.test(label)) { + img = buildCommon.makeSpan(["stretchy", label], [], options); + + if (label === "fbox") { + var color = options.color && options.getColor(); + + if (color) { + img.style.borderColor = color; + } + } + } else { + // \cancel, \bcancel, or \xcancel + // Since \cancel's SVG is inline and it omits the viewBox attribute, + // its stroke-width will not vary with span area. + var lines = []; + + if (/^[bx]cancel$/.test(label)) { + lines.push(new LineNode({ + "x1": "0", + "y1": "0", + "x2": "100%", + "y2": "100%", + "stroke-width": "0.046em" + })); + } + + if (/^x?cancel$/.test(label)) { + lines.push(new LineNode({ + "x1": "0", + "y1": "100%", + "x2": "100%", + "y2": "0", + "stroke-width": "0.046em" + })); + } + + var svgNode = new SvgNode(lines, { + "width": "100%", + "height": totalHeight + "em" + }); + img = buildCommon.makeSvgSpan([], [svgNode], options); + } + + img.height = totalHeight; + img.style.height = totalHeight + "em"; + return img; +}; + +/* harmony default export */ var stretchy = ({ + encloseSpan: encloseSpan, + mathMLnode: mathMLnode, + svgSpan: svgSpan +}); +;// CONCATENATED MODULE: ./src/parseNode.js + + +/** + * Asserts that the node is of the given type and returns it with stricter + * typing. Throws if the node's type does not match. + */ +function assertNodeType(node, type) { + if (!node || node.type !== type) { + throw new Error("Expected node of type " + type + ", but got " + (node ? "node of type " + node.type : String(node))); + } // $FlowFixMe, >=0.125 + + + return node; +} +/** + * Returns the node more strictly typed iff it is of the given type. Otherwise, + * returns null. + */ + +function assertSymbolNodeType(node) { + var typedNode = checkSymbolNodeType(node); + + if (!typedNode) { + throw new Error("Expected node of symbol group type, but got " + (node ? "node of type " + node.type : String(node))); + } + + return typedNode; +} +/** + * Returns the node more strictly typed iff it is of the given type. Otherwise, + * returns null. + */ + +function checkSymbolNodeType(node) { + if (node && (node.type === "atom" || NON_ATOMS.hasOwnProperty(node.type))) { + // $FlowFixMe + return node; + } + + return null; +} +;// CONCATENATED MODULE: ./src/functions/accent.js + + + + + + + + + +// NOTE: Unlike most `htmlBuilder`s, this one handles not only "accent", but +// also "supsub" since an accent can affect super/subscripting. +var htmlBuilder = function htmlBuilder(grp, options) { + // Accents are handled in the TeXbook pg. 443, rule 12. + var base; + var group; + var supSubGroup; + + if (grp && grp.type === "supsub") { + // If our base is a character box, and we have superscripts and + // subscripts, the supsub will defer to us. In particular, we want + // to attach the superscripts and subscripts to the inner body (so + // that the position of the superscripts and subscripts won't be + // affected by the height of the accent). We accomplish this by + // sticking the base of the accent into the base of the supsub, and + // rendering that, while keeping track of where the accent is. + // The real accent group is the base of the supsub group + group = assertNodeType(grp.base, "accent"); // The character box is the base of the accent group + + base = group.base; // Stick the character box into the base of the supsub group + + grp.base = base; // Rerender the supsub group with its new base, and store that + // result. + + supSubGroup = assertSpan(buildGroup(grp, options)); // reset original base + + grp.base = group; + } else { + group = assertNodeType(grp, "accent"); + base = group.base; + } // Build the base group + + + var body = buildGroup(base, options.havingCrampedStyle()); // Does the accent need to shift for the skew of a character? + + var mustShift = group.isShifty && utils.isCharacterBox(base); // Calculate the skew of the accent. This is based on the line "If the + // nucleus is not a single character, let s = 0; otherwise set s to the + // kern amount for the nucleus followed by the \skewchar of its font." + // Note that our skew metrics are just the kern between each character + // and the skewchar. + + var skew = 0; + + if (mustShift) { + // If the base is a character box, then we want the skew of the + // innermost character. To do that, we find the innermost character: + var baseChar = utils.getBaseElem(base); // Then, we render its group to get the symbol inside it + + var baseGroup = buildGroup(baseChar, options.havingCrampedStyle()); // Finally, we pull the skew off of the symbol. + + skew = assertSymbolDomNode(baseGroup).skew; // Note that we now throw away baseGroup, because the layers we + // removed with getBaseElem might contain things like \color which + // we can't get rid of. + // TODO(emily): Find a better way to get the skew + } // calculate the amount of space between the body and the accent + + + var clearance = Math.min(body.height, options.fontMetrics().xHeight); // Build the accent + + var accentBody; + + if (!group.isStretchy) { + var accent; + var width; + + if (group.label === "\\vec") { + // Before version 0.9, \vec used the combining font glyph U+20D7. + // But browsers, especially Safari, are not consistent in how they + // render combining characters when not preceded by a character. + // So now we use an SVG. + // If Safari reforms, we should consider reverting to the glyph. + accent = buildCommon.staticSvg("vec", options); + width = buildCommon.svgData.vec[1]; + } else { + accent = buildCommon.makeOrd({ + mode: group.mode, + text: group.label + }, options, "textord"); + accent = assertSymbolDomNode(accent); // Remove the italic correction of the accent, because it only serves to + // shift the accent over to a place we don't want. + + accent.italic = 0; + width = accent.width; + } + + accentBody = buildCommon.makeSpan(["accent-body"], [accent]); // "Full" accents expand the width of the resulting symbol to be + // at least the width of the accent, and overlap directly onto the + // character without any vertical offset. + + var accentFull = group.label === "\\textcircled"; + + if (accentFull) { + accentBody.classes.push('accent-full'); + clearance = body.height; + } // Shift the accent over by the skew. + + + var left = skew; // CSS defines `.katex .accent .accent-body:not(.accent-full) { width: 0 }` + // so that the accent doesn't contribute to the bounding box. + // We need to shift the character by its width (effectively half + // its width) to compensate. + + if (!accentFull) { + left -= width / 2; + } + + accentBody.style.left = left + "em"; // \textcircled uses the \bigcirc glyph, so it needs some + // vertical adjustment to match LaTeX. + + if (group.label === "\\textcircled") { + accentBody.style.top = ".2em"; + } + + accentBody = buildCommon.makeVList({ + positionType: "firstBaseline", + children: [{ + type: "elem", + elem: body + }, { + type: "kern", + size: -clearance + }, { + type: "elem", + elem: accentBody + }] + }, options); + } else { + accentBody = stretchy.svgSpan(group, options); + accentBody = buildCommon.makeVList({ + positionType: "firstBaseline", + children: [{ + type: "elem", + elem: body + }, { + type: "elem", + elem: accentBody, + wrapperClasses: ["svg-align"], + wrapperStyle: skew > 0 ? { + width: "calc(100% - " + 2 * skew + "em)", + marginLeft: 2 * skew + "em" + } : undefined + }] + }, options); + } + + var accentWrap = buildCommon.makeSpan(["mord", "accent"], [accentBody], options); + + if (supSubGroup) { + // Here, we replace the "base" child of the supsub with our newly + // generated accent. + supSubGroup.children[0] = accentWrap; // Since we don't rerun the height calculation after replacing the + // accent, we manually recalculate height. + + supSubGroup.height = Math.max(accentWrap.height, supSubGroup.height); // Accents should always be ords, even when their innards are not. + + supSubGroup.classes[0] = "mord"; + return supSubGroup; + } else { + return accentWrap; + } +}; + +var mathmlBuilder = function mathmlBuilder(group, options) { + var accentNode = group.isStretchy ? stretchy.mathMLnode(group.label) : new mathMLTree.MathNode("mo", [makeText(group.label, group.mode)]); + var node = new mathMLTree.MathNode("mover", [buildMathML_buildGroup(group.base, options), accentNode]); + node.setAttribute("accent", "true"); + return node; +}; + +var NON_STRETCHY_ACCENT_REGEX = new RegExp(["\\acute", "\\grave", "\\ddot", "\\tilde", "\\bar", "\\breve", "\\check", "\\hat", "\\vec", "\\dot", "\\mathring"].map(function (accent) { + return "\\" + accent; +}).join("|")); // Accents + +defineFunction({ + type: "accent", + names: ["\\acute", "\\grave", "\\ddot", "\\tilde", "\\bar", "\\breve", "\\check", "\\hat", "\\vec", "\\dot", "\\mathring", "\\widecheck", "\\widehat", "\\widetilde", "\\overrightarrow", "\\overleftarrow", "\\Overrightarrow", "\\overleftrightarrow", "\\overgroup", "\\overlinesegment", "\\overleftharpoon", "\\overrightharpoon"], + props: { + numArgs: 1 + }, + handler: function handler(context, args) { + var base = normalizeArgument(args[0]); + var isStretchy = !NON_STRETCHY_ACCENT_REGEX.test(context.funcName); + var isShifty = !isStretchy || context.funcName === "\\widehat" || context.funcName === "\\widetilde" || context.funcName === "\\widecheck"; + return { + type: "accent", + mode: context.parser.mode, + label: context.funcName, + isStretchy: isStretchy, + isShifty: isShifty, + base: base + }; + }, + htmlBuilder: htmlBuilder, + mathmlBuilder: mathmlBuilder +}); // Text-mode accents + +defineFunction({ + type: "accent", + names: ["\\'", "\\`", "\\^", "\\~", "\\=", "\\u", "\\.", '\\"', "\\r", "\\H", "\\v", "\\textcircled"], + props: { + numArgs: 1, + allowedInText: true, + allowedInMath: true, + // unless in strict mode + argTypes: ["primitive"] + }, + handler: function handler(context, args) { + var base = args[0]; + var mode = context.parser.mode; + + if (mode === "math") { + context.parser.settings.reportNonstrict("mathVsTextAccents", "LaTeX's accent " + context.funcName + " works only in text mode"); + mode = "text"; + } + + return { + type: "accent", + mode: mode, + label: context.funcName, + isStretchy: false, + isShifty: true, + base: base + }; + }, + htmlBuilder: htmlBuilder, + mathmlBuilder: mathmlBuilder +}); +;// CONCATENATED MODULE: ./src/functions/accentunder.js +// Horizontal overlap functions + + + + + + +defineFunction({ + type: "accentUnder", + names: ["\\underleftarrow", "\\underrightarrow", "\\underleftrightarrow", "\\undergroup", "\\underlinesegment", "\\utilde"], + props: { + numArgs: 1 + }, + handler: function handler(_ref, args) { + var parser = _ref.parser, + funcName = _ref.funcName; + var base = args[0]; + return { + type: "accentUnder", + mode: parser.mode, + label: funcName, + base: base + }; + }, + htmlBuilder: function htmlBuilder(group, options) { + // Treat under accents much like underlines. + var innerGroup = buildGroup(group.base, options); + var accentBody = stretchy.svgSpan(group, options); + var kern = group.label === "\\utilde" ? 0.12 : 0; // Generate the vlist, with the appropriate kerns + + var vlist = buildCommon.makeVList({ + positionType: "top", + positionData: innerGroup.height, + children: [{ + type: "elem", + elem: accentBody, + wrapperClasses: ["svg-align"] + }, { + type: "kern", + size: kern + }, { + type: "elem", + elem: innerGroup + }] + }, options); + return buildCommon.makeSpan(["mord", "accentunder"], [vlist], options); + }, + mathmlBuilder: function mathmlBuilder(group, options) { + var accentNode = stretchy.mathMLnode(group.label); + var node = new mathMLTree.MathNode("munder", [buildMathML_buildGroup(group.base, options), accentNode]); + node.setAttribute("accentunder", "true"); + return node; + } +}); +;// CONCATENATED MODULE: ./src/functions/arrow.js + + + + + + + +// Helper function +var paddedNode = function paddedNode(group) { + var node = new mathMLTree.MathNode("mpadded", group ? [group] : []); + node.setAttribute("width", "+0.6em"); + node.setAttribute("lspace", "0.3em"); + return node; +}; // Stretchy arrows with an optional argument + + +defineFunction({ + type: "xArrow", + names: ["\\xleftarrow", "\\xrightarrow", "\\xLeftarrow", "\\xRightarrow", "\\xleftrightarrow", "\\xLeftrightarrow", "\\xhookleftarrow", "\\xhookrightarrow", "\\xmapsto", "\\xrightharpoondown", "\\xrightharpoonup", "\\xleftharpoondown", "\\xleftharpoonup", "\\xrightleftharpoons", "\\xleftrightharpoons", "\\xlongequal", "\\xtwoheadrightarrow", "\\xtwoheadleftarrow", "\\xtofrom", // The next 3 functions are here to support the mhchem extension. + // Direct use of these functions is discouraged and may break someday. + "\\xrightleftarrows", "\\xrightequilibrium", "\\xleftequilibrium", // The next 3 functions are here only to support the {CD} environment. + "\\\\cdrightarrow", "\\\\cdleftarrow", "\\\\cdlongequal"], + props: { + numArgs: 1, + numOptionalArgs: 1 + }, + handler: function handler(_ref, args, optArgs) { + var parser = _ref.parser, + funcName = _ref.funcName; + return { + type: "xArrow", + mode: parser.mode, + label: funcName, + body: args[0], + below: optArgs[0] + }; + }, + // Flow is unable to correctly infer the type of `group`, even though it's + // unamibiguously determined from the passed-in `type` above. + htmlBuilder: function htmlBuilder(group, options) { + var style = options.style; // Build the argument groups in the appropriate style. + // Ref: amsmath.dtx: \hbox{$\scriptstyle\mkern#3mu{#6}\mkern#4mu$}% + // Some groups can return document fragments. Handle those by wrapping + // them in a span. + + var newOptions = options.havingStyle(style.sup()); + var upperGroup = buildCommon.wrapFragment(buildGroup(group.body, newOptions, options), options); + var arrowPrefix = group.label.slice(0, 2) === "\\x" ? "x" : "cd"; + upperGroup.classes.push(arrowPrefix + "-arrow-pad"); + var lowerGroup; + + if (group.below) { + // Build the lower group + newOptions = options.havingStyle(style.sub()); + lowerGroup = buildCommon.wrapFragment(buildGroup(group.below, newOptions, options), options); + lowerGroup.classes.push(arrowPrefix + "-arrow-pad"); + } + + var arrowBody = stretchy.svgSpan(group, options); // Re shift: Note that stretchy.svgSpan returned arrowBody.depth = 0. + // The point we want on the math axis is at 0.5 * arrowBody.height. + + var arrowShift = -options.fontMetrics().axisHeight + 0.5 * arrowBody.height; // 2 mu kern. Ref: amsmath.dtx: #7\if0#2\else\mkern#2mu\fi + + var upperShift = -options.fontMetrics().axisHeight - 0.5 * arrowBody.height - 0.111; // 0.111 em = 2 mu + + if (upperGroup.depth > 0.25 || group.label === "\\xleftequilibrium") { + upperShift -= upperGroup.depth; // shift up if depth encroaches + } // Generate the vlist + + + var vlist; + + if (lowerGroup) { + var lowerShift = -options.fontMetrics().axisHeight + lowerGroup.height + 0.5 * arrowBody.height + 0.111; + vlist = buildCommon.makeVList({ + positionType: "individualShift", + children: [{ + type: "elem", + elem: upperGroup, + shift: upperShift + }, { + type: "elem", + elem: arrowBody, + shift: arrowShift + }, { + type: "elem", + elem: lowerGroup, + shift: lowerShift + }] + }, options); + } else { + vlist = buildCommon.makeVList({ + positionType: "individualShift", + children: [{ + type: "elem", + elem: upperGroup, + shift: upperShift + }, { + type: "elem", + elem: arrowBody, + shift: arrowShift + }] + }, options); + } // $FlowFixMe: Replace this with passing "svg-align" into makeVList. + + + vlist.children[0].children[0].children[1].classes.push("svg-align"); + return buildCommon.makeSpan(["mrel", "x-arrow"], [vlist], options); + }, + mathmlBuilder: function mathmlBuilder(group, options) { + var arrowNode = stretchy.mathMLnode(group.label); + arrowNode.setAttribute("minsize", group.label.charAt(0) === "x" ? "1.75em" : "3.0em"); + var node; + + if (group.body) { + var upperNode = paddedNode(buildMathML_buildGroup(group.body, options)); + + if (group.below) { + var lowerNode = paddedNode(buildMathML_buildGroup(group.below, options)); + node = new mathMLTree.MathNode("munderover", [arrowNode, lowerNode, upperNode]); + } else { + node = new mathMLTree.MathNode("mover", [arrowNode, upperNode]); + } + } else if (group.below) { + var _lowerNode = paddedNode(buildMathML_buildGroup(group.below, options)); + + node = new mathMLTree.MathNode("munder", [arrowNode, _lowerNode]); + } else { + // This should never happen. + // Parser.js throws an error if there is no argument. + node = paddedNode(); + node = new mathMLTree.MathNode("mover", [arrowNode, node]); + } + + return node; + } +}); +;// CONCATENATED MODULE: ./src/environments/cd.js + + + + + + + +var cdArrowFunctionName = { + ">": "\\\\cdrightarrow", + "<": "\\\\cdleftarrow", + "=": "\\\\cdlongequal", + "A": "\\uparrow", + "V": "\\downarrow", + "|": "\\Vert", + ".": "no arrow" +}; + +var newCell = function newCell() { + // Create an empty cell, to be filled below with parse nodes. + // The parseTree from this module must be constructed like the + // one created by parseArray(), so an empty CD cell must + // be a ParseNode<"styling">. And CD is always displaystyle. + // So these values are fixed and flow can do implicit typing. + return { + type: "styling", + body: [], + mode: "math", + style: "display" + }; +}; + +var isStartOfArrow = function isStartOfArrow(node) { + return node.type === "textord" && node.text === "@"; +}; + +var isLabelEnd = function isLabelEnd(node, endChar) { + return (node.type === "mathord" || node.type === "atom") && node.text === endChar; +}; + +function cdArrow(arrowChar, labels, parser) { + // Return a parse tree of an arrow and its labels. + // This acts in a way similar to a macro expansion. + var funcName = cdArrowFunctionName[arrowChar]; + + switch (funcName) { + case "\\\\cdrightarrow": + case "\\\\cdleftarrow": + return parser.callFunction(funcName, [labels[0]], [labels[1]]); + + case "\\uparrow": + case "\\downarrow": + { + var leftLabel = parser.callFunction("\\\\cdleft", [labels[0]], []); + var bareArrow = { + type: "atom", + text: funcName, + mode: "math", + family: "rel" + }; + var sizedArrow = parser.callFunction("\\Big", [bareArrow], []); + var rightLabel = parser.callFunction("\\\\cdright", [labels[1]], []); + var arrowGroup = { + type: "ordgroup", + mode: "math", + body: [leftLabel, sizedArrow, rightLabel] + }; + return parser.callFunction("\\\\cdparent", [arrowGroup], []); + } + + case "\\\\cdlongequal": + return parser.callFunction("\\\\cdlongequal", [], []); + + case "\\Vert": + { + var arrow = { + type: "textord", + text: "\\Vert", + mode: "math" + }; + return parser.callFunction("\\Big", [arrow], []); + } + + default: + return { + type: "textord", + text: " ", + mode: "math" + }; + } +} + +function parseCD(parser) { + // Get the array's parse nodes with \\ temporarily mapped to \cr. + var parsedRows = []; + parser.gullet.beginGroup(); + parser.gullet.macros.set("\\cr", "\\\\\\relax"); + parser.gullet.beginGroup(); + + while (true) { + // eslint-disable-line no-constant-condition + // Get the parse nodes for the next row. + parsedRows.push(parser.parseExpression(false, "\\\\")); + parser.gullet.endGroup(); + parser.gullet.beginGroup(); + var next = parser.fetch().text; + + if (next === "&" || next === "\\\\") { + parser.consume(); + } else if (next === "\\end") { + if (parsedRows[parsedRows.length - 1].length === 0) { + parsedRows.pop(); // final row ended in \\ + } + + break; + } else { + throw new src_ParseError("Expected \\\\ or \\cr or \\end", parser.nextToken); + } + } + + var row = []; + var body = [row]; // Loop thru the parse nodes. Collect them into cells and arrows. + + for (var i = 0; i < parsedRows.length; i++) { + // Start a new row. + var rowNodes = parsedRows[i]; // Create the first cell. + + var cell = newCell(); + + for (var j = 0; j < rowNodes.length; j++) { + if (!isStartOfArrow(rowNodes[j])) { + // If a parseNode is not an arrow, it goes into a cell. + cell.body.push(rowNodes[j]); + } else { + // Parse node j is an "@", the start of an arrow. + // Before starting on the arrow, push the cell into `row`. + row.push(cell); // Now collect parseNodes into an arrow. + // The character after "@" defines the arrow type. + + j += 1; + var arrowChar = assertSymbolNodeType(rowNodes[j]).text; // Create two empty label nodes. We may or may not use them. + + var labels = new Array(2); + labels[0] = { + type: "ordgroup", + mode: "math", + body: [] + }; + labels[1] = { + type: "ordgroup", + mode: "math", + body: [] + }; // Process the arrow. + + if ("=|.".indexOf(arrowChar) > -1) {// Three "arrows", ``@=`, `@|`, and `@.`, do not take labels. + // Do nothing here. + } else if ("<>AV".indexOf(arrowChar) > -1) { + // Four arrows, `@>>>`, `@<<<`, `@AAA`, and `@VVV`, each take + // two optional labels. E.g. the right-point arrow syntax is + // really: @>{optional label}>{optional label}> + // Collect parseNodes into labels. + for (var labelNum = 0; labelNum < 2; labelNum++) { + var inLabel = true; + + for (var k = j + 1; k < rowNodes.length; k++) { + if (isLabelEnd(rowNodes[k], arrowChar)) { + inLabel = false; + j = k; + break; + } + + if (isStartOfArrow(rowNodes[k])) { + throw new src_ParseError("Missing a " + arrowChar + " character to complete a CD arrow.", rowNodes[k]); + } + + labels[labelNum].body.push(rowNodes[k]); + } + + if (inLabel) { + // isLabelEnd never returned a true. + throw new src_ParseError("Missing a " + arrowChar + " character to complete a CD arrow.", rowNodes[j]); + } + } + } else { + throw new src_ParseError("Expected one of \"<>AV=|.\" after @", rowNodes[j]); + } // Now join the arrow to its labels. + + + var arrow = cdArrow(arrowChar, labels, parser); // Wrap the arrow in ParseNode<"styling">. + // This is done to match parseArray() behavior. + + var wrappedArrow = { + type: "styling", + body: [arrow], + mode: "math", + style: "display" // CD is always displaystyle. + + }; + row.push(wrappedArrow); // In CD's syntax, cells are implicit. That is, everything that + // is not an arrow gets collected into a cell. So create an empty + // cell now. It will collect upcoming parseNodes. + + cell = newCell(); + } + } + + if (i % 2 === 0) { + // Even-numbered rows consist of: cell, arrow, cell, arrow, ... cell + // The last cell is not yet pushed into `row`, so: + row.push(cell); + } else { + // Odd-numbered rows consist of: vert arrow, empty cell, ... vert arrow + // Remove the empty cell that was placed at the beginning of `row`. + row.shift(); + } + + row = []; + body.push(row); + } // End row group + + + parser.gullet.endGroup(); // End array group defining \\ + + parser.gullet.endGroup(); // define column separation. + + var cols = new Array(body[0].length).fill({ + type: "align", + align: "c", + pregap: 0.25, + // CD package sets \enskip between columns. + postgap: 0.25 // So pre and post each get half an \enskip, i.e. 0.25em. + + }); + return { + type: "array", + mode: "math", + body: body, + arraystretch: 1, + addJot: true, + rowGaps: [null], + cols: cols, + colSeparationType: "CD", + hLinesBeforeRow: new Array(body.length + 1).fill([]) + }; +} // The functions below are not available for general use. +// They are here only for internal use by the {CD} environment in placing labels +// next to vertical arrows. +// We don't need any such functions for horizontal arrows because we can reuse +// the functionality that already exists for extensible arrows. + +defineFunction({ + type: "cdlabel", + names: ["\\\\cdleft", "\\\\cdright"], + props: { + numArgs: 1 + }, + handler: function handler(_ref, args) { + var parser = _ref.parser, + funcName = _ref.funcName; + return { + type: "cdlabel", + mode: parser.mode, + side: funcName.slice(4), + label: args[0] + }; + }, + htmlBuilder: function htmlBuilder(group, options) { + var newOptions = options.havingStyle(options.style.sup()); + var label = buildCommon.wrapFragment(buildGroup(group.label, newOptions, options), options); + label.classes.push("cd-label-" + group.side); + label.style.bottom = 0.8 - label.depth + "em"; // Zero out label height & depth, so vertical align of arrow is set + // by the arrow height, not by the label. + + label.height = 0; + label.depth = 0; + return label; + }, + mathmlBuilder: function mathmlBuilder(group, options) { + var label = new mathMLTree.MathNode("mrow", [buildMathML_buildGroup(group.label, options)]); + label = new mathMLTree.MathNode("mpadded", [label]); + label.setAttribute("width", "0"); + + if (group.side === "left") { + label.setAttribute("lspace", "-1width"); + } // We have to guess at vertical alignment. We know the arrow is 1.8em tall, + // But we don't know the height or depth of the label. + + + label.setAttribute("voffset", "0.7em"); + label = new mathMLTree.MathNode("mstyle", [label]); + label.setAttribute("displaystyle", "false"); + label.setAttribute("scriptlevel", "1"); + return label; + } +}); +defineFunction({ + type: "cdlabelparent", + names: ["\\\\cdparent"], + props: { + numArgs: 1 + }, + handler: function handler(_ref2, args) { + var parser = _ref2.parser; + return { + type: "cdlabelparent", + mode: parser.mode, + fragment: args[0] + }; + }, + htmlBuilder: function htmlBuilder(group, options) { + // Wrap the vertical arrow and its labels. + // The parent gets position: relative. The child gets position: absolute. + // So CSS can locate the label correctly. + var parent = buildCommon.wrapFragment(buildGroup(group.fragment, options), options); + parent.classes.push("cd-vert-arrow"); + return parent; + }, + mathmlBuilder: function mathmlBuilder(group, options) { + return new mathMLTree.MathNode("mrow", [buildMathML_buildGroup(group.fragment, options)]); + } +}); +;// CONCATENATED MODULE: ./src/functions/char.js + + + // \@char is an internal function that takes a grouped decimal argument like +// {123} and converts into symbol with code 123. It is used by the *macro* +// \char defined in macros.js. + +defineFunction({ + type: "textord", + names: ["\\@char"], + props: { + numArgs: 1, + allowedInText: true + }, + handler: function handler(_ref, args) { + var parser = _ref.parser; + var arg = assertNodeType(args[0], "ordgroup"); + var group = arg.body; + var number = ""; + + for (var i = 0; i < group.length; i++) { + var node = assertNodeType(group[i], "textord"); + number += node.text; + } + + var code = parseInt(number); + + if (isNaN(code)) { + throw new src_ParseError("\\@char has non-numeric argument " + number); + } + + return { + type: "textord", + mode: parser.mode, + text: String.fromCharCode(code) + }; + } +}); +;// CONCATENATED MODULE: ./src/functions/color.js + + + + + + + +var color_htmlBuilder = function htmlBuilder(group, options) { + var elements = buildExpression(group.body, options.withColor(group.color), false); // \color isn't supposed to affect the type of the elements it contains. + // To accomplish this, we wrap the results in a fragment, so the inner + // elements will be able to directly interact with their neighbors. For + // example, `\color{red}{2 +} 3` has the same spacing as `2 + 3` + + return buildCommon.makeFragment(elements); +}; + +var color_mathmlBuilder = function mathmlBuilder(group, options) { + var inner = buildMathML_buildExpression(group.body, options.withColor(group.color)); + var node = new mathMLTree.MathNode("mstyle", inner); + node.setAttribute("mathcolor", group.color); + return node; +}; + +defineFunction({ + type: "color", + names: ["\\textcolor"], + props: { + numArgs: 2, + allowedInText: true, + argTypes: ["color", "original"] + }, + handler: function handler(_ref, args) { + var parser = _ref.parser; + var color = assertNodeType(args[0], "color-token").color; + var body = args[1]; + return { + type: "color", + mode: parser.mode, + color: color, + body: ordargument(body) + }; + }, + htmlBuilder: color_htmlBuilder, + mathmlBuilder: color_mathmlBuilder +}); +defineFunction({ + type: "color", + names: ["\\color"], + props: { + numArgs: 1, + allowedInText: true, + argTypes: ["color"] + }, + handler: function handler(_ref2, args) { + var parser = _ref2.parser, + breakOnTokenText = _ref2.breakOnTokenText; + var color = assertNodeType(args[0], "color-token").color; // Set macro \current@color in current namespace to store the current + // color, mimicking the behavior of color.sty. + // This is currently used just to correctly color a \right + // that follows a \color command. + + parser.gullet.macros.set("\\current@color", color); // Parse out the implicit body that should be colored. + + var body = parser.parseExpression(true, breakOnTokenText); + return { + type: "color", + mode: parser.mode, + color: color, + body: body + }; + }, + htmlBuilder: color_htmlBuilder, + mathmlBuilder: color_mathmlBuilder +}); +;// CONCATENATED MODULE: ./src/functions/cr.js +// Row breaks within tabular environments, and line breaks at top level + + + + + // \DeclareRobustCommand\\{...\@xnewline} + +defineFunction({ + type: "cr", + names: ["\\\\"], + props: { + numArgs: 0, + numOptionalArgs: 1, + argTypes: ["size"], + allowedInText: true + }, + handler: function handler(_ref, args, optArgs) { + var parser = _ref.parser; + var size = optArgs[0]; + var newLine = !parser.settings.displayMode || !parser.settings.useStrictBehavior("newLineInDisplayMode", "In LaTeX, \\\\ or \\newline " + "does nothing in display mode"); + return { + type: "cr", + mode: parser.mode, + newLine: newLine, + size: size && assertNodeType(size, "size").value + }; + }, + // The following builders are called only at the top level, + // not within tabular/array environments. + htmlBuilder: function htmlBuilder(group, options) { + var span = buildCommon.makeSpan(["mspace"], [], options); + + if (group.newLine) { + span.classes.push("newline"); + + if (group.size) { + span.style.marginTop = calculateSize(group.size, options) + "em"; + } + } + + return span; + }, + mathmlBuilder: function mathmlBuilder(group, options) { + var node = new mathMLTree.MathNode("mspace"); + + if (group.newLine) { + node.setAttribute("linebreak", "newline"); + + if (group.size) { + node.setAttribute("height", calculateSize(group.size, options) + "em"); + } + } + + return node; + } +}); +;// CONCATENATED MODULE: ./src/functions/def.js + + + +var globalMap = { + "\\global": "\\global", + "\\long": "\\\\globallong", + "\\\\globallong": "\\\\globallong", + "\\def": "\\gdef", + "\\gdef": "\\gdef", + "\\edef": "\\xdef", + "\\xdef": "\\xdef", + "\\let": "\\\\globallet", + "\\futurelet": "\\\\globalfuture" +}; + +var checkControlSequence = function checkControlSequence(tok) { + var name = tok.text; + + if (/^(?:[\\{}$&#^_]|EOF)$/.test(name)) { + throw new src_ParseError("Expected a control sequence", tok); + } + + return name; +}; + +var getRHS = function getRHS(parser) { + var tok = parser.gullet.popToken(); + + if (tok.text === "=") { + // consume optional equals + tok = parser.gullet.popToken(); + + if (tok.text === " ") { + // consume one optional space + tok = parser.gullet.popToken(); + } + } + + return tok; +}; + +var letCommand = function letCommand(parser, name, tok, global) { + var macro = parser.gullet.macros.get(tok.text); + + if (macro == null) { + // don't expand it later even if a macro with the same name is defined + // e.g., \let\foo=\frac \def\frac{\relax} \frac12 + tok.noexpand = true; + macro = { + tokens: [tok], + numArgs: 0, + // reproduce the same behavior in expansion + unexpandable: !parser.gullet.isExpandable(tok.text) + }; + } + + parser.gullet.macros.set(name, macro, global); +}; // -> | +// -> |\global +// -> | +// -> \global|\long|\outer + + +defineFunction({ + type: "internal", + names: ["\\global", "\\long", "\\\\globallong" // can’t be entered directly + ], + props: { + numArgs: 0, + allowedInText: true + }, + handler: function handler(_ref) { + var parser = _ref.parser, + funcName = _ref.funcName; + parser.consumeSpaces(); + var token = parser.fetch(); + + if (globalMap[token.text]) { + // KaTeX doesn't have \par, so ignore \long + if (funcName === "\\global" || funcName === "\\\\globallong") { + token.text = globalMap[token.text]; + } + + return assertNodeType(parser.parseFunction(), "internal"); + } + + throw new src_ParseError("Invalid token after macro prefix", token); + } +}); // Basic support for macro definitions: \def, \gdef, \edef, \xdef +// -> +// -> \def|\gdef|\edef|\xdef +// -> + +defineFunction({ + type: "internal", + names: ["\\def", "\\gdef", "\\edef", "\\xdef"], + props: { + numArgs: 0, + allowedInText: true, + primitive: true + }, + handler: function handler(_ref2) { + var parser = _ref2.parser, + funcName = _ref2.funcName; + var tok = parser.gullet.popToken(); + var name = tok.text; + + if (/^(?:[\\{}$&#^_]|EOF)$/.test(name)) { + throw new src_ParseError("Expected a control sequence", tok); + } + + var numArgs = 0; + var insert; + var delimiters = [[]]; // contains no braces + + while (parser.gullet.future().text !== "{") { + tok = parser.gullet.popToken(); + + if (tok.text === "#") { + // If the very last character of the is #, so that + // this # is immediately followed by {, TeX will behave as if the { + // had been inserted at the right end of both the parameter text + // and the replacement text. + if (parser.gullet.future().text === "{") { + insert = parser.gullet.future(); + delimiters[numArgs].push("{"); + break; + } // A parameter, the first appearance of # must be followed by 1, + // the next by 2, and so on; up to nine #’s are allowed + + + tok = parser.gullet.popToken(); + + if (!/^[1-9]$/.test(tok.text)) { + throw new src_ParseError("Invalid argument number \"" + tok.text + "\""); + } + + if (parseInt(tok.text) !== numArgs + 1) { + throw new src_ParseError("Argument number \"" + tok.text + "\" out of order"); + } + + numArgs++; + delimiters.push([]); + } else if (tok.text === "EOF") { + throw new src_ParseError("Expected a macro definition"); + } else { + delimiters[numArgs].push(tok.text); + } + } // replacement text, enclosed in '{' and '}' and properly nested + + + var _parser$gullet$consum = parser.gullet.consumeArg(), + tokens = _parser$gullet$consum.tokens; + + if (insert) { + tokens.unshift(insert); + } + + if (funcName === "\\edef" || funcName === "\\xdef") { + tokens = parser.gullet.expandTokens(tokens); + tokens.reverse(); // to fit in with stack order + } // Final arg is the expansion of the macro + + + parser.gullet.macros.set(name, { + tokens: tokens, + numArgs: numArgs, + delimiters: delimiters + }, funcName === globalMap[funcName]); + return { + type: "internal", + mode: parser.mode + }; + } +}); // -> +// -> \futurelet +// | \let +// -> |= + +defineFunction({ + type: "internal", + names: ["\\let", "\\\\globallet" // can’t be entered directly + ], + props: { + numArgs: 0, + allowedInText: true, + primitive: true + }, + handler: function handler(_ref3) { + var parser = _ref3.parser, + funcName = _ref3.funcName; + var name = checkControlSequence(parser.gullet.popToken()); + parser.gullet.consumeSpaces(); + var tok = getRHS(parser); + letCommand(parser, name, tok, funcName === "\\\\globallet"); + return { + type: "internal", + mode: parser.mode + }; + } +}); // ref: https://www.tug.org/TUGboat/tb09-3/tb22bechtolsheim.pdf + +defineFunction({ + type: "internal", + names: ["\\futurelet", "\\\\globalfuture" // can’t be entered directly + ], + props: { + numArgs: 0, + allowedInText: true, + primitive: true + }, + handler: function handler(_ref4) { + var parser = _ref4.parser, + funcName = _ref4.funcName; + var name = checkControlSequence(parser.gullet.popToken()); + var middle = parser.gullet.popToken(); + var tok = parser.gullet.popToken(); + letCommand(parser, name, tok, funcName === "\\\\globalfuture"); + parser.gullet.pushToken(tok); + parser.gullet.pushToken(middle); + return { + type: "internal", + mode: parser.mode + }; + } +}); +;// CONCATENATED MODULE: ./src/delimiter.js +/** + * This file deals with creating delimiters of various sizes. The TeXbook + * discusses these routines on page 441-442, in the "Another subroutine sets box + * x to a specified variable delimiter" paragraph. + * + * There are three main routines here. `makeSmallDelim` makes a delimiter in the + * normal font, but in either text, script, or scriptscript style. + * `makeLargeDelim` makes a delimiter in textstyle, but in one of the Size1, + * Size2, Size3, or Size4 fonts. `makeStackedDelim` makes a delimiter out of + * smaller pieces that are stacked on top of one another. + * + * The functions take a parameter `center`, which determines if the delimiter + * should be centered around the axis. + * + * Then, there are three exposed functions. `sizedDelim` makes a delimiter in + * one of the given sizes. This is used for things like `\bigl`. + * `customSizedDelim` makes a delimiter with a given total height+depth. It is + * called in places like `\sqrt`. `leftRightDelim` makes an appropriate + * delimiter which surrounds an expression of a given height an depth. It is + * used in `\left` and `\right`. + */ + + + + + + + + + + +/** + * Get the metrics for a given symbol and font, after transformation (i.e. + * after following replacement from symbols.js) + */ +var getMetrics = function getMetrics(symbol, font, mode) { + var replace = src_symbols.math[symbol] && src_symbols.math[symbol].replace; + var metrics = getCharacterMetrics(replace || symbol, font, mode); + + if (!metrics) { + throw new Error("Unsupported symbol " + symbol + " and font size " + font + "."); + } + + return metrics; +}; +/** + * Puts a delimiter span in a given style, and adds appropriate height, depth, + * and maxFontSizes. + */ + + +var styleWrap = function styleWrap(delim, toStyle, options, classes) { + var newOptions = options.havingBaseStyle(toStyle); + var span = buildCommon.makeSpan(classes.concat(newOptions.sizingClasses(options)), [delim], options); + var delimSizeMultiplier = newOptions.sizeMultiplier / options.sizeMultiplier; + span.height *= delimSizeMultiplier; + span.depth *= delimSizeMultiplier; + span.maxFontSize = newOptions.sizeMultiplier; + return span; +}; + +var centerSpan = function centerSpan(span, options, style) { + var newOptions = options.havingBaseStyle(style); + var shift = (1 - options.sizeMultiplier / newOptions.sizeMultiplier) * options.fontMetrics().axisHeight; + span.classes.push("delimcenter"); + span.style.top = shift + "em"; + span.height -= shift; + span.depth += shift; +}; +/** + * Makes a small delimiter. This is a delimiter that comes in the Main-Regular + * font, but is restyled to either be in textstyle, scriptstyle, or + * scriptscriptstyle. + */ + + +var makeSmallDelim = function makeSmallDelim(delim, style, center, options, mode, classes) { + var text = buildCommon.makeSymbol(delim, "Main-Regular", mode, options); + var span = styleWrap(text, style, options, classes); + + if (center) { + centerSpan(span, options, style); + } + + return span; +}; +/** + * Builds a symbol in the given font size (note size is an integer) + */ + + +var mathrmSize = function mathrmSize(value, size, mode, options) { + return buildCommon.makeSymbol(value, "Size" + size + "-Regular", mode, options); +}; +/** + * Makes a large delimiter. This is a delimiter that comes in the Size1, Size2, + * Size3, or Size4 fonts. It is always rendered in textstyle. + */ + + +var makeLargeDelim = function makeLargeDelim(delim, size, center, options, mode, classes) { + var inner = mathrmSize(delim, size, mode, options); + var span = styleWrap(buildCommon.makeSpan(["delimsizing", "size" + size], [inner], options), src_Style.TEXT, options, classes); + + if (center) { + centerSpan(span, options, src_Style.TEXT); + } + + return span; +}; +/** + * Make a span from a font glyph with the given offset and in the given font. + * This is used in makeStackedDelim to make the stacking pieces for the delimiter. + */ + + +var makeGlyphSpan = function makeGlyphSpan(symbol, font, mode) { + var sizeClass; // Apply the correct CSS class to choose the right font. + + if (font === "Size1-Regular") { + sizeClass = "delim-size1"; + } else + /* if (font === "Size4-Regular") */ + { + sizeClass = "delim-size4"; + } + + var corner = buildCommon.makeSpan(["delimsizinginner", sizeClass], [buildCommon.makeSpan([], [buildCommon.makeSymbol(symbol, font, mode)])]); // Since this will be passed into `makeVList` in the end, wrap the element + // in the appropriate tag that VList uses. + + return { + type: "elem", + elem: corner + }; +}; + +var makeInner = function makeInner(ch, height, options) { + // Create a span with inline SVG for the inner part of a tall stacked delimiter. + var width = fontMetricsData["Size4-Regular"][ch.charCodeAt(0)] ? fontMetricsData["Size4-Regular"][ch.charCodeAt(0)][4].toFixed(3) : fontMetricsData["Size1-Regular"][ch.charCodeAt(0)][4].toFixed(3); + var path = new PathNode("inner", innerPath(ch, Math.round(1000 * height))); + var svgNode = new SvgNode([path], { + "width": width + "em", + "height": height + "em", + // Override CSS rule `.katex svg { width: 100% }` + "style": "width:" + width + "em", + "viewBox": "0 0 " + 1000 * width + " " + Math.round(1000 * height), + "preserveAspectRatio": "xMinYMin" + }); + var span = buildCommon.makeSvgSpan([], [svgNode], options); + span.height = height; + span.style.height = height + "em"; + span.style.width = width + "em"; + return { + type: "elem", + elem: span + }; +}; // Helpers for makeStackedDelim + + +var lapInEms = 0.008; +var lap = { + type: "kern", + size: -1 * lapInEms +}; +var verts = ["|", "\\lvert", "\\rvert", "\\vert"]; +var doubleVerts = ["\\|", "\\lVert", "\\rVert", "\\Vert"]; +/** + * Make a stacked delimiter out of a given delimiter, with the total height at + * least `heightTotal`. This routine is mentioned on page 442 of the TeXbook. + */ + +var makeStackedDelim = function makeStackedDelim(delim, heightTotal, center, options, mode, classes) { + // There are four parts, the top, an optional middle, a repeated part, and a + // bottom. + var top; + var middle; + var repeat; + var bottom; + top = repeat = bottom = delim; + middle = null; // Also keep track of what font the delimiters are in + + var font = "Size1-Regular"; // We set the parts and font based on the symbol. Note that we use + // '\u23d0' instead of '|' and '\u2016' instead of '\\|' for the + // repeats of the arrows + + if (delim === "\\uparrow") { + repeat = bottom = "\u23D0"; + } else if (delim === "\\Uparrow") { + repeat = bottom = "\u2016"; + } else if (delim === "\\downarrow") { + top = repeat = "\u23D0"; + } else if (delim === "\\Downarrow") { + top = repeat = "\u2016"; + } else if (delim === "\\updownarrow") { + top = "\\uparrow"; + repeat = "\u23D0"; + bottom = "\\downarrow"; + } else if (delim === "\\Updownarrow") { + top = "\\Uparrow"; + repeat = "\u2016"; + bottom = "\\Downarrow"; + } else if (utils.contains(verts, delim)) { + repeat = "\u2223"; + } else if (utils.contains(doubleVerts, delim)) { + repeat = "\u2225"; + } else if (delim === "[" || delim === "\\lbrack") { + top = "\u23A1"; + repeat = "\u23A2"; + bottom = "\u23A3"; + font = "Size4-Regular"; + } else if (delim === "]" || delim === "\\rbrack") { + top = "\u23A4"; + repeat = "\u23A5"; + bottom = "\u23A6"; + font = "Size4-Regular"; + } else if (delim === "\\lfloor" || delim === "\u230A") { + repeat = top = "\u23A2"; + bottom = "\u23A3"; + font = "Size4-Regular"; + } else if (delim === "\\lceil" || delim === "\u2308") { + top = "\u23A1"; + repeat = bottom = "\u23A2"; + font = "Size4-Regular"; + } else if (delim === "\\rfloor" || delim === "\u230B") { + repeat = top = "\u23A5"; + bottom = "\u23A6"; + font = "Size4-Regular"; + } else if (delim === "\\rceil" || delim === "\u2309") { + top = "\u23A4"; + repeat = bottom = "\u23A5"; + font = "Size4-Regular"; + } else if (delim === "(" || delim === "\\lparen") { + top = "\u239B"; + repeat = "\u239C"; + bottom = "\u239D"; + font = "Size4-Regular"; + } else if (delim === ")" || delim === "\\rparen") { + top = "\u239E"; + repeat = "\u239F"; + bottom = "\u23A0"; + font = "Size4-Regular"; + } else if (delim === "\\{" || delim === "\\lbrace") { + top = "\u23A7"; + middle = "\u23A8"; + bottom = "\u23A9"; + repeat = "\u23AA"; + font = "Size4-Regular"; + } else if (delim === "\\}" || delim === "\\rbrace") { + top = "\u23AB"; + middle = "\u23AC"; + bottom = "\u23AD"; + repeat = "\u23AA"; + font = "Size4-Regular"; + } else if (delim === "\\lgroup" || delim === "\u27EE") { + top = "\u23A7"; + bottom = "\u23A9"; + repeat = "\u23AA"; + font = "Size4-Regular"; + } else if (delim === "\\rgroup" || delim === "\u27EF") { + top = "\u23AB"; + bottom = "\u23AD"; + repeat = "\u23AA"; + font = "Size4-Regular"; + } else if (delim === "\\lmoustache" || delim === "\u23B0") { + top = "\u23A7"; + bottom = "\u23AD"; + repeat = "\u23AA"; + font = "Size4-Regular"; + } else if (delim === "\\rmoustache" || delim === "\u23B1") { + top = "\u23AB"; + bottom = "\u23A9"; + repeat = "\u23AA"; + font = "Size4-Regular"; + } // Get the metrics of the four sections + + + var topMetrics = getMetrics(top, font, mode); + var topHeightTotal = topMetrics.height + topMetrics.depth; + var repeatMetrics = getMetrics(repeat, font, mode); + var repeatHeightTotal = repeatMetrics.height + repeatMetrics.depth; + var bottomMetrics = getMetrics(bottom, font, mode); + var bottomHeightTotal = bottomMetrics.height + bottomMetrics.depth; + var middleHeightTotal = 0; + var middleFactor = 1; + + if (middle !== null) { + var middleMetrics = getMetrics(middle, font, mode); + middleHeightTotal = middleMetrics.height + middleMetrics.depth; + middleFactor = 2; // repeat symmetrically above and below middle + } // Calcuate the minimal height that the delimiter can have. + // It is at least the size of the top, bottom, and optional middle combined. + + + var minHeight = topHeightTotal + bottomHeightTotal + middleHeightTotal; // Compute the number of copies of the repeat symbol we will need + + var repeatCount = Math.max(0, Math.ceil((heightTotal - minHeight) / (middleFactor * repeatHeightTotal))); // Compute the total height of the delimiter including all the symbols + + var realHeightTotal = minHeight + repeatCount * middleFactor * repeatHeightTotal; // The center of the delimiter is placed at the center of the axis. Note + // that in this context, "center" means that the delimiter should be + // centered around the axis in the current style, while normally it is + // centered around the axis in textstyle. + + var axisHeight = options.fontMetrics().axisHeight; + + if (center) { + axisHeight *= options.sizeMultiplier; + } // Calculate the depth + + + var depth = realHeightTotal / 2 - axisHeight; // Now, we start building the pieces that will go into the vlist + // Keep a list of the pieces of the stacked delimiter + + var stack = []; // Add the bottom symbol + + stack.push(makeGlyphSpan(bottom, font, mode)); + stack.push(lap); // overlap + + if (middle === null) { + // The middle section will be an SVG. Make it an extra 0.016em tall. + // We'll overlap by 0.008em at top and bottom. + var innerHeight = realHeightTotal - topHeightTotal - bottomHeightTotal + 2 * lapInEms; + stack.push(makeInner(repeat, innerHeight, options)); + } else { + // When there is a middle bit, we need the middle part and two repeated + // sections + var _innerHeight = (realHeightTotal - topHeightTotal - bottomHeightTotal - middleHeightTotal) / 2 + 2 * lapInEms; + + stack.push(makeInner(repeat, _innerHeight, options)); // Now insert the middle of the brace. + + stack.push(lap); + stack.push(makeGlyphSpan(middle, font, mode)); + stack.push(lap); + stack.push(makeInner(repeat, _innerHeight, options)); + } // Add the top symbol + + + stack.push(lap); + stack.push(makeGlyphSpan(top, font, mode)); // Finally, build the vlist + + var newOptions = options.havingBaseStyle(src_Style.TEXT); + var inner = buildCommon.makeVList({ + positionType: "bottom", + positionData: depth, + children: stack + }, newOptions); + return styleWrap(buildCommon.makeSpan(["delimsizing", "mult"], [inner], newOptions), src_Style.TEXT, options, classes); +}; // All surds have 0.08em padding above the viniculum inside the SVG. +// That keeps browser span height rounding error from pinching the line. + + +var vbPad = 80; // padding above the surd, measured inside the viewBox. + +var emPad = 0.08; // padding, in ems, measured in the document. + +var sqrtSvg = function sqrtSvg(sqrtName, height, viewBoxHeight, extraViniculum, options) { + var path = sqrtPath(sqrtName, extraViniculum, viewBoxHeight); + var pathNode = new PathNode(sqrtName, path); + var svg = new SvgNode([pathNode], { + // Note: 1000:1 ratio of viewBox to document em width. + "width": "400em", + "height": height + "em", + "viewBox": "0 0 400000 " + viewBoxHeight, + "preserveAspectRatio": "xMinYMin slice" + }); + return buildCommon.makeSvgSpan(["hide-tail"], [svg], options); +}; +/** + * Make a sqrt image of the given height, + */ + + +var makeSqrtImage = function makeSqrtImage(height, options) { + // Define a newOptions that removes the effect of size changes such as \Huge. + // We don't pick different a height surd for \Huge. For it, we scale up. + var newOptions = options.havingBaseSizing(); // Pick the desired surd glyph from a sequence of surds. + + var delim = traverseSequence("\\surd", height * newOptions.sizeMultiplier, stackLargeDelimiterSequence, newOptions); + var sizeMultiplier = newOptions.sizeMultiplier; // default + // The standard sqrt SVGs each have a 0.04em thick viniculum. + // If Settings.minRuleThickness is larger than that, we add extraViniculum. + + var extraViniculum = Math.max(0, options.minRuleThickness - options.fontMetrics().sqrtRuleThickness); // Create a span containing an SVG image of a sqrt symbol. + + var span; + var spanHeight = 0; + var texHeight = 0; + var viewBoxHeight = 0; + var advanceWidth; // We create viewBoxes with 80 units of "padding" above each surd. + // Then browser rounding error on the parent span height will not + // encroach on the ink of the viniculum. But that padding is not + // included in the TeX-like `height` used for calculation of + // vertical alignment. So texHeight = span.height < span.style.height. + + if (delim.type === "small") { + // Get an SVG that is derived from glyph U+221A in font KaTeX-Main. + // 1000 unit normal glyph height. + viewBoxHeight = 1000 + 1000 * extraViniculum + vbPad; + + if (height < 1.0) { + sizeMultiplier = 1.0; // mimic a \textfont radical + } else if (height < 1.4) { + sizeMultiplier = 0.7; // mimic a \scriptfont radical + } + + spanHeight = (1.0 + extraViniculum + emPad) / sizeMultiplier; + texHeight = (1.00 + extraViniculum) / sizeMultiplier; + span = sqrtSvg("sqrtMain", spanHeight, viewBoxHeight, extraViniculum, options); + span.style.minWidth = "0.853em"; + advanceWidth = 0.833 / sizeMultiplier; // from the font. + } else if (delim.type === "large") { + // These SVGs come from fonts: KaTeX_Size1, _Size2, etc. + viewBoxHeight = (1000 + vbPad) * sizeToMaxHeight[delim.size]; + texHeight = (sizeToMaxHeight[delim.size] + extraViniculum) / sizeMultiplier; + spanHeight = (sizeToMaxHeight[delim.size] + extraViniculum + emPad) / sizeMultiplier; + span = sqrtSvg("sqrtSize" + delim.size, spanHeight, viewBoxHeight, extraViniculum, options); + span.style.minWidth = "1.02em"; + advanceWidth = 1.0 / sizeMultiplier; // 1.0 from the font. + } else { + // Tall sqrt. In TeX, this would be stacked using multiple glyphs. + // We'll use a single SVG to accomplish the same thing. + spanHeight = height + extraViniculum + emPad; + texHeight = height + extraViniculum; + viewBoxHeight = Math.floor(1000 * height + extraViniculum) + vbPad; + span = sqrtSvg("sqrtTall", spanHeight, viewBoxHeight, extraViniculum, options); + span.style.minWidth = "0.742em"; + advanceWidth = 1.056; + } + + span.height = texHeight; + span.style.height = spanHeight + "em"; + return { + span: span, + advanceWidth: advanceWidth, + // Calculate the actual line width. + // This actually should depend on the chosen font -- e.g. \boldmath + // should use the thicker surd symbols from e.g. KaTeX_Main-Bold, and + // have thicker rules. + ruleWidth: (options.fontMetrics().sqrtRuleThickness + extraViniculum) * sizeMultiplier + }; +}; // There are three kinds of delimiters, delimiters that stack when they become +// too large + + +var stackLargeDelimiters = ["(", "\\lparen", ")", "\\rparen", "[", "\\lbrack", "]", "\\rbrack", "\\{", "\\lbrace", "\\}", "\\rbrace", "\\lfloor", "\\rfloor", "\u230A", "\u230B", "\\lceil", "\\rceil", "\u2308", "\u2309", "\\surd"]; // delimiters that always stack + +var stackAlwaysDelimiters = ["\\uparrow", "\\downarrow", "\\updownarrow", "\\Uparrow", "\\Downarrow", "\\Updownarrow", "|", "\\|", "\\vert", "\\Vert", "\\lvert", "\\rvert", "\\lVert", "\\rVert", "\\lgroup", "\\rgroup", "\u27EE", "\u27EF", "\\lmoustache", "\\rmoustache", "\u23B0", "\u23B1"]; // and delimiters that never stack + +var stackNeverDelimiters = ["<", ">", "\\langle", "\\rangle", "/", "\\backslash", "\\lt", "\\gt"]; // Metrics of the different sizes. Found by looking at TeX's output of +// $\bigl| // \Bigl| \biggl| \Biggl| \showlists$ +// Used to create stacked delimiters of appropriate sizes in makeSizedDelim. + +var sizeToMaxHeight = [0, 1.2, 1.8, 2.4, 3.0]; +/** + * Used to create a delimiter of a specific size, where `size` is 1, 2, 3, or 4. + */ + +var makeSizedDelim = function makeSizedDelim(delim, size, options, mode, classes) { + // < and > turn into \langle and \rangle in delimiters + if (delim === "<" || delim === "\\lt" || delim === "\u27E8") { + delim = "\\langle"; + } else if (delim === ">" || delim === "\\gt" || delim === "\u27E9") { + delim = "\\rangle"; + } // Sized delimiters are never centered. + + + if (utils.contains(stackLargeDelimiters, delim) || utils.contains(stackNeverDelimiters, delim)) { + return makeLargeDelim(delim, size, false, options, mode, classes); + } else if (utils.contains(stackAlwaysDelimiters, delim)) { + return makeStackedDelim(delim, sizeToMaxHeight[size], false, options, mode, classes); + } else { + throw new src_ParseError("Illegal delimiter: '" + delim + "'"); + } +}; +/** + * There are three different sequences of delimiter sizes that the delimiters + * follow depending on the kind of delimiter. This is used when creating custom + * sized delimiters to decide whether to create a small, large, or stacked + * delimiter. + * + * In real TeX, these sequences aren't explicitly defined, but are instead + * defined inside the font metrics. Since there are only three sequences that + * are possible for the delimiters that TeX defines, it is easier to just encode + * them explicitly here. + */ + + +// Delimiters that never stack try small delimiters and large delimiters only +var stackNeverDelimiterSequence = [{ + type: "small", + style: src_Style.SCRIPTSCRIPT +}, { + type: "small", + style: src_Style.SCRIPT +}, { + type: "small", + style: src_Style.TEXT +}, { + type: "large", + size: 1 +}, { + type: "large", + size: 2 +}, { + type: "large", + size: 3 +}, { + type: "large", + size: 4 +}]; // Delimiters that always stack try the small delimiters first, then stack + +var stackAlwaysDelimiterSequence = [{ + type: "small", + style: src_Style.SCRIPTSCRIPT +}, { + type: "small", + style: src_Style.SCRIPT +}, { + type: "small", + style: src_Style.TEXT +}, { + type: "stack" +}]; // Delimiters that stack when large try the small and then large delimiters, and +// stack afterwards + +var stackLargeDelimiterSequence = [{ + type: "small", + style: src_Style.SCRIPTSCRIPT +}, { + type: "small", + style: src_Style.SCRIPT +}, { + type: "small", + style: src_Style.TEXT +}, { + type: "large", + size: 1 +}, { + type: "large", + size: 2 +}, { + type: "large", + size: 3 +}, { + type: "large", + size: 4 +}, { + type: "stack" +}]; +/** + * Get the font used in a delimiter based on what kind of delimiter it is. + * TODO(#963) Use more specific font family return type once that is introduced. + */ + +var delimTypeToFont = function delimTypeToFont(type) { + if (type.type === "small") { + return "Main-Regular"; + } else if (type.type === "large") { + return "Size" + type.size + "-Regular"; + } else if (type.type === "stack") { + return "Size4-Regular"; + } else { + throw new Error("Add support for delim type '" + type.type + "' here."); + } +}; +/** + * Traverse a sequence of types of delimiters to decide what kind of delimiter + * should be used to create a delimiter of the given height+depth. + */ + + +var traverseSequence = function traverseSequence(delim, height, sequence, options) { + // Here, we choose the index we should start at in the sequences. In smaller + // sizes (which correspond to larger numbers in style.size) we start earlier + // in the sequence. Thus, scriptscript starts at index 3-3=0, script starts + // at index 3-2=1, text starts at 3-1=2, and display starts at min(2,3-0)=2 + var start = Math.min(2, 3 - options.style.size); + + for (var i = start; i < sequence.length; i++) { + if (sequence[i].type === "stack") { + // This is always the last delimiter, so we just break the loop now. + break; + } + + var metrics = getMetrics(delim, delimTypeToFont(sequence[i]), "math"); + var heightDepth = metrics.height + metrics.depth; // Small delimiters are scaled down versions of the same font, so we + // account for the style change size. + + if (sequence[i].type === "small") { + var newOptions = options.havingBaseStyle(sequence[i].style); + heightDepth *= newOptions.sizeMultiplier; + } // Check if the delimiter at this size works for the given height. + + + if (heightDepth > height) { + return sequence[i]; + } + } // If we reached the end of the sequence, return the last sequence element. + + + return sequence[sequence.length - 1]; +}; +/** + * Make a delimiter of a given height+depth, with optional centering. Here, we + * traverse the sequences, and create a delimiter that the sequence tells us to. + */ + + +var makeCustomSizedDelim = function makeCustomSizedDelim(delim, height, center, options, mode, classes) { + if (delim === "<" || delim === "\\lt" || delim === "\u27E8") { + delim = "\\langle"; + } else if (delim === ">" || delim === "\\gt" || delim === "\u27E9") { + delim = "\\rangle"; + } // Decide what sequence to use + + + var sequence; + + if (utils.contains(stackNeverDelimiters, delim)) { + sequence = stackNeverDelimiterSequence; + } else if (utils.contains(stackLargeDelimiters, delim)) { + sequence = stackLargeDelimiterSequence; + } else { + sequence = stackAlwaysDelimiterSequence; + } // Look through the sequence + + + var delimType = traverseSequence(delim, height, sequence, options); // Get the delimiter from font glyphs. + // Depending on the sequence element we decided on, call the + // appropriate function. + + if (delimType.type === "small") { + return makeSmallDelim(delim, delimType.style, center, options, mode, classes); + } else if (delimType.type === "large") { + return makeLargeDelim(delim, delimType.size, center, options, mode, classes); + } else + /* if (delimType.type === "stack") */ + { + return makeStackedDelim(delim, height, center, options, mode, classes); + } +}; +/** + * Make a delimiter for use with `\left` and `\right`, given a height and depth + * of an expression that the delimiters surround. + */ + + +var makeLeftRightDelim = function makeLeftRightDelim(delim, height, depth, options, mode, classes) { + // We always center \left/\right delimiters, so the axis is always shifted + var axisHeight = options.fontMetrics().axisHeight * options.sizeMultiplier; // Taken from TeX source, tex.web, function make_left_right + + var delimiterFactor = 901; + var delimiterExtend = 5.0 / options.fontMetrics().ptPerEm; + var maxDistFromAxis = Math.max(height - axisHeight, depth + axisHeight); + var totalHeight = Math.max( // In real TeX, calculations are done using integral values which are + // 65536 per pt, or 655360 per em. So, the division here truncates in + // TeX but doesn't here, producing different results. If we wanted to + // exactly match TeX's calculation, we could do + // Math.floor(655360 * maxDistFromAxis / 500) * + // delimiterFactor / 655360 + // (To see the difference, compare + // x^{x^{\left(\rule{0.1em}{0.68em}\right)}} + // in TeX and KaTeX) + maxDistFromAxis / 500 * delimiterFactor, 2 * maxDistFromAxis - delimiterExtend); // Finally, we defer to `makeCustomSizedDelim` with our calculated total + // height + + return makeCustomSizedDelim(delim, totalHeight, true, options, mode, classes); +}; + +/* harmony default export */ var delimiter = ({ + sqrtImage: makeSqrtImage, + sizedDelim: makeSizedDelim, + sizeToMaxHeight: sizeToMaxHeight, + customSizedDelim: makeCustomSizedDelim, + leftRightDelim: makeLeftRightDelim +}); +;// CONCATENATED MODULE: ./src/functions/delimsizing.js + + + + + + + + + +// Extra data needed for the delimiter handler down below +var delimiterSizes = { + "\\bigl": { + mclass: "mopen", + size: 1 + }, + "\\Bigl": { + mclass: "mopen", + size: 2 + }, + "\\biggl": { + mclass: "mopen", + size: 3 + }, + "\\Biggl": { + mclass: "mopen", + size: 4 + }, + "\\bigr": { + mclass: "mclose", + size: 1 + }, + "\\Bigr": { + mclass: "mclose", + size: 2 + }, + "\\biggr": { + mclass: "mclose", + size: 3 + }, + "\\Biggr": { + mclass: "mclose", + size: 4 + }, + "\\bigm": { + mclass: "mrel", + size: 1 + }, + "\\Bigm": { + mclass: "mrel", + size: 2 + }, + "\\biggm": { + mclass: "mrel", + size: 3 + }, + "\\Biggm": { + mclass: "mrel", + size: 4 + }, + "\\big": { + mclass: "mord", + size: 1 + }, + "\\Big": { + mclass: "mord", + size: 2 + }, + "\\bigg": { + mclass: "mord", + size: 3 + }, + "\\Bigg": { + mclass: "mord", + size: 4 + } +}; +var delimiters = ["(", "\\lparen", ")", "\\rparen", "[", "\\lbrack", "]", "\\rbrack", "\\{", "\\lbrace", "\\}", "\\rbrace", "\\lfloor", "\\rfloor", "\u230A", "\u230B", "\\lceil", "\\rceil", "\u2308", "\u2309", "<", ">", "\\langle", "\u27E8", "\\rangle", "\u27E9", "\\lt", "\\gt", "\\lvert", "\\rvert", "\\lVert", "\\rVert", "\\lgroup", "\\rgroup", "\u27EE", "\u27EF", "\\lmoustache", "\\rmoustache", "\u23B0", "\u23B1", "/", "\\backslash", "|", "\\vert", "\\|", "\\Vert", "\\uparrow", "\\Uparrow", "\\downarrow", "\\Downarrow", "\\updownarrow", "\\Updownarrow", "."]; + +// Delimiter functions +function checkDelimiter(delim, context) { + var symDelim = checkSymbolNodeType(delim); + + if (symDelim && utils.contains(delimiters, symDelim.text)) { + return symDelim; + } else if (symDelim) { + throw new src_ParseError("Invalid delimiter '" + symDelim.text + "' after '" + context.funcName + "'", delim); + } else { + throw new src_ParseError("Invalid delimiter type '" + delim.type + "'", delim); + } +} + +defineFunction({ + type: "delimsizing", + names: ["\\bigl", "\\Bigl", "\\biggl", "\\Biggl", "\\bigr", "\\Bigr", "\\biggr", "\\Biggr", "\\bigm", "\\Bigm", "\\biggm", "\\Biggm", "\\big", "\\Big", "\\bigg", "\\Bigg"], + props: { + numArgs: 1, + argTypes: ["primitive"] + }, + handler: function handler(context, args) { + var delim = checkDelimiter(args[0], context); + return { + type: "delimsizing", + mode: context.parser.mode, + size: delimiterSizes[context.funcName].size, + mclass: delimiterSizes[context.funcName].mclass, + delim: delim.text + }; + }, + htmlBuilder: function htmlBuilder(group, options) { + if (group.delim === ".") { + // Empty delimiters still count as elements, even though they don't + // show anything. + return buildCommon.makeSpan([group.mclass]); + } // Use delimiter.sizedDelim to generate the delimiter. + + + return delimiter.sizedDelim(group.delim, group.size, options, group.mode, [group.mclass]); + }, + mathmlBuilder: function mathmlBuilder(group) { + var children = []; + + if (group.delim !== ".") { + children.push(makeText(group.delim, group.mode)); + } + + var node = new mathMLTree.MathNode("mo", children); + + if (group.mclass === "mopen" || group.mclass === "mclose") { + // Only some of the delimsizing functions act as fences, and they + // return "mopen" or "mclose" mclass. + node.setAttribute("fence", "true"); + } else { + // Explicitly disable fencing if it's not a fence, to override the + // defaults. + node.setAttribute("fence", "false"); + } + + node.setAttribute("stretchy", "true"); + node.setAttribute("minsize", delimiter.sizeToMaxHeight[group.size] + "em"); + node.setAttribute("maxsize", delimiter.sizeToMaxHeight[group.size] + "em"); + return node; + } +}); + +function assertParsed(group) { + if (!group.body) { + throw new Error("Bug: The leftright ParseNode wasn't fully parsed."); + } +} + +defineFunction({ + type: "leftright-right", + names: ["\\right"], + props: { + numArgs: 1, + primitive: true + }, + handler: function handler(context, args) { + // \left case below triggers parsing of \right in + // `const right = parser.parseFunction();` + // uses this return value. + var color = context.parser.gullet.macros.get("\\current@color"); + + if (color && typeof color !== "string") { + throw new src_ParseError("\\current@color set to non-string in \\right"); + } + + return { + type: "leftright-right", + mode: context.parser.mode, + delim: checkDelimiter(args[0], context).text, + color: color // undefined if not set via \color + + }; + } +}); +defineFunction({ + type: "leftright", + names: ["\\left"], + props: { + numArgs: 1, + primitive: true + }, + handler: function handler(context, args) { + var delim = checkDelimiter(args[0], context); + var parser = context.parser; // Parse out the implicit body + + ++parser.leftrightDepth; // parseExpression stops before '\\right' + + var body = parser.parseExpression(false); + --parser.leftrightDepth; // Check the next token + + parser.expect("\\right", false); + var right = assertNodeType(parser.parseFunction(), "leftright-right"); + return { + type: "leftright", + mode: parser.mode, + body: body, + left: delim.text, + right: right.delim, + rightColor: right.color + }; + }, + htmlBuilder: function htmlBuilder(group, options) { + assertParsed(group); // Build the inner expression + + var inner = buildExpression(group.body, options, true, ["mopen", "mclose"]); + var innerHeight = 0; + var innerDepth = 0; + var hadMiddle = false; // Calculate its height and depth + + for (var i = 0; i < inner.length; i++) { + // Property `isMiddle` not defined on `span`. See comment in + // "middle"'s htmlBuilder. + // $FlowFixMe + if (inner[i].isMiddle) { + hadMiddle = true; + } else { + innerHeight = Math.max(inner[i].height, innerHeight); + innerDepth = Math.max(inner[i].depth, innerDepth); + } + } // The size of delimiters is the same, regardless of what style we are + // in. Thus, to correctly calculate the size of delimiter we need around + // a group, we scale down the inner size based on the size. + + + innerHeight *= options.sizeMultiplier; + innerDepth *= options.sizeMultiplier; + var leftDelim; + + if (group.left === ".") { + // Empty delimiters in \left and \right make null delimiter spaces. + leftDelim = makeNullDelimiter(options, ["mopen"]); + } else { + // Otherwise, use leftRightDelim to generate the correct sized + // delimiter. + leftDelim = delimiter.leftRightDelim(group.left, innerHeight, innerDepth, options, group.mode, ["mopen"]); + } // Add it to the beginning of the expression + + + inner.unshift(leftDelim); // Handle middle delimiters + + if (hadMiddle) { + for (var _i = 1; _i < inner.length; _i++) { + var middleDelim = inner[_i]; // Property `isMiddle` not defined on `span`. See comment in + // "middle"'s htmlBuilder. + // $FlowFixMe + + var isMiddle = middleDelim.isMiddle; + + if (isMiddle) { + // Apply the options that were active when \middle was called + inner[_i] = delimiter.leftRightDelim(isMiddle.delim, innerHeight, innerDepth, isMiddle.options, group.mode, []); + } + } + } + + var rightDelim; // Same for the right delimiter, but using color specified by \color + + if (group.right === ".") { + rightDelim = makeNullDelimiter(options, ["mclose"]); + } else { + var colorOptions = group.rightColor ? options.withColor(group.rightColor) : options; + rightDelim = delimiter.leftRightDelim(group.right, innerHeight, innerDepth, colorOptions, group.mode, ["mclose"]); + } // Add it to the end of the expression. + + + inner.push(rightDelim); + return buildCommon.makeSpan(["minner"], inner, options); + }, + mathmlBuilder: function mathmlBuilder(group, options) { + assertParsed(group); + var inner = buildMathML_buildExpression(group.body, options); + + if (group.left !== ".") { + var leftNode = new mathMLTree.MathNode("mo", [makeText(group.left, group.mode)]); + leftNode.setAttribute("fence", "true"); + inner.unshift(leftNode); + } + + if (group.right !== ".") { + var rightNode = new mathMLTree.MathNode("mo", [makeText(group.right, group.mode)]); + rightNode.setAttribute("fence", "true"); + + if (group.rightColor) { + rightNode.setAttribute("mathcolor", group.rightColor); + } + + inner.push(rightNode); + } + + return makeRow(inner); + } +}); +defineFunction({ + type: "middle", + names: ["\\middle"], + props: { + numArgs: 1, + primitive: true + }, + handler: function handler(context, args) { + var delim = checkDelimiter(args[0], context); + + if (!context.parser.leftrightDepth) { + throw new src_ParseError("\\middle without preceding \\left", delim); + } + + return { + type: "middle", + mode: context.parser.mode, + delim: delim.text + }; + }, + htmlBuilder: function htmlBuilder(group, options) { + var middleDelim; + + if (group.delim === ".") { + middleDelim = makeNullDelimiter(options, []); + } else { + middleDelim = delimiter.sizedDelim(group.delim, 1, options, group.mode, []); + var isMiddle = { + delim: group.delim, + options: options + }; // Property `isMiddle` not defined on `span`. It is only used in + // this file above. + // TODO: Fix this violation of the `span` type and possibly rename + // things since `isMiddle` sounds like a boolean, but is a struct. + // $FlowFixMe + + middleDelim.isMiddle = isMiddle; + } + + return middleDelim; + }, + mathmlBuilder: function mathmlBuilder(group, options) { + // A Firefox \middle will strech a character vertically only if it + // is in the fence part of the operator dictionary at: + // https://www.w3.org/TR/MathML3/appendixc.html. + // So we need to avoid U+2223 and use plain "|" instead. + var textNode = group.delim === "\\vert" || group.delim === "|" ? makeText("|", "text") : makeText(group.delim, group.mode); + var middleNode = new mathMLTree.MathNode("mo", [textNode]); + middleNode.setAttribute("fence", "true"); // MathML gives 5/18em spacing to each element. + // \middle should get delimiter spacing instead. + + middleNode.setAttribute("lspace", "0.05em"); + middleNode.setAttribute("rspace", "0.05em"); + return middleNode; + } +}); +;// CONCATENATED MODULE: ./src/functions/enclose.js + + + + + + + + + + + + +var enclose_htmlBuilder = function htmlBuilder(group, options) { + // \cancel, \bcancel, \xcancel, \sout, \fbox, \colorbox, \fcolorbox, \phase + // Some groups can return document fragments. Handle those by wrapping + // them in a span. + var inner = buildCommon.wrapFragment(buildGroup(group.body, options), options); + var label = group.label.substr(1); + var scale = options.sizeMultiplier; + var img; + var imgShift = 0; // In the LaTeX cancel package, line geometry is slightly different + // depending on whether the subject is wider than it is tall, or vice versa. + // We don't know the width of a group, so as a proxy, we test if + // the subject is a single character. This captures most of the + // subjects that should get the "tall" treatment. + + var isSingleChar = utils.isCharacterBox(group.body); + + if (label === "sout") { + img = buildCommon.makeSpan(["stretchy", "sout"]); + img.height = options.fontMetrics().defaultRuleThickness / scale; + imgShift = -0.5 * options.fontMetrics().xHeight; + } else if (label === "phase") { + // Set a couple of dimensions from the steinmetz package. + var lineWeight = calculateSize({ + number: 0.6, + unit: "pt" + }, options); + var clearance = calculateSize({ + number: 0.35, + unit: "ex" + }, options); // Prevent size changes like \Huge from affecting line thickness + + var newOptions = options.havingBaseSizing(); + scale = scale / newOptions.sizeMultiplier; + var angleHeight = inner.height + inner.depth + lineWeight + clearance; // Reserve a left pad for the angle. + + inner.style.paddingLeft = angleHeight / 2 + lineWeight + "em"; // Create an SVG + + var viewBoxHeight = Math.floor(1000 * angleHeight * scale); + var path = phasePath(viewBoxHeight); + var svgNode = new SvgNode([new PathNode("phase", path)], { + "width": "400em", + "height": viewBoxHeight / 1000 + "em", + "viewBox": "0 0 400000 " + viewBoxHeight, + "preserveAspectRatio": "xMinYMin slice" + }); // Wrap it in a span with overflow: hidden. + + img = buildCommon.makeSvgSpan(["hide-tail"], [svgNode], options); + img.style.height = angleHeight + "em"; + imgShift = inner.depth + lineWeight + clearance; + } else { + // Add horizontal padding + if (/cancel/.test(label)) { + if (!isSingleChar) { + inner.classes.push("cancel-pad"); + } + } else if (label === "angl") { + inner.classes.push("anglpad"); + } else { + inner.classes.push("boxpad"); + } // Add vertical padding + + + var topPad = 0; + var bottomPad = 0; + var ruleThickness = 0; // ref: cancel package: \advance\totalheight2\p@ % "+2" + + if (/box/.test(label)) { + ruleThickness = Math.max(options.fontMetrics().fboxrule, // default + options.minRuleThickness // User override. + ); + topPad = options.fontMetrics().fboxsep + (label === "colorbox" ? 0 : ruleThickness); + bottomPad = topPad; + } else if (label === "angl") { + ruleThickness = Math.max(options.fontMetrics().defaultRuleThickness, options.minRuleThickness); + topPad = 4 * ruleThickness; // gap = 3 × line, plus the line itself. + + bottomPad = Math.max(0, 0.25 - inner.depth); + } else { + topPad = isSingleChar ? 0.2 : 0; + bottomPad = topPad; + } + + img = stretchy.encloseSpan(inner, label, topPad, bottomPad, options); + + if (/fbox|boxed|fcolorbox/.test(label)) { + img.style.borderStyle = "solid"; + img.style.borderWidth = ruleThickness + "em"; + } else if (label === "angl" && ruleThickness !== 0.049) { + img.style.borderTopWidth = ruleThickness + "em"; + img.style.borderRightWidth = ruleThickness + "em"; + } + + imgShift = inner.depth + bottomPad; + + if (group.backgroundColor) { + img.style.backgroundColor = group.backgroundColor; + + if (group.borderColor) { + img.style.borderColor = group.borderColor; + } + } + } + + var vlist; + + if (group.backgroundColor) { + vlist = buildCommon.makeVList({ + positionType: "individualShift", + children: [// Put the color background behind inner; + { + type: "elem", + elem: img, + shift: imgShift + }, { + type: "elem", + elem: inner, + shift: 0 + }] + }, options); + } else { + var classes = /cancel|phase/.test(label) ? ["svg-align"] : []; + vlist = buildCommon.makeVList({ + positionType: "individualShift", + children: [// Write the \cancel stroke on top of inner. + { + type: "elem", + elem: inner, + shift: 0 + }, { + type: "elem", + elem: img, + shift: imgShift, + wrapperClasses: classes + }] + }, options); + } + + if (/cancel/.test(label)) { + // The cancel package documentation says that cancel lines add their height + // to the expression, but tests show that isn't how it actually works. + vlist.height = inner.height; + vlist.depth = inner.depth; + } + + if (/cancel/.test(label) && !isSingleChar) { + // cancel does not create horiz space for its line extension. + return buildCommon.makeSpan(["mord", "cancel-lap"], [vlist], options); + } else { + return buildCommon.makeSpan(["mord"], [vlist], options); + } +}; + +var enclose_mathmlBuilder = function mathmlBuilder(group, options) { + var fboxsep = 0; + var node = new mathMLTree.MathNode(group.label.indexOf("colorbox") > -1 ? "mpadded" : "menclose", [buildMathML_buildGroup(group.body, options)]); + + switch (group.label) { + case "\\cancel": + node.setAttribute("notation", "updiagonalstrike"); + break; + + case "\\bcancel": + node.setAttribute("notation", "downdiagonalstrike"); + break; + + case "\\phase": + node.setAttribute("notation", "phasorangle"); + break; + + case "\\sout": + node.setAttribute("notation", "horizontalstrike"); + break; + + case "\\fbox": + node.setAttribute("notation", "box"); + break; + + case "\\angl": + node.setAttribute("notation", "actuarial"); + break; + + case "\\fcolorbox": + case "\\colorbox": + // doesn't have a good notation option. So use + // instead. Set some attributes that come included with . + fboxsep = options.fontMetrics().fboxsep * options.fontMetrics().ptPerEm; + node.setAttribute("width", "+" + 2 * fboxsep + "pt"); + node.setAttribute("height", "+" + 2 * fboxsep + "pt"); + node.setAttribute("lspace", fboxsep + "pt"); // + + node.setAttribute("voffset", fboxsep + "pt"); + + if (group.label === "\\fcolorbox") { + var thk = Math.max(options.fontMetrics().fboxrule, // default + options.minRuleThickness // user override + ); + node.setAttribute("style", "border: " + thk + "em solid " + String(group.borderColor)); + } + + break; + + case "\\xcancel": + node.setAttribute("notation", "updiagonalstrike downdiagonalstrike"); + break; + } + + if (group.backgroundColor) { + node.setAttribute("mathbackground", group.backgroundColor); + } + + return node; +}; + +defineFunction({ + type: "enclose", + names: ["\\colorbox"], + props: { + numArgs: 2, + allowedInText: true, + argTypes: ["color", "text"] + }, + handler: function handler(_ref, args, optArgs) { + var parser = _ref.parser, + funcName = _ref.funcName; + var color = assertNodeType(args[0], "color-token").color; + var body = args[1]; + return { + type: "enclose", + mode: parser.mode, + label: funcName, + backgroundColor: color, + body: body + }; + }, + htmlBuilder: enclose_htmlBuilder, + mathmlBuilder: enclose_mathmlBuilder +}); +defineFunction({ + type: "enclose", + names: ["\\fcolorbox"], + props: { + numArgs: 3, + allowedInText: true, + argTypes: ["color", "color", "text"] + }, + handler: function handler(_ref2, args, optArgs) { + var parser = _ref2.parser, + funcName = _ref2.funcName; + var borderColor = assertNodeType(args[0], "color-token").color; + var backgroundColor = assertNodeType(args[1], "color-token").color; + var body = args[2]; + return { + type: "enclose", + mode: parser.mode, + label: funcName, + backgroundColor: backgroundColor, + borderColor: borderColor, + body: body + }; + }, + htmlBuilder: enclose_htmlBuilder, + mathmlBuilder: enclose_mathmlBuilder +}); +defineFunction({ + type: "enclose", + names: ["\\fbox"], + props: { + numArgs: 1, + argTypes: ["hbox"], + allowedInText: true + }, + handler: function handler(_ref3, args) { + var parser = _ref3.parser; + return { + type: "enclose", + mode: parser.mode, + label: "\\fbox", + body: args[0] + }; + } +}); +defineFunction({ + type: "enclose", + names: ["\\cancel", "\\bcancel", "\\xcancel", "\\sout", "\\phase"], + props: { + numArgs: 1 + }, + handler: function handler(_ref4, args) { + var parser = _ref4.parser, + funcName = _ref4.funcName; + var body = args[0]; + return { + type: "enclose", + mode: parser.mode, + label: funcName, + body: body + }; + }, + htmlBuilder: enclose_htmlBuilder, + mathmlBuilder: enclose_mathmlBuilder +}); +defineFunction({ + type: "enclose", + names: ["\\angl"], + props: { + numArgs: 1, + argTypes: ["hbox"], + allowedInText: false + }, + handler: function handler(_ref5, args) { + var parser = _ref5.parser; + return { + type: "enclose", + mode: parser.mode, + label: "\\angl", + body: args[0] + }; + } +}); +;// CONCATENATED MODULE: ./src/defineEnvironment.js + + +/** + * All registered environments. + * `environments.js` exports this same dictionary again and makes it public. + * `Parser.js` requires this dictionary via `environments.js`. + */ +var _environments = {}; +function defineEnvironment(_ref) { + var type = _ref.type, + names = _ref.names, + props = _ref.props, + handler = _ref.handler, + htmlBuilder = _ref.htmlBuilder, + mathmlBuilder = _ref.mathmlBuilder; + // Set default values of environments. + var data = { + type: type, + numArgs: props.numArgs || 0, + allowedInText: false, + numOptionalArgs: 0, + handler: handler + }; + + for (var i = 0; i < names.length; ++i) { + // TODO: The value type of _environments should be a type union of all + // possible `EnvSpec<>` possibilities instead of `EnvSpec<*>`, which is + // an existential type. + _environments[names[i]] = data; + } + + if (htmlBuilder) { + _htmlGroupBuilders[type] = htmlBuilder; + } + + if (mathmlBuilder) { + _mathmlGroupBuilders[type] = mathmlBuilder; + } +} +;// CONCATENATED MODULE: ./src/environments/array.js + + + + + + + + + + + + + + +// Helper functions +function getHLines(parser) { + // Return an array. The array length = number of hlines. + // Each element in the array tells if the line is dashed. + var hlineInfo = []; + parser.consumeSpaces(); + var nxt = parser.fetch().text; + + while (nxt === "\\hline" || nxt === "\\hdashline") { + parser.consume(); + hlineInfo.push(nxt === "\\hdashline"); + parser.consumeSpaces(); + nxt = parser.fetch().text; + } + + return hlineInfo; +} + +var validateAmsEnvironmentContext = function validateAmsEnvironmentContext(context) { + var settings = context.parser.settings; + + if (!settings.displayMode) { + throw new src_ParseError("{" + context.envName + "} can be used only in" + " display mode."); + } +}; +/** + * Parse the body of the environment, with rows delimited by \\ and + * columns delimited by &, and create a nested list in row-major order + * with one group per cell. If given an optional argument style + * ("text", "display", etc.), then each cell is cast into that style. + */ + + +function parseArray(parser, _ref, style) { + var hskipBeforeAndAfter = _ref.hskipBeforeAndAfter, + addJot = _ref.addJot, + cols = _ref.cols, + arraystretch = _ref.arraystretch, + colSeparationType = _ref.colSeparationType, + addEqnNum = _ref.addEqnNum, + singleRow = _ref.singleRow, + emptySingleRow = _ref.emptySingleRow, + maxNumCols = _ref.maxNumCols, + leqno = _ref.leqno; + parser.gullet.beginGroup(); + + if (!singleRow) { + // \cr is equivalent to \\ without the optional size argument (see below) + // TODO: provide helpful error when \cr is used outside array environment + parser.gullet.macros.set("\\cr", "\\\\\\relax"); + } // Get current arraystretch if it's not set by the environment + + + if (!arraystretch) { + var stretch = parser.gullet.expandMacroAsText("\\arraystretch"); + + if (stretch == null) { + // Default \arraystretch from lttab.dtx + arraystretch = 1; + } else { + arraystretch = parseFloat(stretch); + + if (!arraystretch || arraystretch < 0) { + throw new src_ParseError("Invalid \\arraystretch: " + stretch); + } + } + } // Start group for first cell + + + parser.gullet.beginGroup(); + var row = []; + var body = [row]; + var rowGaps = []; + var hLinesBeforeRow = []; // Test for \hline at the top of the array. + + hLinesBeforeRow.push(getHLines(parser)); + + while (true) { + // eslint-disable-line no-constant-condition + // Parse each cell in its own group (namespace) + var cell = parser.parseExpression(false, singleRow ? "\\end" : "\\\\"); + parser.gullet.endGroup(); + parser.gullet.beginGroup(); + cell = { + type: "ordgroup", + mode: parser.mode, + body: cell + }; + + if (style) { + cell = { + type: "styling", + mode: parser.mode, + style: style, + body: [cell] + }; + } + + row.push(cell); + var next = parser.fetch().text; + + if (next === "&") { + if (maxNumCols && row.length === maxNumCols) { + if (singleRow || colSeparationType) { + // {equation} or {split} + throw new src_ParseError("Too many tab characters: &", parser.nextToken); + } else { + // {array} environment + parser.settings.reportNonstrict("textEnv", "Too few columns " + "specified in the {array} column argument."); + } + } + + parser.consume(); + } else if (next === "\\end") { + // Arrays terminate newlines with `\crcr` which consumes a `\cr` if + // the last line is empty. However, AMS environments keep the + // empty row if it's the only one. + // NOTE: Currently, `cell` is the last item added into `row`. + if (row.length === 1 && cell.type === "styling" && cell.body[0].body.length === 0 && (body.length > 1 || !emptySingleRow)) { + body.pop(); + } + + if (hLinesBeforeRow.length < body.length + 1) { + hLinesBeforeRow.push([]); + } + + break; + } else if (next === "\\\\") { + parser.consume(); + var size = void 0; // \def\Let@{\let\\\math@cr} + // \def\math@cr{...\math@cr@} + // \def\math@cr@{\new@ifnextchar[\math@cr@@{\math@cr@@[\z@]}} + // \def\math@cr@@[#1]{...\math@cr@@@...} + // \def\math@cr@@@{\cr} + + if (parser.gullet.future().text !== " ") { + size = parser.parseSizeGroup(true); + } + + rowGaps.push(size ? size.value : null); // check for \hline(s) following the row separator + + hLinesBeforeRow.push(getHLines(parser)); + row = []; + body.push(row); + } else { + throw new src_ParseError("Expected & or \\\\ or \\cr or \\end", parser.nextToken); + } + } // End cell group + + + parser.gullet.endGroup(); // End array group defining \cr + + parser.gullet.endGroup(); + return { + type: "array", + mode: parser.mode, + addJot: addJot, + arraystretch: arraystretch, + body: body, + cols: cols, + rowGaps: rowGaps, + hskipBeforeAndAfter: hskipBeforeAndAfter, + hLinesBeforeRow: hLinesBeforeRow, + colSeparationType: colSeparationType, + addEqnNum: addEqnNum, + leqno: leqno + }; +} // Decides on a style for cells in an array according to whether the given +// environment name starts with the letter 'd'. + + +function dCellStyle(envName) { + if (envName.substr(0, 1) === "d") { + return "display"; + } else { + return "text"; + } +} + +var array_htmlBuilder = function htmlBuilder(group, options) { + var r; + var c; + var nr = group.body.length; + var hLinesBeforeRow = group.hLinesBeforeRow; + var nc = 0; + var body = new Array(nr); + var hlines = []; + var ruleThickness = Math.max( // From LaTeX \showthe\arrayrulewidth. Equals 0.04 em. + options.fontMetrics().arrayRuleWidth, options.minRuleThickness // User override. + ); // Horizontal spacing + + var pt = 1 / options.fontMetrics().ptPerEm; + var arraycolsep = 5 * pt; // default value, i.e. \arraycolsep in article.cls + + if (group.colSeparationType && group.colSeparationType === "small") { + // We're in a {smallmatrix}. Default column space is \thickspace, + // i.e. 5/18em = 0.2778em, per amsmath.dtx for {smallmatrix}. + // But that needs adjustment because LaTeX applies \scriptstyle to the + // entire array, including the colspace, but this function applies + // \scriptstyle only inside each element. + var localMultiplier = options.havingStyle(src_Style.SCRIPT).sizeMultiplier; + arraycolsep = 0.2778 * (localMultiplier / options.sizeMultiplier); + } // Vertical spacing + + + var baselineskip = group.colSeparationType === "CD" ? calculateSize({ + number: 3, + unit: "ex" + }, options) : 12 * pt; // see size10.clo + // Default \jot from ltmath.dtx + // TODO(edemaine): allow overriding \jot via \setlength (#687) + + var jot = 3 * pt; + var arrayskip = group.arraystretch * baselineskip; + var arstrutHeight = 0.7 * arrayskip; // \strutbox in ltfsstrc.dtx and + + var arstrutDepth = 0.3 * arrayskip; // \@arstrutbox in lttab.dtx + + var totalHeight = 0; // Set a position for \hline(s) at the top of the array, if any. + + function setHLinePos(hlinesInGap) { + for (var i = 0; i < hlinesInGap.length; ++i) { + if (i > 0) { + totalHeight += 0.25; + } + + hlines.push({ + pos: totalHeight, + isDashed: hlinesInGap[i] + }); + } + } + + setHLinePos(hLinesBeforeRow[0]); + + for (r = 0; r < group.body.length; ++r) { + var inrow = group.body[r]; + var height = arstrutHeight; // \@array adds an \@arstrut + + var depth = arstrutDepth; // to each tow (via the template) + + if (nc < inrow.length) { + nc = inrow.length; + } + + var outrow = new Array(inrow.length); + + for (c = 0; c < inrow.length; ++c) { + var elt = buildGroup(inrow[c], options); + + if (depth < elt.depth) { + depth = elt.depth; + } + + if (height < elt.height) { + height = elt.height; + } + + outrow[c] = elt; + } + + var rowGap = group.rowGaps[r]; + var gap = 0; + + if (rowGap) { + gap = calculateSize(rowGap, options); + + if (gap > 0) { + // \@argarraycr + gap += arstrutDepth; + + if (depth < gap) { + depth = gap; // \@xargarraycr + } + + gap = 0; + } + } // In AMS multiline environments such as aligned and gathered, rows + // correspond to lines that have additional \jot added to the + // \baselineskip via \openup. + + + if (group.addJot) { + depth += jot; + } + + outrow.height = height; + outrow.depth = depth; + totalHeight += height; + outrow.pos = totalHeight; + totalHeight += depth + gap; // \@yargarraycr + + body[r] = outrow; // Set a position for \hline(s), if any. + + setHLinePos(hLinesBeforeRow[r + 1]); + } + + var offset = totalHeight / 2 + options.fontMetrics().axisHeight; + var colDescriptions = group.cols || []; + var cols = []; + var colSep; + var colDescrNum; + var eqnNumSpans = []; + + if (group.addEqnNum) { + // An environment with automatic equation numbers. + // Create node(s) that will trigger CSS counter increment. + for (r = 0; r < nr; ++r) { + var rw = body[r]; + var shift = rw.pos - offset; + var eqnTag = buildCommon.makeSpan(["eqn-num"], [], options); + eqnTag.depth = rw.depth; + eqnTag.height = rw.height; + eqnNumSpans.push({ + type: "elem", + elem: eqnTag, + shift: shift + }); + } + } + + for (c = 0, colDescrNum = 0; // Continue while either there are more columns or more column + // descriptions, so trailing separators don't get lost. + c < nc || colDescrNum < colDescriptions.length; ++c, ++colDescrNum) { + var colDescr = colDescriptions[colDescrNum] || {}; + var firstSeparator = true; + + while (colDescr.type === "separator") { + // If there is more than one separator in a row, add a space + // between them. + if (!firstSeparator) { + colSep = buildCommon.makeSpan(["arraycolsep"], []); + colSep.style.width = options.fontMetrics().doubleRuleSep + "em"; + cols.push(colSep); + } + + if (colDescr.separator === "|" || colDescr.separator === ":") { + var lineType = colDescr.separator === "|" ? "solid" : "dashed"; + var separator = buildCommon.makeSpan(["vertical-separator"], [], options); + separator.style.height = totalHeight + "em"; + separator.style.borderRightWidth = ruleThickness + "em"; + separator.style.borderRightStyle = lineType; + separator.style.margin = "0 -" + ruleThickness / 2 + "em"; + separator.style.verticalAlign = -(totalHeight - offset) + "em"; + cols.push(separator); + } else { + throw new src_ParseError("Invalid separator type: " + colDescr.separator); + } + + colDescrNum++; + colDescr = colDescriptions[colDescrNum] || {}; + firstSeparator = false; + } + + if (c >= nc) { + continue; + } + + var sepwidth = void 0; + + if (c > 0 || group.hskipBeforeAndAfter) { + sepwidth = utils.deflt(colDescr.pregap, arraycolsep); + + if (sepwidth !== 0) { + colSep = buildCommon.makeSpan(["arraycolsep"], []); + colSep.style.width = sepwidth + "em"; + cols.push(colSep); + } + } + + var col = []; + + for (r = 0; r < nr; ++r) { + var row = body[r]; + var elem = row[c]; + + if (!elem) { + continue; + } + + var _shift = row.pos - offset; + + elem.depth = row.depth; + elem.height = row.height; + col.push({ + type: "elem", + elem: elem, + shift: _shift + }); + } + + col = buildCommon.makeVList({ + positionType: "individualShift", + children: col + }, options); + col = buildCommon.makeSpan(["col-align-" + (colDescr.align || "c")], [col]); + cols.push(col); + + if (c < nc - 1 || group.hskipBeforeAndAfter) { + sepwidth = utils.deflt(colDescr.postgap, arraycolsep); + + if (sepwidth !== 0) { + colSep = buildCommon.makeSpan(["arraycolsep"], []); + colSep.style.width = sepwidth + "em"; + cols.push(colSep); + } + } + } + + body = buildCommon.makeSpan(["mtable"], cols); // Add \hline(s), if any. + + if (hlines.length > 0) { + var line = buildCommon.makeLineSpan("hline", options, ruleThickness); + var dashes = buildCommon.makeLineSpan("hdashline", options, ruleThickness); + var vListElems = [{ + type: "elem", + elem: body, + shift: 0 + }]; + + while (hlines.length > 0) { + var hline = hlines.pop(); + var lineShift = hline.pos - offset; + + if (hline.isDashed) { + vListElems.push({ + type: "elem", + elem: dashes, + shift: lineShift + }); + } else { + vListElems.push({ + type: "elem", + elem: line, + shift: lineShift + }); + } + } + + body = buildCommon.makeVList({ + positionType: "individualShift", + children: vListElems + }, options); + } + + if (!group.addEqnNum) { + return buildCommon.makeSpan(["mord"], [body], options); + } else { + var eqnNumCol = buildCommon.makeVList({ + positionType: "individualShift", + children: eqnNumSpans + }, options); + eqnNumCol = buildCommon.makeSpan(["tag"], [eqnNumCol], options); + return buildCommon.makeFragment([body, eqnNumCol]); + } +}; + +var alignMap = { + c: "center ", + l: "left ", + r: "right " +}; + +var array_mathmlBuilder = function mathmlBuilder(group, options) { + var tbl = []; + var glue = new mathMLTree.MathNode("mtd", [], ["mtr-glue"]); + var tag = new mathMLTree.MathNode("mtd", [], ["mml-eqn-num"]); + + for (var i = 0; i < group.body.length; i++) { + var rw = group.body[i]; + var row = []; + + for (var j = 0; j < rw.length; j++) { + row.push(new mathMLTree.MathNode("mtd", [buildMathML_buildGroup(rw[j], options)])); + } + + if (group.addEqnNum) { + row.unshift(glue); + row.push(glue); + + if (group.leqno) { + row.unshift(tag); + } else { + row.push(tag); + } + } + + tbl.push(new mathMLTree.MathNode("mtr", row)); + } + + var table = new mathMLTree.MathNode("mtable", tbl); // Set column alignment, row spacing, column spacing, and + // array lines by setting attributes on the table element. + // Set the row spacing. In MathML, we specify a gap distance. + // We do not use rowGap[] because MathML automatically increases + // cell height with the height/depth of the element content. + // LaTeX \arraystretch multiplies the row baseline-to-baseline distance. + // We simulate this by adding (arraystretch - 1)em to the gap. This + // does a reasonable job of adjusting arrays containing 1 em tall content. + // The 0.16 and 0.09 values are found emprically. They produce an array + // similar to LaTeX and in which content does not interfere with \hines. + + var gap = group.arraystretch === 0.5 ? 0.1 // {smallmatrix}, {subarray} + : 0.16 + group.arraystretch - 1 + (group.addJot ? 0.09 : 0); + table.setAttribute("rowspacing", gap.toFixed(4) + "em"); // MathML table lines go only between cells. + // To place a line on an edge we'll use , if necessary. + + var menclose = ""; + var align = ""; + + if (group.cols && group.cols.length > 0) { + // Find column alignment, column spacing, and vertical lines. + var cols = group.cols; + var columnLines = ""; + var prevTypeWasAlign = false; + var iStart = 0; + var iEnd = cols.length; + + if (cols[0].type === "separator") { + menclose += "top "; + iStart = 1; + } + + if (cols[cols.length - 1].type === "separator") { + menclose += "bottom "; + iEnd -= 1; + } + + for (var _i = iStart; _i < iEnd; _i++) { + if (cols[_i].type === "align") { + align += alignMap[cols[_i].align]; + + if (prevTypeWasAlign) { + columnLines += "none "; + } + + prevTypeWasAlign = true; + } else if (cols[_i].type === "separator") { + // MathML accepts only single lines between cells. + // So we read only the first of consecutive separators. + if (prevTypeWasAlign) { + columnLines += cols[_i].separator === "|" ? "solid " : "dashed "; + prevTypeWasAlign = false; + } + } + } + + table.setAttribute("columnalign", align.trim()); + + if (/[sd]/.test(columnLines)) { + table.setAttribute("columnlines", columnLines.trim()); + } + } // Set column spacing. + + + if (group.colSeparationType === "align") { + var _cols = group.cols || []; + + var spacing = ""; + + for (var _i2 = 1; _i2 < _cols.length; _i2++) { + spacing += _i2 % 2 ? "0em " : "1em "; + } + + table.setAttribute("columnspacing", spacing.trim()); + } else if (group.colSeparationType === "alignat" || group.colSeparationType === "gather") { + table.setAttribute("columnspacing", "0em"); + } else if (group.colSeparationType === "small") { + table.setAttribute("columnspacing", "0.2778em"); + } else if (group.colSeparationType === "CD") { + table.setAttribute("columnspacing", "0.5em"); + } else { + table.setAttribute("columnspacing", "1em"); + } // Address \hline and \hdashline + + + var rowLines = ""; + var hlines = group.hLinesBeforeRow; + menclose += hlines[0].length > 0 ? "left " : ""; + menclose += hlines[hlines.length - 1].length > 0 ? "right " : ""; + + for (var _i3 = 1; _i3 < hlines.length - 1; _i3++) { + rowLines += hlines[_i3].length === 0 ? "none " // MathML accepts only a single line between rows. Read one element. + : hlines[_i3][0] ? "dashed " : "solid "; + } + + if (/[sd]/.test(rowLines)) { + table.setAttribute("rowlines", rowLines.trim()); + } + + if (menclose !== "") { + table = new mathMLTree.MathNode("menclose", [table]); + table.setAttribute("notation", menclose.trim()); + } + + if (group.arraystretch && group.arraystretch < 1) { + // A small array. Wrap in scriptstyle so row gap is not too large. + table = new mathMLTree.MathNode("mstyle", [table]); + table.setAttribute("scriptlevel", "1"); + } + + return table; +}; // Convenience function for align, align*, aligned, alignat, alignat*, alignedat. + + +var alignedHandler = function alignedHandler(context, args) { + if (context.envName.indexOf("ed") === -1) { + validateAmsEnvironmentContext(context); + } + + var cols = []; + var separationType = context.envName.indexOf("at") > -1 ? "alignat" : "align"; + var res = parseArray(context.parser, { + cols: cols, + addJot: true, + addEqnNum: context.envName === "align" || context.envName === "alignat", + emptySingleRow: true, + colSeparationType: separationType, + maxNumCols: context.envName === "split" ? 2 : undefined, + leqno: context.parser.settings.leqno + }, "display"); // Determining number of columns. + // 1. If the first argument is given, we use it as a number of columns, + // and makes sure that each row doesn't exceed that number. + // 2. Otherwise, just count number of columns = maximum number + // of cells in each row ("aligned" mode -- isAligned will be true). + // + // At the same time, prepend empty group {} at beginning of every second + // cell in each row (starting with second cell) so that operators become + // binary. This behavior is implemented in amsmath's \start@aligned. + + var numMaths; + var numCols = 0; + var emptyGroup = { + type: "ordgroup", + mode: context.mode, + body: [] + }; + + if (args[0] && args[0].type === "ordgroup") { + var arg0 = ""; + + for (var i = 0; i < args[0].body.length; i++) { + var textord = assertNodeType(args[0].body[i], "textord"); + arg0 += textord.text; + } + + numMaths = Number(arg0); + numCols = numMaths * 2; + } + + var isAligned = !numCols; + res.body.forEach(function (row) { + for (var _i4 = 1; _i4 < row.length; _i4 += 2) { + // Modify ordgroup node within styling node + var styling = assertNodeType(row[_i4], "styling"); + var ordgroup = assertNodeType(styling.body[0], "ordgroup"); + ordgroup.body.unshift(emptyGroup); + } + + if (!isAligned) { + // Case 1 + var curMaths = row.length / 2; + + if (numMaths < curMaths) { + throw new src_ParseError("Too many math in a row: " + ("expected " + numMaths + ", but got " + curMaths), row[0]); + } + } else if (numCols < row.length) { + // Case 2 + numCols = row.length; + } + }); // Adjusting alignment. + // In aligned mode, we add one \qquad between columns; + // otherwise we add nothing. + + for (var _i5 = 0; _i5 < numCols; ++_i5) { + var align = "r"; + var pregap = 0; + + if (_i5 % 2 === 1) { + align = "l"; + } else if (_i5 > 0 && isAligned) { + // "aligned" mode. + pregap = 1; // add one \quad + } + + cols[_i5] = { + type: "align", + align: align, + pregap: pregap, + postgap: 0 + }; + } + + res.colSeparationType = isAligned ? "align" : "alignat"; + return res; +}; // Arrays are part of LaTeX, defined in lttab.dtx so its documentation +// is part of the source2e.pdf file of LaTeX2e source documentation. +// {darray} is an {array} environment where cells are set in \displaystyle, +// as defined in nccmath.sty. + + +defineEnvironment({ + type: "array", + names: ["array", "darray"], + props: { + numArgs: 1 + }, + handler: function handler(context, args) { + // Since no types are specified above, the two possibilities are + // - The argument is wrapped in {} or [], in which case Parser's + // parseGroup() returns an "ordgroup" wrapping some symbol node. + // - The argument is a bare symbol node. + var symNode = checkSymbolNodeType(args[0]); + var colalign = symNode ? [args[0]] : assertNodeType(args[0], "ordgroup").body; + var cols = colalign.map(function (nde) { + var node = assertSymbolNodeType(nde); + var ca = node.text; + + if ("lcr".indexOf(ca) !== -1) { + return { + type: "align", + align: ca + }; + } else if (ca === "|") { + return { + type: "separator", + separator: "|" + }; + } else if (ca === ":") { + return { + type: "separator", + separator: ":" + }; + } + + throw new src_ParseError("Unknown column alignment: " + ca, nde); + }); + var res = { + cols: cols, + hskipBeforeAndAfter: true, + // \@preamble in lttab.dtx + maxNumCols: cols.length + }; + return parseArray(context.parser, res, dCellStyle(context.envName)); + }, + htmlBuilder: array_htmlBuilder, + mathmlBuilder: array_mathmlBuilder +}); // The matrix environments of amsmath builds on the array environment +// of LaTeX, which is discussed above. +// The mathtools package adds starred versions of the same environments. +// These have an optional argument to choose left|center|right justification. + +defineEnvironment({ + type: "array", + names: ["matrix", "pmatrix", "bmatrix", "Bmatrix", "vmatrix", "Vmatrix", "matrix*", "pmatrix*", "bmatrix*", "Bmatrix*", "vmatrix*", "Vmatrix*"], + props: { + numArgs: 0 + }, + handler: function handler(context) { + var delimiters = { + "matrix": null, + "pmatrix": ["(", ")"], + "bmatrix": ["[", "]"], + "Bmatrix": ["\\{", "\\}"], + "vmatrix": ["|", "|"], + "Vmatrix": ["\\Vert", "\\Vert"] + }[context.envName.replace("*", "")]; // \hskip -\arraycolsep in amsmath + + var colAlign = "c"; + var payload = { + hskipBeforeAndAfter: false, + cols: [{ + type: "align", + align: colAlign + }] + }; + + if (context.envName.charAt(context.envName.length - 1) === "*") { + // It's one of the mathtools starred functions. + // Parse the optional alignment argument. + var parser = context.parser; + parser.consumeSpaces(); + + if (parser.fetch().text === "[") { + parser.consume(); + parser.consumeSpaces(); + colAlign = parser.fetch().text; + + if ("lcr".indexOf(colAlign) === -1) { + throw new src_ParseError("Expected l or c or r", parser.nextToken); + } + + parser.consume(); + parser.consumeSpaces(); + parser.expect("]"); + parser.consume(); + payload.cols = [{ + type: "align", + align: colAlign + }]; + } + } + + var res = parseArray(context.parser, payload, dCellStyle(context.envName)); // Populate cols with the correct number of column alignment specs. + + var numCols = Math.max.apply(Math, [0].concat(res.body.map(function (row) { + return row.length; + }))); + res.cols = new Array(numCols).fill({ + type: "align", + align: colAlign + }); + return delimiters ? { + type: "leftright", + mode: context.mode, + body: [res], + left: delimiters[0], + right: delimiters[1], + rightColor: undefined // \right uninfluenced by \color in array + + } : res; + }, + htmlBuilder: array_htmlBuilder, + mathmlBuilder: array_mathmlBuilder +}); +defineEnvironment({ + type: "array", + names: ["smallmatrix"], + props: { + numArgs: 0 + }, + handler: function handler(context) { + var payload = { + arraystretch: 0.5 + }; + var res = parseArray(context.parser, payload, "script"); + res.colSeparationType = "small"; + return res; + }, + htmlBuilder: array_htmlBuilder, + mathmlBuilder: array_mathmlBuilder +}); +defineEnvironment({ + type: "array", + names: ["subarray"], + props: { + numArgs: 1 + }, + handler: function handler(context, args) { + // Parsing of {subarray} is similar to {array} + var symNode = checkSymbolNodeType(args[0]); + var colalign = symNode ? [args[0]] : assertNodeType(args[0], "ordgroup").body; + var cols = colalign.map(function (nde) { + var node = assertSymbolNodeType(nde); + var ca = node.text; // {subarray} only recognizes "l" & "c" + + if ("lc".indexOf(ca) !== -1) { + return { + type: "align", + align: ca + }; + } + + throw new src_ParseError("Unknown column alignment: " + ca, nde); + }); + + if (cols.length > 1) { + throw new src_ParseError("{subarray} can contain only one column"); + } + + var res = { + cols: cols, + hskipBeforeAndAfter: false, + arraystretch: 0.5 + }; + res = parseArray(context.parser, res, "script"); + + if (res.body.length > 0 && res.body[0].length > 1) { + throw new src_ParseError("{subarray} can contain only one column"); + } + + return res; + }, + htmlBuilder: array_htmlBuilder, + mathmlBuilder: array_mathmlBuilder +}); // A cases environment (in amsmath.sty) is almost equivalent to +// \def\arraystretch{1.2}% +// \left\{\begin{array}{@{}l@{\quad}l@{}} … \end{array}\right. +// {dcases} is a {cases} environment where cells are set in \displaystyle, +// as defined in mathtools.sty. +// {rcases} is another mathtools environment. It's brace is on the right side. + +defineEnvironment({ + type: "array", + names: ["cases", "dcases", "rcases", "drcases"], + props: { + numArgs: 0 + }, + handler: function handler(context) { + var payload = { + arraystretch: 1.2, + cols: [{ + type: "align", + align: "l", + pregap: 0, + // TODO(kevinb) get the current style. + // For now we use the metrics for TEXT style which is what we were + // doing before. Before attempting to get the current style we + // should look at TeX's behavior especially for \over and matrices. + postgap: 1.0 + /* 1em quad */ + + }, { + type: "align", + align: "l", + pregap: 0, + postgap: 0 + }] + }; + var res = parseArray(context.parser, payload, dCellStyle(context.envName)); + return { + type: "leftright", + mode: context.mode, + body: [res], + left: context.envName.indexOf("r") > -1 ? "." : "\\{", + right: context.envName.indexOf("r") > -1 ? "\\}" : ".", + rightColor: undefined + }; + }, + htmlBuilder: array_htmlBuilder, + mathmlBuilder: array_mathmlBuilder +}); // In the align environment, one uses ampersands, &, to specify number of +// columns in each row, and to locate spacing between each column. +// align gets automatic numbering. align* and aligned do not. +// The alignedat environment can be used in math mode. +// Note that we assume \nomallineskiplimit to be zero, +// so that \strut@ is the same as \strut. + +defineEnvironment({ + type: "array", + names: ["align", "align*", "aligned", "split"], + props: { + numArgs: 0 + }, + handler: alignedHandler, + htmlBuilder: array_htmlBuilder, + mathmlBuilder: array_mathmlBuilder +}); // A gathered environment is like an array environment with one centered +// column, but where rows are considered lines so get \jot line spacing +// and contents are set in \displaystyle. + +defineEnvironment({ + type: "array", + names: ["gathered", "gather", "gather*"], + props: { + numArgs: 0 + }, + handler: function handler(context) { + if (utils.contains(["gather", "gather*"], context.envName)) { + validateAmsEnvironmentContext(context); + } + + var res = { + cols: [{ + type: "align", + align: "c" + }], + addJot: true, + colSeparationType: "gather", + addEqnNum: context.envName === "gather", + emptySingleRow: true, + leqno: context.parser.settings.leqno + }; + return parseArray(context.parser, res, "display"); + }, + htmlBuilder: array_htmlBuilder, + mathmlBuilder: array_mathmlBuilder +}); // alignat environment is like an align environment, but one must explicitly +// specify maximum number of columns in each row, and can adjust spacing between +// each columns. + +defineEnvironment({ + type: "array", + names: ["alignat", "alignat*", "alignedat"], + props: { + numArgs: 1 + }, + handler: alignedHandler, + htmlBuilder: array_htmlBuilder, + mathmlBuilder: array_mathmlBuilder +}); +defineEnvironment({ + type: "array", + names: ["equation", "equation*"], + props: { + numArgs: 0 + }, + handler: function handler(context) { + validateAmsEnvironmentContext(context); + var res = { + addEqnNum: context.envName === "equation", + emptySingleRow: true, + singleRow: true, + maxNumCols: 1, + leqno: context.parser.settings.leqno + }; + return parseArray(context.parser, res, "display"); + }, + htmlBuilder: array_htmlBuilder, + mathmlBuilder: array_mathmlBuilder +}); +defineEnvironment({ + type: "array", + names: ["CD"], + props: { + numArgs: 0 + }, + handler: function handler(context) { + validateAmsEnvironmentContext(context); + return parseCD(context.parser); + }, + htmlBuilder: array_htmlBuilder, + mathmlBuilder: array_mathmlBuilder +}); // Catch \hline outside array environment + +defineFunction({ + type: "text", + // Doesn't matter what this is. + names: ["\\hline", "\\hdashline"], + props: { + numArgs: 0, + allowedInText: true, + allowedInMath: true + }, + handler: function handler(context, args) { + throw new src_ParseError(context.funcName + " valid only within array environment"); + } +}); +;// CONCATENATED MODULE: ./src/environments.js + +var environments = _environments; +/* harmony default export */ var src_environments = (environments); // All environment definitions should be imported below + + +;// CONCATENATED MODULE: ./src/functions/environment.js + + + + // Environment delimiters. HTML/MathML rendering is defined in the corresponding +// defineEnvironment definitions. + +defineFunction({ + type: "environment", + names: ["\\begin", "\\end"], + props: { + numArgs: 1, + argTypes: ["text"] + }, + handler: function handler(_ref, args) { + var parser = _ref.parser, + funcName = _ref.funcName; + var nameGroup = args[0]; + + if (nameGroup.type !== "ordgroup") { + throw new src_ParseError("Invalid environment name", nameGroup); + } + + var envName = ""; + + for (var i = 0; i < nameGroup.body.length; ++i) { + envName += assertNodeType(nameGroup.body[i], "textord").text; + } + + if (funcName === "\\begin") { + // begin...end is similar to left...right + if (!src_environments.hasOwnProperty(envName)) { + throw new src_ParseError("No such environment: " + envName, nameGroup); + } // Build the environment object. Arguments and other information will + // be made available to the begin and end methods using properties. + + + var env = src_environments[envName]; + + var _parser$parseArgument = parser.parseArguments("\\begin{" + envName + "}", env), + _args = _parser$parseArgument.args, + optArgs = _parser$parseArgument.optArgs; + + var context = { + mode: parser.mode, + envName: envName, + parser: parser + }; + var result = env.handler(context, _args, optArgs); + parser.expect("\\end", false); + var endNameToken = parser.nextToken; + var end = assertNodeType(parser.parseFunction(), "environment"); + + if (end.name !== envName) { + throw new src_ParseError("Mismatch: \\begin{" + envName + "} matched by \\end{" + end.name + "}", endNameToken); + } // $FlowFixMe, "environment" handler returns an environment ParseNode + + + return result; + } + + return { + type: "environment", + mode: parser.mode, + name: envName, + nameGroup: nameGroup + }; + } +}); +;// CONCATENATED MODULE: ./src/functions/mclass.js + + + + + + +var mclass_makeSpan = buildCommon.makeSpan; + +function mclass_htmlBuilder(group, options) { + var elements = buildExpression(group.body, options, true); + return mclass_makeSpan([group.mclass], elements, options); +} + +function mclass_mathmlBuilder(group, options) { + var node; + var inner = buildMathML_buildExpression(group.body, options); + + if (group.mclass === "minner") { + return mathMLTree.newDocumentFragment(inner); + } else if (group.mclass === "mord") { + if (group.isCharacterBox) { + node = inner[0]; + node.type = "mi"; + } else { + node = new mathMLTree.MathNode("mi", inner); + } + } else { + if (group.isCharacterBox) { + node = inner[0]; + node.type = "mo"; + } else { + node = new mathMLTree.MathNode("mo", inner); + } // Set spacing based on what is the most likely adjacent atom type. + // See TeXbook p170. + + + if (group.mclass === "mbin") { + node.attributes.lspace = "0.22em"; // medium space + + node.attributes.rspace = "0.22em"; + } else if (group.mclass === "mpunct") { + node.attributes.lspace = "0em"; + node.attributes.rspace = "0.17em"; // thinspace + } else if (group.mclass === "mopen" || group.mclass === "mclose") { + node.attributes.lspace = "0em"; + node.attributes.rspace = "0em"; + } // MathML default space is 5/18 em, so needs no action. + // Ref: https://developer.mozilla.org/en-US/docs/Web/MathML/Element/mo + + } + + return node; +} // Math class commands except \mathop + + +defineFunction({ + type: "mclass", + names: ["\\mathord", "\\mathbin", "\\mathrel", "\\mathopen", "\\mathclose", "\\mathpunct", "\\mathinner"], + props: { + numArgs: 1, + primitive: true + }, + handler: function handler(_ref, args) { + var parser = _ref.parser, + funcName = _ref.funcName; + var body = args[0]; + return { + type: "mclass", + mode: parser.mode, + mclass: "m" + funcName.substr(5), + // TODO(kevinb): don't prefix with 'm' + body: ordargument(body), + isCharacterBox: utils.isCharacterBox(body) + }; + }, + htmlBuilder: mclass_htmlBuilder, + mathmlBuilder: mclass_mathmlBuilder +}); +var binrelClass = function binrelClass(arg) { + // \binrel@ spacing varies with (bin|rel|ord) of the atom in the argument. + // (by rendering separately and with {}s before and after, and measuring + // the change in spacing). We'll do roughly the same by detecting the + // atom type directly. + var atom = arg.type === "ordgroup" && arg.body.length ? arg.body[0] : arg; + + if (atom.type === "atom" && (atom.family === "bin" || atom.family === "rel")) { + return "m" + atom.family; + } else { + return "mord"; + } +}; // \@binrel{x}{y} renders like y but as mbin/mrel/mord if x is mbin/mrel/mord. +// This is equivalent to \binrel@{x}\binrel@@{y} in AMSTeX. + +defineFunction({ + type: "mclass", + names: ["\\@binrel"], + props: { + numArgs: 2 + }, + handler: function handler(_ref2, args) { + var parser = _ref2.parser; + return { + type: "mclass", + mode: parser.mode, + mclass: binrelClass(args[0]), + body: ordargument(args[1]), + isCharacterBox: utils.isCharacterBox(args[1]) + }; + } +}); // Build a relation or stacked op by placing one symbol on top of another + +defineFunction({ + type: "mclass", + names: ["\\stackrel", "\\overset", "\\underset"], + props: { + numArgs: 2 + }, + handler: function handler(_ref3, args) { + var parser = _ref3.parser, + funcName = _ref3.funcName; + var baseArg = args[1]; + var shiftedArg = args[0]; + var mclass; + + if (funcName !== "\\stackrel") { + // LaTeX applies \binrel spacing to \overset and \underset. + mclass = binrelClass(baseArg); + } else { + mclass = "mrel"; // for \stackrel + } + + var baseOp = { + type: "op", + mode: baseArg.mode, + limits: true, + alwaysHandleSupSub: true, + parentIsSupSub: false, + symbol: false, + suppressBaseShift: funcName !== "\\stackrel", + body: ordargument(baseArg) + }; + var supsub = { + type: "supsub", + mode: shiftedArg.mode, + base: baseOp, + sup: funcName === "\\underset" ? null : shiftedArg, + sub: funcName === "\\underset" ? shiftedArg : null + }; + return { + type: "mclass", + mode: parser.mode, + mclass: mclass, + body: [supsub], + isCharacterBox: utils.isCharacterBox(supsub) + }; + }, + htmlBuilder: mclass_htmlBuilder, + mathmlBuilder: mclass_mathmlBuilder +}); +;// CONCATENATED MODULE: ./src/functions/font.js +// TODO(kevinb): implement \\sl and \\sc + + + + + + +var font_htmlBuilder = function htmlBuilder(group, options) { + var font = group.font; + var newOptions = options.withFont(font); + return buildGroup(group.body, newOptions); +}; + +var font_mathmlBuilder = function mathmlBuilder(group, options) { + var font = group.font; + var newOptions = options.withFont(font); + return buildMathML_buildGroup(group.body, newOptions); +}; + +var fontAliases = { + "\\Bbb": "\\mathbb", + "\\bold": "\\mathbf", + "\\frak": "\\mathfrak", + "\\bm": "\\boldsymbol" +}; +defineFunction({ + type: "font", + names: [// styles, except \boldsymbol defined below + "\\mathrm", "\\mathit", "\\mathbf", "\\mathnormal", // families + "\\mathbb", "\\mathcal", "\\mathfrak", "\\mathscr", "\\mathsf", "\\mathtt", // aliases, except \bm defined below + "\\Bbb", "\\bold", "\\frak"], + props: { + numArgs: 1, + allowedInArgument: true + }, + handler: function handler(_ref, args) { + var parser = _ref.parser, + funcName = _ref.funcName; + var body = normalizeArgument(args[0]); + var func = funcName; + + if (func in fontAliases) { + func = fontAliases[func]; + } + + return { + type: "font", + mode: parser.mode, + font: func.slice(1), + body: body + }; + }, + htmlBuilder: font_htmlBuilder, + mathmlBuilder: font_mathmlBuilder +}); +defineFunction({ + type: "mclass", + names: ["\\boldsymbol", "\\bm"], + props: { + numArgs: 1 + }, + handler: function handler(_ref2, args) { + var parser = _ref2.parser; + var body = args[0]; + var isCharacterBox = utils.isCharacterBox(body); // amsbsy.sty's \boldsymbol uses \binrel spacing to inherit the + // argument's bin|rel|ord status + + return { + type: "mclass", + mode: parser.mode, + mclass: binrelClass(body), + body: [{ + type: "font", + mode: parser.mode, + font: "boldsymbol", + body: body + }], + isCharacterBox: isCharacterBox + }; + } +}); // Old font changing functions + +defineFunction({ + type: "font", + names: ["\\rm", "\\sf", "\\tt", "\\bf", "\\it", "\\cal"], + props: { + numArgs: 0, + allowedInText: true + }, + handler: function handler(_ref3, args) { + var parser = _ref3.parser, + funcName = _ref3.funcName, + breakOnTokenText = _ref3.breakOnTokenText; + var mode = parser.mode; + var body = parser.parseExpression(true, breakOnTokenText); + var style = "math" + funcName.slice(1); + return { + type: "font", + mode: mode, + font: style, + body: { + type: "ordgroup", + mode: parser.mode, + body: body + } + }; + }, + htmlBuilder: font_htmlBuilder, + mathmlBuilder: font_mathmlBuilder +}); +;// CONCATENATED MODULE: ./src/functions/genfrac.js + + + + + + + + + + + +var adjustStyle = function adjustStyle(size, originalStyle) { + // Figure out what style this fraction should be in based on the + // function used + var style = originalStyle; + + if (size === "display") { + // Get display style as a default. + // If incoming style is sub/sup, use style.text() to get correct size. + style = style.id >= src_Style.SCRIPT.id ? style.text() : src_Style.DISPLAY; + } else if (size === "text" && style.size === src_Style.DISPLAY.size) { + // We're in a \tfrac but incoming style is displaystyle, so: + style = src_Style.TEXT; + } else if (size === "script") { + style = src_Style.SCRIPT; + } else if (size === "scriptscript") { + style = src_Style.SCRIPTSCRIPT; + } + + return style; +}; + +var genfrac_htmlBuilder = function htmlBuilder(group, options) { + // Fractions are handled in the TeXbook on pages 444-445, rules 15(a-e). + var style = adjustStyle(group.size, options.style); + var nstyle = style.fracNum(); + var dstyle = style.fracDen(); + var newOptions; + newOptions = options.havingStyle(nstyle); + var numerm = buildGroup(group.numer, newOptions, options); + + if (group.continued) { + // \cfrac inserts a \strut into the numerator. + // Get \strut dimensions from TeXbook page 353. + var hStrut = 8.5 / options.fontMetrics().ptPerEm; + var dStrut = 3.5 / options.fontMetrics().ptPerEm; + numerm.height = numerm.height < hStrut ? hStrut : numerm.height; + numerm.depth = numerm.depth < dStrut ? dStrut : numerm.depth; + } + + newOptions = options.havingStyle(dstyle); + var denomm = buildGroup(group.denom, newOptions, options); + var rule; + var ruleWidth; + var ruleSpacing; + + if (group.hasBarLine) { + if (group.barSize) { + ruleWidth = calculateSize(group.barSize, options); + rule = buildCommon.makeLineSpan("frac-line", options, ruleWidth); + } else { + rule = buildCommon.makeLineSpan("frac-line", options); + } + + ruleWidth = rule.height; + ruleSpacing = rule.height; + } else { + rule = null; + ruleWidth = 0; + ruleSpacing = options.fontMetrics().defaultRuleThickness; + } // Rule 15b + + + var numShift; + var clearance; + var denomShift; + + if (style.size === src_Style.DISPLAY.size || group.size === "display") { + numShift = options.fontMetrics().num1; + + if (ruleWidth > 0) { + clearance = 3 * ruleSpacing; + } else { + clearance = 7 * ruleSpacing; + } + + denomShift = options.fontMetrics().denom1; + } else { + if (ruleWidth > 0) { + numShift = options.fontMetrics().num2; + clearance = ruleSpacing; + } else { + numShift = options.fontMetrics().num3; + clearance = 3 * ruleSpacing; + } + + denomShift = options.fontMetrics().denom2; + } + + var frac; + + if (!rule) { + // Rule 15c + var candidateClearance = numShift - numerm.depth - (denomm.height - denomShift); + + if (candidateClearance < clearance) { + numShift += 0.5 * (clearance - candidateClearance); + denomShift += 0.5 * (clearance - candidateClearance); + } + + frac = buildCommon.makeVList({ + positionType: "individualShift", + children: [{ + type: "elem", + elem: denomm, + shift: denomShift + }, { + type: "elem", + elem: numerm, + shift: -numShift + }] + }, options); + } else { + // Rule 15d + var axisHeight = options.fontMetrics().axisHeight; + + if (numShift - numerm.depth - (axisHeight + 0.5 * ruleWidth) < clearance) { + numShift += clearance - (numShift - numerm.depth - (axisHeight + 0.5 * ruleWidth)); + } + + if (axisHeight - 0.5 * ruleWidth - (denomm.height - denomShift) < clearance) { + denomShift += clearance - (axisHeight - 0.5 * ruleWidth - (denomm.height - denomShift)); + } + + var midShift = -(axisHeight - 0.5 * ruleWidth); + frac = buildCommon.makeVList({ + positionType: "individualShift", + children: [{ + type: "elem", + elem: denomm, + shift: denomShift + }, { + type: "elem", + elem: rule, + shift: midShift + }, { + type: "elem", + elem: numerm, + shift: -numShift + }] + }, options); + } // Since we manually change the style sometimes (with \dfrac or \tfrac), + // account for the possible size change here. + + + newOptions = options.havingStyle(style); + frac.height *= newOptions.sizeMultiplier / options.sizeMultiplier; + frac.depth *= newOptions.sizeMultiplier / options.sizeMultiplier; // Rule 15e + + var delimSize; + + if (style.size === src_Style.DISPLAY.size) { + delimSize = options.fontMetrics().delim1; + } else if (style.size === src_Style.SCRIPTSCRIPT.size) { + delimSize = options.havingStyle(src_Style.SCRIPT).fontMetrics().delim2; + } else { + delimSize = options.fontMetrics().delim2; + } + + var leftDelim; + var rightDelim; + + if (group.leftDelim == null) { + leftDelim = makeNullDelimiter(options, ["mopen"]); + } else { + leftDelim = delimiter.customSizedDelim(group.leftDelim, delimSize, true, options.havingStyle(style), group.mode, ["mopen"]); + } + + if (group.continued) { + rightDelim = buildCommon.makeSpan([]); // zero width for \cfrac + } else if (group.rightDelim == null) { + rightDelim = makeNullDelimiter(options, ["mclose"]); + } else { + rightDelim = delimiter.customSizedDelim(group.rightDelim, delimSize, true, options.havingStyle(style), group.mode, ["mclose"]); + } + + return buildCommon.makeSpan(["mord"].concat(newOptions.sizingClasses(options)), [leftDelim, buildCommon.makeSpan(["mfrac"], [frac]), rightDelim], options); +}; + +var genfrac_mathmlBuilder = function mathmlBuilder(group, options) { + var node = new mathMLTree.MathNode("mfrac", [buildMathML_buildGroup(group.numer, options), buildMathML_buildGroup(group.denom, options)]); + + if (!group.hasBarLine) { + node.setAttribute("linethickness", "0px"); + } else if (group.barSize) { + var ruleWidth = calculateSize(group.barSize, options); + node.setAttribute("linethickness", ruleWidth + "em"); + } + + var style = adjustStyle(group.size, options.style); + + if (style.size !== options.style.size) { + node = new mathMLTree.MathNode("mstyle", [node]); + var isDisplay = style.size === src_Style.DISPLAY.size ? "true" : "false"; + node.setAttribute("displaystyle", isDisplay); + node.setAttribute("scriptlevel", "0"); + } + + if (group.leftDelim != null || group.rightDelim != null) { + var withDelims = []; + + if (group.leftDelim != null) { + var leftOp = new mathMLTree.MathNode("mo", [new mathMLTree.TextNode(group.leftDelim.replace("\\", ""))]); + leftOp.setAttribute("fence", "true"); + withDelims.push(leftOp); + } + + withDelims.push(node); + + if (group.rightDelim != null) { + var rightOp = new mathMLTree.MathNode("mo", [new mathMLTree.TextNode(group.rightDelim.replace("\\", ""))]); + rightOp.setAttribute("fence", "true"); + withDelims.push(rightOp); + } + + return makeRow(withDelims); + } + + return node; +}; + +defineFunction({ + type: "genfrac", + names: ["\\dfrac", "\\frac", "\\tfrac", "\\dbinom", "\\binom", "\\tbinom", "\\\\atopfrac", // can’t be entered directly + "\\\\bracefrac", "\\\\brackfrac" // ditto + ], + props: { + numArgs: 2, + allowedInArgument: true + }, + handler: function handler(_ref, args) { + var parser = _ref.parser, + funcName = _ref.funcName; + var numer = args[0]; + var denom = args[1]; + var hasBarLine; + var leftDelim = null; + var rightDelim = null; + var size = "auto"; + + switch (funcName) { + case "\\dfrac": + case "\\frac": + case "\\tfrac": + hasBarLine = true; + break; + + case "\\\\atopfrac": + hasBarLine = false; + break; + + case "\\dbinom": + case "\\binom": + case "\\tbinom": + hasBarLine = false; + leftDelim = "("; + rightDelim = ")"; + break; + + case "\\\\bracefrac": + hasBarLine = false; + leftDelim = "\\{"; + rightDelim = "\\}"; + break; + + case "\\\\brackfrac": + hasBarLine = false; + leftDelim = "["; + rightDelim = "]"; + break; + + default: + throw new Error("Unrecognized genfrac command"); + } + + switch (funcName) { + case "\\dfrac": + case "\\dbinom": + size = "display"; + break; + + case "\\tfrac": + case "\\tbinom": + size = "text"; + break; + } + + return { + type: "genfrac", + mode: parser.mode, + continued: false, + numer: numer, + denom: denom, + hasBarLine: hasBarLine, + leftDelim: leftDelim, + rightDelim: rightDelim, + size: size, + barSize: null + }; + }, + htmlBuilder: genfrac_htmlBuilder, + mathmlBuilder: genfrac_mathmlBuilder +}); +defineFunction({ + type: "genfrac", + names: ["\\cfrac"], + props: { + numArgs: 2 + }, + handler: function handler(_ref2, args) { + var parser = _ref2.parser, + funcName = _ref2.funcName; + var numer = args[0]; + var denom = args[1]; + return { + type: "genfrac", + mode: parser.mode, + continued: true, + numer: numer, + denom: denom, + hasBarLine: true, + leftDelim: null, + rightDelim: null, + size: "display", + barSize: null + }; + } +}); // Infix generalized fractions -- these are not rendered directly, but replaced +// immediately by one of the variants above. + +defineFunction({ + type: "infix", + names: ["\\over", "\\choose", "\\atop", "\\brace", "\\brack"], + props: { + numArgs: 0, + infix: true + }, + handler: function handler(_ref3) { + var parser = _ref3.parser, + funcName = _ref3.funcName, + token = _ref3.token; + var replaceWith; + + switch (funcName) { + case "\\over": + replaceWith = "\\frac"; + break; + + case "\\choose": + replaceWith = "\\binom"; + break; + + case "\\atop": + replaceWith = "\\\\atopfrac"; + break; + + case "\\brace": + replaceWith = "\\\\bracefrac"; + break; + + case "\\brack": + replaceWith = "\\\\brackfrac"; + break; + + default: + throw new Error("Unrecognized infix genfrac command"); + } + + return { + type: "infix", + mode: parser.mode, + replaceWith: replaceWith, + token: token + }; + } +}); +var stylArray = ["display", "text", "script", "scriptscript"]; + +var delimFromValue = function delimFromValue(delimString) { + var delim = null; + + if (delimString.length > 0) { + delim = delimString; + delim = delim === "." ? null : delim; + } + + return delim; +}; + +defineFunction({ + type: "genfrac", + names: ["\\genfrac"], + props: { + numArgs: 6, + allowedInArgument: true, + argTypes: ["math", "math", "size", "text", "math", "math"] + }, + handler: function handler(_ref4, args) { + var parser = _ref4.parser; + var numer = args[4]; + var denom = args[5]; // Look into the parse nodes to get the desired delimiters. + + var leftNode = normalizeArgument(args[0]); + var leftDelim = leftNode.type === "atom" && leftNode.family === "open" ? delimFromValue(leftNode.text) : null; + var rightNode = normalizeArgument(args[1]); + var rightDelim = rightNode.type === "atom" && rightNode.family === "close" ? delimFromValue(rightNode.text) : null; + var barNode = assertNodeType(args[2], "size"); + var hasBarLine; + var barSize = null; + + if (barNode.isBlank) { + // \genfrac acts differently than \above. + // \genfrac treats an empty size group as a signal to use a + // standard bar size. \above would see size = 0 and omit the bar. + hasBarLine = true; + } else { + barSize = barNode.value; + hasBarLine = barSize.number > 0; + } // Find out if we want displaystyle, textstyle, etc. + + + var size = "auto"; + var styl = args[3]; + + if (styl.type === "ordgroup") { + if (styl.body.length > 0) { + var textOrd = assertNodeType(styl.body[0], "textord"); + size = stylArray[Number(textOrd.text)]; + } + } else { + styl = assertNodeType(styl, "textord"); + size = stylArray[Number(styl.text)]; + } + + return { + type: "genfrac", + mode: parser.mode, + numer: numer, + denom: denom, + continued: false, + hasBarLine: hasBarLine, + barSize: barSize, + leftDelim: leftDelim, + rightDelim: rightDelim, + size: size + }; + }, + htmlBuilder: genfrac_htmlBuilder, + mathmlBuilder: genfrac_mathmlBuilder +}); // \above is an infix fraction that also defines a fraction bar size. + +defineFunction({ + type: "infix", + names: ["\\above"], + props: { + numArgs: 1, + argTypes: ["size"], + infix: true + }, + handler: function handler(_ref5, args) { + var parser = _ref5.parser, + funcName = _ref5.funcName, + token = _ref5.token; + return { + type: "infix", + mode: parser.mode, + replaceWith: "\\\\abovefrac", + size: assertNodeType(args[0], "size").value, + token: token + }; + } +}); +defineFunction({ + type: "genfrac", + names: ["\\\\abovefrac"], + props: { + numArgs: 3, + argTypes: ["math", "size", "math"] + }, + handler: function handler(_ref6, args) { + var parser = _ref6.parser, + funcName = _ref6.funcName; + var numer = args[0]; + var barSize = assert(assertNodeType(args[1], "infix").size); + var denom = args[2]; + var hasBarLine = barSize.number > 0; + return { + type: "genfrac", + mode: parser.mode, + numer: numer, + denom: denom, + continued: false, + hasBarLine: hasBarLine, + barSize: barSize, + leftDelim: null, + rightDelim: null, + size: "auto" + }; + }, + htmlBuilder: genfrac_htmlBuilder, + mathmlBuilder: genfrac_mathmlBuilder +}); +;// CONCATENATED MODULE: ./src/functions/horizBrace.js + + + + + + + + +// NOTE: Unlike most `htmlBuilder`s, this one handles not only "horizBrace", but +// also "supsub" since an over/underbrace can affect super/subscripting. +var horizBrace_htmlBuilder = function htmlBuilder(grp, options) { + var style = options.style; // Pull out the `ParseNode<"horizBrace">` if `grp` is a "supsub" node. + + var supSubGroup; + var group; + + if (grp.type === "supsub") { + // Ref: LaTeX source2e: }}}}\limits} + // i.e. LaTeX treats the brace similar to an op and passes it + // with \limits, so we need to assign supsub style. + supSubGroup = grp.sup ? buildGroup(grp.sup, options.havingStyle(style.sup()), options) : buildGroup(grp.sub, options.havingStyle(style.sub()), options); + group = assertNodeType(grp.base, "horizBrace"); + } else { + group = assertNodeType(grp, "horizBrace"); + } // Build the base group + + + var body = buildGroup(group.base, options.havingBaseStyle(src_Style.DISPLAY)); // Create the stretchy element + + var braceBody = stretchy.svgSpan(group, options); // Generate the vlist, with the appropriate kerns ┏━━━━━━━━┓ + // This first vlist contains the content and the brace: equation + + var vlist; + + if (group.isOver) { + vlist = buildCommon.makeVList({ + positionType: "firstBaseline", + children: [{ + type: "elem", + elem: body + }, { + type: "kern", + size: 0.1 + }, { + type: "elem", + elem: braceBody + }] + }, options); // $FlowFixMe: Replace this with passing "svg-align" into makeVList. + + vlist.children[0].children[0].children[1].classes.push("svg-align"); + } else { + vlist = buildCommon.makeVList({ + positionType: "bottom", + positionData: body.depth + 0.1 + braceBody.height, + children: [{ + type: "elem", + elem: braceBody + }, { + type: "kern", + size: 0.1 + }, { + type: "elem", + elem: body + }] + }, options); // $FlowFixMe: Replace this with passing "svg-align" into makeVList. + + vlist.children[0].children[0].children[0].classes.push("svg-align"); + } + + if (supSubGroup) { + // To write the supsub, wrap the first vlist in another vlist: + // They can't all go in the same vlist, because the note might be + // wider than the equation. We want the equation to control the + // brace width. + // note long note long note + // ┏━━━━━━━━┓ or ┏━━━┓ not ┏━━━━━━━━━┓ + // equation eqn eqn + var vSpan = buildCommon.makeSpan(["mord", group.isOver ? "mover" : "munder"], [vlist], options); + + if (group.isOver) { + vlist = buildCommon.makeVList({ + positionType: "firstBaseline", + children: [{ + type: "elem", + elem: vSpan + }, { + type: "kern", + size: 0.2 + }, { + type: "elem", + elem: supSubGroup + }] + }, options); + } else { + vlist = buildCommon.makeVList({ + positionType: "bottom", + positionData: vSpan.depth + 0.2 + supSubGroup.height + supSubGroup.depth, + children: [{ + type: "elem", + elem: supSubGroup + }, { + type: "kern", + size: 0.2 + }, { + type: "elem", + elem: vSpan + }] + }, options); + } + } + + return buildCommon.makeSpan(["mord", group.isOver ? "mover" : "munder"], [vlist], options); +}; + +var horizBrace_mathmlBuilder = function mathmlBuilder(group, options) { + var accentNode = stretchy.mathMLnode(group.label); + return new mathMLTree.MathNode(group.isOver ? "mover" : "munder", [buildMathML_buildGroup(group.base, options), accentNode]); +}; // Horizontal stretchy braces + + +defineFunction({ + type: "horizBrace", + names: ["\\overbrace", "\\underbrace"], + props: { + numArgs: 1 + }, + handler: function handler(_ref, args) { + var parser = _ref.parser, + funcName = _ref.funcName; + return { + type: "horizBrace", + mode: parser.mode, + label: funcName, + isOver: /^\\over/.test(funcName), + base: args[0] + }; + }, + htmlBuilder: horizBrace_htmlBuilder, + mathmlBuilder: horizBrace_mathmlBuilder +}); +;// CONCATENATED MODULE: ./src/functions/href.js + + + + + + +defineFunction({ + type: "href", + names: ["\\href"], + props: { + numArgs: 2, + argTypes: ["url", "original"], + allowedInText: true + }, + handler: function handler(_ref, args) { + var parser = _ref.parser; + var body = args[1]; + var href = assertNodeType(args[0], "url").url; + + if (!parser.settings.isTrusted({ + command: "\\href", + url: href + })) { + return parser.formatUnsupportedCmd("\\href"); + } + + return { + type: "href", + mode: parser.mode, + href: href, + body: ordargument(body) + }; + }, + htmlBuilder: function htmlBuilder(group, options) { + var elements = buildExpression(group.body, options, false); + return buildCommon.makeAnchor(group.href, [], elements, options); + }, + mathmlBuilder: function mathmlBuilder(group, options) { + var math = buildExpressionRow(group.body, options); + + if (!(math instanceof MathNode)) { + math = new MathNode("mrow", [math]); + } + + math.setAttribute("href", group.href); + return math; + } +}); +defineFunction({ + type: "href", + names: ["\\url"], + props: { + numArgs: 1, + argTypes: ["url"], + allowedInText: true + }, + handler: function handler(_ref2, args) { + var parser = _ref2.parser; + var href = assertNodeType(args[0], "url").url; + + if (!parser.settings.isTrusted({ + command: "\\url", + url: href + })) { + return parser.formatUnsupportedCmd("\\url"); + } + + var chars = []; + + for (var i = 0; i < href.length; i++) { + var c = href[i]; + + if (c === "~") { + c = "\\textasciitilde"; + } + + chars.push({ + type: "textord", + mode: "text", + text: c + }); + } + + var body = { + type: "text", + mode: parser.mode, + font: "\\texttt", + body: chars + }; + return { + type: "href", + mode: parser.mode, + href: href, + body: ordargument(body) + }; + } +}); +;// CONCATENATED MODULE: ./src/functions/hbox.js + + + + + // \hbox is provided for compatibility with LaTeX \vcenter. +// In LaTeX, \vcenter can act only on a box, as in +// \vcenter{\hbox{$\frac{a+b}{\dfrac{c}{d}}$}} +// This function by itself doesn't do anything but prevent a soft line break. + +defineFunction({ + type: "hbox", + names: ["\\hbox"], + props: { + numArgs: 1, + argTypes: ["text"], + allowedInText: true, + primitive: true + }, + handler: function handler(_ref, args) { + var parser = _ref.parser; + return { + type: "hbox", + mode: parser.mode, + body: ordargument(args[0]) + }; + }, + htmlBuilder: function htmlBuilder(group, options) { + var elements = buildExpression(group.body, options, false); + return buildCommon.makeFragment(elements); + }, + mathmlBuilder: function mathmlBuilder(group, options) { + return new mathMLTree.MathNode("mrow", buildMathML_buildExpression(group.body, options)); + } +}); +;// CONCATENATED MODULE: ./src/functions/html.js + + + + + + +defineFunction({ + type: "html", + names: ["\\htmlClass", "\\htmlId", "\\htmlStyle", "\\htmlData"], + props: { + numArgs: 2, + argTypes: ["raw", "original"], + allowedInText: true + }, + handler: function handler(_ref, args) { + var parser = _ref.parser, + funcName = _ref.funcName, + token = _ref.token; + var value = assertNodeType(args[0], "raw").string; + var body = args[1]; + + if (parser.settings.strict) { + parser.settings.reportNonstrict("htmlExtension", "HTML extension is disabled on strict mode"); + } + + var trustContext; + var attributes = {}; + + switch (funcName) { + case "\\htmlClass": + attributes.class = value; + trustContext = { + command: "\\htmlClass", + class: value + }; + break; + + case "\\htmlId": + attributes.id = value; + trustContext = { + command: "\\htmlId", + id: value + }; + break; + + case "\\htmlStyle": + attributes.style = value; + trustContext = { + command: "\\htmlStyle", + style: value + }; + break; + + case "\\htmlData": + { + var data = value.split(","); + + for (var i = 0; i < data.length; i++) { + var keyVal = data[i].split("="); + + if (keyVal.length !== 2) { + throw new src_ParseError("Error parsing key-value for \\htmlData"); + } + + attributes["data-" + keyVal[0].trim()] = keyVal[1].trim(); + } + + trustContext = { + command: "\\htmlData", + attributes: attributes + }; + break; + } + + default: + throw new Error("Unrecognized html command"); + } + + if (!parser.settings.isTrusted(trustContext)) { + return parser.formatUnsupportedCmd(funcName); + } + + return { + type: "html", + mode: parser.mode, + attributes: attributes, + body: ordargument(body) + }; + }, + htmlBuilder: function htmlBuilder(group, options) { + var elements = buildExpression(group.body, options, false); + var classes = ["enclosing"]; + + if (group.attributes.class) { + classes.push.apply(classes, group.attributes.class.trim().split(/\s+/)); + } + + var span = buildCommon.makeSpan(classes, elements, options); + + for (var attr in group.attributes) { + if (attr !== "class" && group.attributes.hasOwnProperty(attr)) { + span.setAttribute(attr, group.attributes[attr]); + } + } + + return span; + }, + mathmlBuilder: function mathmlBuilder(group, options) { + return buildExpressionRow(group.body, options); + } +}); +;// CONCATENATED MODULE: ./src/functions/htmlmathml.js + + + + +defineFunction({ + type: "htmlmathml", + names: ["\\html@mathml"], + props: { + numArgs: 2, + allowedInText: true + }, + handler: function handler(_ref, args) { + var parser = _ref.parser; + return { + type: "htmlmathml", + mode: parser.mode, + html: ordargument(args[0]), + mathml: ordargument(args[1]) + }; + }, + htmlBuilder: function htmlBuilder(group, options) { + var elements = buildExpression(group.html, options, false); + return buildCommon.makeFragment(elements); + }, + mathmlBuilder: function mathmlBuilder(group, options) { + return buildExpressionRow(group.mathml, options); + } +}); +;// CONCATENATED MODULE: ./src/functions/includegraphics.js + + + + + + + +var sizeData = function sizeData(str) { + if (/^[-+]? *(\d+(\.\d*)?|\.\d+)$/.test(str)) { + // str is a number with no unit specified. + // default unit is bp, per graphix package. + return { + number: +str, + unit: "bp" + }; + } else { + var match = /([-+]?) *(\d+(?:\.\d*)?|\.\d+) *([a-z]{2})/.exec(str); + + if (!match) { + throw new src_ParseError("Invalid size: '" + str + "' in \\includegraphics"); + } + + var data = { + number: +(match[1] + match[2]), + // sign + magnitude, cast to number + unit: match[3] + }; + + if (!validUnit(data)) { + throw new src_ParseError("Invalid unit: '" + data.unit + "' in \\includegraphics."); + } + + return data; + } +}; + +defineFunction({ + type: "includegraphics", + names: ["\\includegraphics"], + props: { + numArgs: 1, + numOptionalArgs: 1, + argTypes: ["raw", "url"], + allowedInText: false + }, + handler: function handler(_ref, args, optArgs) { + var parser = _ref.parser; + var width = { + number: 0, + unit: "em" + }; + var height = { + number: 0.9, + unit: "em" + }; // sorta character sized. + + var totalheight = { + number: 0, + unit: "em" + }; + var alt = ""; + + if (optArgs[0]) { + var attributeStr = assertNodeType(optArgs[0], "raw").string; // Parser.js does not parse key/value pairs. We get a string. + + var attributes = attributeStr.split(","); + + for (var i = 0; i < attributes.length; i++) { + var keyVal = attributes[i].split("="); + + if (keyVal.length === 2) { + var str = keyVal[1].trim(); + + switch (keyVal[0].trim()) { + case "alt": + alt = str; + break; + + case "width": + width = sizeData(str); + break; + + case "height": + height = sizeData(str); + break; + + case "totalheight": + totalheight = sizeData(str); + break; + + default: + throw new src_ParseError("Invalid key: '" + keyVal[0] + "' in \\includegraphics."); + } + } + } + } + + var src = assertNodeType(args[0], "url").url; + + if (alt === "") { + // No alt given. Use the file name. Strip away the path. + alt = src; + alt = alt.replace(/^.*[\\/]/, ''); + alt = alt.substring(0, alt.lastIndexOf('.')); + } + + if (!parser.settings.isTrusted({ + command: "\\includegraphics", + url: src + })) { + return parser.formatUnsupportedCmd("\\includegraphics"); + } + + return { + type: "includegraphics", + mode: parser.mode, + alt: alt, + width: width, + height: height, + totalheight: totalheight, + src: src + }; + }, + htmlBuilder: function htmlBuilder(group, options) { + var height = calculateSize(group.height, options); + var depth = 0; + + if (group.totalheight.number > 0) { + depth = calculateSize(group.totalheight, options) - height; + depth = Number(depth.toFixed(2)); + } + + var width = 0; + + if (group.width.number > 0) { + width = calculateSize(group.width, options); + } + + var style = { + height: height + depth + "em" + }; + + if (width > 0) { + style.width = width + "em"; + } + + if (depth > 0) { + style.verticalAlign = -depth + "em"; + } + + var node = new Img(group.src, group.alt, style); + node.height = height; + node.depth = depth; + return node; + }, + mathmlBuilder: function mathmlBuilder(group, options) { + var node = new mathMLTree.MathNode("mglyph", []); + node.setAttribute("alt", group.alt); + var height = calculateSize(group.height, options); + var depth = 0; + + if (group.totalheight.number > 0) { + depth = calculateSize(group.totalheight, options) - height; + depth = depth.toFixed(2); + node.setAttribute("valign", "-" + depth + "em"); + } + + node.setAttribute("height", height + depth + "em"); + + if (group.width.number > 0) { + var width = calculateSize(group.width, options); + node.setAttribute("width", width + "em"); + } + + node.setAttribute("src", group.src); + return node; + } +}); +;// CONCATENATED MODULE: ./src/functions/kern.js +// Horizontal spacing commands + + + + + // TODO: \hskip and \mskip should support plus and minus in lengths + +defineFunction({ + type: "kern", + names: ["\\kern", "\\mkern", "\\hskip", "\\mskip"], + props: { + numArgs: 1, + argTypes: ["size"], + primitive: true, + allowedInText: true + }, + handler: function handler(_ref, args) { + var parser = _ref.parser, + funcName = _ref.funcName; + var size = assertNodeType(args[0], "size"); + + if (parser.settings.strict) { + var mathFunction = funcName[1] === 'm'; // \mkern, \mskip + + var muUnit = size.value.unit === 'mu'; + + if (mathFunction) { + if (!muUnit) { + parser.settings.reportNonstrict("mathVsTextUnits", "LaTeX's " + funcName + " supports only mu units, " + ("not " + size.value.unit + " units")); + } + + if (parser.mode !== "math") { + parser.settings.reportNonstrict("mathVsTextUnits", "LaTeX's " + funcName + " works only in math mode"); + } + } else { + // !mathFunction + if (muUnit) { + parser.settings.reportNonstrict("mathVsTextUnits", "LaTeX's " + funcName + " doesn't support mu units"); + } + } + } + + return { + type: "kern", + mode: parser.mode, + dimension: size.value + }; + }, + htmlBuilder: function htmlBuilder(group, options) { + return buildCommon.makeGlue(group.dimension, options); + }, + mathmlBuilder: function mathmlBuilder(group, options) { + var dimension = calculateSize(group.dimension, options); + return new mathMLTree.SpaceNode(dimension); + } +}); +;// CONCATENATED MODULE: ./src/functions/lap.js +// Horizontal overlap functions + + + + + +defineFunction({ + type: "lap", + names: ["\\mathllap", "\\mathrlap", "\\mathclap"], + props: { + numArgs: 1, + allowedInText: true + }, + handler: function handler(_ref, args) { + var parser = _ref.parser, + funcName = _ref.funcName; + var body = args[0]; + return { + type: "lap", + mode: parser.mode, + alignment: funcName.slice(5), + body: body + }; + }, + htmlBuilder: function htmlBuilder(group, options) { + // mathllap, mathrlap, mathclap + var inner; + + if (group.alignment === "clap") { + // ref: https://www.math.lsu.edu/~aperlis/publications/mathclap/ + inner = buildCommon.makeSpan([], [buildGroup(group.body, options)]); // wrap, since CSS will center a .clap > .inner > span + + inner = buildCommon.makeSpan(["inner"], [inner], options); + } else { + inner = buildCommon.makeSpan(["inner"], [buildGroup(group.body, options)]); + } + + var fix = buildCommon.makeSpan(["fix"], []); + var node = buildCommon.makeSpan([group.alignment], [inner, fix], options); // At this point, we have correctly set horizontal alignment of the + // two items involved in the lap. + // Next, use a strut to set the height of the HTML bounding box. + // Otherwise, a tall argument may be misplaced. + // This code resolved issue #1153 + + var strut = buildCommon.makeSpan(["strut"]); + strut.style.height = node.height + node.depth + "em"; + strut.style.verticalAlign = -node.depth + "em"; + node.children.unshift(strut); // Next, prevent vertical misplacement when next to something tall. + // This code resolves issue #1234 + + node = buildCommon.makeSpan(["thinbox"], [node], options); + return buildCommon.makeSpan(["mord", "vbox"], [node], options); + }, + mathmlBuilder: function mathmlBuilder(group, options) { + // mathllap, mathrlap, mathclap + var node = new mathMLTree.MathNode("mpadded", [buildMathML_buildGroup(group.body, options)]); + + if (group.alignment !== "rlap") { + var offset = group.alignment === "llap" ? "-1" : "-0.5"; + node.setAttribute("lspace", offset + "width"); + } + + node.setAttribute("width", "0px"); + return node; + } +}); +;// CONCATENATED MODULE: ./src/functions/math.js + + // Switching from text mode back to math mode + +defineFunction({ + type: "styling", + names: ["\\(", "$"], + props: { + numArgs: 0, + allowedInText: true, + allowedInMath: false + }, + handler: function handler(_ref, args) { + var funcName = _ref.funcName, + parser = _ref.parser; + var outerMode = parser.mode; + parser.switchMode("math"); + var close = funcName === "\\(" ? "\\)" : "$"; + var body = parser.parseExpression(false, close); + parser.expect(close); + parser.switchMode(outerMode); + return { + type: "styling", + mode: parser.mode, + style: "text", + body: body + }; + } +}); // Check for extra closing math delimiters + +defineFunction({ + type: "text", + // Doesn't matter what this is. + names: ["\\)", "\\]"], + props: { + numArgs: 0, + allowedInText: true, + allowedInMath: false + }, + handler: function handler(context, args) { + throw new src_ParseError("Mismatched " + context.funcName); + } +}); +;// CONCATENATED MODULE: ./src/functions/mathchoice.js + + + + + + +var chooseMathStyle = function chooseMathStyle(group, options) { + switch (options.style.size) { + case src_Style.DISPLAY.size: + return group.display; + + case src_Style.TEXT.size: + return group.text; + + case src_Style.SCRIPT.size: + return group.script; + + case src_Style.SCRIPTSCRIPT.size: + return group.scriptscript; + + default: + return group.text; + } +}; + +defineFunction({ + type: "mathchoice", + names: ["\\mathchoice"], + props: { + numArgs: 4, + primitive: true + }, + handler: function handler(_ref, args) { + var parser = _ref.parser; + return { + type: "mathchoice", + mode: parser.mode, + display: ordargument(args[0]), + text: ordargument(args[1]), + script: ordargument(args[2]), + scriptscript: ordargument(args[3]) + }; + }, + htmlBuilder: function htmlBuilder(group, options) { + var body = chooseMathStyle(group, options); + var elements = buildExpression(body, options, false); + return buildCommon.makeFragment(elements); + }, + mathmlBuilder: function mathmlBuilder(group, options) { + var body = chooseMathStyle(group, options); + return buildExpressionRow(body, options); + } +}); +;// CONCATENATED MODULE: ./src/functions/utils/assembleSupSub.js + + + +// For an operator with limits, assemble the base, sup, and sub into a span. +var assembleSupSub = function assembleSupSub(base, supGroup, subGroup, options, style, slant, baseShift) { + base = buildCommon.makeSpan([], [base]); + var subIsSingleCharacter = subGroup && utils.isCharacterBox(subGroup); + var sub; + var sup; // We manually have to handle the superscripts and subscripts. This, + // aside from the kern calculations, is copied from supsub. + + if (supGroup) { + var elem = buildGroup(supGroup, options.havingStyle(style.sup()), options); + sup = { + elem: elem, + kern: Math.max(options.fontMetrics().bigOpSpacing1, options.fontMetrics().bigOpSpacing3 - elem.depth) + }; + } + + if (subGroup) { + var _elem = buildGroup(subGroup, options.havingStyle(style.sub()), options); + + sub = { + elem: _elem, + kern: Math.max(options.fontMetrics().bigOpSpacing2, options.fontMetrics().bigOpSpacing4 - _elem.height) + }; + } // Build the final group as a vlist of the possible subscript, base, + // and possible superscript. + + + var finalGroup; + + if (sup && sub) { + var bottom = options.fontMetrics().bigOpSpacing5 + sub.elem.height + sub.elem.depth + sub.kern + base.depth + baseShift; + finalGroup = buildCommon.makeVList({ + positionType: "bottom", + positionData: bottom, + children: [{ + type: "kern", + size: options.fontMetrics().bigOpSpacing5 + }, { + type: "elem", + elem: sub.elem, + marginLeft: -slant + "em" + }, { + type: "kern", + size: sub.kern + }, { + type: "elem", + elem: base + }, { + type: "kern", + size: sup.kern + }, { + type: "elem", + elem: sup.elem, + marginLeft: slant + "em" + }, { + type: "kern", + size: options.fontMetrics().bigOpSpacing5 + }] + }, options); + } else if (sub) { + var top = base.height - baseShift; // Shift the limits by the slant of the symbol. Note + // that we are supposed to shift the limits by 1/2 of the slant, + // but since we are centering the limits adding a full slant of + // margin will shift by 1/2 that. + + finalGroup = buildCommon.makeVList({ + positionType: "top", + positionData: top, + children: [{ + type: "kern", + size: options.fontMetrics().bigOpSpacing5 + }, { + type: "elem", + elem: sub.elem, + marginLeft: -slant + "em" + }, { + type: "kern", + size: sub.kern + }, { + type: "elem", + elem: base + }] + }, options); + } else if (sup) { + var _bottom = base.depth + baseShift; + + finalGroup = buildCommon.makeVList({ + positionType: "bottom", + positionData: _bottom, + children: [{ + type: "elem", + elem: base + }, { + type: "kern", + size: sup.kern + }, { + type: "elem", + elem: sup.elem, + marginLeft: slant + "em" + }, { + type: "kern", + size: options.fontMetrics().bigOpSpacing5 + }] + }, options); + } else { + // This case probably shouldn't occur (this would mean the + // supsub was sending us a group with no superscript or + // subscript) but be safe. + return base; + } + + var parts = [finalGroup]; + + if (sub && slant !== 0 && !subIsSingleCharacter) { + // A negative margin-left was applied to the lower limit. + // Avoid an overlap by placing a spacer on the left on the group. + var spacer = buildCommon.makeSpan(["mspace"], [], options); + spacer.style.marginRight = slant + "em"; + parts.unshift(spacer); + } + + return buildCommon.makeSpan(["mop", "op-limits"], parts, options); +}; +;// CONCATENATED MODULE: ./src/functions/op.js +// Limits, symbols + + + + + + + + + + +// Most operators have a large successor symbol, but these don't. +var noSuccessor = ["\\smallint"]; // NOTE: Unlike most `htmlBuilder`s, this one handles not only "op", but also +// "supsub" since some of them (like \int) can affect super/subscripting. + +var op_htmlBuilder = function htmlBuilder(grp, options) { + // Operators are handled in the TeXbook pg. 443-444, rule 13(a). + var supGroup; + var subGroup; + var hasLimits = false; + var group; + + if (grp.type === "supsub") { + // If we have limits, supsub will pass us its group to handle. Pull + // out the superscript and subscript and set the group to the op in + // its base. + supGroup = grp.sup; + subGroup = grp.sub; + group = assertNodeType(grp.base, "op"); + hasLimits = true; + } else { + group = assertNodeType(grp, "op"); + } + + var style = options.style; + var large = false; + + if (style.size === src_Style.DISPLAY.size && group.symbol && !utils.contains(noSuccessor, group.name)) { + // Most symbol operators get larger in displaystyle (rule 13) + large = true; + } + + var base; + + if (group.symbol) { + // If this is a symbol, create the symbol. + var fontName = large ? "Size2-Regular" : "Size1-Regular"; + var stash = ""; + + if (group.name === "\\oiint" || group.name === "\\oiiint") { + // No font glyphs yet, so use a glyph w/o the oval. + // TODO: When font glyphs are available, delete this code. + stash = group.name.substr(1); + group.name = stash === "oiint" ? "\\iint" : "\\iiint"; + } + + base = buildCommon.makeSymbol(group.name, fontName, "math", options, ["mop", "op-symbol", large ? "large-op" : "small-op"]); + + if (stash.length > 0) { + // We're in \oiint or \oiiint. Overlay the oval. + // TODO: When font glyphs are available, delete this code. + var italic = base.italic; + var oval = buildCommon.staticSvg(stash + "Size" + (large ? "2" : "1"), options); + base = buildCommon.makeVList({ + positionType: "individualShift", + children: [{ + type: "elem", + elem: base, + shift: 0 + }, { + type: "elem", + elem: oval, + shift: large ? 0.08 : 0 + }] + }, options); + group.name = "\\" + stash; + base.classes.unshift("mop"); // $FlowFixMe + + base.italic = italic; + } + } else if (group.body) { + // If this is a list, compose that list. + var inner = buildExpression(group.body, options, true); + + if (inner.length === 1 && inner[0] instanceof SymbolNode) { + base = inner[0]; + base.classes[0] = "mop"; // replace old mclass + } else { + base = buildCommon.makeSpan(["mop"], inner, options); + } + } else { + // Otherwise, this is a text operator. Build the text from the + // operator's name. + var output = []; + + for (var i = 1; i < group.name.length; i++) { + output.push(buildCommon.mathsym(group.name[i], group.mode, options)); + } + + base = buildCommon.makeSpan(["mop"], output, options); + } // If content of op is a single symbol, shift it vertically. + + + var baseShift = 0; + var slant = 0; + + if ((base instanceof SymbolNode || group.name === "\\oiint" || group.name === "\\oiiint") && !group.suppressBaseShift) { + // We suppress the shift of the base of \overset and \underset. Otherwise, + // shift the symbol so its center lies on the axis (rule 13). It + // appears that our fonts have the centers of the symbols already + // almost on the axis, so these numbers are very small. Note we + // don't actually apply this here, but instead it is used either in + // the vlist creation or separately when there are no limits. + baseShift = (base.height - base.depth) / 2 - options.fontMetrics().axisHeight; // The slant of the symbol is just its italic correction. + // $FlowFixMe + + slant = base.italic; + } + + if (hasLimits) { + return assembleSupSub(base, supGroup, subGroup, options, style, slant, baseShift); + } else { + if (baseShift) { + base.style.position = "relative"; + base.style.top = baseShift + "em"; + } + + return base; + } +}; + +var op_mathmlBuilder = function mathmlBuilder(group, options) { + var node; + + if (group.symbol) { + // This is a symbol. Just add the symbol. + node = new MathNode("mo", [makeText(group.name, group.mode)]); + + if (utils.contains(noSuccessor, group.name)) { + node.setAttribute("largeop", "false"); + } + } else if (group.body) { + // This is an operator with children. Add them. + node = new MathNode("mo", buildMathML_buildExpression(group.body, options)); + } else { + // This is a text operator. Add all of the characters from the + // operator's name. + node = new MathNode("mi", [new TextNode(group.name.slice(1))]); // Append an . + // ref: https://www.w3.org/TR/REC-MathML/chap3_2.html#sec3.2.4 + + var operator = new MathNode("mo", [makeText("\u2061", "text")]); + + if (group.parentIsSupSub) { + node = new MathNode("mrow", [node, operator]); + } else { + node = newDocumentFragment([node, operator]); + } + } + + return node; +}; + +var singleCharBigOps = { + "\u220F": "\\prod", + "\u2210": "\\coprod", + "\u2211": "\\sum", + "\u22C0": "\\bigwedge", + "\u22C1": "\\bigvee", + "\u22C2": "\\bigcap", + "\u22C3": "\\bigcup", + "\u2A00": "\\bigodot", + "\u2A01": "\\bigoplus", + "\u2A02": "\\bigotimes", + "\u2A04": "\\biguplus", + "\u2A06": "\\bigsqcup" +}; +defineFunction({ + type: "op", + names: ["\\coprod", "\\bigvee", "\\bigwedge", "\\biguplus", "\\bigcap", "\\bigcup", "\\intop", "\\prod", "\\sum", "\\bigotimes", "\\bigoplus", "\\bigodot", "\\bigsqcup", "\\smallint", "\u220F", "\u2210", "\u2211", "\u22C0", "\u22C1", "\u22C2", "\u22C3", "\u2A00", "\u2A01", "\u2A02", "\u2A04", "\u2A06"], + props: { + numArgs: 0 + }, + handler: function handler(_ref, args) { + var parser = _ref.parser, + funcName = _ref.funcName; + var fName = funcName; + + if (fName.length === 1) { + fName = singleCharBigOps[fName]; + } + + return { + type: "op", + mode: parser.mode, + limits: true, + parentIsSupSub: false, + symbol: true, + name: fName + }; + }, + htmlBuilder: op_htmlBuilder, + mathmlBuilder: op_mathmlBuilder +}); // Note: calling defineFunction with a type that's already been defined only +// works because the same htmlBuilder and mathmlBuilder are being used. + +defineFunction({ + type: "op", + names: ["\\mathop"], + props: { + numArgs: 1, + primitive: true + }, + handler: function handler(_ref2, args) { + var parser = _ref2.parser; + var body = args[0]; + return { + type: "op", + mode: parser.mode, + limits: false, + parentIsSupSub: false, + symbol: false, + body: ordargument(body) + }; + }, + htmlBuilder: op_htmlBuilder, + mathmlBuilder: op_mathmlBuilder +}); // There are 2 flags for operators; whether they produce limits in +// displaystyle, and whether they are symbols and should grow in +// displaystyle. These four groups cover the four possible choices. + +var singleCharIntegrals = { + "\u222B": "\\int", + "\u222C": "\\iint", + "\u222D": "\\iiint", + "\u222E": "\\oint", + "\u222F": "\\oiint", + "\u2230": "\\oiiint" +}; // No limits, not symbols + +defineFunction({ + type: "op", + names: ["\\arcsin", "\\arccos", "\\arctan", "\\arctg", "\\arcctg", "\\arg", "\\ch", "\\cos", "\\cosec", "\\cosh", "\\cot", "\\cotg", "\\coth", "\\csc", "\\ctg", "\\cth", "\\deg", "\\dim", "\\exp", "\\hom", "\\ker", "\\lg", "\\ln", "\\log", "\\sec", "\\sin", "\\sinh", "\\sh", "\\tan", "\\tanh", "\\tg", "\\th"], + props: { + numArgs: 0 + }, + handler: function handler(_ref3) { + var parser = _ref3.parser, + funcName = _ref3.funcName; + return { + type: "op", + mode: parser.mode, + limits: false, + parentIsSupSub: false, + symbol: false, + name: funcName + }; + }, + htmlBuilder: op_htmlBuilder, + mathmlBuilder: op_mathmlBuilder +}); // Limits, not symbols + +defineFunction({ + type: "op", + names: ["\\det", "\\gcd", "\\inf", "\\lim", "\\max", "\\min", "\\Pr", "\\sup"], + props: { + numArgs: 0 + }, + handler: function handler(_ref4) { + var parser = _ref4.parser, + funcName = _ref4.funcName; + return { + type: "op", + mode: parser.mode, + limits: true, + parentIsSupSub: false, + symbol: false, + name: funcName + }; + }, + htmlBuilder: op_htmlBuilder, + mathmlBuilder: op_mathmlBuilder +}); // No limits, symbols + +defineFunction({ + type: "op", + names: ["\\int", "\\iint", "\\iiint", "\\oint", "\\oiint", "\\oiiint", "\u222B", "\u222C", "\u222D", "\u222E", "\u222F", "\u2230"], + props: { + numArgs: 0 + }, + handler: function handler(_ref5) { + var parser = _ref5.parser, + funcName = _ref5.funcName; + var fName = funcName; + + if (fName.length === 1) { + fName = singleCharIntegrals[fName]; + } + + return { + type: "op", + mode: parser.mode, + limits: false, + parentIsSupSub: false, + symbol: true, + name: fName + }; + }, + htmlBuilder: op_htmlBuilder, + mathmlBuilder: op_mathmlBuilder +}); +;// CONCATENATED MODULE: ./src/macros.js +/** + * Predefined macros for KaTeX. + * This can be used to define some commands in terms of others. + */ + + + + + + +var builtinMacros = {}; +/* harmony default export */ var macros = (builtinMacros); // This function might one day accept an additional argument and do more things. + +function defineMacro(name, body) { + builtinMacros[name] = body; +} ////////////////////////////////////////////////////////////////////// +// macro tools + +defineMacro("\\noexpand", function (context) { + // The expansion is the token itself; but that token is interpreted + // as if its meaning were ‘\relax’ if it is a control sequence that + // would ordinarily be expanded by TeX’s expansion rules. + var t = context.popToken(); + + if (context.isExpandable(t.text)) { + t.noexpand = true; + t.treatAsRelax = true; + } + + return { + tokens: [t], + numArgs: 0 + }; +}); +defineMacro("\\expandafter", function (context) { + // TeX first reads the token that comes immediately after \expandafter, + // without expanding it; let’s call this token t. Then TeX reads the + // token that comes after t (and possibly more tokens, if that token + // has an argument), replacing it by its expansion. Finally TeX puts + // t back in front of that expansion. + var t = context.popToken(); + context.expandOnce(true); // expand only an expandable token + + return { + tokens: [t], + numArgs: 0 + }; +}); // LaTeX's \@firstoftwo{#1}{#2} expands to #1, skipping #2 +// TeX source: \long\def\@firstoftwo#1#2{#1} + +defineMacro("\\@firstoftwo", function (context) { + var args = context.consumeArgs(2); + return { + tokens: args[0], + numArgs: 0 + }; +}); // LaTeX's \@secondoftwo{#1}{#2} expands to #2, skipping #1 +// TeX source: \long\def\@secondoftwo#1#2{#2} + +defineMacro("\\@secondoftwo", function (context) { + var args = context.consumeArgs(2); + return { + tokens: args[1], + numArgs: 0 + }; +}); // LaTeX's \@ifnextchar{#1}{#2}{#3} looks ahead to the next (unexpanded) +// symbol that isn't a space, consuming any spaces but not consuming the +// first nonspace character. If that nonspace character matches #1, then +// the macro expands to #2; otherwise, it expands to #3. + +defineMacro("\\@ifnextchar", function (context) { + var args = context.consumeArgs(3); // symbol, if, else + + context.consumeSpaces(); + var nextToken = context.future(); + + if (args[0].length === 1 && args[0][0].text === nextToken.text) { + return { + tokens: args[1], + numArgs: 0 + }; + } else { + return { + tokens: args[2], + numArgs: 0 + }; + } +}); // LaTeX's \@ifstar{#1}{#2} looks ahead to the next (unexpanded) symbol. +// If it is `*`, then it consumes the symbol, and the macro expands to #1; +// otherwise, the macro expands to #2 (without consuming the symbol). +// TeX source: \def\@ifstar#1{\@ifnextchar *{\@firstoftwo{#1}}} + +defineMacro("\\@ifstar", "\\@ifnextchar *{\\@firstoftwo{#1}}"); // LaTeX's \TextOrMath{#1}{#2} expands to #1 in text mode, #2 in math mode + +defineMacro("\\TextOrMath", function (context) { + var args = context.consumeArgs(2); + + if (context.mode === 'text') { + return { + tokens: args[0], + numArgs: 0 + }; + } else { + return { + tokens: args[1], + numArgs: 0 + }; + } +}); // Lookup table for parsing numbers in base 8 through 16 + +var digitToNumber = { + "0": 0, + "1": 1, + "2": 2, + "3": 3, + "4": 4, + "5": 5, + "6": 6, + "7": 7, + "8": 8, + "9": 9, + "a": 10, + "A": 10, + "b": 11, + "B": 11, + "c": 12, + "C": 12, + "d": 13, + "D": 13, + "e": 14, + "E": 14, + "f": 15, + "F": 15 +}; // TeX \char makes a literal character (catcode 12) using the following forms: +// (see The TeXBook, p. 43) +// \char123 -- decimal +// \char'123 -- octal +// \char"123 -- hex +// \char`x -- character that can be written (i.e. isn't active) +// \char`\x -- character that cannot be written (e.g. %) +// These all refer to characters from the font, so we turn them into special +// calls to a function \@char dealt with in the Parser. + +defineMacro("\\char", function (context) { + var token = context.popToken(); + var base; + var number = ''; + + if (token.text === "'") { + base = 8; + token = context.popToken(); + } else if (token.text === '"') { + base = 16; + token = context.popToken(); + } else if (token.text === "`") { + token = context.popToken(); + + if (token.text[0] === "\\") { + number = token.text.charCodeAt(1); + } else if (token.text === "EOF") { + throw new src_ParseError("\\char` missing argument"); + } else { + number = token.text.charCodeAt(0); + } + } else { + base = 10; + } + + if (base) { + // Parse a number in the given base, starting with first `token`. + number = digitToNumber[token.text]; + + if (number == null || number >= base) { + throw new src_ParseError("Invalid base-" + base + " digit " + token.text); + } + + var digit; + + while ((digit = digitToNumber[context.future().text]) != null && digit < base) { + number *= base; + number += digit; + context.popToken(); + } + } + + return "\\@char{" + number + "}"; +}); // \newcommand{\macro}[args]{definition} +// \renewcommand{\macro}[args]{definition} +// TODO: Optional arguments: \newcommand{\macro}[args][default]{definition} + +var newcommand = function newcommand(context, existsOK, nonexistsOK) { + var arg = context.consumeArg().tokens; + + if (arg.length !== 1) { + throw new src_ParseError("\\newcommand's first argument must be a macro name"); + } + + var name = arg[0].text; + var exists = context.isDefined(name); + + if (exists && !existsOK) { + throw new src_ParseError("\\newcommand{" + name + "} attempting to redefine " + (name + "; use \\renewcommand")); + } + + if (!exists && !nonexistsOK) { + throw new src_ParseError("\\renewcommand{" + name + "} when command " + name + " " + "does not yet exist; use \\newcommand"); + } + + var numArgs = 0; + arg = context.consumeArg().tokens; + + if (arg.length === 1 && arg[0].text === "[") { + var argText = ''; + var token = context.expandNextToken(); + + while (token.text !== "]" && token.text !== "EOF") { + // TODO: Should properly expand arg, e.g., ignore {}s + argText += token.text; + token = context.expandNextToken(); + } + + if (!argText.match(/^\s*[0-9]+\s*$/)) { + throw new src_ParseError("Invalid number of arguments: " + argText); + } + + numArgs = parseInt(argText); + arg = context.consumeArg().tokens; + } // Final arg is the expansion of the macro + + + context.macros.set(name, { + tokens: arg, + numArgs: numArgs + }); + return ''; +}; + +defineMacro("\\newcommand", function (context) { + return newcommand(context, false, true); +}); +defineMacro("\\renewcommand", function (context) { + return newcommand(context, true, false); +}); +defineMacro("\\providecommand", function (context) { + return newcommand(context, true, true); +}); // terminal (console) tools + +defineMacro("\\message", function (context) { + var arg = context.consumeArgs(1)[0]; // eslint-disable-next-line no-console + + console.log(arg.reverse().map(function (token) { + return token.text; + }).join("")); + return ''; +}); +defineMacro("\\errmessage", function (context) { + var arg = context.consumeArgs(1)[0]; // eslint-disable-next-line no-console + + console.error(arg.reverse().map(function (token) { + return token.text; + }).join("")); + return ''; +}); +defineMacro("\\show", function (context) { + var tok = context.popToken(); + var name = tok.text; // eslint-disable-next-line no-console + + console.log(tok, context.macros.get(name), src_functions[name], src_symbols.math[name], src_symbols.text[name]); + return ''; +}); ////////////////////////////////////////////////////////////////////// +// Grouping +// \let\bgroup={ \let\egroup=} + +defineMacro("\\bgroup", "{"); +defineMacro("\\egroup", "}"); // Symbols from latex.ltx: +// \def~{\nobreakspace{}} +// \def\lq{`} +// \def\rq{'} +// \def \aa {\r a} +// \def \AA {\r A} + +defineMacro("~", "\\nobreakspace"); +defineMacro("\\lq", "`"); +defineMacro("\\rq", "'"); +defineMacro("\\aa", "\\r a"); +defineMacro("\\AA", "\\r A"); // Copyright (C) and registered (R) symbols. Use raw symbol in MathML. +// \DeclareTextCommandDefault{\textcopyright}{\textcircled{c}} +// \DeclareTextCommandDefault{\textregistered}{\textcircled{% +// \check@mathfonts\fontsize\sf@size\z@\math@fontsfalse\selectfont R}} +// \DeclareRobustCommand{\copyright}{% +// \ifmmode{\nfss@text{\textcopyright}}\else\textcopyright\fi} + +defineMacro("\\textcopyright", "\\html@mathml{\\textcircled{c}}{\\char`©}"); +defineMacro("\\copyright", "\\TextOrMath{\\textcopyright}{\\text{\\textcopyright}}"); +defineMacro("\\textregistered", "\\html@mathml{\\textcircled{\\scriptsize R}}{\\char`®}"); // Characters omitted from Unicode range 1D400–1D7FF + +defineMacro("\u212C", "\\mathscr{B}"); // script + +defineMacro("\u2130", "\\mathscr{E}"); +defineMacro("\u2131", "\\mathscr{F}"); +defineMacro("\u210B", "\\mathscr{H}"); +defineMacro("\u2110", "\\mathscr{I}"); +defineMacro("\u2112", "\\mathscr{L}"); +defineMacro("\u2133", "\\mathscr{M}"); +defineMacro("\u211B", "\\mathscr{R}"); +defineMacro("\u212D", "\\mathfrak{C}"); // Fraktur + +defineMacro("\u210C", "\\mathfrak{H}"); +defineMacro("\u2128", "\\mathfrak{Z}"); // Define \Bbbk with a macro that works in both HTML and MathML. + +defineMacro("\\Bbbk", "\\Bbb{k}"); // Unicode middle dot +// The KaTeX fonts do not contain U+00B7. Instead, \cdotp displays +// the dot at U+22C5 and gives it punct spacing. + +defineMacro("\xB7", "\\cdotp"); // \llap and \rlap render their contents in text mode + +defineMacro("\\llap", "\\mathllap{\\textrm{#1}}"); +defineMacro("\\rlap", "\\mathrlap{\\textrm{#1}}"); +defineMacro("\\clap", "\\mathclap{\\textrm{#1}}"); // \mathstrut from the TeXbook, p 360 + +defineMacro("\\mathstrut", "\\vphantom{(}"); // \underbar from TeXbook p 353 + +defineMacro("\\underbar", "\\underline{\\text{#1}}"); // \not is defined by base/fontmath.ltx via +// \DeclareMathSymbol{\not}{\mathrel}{symbols}{"36} +// It's thus treated like a \mathrel, but defined by a symbol that has zero +// width but extends to the right. We use \rlap to get that spacing. +// For MathML we write U+0338 here. buildMathML.js will then do the overlay. + +defineMacro("\\not", '\\html@mathml{\\mathrel{\\mathrlap\\@not}}{\\char"338}'); // Negated symbols from base/fontmath.ltx: +// \def\neq{\not=} \let\ne=\neq +// \DeclareRobustCommand +// \notin{\mathrel{\m@th\mathpalette\c@ncel\in}} +// \def\c@ncel#1#2{\m@th\ooalign{$\hfil#1\mkern1mu/\hfil$\crcr$#1#2$}} + +defineMacro("\\neq", "\\html@mathml{\\mathrel{\\not=}}{\\mathrel{\\char`≠}}"); +defineMacro("\\ne", "\\neq"); +defineMacro("\u2260", "\\neq"); +defineMacro("\\notin", "\\html@mathml{\\mathrel{{\\in}\\mathllap{/\\mskip1mu}}}" + "{\\mathrel{\\char`∉}}"); +defineMacro("\u2209", "\\notin"); // Unicode stacked relations + +defineMacro("\u2258", "\\html@mathml{" + "\\mathrel{=\\kern{-1em}\\raisebox{0.4em}{$\\scriptsize\\frown$}}" + "}{\\mathrel{\\char`\u2258}}"); +defineMacro("\u2259", "\\html@mathml{\\stackrel{\\tiny\\wedge}{=}}{\\mathrel{\\char`\u2258}}"); +defineMacro("\u225A", "\\html@mathml{\\stackrel{\\tiny\\vee}{=}}{\\mathrel{\\char`\u225A}}"); +defineMacro("\u225B", "\\html@mathml{\\stackrel{\\scriptsize\\star}{=}}" + "{\\mathrel{\\char`\u225B}}"); +defineMacro("\u225D", "\\html@mathml{\\stackrel{\\tiny\\mathrm{def}}{=}}" + "{\\mathrel{\\char`\u225D}}"); +defineMacro("\u225E", "\\html@mathml{\\stackrel{\\tiny\\mathrm{m}}{=}}" + "{\\mathrel{\\char`\u225E}}"); +defineMacro("\u225F", "\\html@mathml{\\stackrel{\\tiny?}{=}}{\\mathrel{\\char`\u225F}}"); // Misc Unicode + +defineMacro("\u27C2", "\\perp"); +defineMacro("\u203C", "\\mathclose{!\\mkern-0.8mu!}"); +defineMacro("\u220C", "\\notni"); +defineMacro("\u231C", "\\ulcorner"); +defineMacro("\u231D", "\\urcorner"); +defineMacro("\u231E", "\\llcorner"); +defineMacro("\u231F", "\\lrcorner"); +defineMacro("\xA9", "\\copyright"); +defineMacro("\xAE", "\\textregistered"); +defineMacro("\uFE0F", "\\textregistered"); // The KaTeX fonts have corners at codepoints that don't match Unicode. +// For MathML purposes, use the Unicode code point. + +defineMacro("\\ulcorner", "\\html@mathml{\\@ulcorner}{\\mathop{\\char\"231c}}"); +defineMacro("\\urcorner", "\\html@mathml{\\@urcorner}{\\mathop{\\char\"231d}}"); +defineMacro("\\llcorner", "\\html@mathml{\\@llcorner}{\\mathop{\\char\"231e}}"); +defineMacro("\\lrcorner", "\\html@mathml{\\@lrcorner}{\\mathop{\\char\"231f}}"); ////////////////////////////////////////////////////////////////////// +// LaTeX_2ε +// \vdots{\vbox{\baselineskip4\p@ \lineskiplimit\z@ +// \kern6\p@\hbox{.}\hbox{.}\hbox{.}}} +// We'll call \varvdots, which gets a glyph from symbols.js. +// The zero-width rule gets us an equivalent to the vertical 6pt kern. + +defineMacro("\\vdots", "\\mathord{\\varvdots\\rule{0pt}{15pt}}"); +defineMacro("\u22EE", "\\vdots"); ////////////////////////////////////////////////////////////////////// +// amsmath.sty +// http://mirrors.concertpass.com/tex-archive/macros/latex/required/amsmath/amsmath.pdf +// Italic Greek capital letters. AMS defines these with \DeclareMathSymbol, +// but they are equivalent to \mathit{\Letter}. + +defineMacro("\\varGamma", "\\mathit{\\Gamma}"); +defineMacro("\\varDelta", "\\mathit{\\Delta}"); +defineMacro("\\varTheta", "\\mathit{\\Theta}"); +defineMacro("\\varLambda", "\\mathit{\\Lambda}"); +defineMacro("\\varXi", "\\mathit{\\Xi}"); +defineMacro("\\varPi", "\\mathit{\\Pi}"); +defineMacro("\\varSigma", "\\mathit{\\Sigma}"); +defineMacro("\\varUpsilon", "\\mathit{\\Upsilon}"); +defineMacro("\\varPhi", "\\mathit{\\Phi}"); +defineMacro("\\varPsi", "\\mathit{\\Psi}"); +defineMacro("\\varOmega", "\\mathit{\\Omega}"); //\newcommand{\substack}[1]{\subarray{c}#1\endsubarray} + +defineMacro("\\substack", "\\begin{subarray}{c}#1\\end{subarray}"); // \renewcommand{\colon}{\nobreak\mskip2mu\mathpunct{}\nonscript +// \mkern-\thinmuskip{:}\mskip6muplus1mu\relax} + +defineMacro("\\colon", "\\nobreak\\mskip2mu\\mathpunct{}" + "\\mathchoice{\\mkern-3mu}{\\mkern-3mu}{}{}{:}\\mskip6mu"); // \newcommand{\boxed}[1]{\fbox{\m@th$\displaystyle#1$}} + +defineMacro("\\boxed", "\\fbox{$\\displaystyle{#1}$}"); // \def\iff{\DOTSB\;\Longleftrightarrow\;} +// \def\implies{\DOTSB\;\Longrightarrow\;} +// \def\impliedby{\DOTSB\;\Longleftarrow\;} + +defineMacro("\\iff", "\\DOTSB\\;\\Longleftrightarrow\\;"); +defineMacro("\\implies", "\\DOTSB\\;\\Longrightarrow\\;"); +defineMacro("\\impliedby", "\\DOTSB\\;\\Longleftarrow\\;"); // AMSMath's automatic \dots, based on \mdots@@ macro. + +var dotsByToken = { + ',': '\\dotsc', + '\\not': '\\dotsb', + // \keybin@ checks for the following: + '+': '\\dotsb', + '=': '\\dotsb', + '<': '\\dotsb', + '>': '\\dotsb', + '-': '\\dotsb', + '*': '\\dotsb', + ':': '\\dotsb', + // Symbols whose definition starts with \DOTSB: + '\\DOTSB': '\\dotsb', + '\\coprod': '\\dotsb', + '\\bigvee': '\\dotsb', + '\\bigwedge': '\\dotsb', + '\\biguplus': '\\dotsb', + '\\bigcap': '\\dotsb', + '\\bigcup': '\\dotsb', + '\\prod': '\\dotsb', + '\\sum': '\\dotsb', + '\\bigotimes': '\\dotsb', + '\\bigoplus': '\\dotsb', + '\\bigodot': '\\dotsb', + '\\bigsqcup': '\\dotsb', + '\\And': '\\dotsb', + '\\longrightarrow': '\\dotsb', + '\\Longrightarrow': '\\dotsb', + '\\longleftarrow': '\\dotsb', + '\\Longleftarrow': '\\dotsb', + '\\longleftrightarrow': '\\dotsb', + '\\Longleftrightarrow': '\\dotsb', + '\\mapsto': '\\dotsb', + '\\longmapsto': '\\dotsb', + '\\hookrightarrow': '\\dotsb', + '\\doteq': '\\dotsb', + // Symbols whose definition starts with \mathbin: + '\\mathbin': '\\dotsb', + // Symbols whose definition starts with \mathrel: + '\\mathrel': '\\dotsb', + '\\relbar': '\\dotsb', + '\\Relbar': '\\dotsb', + '\\xrightarrow': '\\dotsb', + '\\xleftarrow': '\\dotsb', + // Symbols whose definition starts with \DOTSI: + '\\DOTSI': '\\dotsi', + '\\int': '\\dotsi', + '\\oint': '\\dotsi', + '\\iint': '\\dotsi', + '\\iiint': '\\dotsi', + '\\iiiint': '\\dotsi', + '\\idotsint': '\\dotsi', + // Symbols whose definition starts with \DOTSX: + '\\DOTSX': '\\dotsx' +}; +defineMacro("\\dots", function (context) { + // TODO: If used in text mode, should expand to \textellipsis. + // However, in KaTeX, \textellipsis and \ldots behave the same + // (in text mode), and it's unlikely we'd see any of the math commands + // that affect the behavior of \dots when in text mode. So fine for now + // (until we support \ifmmode ... \else ... \fi). + var thedots = '\\dotso'; + var next = context.expandAfterFuture().text; + + if (next in dotsByToken) { + thedots = dotsByToken[next]; + } else if (next.substr(0, 4) === '\\not') { + thedots = '\\dotsb'; + } else if (next in src_symbols.math) { + if (utils.contains(['bin', 'rel'], src_symbols.math[next].group)) { + thedots = '\\dotsb'; + } + } + + return thedots; +}); +var spaceAfterDots = { + // \rightdelim@ checks for the following: + ')': true, + ']': true, + '\\rbrack': true, + '\\}': true, + '\\rbrace': true, + '\\rangle': true, + '\\rceil': true, + '\\rfloor': true, + '\\rgroup': true, + '\\rmoustache': true, + '\\right': true, + '\\bigr': true, + '\\biggr': true, + '\\Bigr': true, + '\\Biggr': true, + // \extra@ also tests for the following: + '$': true, + // \extrap@ checks for the following: + ';': true, + '.': true, + ',': true +}; +defineMacro("\\dotso", function (context) { + var next = context.future().text; + + if (next in spaceAfterDots) { + return "\\ldots\\,"; + } else { + return "\\ldots"; + } +}); +defineMacro("\\dotsc", function (context) { + var next = context.future().text; // \dotsc uses \extra@ but not \extrap@, instead specially checking for + // ';' and '.', but doesn't check for ','. + + if (next in spaceAfterDots && next !== ',') { + return "\\ldots\\,"; + } else { + return "\\ldots"; + } +}); +defineMacro("\\cdots", function (context) { + var next = context.future().text; + + if (next in spaceAfterDots) { + return "\\@cdots\\,"; + } else { + return "\\@cdots"; + } +}); +defineMacro("\\dotsb", "\\cdots"); +defineMacro("\\dotsm", "\\cdots"); +defineMacro("\\dotsi", "\\!\\cdots"); // amsmath doesn't actually define \dotsx, but \dots followed by a macro +// starting with \DOTSX implies \dotso, and then \extra@ detects this case +// and forces the added `\,`. + +defineMacro("\\dotsx", "\\ldots\\,"); // \let\DOTSI\relax +// \let\DOTSB\relax +// \let\DOTSX\relax + +defineMacro("\\DOTSI", "\\relax"); +defineMacro("\\DOTSB", "\\relax"); +defineMacro("\\DOTSX", "\\relax"); // Spacing, based on amsmath.sty's override of LaTeX defaults +// \DeclareRobustCommand{\tmspace}[3]{% +// \ifmmode\mskip#1#2\else\kern#1#3\fi\relax} + +defineMacro("\\tmspace", "\\TextOrMath{\\kern#1#3}{\\mskip#1#2}\\relax"); // \renewcommand{\,}{\tmspace+\thinmuskip{.1667em}} +// TODO: math mode should use \thinmuskip + +defineMacro("\\,", "\\tmspace+{3mu}{.1667em}"); // \let\thinspace\, + +defineMacro("\\thinspace", "\\,"); // \def\>{\mskip\medmuskip} +// \renewcommand{\:}{\tmspace+\medmuskip{.2222em}} +// TODO: \> and math mode of \: should use \medmuskip = 4mu plus 2mu minus 4mu + +defineMacro("\\>", "\\mskip{4mu}"); +defineMacro("\\:", "\\tmspace+{4mu}{.2222em}"); // \let\medspace\: + +defineMacro("\\medspace", "\\:"); // \renewcommand{\;}{\tmspace+\thickmuskip{.2777em}} +// TODO: math mode should use \thickmuskip = 5mu plus 5mu + +defineMacro("\\;", "\\tmspace+{5mu}{.2777em}"); // \let\thickspace\; + +defineMacro("\\thickspace", "\\;"); // \renewcommand{\!}{\tmspace-\thinmuskip{.1667em}} +// TODO: math mode should use \thinmuskip + +defineMacro("\\!", "\\tmspace-{3mu}{.1667em}"); // \let\negthinspace\! + +defineMacro("\\negthinspace", "\\!"); // \newcommand{\negmedspace}{\tmspace-\medmuskip{.2222em}} +// TODO: math mode should use \medmuskip + +defineMacro("\\negmedspace", "\\tmspace-{4mu}{.2222em}"); // \newcommand{\negthickspace}{\tmspace-\thickmuskip{.2777em}} +// TODO: math mode should use \thickmuskip + +defineMacro("\\negthickspace", "\\tmspace-{5mu}{.277em}"); // \def\enspace{\kern.5em } + +defineMacro("\\enspace", "\\kern.5em "); // \def\enskip{\hskip.5em\relax} + +defineMacro("\\enskip", "\\hskip.5em\\relax"); // \def\quad{\hskip1em\relax} + +defineMacro("\\quad", "\\hskip1em\\relax"); // \def\qquad{\hskip2em\relax} + +defineMacro("\\qquad", "\\hskip2em\\relax"); // \tag@in@display form of \tag + +defineMacro("\\tag", "\\@ifstar\\tag@literal\\tag@paren"); +defineMacro("\\tag@paren", "\\tag@literal{({#1})}"); +defineMacro("\\tag@literal", function (context) { + if (context.macros.get("\\df@tag")) { + throw new src_ParseError("Multiple \\tag"); + } + + return "\\gdef\\df@tag{\\text{#1}}"; +}); // \renewcommand{\bmod}{\nonscript\mskip-\medmuskip\mkern5mu\mathbin +// {\operator@font mod}\penalty900 +// \mkern5mu\nonscript\mskip-\medmuskip} +// \newcommand{\pod}[1]{\allowbreak +// \if@display\mkern18mu\else\mkern8mu\fi(#1)} +// \renewcommand{\pmod}[1]{\pod{{\operator@font mod}\mkern6mu#1}} +// \newcommand{\mod}[1]{\allowbreak\if@display\mkern18mu +// \else\mkern12mu\fi{\operator@font mod}\,\,#1} +// TODO: math mode should use \medmuskip = 4mu plus 2mu minus 4mu + +defineMacro("\\bmod", "\\mathchoice{\\mskip1mu}{\\mskip1mu}{\\mskip5mu}{\\mskip5mu}" + "\\mathbin{\\rm mod}" + "\\mathchoice{\\mskip1mu}{\\mskip1mu}{\\mskip5mu}{\\mskip5mu}"); +defineMacro("\\pod", "\\allowbreak" + "\\mathchoice{\\mkern18mu}{\\mkern8mu}{\\mkern8mu}{\\mkern8mu}(#1)"); +defineMacro("\\pmod", "\\pod{{\\rm mod}\\mkern6mu#1}"); +defineMacro("\\mod", "\\allowbreak" + "\\mathchoice{\\mkern18mu}{\\mkern12mu}{\\mkern12mu}{\\mkern12mu}" + "{\\rm mod}\\,\\,#1"); // \pmb -- A simulation of bold. +// The version in ambsy.sty works by typesetting three copies of the argument +// with small offsets. We use two copies. We omit the vertical offset because +// of rendering problems that makeVList encounters in Safari. + +defineMacro("\\pmb", "\\html@mathml{" + "\\@binrel{#1}{\\mathrlap{#1}\\kern0.5px#1}}" + "{\\mathbf{#1}}"); ////////////////////////////////////////////////////////////////////// +// LaTeX source2e +// \expandafter\let\expandafter\@normalcr +// \csname\expandafter\@gobble\string\\ \endcsname +// \DeclareRobustCommand\newline{\@normalcr\relax} + +defineMacro("\\newline", "\\\\\\relax"); // \def\TeX{T\kern-.1667em\lower.5ex\hbox{E}\kern-.125emX\@} +// TODO: Doesn't normally work in math mode because \@ fails. KaTeX doesn't +// support \@ yet, so that's omitted, and we add \text so that the result +// doesn't look funny in math mode. + +defineMacro("\\TeX", "\\textrm{\\html@mathml{" + "T\\kern-.1667em\\raisebox{-.5ex}{E}\\kern-.125emX" + "}{TeX}}"); // \DeclareRobustCommand{\LaTeX}{L\kern-.36em% +// {\sbox\z@ T% +// \vbox to\ht\z@{\hbox{\check@mathfonts +// \fontsize\sf@size\z@ +// \math@fontsfalse\selectfont +// A}% +// \vss}% +// }% +// \kern-.15em% +// \TeX} +// This code aligns the top of the A with the T (from the perspective of TeX's +// boxes, though visually the A appears to extend above slightly). +// We compute the corresponding \raisebox when A is rendered in \normalsize +// \scriptstyle, which has a scale factor of 0.7 (see Options.js). + +var latexRaiseA = fontMetricsData["Main-Regular"]["T".charCodeAt(0)][1] - 0.7 * fontMetricsData["Main-Regular"]["A".charCodeAt(0)][1] + "em"; +defineMacro("\\LaTeX", "\\textrm{\\html@mathml{" + ("L\\kern-.36em\\raisebox{" + latexRaiseA + "}{\\scriptstyle A}") + "\\kern-.15em\\TeX}{LaTeX}}"); // New KaTeX logo based on tweaking LaTeX logo + +defineMacro("\\KaTeX", "\\textrm{\\html@mathml{" + ("K\\kern-.17em\\raisebox{" + latexRaiseA + "}{\\scriptstyle A}") + "\\kern-.15em\\TeX}{KaTeX}}"); // \DeclareRobustCommand\hspace{\@ifstar\@hspacer\@hspace} +// \def\@hspace#1{\hskip #1\relax} +// \def\@hspacer#1{\vrule \@width\z@\nobreak +// \hskip #1\hskip \z@skip} + +defineMacro("\\hspace", "\\@ifstar\\@hspacer\\@hspace"); +defineMacro("\\@hspace", "\\hskip #1\\relax"); +defineMacro("\\@hspacer", "\\rule{0pt}{0pt}\\hskip #1\\relax"); ////////////////////////////////////////////////////////////////////// +// mathtools.sty +//\providecommand\ordinarycolon{:} + +defineMacro("\\ordinarycolon", ":"); //\def\vcentcolon{\mathrel{\mathop\ordinarycolon}} +//TODO(edemaine): Not yet centered. Fix via \raisebox or #726 + +defineMacro("\\vcentcolon", "\\mathrel{\\mathop\\ordinarycolon}"); // \providecommand*\dblcolon{\vcentcolon\mathrel{\mkern-.9mu}\vcentcolon} + +defineMacro("\\dblcolon", "\\html@mathml{" + "\\mathrel{\\vcentcolon\\mathrel{\\mkern-.9mu}\\vcentcolon}}" + "{\\mathop{\\char\"2237}}"); // \providecommand*\coloneqq{\vcentcolon\mathrel{\mkern-1.2mu}=} + +defineMacro("\\coloneqq", "\\html@mathml{" + "\\mathrel{\\vcentcolon\\mathrel{\\mkern-1.2mu}=}}" + "{\\mathop{\\char\"2254}}"); // ≔ +// \providecommand*\Coloneqq{\dblcolon\mathrel{\mkern-1.2mu}=} + +defineMacro("\\Coloneqq", "\\html@mathml{" + "\\mathrel{\\dblcolon\\mathrel{\\mkern-1.2mu}=}}" + "{\\mathop{\\char\"2237\\char\"3d}}"); // \providecommand*\coloneq{\vcentcolon\mathrel{\mkern-1.2mu}\mathrel{-}} + +defineMacro("\\coloneq", "\\html@mathml{" + "\\mathrel{\\vcentcolon\\mathrel{\\mkern-1.2mu}\\mathrel{-}}}" + "{\\mathop{\\char\"3a\\char\"2212}}"); // \providecommand*\Coloneq{\dblcolon\mathrel{\mkern-1.2mu}\mathrel{-}} + +defineMacro("\\Coloneq", "\\html@mathml{" + "\\mathrel{\\dblcolon\\mathrel{\\mkern-1.2mu}\\mathrel{-}}}" + "{\\mathop{\\char\"2237\\char\"2212}}"); // \providecommand*\eqqcolon{=\mathrel{\mkern-1.2mu}\vcentcolon} + +defineMacro("\\eqqcolon", "\\html@mathml{" + "\\mathrel{=\\mathrel{\\mkern-1.2mu}\\vcentcolon}}" + "{\\mathop{\\char\"2255}}"); // ≕ +// \providecommand*\Eqqcolon{=\mathrel{\mkern-1.2mu}\dblcolon} + +defineMacro("\\Eqqcolon", "\\html@mathml{" + "\\mathrel{=\\mathrel{\\mkern-1.2mu}\\dblcolon}}" + "{\\mathop{\\char\"3d\\char\"2237}}"); // \providecommand*\eqcolon{\mathrel{-}\mathrel{\mkern-1.2mu}\vcentcolon} + +defineMacro("\\eqcolon", "\\html@mathml{" + "\\mathrel{\\mathrel{-}\\mathrel{\\mkern-1.2mu}\\vcentcolon}}" + "{\\mathop{\\char\"2239}}"); // \providecommand*\Eqcolon{\mathrel{-}\mathrel{\mkern-1.2mu}\dblcolon} + +defineMacro("\\Eqcolon", "\\html@mathml{" + "\\mathrel{\\mathrel{-}\\mathrel{\\mkern-1.2mu}\\dblcolon}}" + "{\\mathop{\\char\"2212\\char\"2237}}"); // \providecommand*\colonapprox{\vcentcolon\mathrel{\mkern-1.2mu}\approx} + +defineMacro("\\colonapprox", "\\html@mathml{" + "\\mathrel{\\vcentcolon\\mathrel{\\mkern-1.2mu}\\approx}}" + "{\\mathop{\\char\"3a\\char\"2248}}"); // \providecommand*\Colonapprox{\dblcolon\mathrel{\mkern-1.2mu}\approx} + +defineMacro("\\Colonapprox", "\\html@mathml{" + "\\mathrel{\\dblcolon\\mathrel{\\mkern-1.2mu}\\approx}}" + "{\\mathop{\\char\"2237\\char\"2248}}"); // \providecommand*\colonsim{\vcentcolon\mathrel{\mkern-1.2mu}\sim} + +defineMacro("\\colonsim", "\\html@mathml{" + "\\mathrel{\\vcentcolon\\mathrel{\\mkern-1.2mu}\\sim}}" + "{\\mathop{\\char\"3a\\char\"223c}}"); // \providecommand*\Colonsim{\dblcolon\mathrel{\mkern-1.2mu}\sim} + +defineMacro("\\Colonsim", "\\html@mathml{" + "\\mathrel{\\dblcolon\\mathrel{\\mkern-1.2mu}\\sim}}" + "{\\mathop{\\char\"2237\\char\"223c}}"); // Some Unicode characters are implemented with macros to mathtools functions. + +defineMacro("\u2237", "\\dblcolon"); // :: + +defineMacro("\u2239", "\\eqcolon"); // -: + +defineMacro("\u2254", "\\coloneqq"); // := + +defineMacro("\u2255", "\\eqqcolon"); // =: + +defineMacro("\u2A74", "\\Coloneqq"); // ::= +////////////////////////////////////////////////////////////////////// +// colonequals.sty +// Alternate names for mathtools's macros: + +defineMacro("\\ratio", "\\vcentcolon"); +defineMacro("\\coloncolon", "\\dblcolon"); +defineMacro("\\colonequals", "\\coloneqq"); +defineMacro("\\coloncolonequals", "\\Coloneqq"); +defineMacro("\\equalscolon", "\\eqqcolon"); +defineMacro("\\equalscoloncolon", "\\Eqqcolon"); +defineMacro("\\colonminus", "\\coloneq"); +defineMacro("\\coloncolonminus", "\\Coloneq"); +defineMacro("\\minuscolon", "\\eqcolon"); +defineMacro("\\minuscoloncolon", "\\Eqcolon"); // \colonapprox name is same in mathtools and colonequals. + +defineMacro("\\coloncolonapprox", "\\Colonapprox"); // \colonsim name is same in mathtools and colonequals. + +defineMacro("\\coloncolonsim", "\\Colonsim"); // Additional macros, implemented by analogy with mathtools definitions: + +defineMacro("\\simcolon", "\\mathrel{\\sim\\mathrel{\\mkern-1.2mu}\\vcentcolon}"); +defineMacro("\\simcoloncolon", "\\mathrel{\\sim\\mathrel{\\mkern-1.2mu}\\dblcolon}"); +defineMacro("\\approxcolon", "\\mathrel{\\approx\\mathrel{\\mkern-1.2mu}\\vcentcolon}"); +defineMacro("\\approxcoloncolon", "\\mathrel{\\approx\\mathrel{\\mkern-1.2mu}\\dblcolon}"); // Present in newtxmath, pxfonts and txfonts + +defineMacro("\\notni", "\\html@mathml{\\not\\ni}{\\mathrel{\\char`\u220C}}"); +defineMacro("\\limsup", "\\DOTSB\\operatorname*{lim\\,sup}"); +defineMacro("\\liminf", "\\DOTSB\\operatorname*{lim\\,inf}"); ////////////////////////////////////////////////////////////////////// +// From amsopn.sty + +defineMacro("\\injlim", "\\DOTSB\\operatorname*{inj\\,lim}"); +defineMacro("\\projlim", "\\DOTSB\\operatorname*{proj\\,lim}"); +defineMacro("\\varlimsup", "\\DOTSB\\operatorname*{\\overline{lim}}"); +defineMacro("\\varliminf", "\\DOTSB\\operatorname*{\\underline{lim}}"); +defineMacro("\\varinjlim", "\\DOTSB\\operatorname*{\\underrightarrow{lim}}"); +defineMacro("\\varprojlim", "\\DOTSB\\operatorname*{\\underleftarrow{lim}}"); ////////////////////////////////////////////////////////////////////// +// MathML alternates for KaTeX glyphs in the Unicode private area + +defineMacro("\\gvertneqq", "\\html@mathml{\\@gvertneqq}{\u2269}"); +defineMacro("\\lvertneqq", "\\html@mathml{\\@lvertneqq}{\u2268}"); +defineMacro("\\ngeqq", "\\html@mathml{\\@ngeqq}{\u2271}"); +defineMacro("\\ngeqslant", "\\html@mathml{\\@ngeqslant}{\u2271}"); +defineMacro("\\nleqq", "\\html@mathml{\\@nleqq}{\u2270}"); +defineMacro("\\nleqslant", "\\html@mathml{\\@nleqslant}{\u2270}"); +defineMacro("\\nshortmid", "\\html@mathml{\\@nshortmid}{∤}"); +defineMacro("\\nshortparallel", "\\html@mathml{\\@nshortparallel}{∦}"); +defineMacro("\\nsubseteqq", "\\html@mathml{\\@nsubseteqq}{\u2288}"); +defineMacro("\\nsupseteqq", "\\html@mathml{\\@nsupseteqq}{\u2289}"); +defineMacro("\\varsubsetneq", "\\html@mathml{\\@varsubsetneq}{⊊}"); +defineMacro("\\varsubsetneqq", "\\html@mathml{\\@varsubsetneqq}{⫋}"); +defineMacro("\\varsupsetneq", "\\html@mathml{\\@varsupsetneq}{⊋}"); +defineMacro("\\varsupsetneqq", "\\html@mathml{\\@varsupsetneqq}{⫌}"); +defineMacro("\\imath", "\\html@mathml{\\@imath}{\u0131}"); +defineMacro("\\jmath", "\\html@mathml{\\@jmath}{\u0237}"); ////////////////////////////////////////////////////////////////////// +// stmaryrd and semantic +// The stmaryrd and semantic packages render the next four items by calling a +// glyph. Those glyphs do not exist in the KaTeX fonts. Hence the macros. + +defineMacro("\\llbracket", "\\html@mathml{" + "\\mathopen{[\\mkern-3.2mu[}}" + "{\\mathopen{\\char`\u27E6}}"); +defineMacro("\\rrbracket", "\\html@mathml{" + "\\mathclose{]\\mkern-3.2mu]}}" + "{\\mathclose{\\char`\u27E7}}"); +defineMacro("\u27E6", "\\llbracket"); // blackboard bold [ + +defineMacro("\u27E7", "\\rrbracket"); // blackboard bold ] + +defineMacro("\\lBrace", "\\html@mathml{" + "\\mathopen{\\{\\mkern-3.2mu[}}" + "{\\mathopen{\\char`\u2983}}"); +defineMacro("\\rBrace", "\\html@mathml{" + "\\mathclose{]\\mkern-3.2mu\\}}}" + "{\\mathclose{\\char`\u2984}}"); +defineMacro("\u2983", "\\lBrace"); // blackboard bold { + +defineMacro("\u2984", "\\rBrace"); // blackboard bold } +// TODO: Create variable sized versions of the last two items. I believe that +// will require new font glyphs. +// The stmaryrd function `\minuso` provides a "Plimsoll" symbol that +// superimposes the characters \circ and \mathminus. Used in chemistry. + +defineMacro("\\minuso", "\\mathbin{\\html@mathml{" + "{\\mathrlap{\\mathchoice{\\kern{0.145em}}{\\kern{0.145em}}" + "{\\kern{0.1015em}}{\\kern{0.0725em}}\\circ}{-}}}" + "{\\char`⦵}}"); +defineMacro("⦵", "\\minuso"); ////////////////////////////////////////////////////////////////////// +// texvc.sty +// The texvc package contains macros available in mediawiki pages. +// We omit the functions deprecated at +// https://en.wikipedia.org/wiki/Help:Displaying_a_formula#Deprecated_syntax +// We also omit texvc's \O, which conflicts with \text{\O} + +defineMacro("\\darr", "\\downarrow"); +defineMacro("\\dArr", "\\Downarrow"); +defineMacro("\\Darr", "\\Downarrow"); +defineMacro("\\lang", "\\langle"); +defineMacro("\\rang", "\\rangle"); +defineMacro("\\uarr", "\\uparrow"); +defineMacro("\\uArr", "\\Uparrow"); +defineMacro("\\Uarr", "\\Uparrow"); +defineMacro("\\N", "\\mathbb{N}"); +defineMacro("\\R", "\\mathbb{R}"); +defineMacro("\\Z", "\\mathbb{Z}"); +defineMacro("\\alef", "\\aleph"); +defineMacro("\\alefsym", "\\aleph"); +defineMacro("\\Alpha", "\\mathrm{A}"); +defineMacro("\\Beta", "\\mathrm{B}"); +defineMacro("\\bull", "\\bullet"); +defineMacro("\\Chi", "\\mathrm{X}"); +defineMacro("\\clubs", "\\clubsuit"); +defineMacro("\\cnums", "\\mathbb{C}"); +defineMacro("\\Complex", "\\mathbb{C}"); +defineMacro("\\Dagger", "\\ddagger"); +defineMacro("\\diamonds", "\\diamondsuit"); +defineMacro("\\empty", "\\emptyset"); +defineMacro("\\Epsilon", "\\mathrm{E}"); +defineMacro("\\Eta", "\\mathrm{H}"); +defineMacro("\\exist", "\\exists"); +defineMacro("\\harr", "\\leftrightarrow"); +defineMacro("\\hArr", "\\Leftrightarrow"); +defineMacro("\\Harr", "\\Leftrightarrow"); +defineMacro("\\hearts", "\\heartsuit"); +defineMacro("\\image", "\\Im"); +defineMacro("\\infin", "\\infty"); +defineMacro("\\Iota", "\\mathrm{I}"); +defineMacro("\\isin", "\\in"); +defineMacro("\\Kappa", "\\mathrm{K}"); +defineMacro("\\larr", "\\leftarrow"); +defineMacro("\\lArr", "\\Leftarrow"); +defineMacro("\\Larr", "\\Leftarrow"); +defineMacro("\\lrarr", "\\leftrightarrow"); +defineMacro("\\lrArr", "\\Leftrightarrow"); +defineMacro("\\Lrarr", "\\Leftrightarrow"); +defineMacro("\\Mu", "\\mathrm{M}"); +defineMacro("\\natnums", "\\mathbb{N}"); +defineMacro("\\Nu", "\\mathrm{N}"); +defineMacro("\\Omicron", "\\mathrm{O}"); +defineMacro("\\plusmn", "\\pm"); +defineMacro("\\rarr", "\\rightarrow"); +defineMacro("\\rArr", "\\Rightarrow"); +defineMacro("\\Rarr", "\\Rightarrow"); +defineMacro("\\real", "\\Re"); +defineMacro("\\reals", "\\mathbb{R}"); +defineMacro("\\Reals", "\\mathbb{R}"); +defineMacro("\\Rho", "\\mathrm{P}"); +defineMacro("\\sdot", "\\cdot"); +defineMacro("\\sect", "\\S"); +defineMacro("\\spades", "\\spadesuit"); +defineMacro("\\sub", "\\subset"); +defineMacro("\\sube", "\\subseteq"); +defineMacro("\\supe", "\\supseteq"); +defineMacro("\\Tau", "\\mathrm{T}"); +defineMacro("\\thetasym", "\\vartheta"); // TODO: defineMacro("\\varcoppa", "\\\mbox{\\coppa}"); + +defineMacro("\\weierp", "\\wp"); +defineMacro("\\Zeta", "\\mathrm{Z}"); ////////////////////////////////////////////////////////////////////// +// statmath.sty +// https://ctan.math.illinois.edu/macros/latex/contrib/statmath/statmath.pdf + +defineMacro("\\argmin", "\\DOTSB\\operatorname*{arg\\,min}"); +defineMacro("\\argmax", "\\DOTSB\\operatorname*{arg\\,max}"); +defineMacro("\\plim", "\\DOTSB\\mathop{\\operatorname{plim}}\\limits"); ////////////////////////////////////////////////////////////////////// +// braket.sty +// http://ctan.math.washington.edu/tex-archive/macros/latex/contrib/braket/braket.pdf + +defineMacro("\\bra", "\\mathinner{\\langle{#1}|}"); +defineMacro("\\ket", "\\mathinner{|{#1}\\rangle}"); +defineMacro("\\braket", "\\mathinner{\\langle{#1}\\rangle}"); +defineMacro("\\Bra", "\\left\\langle#1\\right|"); +defineMacro("\\Ket", "\\left|#1\\right\\rangle"); ////////////////////////////////////////////////////////////////////// +// actuarialangle.dtx + +defineMacro("\\angln", "{\\angl n}"); // Custom Khan Academy colors, should be moved to an optional package + +defineMacro("\\blue", "\\textcolor{##6495ed}{#1}"); +defineMacro("\\orange", "\\textcolor{##ffa500}{#1}"); +defineMacro("\\pink", "\\textcolor{##ff00af}{#1}"); +defineMacro("\\red", "\\textcolor{##df0030}{#1}"); +defineMacro("\\green", "\\textcolor{##28ae7b}{#1}"); +defineMacro("\\gray", "\\textcolor{gray}{#1}"); +defineMacro("\\purple", "\\textcolor{##9d38bd}{#1}"); +defineMacro("\\blueA", "\\textcolor{##ccfaff}{#1}"); +defineMacro("\\blueB", "\\textcolor{##80f6ff}{#1}"); +defineMacro("\\blueC", "\\textcolor{##63d9ea}{#1}"); +defineMacro("\\blueD", "\\textcolor{##11accd}{#1}"); +defineMacro("\\blueE", "\\textcolor{##0c7f99}{#1}"); +defineMacro("\\tealA", "\\textcolor{##94fff5}{#1}"); +defineMacro("\\tealB", "\\textcolor{##26edd5}{#1}"); +defineMacro("\\tealC", "\\textcolor{##01d1c1}{#1}"); +defineMacro("\\tealD", "\\textcolor{##01a995}{#1}"); +defineMacro("\\tealE", "\\textcolor{##208170}{#1}"); +defineMacro("\\greenA", "\\textcolor{##b6ffb0}{#1}"); +defineMacro("\\greenB", "\\textcolor{##8af281}{#1}"); +defineMacro("\\greenC", "\\textcolor{##74cf70}{#1}"); +defineMacro("\\greenD", "\\textcolor{##1fab54}{#1}"); +defineMacro("\\greenE", "\\textcolor{##0d923f}{#1}"); +defineMacro("\\goldA", "\\textcolor{##ffd0a9}{#1}"); +defineMacro("\\goldB", "\\textcolor{##ffbb71}{#1}"); +defineMacro("\\goldC", "\\textcolor{##ff9c39}{#1}"); +defineMacro("\\goldD", "\\textcolor{##e07d10}{#1}"); +defineMacro("\\goldE", "\\textcolor{##a75a05}{#1}"); +defineMacro("\\redA", "\\textcolor{##fca9a9}{#1}"); +defineMacro("\\redB", "\\textcolor{##ff8482}{#1}"); +defineMacro("\\redC", "\\textcolor{##f9685d}{#1}"); +defineMacro("\\redD", "\\textcolor{##e84d39}{#1}"); +defineMacro("\\redE", "\\textcolor{##bc2612}{#1}"); +defineMacro("\\maroonA", "\\textcolor{##ffbde0}{#1}"); +defineMacro("\\maroonB", "\\textcolor{##ff92c6}{#1}"); +defineMacro("\\maroonC", "\\textcolor{##ed5fa6}{#1}"); +defineMacro("\\maroonD", "\\textcolor{##ca337c}{#1}"); +defineMacro("\\maroonE", "\\textcolor{##9e034e}{#1}"); +defineMacro("\\purpleA", "\\textcolor{##ddd7ff}{#1}"); +defineMacro("\\purpleB", "\\textcolor{##c6b9fc}{#1}"); +defineMacro("\\purpleC", "\\textcolor{##aa87ff}{#1}"); +defineMacro("\\purpleD", "\\textcolor{##7854ab}{#1}"); +defineMacro("\\purpleE", "\\textcolor{##543b78}{#1}"); +defineMacro("\\mintA", "\\textcolor{##f5f9e8}{#1}"); +defineMacro("\\mintB", "\\textcolor{##edf2df}{#1}"); +defineMacro("\\mintC", "\\textcolor{##e0e5cc}{#1}"); +defineMacro("\\grayA", "\\textcolor{##f6f7f7}{#1}"); +defineMacro("\\grayB", "\\textcolor{##f0f1f2}{#1}"); +defineMacro("\\grayC", "\\textcolor{##e3e5e6}{#1}"); +defineMacro("\\grayD", "\\textcolor{##d6d8da}{#1}"); +defineMacro("\\grayE", "\\textcolor{##babec2}{#1}"); +defineMacro("\\grayF", "\\textcolor{##888d93}{#1}"); +defineMacro("\\grayG", "\\textcolor{##626569}{#1}"); +defineMacro("\\grayH", "\\textcolor{##3b3e40}{#1}"); +defineMacro("\\grayI", "\\textcolor{##21242c}{#1}"); +defineMacro("\\kaBlue", "\\textcolor{##314453}{#1}"); +defineMacro("\\kaGreen", "\\textcolor{##71B307}{#1}"); +;// CONCATENATED MODULE: ./src/functions/operatorname.js + + + + + + + + + +// NOTE: Unlike most `htmlBuilder`s, this one handles not only +// "operatorname", but also "supsub" since \operatorname* can +// affect super/subscripting. +var operatorname_htmlBuilder = function htmlBuilder(grp, options) { + // Operators are handled in the TeXbook pg. 443-444, rule 13(a). + var supGroup; + var subGroup; + var hasLimits = false; + var group; + + if (grp.type === "supsub") { + // If we have limits, supsub will pass us its group to handle. Pull + // out the superscript and subscript and set the group to the op in + // its base. + supGroup = grp.sup; + subGroup = grp.sub; + group = assertNodeType(grp.base, "operatorname"); + hasLimits = true; + } else { + group = assertNodeType(grp, "operatorname"); + } + + var base; + + if (group.body.length > 0) { + var body = group.body.map(function (child) { + // $FlowFixMe: Check if the node has a string `text` property. + var childText = child.text; + + if (typeof childText === "string") { + return { + type: "textord", + mode: child.mode, + text: childText + }; + } else { + return child; + } + }); // Consolidate function names into symbol characters. + + var expression = buildExpression(body, options.withFont("mathrm"), true); + + for (var i = 0; i < expression.length; i++) { + var child = expression[i]; + + if (child instanceof SymbolNode) { + // Per amsopn package, + // change minus to hyphen and \ast to asterisk + child.text = child.text.replace(/\u2212/, "-").replace(/\u2217/, "*"); + } + } + + base = buildCommon.makeSpan(["mop"], expression, options); + } else { + base = buildCommon.makeSpan(["mop"], [], options); + } + + if (hasLimits) { + return assembleSupSub(base, supGroup, subGroup, options, options.style, 0, 0); + } else { + return base; + } +}; + +var operatorname_mathmlBuilder = function mathmlBuilder(group, options) { + // The steps taken here are similar to the html version. + var expression = buildMathML_buildExpression(group.body, options.withFont("mathrm")); // Is expression a string or has it something like a fraction? + + var isAllString = true; // default + + for (var i = 0; i < expression.length; i++) { + var node = expression[i]; + + if (node instanceof mathMLTree.SpaceNode) {// Do nothing + } else if (node instanceof mathMLTree.MathNode) { + switch (node.type) { + case "mi": + case "mn": + case "ms": + case "mspace": + case "mtext": + break; + // Do nothing yet. + + case "mo": + { + var child = node.children[0]; + + if (node.children.length === 1 && child instanceof mathMLTree.TextNode) { + child.text = child.text.replace(/\u2212/, "-").replace(/\u2217/, "*"); + } else { + isAllString = false; + } + + break; + } + + default: + isAllString = false; + } + } else { + isAllString = false; + } + } + + if (isAllString) { + // Write a single TextNode instead of multiple nested tags. + var word = expression.map(function (node) { + return node.toText(); + }).join(""); + expression = [new mathMLTree.TextNode(word)]; + } + + var identifier = new mathMLTree.MathNode("mi", expression); + identifier.setAttribute("mathvariant", "normal"); // \u2061 is the same as ⁡ + // ref: https://www.w3schools.com/charsets/ref_html_entities_a.asp + + var operator = new mathMLTree.MathNode("mo", [makeText("\u2061", "text")]); + + if (group.parentIsSupSub) { + return new mathMLTree.MathNode("mrow", [identifier, operator]); + } else { + return mathMLTree.newDocumentFragment([identifier, operator]); + } +}; // \operatorname +// amsopn.dtx: \mathop{#1\kern\z@\operator@font#3}\newmcodes@ + + +defineFunction({ + type: "operatorname", + names: ["\\operatorname@", "\\operatornamewithlimits"], + props: { + numArgs: 1 + }, + handler: function handler(_ref, args) { + var parser = _ref.parser, + funcName = _ref.funcName; + var body = args[0]; + return { + type: "operatorname", + mode: parser.mode, + body: ordargument(body), + alwaysHandleSupSub: funcName === "\\operatornamewithlimits", + limits: false, + parentIsSupSub: false + }; + }, + htmlBuilder: operatorname_htmlBuilder, + mathmlBuilder: operatorname_mathmlBuilder +}); +defineMacro("\\operatorname", "\\@ifstar\\operatornamewithlimits\\operatorname@"); +;// CONCATENATED MODULE: ./src/functions/ordgroup.js + + + + +defineFunctionBuilders({ + type: "ordgroup", + htmlBuilder: function htmlBuilder(group, options) { + if (group.semisimple) { + return buildCommon.makeFragment(buildExpression(group.body, options, false)); + } + + return buildCommon.makeSpan(["mord"], buildExpression(group.body, options, true), options); + }, + mathmlBuilder: function mathmlBuilder(group, options) { + return buildExpressionRow(group.body, options, true); + } +}); +;// CONCATENATED MODULE: ./src/functions/overline.js + + + + + +defineFunction({ + type: "overline", + names: ["\\overline"], + props: { + numArgs: 1 + }, + handler: function handler(_ref, args) { + var parser = _ref.parser; + var body = args[0]; + return { + type: "overline", + mode: parser.mode, + body: body + }; + }, + htmlBuilder: function htmlBuilder(group, options) { + // Overlines are handled in the TeXbook pg 443, Rule 9. + // Build the inner group in the cramped style. + var innerGroup = buildGroup(group.body, options.havingCrampedStyle()); // Create the line above the body + + var line = buildCommon.makeLineSpan("overline-line", options); // Generate the vlist, with the appropriate kerns + + var defaultRuleThickness = options.fontMetrics().defaultRuleThickness; + var vlist = buildCommon.makeVList({ + positionType: "firstBaseline", + children: [{ + type: "elem", + elem: innerGroup + }, { + type: "kern", + size: 3 * defaultRuleThickness + }, { + type: "elem", + elem: line + }, { + type: "kern", + size: defaultRuleThickness + }] + }, options); + return buildCommon.makeSpan(["mord", "overline"], [vlist], options); + }, + mathmlBuilder: function mathmlBuilder(group, options) { + var operator = new mathMLTree.MathNode("mo", [new mathMLTree.TextNode("\u203E")]); + operator.setAttribute("stretchy", "true"); + var node = new mathMLTree.MathNode("mover", [buildMathML_buildGroup(group.body, options), operator]); + node.setAttribute("accent", "true"); + return node; + } +}); +;// CONCATENATED MODULE: ./src/functions/phantom.js + + + + + +defineFunction({ + type: "phantom", + names: ["\\phantom"], + props: { + numArgs: 1, + allowedInText: true + }, + handler: function handler(_ref, args) { + var parser = _ref.parser; + var body = args[0]; + return { + type: "phantom", + mode: parser.mode, + body: ordargument(body) + }; + }, + htmlBuilder: function htmlBuilder(group, options) { + var elements = buildExpression(group.body, options.withPhantom(), false); // \phantom isn't supposed to affect the elements it contains. + // See "color" for more details. + + return buildCommon.makeFragment(elements); + }, + mathmlBuilder: function mathmlBuilder(group, options) { + var inner = buildMathML_buildExpression(group.body, options); + return new mathMLTree.MathNode("mphantom", inner); + } +}); +defineFunction({ + type: "hphantom", + names: ["\\hphantom"], + props: { + numArgs: 1, + allowedInText: true + }, + handler: function handler(_ref2, args) { + var parser = _ref2.parser; + var body = args[0]; + return { + type: "hphantom", + mode: parser.mode, + body: body + }; + }, + htmlBuilder: function htmlBuilder(group, options) { + var node = buildCommon.makeSpan([], [buildGroup(group.body, options.withPhantom())]); + node.height = 0; + node.depth = 0; + + if (node.children) { + for (var i = 0; i < node.children.length; i++) { + node.children[i].height = 0; + node.children[i].depth = 0; + } + } // See smash for comment re: use of makeVList + + + node = buildCommon.makeVList({ + positionType: "firstBaseline", + children: [{ + type: "elem", + elem: node + }] + }, options); // For spacing, TeX treats \smash as a math group (same spacing as ord). + + return buildCommon.makeSpan(["mord"], [node], options); + }, + mathmlBuilder: function mathmlBuilder(group, options) { + var inner = buildMathML_buildExpression(ordargument(group.body), options); + var phantom = new mathMLTree.MathNode("mphantom", inner); + var node = new mathMLTree.MathNode("mpadded", [phantom]); + node.setAttribute("height", "0px"); + node.setAttribute("depth", "0px"); + return node; + } +}); +defineFunction({ + type: "vphantom", + names: ["\\vphantom"], + props: { + numArgs: 1, + allowedInText: true + }, + handler: function handler(_ref3, args) { + var parser = _ref3.parser; + var body = args[0]; + return { + type: "vphantom", + mode: parser.mode, + body: body + }; + }, + htmlBuilder: function htmlBuilder(group, options) { + var inner = buildCommon.makeSpan(["inner"], [buildGroup(group.body, options.withPhantom())]); + var fix = buildCommon.makeSpan(["fix"], []); + return buildCommon.makeSpan(["mord", "rlap"], [inner, fix], options); + }, + mathmlBuilder: function mathmlBuilder(group, options) { + var inner = buildMathML_buildExpression(ordargument(group.body), options); + var phantom = new mathMLTree.MathNode("mphantom", inner); + var node = new mathMLTree.MathNode("mpadded", [phantom]); + node.setAttribute("width", "0px"); + return node; + } +}); +;// CONCATENATED MODULE: ./src/functions/raisebox.js + + + + + + + // Box manipulation + +defineFunction({ + type: "raisebox", + names: ["\\raisebox"], + props: { + numArgs: 2, + argTypes: ["size", "hbox"], + allowedInText: true + }, + handler: function handler(_ref, args) { + var parser = _ref.parser; + var amount = assertNodeType(args[0], "size").value; + var body = args[1]; + return { + type: "raisebox", + mode: parser.mode, + dy: amount, + body: body + }; + }, + htmlBuilder: function htmlBuilder(group, options) { + var body = buildGroup(group.body, options); + var dy = calculateSize(group.dy, options); + return buildCommon.makeVList({ + positionType: "shift", + positionData: -dy, + children: [{ + type: "elem", + elem: body + }] + }, options); + }, + mathmlBuilder: function mathmlBuilder(group, options) { + var node = new mathMLTree.MathNode("mpadded", [buildMathML_buildGroup(group.body, options)]); + var dy = group.dy.number + group.dy.unit; + node.setAttribute("voffset", dy); + return node; + } +}); +;// CONCATENATED MODULE: ./src/functions/rule.js + + + + + +defineFunction({ + type: "rule", + names: ["\\rule"], + props: { + numArgs: 2, + numOptionalArgs: 1, + argTypes: ["size", "size", "size"] + }, + handler: function handler(_ref, args, optArgs) { + var parser = _ref.parser; + var shift = optArgs[0]; + var width = assertNodeType(args[0], "size"); + var height = assertNodeType(args[1], "size"); + return { + type: "rule", + mode: parser.mode, + shift: shift && assertNodeType(shift, "size").value, + width: width.value, + height: height.value + }; + }, + htmlBuilder: function htmlBuilder(group, options) { + // Make an empty span for the rule + var rule = buildCommon.makeSpan(["mord", "rule"], [], options); // Calculate the shift, width, and height of the rule, and account for units + + var width = calculateSize(group.width, options); + var height = calculateSize(group.height, options); + var shift = group.shift ? calculateSize(group.shift, options) : 0; // Style the rule to the right size + + rule.style.borderRightWidth = width + "em"; + rule.style.borderTopWidth = height + "em"; + rule.style.bottom = shift + "em"; // Record the height and width + + rule.width = width; + rule.height = height + shift; + rule.depth = -shift; // Font size is the number large enough that the browser will + // reserve at least `absHeight` space above the baseline. + // The 1.125 factor was empirically determined + + rule.maxFontSize = height * 1.125 * options.sizeMultiplier; + return rule; + }, + mathmlBuilder: function mathmlBuilder(group, options) { + var width = calculateSize(group.width, options); + var height = calculateSize(group.height, options); + var shift = group.shift ? calculateSize(group.shift, options) : 0; + var color = options.color && options.getColor() || "black"; + var rule = new mathMLTree.MathNode("mspace"); + rule.setAttribute("mathbackground", color); + rule.setAttribute("width", width + "em"); + rule.setAttribute("height", height + "em"); + var wrapper = new mathMLTree.MathNode("mpadded", [rule]); + + if (shift >= 0) { + wrapper.setAttribute("height", "+" + shift + "em"); + } else { + wrapper.setAttribute("height", shift + "em"); + wrapper.setAttribute("depth", "+" + -shift + "em"); + } + + wrapper.setAttribute("voffset", shift + "em"); + return wrapper; + } +}); +;// CONCATENATED MODULE: ./src/functions/sizing.js + + + + + +function sizingGroup(value, options, baseOptions) { + var inner = buildExpression(value, options, false); + var multiplier = options.sizeMultiplier / baseOptions.sizeMultiplier; // Add size-resetting classes to the inner list and set maxFontSize + // manually. Handle nested size changes. + + for (var i = 0; i < inner.length; i++) { + var pos = inner[i].classes.indexOf("sizing"); + + if (pos < 0) { + Array.prototype.push.apply(inner[i].classes, options.sizingClasses(baseOptions)); + } else if (inner[i].classes[pos + 1] === "reset-size" + options.size) { + // This is a nested size change: e.g., inner[i] is the "b" in + // `\Huge a \small b`. Override the old size (the `reset-` class) + // but not the new size. + inner[i].classes[pos + 1] = "reset-size" + baseOptions.size; + } + + inner[i].height *= multiplier; + inner[i].depth *= multiplier; + } + + return buildCommon.makeFragment(inner); +} +var sizeFuncs = ["\\tiny", "\\sixptsize", "\\scriptsize", "\\footnotesize", "\\small", "\\normalsize", "\\large", "\\Large", "\\LARGE", "\\huge", "\\Huge"]; +var sizing_htmlBuilder = function htmlBuilder(group, options) { + // Handle sizing operators like \Huge. Real TeX doesn't actually allow + // these functions inside of math expressions, so we do some special + // handling. + var newOptions = options.havingSize(group.size); + return sizingGroup(group.body, newOptions, options); +}; +defineFunction({ + type: "sizing", + names: sizeFuncs, + props: { + numArgs: 0, + allowedInText: true + }, + handler: function handler(_ref, args) { + var breakOnTokenText = _ref.breakOnTokenText, + funcName = _ref.funcName, + parser = _ref.parser; + var body = parser.parseExpression(false, breakOnTokenText); + return { + type: "sizing", + mode: parser.mode, + // Figure out what size to use based on the list of functions above + size: sizeFuncs.indexOf(funcName) + 1, + body: body + }; + }, + htmlBuilder: sizing_htmlBuilder, + mathmlBuilder: function mathmlBuilder(group, options) { + var newOptions = options.havingSize(group.size); + var inner = buildMathML_buildExpression(group.body, newOptions); + var node = new mathMLTree.MathNode("mstyle", inner); // TODO(emily): This doesn't produce the correct size for nested size + // changes, because we don't keep state of what style we're currently + // in, so we can't reset the size to normal before changing it. Now + // that we're passing an options parameter we should be able to fix + // this. + + node.setAttribute("mathsize", newOptions.sizeMultiplier + "em"); + return node; + } +}); +;// CONCATENATED MODULE: ./src/functions/smash.js +// smash, with optional [tb], as in AMS + + + + + + +defineFunction({ + type: "smash", + names: ["\\smash"], + props: { + numArgs: 1, + numOptionalArgs: 1, + allowedInText: true + }, + handler: function handler(_ref, args, optArgs) { + var parser = _ref.parser; + var smashHeight = false; + var smashDepth = false; + var tbArg = optArgs[0] && assertNodeType(optArgs[0], "ordgroup"); + + if (tbArg) { + // Optional [tb] argument is engaged. + // ref: amsmath: \renewcommand{\smash}[1][tb]{% + // def\mb@t{\ht}\def\mb@b{\dp}\def\mb@tb{\ht\z@\z@\dp}% + var letter = ""; + + for (var i = 0; i < tbArg.body.length; ++i) { + var node = tbArg.body[i]; // $FlowFixMe: Not every node type has a `text` property. + + letter = node.text; + + if (letter === "t") { + smashHeight = true; + } else if (letter === "b") { + smashDepth = true; + } else { + smashHeight = false; + smashDepth = false; + break; + } + } + } else { + smashHeight = true; + smashDepth = true; + } + + var body = args[0]; + return { + type: "smash", + mode: parser.mode, + body: body, + smashHeight: smashHeight, + smashDepth: smashDepth + }; + }, + htmlBuilder: function htmlBuilder(group, options) { + var node = buildCommon.makeSpan([], [buildGroup(group.body, options)]); + + if (!group.smashHeight && !group.smashDepth) { + return node; + } + + if (group.smashHeight) { + node.height = 0; // In order to influence makeVList, we have to reset the children. + + if (node.children) { + for (var i = 0; i < node.children.length; i++) { + node.children[i].height = 0; + } + } + } + + if (group.smashDepth) { + node.depth = 0; + + if (node.children) { + for (var _i = 0; _i < node.children.length; _i++) { + node.children[_i].depth = 0; + } + } + } // At this point, we've reset the TeX-like height and depth values. + // But the span still has an HTML line height. + // makeVList applies "display: table-cell", which prevents the browser + // from acting on that line height. So we'll call makeVList now. + + + var smashedNode = buildCommon.makeVList({ + positionType: "firstBaseline", + children: [{ + type: "elem", + elem: node + }] + }, options); // For spacing, TeX treats \hphantom as a math group (same spacing as ord). + + return buildCommon.makeSpan(["mord"], [smashedNode], options); + }, + mathmlBuilder: function mathmlBuilder(group, options) { + var node = new mathMLTree.MathNode("mpadded", [buildMathML_buildGroup(group.body, options)]); + + if (group.smashHeight) { + node.setAttribute("height", "0px"); + } + + if (group.smashDepth) { + node.setAttribute("depth", "0px"); + } + + return node; + } +}); +;// CONCATENATED MODULE: ./src/functions/sqrt.js + + + + + + + +defineFunction({ + type: "sqrt", + names: ["\\sqrt"], + props: { + numArgs: 1, + numOptionalArgs: 1 + }, + handler: function handler(_ref, args, optArgs) { + var parser = _ref.parser; + var index = optArgs[0]; + var body = args[0]; + return { + type: "sqrt", + mode: parser.mode, + body: body, + index: index + }; + }, + htmlBuilder: function htmlBuilder(group, options) { + // Square roots are handled in the TeXbook pg. 443, Rule 11. + // First, we do the same steps as in overline to build the inner group + // and line + var inner = buildGroup(group.body, options.havingCrampedStyle()); + + if (inner.height === 0) { + // Render a small surd. + inner.height = options.fontMetrics().xHeight; + } // Some groups can return document fragments. Handle those by wrapping + // them in a span. + + + inner = buildCommon.wrapFragment(inner, options); // Calculate the minimum size for the \surd delimiter + + var metrics = options.fontMetrics(); + var theta = metrics.defaultRuleThickness; + var phi = theta; + + if (options.style.id < src_Style.TEXT.id) { + phi = options.fontMetrics().xHeight; + } // Calculate the clearance between the body and line + + + var lineClearance = theta + phi / 4; + var minDelimiterHeight = inner.height + inner.depth + lineClearance + theta; // Create a sqrt SVG of the required minimum size + + var _delimiter$sqrtImage = delimiter.sqrtImage(minDelimiterHeight, options), + img = _delimiter$sqrtImage.span, + ruleWidth = _delimiter$sqrtImage.ruleWidth, + advanceWidth = _delimiter$sqrtImage.advanceWidth; + + var delimDepth = img.height - ruleWidth; // Adjust the clearance based on the delimiter size + + if (delimDepth > inner.height + inner.depth + lineClearance) { + lineClearance = (lineClearance + delimDepth - inner.height - inner.depth) / 2; + } // Shift the sqrt image + + + var imgShift = img.height - inner.height - lineClearance - ruleWidth; + inner.style.paddingLeft = advanceWidth + "em"; // Overlay the image and the argument. + + var body = buildCommon.makeVList({ + positionType: "firstBaseline", + children: [{ + type: "elem", + elem: inner, + wrapperClasses: ["svg-align"] + }, { + type: "kern", + size: -(inner.height + imgShift) + }, { + type: "elem", + elem: img + }, { + type: "kern", + size: ruleWidth + }] + }, options); + + if (!group.index) { + return buildCommon.makeSpan(["mord", "sqrt"], [body], options); + } else { + // Handle the optional root index + // The index is always in scriptscript style + var newOptions = options.havingStyle(src_Style.SCRIPTSCRIPT); + var rootm = buildGroup(group.index, newOptions, options); // The amount the index is shifted by. This is taken from the TeX + // source, in the definition of `\r@@t`. + + var toShift = 0.6 * (body.height - body.depth); // Build a VList with the superscript shifted up correctly + + var rootVList = buildCommon.makeVList({ + positionType: "shift", + positionData: -toShift, + children: [{ + type: "elem", + elem: rootm + }] + }, options); // Add a class surrounding it so we can add on the appropriate + // kerning + + var rootVListWrap = buildCommon.makeSpan(["root"], [rootVList]); + return buildCommon.makeSpan(["mord", "sqrt"], [rootVListWrap, body], options); + } + }, + mathmlBuilder: function mathmlBuilder(group, options) { + var body = group.body, + index = group.index; + return index ? new mathMLTree.MathNode("mroot", [buildMathML_buildGroup(body, options), buildMathML_buildGroup(index, options)]) : new mathMLTree.MathNode("msqrt", [buildMathML_buildGroup(body, options)]); + } +}); +;// CONCATENATED MODULE: ./src/functions/styling.js + + + + + +var styling_styleMap = { + "display": src_Style.DISPLAY, + "text": src_Style.TEXT, + "script": src_Style.SCRIPT, + "scriptscript": src_Style.SCRIPTSCRIPT +}; +defineFunction({ + type: "styling", + names: ["\\displaystyle", "\\textstyle", "\\scriptstyle", "\\scriptscriptstyle"], + props: { + numArgs: 0, + allowedInText: true, + primitive: true + }, + handler: function handler(_ref, args) { + var breakOnTokenText = _ref.breakOnTokenText, + funcName = _ref.funcName, + parser = _ref.parser; + // parse out the implicit body + var body = parser.parseExpression(true, breakOnTokenText); // TODO: Refactor to avoid duplicating styleMap in multiple places (e.g. + // here and in buildHTML and de-dupe the enumeration of all the styles). + // $FlowFixMe: The names above exactly match the styles. + + var style = funcName.slice(1, funcName.length - 5); + return { + type: "styling", + mode: parser.mode, + // Figure out what style to use by pulling out the style from + // the function name + style: style, + body: body + }; + }, + htmlBuilder: function htmlBuilder(group, options) { + // Style changes are handled in the TeXbook on pg. 442, Rule 3. + var newStyle = styling_styleMap[group.style]; + var newOptions = options.havingStyle(newStyle).withFont(''); + return sizingGroup(group.body, newOptions, options); + }, + mathmlBuilder: function mathmlBuilder(group, options) { + // Figure out what style we're changing to. + var newStyle = styling_styleMap[group.style]; + var newOptions = options.havingStyle(newStyle); + var inner = buildMathML_buildExpression(group.body, newOptions); + var node = new mathMLTree.MathNode("mstyle", inner); + var styleAttributes = { + "display": ["0", "true"], + "text": ["0", "false"], + "script": ["1", "false"], + "scriptscript": ["2", "false"] + }; + var attr = styleAttributes[group.style]; + node.setAttribute("scriptlevel", attr[0]); + node.setAttribute("displaystyle", attr[1]); + return node; + } +}); +;// CONCATENATED MODULE: ./src/functions/supsub.js + + + + + + + + + + + + + +/** + * Sometimes, groups perform special rules when they have superscripts or + * subscripts attached to them. This function lets the `supsub` group know that + * Sometimes, groups perform special rules when they have superscripts or + * its inner element should handle the superscripts and subscripts instead of + * handling them itself. + */ +var htmlBuilderDelegate = function htmlBuilderDelegate(group, options) { + var base = group.base; + + if (!base) { + return null; + } else if (base.type === "op") { + // Operators handle supsubs differently when they have limits + // (e.g. `\displaystyle\sum_2^3`) + var delegate = base.limits && (options.style.size === src_Style.DISPLAY.size || base.alwaysHandleSupSub); + return delegate ? op_htmlBuilder : null; + } else if (base.type === "operatorname") { + var _delegate = base.alwaysHandleSupSub && (options.style.size === src_Style.DISPLAY.size || base.limits); + + return _delegate ? operatorname_htmlBuilder : null; + } else if (base.type === "accent") { + return utils.isCharacterBox(base.base) ? htmlBuilder : null; + } else if (base.type === "horizBrace") { + var isSup = !group.sub; + return isSup === base.isOver ? horizBrace_htmlBuilder : null; + } else { + return null; + } +}; // Super scripts and subscripts, whose precise placement can depend on other +// functions that precede them. + + +defineFunctionBuilders({ + type: "supsub", + htmlBuilder: function htmlBuilder(group, options) { + // Superscript and subscripts are handled in the TeXbook on page + // 445-446, rules 18(a-f). + // Here is where we defer to the inner group if it should handle + // superscripts and subscripts itself. + var builderDelegate = htmlBuilderDelegate(group, options); + + if (builderDelegate) { + return builderDelegate(group, options); + } + + var valueBase = group.base, + valueSup = group.sup, + valueSub = group.sub; + var base = buildGroup(valueBase, options); + var supm; + var subm; + var metrics = options.fontMetrics(); // Rule 18a + + var supShift = 0; + var subShift = 0; + var isCharacterBox = valueBase && utils.isCharacterBox(valueBase); + + if (valueSup) { + var newOptions = options.havingStyle(options.style.sup()); + supm = buildGroup(valueSup, newOptions, options); + + if (!isCharacterBox) { + supShift = base.height - newOptions.fontMetrics().supDrop * newOptions.sizeMultiplier / options.sizeMultiplier; + } + } + + if (valueSub) { + var _newOptions = options.havingStyle(options.style.sub()); + + subm = buildGroup(valueSub, _newOptions, options); + + if (!isCharacterBox) { + subShift = base.depth + _newOptions.fontMetrics().subDrop * _newOptions.sizeMultiplier / options.sizeMultiplier; + } + } // Rule 18c + + + var minSupShift; + + if (options.style === src_Style.DISPLAY) { + minSupShift = metrics.sup1; + } else if (options.style.cramped) { + minSupShift = metrics.sup3; + } else { + minSupShift = metrics.sup2; + } // scriptspace is a font-size-independent size, so scale it + // appropriately for use as the marginRight. + + + var multiplier = options.sizeMultiplier; + var marginRight = 0.5 / metrics.ptPerEm / multiplier + "em"; + var marginLeft = null; + + if (subm) { + // Subscripts shouldn't be shifted by the base's italic correction. + // Account for that by shifting the subscript back the appropriate + // amount. Note we only do this when the base is a single symbol. + var isOiint = group.base && group.base.type === "op" && group.base.name && (group.base.name === "\\oiint" || group.base.name === "\\oiiint"); + + if (base instanceof SymbolNode || isOiint) { + // $FlowFixMe + marginLeft = -base.italic + "em"; + } + } + + var supsub; + + if (supm && subm) { + supShift = Math.max(supShift, minSupShift, supm.depth + 0.25 * metrics.xHeight); + subShift = Math.max(subShift, metrics.sub2); + var ruleWidth = metrics.defaultRuleThickness; // Rule 18e + + var maxWidth = 4 * ruleWidth; + + if (supShift - supm.depth - (subm.height - subShift) < maxWidth) { + subShift = maxWidth - (supShift - supm.depth) + subm.height; + var psi = 0.8 * metrics.xHeight - (supShift - supm.depth); + + if (psi > 0) { + supShift += psi; + subShift -= psi; + } + } + + var vlistElem = [{ + type: "elem", + elem: subm, + shift: subShift, + marginRight: marginRight, + marginLeft: marginLeft + }, { + type: "elem", + elem: supm, + shift: -supShift, + marginRight: marginRight + }]; + supsub = buildCommon.makeVList({ + positionType: "individualShift", + children: vlistElem + }, options); + } else if (subm) { + // Rule 18b + subShift = Math.max(subShift, metrics.sub1, subm.height - 0.8 * metrics.xHeight); + var _vlistElem = [{ + type: "elem", + elem: subm, + marginLeft: marginLeft, + marginRight: marginRight + }]; + supsub = buildCommon.makeVList({ + positionType: "shift", + positionData: subShift, + children: _vlistElem + }, options); + } else if (supm) { + // Rule 18c, d + supShift = Math.max(supShift, minSupShift, supm.depth + 0.25 * metrics.xHeight); + supsub = buildCommon.makeVList({ + positionType: "shift", + positionData: -supShift, + children: [{ + type: "elem", + elem: supm, + marginRight: marginRight + }] + }, options); + } else { + throw new Error("supsub must have either sup or sub."); + } // Wrap the supsub vlist in a span.msupsub to reset text-align. + + + var mclass = getTypeOfDomTree(base, "right") || "mord"; + return buildCommon.makeSpan([mclass], [base, buildCommon.makeSpan(["msupsub"], [supsub])], options); + }, + mathmlBuilder: function mathmlBuilder(group, options) { + // Is the inner group a relevant horizonal brace? + var isBrace = false; + var isOver; + var isSup; + + if (group.base && group.base.type === "horizBrace") { + isSup = !!group.sup; + + if (isSup === group.base.isOver) { + isBrace = true; + isOver = group.base.isOver; + } + } + + if (group.base && (group.base.type === "op" || group.base.type === "operatorname")) { + group.base.parentIsSupSub = true; + } + + var children = [buildMathML_buildGroup(group.base, options)]; + + if (group.sub) { + children.push(buildMathML_buildGroup(group.sub, options)); + } + + if (group.sup) { + children.push(buildMathML_buildGroup(group.sup, options)); + } + + var nodeType; + + if (isBrace) { + nodeType = isOver ? "mover" : "munder"; + } else if (!group.sub) { + var base = group.base; + + if (base && base.type === "op" && base.limits && (options.style === src_Style.DISPLAY || base.alwaysHandleSupSub)) { + nodeType = "mover"; + } else if (base && base.type === "operatorname" && base.alwaysHandleSupSub && (base.limits || options.style === src_Style.DISPLAY)) { + nodeType = "mover"; + } else { + nodeType = "msup"; + } + } else if (!group.sup) { + var _base = group.base; + + if (_base && _base.type === "op" && _base.limits && (options.style === src_Style.DISPLAY || _base.alwaysHandleSupSub)) { + nodeType = "munder"; + } else if (_base && _base.type === "operatorname" && _base.alwaysHandleSupSub && (_base.limits || options.style === src_Style.DISPLAY)) { + nodeType = "munder"; + } else { + nodeType = "msub"; + } + } else { + var _base2 = group.base; + + if (_base2 && _base2.type === "op" && _base2.limits && options.style === src_Style.DISPLAY) { + nodeType = "munderover"; + } else if (_base2 && _base2.type === "operatorname" && _base2.alwaysHandleSupSub && (options.style === src_Style.DISPLAY || _base2.limits)) { + nodeType = "munderover"; + } else { + nodeType = "msubsup"; + } + } + + return new mathMLTree.MathNode(nodeType, children); + } +}); +;// CONCATENATED MODULE: ./src/functions/symbolsOp.js + + + + // Operator ParseNodes created in Parser.js from symbol Groups in src/symbols.js. + +defineFunctionBuilders({ + type: "atom", + htmlBuilder: function htmlBuilder(group, options) { + return buildCommon.mathsym(group.text, group.mode, options, ["m" + group.family]); + }, + mathmlBuilder: function mathmlBuilder(group, options) { + var node = new mathMLTree.MathNode("mo", [makeText(group.text, group.mode)]); + + if (group.family === "bin") { + var variant = getVariant(group, options); + + if (variant === "bold-italic") { + node.setAttribute("mathvariant", variant); + } + } else if (group.family === "punct") { + node.setAttribute("separator", "true"); + } else if (group.family === "open" || group.family === "close") { + // Delims built here should not stretch vertically. + // See delimsizing.js for stretchy delims. + node.setAttribute("stretchy", "false"); + } + + return node; + } +}); +;// CONCATENATED MODULE: ./src/functions/symbolsOrd.js + + + + +// "mathord" and "textord" ParseNodes created in Parser.js from symbol Groups in +// src/symbols.js. +var defaultVariant = { + "mi": "italic", + "mn": "normal", + "mtext": "normal" +}; +defineFunctionBuilders({ + type: "mathord", + htmlBuilder: function htmlBuilder(group, options) { + return buildCommon.makeOrd(group, options, "mathord"); + }, + mathmlBuilder: function mathmlBuilder(group, options) { + var node = new mathMLTree.MathNode("mi", [makeText(group.text, group.mode, options)]); + var variant = getVariant(group, options) || "italic"; + + if (variant !== defaultVariant[node.type]) { + node.setAttribute("mathvariant", variant); + } + + return node; + } +}); +defineFunctionBuilders({ + type: "textord", + htmlBuilder: function htmlBuilder(group, options) { + return buildCommon.makeOrd(group, options, "textord"); + }, + mathmlBuilder: function mathmlBuilder(group, options) { + var text = makeText(group.text, group.mode, options); + var variant = getVariant(group, options) || "normal"; + var node; + + if (group.mode === 'text') { + node = new mathMLTree.MathNode("mtext", [text]); + } else if (/[0-9]/.test(group.text)) { + node = new mathMLTree.MathNode("mn", [text]); + } else if (group.text === "\\prime") { + node = new mathMLTree.MathNode("mo", [text]); + } else { + node = new mathMLTree.MathNode("mi", [text]); + } + + if (variant !== defaultVariant[node.type]) { + node.setAttribute("mathvariant", variant); + } + + return node; + } +}); +;// CONCATENATED MODULE: ./src/functions/symbolsSpacing.js + + + + // A map of CSS-based spacing functions to their CSS class. + +var cssSpace = { + "\\nobreak": "nobreak", + "\\allowbreak": "allowbreak" +}; // A lookup table to determine whether a spacing function/symbol should be +// treated like a regular space character. If a symbol or command is a key +// in this table, then it should be a regular space character. Furthermore, +// the associated value may have a `className` specifying an extra CSS class +// to add to the created `span`. + +var regularSpace = { + " ": {}, + "\\ ": {}, + "~": { + className: "nobreak" + }, + "\\space": {}, + "\\nobreakspace": { + className: "nobreak" + } +}; // ParseNode<"spacing"> created in Parser.js from the "spacing" symbol Groups in +// src/symbols.js. + +defineFunctionBuilders({ + type: "spacing", + htmlBuilder: function htmlBuilder(group, options) { + if (regularSpace.hasOwnProperty(group.text)) { + var className = regularSpace[group.text].className || ""; // Spaces are generated by adding an actual space. Each of these + // things has an entry in the symbols table, so these will be turned + // into appropriate outputs. + + if (group.mode === "text") { + var ord = buildCommon.makeOrd(group, options, "textord"); + ord.classes.push(className); + return ord; + } else { + return buildCommon.makeSpan(["mspace", className], [buildCommon.mathsym(group.text, group.mode, options)], options); + } + } else if (cssSpace.hasOwnProperty(group.text)) { + // Spaces based on just a CSS class. + return buildCommon.makeSpan(["mspace", cssSpace[group.text]], [], options); + } else { + throw new src_ParseError("Unknown type of space \"" + group.text + "\""); + } + }, + mathmlBuilder: function mathmlBuilder(group, options) { + var node; + + if (regularSpace.hasOwnProperty(group.text)) { + node = new mathMLTree.MathNode("mtext", [new mathMLTree.TextNode("\xA0")]); + } else if (cssSpace.hasOwnProperty(group.text)) { + // CSS-based MathML spaces (\nobreak, \allowbreak) are ignored + return new mathMLTree.MathNode("mspace"); + } else { + throw new src_ParseError("Unknown type of space \"" + group.text + "\""); + } + + return node; + } +}); +;// CONCATENATED MODULE: ./src/functions/tag.js + + + + +var pad = function pad() { + var padNode = new mathMLTree.MathNode("mtd", []); + padNode.setAttribute("width", "50%"); + return padNode; +}; + +defineFunctionBuilders({ + type: "tag", + mathmlBuilder: function mathmlBuilder(group, options) { + var table = new mathMLTree.MathNode("mtable", [new mathMLTree.MathNode("mtr", [pad(), new mathMLTree.MathNode("mtd", [buildExpressionRow(group.body, options)]), pad(), new mathMLTree.MathNode("mtd", [buildExpressionRow(group.tag, options)])])]); + table.setAttribute("width", "100%"); + return table; // TODO: Left-aligned tags. + // Currently, the group and options passed here do not contain + // enough info to set tag alignment. `leqno` is in Settings but it is + // not passed to Options. On the HTML side, leqno is + // set by a CSS class applied in buildTree.js. That would have worked + // in MathML if browsers supported . Since they don't, we + // need to rewrite the way this function is called. + } +}); +;// CONCATENATED MODULE: ./src/functions/text.js + + + + // Non-mathy text, possibly in a font + +var textFontFamilies = { + "\\text": undefined, + "\\textrm": "textrm", + "\\textsf": "textsf", + "\\texttt": "texttt", + "\\textnormal": "textrm" +}; +var textFontWeights = { + "\\textbf": "textbf", + "\\textmd": "textmd" +}; +var textFontShapes = { + "\\textit": "textit", + "\\textup": "textup" +}; + +var optionsWithFont = function optionsWithFont(group, options) { + var font = group.font; // Checks if the argument is a font family or a font style. + + if (!font) { + return options; + } else if (textFontFamilies[font]) { + return options.withTextFontFamily(textFontFamilies[font]); + } else if (textFontWeights[font]) { + return options.withTextFontWeight(textFontWeights[font]); + } else { + return options.withTextFontShape(textFontShapes[font]); + } +}; + +defineFunction({ + type: "text", + names: [// Font families + "\\text", "\\textrm", "\\textsf", "\\texttt", "\\textnormal", // Font weights + "\\textbf", "\\textmd", // Font Shapes + "\\textit", "\\textup"], + props: { + numArgs: 1, + argTypes: ["text"], + allowedInArgument: true, + allowedInText: true + }, + handler: function handler(_ref, args) { + var parser = _ref.parser, + funcName = _ref.funcName; + var body = args[0]; + return { + type: "text", + mode: parser.mode, + body: ordargument(body), + font: funcName + }; + }, + htmlBuilder: function htmlBuilder(group, options) { + var newOptions = optionsWithFont(group, options); + var inner = buildExpression(group.body, newOptions, true); + return buildCommon.makeSpan(["mord", "text"], inner, newOptions); + }, + mathmlBuilder: function mathmlBuilder(group, options) { + var newOptions = optionsWithFont(group, options); + return buildExpressionRow(group.body, newOptions); + } +}); +;// CONCATENATED MODULE: ./src/functions/underline.js + + + + + +defineFunction({ + type: "underline", + names: ["\\underline"], + props: { + numArgs: 1, + allowedInText: true + }, + handler: function handler(_ref, args) { + var parser = _ref.parser; + return { + type: "underline", + mode: parser.mode, + body: args[0] + }; + }, + htmlBuilder: function htmlBuilder(group, options) { + // Underlines are handled in the TeXbook pg 443, Rule 10. + // Build the inner group. + var innerGroup = buildGroup(group.body, options); // Create the line to go below the body + + var line = buildCommon.makeLineSpan("underline-line", options); // Generate the vlist, with the appropriate kerns + + var defaultRuleThickness = options.fontMetrics().defaultRuleThickness; + var vlist = buildCommon.makeVList({ + positionType: "top", + positionData: innerGroup.height, + children: [{ + type: "kern", + size: defaultRuleThickness + }, { + type: "elem", + elem: line + }, { + type: "kern", + size: 3 * defaultRuleThickness + }, { + type: "elem", + elem: innerGroup + }] + }, options); + return buildCommon.makeSpan(["mord", "underline"], [vlist], options); + }, + mathmlBuilder: function mathmlBuilder(group, options) { + var operator = new mathMLTree.MathNode("mo", [new mathMLTree.TextNode("\u203E")]); + operator.setAttribute("stretchy", "true"); + var node = new mathMLTree.MathNode("munder", [buildMathML_buildGroup(group.body, options), operator]); + node.setAttribute("accentunder", "true"); + return node; + } +}); +;// CONCATENATED MODULE: ./src/functions/vcenter.js + + + + + // \vcenter: Vertically center the argument group on the math axis. + +defineFunction({ + type: "vcenter", + names: ["\\vcenter"], + props: { + numArgs: 1, + argTypes: ["original"], + // In LaTeX, \vcenter can act only on a box. + allowedInText: false + }, + handler: function handler(_ref, args) { + var parser = _ref.parser; + return { + type: "vcenter", + mode: parser.mode, + body: args[0] + }; + }, + htmlBuilder: function htmlBuilder(group, options) { + var body = buildGroup(group.body, options); + var axisHeight = options.fontMetrics().axisHeight; + var dy = 0.5 * (body.height - axisHeight - (body.depth + axisHeight)); + return buildCommon.makeVList({ + positionType: "shift", + positionData: dy, + children: [{ + type: "elem", + elem: body + }] + }, options); + }, + mathmlBuilder: function mathmlBuilder(group, options) { + // There is no way to do this in MathML. + // Write a class as a breadcrumb in case some post-processor wants + // to perform a vcenter adjustment. + return new mathMLTree.MathNode("mpadded", [buildMathML_buildGroup(group.body, options)], ["vcenter"]); + } +}); +;// CONCATENATED MODULE: ./src/functions/verb.js + + + + +defineFunction({ + type: "verb", + names: ["\\verb"], + props: { + numArgs: 0, + allowedInText: true + }, + handler: function handler(context, args, optArgs) { + // \verb and \verb* are dealt with directly in Parser.js. + // If we end up here, it's because of a failure to match the two delimiters + // in the regex in Lexer.js. LaTeX raises the following error when \verb is + // terminated by end of line (or file). + throw new src_ParseError("\\verb ended by end of line instead of matching delimiter"); + }, + htmlBuilder: function htmlBuilder(group, options) { + var text = makeVerb(group); + var body = []; // \verb enters text mode and therefore is sized like \textstyle + + var newOptions = options.havingStyle(options.style.text()); + + for (var i = 0; i < text.length; i++) { + var c = text[i]; + + if (c === '~') { + c = '\\textasciitilde'; + } + + body.push(buildCommon.makeSymbol(c, "Typewriter-Regular", group.mode, newOptions, ["mord", "texttt"])); + } + + return buildCommon.makeSpan(["mord", "text"].concat(newOptions.sizingClasses(options)), buildCommon.tryCombineChars(body), newOptions); + }, + mathmlBuilder: function mathmlBuilder(group, options) { + var text = new mathMLTree.TextNode(makeVerb(group)); + var node = new mathMLTree.MathNode("mtext", [text]); + node.setAttribute("mathvariant", "monospace"); + return node; + } +}); +/** + * Converts verb group into body string. + * + * \verb* replaces each space with an open box \u2423 + * \verb replaces each space with a no-break space \xA0 + */ + +var makeVerb = function makeVerb(group) { + return group.body.replace(/ /g, group.star ? "\u2423" : '\xA0'); +}; +;// CONCATENATED MODULE: ./src/functions.js +/** Include this to ensure that all functions are defined. */ + +var functions = _functions; +/* harmony default export */ var src_functions = (functions); // TODO(kevinb): have functions return an object and call defineFunction with +// that object in this file instead of relying on side-effects. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +;// CONCATENATED MODULE: ./src/SourceLocation.js +/** + * Lexing or parsing positional information for error reporting. + * This object is immutable. + */ +var SourceLocation = /*#__PURE__*/function () { + // The + prefix indicates that these fields aren't writeable + // Lexer holding the input string. + // Start offset, zero-based inclusive. + // End offset, zero-based exclusive. + function SourceLocation(lexer, start, end) { + this.lexer = void 0; + this.start = void 0; + this.end = void 0; + this.lexer = lexer; + this.start = start; + this.end = end; + } + /** + * Merges two `SourceLocation`s from location providers, given they are + * provided in order of appearance. + * - Returns the first one's location if only the first is provided. + * - Returns a merged range of the first and the last if both are provided + * and their lexers match. + * - Otherwise, returns null. + */ + + + SourceLocation.range = function range(first, second) { + if (!second) { + return first && first.loc; + } else if (!first || !first.loc || !second.loc || first.loc.lexer !== second.loc.lexer) { + return null; + } else { + return new SourceLocation(first.loc.lexer, first.loc.start, second.loc.end); + } + }; + + return SourceLocation; +}(); + + +;// CONCATENATED MODULE: ./src/Token.js + +/** + * Interface required to break circular dependency between Token, Lexer, and + * ParseError. + */ + +/** + * The resulting token returned from `lex`. + * + * It consists of the token text plus some position information. + * The position information is essentially a range in an input string, + * but instead of referencing the bare input string, we refer to the lexer. + * That way it is possible to attach extra metadata to the input string, + * like for example a file name or similar. + * + * The position information is optional, so it is OK to construct synthetic + * tokens if appropriate. Not providing available position information may + * lead to degraded error reporting, though. + */ +var Token = /*#__PURE__*/function () { + // don't expand the token + // used in \noexpand + function Token(text, // the text of this token + loc) { + this.text = void 0; + this.loc = void 0; + this.noexpand = void 0; + this.treatAsRelax = void 0; + this.text = text; + this.loc = loc; + } + /** + * Given a pair of tokens (this and endToken), compute a `Token` encompassing + * the whole input range enclosed by these two. + */ + + + var _proto = Token.prototype; + + _proto.range = function range(endToken, // last token of the range, inclusive + text) { + return new Token(text, SourceLocation.range(this, endToken)); + }; + + return Token; +}(); +;// CONCATENATED MODULE: ./src/Lexer.js +/** + * The Lexer class handles tokenizing the input in various ways. Since our + * parser expects us to be able to backtrack, the lexer allows lexing from any + * given starting point. + * + * Its main exposed function is the `lex` function, which takes a position to + * lex from and a type of token to lex. It defers to the appropriate `_innerLex` + * function. + * + * The various `_innerLex` functions perform the actual lexing of different + * kinds. + */ + + + + +/* The following tokenRegex + * - matches typical whitespace (but not NBSP etc.) using its first group + * - does not match any control character \x00-\x1f except whitespace + * - does not match a bare backslash + * - matches any ASCII character except those just mentioned + * - does not match the BMP private use area \uE000-\uF8FF + * - does not match bare surrogate code units + * - matches any BMP character except for those just described + * - matches any valid Unicode surrogate pair + * - matches a backslash followed by one or more whitespace characters + * - matches a backslash followed by one or more letters then whitespace + * - matches a backslash followed by any BMP character + * Capturing groups: + * [1] regular whitespace + * [2] backslash followed by whitespace + * [3] anything else, which may include: + * [4] left character of \verb* + * [5] left character of \verb + * [6] backslash followed by word, excluding any trailing whitespace + * Just because the Lexer matches something doesn't mean it's valid input: + * If there is no matching function or symbol definition, the Parser will + * still reject the input. + */ +var spaceRegexString = "[ \r\n\t]"; +var controlWordRegexString = "\\\\[a-zA-Z@]+"; +var controlSymbolRegexString = "\\\\[^\uD800-\uDFFF]"; +var controlWordWhitespaceRegexString = "(" + controlWordRegexString + ")" + spaceRegexString + "*"; +var controlSpaceRegexString = "\\\\(\n|[ \r\t]+\n?)[ \r\t]*"; +var combiningDiacriticalMarkString = "[\u0300-\u036F]"; +var combiningDiacriticalMarksEndRegex = new RegExp(combiningDiacriticalMarkString + "+$"); +var tokenRegexString = "(" + spaceRegexString + "+)|" + ( // whitespace +controlSpaceRegexString + "|") + // \whitespace +"([!-\\[\\]-\u2027\u202A-\uD7FF\uF900-\uFFFF]" + ( // single codepoint +combiningDiacriticalMarkString + "*") + // ...plus accents +"|[\uD800-\uDBFF][\uDC00-\uDFFF]" + ( // surrogate pair +combiningDiacriticalMarkString + "*") + // ...plus accents +"|\\\\verb\\*([^]).*?\\4" + // \verb* +"|\\\\verb([^*a-zA-Z]).*?\\5" + ( // \verb unstarred +"|" + controlWordWhitespaceRegexString) + ( // \macroName + spaces +"|" + controlSymbolRegexString + ")"); // \\, \', etc. + +/** Main Lexer class */ + +var Lexer = /*#__PURE__*/function () { + // Category codes. The lexer only supports comment characters (14) for now. + // MacroExpander additionally distinguishes active (13). + function Lexer(input, settings) { + this.input = void 0; + this.settings = void 0; + this.tokenRegex = void 0; + this.catcodes = void 0; + // Separate accents from characters + this.input = input; + this.settings = settings; + this.tokenRegex = new RegExp(tokenRegexString, 'g'); + this.catcodes = { + "%": 14, + // comment character + "~": 13 // active character + + }; + } + + var _proto = Lexer.prototype; + + _proto.setCatcode = function setCatcode(char, code) { + this.catcodes[char] = code; + } + /** + * This function lexes a single token. + */ + ; + + _proto.lex = function lex() { + var input = this.input; + var pos = this.tokenRegex.lastIndex; + + if (pos === input.length) { + return new Token("EOF", new SourceLocation(this, pos, pos)); + } + + var match = this.tokenRegex.exec(input); + + if (match === null || match.index !== pos) { + throw new src_ParseError("Unexpected character: '" + input[pos] + "'", new Token(input[pos], new SourceLocation(this, pos, pos + 1))); + } + + var text = match[6] || match[3] || (match[2] ? "\\ " : " "); + + if (this.catcodes[text] === 14) { + // comment character + var nlIndex = input.indexOf('\n', this.tokenRegex.lastIndex); + + if (nlIndex === -1) { + this.tokenRegex.lastIndex = input.length; // EOF + + this.settings.reportNonstrict("commentAtEnd", "% comment has no terminating newline; LaTeX would " + "fail because of commenting the end of math mode (e.g. $)"); + } else { + this.tokenRegex.lastIndex = nlIndex + 1; + } + + return this.lex(); + } + + return new Token(text, new SourceLocation(this, pos, this.tokenRegex.lastIndex)); + }; + + return Lexer; +}(); + + +;// CONCATENATED MODULE: ./src/Namespace.js +/** + * A `Namespace` refers to a space of nameable things like macros or lengths, + * which can be `set` either globally or local to a nested group, using an + * undo stack similar to how TeX implements this functionality. + * Performance-wise, `get` and local `set` take constant time, while global + * `set` takes time proportional to the depth of group nesting. + */ + + +var Namespace = /*#__PURE__*/function () { + /** + * Both arguments are optional. The first argument is an object of + * built-in mappings which never change. The second argument is an object + * of initial (global-level) mappings, which will constantly change + * according to any global/top-level `set`s done. + */ + function Namespace(builtins, globalMacros) { + if (builtins === void 0) { + builtins = {}; + } + + if (globalMacros === void 0) { + globalMacros = {}; + } + + this.current = void 0; + this.builtins = void 0; + this.undefStack = void 0; + this.current = globalMacros; + this.builtins = builtins; + this.undefStack = []; + } + /** + * Start a new nested group, affecting future local `set`s. + */ + + + var _proto = Namespace.prototype; + + _proto.beginGroup = function beginGroup() { + this.undefStack.push({}); + } + /** + * End current nested group, restoring values before the group began. + */ + ; + + _proto.endGroup = function endGroup() { + if (this.undefStack.length === 0) { + throw new src_ParseError("Unbalanced namespace destruction: attempt " + "to pop global namespace; please report this as a bug"); + } + + var undefs = this.undefStack.pop(); + + for (var undef in undefs) { + if (undefs.hasOwnProperty(undef)) { + if (undefs[undef] === undefined) { + delete this.current[undef]; + } else { + this.current[undef] = undefs[undef]; + } + } + } + } + /** + * Detect whether `name` has a definition. Equivalent to + * `get(name) != null`. + */ + ; + + _proto.has = function has(name) { + return this.current.hasOwnProperty(name) || this.builtins.hasOwnProperty(name); + } + /** + * Get the current value of a name, or `undefined` if there is no value. + * + * Note: Do not use `if (namespace.get(...))` to detect whether a macro + * is defined, as the definition may be the empty string which evaluates + * to `false` in JavaScript. Use `if (namespace.get(...) != null)` or + * `if (namespace.has(...))`. + */ + ; + + _proto.get = function get(name) { + if (this.current.hasOwnProperty(name)) { + return this.current[name]; + } else { + return this.builtins[name]; + } + } + /** + * Set the current value of a name, and optionally set it globally too. + * Local set() sets the current value and (when appropriate) adds an undo + * operation to the undo stack. Global set() may change the undo + * operation at every level, so takes time linear in their number. + */ + ; + + _proto.set = function set(name, value, global) { + if (global === void 0) { + global = false; + } + + if (global) { + // Global set is equivalent to setting in all groups. Simulate this + // by destroying any undos currently scheduled for this name, + // and adding an undo with the *new* value (in case it later gets + // locally reset within this environment). + for (var i = 0; i < this.undefStack.length; i++) { + delete this.undefStack[i][name]; + } + + if (this.undefStack.length > 0) { + this.undefStack[this.undefStack.length - 1][name] = value; + } + } else { + // Undo this set at end of this group (possibly to `undefined`), + // unless an undo is already in place, in which case that older + // value is the correct one. + var top = this.undefStack[this.undefStack.length - 1]; + + if (top && !top.hasOwnProperty(name)) { + top[name] = this.current[name]; + } + } + + this.current[name] = value; + }; + + return Namespace; +}(); + + +;// CONCATENATED MODULE: ./src/MacroExpander.js +/** + * This file contains the “gullet” where macros are expanded + * until only non-macro tokens remain. + */ + + + + + + + +// List of commands that act like macros but aren't defined as a macro, +// function, or symbol. Used in `isDefined`. +var implicitCommands = { + "\\relax": true, + // MacroExpander.js + "^": true, + // Parser.js + "_": true, + // Parser.js + "\\limits": true, + // Parser.js + "\\nolimits": true // Parser.js + +}; + +var MacroExpander = /*#__PURE__*/function () { + function MacroExpander(input, settings, mode) { + this.settings = void 0; + this.expansionCount = void 0; + this.lexer = void 0; + this.macros = void 0; + this.stack = void 0; + this.mode = void 0; + this.settings = settings; + this.expansionCount = 0; + this.feed(input); // Make new global namespace + + this.macros = new Namespace(macros, settings.macros); + this.mode = mode; + this.stack = []; // contains tokens in REVERSE order + } + /** + * Feed a new input string to the same MacroExpander + * (with existing macros etc.). + */ + + + var _proto = MacroExpander.prototype; + + _proto.feed = function feed(input) { + this.lexer = new Lexer(input, this.settings); + } + /** + * Switches between "text" and "math" modes. + */ + ; + + _proto.switchMode = function switchMode(newMode) { + this.mode = newMode; + } + /** + * Start a new group nesting within all namespaces. + */ + ; + + _proto.beginGroup = function beginGroup() { + this.macros.beginGroup(); + } + /** + * End current group nesting within all namespaces. + */ + ; + + _proto.endGroup = function endGroup() { + this.macros.endGroup(); + } + /** + * Returns the topmost token on the stack, without expanding it. + * Similar in behavior to TeX's `\futurelet`. + */ + ; + + _proto.future = function future() { + if (this.stack.length === 0) { + this.pushToken(this.lexer.lex()); + } + + return this.stack[this.stack.length - 1]; + } + /** + * Remove and return the next unexpanded token. + */ + ; + + _proto.popToken = function popToken() { + this.future(); // ensure non-empty stack + + return this.stack.pop(); + } + /** + * Add a given token to the token stack. In particular, this get be used + * to put back a token returned from one of the other methods. + */ + ; + + _proto.pushToken = function pushToken(token) { + this.stack.push(token); + } + /** + * Append an array of tokens to the token stack. + */ + ; + + _proto.pushTokens = function pushTokens(tokens) { + var _this$stack; + + (_this$stack = this.stack).push.apply(_this$stack, tokens); + } + /** + * Find an macro argument without expanding tokens and append the array of + * tokens to the token stack. Uses Token as a container for the result. + */ + ; + + _proto.scanArgument = function scanArgument(isOptional) { + var start; + var end; + var tokens; + + if (isOptional) { + this.consumeSpaces(); // \@ifnextchar gobbles any space following it + + if (this.future().text !== "[") { + return null; + } + + start = this.popToken(); // don't include [ in tokens + + var _this$consumeArg = this.consumeArg(["]"]); + + tokens = _this$consumeArg.tokens; + end = _this$consumeArg.end; + } else { + var _this$consumeArg2 = this.consumeArg(); + + tokens = _this$consumeArg2.tokens; + start = _this$consumeArg2.start; + end = _this$consumeArg2.end; + } // indicate the end of an argument + + + this.pushToken(new Token("EOF", end.loc)); + this.pushTokens(tokens); + return start.range(end, ""); + } + /** + * Consume all following space tokens, without expansion. + */ + ; + + _proto.consumeSpaces = function consumeSpaces() { + for (;;) { + var token = this.future(); + + if (token.text === " ") { + this.stack.pop(); + } else { + break; + } + } + } + /** + * Consume an argument from the token stream, and return the resulting array + * of tokens and start/end token. + */ + ; + + _proto.consumeArg = function consumeArg(delims) { + // The argument for a delimited parameter is the shortest (possibly + // empty) sequence of tokens with properly nested {...} groups that is + // followed ... by this particular list of non-parameter tokens. + // The argument for an undelimited parameter is the next nonblank + // token, unless that token is ‘{’, when the argument will be the + // entire {...} group that follows. + var tokens = []; + var isDelimited = delims && delims.length > 0; + + if (!isDelimited) { + // Ignore spaces between arguments. As the TeXbook says: + // "After you have said ‘\def\row#1#2{...}’, you are allowed to + // put spaces between the arguments (e.g., ‘\row x n’), because + // TeX doesn’t use single spaces as undelimited arguments." + this.consumeSpaces(); + } + + var start = this.future(); + var tok; + var depth = 0; + var match = 0; + + do { + tok = this.popToken(); + tokens.push(tok); + + if (tok.text === "{") { + ++depth; + } else if (tok.text === "}") { + --depth; + + if (depth === -1) { + throw new src_ParseError("Extra }", tok); + } + } else if (tok.text === "EOF") { + throw new src_ParseError("Unexpected end of input in a macro argument" + ", expected '" + (delims && isDelimited ? delims[match] : "}") + "'", tok); + } + + if (delims && isDelimited) { + if ((depth === 0 || depth === 1 && delims[match] === "{") && tok.text === delims[match]) { + ++match; + + if (match === delims.length) { + // don't include delims in tokens + tokens.splice(-match, match); + break; + } + } else { + match = 0; + } + } + } while (depth !== 0 || isDelimited); // If the argument found ... has the form ‘{}’, + // ... the outermost braces enclosing the argument are removed + + + if (start.text === "{" && tokens[tokens.length - 1].text === "}") { + tokens.pop(); + tokens.shift(); + } + + tokens.reverse(); // to fit in with stack order + + return { + tokens: tokens, + start: start, + end: tok + }; + } + /** + * Consume the specified number of (delimited) arguments from the token + * stream and return the resulting array of arguments. + */ + ; + + _proto.consumeArgs = function consumeArgs(numArgs, delimiters) { + if (delimiters) { + if (delimiters.length !== numArgs + 1) { + throw new src_ParseError("The length of delimiters doesn't match the number of args!"); + } + + var delims = delimiters[0]; + + for (var i = 0; i < delims.length; i++) { + var tok = this.popToken(); + + if (delims[i] !== tok.text) { + throw new src_ParseError("Use of the macro doesn't match its definition", tok); + } + } + } + + var args = []; + + for (var _i = 0; _i < numArgs; _i++) { + args.push(this.consumeArg(delimiters && delimiters[_i + 1]).tokens); + } + + return args; + } + /** + * Expand the next token only once if possible. + * + * If the token is expanded, the resulting tokens will be pushed onto + * the stack in reverse order and will be returned as an array, + * also in reverse order. + * + * If not, the next token will be returned without removing it + * from the stack. This case can be detected by a `Token` return value + * instead of an `Array` return value. + * + * In either case, the next token will be on the top of the stack, + * or the stack will be empty. + * + * Used to implement `expandAfterFuture` and `expandNextToken`. + * + * If expandableOnly, only expandable tokens are expanded and + * an undefined control sequence results in an error. + */ + ; + + _proto.expandOnce = function expandOnce(expandableOnly) { + var topToken = this.popToken(); + var name = topToken.text; + var expansion = !topToken.noexpand ? this._getExpansion(name) : null; + + if (expansion == null || expandableOnly && expansion.unexpandable) { + if (expandableOnly && expansion == null && name[0] === "\\" && !this.isDefined(name)) { + throw new src_ParseError("Undefined control sequence: " + name); + } + + this.pushToken(topToken); + return topToken; + } + + this.expansionCount++; + + if (this.expansionCount > this.settings.maxExpand) { + throw new src_ParseError("Too many expansions: infinite loop or " + "need to increase maxExpand setting"); + } + + var tokens = expansion.tokens; + var args = this.consumeArgs(expansion.numArgs, expansion.delimiters); + + if (expansion.numArgs) { + // paste arguments in place of the placeholders + tokens = tokens.slice(); // make a shallow copy + + for (var i = tokens.length - 1; i >= 0; --i) { + var tok = tokens[i]; + + if (tok.text === "#") { + if (i === 0) { + throw new src_ParseError("Incomplete placeholder at end of macro body", tok); + } + + tok = tokens[--i]; // next token on stack + + if (tok.text === "#") { + // ## → # + tokens.splice(i + 1, 1); // drop first # + } else if (/^[1-9]$/.test(tok.text)) { + var _tokens; + + // replace the placeholder with the indicated argument + (_tokens = tokens).splice.apply(_tokens, [i, 2].concat(args[+tok.text - 1])); + } else { + throw new src_ParseError("Not a valid argument number", tok); + } + } + } + } // Concatenate expansion onto top of stack. + + + this.pushTokens(tokens); + return tokens; + } + /** + * Expand the next token only once (if possible), and return the resulting + * top token on the stack (without removing anything from the stack). + * Similar in behavior to TeX's `\expandafter\futurelet`. + * Equivalent to expandOnce() followed by future(). + */ + ; + + _proto.expandAfterFuture = function expandAfterFuture() { + this.expandOnce(); + return this.future(); + } + /** + * Recursively expand first token, then return first non-expandable token. + */ + ; + + _proto.expandNextToken = function expandNextToken() { + for (;;) { + var expanded = this.expandOnce(); // expandOnce returns Token if and only if it's fully expanded. + + if (expanded instanceof Token) { + // \relax stops the expansion, but shouldn't get returned (a + // null return value couldn't get implemented as a function). + // the token after \noexpand is interpreted as if its meaning + // were ‘\relax’ + if (expanded.text === "\\relax" || expanded.treatAsRelax) { + this.stack.pop(); + } else { + return this.stack.pop(); // === expanded + } + } + } // Flow unable to figure out that this pathway is impossible. + // https://github.com/facebook/flow/issues/4808 + + + throw new Error(); // eslint-disable-line no-unreachable + } + /** + * Fully expand the given macro name and return the resulting list of + * tokens, or return `undefined` if no such macro is defined. + */ + ; + + _proto.expandMacro = function expandMacro(name) { + return this.macros.has(name) ? this.expandTokens([new Token(name)]) : undefined; + } + /** + * Fully expand the given token stream and return the resulting list of tokens + */ + ; + + _proto.expandTokens = function expandTokens(tokens) { + var output = []; + var oldStackLength = this.stack.length; + this.pushTokens(tokens); + + while (this.stack.length > oldStackLength) { + var expanded = this.expandOnce(true); // expand only expandable tokens + // expandOnce returns Token if and only if it's fully expanded. + + if (expanded instanceof Token) { + if (expanded.treatAsRelax) { + // the expansion of \noexpand is the token itself + expanded.noexpand = false; + expanded.treatAsRelax = false; + } + + output.push(this.stack.pop()); + } + } + + return output; + } + /** + * Fully expand the given macro name and return the result as a string, + * or return `undefined` if no such macro is defined. + */ + ; + + _proto.expandMacroAsText = function expandMacroAsText(name) { + var tokens = this.expandMacro(name); + + if (tokens) { + return tokens.map(function (token) { + return token.text; + }).join(""); + } else { + return tokens; + } + } + /** + * Returns the expanded macro as a reversed array of tokens and a macro + * argument count. Or returns `null` if no such macro. + */ + ; + + _proto._getExpansion = function _getExpansion(name) { + var definition = this.macros.get(name); + + if (definition == null) { + // mainly checking for undefined here + return definition; + } // If a single character has an associated catcode other than 13 + // (active character), then don't expand it. + + + if (name.length === 1) { + var catcode = this.lexer.catcodes[name]; + + if (catcode != null && catcode !== 13) { + return; + } + } + + var expansion = typeof definition === "function" ? definition(this) : definition; + + if (typeof expansion === "string") { + var numArgs = 0; + + if (expansion.indexOf("#") !== -1) { + var stripped = expansion.replace(/##/g, ""); + + while (stripped.indexOf("#" + (numArgs + 1)) !== -1) { + ++numArgs; + } + } + + var bodyLexer = new Lexer(expansion, this.settings); + var tokens = []; + var tok = bodyLexer.lex(); + + while (tok.text !== "EOF") { + tokens.push(tok); + tok = bodyLexer.lex(); + } + + tokens.reverse(); // to fit in with stack using push and pop + + var expanded = { + tokens: tokens, + numArgs: numArgs + }; + return expanded; + } + + return expansion; + } + /** + * Determine whether a command is currently "defined" (has some + * functionality), meaning that it's a macro (in the current group), + * a function, a symbol, or one of the special commands listed in + * `implicitCommands`. + */ + ; + + _proto.isDefined = function isDefined(name) { + return this.macros.has(name) || src_functions.hasOwnProperty(name) || src_symbols.math.hasOwnProperty(name) || src_symbols.text.hasOwnProperty(name) || implicitCommands.hasOwnProperty(name); + } + /** + * Determine whether a command is expandable. + */ + ; + + _proto.isExpandable = function isExpandable(name) { + var macro = this.macros.get(name); + return macro != null ? typeof macro === "string" || typeof macro === "function" || !macro.unexpandable : src_functions.hasOwnProperty(name) && !src_functions[name].primitive; + }; + + return MacroExpander; +}(); + + +;// CONCATENATED MODULE: ./src/Parser.js +/* eslint no-constant-condition:0 */ + + + + + + + + + + // Pre-evaluate both modules as unicodeSymbols require String.normalize() + +var unicodeAccents = { + "́": { + "text": "\\'", + "math": "\\acute" + }, + "̀": { + "text": "\\`", + "math": "\\grave" + }, + "̈": { + "text": "\\\"", + "math": "\\ddot" + }, + "̃": { + "text": "\\~", + "math": "\\tilde" + }, + "̄": { + "text": "\\=", + "math": "\\bar" + }, + "̆": { + "text": "\\u", + "math": "\\breve" + }, + "̌": { + "text": "\\v", + "math": "\\check" + }, + "̂": { + "text": "\\^", + "math": "\\hat" + }, + "̇": { + "text": "\\.", + "math": "\\dot" + }, + "̊": { + "text": "\\r", + "math": "\\mathring" + }, + "̋": { + "text": "\\H" + } +}; +var unicodeSymbols = { + "á": "á", + "à": "à", + "ä": "ä", + "ǟ": "ǟ", + "ã": "ã", + "ā": "ā", + "ă": "ă", + "ắ": "ắ", + "ằ": "ằ", + "ẵ": "ẵ", + "ǎ": "ǎ", + "â": "â", + "ấ": "ấ", + "ầ": "ầ", + "ẫ": "ẫ", + "ȧ": "ȧ", + "ǡ": "ǡ", + "å": "å", + "ǻ": "ǻ", + "ḃ": "ḃ", + "ć": "ć", + "č": "č", + "ĉ": "ĉ", + "ċ": "ċ", + "ď": "ď", + "ḋ": "ḋ", + "é": "é", + "è": "è", + "ë": "ë", + "ẽ": "ẽ", + "ē": "ē", + "ḗ": "ḗ", + "ḕ": "ḕ", + "ĕ": "ĕ", + "ě": "ě", + "ê": "ê", + "ế": "ế", + "ề": "ề", + "ễ": "ễ", + "ė": "ė", + "ḟ": "ḟ", + "ǵ": "ǵ", + "ḡ": "ḡ", + "ğ": "ğ", + "ǧ": "ǧ", + "ĝ": "ĝ", + "ġ": "ġ", + "ḧ": "ḧ", + "ȟ": "ȟ", + "ĥ": "ĥ", + "ḣ": "ḣ", + "í": "í", + "ì": "ì", + "ï": "ï", + "ḯ": "ḯ", + "ĩ": "ĩ", + "ī": "ī", + "ĭ": "ĭ", + "ǐ": "ǐ", + "î": "î", + "ǰ": "ǰ", + "ĵ": "ĵ", + "ḱ": "ḱ", + "ǩ": "ǩ", + "ĺ": "ĺ", + "ľ": "ľ", + "ḿ": "ḿ", + "ṁ": "ṁ", + "ń": "ń", + "ǹ": "ǹ", + "ñ": "ñ", + "ň": "ň", + "ṅ": "ṅ", + "ó": "ó", + "ò": "ò", + "ö": "ö", + "ȫ": "ȫ", + "õ": "õ", + "ṍ": "ṍ", + "ṏ": "ṏ", + "ȭ": "ȭ", + "ō": "ō", + "ṓ": "ṓ", + "ṑ": "ṑ", + "ŏ": "ŏ", + "ǒ": "ǒ", + "ô": "ô", + "ố": "ố", + "ồ": "ồ", + "ỗ": "ỗ", + "ȯ": "ȯ", + "ȱ": "ȱ", + "ő": "ő", + "ṕ": "ṕ", + "ṗ": "ṗ", + "ŕ": "ŕ", + "ř": "ř", + "ṙ": "ṙ", + "ś": "ś", + "ṥ": "ṥ", + "š": "š", + "ṧ": "ṧ", + "ŝ": "ŝ", + "ṡ": "ṡ", + "ẗ": "ẗ", + "ť": "ť", + "ṫ": "ṫ", + "ú": "ú", + "ù": "ù", + "ü": "ü", + "ǘ": "ǘ", + "ǜ": "ǜ", + "ǖ": "ǖ", + "ǚ": "ǚ", + "ũ": "ũ", + "ṹ": "ṹ", + "ū": "ū", + "ṻ": "ṻ", + "ŭ": "ŭ", + "ǔ": "ǔ", + "û": "û", + "ů": "ů", + "ű": "ű", + "ṽ": "ṽ", + "ẃ": "ẃ", + "ẁ": "ẁ", + "ẅ": "ẅ", + "ŵ": "ŵ", + "ẇ": "ẇ", + "ẘ": "ẘ", + "ẍ": "ẍ", + "ẋ": "ẋ", + "ý": "ý", + "ỳ": "ỳ", + "ÿ": "ÿ", + "ỹ": "ỹ", + "ȳ": "ȳ", + "ŷ": "ŷ", + "ẏ": "ẏ", + "ẙ": "ẙ", + "ź": "ź", + "ž": "ž", + "ẑ": "ẑ", + "ż": "ż", + "Á": "Á", + "À": "À", + "Ä": "Ä", + "Ǟ": "Ǟ", + "Ã": "Ã", + "Ā": "Ā", + "Ă": "Ă", + "Ắ": "Ắ", + "Ằ": "Ằ", + "Ẵ": "Ẵ", + "Ǎ": "Ǎ", + "Â": "Â", + "Ấ": "Ấ", + "Ầ": "Ầ", + "Ẫ": "Ẫ", + "Ȧ": "Ȧ", + "Ǡ": "Ǡ", + "Å": "Å", + "Ǻ": "Ǻ", + "Ḃ": "Ḃ", + "Ć": "Ć", + "Č": "Č", + "Ĉ": "Ĉ", + "Ċ": "Ċ", + "Ď": "Ď", + "Ḋ": "Ḋ", + "É": "É", + "È": "È", + "Ë": "Ë", + "Ẽ": "Ẽ", + "Ē": "Ē", + "Ḗ": "Ḗ", + "Ḕ": "Ḕ", + "Ĕ": "Ĕ", + "Ě": "Ě", + "Ê": "Ê", + "Ế": "Ế", + "Ề": "Ề", + "Ễ": "Ễ", + "Ė": "Ė", + "Ḟ": "Ḟ", + "Ǵ": "Ǵ", + "Ḡ": "Ḡ", + "Ğ": "Ğ", + "Ǧ": "Ǧ", + "Ĝ": "Ĝ", + "Ġ": "Ġ", + "Ḧ": "Ḧ", + "Ȟ": "Ȟ", + "Ĥ": "Ĥ", + "Ḣ": "Ḣ", + "Í": "Í", + "Ì": "Ì", + "Ï": "Ï", + "Ḯ": "Ḯ", + "Ĩ": "Ĩ", + "Ī": "Ī", + "Ĭ": "Ĭ", + "Ǐ": "Ǐ", + "Î": "Î", + "İ": "İ", + "Ĵ": "Ĵ", + "Ḱ": "Ḱ", + "Ǩ": "Ǩ", + "Ĺ": "Ĺ", + "Ľ": "Ľ", + "Ḿ": "Ḿ", + "Ṁ": "Ṁ", + "Ń": "Ń", + "Ǹ": "Ǹ", + "Ñ": "Ñ", + "Ň": "Ň", + "Ṅ": "Ṅ", + "Ó": "Ó", + "Ò": "Ò", + "Ö": "Ö", + "Ȫ": "Ȫ", + "Õ": "Õ", + "Ṍ": "Ṍ", + "Ṏ": "Ṏ", + "Ȭ": "Ȭ", + "Ō": "Ō", + "Ṓ": "Ṓ", + "Ṑ": "Ṑ", + "Ŏ": "Ŏ", + "Ǒ": "Ǒ", + "Ô": "Ô", + "Ố": "Ố", + "Ồ": "Ồ", + "Ỗ": "Ỗ", + "Ȯ": "Ȯ", + "Ȱ": "Ȱ", + "Ő": "Ő", + "Ṕ": "Ṕ", + "Ṗ": "Ṗ", + "Ŕ": "Ŕ", + "Ř": "Ř", + "Ṙ": "Ṙ", + "Ś": "Ś", + "Ṥ": "Ṥ", + "Š": "Š", + "Ṧ": "Ṧ", + "Ŝ": "Ŝ", + "Ṡ": "Ṡ", + "Ť": "Ť", + "Ṫ": "Ṫ", + "Ú": "Ú", + "Ù": "Ù", + "Ü": "Ü", + "Ǘ": "Ǘ", + "Ǜ": "Ǜ", + "Ǖ": "Ǖ", + "Ǚ": "Ǚ", + "Ũ": "Ũ", + "Ṹ": "Ṹ", + "Ū": "Ū", + "Ṻ": "Ṻ", + "Ŭ": "Ŭ", + "Ǔ": "Ǔ", + "Û": "Û", + "Ů": "Ů", + "Ű": "Ű", + "Ṽ": "Ṽ", + "Ẃ": "Ẃ", + "Ẁ": "Ẁ", + "Ẅ": "Ẅ", + "Ŵ": "Ŵ", + "Ẇ": "Ẇ", + "Ẍ": "Ẍ", + "Ẋ": "Ẋ", + "Ý": "Ý", + "Ỳ": "Ỳ", + "Ÿ": "Ÿ", + "Ỹ": "Ỹ", + "Ȳ": "Ȳ", + "Ŷ": "Ŷ", + "Ẏ": "Ẏ", + "Ź": "Ź", + "Ž": "Ž", + "Ẑ": "Ẑ", + "Ż": "Ż", + "ά": "ά", + "ὰ": "ὰ", + "ᾱ": "ᾱ", + "ᾰ": "ᾰ", + "έ": "έ", + "ὲ": "ὲ", + "ή": "ή", + "ὴ": "ὴ", + "ί": "ί", + "ὶ": "ὶ", + "ϊ": "ϊ", + "ΐ": "ΐ", + "ῒ": "ῒ", + "ῑ": "ῑ", + "ῐ": "ῐ", + "ό": "ό", + "ὸ": "ὸ", + "ύ": "ύ", + "ὺ": "ὺ", + "ϋ": "ϋ", + "ΰ": "ΰ", + "ῢ": "ῢ", + "ῡ": "ῡ", + "ῠ": "ῠ", + "ώ": "ώ", + "ὼ": "ὼ", + "Ύ": "Ύ", + "Ὺ": "Ὺ", + "Ϋ": "Ϋ", + "Ῡ": "Ῡ", + "Ῠ": "Ῠ", + "Ώ": "Ώ", + "Ὼ": "Ὼ" +}; + +/** + * This file contains the parser used to parse out a TeX expression from the + * input. Since TeX isn't context-free, standard parsers don't work particularly + * well. + * + * The strategy of this parser is as such: + * + * The main functions (the `.parse...` ones) take a position in the current + * parse string to parse tokens from. The lexer (found in Lexer.js, stored at + * this.gullet.lexer) also supports pulling out tokens at arbitrary places. When + * individual tokens are needed at a position, the lexer is called to pull out a + * token, which is then used. + * + * The parser has a property called "mode" indicating the mode that + * the parser is currently in. Currently it has to be one of "math" or + * "text", which denotes whether the current environment is a math-y + * one or a text-y one (e.g. inside \text). Currently, this serves to + * limit the functions which can be used in text mode. + * + * The main functions then return an object which contains the useful data that + * was parsed at its given point, and a new position at the end of the parsed + * data. The main functions can call each other and continue the parsing by + * using the returned position as a new starting point. + * + * There are also extra `.handle...` functions, which pull out some reused + * functionality into self-contained functions. + * + * The functions return ParseNodes. + */ +var Parser = /*#__PURE__*/function () { + function Parser(input, settings) { + this.mode = void 0; + this.gullet = void 0; + this.settings = void 0; + this.leftrightDepth = void 0; + this.nextToken = void 0; + // Start in math mode + this.mode = "math"; // Create a new macro expander (gullet) and (indirectly via that) also a + // new lexer (mouth) for this parser (stomach, in the language of TeX) + + this.gullet = new MacroExpander(input, settings, this.mode); // Store the settings for use in parsing + + this.settings = settings; // Count leftright depth (for \middle errors) + + this.leftrightDepth = 0; + } + /** + * Checks a result to make sure it has the right type, and throws an + * appropriate error otherwise. + */ + + + var _proto = Parser.prototype; + + _proto.expect = function expect(text, consume) { + if (consume === void 0) { + consume = true; + } + + if (this.fetch().text !== text) { + throw new src_ParseError("Expected '" + text + "', got '" + this.fetch().text + "'", this.fetch()); + } + + if (consume) { + this.consume(); + } + } + /** + * Discards the current lookahead token, considering it consumed. + */ + ; + + _proto.consume = function consume() { + this.nextToken = null; + } + /** + * Return the current lookahead token, or if there isn't one (at the + * beginning, or if the previous lookahead token was consume()d), + * fetch the next token as the new lookahead token and return it. + */ + ; + + _proto.fetch = function fetch() { + if (this.nextToken == null) { + this.nextToken = this.gullet.expandNextToken(); + } + + return this.nextToken; + } + /** + * Switches between "text" and "math" modes. + */ + ; + + _proto.switchMode = function switchMode(newMode) { + this.mode = newMode; + this.gullet.switchMode(newMode); + } + /** + * Main parsing function, which parses an entire input. + */ + ; + + _proto.parse = function parse() { + if (!this.settings.globalGroup) { + // Create a group namespace for the math expression. + // (LaTeX creates a new group for every $...$, $$...$$, \[...\].) + this.gullet.beginGroup(); + } // Use old \color behavior (same as LaTeX's \textcolor) if requested. + // We do this within the group for the math expression, so it doesn't + // pollute settings.macros. + + + if (this.settings.colorIsTextColor) { + this.gullet.macros.set("\\color", "\\textcolor"); + } // Try to parse the input + + + var parse = this.parseExpression(false); // If we succeeded, make sure there's an EOF at the end + + this.expect("EOF"); // End the group namespace for the expression + + if (!this.settings.globalGroup) { + this.gullet.endGroup(); + } + + return parse; + }; + + /** + * Parses an "expression", which is a list of atoms. + * + * `breakOnInfix`: Should the parsing stop when we hit infix nodes? This + * happens when functions have higher precendence han infix + * nodes in implicit parses. + * + * `breakOnTokenText`: The text of the token that the expression should end + * with, or `null` if something else should end the + * expression. + */ + _proto.parseExpression = function parseExpression(breakOnInfix, breakOnTokenText) { + var body = []; // Keep adding atoms to the body until we can't parse any more atoms (either + // we reached the end, a }, or a \right) + + while (true) { + // Ignore spaces in math mode + if (this.mode === "math") { + this.consumeSpaces(); + } + + var lex = this.fetch(); + + if (Parser.endOfExpression.indexOf(lex.text) !== -1) { + break; + } + + if (breakOnTokenText && lex.text === breakOnTokenText) { + break; + } + + if (breakOnInfix && src_functions[lex.text] && src_functions[lex.text].infix) { + break; + } + + var atom = this.parseAtom(breakOnTokenText); + + if (!atom) { + break; + } else if (atom.type === "internal") { + continue; + } + + body.push(atom); + } + + if (this.mode === "text") { + this.formLigatures(body); + } + + return this.handleInfixNodes(body); + } + /** + * Rewrites infix operators such as \over with corresponding commands such + * as \frac. + * + * There can only be one infix operator per group. If there's more than one + * then the expression is ambiguous. This can be resolved by adding {}. + */ + ; + + _proto.handleInfixNodes = function handleInfixNodes(body) { + var overIndex = -1; + var funcName; + + for (var i = 0; i < body.length; i++) { + if (body[i].type === "infix") { + if (overIndex !== -1) { + throw new src_ParseError("only one infix operator per group", body[i].token); + } + + overIndex = i; + funcName = body[i].replaceWith; + } + } + + if (overIndex !== -1 && funcName) { + var numerNode; + var denomNode; + var numerBody = body.slice(0, overIndex); + var denomBody = body.slice(overIndex + 1); + + if (numerBody.length === 1 && numerBody[0].type === "ordgroup") { + numerNode = numerBody[0]; + } else { + numerNode = { + type: "ordgroup", + mode: this.mode, + body: numerBody + }; + } + + if (denomBody.length === 1 && denomBody[0].type === "ordgroup") { + denomNode = denomBody[0]; + } else { + denomNode = { + type: "ordgroup", + mode: this.mode, + body: denomBody + }; + } + + var node; + + if (funcName === "\\\\abovefrac") { + node = this.callFunction(funcName, [numerNode, body[overIndex], denomNode], []); + } else { + node = this.callFunction(funcName, [numerNode, denomNode], []); + } + + return [node]; + } else { + return body; + } + } + /** + * Handle a subscript or superscript with nice errors. + */ + ; + + _proto.handleSupSubscript = function handleSupSubscript(name) { + var symbolToken = this.fetch(); + var symbol = symbolToken.text; + this.consume(); + this.consumeSpaces(); // ignore spaces before sup/subscript argument + + var group = this.parseGroup(name); + + if (!group) { + throw new src_ParseError("Expected group after '" + symbol + "'", symbolToken); + } + + return group; + } + /** + * Converts the textual input of an unsupported command into a text node + * contained within a color node whose color is determined by errorColor + */ + ; + + _proto.formatUnsupportedCmd = function formatUnsupportedCmd(text) { + var textordArray = []; + + for (var i = 0; i < text.length; i++) { + textordArray.push({ + type: "textord", + mode: "text", + text: text[i] + }); + } + + var textNode = { + type: "text", + mode: this.mode, + body: textordArray + }; + var colorNode = { + type: "color", + mode: this.mode, + color: this.settings.errorColor, + body: [textNode] + }; + return colorNode; + } + /** + * Parses a group with optional super/subscripts. + */ + ; + + _proto.parseAtom = function parseAtom(breakOnTokenText) { + // The body of an atom is an implicit group, so that things like + // \left(x\right)^2 work correctly. + var base = this.parseGroup("atom", breakOnTokenText); // In text mode, we don't have superscripts or subscripts + + if (this.mode === "text") { + return base; + } // Note that base may be empty (i.e. null) at this point. + + + var superscript; + var subscript; + + while (true) { + // Guaranteed in math mode, so eat any spaces first. + this.consumeSpaces(); // Lex the first token + + var lex = this.fetch(); + + if (lex.text === "\\limits" || lex.text === "\\nolimits") { + // We got a limit control + if (base && base.type === "op") { + var limits = lex.text === "\\limits"; + base.limits = limits; + base.alwaysHandleSupSub = true; + } else if (base && base.type === "operatorname") { + if (base.alwaysHandleSupSub) { + base.limits = lex.text === "\\limits"; + } + } else { + throw new src_ParseError("Limit controls must follow a math operator", lex); + } + + this.consume(); + } else if (lex.text === "^") { + // We got a superscript start + if (superscript) { + throw new src_ParseError("Double superscript", lex); + } + + superscript = this.handleSupSubscript("superscript"); + } else if (lex.text === "_") { + // We got a subscript start + if (subscript) { + throw new src_ParseError("Double subscript", lex); + } + + subscript = this.handleSupSubscript("subscript"); + } else if (lex.text === "'") { + // We got a prime + if (superscript) { + throw new src_ParseError("Double superscript", lex); + } + + var prime = { + type: "textord", + mode: this.mode, + text: "\\prime" + }; // Many primes can be grouped together, so we handle this here + + var primes = [prime]; + this.consume(); // Keep lexing tokens until we get something that's not a prime + + while (this.fetch().text === "'") { + // For each one, add another prime to the list + primes.push(prime); + this.consume(); + } // If there's a superscript following the primes, combine that + // superscript in with the primes. + + + if (this.fetch().text === "^") { + primes.push(this.handleSupSubscript("superscript")); + } // Put everything into an ordgroup as the superscript + + + superscript = { + type: "ordgroup", + mode: this.mode, + body: primes + }; + } else { + // If it wasn't ^, _, or ', stop parsing super/subscripts + break; + } + } // Base must be set if superscript or subscript are set per logic above, + // but need to check here for type check to pass. + + + if (superscript || subscript) { + // If we got either a superscript or subscript, create a supsub + return { + type: "supsub", + mode: this.mode, + base: base, + sup: superscript, + sub: subscript + }; + } else { + // Otherwise return the original body + return base; + } + } + /** + * Parses an entire function, including its base and all of its arguments. + */ + ; + + _proto.parseFunction = function parseFunction(breakOnTokenText, name) { + var token = this.fetch(); + var func = token.text; + var funcData = src_functions[func]; + + if (!funcData) { + return null; + } + + this.consume(); // consume command token + + if (name && name !== "atom" && !funcData.allowedInArgument) { + throw new src_ParseError("Got function '" + func + "' with no arguments" + (name ? " as " + name : ""), token); + } else if (this.mode === "text" && !funcData.allowedInText) { + throw new src_ParseError("Can't use function '" + func + "' in text mode", token); + } else if (this.mode === "math" && funcData.allowedInMath === false) { + throw new src_ParseError("Can't use function '" + func + "' in math mode", token); + } + + var _this$parseArguments = this.parseArguments(func, funcData), + args = _this$parseArguments.args, + optArgs = _this$parseArguments.optArgs; + + return this.callFunction(func, args, optArgs, token, breakOnTokenText); + } + /** + * Call a function handler with a suitable context and arguments. + */ + ; + + _proto.callFunction = function callFunction(name, args, optArgs, token, breakOnTokenText) { + var context = { + funcName: name, + parser: this, + token: token, + breakOnTokenText: breakOnTokenText + }; + var func = src_functions[name]; + + if (func && func.handler) { + return func.handler(context, args, optArgs); + } else { + throw new src_ParseError("No function handler for " + name); + } + } + /** + * Parses the arguments of a function or environment + */ + ; + + _proto.parseArguments = function parseArguments(func, // Should look like "\name" or "\begin{name}". + funcData) { + var totalArgs = funcData.numArgs + funcData.numOptionalArgs; + + if (totalArgs === 0) { + return { + args: [], + optArgs: [] + }; + } + + var args = []; + var optArgs = []; + + for (var i = 0; i < totalArgs; i++) { + var argType = funcData.argTypes && funcData.argTypes[i]; + var isOptional = i < funcData.numOptionalArgs; + + if (funcData.primitive && argType == null || // \sqrt expands into primitive if optional argument doesn't exist + funcData.type === "sqrt" && i === 1 && optArgs[0] == null) { + argType = "primitive"; + } + + var arg = this.parseGroupOfType("argument to '" + func + "'", argType, isOptional); + + if (isOptional) { + optArgs.push(arg); + } else if (arg != null) { + args.push(arg); + } else { + // should be unreachable + throw new src_ParseError("Null argument, please report this as a bug"); + } + } + + return { + args: args, + optArgs: optArgs + }; + } + /** + * Parses a group when the mode is changing. + */ + ; + + _proto.parseGroupOfType = function parseGroupOfType(name, type, optional) { + switch (type) { + case "color": + return this.parseColorGroup(optional); + + case "size": + return this.parseSizeGroup(optional); + + case "url": + return this.parseUrlGroup(optional); + + case "math": + case "text": + return this.parseArgumentGroup(optional, type); + + case "hbox": + { + // hbox argument type wraps the argument in the equivalent of + // \hbox, which is like \text but switching to \textstyle size. + var group = this.parseArgumentGroup(optional, "text"); + return group != null ? { + type: "styling", + mode: group.mode, + body: [group], + style: "text" // simulate \textstyle + + } : null; + } + + case "raw": + { + var token = this.parseStringGroup("raw", optional); + return token != null ? { + type: "raw", + mode: "text", + string: token.text + } : null; + } + + case "primitive": + { + if (optional) { + throw new src_ParseError("A primitive argument cannot be optional"); + } + + var _group = this.parseGroup(name); + + if (_group == null) { + throw new src_ParseError("Expected group as " + name, this.fetch()); + } + + return _group; + } + + case "original": + case null: + case undefined: + return this.parseArgumentGroup(optional); + + default: + throw new src_ParseError("Unknown group type as " + name, this.fetch()); + } + } + /** + * Discard any space tokens, fetching the next non-space token. + */ + ; + + _proto.consumeSpaces = function consumeSpaces() { + while (this.fetch().text === " ") { + this.consume(); + } + } + /** + * Parses a group, essentially returning the string formed by the + * brace-enclosed tokens plus some position information. + */ + ; + + _proto.parseStringGroup = function parseStringGroup(modeName, // Used to describe the mode in error messages. + optional) { + var argToken = this.gullet.scanArgument(optional); + + if (argToken == null) { + return null; + } + + var str = ""; + var nextToken; + + while ((nextToken = this.fetch()).text !== "EOF") { + str += nextToken.text; + this.consume(); + } + + this.consume(); // consume the end of the argument + + argToken.text = str; + return argToken; + } + /** + * Parses a regex-delimited group: the largest sequence of tokens + * whose concatenated strings match `regex`. Returns the string + * formed by the tokens plus some position information. + */ + ; + + _proto.parseRegexGroup = function parseRegexGroup(regex, modeName) { + var firstToken = this.fetch(); + var lastToken = firstToken; + var str = ""; + var nextToken; + + while ((nextToken = this.fetch()).text !== "EOF" && regex.test(str + nextToken.text)) { + lastToken = nextToken; + str += lastToken.text; + this.consume(); + } + + if (str === "") { + throw new src_ParseError("Invalid " + modeName + ": '" + firstToken.text + "'", firstToken); + } + + return firstToken.range(lastToken, str); + } + /** + * Parses a color description. + */ + ; + + _proto.parseColorGroup = function parseColorGroup(optional) { + var res = this.parseStringGroup("color", optional); + + if (res == null) { + return null; + } + + var match = /^(#[a-f0-9]{3}|#?[a-f0-9]{6}|[a-z]+)$/i.exec(res.text); + + if (!match) { + throw new src_ParseError("Invalid color: '" + res.text + "'", res); + } + + var color = match[0]; + + if (/^[0-9a-f]{6}$/i.test(color)) { + // We allow a 6-digit HTML color spec without a leading "#". + // This follows the xcolor package's HTML color model. + // Predefined color names are all missed by this RegEx pattern. + color = "#" + color; + } + + return { + type: "color-token", + mode: this.mode, + color: color + }; + } + /** + * Parses a size specification, consisting of magnitude and unit. + */ + ; + + _proto.parseSizeGroup = function parseSizeGroup(optional) { + var res; + var isBlank = false; // don't expand before parseStringGroup + + this.gullet.consumeSpaces(); + + if (!optional && this.gullet.future().text !== "{") { + res = this.parseRegexGroup(/^[-+]? *(?:$|\d+|\d+\.\d*|\.\d*) *[a-z]{0,2} *$/, "size"); + } else { + res = this.parseStringGroup("size", optional); + } + + if (!res) { + return null; + } + + if (!optional && res.text.length === 0) { + // Because we've tested for what is !optional, this block won't + // affect \kern, \hspace, etc. It will capture the mandatory arguments + // to \genfrac and \above. + res.text = "0pt"; // Enable \above{} + + isBlank = true; // This is here specifically for \genfrac + } + + var match = /([-+]?) *(\d+(?:\.\d*)?|\.\d+) *([a-z]{2})/.exec(res.text); + + if (!match) { + throw new src_ParseError("Invalid size: '" + res.text + "'", res); + } + + var data = { + number: +(match[1] + match[2]), + // sign + magnitude, cast to number + unit: match[3] + }; + + if (!validUnit(data)) { + throw new src_ParseError("Invalid unit: '" + data.unit + "'", res); + } + + return { + type: "size", + mode: this.mode, + value: data, + isBlank: isBlank + }; + } + /** + * Parses an URL, checking escaped letters and allowed protocols, + * and setting the catcode of % as an active character (as in \hyperref). + */ + ; + + _proto.parseUrlGroup = function parseUrlGroup(optional) { + this.gullet.lexer.setCatcode("%", 13); // active character + + this.gullet.lexer.setCatcode("~", 12); // other character + + var res = this.parseStringGroup("url", optional); + this.gullet.lexer.setCatcode("%", 14); // comment character + + this.gullet.lexer.setCatcode("~", 13); // active character + + if (res == null) { + return null; + } // hyperref package allows backslashes alone in href, but doesn't + // generate valid links in such cases; we interpret this as + // "undefined" behaviour, and keep them as-is. Some browser will + // replace backslashes with forward slashes. + + + var url = res.text.replace(/\\([#$%&~_^{}])/g, '$1'); + return { + type: "url", + mode: this.mode, + url: url + }; + } + /** + * Parses an argument with the mode specified. + */ + ; + + _proto.parseArgumentGroup = function parseArgumentGroup(optional, mode) { + var argToken = this.gullet.scanArgument(optional); + + if (argToken == null) { + return null; + } + + var outerMode = this.mode; + + if (mode) { + // Switch to specified mode + this.switchMode(mode); + } + + this.gullet.beginGroup(); + var expression = this.parseExpression(false, "EOF"); // TODO: find an alternative way to denote the end + + this.expect("EOF"); // expect the end of the argument + + this.gullet.endGroup(); + var result = { + type: "ordgroup", + mode: this.mode, + loc: argToken.loc, + body: expression + }; + + if (mode) { + // Switch mode back + this.switchMode(outerMode); + } + + return result; + } + /** + * Parses an ordinary group, which is either a single nucleus (like "x") + * or an expression in braces (like "{x+y}") or an implicit group, a group + * that starts at the current position, and ends right before a higher explicit + * group ends, or at EOF. + */ + ; + + _proto.parseGroup = function parseGroup(name, // For error reporting. + breakOnTokenText) { + var firstToken = this.fetch(); + var text = firstToken.text; + var result; // Try to parse an open brace or \begingroup + + if (text === "{" || text === "\\begingroup") { + this.consume(); + var groupEnd = text === "{" ? "}" : "\\endgroup"; + this.gullet.beginGroup(); // If we get a brace, parse an expression + + var expression = this.parseExpression(false, groupEnd); + var lastToken = this.fetch(); + this.expect(groupEnd); // Check that we got a matching closing brace + + this.gullet.endGroup(); + result = { + type: "ordgroup", + mode: this.mode, + loc: SourceLocation.range(firstToken, lastToken), + body: expression, + // A group formed by \begingroup...\endgroup is a semi-simple group + // which doesn't affect spacing in math mode, i.e., is transparent. + // https://tex.stackexchange.com/questions/1930/when-should-one- + // use-begingroup-instead-of-bgroup + semisimple: text === "\\begingroup" || undefined + }; + } else { + // If there exists a function with this name, parse the function. + // Otherwise, just return a nucleus + result = this.parseFunction(breakOnTokenText, name) || this.parseSymbol(); + + if (result == null && text[0] === "\\" && !implicitCommands.hasOwnProperty(text)) { + if (this.settings.throwOnError) { + throw new src_ParseError("Undefined control sequence: " + text, firstToken); + } + + result = this.formatUnsupportedCmd(text); + this.consume(); + } + } + + return result; + } + /** + * Form ligature-like combinations of characters for text mode. + * This includes inputs like "--", "---", "``" and "''". + * The result will simply replace multiple textord nodes with a single + * character in each value by a single textord node having multiple + * characters in its value. The representation is still ASCII source. + * The group will be modified in place. + */ + ; + + _proto.formLigatures = function formLigatures(group) { + var n = group.length - 1; + + for (var i = 0; i < n; ++i) { + var a = group[i]; // $FlowFixMe: Not every node type has a `text` property. + + var v = a.text; + + if (v === "-" && group[i + 1].text === "-") { + if (i + 1 < n && group[i + 2].text === "-") { + group.splice(i, 3, { + type: "textord", + mode: "text", + loc: SourceLocation.range(a, group[i + 2]), + text: "---" + }); + n -= 2; + } else { + group.splice(i, 2, { + type: "textord", + mode: "text", + loc: SourceLocation.range(a, group[i + 1]), + text: "--" + }); + n -= 1; + } + } + + if ((v === "'" || v === "`") && group[i + 1].text === v) { + group.splice(i, 2, { + type: "textord", + mode: "text", + loc: SourceLocation.range(a, group[i + 1]), + text: v + v + }); + n -= 1; + } + } + } + /** + * Parse a single symbol out of the string. Here, we handle single character + * symbols and special functions like \verb. + */ + ; + + _proto.parseSymbol = function parseSymbol() { + var nucleus = this.fetch(); + var text = nucleus.text; + + if (/^\\verb[^a-zA-Z]/.test(text)) { + this.consume(); + var arg = text.slice(5); + var star = arg.charAt(0) === "*"; + + if (star) { + arg = arg.slice(1); + } // Lexer's tokenRegex is constructed to always have matching + // first/last characters. + + + if (arg.length < 2 || arg.charAt(0) !== arg.slice(-1)) { + throw new src_ParseError("\\verb assertion failed --\n please report what input caused this bug"); + } + + arg = arg.slice(1, -1); // remove first and last char + + return { + type: "verb", + mode: "text", + body: arg, + star: star + }; + } // At this point, we should have a symbol, possibly with accents. + // First expand any accented base symbol according to unicodeSymbols. + + + if (unicodeSymbols.hasOwnProperty(text[0]) && !src_symbols[this.mode][text[0]]) { + // This behavior is not strict (XeTeX-compatible) in math mode. + if (this.settings.strict && this.mode === "math") { + this.settings.reportNonstrict("unicodeTextInMathMode", "Accented Unicode text character \"" + text[0] + "\" used in " + "math mode", nucleus); + } + + text = unicodeSymbols[text[0]] + text.substr(1); + } // Strip off any combining characters + + + var match = combiningDiacriticalMarksEndRegex.exec(text); + + if (match) { + text = text.substring(0, match.index); + + if (text === 'i') { + text = "\u0131"; // dotless i, in math and text mode + } else if (text === 'j') { + text = "\u0237"; // dotless j, in math and text mode + } + } // Recognize base symbol + + + var symbol; + + if (src_symbols[this.mode][text]) { + if (this.settings.strict && this.mode === 'math' && extraLatin.indexOf(text) >= 0) { + this.settings.reportNonstrict("unicodeTextInMathMode", "Latin-1/Unicode text character \"" + text[0] + "\" used in " + "math mode", nucleus); + } + + var group = src_symbols[this.mode][text].group; + var loc = SourceLocation.range(nucleus); + var s; + + if (ATOMS.hasOwnProperty(group)) { + // $FlowFixMe + var family = group; + s = { + type: "atom", + mode: this.mode, + family: family, + loc: loc, + text: text + }; + } else { + // $FlowFixMe + s = { + type: group, + mode: this.mode, + loc: loc, + text: text + }; + } // $FlowFixMe + + + symbol = s; + } else if (text.charCodeAt(0) >= 0x80) { + // no symbol for e.g. ^ + if (this.settings.strict) { + if (!supportedCodepoint(text.charCodeAt(0))) { + this.settings.reportNonstrict("unknownSymbol", "Unrecognized Unicode character \"" + text[0] + "\"" + (" (" + text.charCodeAt(0) + ")"), nucleus); + } else if (this.mode === "math") { + this.settings.reportNonstrict("unicodeTextInMathMode", "Unicode text character \"" + text[0] + "\" used in math mode", nucleus); + } + } // All nonmathematical Unicode characters are rendered as if they + // are in text mode (wrapped in \text) because that's what it + // takes to render them in LaTeX. Setting `mode: this.mode` is + // another natural choice (the user requested math mode), but + // this makes it more difficult for getCharacterMetrics() to + // distinguish Unicode characters without metrics and those for + // which we want to simulate the letter M. + + + symbol = { + type: "textord", + mode: "text", + loc: SourceLocation.range(nucleus), + text: text + }; + } else { + return null; // EOF, ^, _, {, }, etc. + } + + this.consume(); // Transform combining characters into accents + + if (match) { + for (var i = 0; i < match[0].length; i++) { + var accent = match[0][i]; + + if (!unicodeAccents[accent]) { + throw new src_ParseError("Unknown accent ' " + accent + "'", nucleus); + } + + var command = unicodeAccents[accent][this.mode]; + + if (!command) { + throw new src_ParseError("Accent " + accent + " unsupported in " + this.mode + " mode", nucleus); + } + + symbol = { + type: "accent", + mode: this.mode, + loc: SourceLocation.range(nucleus), + label: command, + isStretchy: false, + isShifty: true, + // $FlowFixMe + base: symbol + }; + } + } // $FlowFixMe + + + return symbol; + }; + + return Parser; +}(); + +Parser.endOfExpression = ["}", "\\endgroup", "\\end", "\\right", "&"]; + +;// CONCATENATED MODULE: ./src/parseTree.js +/** + * Provides a single function for parsing an expression using a Parser + * TODO(emily): Remove this + */ + + + +/** + * Parses an expression using a Parser, then returns the parsed result. + */ +var parseTree = function parseTree(toParse, settings) { + if (!(typeof toParse === 'string' || toParse instanceof String)) { + throw new TypeError('KaTeX can only parse string typed expression'); + } + + var parser = new Parser(toParse, settings); // Blank out any \df@tag to avoid spurious "Duplicate \tag" errors + + delete parser.gullet.macros.current["\\df@tag"]; + var tree = parser.parse(); // Prevent a color definition from persisting between calls to katex.render(). + + delete parser.gullet.macros.current["\\current@color"]; + delete parser.gullet.macros.current["\\color"]; // If the input used \tag, it will set the \df@tag macro to the tag. + // In this case, we separately parse the tag and wrap the tree. + + if (parser.gullet.macros.get("\\df@tag")) { + if (!settings.displayMode) { + throw new src_ParseError("\\tag works only in display equations"); + } + + parser.gullet.feed("\\df@tag"); + tree = [{ + type: "tag", + mode: "text", + body: tree, + tag: parser.parse() + }]; + } + + return tree; +}; + +/* harmony default export */ var src_parseTree = (parseTree); +;// CONCATENATED MODULE: ./katex.js +/* eslint no-console:0 */ + +/** + * This is the main entry point for KaTeX. Here, we expose functions for + * rendering expressions either to DOM nodes or to markup strings. + * + * We also expose the ParseError class to check if errors thrown from KaTeX are + * errors in the expression, or errors in javascript handling. + */ + + + + + + + + + + +/** + * Parse and build an expression, and place that expression in the DOM node + * given. + */ +var render = function render(expression, baseNode, options) { + baseNode.textContent = ""; + var node = renderToDomTree(expression, options).toNode(); + baseNode.appendChild(node); +}; // KaTeX's styles don't work properly in quirks mode. Print out an error, and +// disable rendering. + + +if (typeof document !== "undefined") { + if (document.compatMode !== "CSS1Compat") { + typeof console !== "undefined" && console.warn("Warning: KaTeX doesn't work in quirks mode. Make sure your " + "website has a suitable doctype."); + + render = function render() { + throw new src_ParseError("KaTeX doesn't work in quirks mode."); + }; + } +} +/** + * Parse and build an expression, and return the markup for that. + */ + + +var renderToString = function renderToString(expression, options) { + var markup = renderToDomTree(expression, options).toMarkup(); + return markup; +}; +/** + * Parse an expression and return the parse tree. + */ + + +var generateParseTree = function generateParseTree(expression, options) { + var settings = new Settings(options); + return src_parseTree(expression, settings); +}; +/** + * If the given error is a KaTeX ParseError and options.throwOnError is false, + * renders the invalid LaTeX as a span with hover title giving the KaTeX + * error message. Otherwise, simply throws the error. + */ + + +var renderError = function renderError(error, expression, options) { + if (options.throwOnError || !(error instanceof src_ParseError)) { + throw error; + } + + var node = buildCommon.makeSpan(["katex-error"], [new SymbolNode(expression)]); + node.setAttribute("title", error.toString()); + node.setAttribute("style", "color:" + options.errorColor); + return node; +}; +/** + * Generates and returns the katex build tree. This is used for advanced + * use cases (like rendering to custom output). + */ + + +var renderToDomTree = function renderToDomTree(expression, options) { + var settings = new Settings(options); + + try { + var tree = src_parseTree(expression, settings); + return buildTree(tree, expression, settings); + } catch (error) { + return renderError(error, expression, settings); + } +}; +/** + * Generates and returns the katex build tree, with just HTML (no MathML). + * This is used for advanced use cases (like rendering to custom output). + */ + + +var renderToHTMLTree = function renderToHTMLTree(expression, options) { + var settings = new Settings(options); + + try { + var tree = src_parseTree(expression, settings); + return buildHTMLTree(tree, expression, settings); + } catch (error) { + return renderError(error, expression, settings); + } +}; + +/* harmony default export */ var katex = ({ + /** + * Current KaTeX version + */ + version: "0.13.11", + + /** + * Renders the given LaTeX into an HTML+MathML combination, and adds + * it as a child to the specified DOM node. + */ + render: render, + + /** + * Renders the given LaTeX into an HTML+MathML combination string, + * for sending to the client. + */ + renderToString: renderToString, + + /** + * KaTeX error, usually during parsing. + */ + ParseError: src_ParseError, + + /** + * Parses the given LaTeX into KaTeX's internal parse tree structure, + * without rendering to HTML or MathML. + * + * NOTE: This method is not currently recommended for public use. + * The internal tree representation is unstable and is very likely + * to change. Use at your own risk. + */ + __parse: generateParseTree, + + /** + * Renders the given LaTeX into an HTML+MathML internal DOM tree + * representation, without flattening that representation to a string. + * + * NOTE: This method is not currently recommended for public use. + * The internal tree representation is unstable and is very likely + * to change. Use at your own risk. + */ + __renderToDomTree: renderToDomTree, + + /** + * Renders the given LaTeX into an HTML internal DOM tree representation, + * without MathML and without flattening that representation to a string. + * + * NOTE: This method is not currently recommended for public use. + * The internal tree representation is unstable and is very likely + * to change. Use at your own risk. + */ + __renderToHTMLTree: renderToHTMLTree, + + /** + * extends internal font metrics object with a new object + * each key in the new object represents a font name + */ + __setFontMetrics: setFontMetrics, + + /** + * adds a new symbol to builtin symbols table + */ + __defineSymbol: defineSymbol, + + /** + * adds a new macro to builtin macro list + */ + __defineMacro: defineMacro, + + /** + * Expose the dom tree node types, which can be useful for type checking nodes. + * + * NOTE: This method is not currently recommended for public use. + * The internal tree representation is unstable and is very likely + * to change. Use at your own risk. + */ + __domTree: { + Span: Span, + Anchor: Anchor, + SymbolNode: SymbolNode, + SvgNode: SvgNode, + PathNode: PathNode, + LineNode: LineNode + } +}); +;// CONCATENATED MODULE: ./katex.webpack.js +/** + * This is the webpack entry point for KaTeX. As ECMAScript, flow[1] and jest[2] + * doesn't support CSS modules natively, a separate entry point is used and + * it is not flowtyped. + * + * [1] https://gist.github.com/lambdahands/d19e0da96285b749f0ef + * [2] https://facebook.github.io/jest/docs/en/webpack.html + */ + + +/* harmony default export */ var katex_webpack = (katex); +__webpack_exports__ = __webpack_exports__.default; +/******/ return __webpack_exports__; +/******/ })() +; +}); +const katex = module.exports; +module.exports = undefined; +(function webpackUniversalModuleDefinition(root, factory) { + if(typeof exports === 'object' && typeof module === 'object') + module.exports = factory(katex); + else if(typeof define === 'function' && define.amd) + define(["katex"], factory); + else { + var a = typeof exports === 'object' ? factory(katex) : factory(root["katex"]); + for(var i in a) (typeof exports === 'object' ? exports : root)[i] = a[i]; + } +})((typeof self !== 'undefined' ? self : this), function(__WEBPACK_EXTERNAL_MODULE__974__) { +return /******/ (function() { // webpackBootstrap +/******/ "use strict"; +/******/ var __webpack_modules__ = ({ + +/***/ 974: +/***/ (function(module) { + +module.exports = __WEBPACK_EXTERNAL_MODULE__974__; + +/***/ }) + +/******/ }); +/************************************************************************/ +/******/ // The module cache +/******/ var __webpack_module_cache__ = {}; +/******/ +/******/ // The require function +/******/ function __webpack_require__(moduleId) { +/******/ // Check if module is in cache +/******/ var cachedModule = __webpack_module_cache__[moduleId]; +/******/ if (cachedModule !== undefined) { +/******/ return cachedModule.exports; +/******/ } +/******/ // Create a new module (and put it into the cache) +/******/ var module = __webpack_module_cache__[moduleId] = { +/******/ // no module.id needed +/******/ // no module.loaded needed +/******/ exports: {} +/******/ }; +/******/ +/******/ // Execute the module function +/******/ __webpack_modules__[moduleId](module, module.exports, __webpack_require__); +/******/ +/******/ // Return the exports of the module +/******/ return module.exports; +/******/ } +/******/ +/************************************************************************/ +/******/ /* webpack/runtime/compat get default export */ +/******/ !function() { +/******/ // getDefaultExport function for compatibility with non-harmony modules +/******/ __webpack_require__.n = function(module) { +/******/ var getter = module && module.__esModule ? +/******/ function() { return module['default']; } : +/******/ function() { return module; }; +/******/ __webpack_require__.d(getter, { a: getter }); +/******/ return getter; +/******/ }; +/******/ }(); +/******/ +/******/ /* webpack/runtime/define property getters */ +/******/ !function() { +/******/ // define getter functions for harmony exports +/******/ __webpack_require__.d = function(exports, definition) { +/******/ for(var key in definition) { +/******/ if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) { +/******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] }); +/******/ } +/******/ } +/******/ }; +/******/ }(); +/******/ +/******/ /* webpack/runtime/hasOwnProperty shorthand */ +/******/ !function() { +/******/ __webpack_require__.o = function(obj, prop) { return Object.prototype.hasOwnProperty.call(obj, prop); } +/******/ }(); +/******/ +/************************************************************************/ +var __webpack_exports__ = {}; +// This entry need to be wrapped in an IIFE because it need to be isolated against other modules in the chunk. +!function() { +/* harmony import */ var katex__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(974); +/* harmony import */ var katex__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(katex__WEBPACK_IMPORTED_MODULE_0__); +/* eslint-disable */ + +/* -*- Mode: Javascript; indent-tabs-mode:nil; js-indent-level: 2 -*- */ + +/* vim: set ts=2 et sw=2 tw=80: */ + +/************************************************************* + * + * KaTeX mhchem.js + * + * This file implements a KaTeX version of mhchem version 3.3.0. + * It is adapted from MathJax/extensions/TeX/mhchem.js + * It differs from the MathJax version as follows: + * 1. The interface is changed so that it can be called from KaTeX, not MathJax. + * 2. \rlap and \llap are replaced with \mathrlap and \mathllap. + * 3. Four lines of code are edited in order to use \raisebox instead of \raise. + * 4. The reaction arrow code is simplified. All reaction arrows are rendered + * using KaTeX extensible arrows instead of building non-extensible arrows. + * 5. \tripledash vertical alignment is slightly adjusted. + * + * This code, as other KaTeX code, is released under the MIT license. + * + * /************************************************************* + * + * MathJax/extensions/TeX/mhchem.js + * + * Implements the \ce command for handling chemical formulas + * from the mhchem LaTeX package. + * + * --------------------------------------------------------------------- + * + * Copyright (c) 2011-2015 The MathJax Consortium + * Copyright (c) 2015-2018 Martin Hensel + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +// +// Coding Style +// - use '' for identifiers that can by minified/uglified +// - use "" for strings that need to stay untouched +// version: "3.3.0" for MathJax and KaTeX +// Add \ce, \pu, and \tripledash to the KaTeX macros. +katex__WEBPACK_IMPORTED_MODULE_0___default().__defineMacro("\\ce", function (context) { + return chemParse(context.consumeArgs(1)[0], "ce"); +}); + +katex__WEBPACK_IMPORTED_MODULE_0___default().__defineMacro("\\pu", function (context) { + return chemParse(context.consumeArgs(1)[0], "pu"); +}); // Needed for \bond for the ~ forms +// Raise by 2.56mu, not 2mu. We're raising a hyphen-minus, U+002D, not +// a mathematical minus, U+2212. So we need that extra 0.56. + + +katex__WEBPACK_IMPORTED_MODULE_0___default().__defineMacro("\\tripledash", "{\\vphantom{-}\\raisebox{2.56mu}{$\\mkern2mu" + "\\tiny\\text{-}\\mkern1mu\\text{-}\\mkern1mu\\text{-}\\mkern2mu$}}"); + + // +// This is the main function for handing the \ce and \pu commands. +// It takes the argument to \ce or \pu and returns the corresponding TeX string. +// + +var chemParse = function chemParse(tokens, stateMachine) { + // Recreate the argument string from KaTeX's array of tokens. + var str = ""; + var expectedLoc = tokens[tokens.length - 1].loc.start; + + for (var i = tokens.length - 1; i >= 0; i--) { + if (tokens[i].loc.start > expectedLoc) { + // context.consumeArgs has eaten a space. + str += " "; + expectedLoc = tokens[i].loc.start; + } + + str += tokens[i].text; + expectedLoc += tokens[i].text.length; + } + + var tex = texify.go(mhchemParser.go(str, stateMachine)); + return tex; +}; // +// Core parser for mhchem syntax (recursive) +// + +/** @type {MhchemParser} */ + + +var mhchemParser = { + // + // Parses mchem \ce syntax + // + // Call like + // go("H2O"); + // + go: function go(input, stateMachine) { + if (!input) { + return []; + } + + if (stateMachine === undefined) { + stateMachine = 'ce'; + } + + var state = '0'; // + // String buffers for parsing: + // + // buffer.a == amount + // buffer.o == element + // buffer.b == left-side superscript + // buffer.p == left-side subscript + // buffer.q == right-side subscript + // buffer.d == right-side superscript + // + // buffer.r == arrow + // buffer.rdt == arrow, script above, type + // buffer.rd == arrow, script above, content + // buffer.rqt == arrow, script below, type + // buffer.rq == arrow, script below, content + // + // buffer.text_ + // buffer.rm + // etc. + // + // buffer.parenthesisLevel == int, starting at 0 + // buffer.sb == bool, space before + // buffer.beginsWithBond == bool + // + // These letters are also used as state names. + // + // Other states: + // 0 == begin of main part (arrow/operator unlikely) + // 1 == next entity + // 2 == next entity (arrow/operator unlikely) + // 3 == next atom + // c == macro + // + + /** @type {Buffer} */ + + var buffer = {}; + buffer['parenthesisLevel'] = 0; + input = input.replace(/\n/g, " "); + input = input.replace(/[\u2212\u2013\u2014\u2010]/g, "-"); + input = input.replace(/[\u2026]/g, "..."); // + // Looks through mhchemParser.transitions, to execute a matching action + // (recursive) + // + + var lastInput; + var watchdog = 10; + /** @type {ParserOutput[]} */ + + var output = []; + + while (true) { + if (lastInput !== input) { + watchdog = 10; + lastInput = input; + } else { + watchdog--; + } // + // Find actions in transition table + // + + + var machine = mhchemParser.stateMachines[stateMachine]; + var t = machine.transitions[state] || machine.transitions['*']; + + iterateTransitions: for (var i = 0; i < t.length; i++) { + var matches = mhchemParser.patterns.match_(t[i].pattern, input); + + if (matches) { + // + // Execute actions + // + var task = t[i].task; + + for (var iA = 0; iA < task.action_.length; iA++) { + var o; // + // Find and execute action + // + + if (machine.actions[task.action_[iA].type_]) { + o = machine.actions[task.action_[iA].type_](buffer, matches.match_, task.action_[iA].option); + } else if (mhchemParser.actions[task.action_[iA].type_]) { + o = mhchemParser.actions[task.action_[iA].type_](buffer, matches.match_, task.action_[iA].option); + } else { + throw ["MhchemBugA", "mhchem bug A. Please report. (" + task.action_[iA].type_ + ")"]; // Trying to use non-existing action + } // + // Add output + // + + + mhchemParser.concatArray(output, o); + } // + // Set next state, + // Shorten input, + // Continue with next character + // (= apply only one transition per position) + // + + + state = task.nextState || state; + + if (input.length > 0) { + if (!task.revisit) { + input = matches.remainder; + } + + if (!task.toContinue) { + break iterateTransitions; + } + } else { + return output; + } + } + } // + // Prevent infinite loop + // + + + if (watchdog <= 0) { + throw ["MhchemBugU", "mhchem bug U. Please report."]; // Unexpected character + } + } + }, + concatArray: function concatArray(a, b) { + if (b) { + if (Array.isArray(b)) { + for (var iB = 0; iB < b.length; iB++) { + a.push(b[iB]); + } + } else { + a.push(b); + } + } + }, + patterns: { + // + // Matching patterns + // either regexps or function that return null or {match_:"a", remainder:"bc"} + // + patterns: { + // property names must not look like integers ("2") for correct property traversal order, later on + 'empty': /^$/, + 'else': /^./, + 'else2': /^./, + 'space': /^\s/, + 'space A': /^\s(?=[A-Z\\$])/, + 'space$': /^\s$/, + 'a-z': /^[a-z]/, + 'x': /^x/, + 'x$': /^x$/, + 'i$': /^i$/, + 'letters': /^(?:[a-zA-Z\u03B1-\u03C9\u0391-\u03A9?@]|(?:\\(?:alpha|beta|gamma|delta|epsilon|zeta|eta|theta|iota|kappa|lambda|mu|nu|xi|omicron|pi|rho|sigma|tau|upsilon|phi|chi|psi|omega|Gamma|Delta|Theta|Lambda|Xi|Pi|Sigma|Upsilon|Phi|Psi|Omega)(?:\s+|\{\}|(?![a-zA-Z]))))+/, + '\\greek': /^\\(?:alpha|beta|gamma|delta|epsilon|zeta|eta|theta|iota|kappa|lambda|mu|nu|xi|omicron|pi|rho|sigma|tau|upsilon|phi|chi|psi|omega|Gamma|Delta|Theta|Lambda|Xi|Pi|Sigma|Upsilon|Phi|Psi|Omega)(?:\s+|\{\}|(?![a-zA-Z]))/, + 'one lowercase latin letter $': /^(?:([a-z])(?:$|[^a-zA-Z]))$/, + '$one lowercase latin letter$ $': /^\$(?:([a-z])(?:$|[^a-zA-Z]))\$$/, + 'one lowercase greek letter $': /^(?:\$?[\u03B1-\u03C9]\$?|\$?\\(?:alpha|beta|gamma|delta|epsilon|zeta|eta|theta|iota|kappa|lambda|mu|nu|xi|omicron|pi|rho|sigma|tau|upsilon|phi|chi|psi|omega)\s*\$?)(?:\s+|\{\}|(?![a-zA-Z]))$/, + 'digits': /^[0-9]+/, + '-9.,9': /^[+\-]?(?:[0-9]+(?:[,.][0-9]+)?|[0-9]*(?:\.[0-9]+))/, + '-9.,9 no missing 0': /^[+\-]?[0-9]+(?:[.,][0-9]+)?/, + '(-)(9.,9)(e)(99)': function e99(input) { + var m = input.match(/^(\+\-|\+\/\-|\+|\-|\\pm\s?)?([0-9]+(?:[,.][0-9]+)?|[0-9]*(?:\.[0-9]+))?(\((?:[0-9]+(?:[,.][0-9]+)?|[0-9]*(?:\.[0-9]+))\))?(?:([eE]|\s*(\*|x|\\times|\u00D7)\s*10\^)([+\-]?[0-9]+|\{[+\-]?[0-9]+\}))?/); + + if (m && m[0]) { + return { + match_: m.splice(1), + remainder: input.substr(m[0].length) + }; + } + + return null; + }, + '(-)(9)^(-9)': function _(input) { + var m = input.match(/^(\+\-|\+\/\-|\+|\-|\\pm\s?)?([0-9]+(?:[,.][0-9]+)?|[0-9]*(?:\.[0-9]+)?)\^([+\-]?[0-9]+|\{[+\-]?[0-9]+\})/); + + if (m && m[0]) { + return { + match_: m.splice(1), + remainder: input.substr(m[0].length) + }; + } + + return null; + }, + 'state of aggregation $': function stateOfAggregation$(input) { + // ... or crystal system + var a = mhchemParser.patterns.findObserveGroups(input, "", /^\([a-z]{1,3}(?=[\),])/, ")", ""); // (aq), (aq,$\infty$), (aq, sat) + + if (a && a.remainder.match(/^($|[\s,;\)\]\}])/)) { + return a; + } // AND end of 'phrase' + + + var m = input.match(/^(?:\((?:\\ca\s?)?\$[amothc]\$\))/); // OR crystal system ($o$) (\ca$c$) + + if (m) { + return { + match_: m[0], + remainder: input.substr(m[0].length) + }; + } + + return null; + }, + '_{(state of aggregation)}$': /^_\{(\([a-z]{1,3}\))\}/, + '{[(': /^(?:\\\{|\[|\()/, + ')]}': /^(?:\)|\]|\\\})/, + ', ': /^[,;]\s*/, + ',': /^[,;]/, + '.': /^[.]/, + '. ': /^([.\u22C5\u00B7\u2022])\s*/, + '...': /^\.\.\.(?=$|[^.])/, + '* ': /^([*])\s*/, + '^{(...)}': function _(input) { + return mhchemParser.patterns.findObserveGroups(input, "^{", "", "", "}"); + }, + '^($...$)': function $$(input) { + return mhchemParser.patterns.findObserveGroups(input, "^", "$", "$", ""); + }, + '^a': /^\^([0-9]+|[^\\_])/, + '^\\x{}{}': function x(input) { + return mhchemParser.patterns.findObserveGroups(input, "^", /^\\[a-zA-Z]+\{/, "}", "", "", "{", "}", "", true); + }, + '^\\x{}': function x(input) { + return mhchemParser.patterns.findObserveGroups(input, "^", /^\\[a-zA-Z]+\{/, "}", ""); + }, + '^\\x': /^\^(\\[a-zA-Z]+)\s*/, + '^(-1)': /^\^(-?\d+)/, + '\'': /^'/, + '_{(...)}': function _(input) { + return mhchemParser.patterns.findObserveGroups(input, "_{", "", "", "}"); + }, + '_($...$)': function _$$(input) { + return mhchemParser.patterns.findObserveGroups(input, "_", "$", "$", ""); + }, + '_9': /^_([+\-]?[0-9]+|[^\\])/, + '_\\x{}{}': function _X(input) { + return mhchemParser.patterns.findObserveGroups(input, "_", /^\\[a-zA-Z]+\{/, "}", "", "", "{", "}", "", true); + }, + '_\\x{}': function _X(input) { + return mhchemParser.patterns.findObserveGroups(input, "_", /^\\[a-zA-Z]+\{/, "}", ""); + }, + '_\\x': /^_(\\[a-zA-Z]+)\s*/, + '^_': /^(?:\^(?=_)|\_(?=\^)|[\^_]$)/, + '{}': /^\{\}/, + '{...}': function _(input) { + return mhchemParser.patterns.findObserveGroups(input, "", "{", "}", ""); + }, + '{(...)}': function _(input) { + return mhchemParser.patterns.findObserveGroups(input, "{", "", "", "}"); + }, + '$...$': function $$(input) { + return mhchemParser.patterns.findObserveGroups(input, "", "$", "$", ""); + }, + '${(...)}$': function $$(input) { + return mhchemParser.patterns.findObserveGroups(input, "${", "", "", "}$"); + }, + '$(...)$': function $$(input) { + return mhchemParser.patterns.findObserveGroups(input, "$", "", "", "$"); + }, + '=<>': /^[=<>]/, + '#': /^[#\u2261]/, + '+': /^\+/, + '-$': /^-(?=[\s_},;\]/]|$|\([a-z]+\))/, + // -space -, -; -] -/ -$ -state-of-aggregation + '-9': /^-(?=[0-9])/, + '- orbital overlap': /^-(?=(?:[spd]|sp)(?:$|[\s,;\)\]\}]))/, + '-': /^-/, + 'pm-operator': /^(?:\\pm|\$\\pm\$|\+-|\+\/-)/, + 'operator': /^(?:\+|(?:[\-=<>]|<<|>>|\\approx|\$\\approx\$)(?=\s|$|-?[0-9]))/, + 'arrowUpDown': /^(?:v|\(v\)|\^|\(\^\))(?=$|[\s,;\)\]\}])/, + '\\bond{(...)}': function bond(input) { + return mhchemParser.patterns.findObserveGroups(input, "\\bond{", "", "", "}"); + }, + '->': /^(?:<->|<-->|->|<-|<=>>|<<=>|<=>|[\u2192\u27F6\u21CC])/, + 'CMT': /^[CMT](?=\[)/, + '[(...)]': function _(input) { + return mhchemParser.patterns.findObserveGroups(input, "[", "", "", "]"); + }, + '1st-level escape': /^(&|\\\\|\\hline)\s*/, + '\\,': /^(?:\\[,\ ;:])/, + // \\x - but output no space before + '\\x{}{}': function x(input) { + return mhchemParser.patterns.findObserveGroups(input, "", /^\\[a-zA-Z]+\{/, "}", "", "", "{", "}", "", true); + }, + '\\x{}': function x(input) { + return mhchemParser.patterns.findObserveGroups(input, "", /^\\[a-zA-Z]+\{/, "}", ""); + }, + '\\ca': /^\\ca(?:\s+|(?![a-zA-Z]))/, + '\\x': /^(?:\\[a-zA-Z]+\s*|\\[_&{}%])/, + 'orbital': /^(?:[0-9]{1,2}[spdfgh]|[0-9]{0,2}sp)(?=$|[^a-zA-Z])/, + // only those with numbers in front, because the others will be formatted correctly anyway + 'others': /^[\/~|]/, + '\\frac{(...)}': function frac(input) { + return mhchemParser.patterns.findObserveGroups(input, "\\frac{", "", "", "}", "{", "", "", "}"); + }, + '\\overset{(...)}': function overset(input) { + return mhchemParser.patterns.findObserveGroups(input, "\\overset{", "", "", "}", "{", "", "", "}"); + }, + "\\underset{(...)}": function underset(input) { + return mhchemParser.patterns.findObserveGroups(input, "\\underset{", "", "", "}", "{", "", "", "}"); + }, + "\\underbrace{(...)}": function underbrace(input) { + return mhchemParser.patterns.findObserveGroups(input, "\\underbrace{", "", "", "}_", "{", "", "", "}"); + }, + '\\color{(...)}0': function color0(input) { + return mhchemParser.patterns.findObserveGroups(input, "\\color{", "", "", "}"); + }, + '\\color{(...)}{(...)}1': function color1(input) { + return mhchemParser.patterns.findObserveGroups(input, "\\color{", "", "", "}", "{", "", "", "}"); + }, + '\\color(...){(...)}2': function color2(input) { + return mhchemParser.patterns.findObserveGroups(input, "\\color", "\\", "", /^(?=\{)/, "{", "", "", "}"); + }, + '\\ce{(...)}': function ce(input) { + return mhchemParser.patterns.findObserveGroups(input, "\\ce{", "", "", "}"); + }, + 'oxidation$': /^(?:[+-][IVX]+|\\pm\s*0|\$\\pm\$\s*0)$/, + 'd-oxidation$': /^(?:[+-]?\s?[IVX]+|\\pm\s*0|\$\\pm\$\s*0)$/, + // 0 could be oxidation or charge + 'roman numeral': /^[IVX]+/, + '1/2$': /^[+\-]?(?:[0-9]+|\$[a-z]\$|[a-z])\/[0-9]+(?:\$[a-z]\$|[a-z])?$/, + 'amount': function amount(input) { + var match; // e.g. 2, 0.5, 1/2, -2, n/2, +; $a$ could be added later in parsing + + match = input.match(/^(?:(?:(?:\([+\-]?[0-9]+\/[0-9]+\)|[+\-]?(?:[0-9]+|\$[a-z]\$|[a-z])\/[0-9]+|[+\-]?[0-9]+[.,][0-9]+|[+\-]?\.[0-9]+|[+\-]?[0-9]+)(?:[a-z](?=\s*[A-Z]))?)|[+\-]?[a-z](?=\s*[A-Z])|\+(?!\s))/); + + if (match) { + return { + match_: match[0], + remainder: input.substr(match[0].length) + }; + } + + var a = mhchemParser.patterns.findObserveGroups(input, "", "$", "$", ""); + + if (a) { + // e.g. $2n-1$, $-$ + match = a.match_.match(/^\$(?:\(?[+\-]?(?:[0-9]*[a-z]?[+\-])?[0-9]*[a-z](?:[+\-][0-9]*[a-z]?)?\)?|\+|-)\$$/); + + if (match) { + return { + match_: match[0], + remainder: input.substr(match[0].length) + }; + } + } + + return null; + }, + 'amount2': function amount2(input) { + return this['amount'](input); + }, + '(KV letters),': /^(?:[A-Z][a-z]{0,2}|i)(?=,)/, + 'formula$': function formula$(input) { + if (input.match(/^\([a-z]+\)$/)) { + return null; + } // state of aggregation = no formula + + + var match = input.match(/^(?:[a-z]|(?:[0-9\ \+\-\,\.\(\)]+[a-z])+[0-9\ \+\-\,\.\(\)]*|(?:[a-z][0-9\ \+\-\,\.\(\)]+)+[a-z]?)$/); + + if (match) { + return { + match_: match[0], + remainder: input.substr(match[0].length) + }; + } + + return null; + }, + 'uprightEntities': /^(?:pH|pOH|pC|pK|iPr|iBu)(?=$|[^a-zA-Z])/, + '/': /^\s*(\/)\s*/, + '//': /^\s*(\/\/)\s*/, + '*': /^\s*[*.]\s*/ + }, + findObserveGroups: function findObserveGroups(input, begExcl, begIncl, endIncl, endExcl, beg2Excl, beg2Incl, end2Incl, end2Excl, combine) { + /** @type {{(input: string, pattern: string | RegExp): string | string[] | null;}} */ + var _match = function _match(input, pattern) { + if (typeof pattern === "string") { + if (input.indexOf(pattern) !== 0) { + return null; + } + + return pattern; + } else { + var match = input.match(pattern); + + if (!match) { + return null; + } + + return match[0]; + } + }; + /** @type {{(input: string, i: number, endChars: string | RegExp): {endMatchBegin: number, endMatchEnd: number} | null;}} */ + + + var _findObserveGroups = function _findObserveGroups(input, i, endChars) { + var braces = 0; + + while (i < input.length) { + var a = input.charAt(i); + + var match = _match(input.substr(i), endChars); + + if (match !== null && braces === 0) { + return { + endMatchBegin: i, + endMatchEnd: i + match.length + }; + } else if (a === "{") { + braces++; + } else if (a === "}") { + if (braces === 0) { + throw ["ExtraCloseMissingOpen", "Extra close brace or missing open brace"]; + } else { + braces--; + } + } + + i++; + } + + if (braces > 0) { + return null; + } + + return null; + }; + + var match = _match(input, begExcl); + + if (match === null) { + return null; + } + + input = input.substr(match.length); + match = _match(input, begIncl); + + if (match === null) { + return null; + } + + var e = _findObserveGroups(input, match.length, endIncl || endExcl); + + if (e === null) { + return null; + } + + var match1 = input.substring(0, endIncl ? e.endMatchEnd : e.endMatchBegin); + + if (!(beg2Excl || beg2Incl)) { + return { + match_: match1, + remainder: input.substr(e.endMatchEnd) + }; + } else { + var group2 = this.findObserveGroups(input.substr(e.endMatchEnd), beg2Excl, beg2Incl, end2Incl, end2Excl); + + if (group2 === null) { + return null; + } + /** @type {string[]} */ + + + var matchRet = [match1, group2.match_]; + return { + match_: combine ? matchRet.join("") : matchRet, + remainder: group2.remainder + }; + } + }, + // + // Matching function + // e.g. match("a", input) will look for the regexp called "a" and see if it matches + // returns null or {match_:"a", remainder:"bc"} + // + match_: function match_(m, input) { + var pattern = mhchemParser.patterns.patterns[m]; + + if (pattern === undefined) { + throw ["MhchemBugP", "mhchem bug P. Please report. (" + m + ")"]; // Trying to use non-existing pattern + } else if (typeof pattern === "function") { + return mhchemParser.patterns.patterns[m](input); // cannot use cached var pattern here, because some pattern functions need this===mhchemParser + } else { + // RegExp + var match = input.match(pattern); + + if (match) { + var mm; + + if (match[2]) { + mm = [match[1], match[2]]; + } else if (match[1]) { + mm = match[1]; + } else { + mm = match[0]; + } + + return { + match_: mm, + remainder: input.substr(match[0].length) + }; + } + + return null; + } + } + }, + // + // Generic state machine actions + // + actions: { + 'a=': function a(buffer, m) { + buffer.a = (buffer.a || "") + m; + }, + 'b=': function b(buffer, m) { + buffer.b = (buffer.b || "") + m; + }, + 'p=': function p(buffer, m) { + buffer.p = (buffer.p || "") + m; + }, + 'o=': function o(buffer, m) { + buffer.o = (buffer.o || "") + m; + }, + 'q=': function q(buffer, m) { + buffer.q = (buffer.q || "") + m; + }, + 'd=': function d(buffer, m) { + buffer.d = (buffer.d || "") + m; + }, + 'rm=': function rm(buffer, m) { + buffer.rm = (buffer.rm || "") + m; + }, + 'text=': function text(buffer, m) { + buffer.text_ = (buffer.text_ || "") + m; + }, + 'insert': function insert(buffer, m, a) { + return { + type_: a + }; + }, + 'insert+p1': function insertP1(buffer, m, a) { + return { + type_: a, + p1: m + }; + }, + 'insert+p1+p2': function insertP1P2(buffer, m, a) { + return { + type_: a, + p1: m[0], + p2: m[1] + }; + }, + 'copy': function copy(buffer, m) { + return m; + }, + 'rm': function rm(buffer, m) { + return { + type_: 'rm', + p1: m || "" + }; + }, + 'text': function text(buffer, m) { + return mhchemParser.go(m, 'text'); + }, + '{text}': function text(buffer, m) { + var ret = ["{"]; + mhchemParser.concatArray(ret, mhchemParser.go(m, 'text')); + ret.push("}"); + return ret; + }, + 'tex-math': function texMath(buffer, m) { + return mhchemParser.go(m, 'tex-math'); + }, + 'tex-math tight': function texMathTight(buffer, m) { + return mhchemParser.go(m, 'tex-math tight'); + }, + 'bond': function bond(buffer, m, k) { + return { + type_: 'bond', + kind_: k || m + }; + }, + 'color0-output': function color0Output(buffer, m) { + return { + type_: 'color0', + color: m[0] + }; + }, + 'ce': function ce(buffer, m) { + return mhchemParser.go(m); + }, + '1/2': function _(buffer, m) { + /** @type {ParserOutput[]} */ + var ret = []; + + if (m.match(/^[+\-]/)) { + ret.push(m.substr(0, 1)); + m = m.substr(1); + } + + var n = m.match(/^([0-9]+|\$[a-z]\$|[a-z])\/([0-9]+)(\$[a-z]\$|[a-z])?$/); + n[1] = n[1].replace(/\$/g, ""); + ret.push({ + type_: 'frac', + p1: n[1], + p2: n[2] + }); + + if (n[3]) { + n[3] = n[3].replace(/\$/g, ""); + ret.push({ + type_: 'tex-math', + p1: n[3] + }); + } + + return ret; + }, + '9,9': function _(buffer, m) { + return mhchemParser.go(m, '9,9'); + } + }, + // + // createTransitions + // convert { 'letter': { 'state': { action_: 'output' } } } to { 'state' => [ { pattern: 'letter', task: { action_: [{type_: 'output'}] } } ] } + // with expansion of 'a|b' to 'a' and 'b' (at 2 places) + // + createTransitions: function createTransitions(o) { + var pattern, state; + /** @type {string[]} */ + + var stateArray; + var i; // + // 1. Collect all states + // + + /** @type {Transitions} */ + + var transitions = {}; + + for (pattern in o) { + for (state in o[pattern]) { + stateArray = state.split("|"); + o[pattern][state].stateArray = stateArray; + + for (i = 0; i < stateArray.length; i++) { + transitions[stateArray[i]] = []; + } + } + } // + // 2. Fill states + // + + + for (pattern in o) { + for (state in o[pattern]) { + stateArray = o[pattern][state].stateArray || []; + + for (i = 0; i < stateArray.length; i++) { + // + // 2a. Normalize actions into array: 'text=' ==> [{type_:'text='}] + // (Note to myself: Resolving the function here would be problematic. It would need .bind (for *this*) and currying (for *option*).) + // + + /** @type {any} */ + var p = o[pattern][state]; + + if (p.action_) { + p.action_ = [].concat(p.action_); + + for (var k = 0; k < p.action_.length; k++) { + if (typeof p.action_[k] === "string") { + p.action_[k] = { + type_: p.action_[k] + }; + } + } + } else { + p.action_ = []; + } // + // 2.b Multi-insert + // + + + var patternArray = pattern.split("|"); + + for (var j = 0; j < patternArray.length; j++) { + if (stateArray[i] === '*') { + // insert into all + for (var t in transitions) { + transitions[t].push({ + pattern: patternArray[j], + task: p + }); + } + } else { + transitions[stateArray[i]].push({ + pattern: patternArray[j], + task: p + }); + } + } + } + } + } + + return transitions; + }, + stateMachines: {} +}; // +// Definition of state machines +// + +mhchemParser.stateMachines = { + // + // \ce state machines + // + //#region ce + 'ce': { + // main parser + transitions: mhchemParser.createTransitions({ + 'empty': { + '*': { + action_: 'output' + } + }, + 'else': { + '0|1|2': { + action_: 'beginsWithBond=false', + revisit: true, + toContinue: true + } + }, + 'oxidation$': { + '0': { + action_: 'oxidation-output' + } + }, + 'CMT': { + 'r': { + action_: 'rdt=', + nextState: 'rt' + }, + 'rd': { + action_: 'rqt=', + nextState: 'rdt' + } + }, + 'arrowUpDown': { + '0|1|2|as': { + action_: ['sb=false', 'output', 'operator'], + nextState: '1' + } + }, + 'uprightEntities': { + '0|1|2': { + action_: ['o=', 'output'], + nextState: '1' + } + }, + 'orbital': { + '0|1|2|3': { + action_: 'o=', + nextState: 'o' + } + }, + '->': { + '0|1|2|3': { + action_: 'r=', + nextState: 'r' + }, + 'a|as': { + action_: ['output', 'r='], + nextState: 'r' + }, + '*': { + action_: ['output', 'r='], + nextState: 'r' + } + }, + '+': { + 'o': { + action_: 'd= kv', + nextState: 'd' + }, + 'd|D': { + action_: 'd=', + nextState: 'd' + }, + 'q': { + action_: 'd=', + nextState: 'qd' + }, + 'qd|qD': { + action_: 'd=', + nextState: 'qd' + }, + 'dq': { + action_: ['output', 'd='], + nextState: 'd' + }, + '3': { + action_: ['sb=false', 'output', 'operator'], + nextState: '0' + } + }, + 'amount': { + '0|2': { + action_: 'a=', + nextState: 'a' + } + }, + 'pm-operator': { + '0|1|2|a|as': { + action_: ['sb=false', 'output', { + type_: 'operator', + option: '\\pm' + }], + nextState: '0' + } + }, + 'operator': { + '0|1|2|a|as': { + action_: ['sb=false', 'output', 'operator'], + nextState: '0' + } + }, + '-$': { + 'o|q': { + action_: ['charge or bond', 'output'], + nextState: 'qd' + }, + 'd': { + action_: 'd=', + nextState: 'd' + }, + 'D': { + action_: ['output', { + type_: 'bond', + option: "-" + }], + nextState: '3' + }, + 'q': { + action_: 'd=', + nextState: 'qd' + }, + 'qd': { + action_: 'd=', + nextState: 'qd' + }, + 'qD|dq': { + action_: ['output', { + type_: 'bond', + option: "-" + }], + nextState: '3' + } + }, + '-9': { + '3|o': { + action_: ['output', { + type_: 'insert', + option: 'hyphen' + }], + nextState: '3' + } + }, + '- orbital overlap': { + 'o': { + action_: ['output', { + type_: 'insert', + option: 'hyphen' + }], + nextState: '2' + }, + 'd': { + action_: ['output', { + type_: 'insert', + option: 'hyphen' + }], + nextState: '2' + } + }, + '-': { + '0|1|2': { + action_: [{ + type_: 'output', + option: 1 + }, 'beginsWithBond=true', { + type_: 'bond', + option: "-" + }], + nextState: '3' + }, + '3': { + action_: { + type_: 'bond', + option: "-" + } + }, + 'a': { + action_: ['output', { + type_: 'insert', + option: 'hyphen' + }], + nextState: '2' + }, + 'as': { + action_: [{ + type_: 'output', + option: 2 + }, { + type_: 'bond', + option: "-" + }], + nextState: '3' + }, + 'b': { + action_: 'b=' + }, + 'o': { + action_: { + type_: '- after o/d', + option: false + }, + nextState: '2' + }, + 'q': { + action_: { + type_: '- after o/d', + option: false + }, + nextState: '2' + }, + 'd|qd|dq': { + action_: { + type_: '- after o/d', + option: true + }, + nextState: '2' + }, + 'D|qD|p': { + action_: ['output', { + type_: 'bond', + option: "-" + }], + nextState: '3' + } + }, + 'amount2': { + '1|3': { + action_: 'a=', + nextState: 'a' + } + }, + 'letters': { + '0|1|2|3|a|as|b|p|bp|o': { + action_: 'o=', + nextState: 'o' + }, + 'q|dq': { + action_: ['output', 'o='], + nextState: 'o' + }, + 'd|D|qd|qD': { + action_: 'o after d', + nextState: 'o' + } + }, + 'digits': { + 'o': { + action_: 'q=', + nextState: 'q' + }, + 'd|D': { + action_: 'q=', + nextState: 'dq' + }, + 'q': { + action_: ['output', 'o='], + nextState: 'o' + }, + 'a': { + action_: 'o=', + nextState: 'o' + } + }, + 'space A': { + 'b|p|bp': {} + }, + 'space': { + 'a': { + nextState: 'as' + }, + '0': { + action_: 'sb=false' + }, + '1|2': { + action_: 'sb=true' + }, + 'r|rt|rd|rdt|rdq': { + action_: 'output', + nextState: '0' + }, + '*': { + action_: ['output', 'sb=true'], + nextState: '1' + } + }, + '1st-level escape': { + '1|2': { + action_: ['output', { + type_: 'insert+p1', + option: '1st-level escape' + }] + }, + '*': { + action_: ['output', { + type_: 'insert+p1', + option: '1st-level escape' + }], + nextState: '0' + } + }, + '[(...)]': { + 'r|rt': { + action_: 'rd=', + nextState: 'rd' + }, + 'rd|rdt': { + action_: 'rq=', + nextState: 'rdq' + } + }, + '...': { + 'o|d|D|dq|qd|qD': { + action_: ['output', { + type_: 'bond', + option: "..." + }], + nextState: '3' + }, + '*': { + action_: [{ + type_: 'output', + option: 1 + }, { + type_: 'insert', + option: 'ellipsis' + }], + nextState: '1' + } + }, + '. |* ': { + '*': { + action_: ['output', { + type_: 'insert', + option: 'addition compound' + }], + nextState: '1' + } + }, + 'state of aggregation $': { + '*': { + action_: ['output', 'state of aggregation'], + nextState: '1' + } + }, + '{[(': { + 'a|as|o': { + action_: ['o=', 'output', 'parenthesisLevel++'], + nextState: '2' + }, + '0|1|2|3': { + action_: ['o=', 'output', 'parenthesisLevel++'], + nextState: '2' + }, + '*': { + action_: ['output', 'o=', 'output', 'parenthesisLevel++'], + nextState: '2' + } + }, + ')]}': { + '0|1|2|3|b|p|bp|o': { + action_: ['o=', 'parenthesisLevel--'], + nextState: 'o' + }, + 'a|as|d|D|q|qd|qD|dq': { + action_: ['output', 'o=', 'parenthesisLevel--'], + nextState: 'o' + } + }, + ', ': { + '*': { + action_: ['output', 'comma'], + nextState: '0' + } + }, + '^_': { + // ^ and _ without a sensible argument + '*': {} + }, + '^{(...)}|^($...$)': { + '0|1|2|as': { + action_: 'b=', + nextState: 'b' + }, + 'p': { + action_: 'b=', + nextState: 'bp' + }, + '3|o': { + action_: 'd= kv', + nextState: 'D' + }, + 'q': { + action_: 'd=', + nextState: 'qD' + }, + 'd|D|qd|qD|dq': { + action_: ['output', 'd='], + nextState: 'D' + } + }, + '^a|^\\x{}{}|^\\x{}|^\\x|\'': { + '0|1|2|as': { + action_: 'b=', + nextState: 'b' + }, + 'p': { + action_: 'b=', + nextState: 'bp' + }, + '3|o': { + action_: 'd= kv', + nextState: 'd' + }, + 'q': { + action_: 'd=', + nextState: 'qd' + }, + 'd|qd|D|qD': { + action_: 'd=' + }, + 'dq': { + action_: ['output', 'd='], + nextState: 'd' + } + }, + '_{(state of aggregation)}$': { + 'd|D|q|qd|qD|dq': { + action_: ['output', 'q='], + nextState: 'q' + } + }, + '_{(...)}|_($...$)|_9|_\\x{}{}|_\\x{}|_\\x': { + '0|1|2|as': { + action_: 'p=', + nextState: 'p' + }, + 'b': { + action_: 'p=', + nextState: 'bp' + }, + '3|o': { + action_: 'q=', + nextState: 'q' + }, + 'd|D': { + action_: 'q=', + nextState: 'dq' + }, + 'q|qd|qD|dq': { + action_: ['output', 'q='], + nextState: 'q' + } + }, + '=<>': { + '0|1|2|3|a|as|o|q|d|D|qd|qD|dq': { + action_: [{ + type_: 'output', + option: 2 + }, 'bond'], + nextState: '3' + } + }, + '#': { + '0|1|2|3|a|as|o': { + action_: [{ + type_: 'output', + option: 2 + }, { + type_: 'bond', + option: "#" + }], + nextState: '3' + } + }, + '{}': { + '*': { + action_: { + type_: 'output', + option: 1 + }, + nextState: '1' + } + }, + '{...}': { + '0|1|2|3|a|as|b|p|bp': { + action_: 'o=', + nextState: 'o' + }, + 'o|d|D|q|qd|qD|dq': { + action_: ['output', 'o='], + nextState: 'o' + } + }, + '$...$': { + 'a': { + action_: 'a=' + }, + // 2$n$ + '0|1|2|3|as|b|p|bp|o': { + action_: 'o=', + nextState: 'o' + }, + // not 'amount' + 'as|o': { + action_: 'o=' + }, + 'q|d|D|qd|qD|dq': { + action_: ['output', 'o='], + nextState: 'o' + } + }, + '\\bond{(...)}': { + '*': { + action_: [{ + type_: 'output', + option: 2 + }, 'bond'], + nextState: "3" + } + }, + '\\frac{(...)}': { + '*': { + action_: [{ + type_: 'output', + option: 1 + }, 'frac-output'], + nextState: '3' + } + }, + '\\overset{(...)}': { + '*': { + action_: [{ + type_: 'output', + option: 2 + }, 'overset-output'], + nextState: '3' + } + }, + "\\underset{(...)}": { + '*': { + action_: [{ + type_: 'output', + option: 2 + }, 'underset-output'], + nextState: '3' + } + }, + "\\underbrace{(...)}": { + '*': { + action_: [{ + type_: 'output', + option: 2 + }, 'underbrace-output'], + nextState: '3' + } + }, + '\\color{(...)}{(...)}1|\\color(...){(...)}2': { + '*': { + action_: [{ + type_: 'output', + option: 2 + }, 'color-output'], + nextState: '3' + } + }, + '\\color{(...)}0': { + '*': { + action_: [{ + type_: 'output', + option: 2 + }, 'color0-output'] + } + }, + '\\ce{(...)}': { + '*': { + action_: [{ + type_: 'output', + option: 2 + }, 'ce'], + nextState: '3' + } + }, + '\\,': { + '*': { + action_: [{ + type_: 'output', + option: 1 + }, 'copy'], + nextState: '1' + } + }, + '\\x{}{}|\\x{}|\\x': { + '0|1|2|3|a|as|b|p|bp|o|c0': { + action_: ['o=', 'output'], + nextState: '3' + }, + '*': { + action_: ['output', 'o=', 'output'], + nextState: '3' + } + }, + 'others': { + '*': { + action_: [{ + type_: 'output', + option: 1 + }, 'copy'], + nextState: '3' + } + }, + 'else2': { + 'a': { + action_: 'a to o', + nextState: 'o', + revisit: true + }, + 'as': { + action_: ['output', 'sb=true'], + nextState: '1', + revisit: true + }, + 'r|rt|rd|rdt|rdq': { + action_: ['output'], + nextState: '0', + revisit: true + }, + '*': { + action_: ['output', 'copy'], + nextState: '3' + } + } + }), + actions: { + 'o after d': function oAfterD(buffer, m) { + var ret; + + if ((buffer.d || "").match(/^[0-9]+$/)) { + var tmp = buffer.d; + buffer.d = undefined; + ret = this['output'](buffer); + buffer.b = tmp; + } else { + ret = this['output'](buffer); + } + + mhchemParser.actions['o='](buffer, m); + return ret; + }, + 'd= kv': function dKv(buffer, m) { + buffer.d = m; + buffer.dType = 'kv'; + }, + 'charge or bond': function chargeOrBond(buffer, m) { + if (buffer['beginsWithBond']) { + /** @type {ParserOutput[]} */ + var ret = []; + mhchemParser.concatArray(ret, this['output'](buffer)); + mhchemParser.concatArray(ret, mhchemParser.actions['bond'](buffer, m, "-")); + return ret; + } else { + buffer.d = m; + } + }, + '- after o/d': function afterOD(buffer, m, isAfterD) { + var c1 = mhchemParser.patterns.match_('orbital', buffer.o || ""); + var c2 = mhchemParser.patterns.match_('one lowercase greek letter $', buffer.o || ""); + var c3 = mhchemParser.patterns.match_('one lowercase latin letter $', buffer.o || ""); + var c4 = mhchemParser.patterns.match_('$one lowercase latin letter$ $', buffer.o || ""); + var hyphenFollows = m === "-" && (c1 && c1.remainder === "" || c2 || c3 || c4); + + if (hyphenFollows && !buffer.a && !buffer.b && !buffer.p && !buffer.d && !buffer.q && !c1 && c3) { + buffer.o = '$' + buffer.o + '$'; + } + /** @type {ParserOutput[]} */ + + + var ret = []; + + if (hyphenFollows) { + mhchemParser.concatArray(ret, this['output'](buffer)); + ret.push({ + type_: 'hyphen' + }); + } else { + c1 = mhchemParser.patterns.match_('digits', buffer.d || ""); + + if (isAfterD && c1 && c1.remainder === '') { + mhchemParser.concatArray(ret, mhchemParser.actions['d='](buffer, m)); + mhchemParser.concatArray(ret, this['output'](buffer)); + } else { + mhchemParser.concatArray(ret, this['output'](buffer)); + mhchemParser.concatArray(ret, mhchemParser.actions['bond'](buffer, m, "-")); + } + } + + return ret; + }, + 'a to o': function aToO(buffer) { + buffer.o = buffer.a; + buffer.a = undefined; + }, + 'sb=true': function sbTrue(buffer) { + buffer.sb = true; + }, + 'sb=false': function sbFalse(buffer) { + buffer.sb = false; + }, + 'beginsWithBond=true': function beginsWithBondTrue(buffer) { + buffer['beginsWithBond'] = true; + }, + 'beginsWithBond=false': function beginsWithBondFalse(buffer) { + buffer['beginsWithBond'] = false; + }, + 'parenthesisLevel++': function parenthesisLevel(buffer) { + buffer['parenthesisLevel']++; + }, + 'parenthesisLevel--': function parenthesisLevel(buffer) { + buffer['parenthesisLevel']--; + }, + 'state of aggregation': function stateOfAggregation(buffer, m) { + return { + type_: 'state of aggregation', + p1: mhchemParser.go(m, 'o') + }; + }, + 'comma': function comma(buffer, m) { + var a = m.replace(/\s*$/, ''); + var withSpace = a !== m; + + if (withSpace && buffer['parenthesisLevel'] === 0) { + return { + type_: 'comma enumeration L', + p1: a + }; + } else { + return { + type_: 'comma enumeration M', + p1: a + }; + } + }, + 'output': function output(buffer, m, entityFollows) { + // entityFollows: + // undefined = if we have nothing else to output, also ignore the just read space (buffer.sb) + // 1 = an entity follows, never omit the space if there was one just read before (can only apply to state 1) + // 2 = 1 + the entity can have an amount, so output a\, instead of converting it to o (can only apply to states a|as) + + /** @type {ParserOutput | ParserOutput[]} */ + var ret; + + if (!buffer.r) { + ret = []; + + if (!buffer.a && !buffer.b && !buffer.p && !buffer.o && !buffer.q && !buffer.d && !entityFollows) {//ret = []; + } else { + if (buffer.sb) { + ret.push({ + type_: 'entitySkip' + }); + } + + if (!buffer.o && !buffer.q && !buffer.d && !buffer.b && !buffer.p && entityFollows !== 2) { + buffer.o = buffer.a; + buffer.a = undefined; + } else if (!buffer.o && !buffer.q && !buffer.d && (buffer.b || buffer.p)) { + buffer.o = buffer.a; + buffer.d = buffer.b; + buffer.q = buffer.p; + buffer.a = buffer.b = buffer.p = undefined; + } else { + if (buffer.o && buffer.dType === 'kv' && mhchemParser.patterns.match_('d-oxidation$', buffer.d || "")) { + buffer.dType = 'oxidation'; + } else if (buffer.o && buffer.dType === 'kv' && !buffer.q) { + buffer.dType = undefined; + } + } + + ret.push({ + type_: 'chemfive', + a: mhchemParser.go(buffer.a, 'a'), + b: mhchemParser.go(buffer.b, 'bd'), + p: mhchemParser.go(buffer.p, 'pq'), + o: mhchemParser.go(buffer.o, 'o'), + q: mhchemParser.go(buffer.q, 'pq'), + d: mhchemParser.go(buffer.d, buffer.dType === 'oxidation' ? 'oxidation' : 'bd'), + dType: buffer.dType + }); + } + } else { + // r + + /** @type {ParserOutput[]} */ + var rd; + + if (buffer.rdt === 'M') { + rd = mhchemParser.go(buffer.rd, 'tex-math'); + } else if (buffer.rdt === 'T') { + rd = [{ + type_: 'text', + p1: buffer.rd || "" + }]; + } else { + rd = mhchemParser.go(buffer.rd); + } + /** @type {ParserOutput[]} */ + + + var rq; + + if (buffer.rqt === 'M') { + rq = mhchemParser.go(buffer.rq, 'tex-math'); + } else if (buffer.rqt === 'T') { + rq = [{ + type_: 'text', + p1: buffer.rq || "" + }]; + } else { + rq = mhchemParser.go(buffer.rq); + } + + ret = { + type_: 'arrow', + r: buffer.r, + rd: rd, + rq: rq + }; + } + + for (var p in buffer) { + if (p !== 'parenthesisLevel' && p !== 'beginsWithBond') { + delete buffer[p]; + } + } + + return ret; + }, + 'oxidation-output': function oxidationOutput(buffer, m) { + var ret = ["{"]; + mhchemParser.concatArray(ret, mhchemParser.go(m, 'oxidation')); + ret.push("}"); + return ret; + }, + 'frac-output': function fracOutput(buffer, m) { + return { + type_: 'frac-ce', + p1: mhchemParser.go(m[0]), + p2: mhchemParser.go(m[1]) + }; + }, + 'overset-output': function oversetOutput(buffer, m) { + return { + type_: 'overset', + p1: mhchemParser.go(m[0]), + p2: mhchemParser.go(m[1]) + }; + }, + 'underset-output': function undersetOutput(buffer, m) { + return { + type_: 'underset', + p1: mhchemParser.go(m[0]), + p2: mhchemParser.go(m[1]) + }; + }, + 'underbrace-output': function underbraceOutput(buffer, m) { + return { + type_: 'underbrace', + p1: mhchemParser.go(m[0]), + p2: mhchemParser.go(m[1]) + }; + }, + 'color-output': function colorOutput(buffer, m) { + return { + type_: 'color', + color1: m[0], + color2: mhchemParser.go(m[1]) + }; + }, + 'r=': function r(buffer, m) { + buffer.r = m; + }, + 'rdt=': function rdt(buffer, m) { + buffer.rdt = m; + }, + 'rd=': function rd(buffer, m) { + buffer.rd = m; + }, + 'rqt=': function rqt(buffer, m) { + buffer.rqt = m; + }, + 'rq=': function rq(buffer, m) { + buffer.rq = m; + }, + 'operator': function operator(buffer, m, p1) { + return { + type_: 'operator', + kind_: p1 || m + }; + } + } + }, + 'a': { + transitions: mhchemParser.createTransitions({ + 'empty': { + '*': {} + }, + '1/2$': { + '0': { + action_: '1/2' + } + }, + 'else': { + '0': { + nextState: '1', + revisit: true + } + }, + '$(...)$': { + '*': { + action_: 'tex-math tight', + nextState: '1' + } + }, + ',': { + '*': { + action_: { + type_: 'insert', + option: 'commaDecimal' + } + } + }, + 'else2': { + '*': { + action_: 'copy' + } + } + }), + actions: {} + }, + 'o': { + transitions: mhchemParser.createTransitions({ + 'empty': { + '*': {} + }, + '1/2$': { + '0': { + action_: '1/2' + } + }, + 'else': { + '0': { + nextState: '1', + revisit: true + } + }, + 'letters': { + '*': { + action_: 'rm' + } + }, + '\\ca': { + '*': { + action_: { + type_: 'insert', + option: 'circa' + } + } + }, + '\\x{}{}|\\x{}|\\x': { + '*': { + action_: 'copy' + } + }, + '${(...)}$|$(...)$': { + '*': { + action_: 'tex-math' + } + }, + '{(...)}': { + '*': { + action_: '{text}' + } + }, + 'else2': { + '*': { + action_: 'copy' + } + } + }), + actions: {} + }, + 'text': { + transitions: mhchemParser.createTransitions({ + 'empty': { + '*': { + action_: 'output' + } + }, + '{...}': { + '*': { + action_: 'text=' + } + }, + '${(...)}$|$(...)$': { + '*': { + action_: 'tex-math' + } + }, + '\\greek': { + '*': { + action_: ['output', 'rm'] + } + }, + '\\,|\\x{}{}|\\x{}|\\x': { + '*': { + action_: ['output', 'copy'] + } + }, + 'else': { + '*': { + action_: 'text=' + } + } + }), + actions: { + 'output': function output(buffer) { + if (buffer.text_) { + /** @type {ParserOutput} */ + var ret = { + type_: 'text', + p1: buffer.text_ + }; + + for (var p in buffer) { + delete buffer[p]; + } + + return ret; + } + } + } + }, + 'pq': { + transitions: mhchemParser.createTransitions({ + 'empty': { + '*': {} + }, + 'state of aggregation $': { + '*': { + action_: 'state of aggregation' + } + }, + 'i$': { + '0': { + nextState: '!f', + revisit: true + } + }, + '(KV letters),': { + '0': { + action_: 'rm', + nextState: '0' + } + }, + 'formula$': { + '0': { + nextState: 'f', + revisit: true + } + }, + '1/2$': { + '0': { + action_: '1/2' + } + }, + 'else': { + '0': { + nextState: '!f', + revisit: true + } + }, + '${(...)}$|$(...)$': { + '*': { + action_: 'tex-math' + } + }, + '{(...)}': { + '*': { + action_: 'text' + } + }, + 'a-z': { + 'f': { + action_: 'tex-math' + } + }, + 'letters': { + '*': { + action_: 'rm' + } + }, + '-9.,9': { + '*': { + action_: '9,9' + } + }, + ',': { + '*': { + action_: { + type_: 'insert+p1', + option: 'comma enumeration S' + } + } + }, + '\\color{(...)}{(...)}1|\\color(...){(...)}2': { + '*': { + action_: 'color-output' + } + }, + '\\color{(...)}0': { + '*': { + action_: 'color0-output' + } + }, + '\\ce{(...)}': { + '*': { + action_: 'ce' + } + }, + '\\,|\\x{}{}|\\x{}|\\x': { + '*': { + action_: 'copy' + } + }, + 'else2': { + '*': { + action_: 'copy' + } + } + }), + actions: { + 'state of aggregation': function stateOfAggregation(buffer, m) { + return { + type_: 'state of aggregation subscript', + p1: mhchemParser.go(m, 'o') + }; + }, + 'color-output': function colorOutput(buffer, m) { + return { + type_: 'color', + color1: m[0], + color2: mhchemParser.go(m[1], 'pq') + }; + } + } + }, + 'bd': { + transitions: mhchemParser.createTransitions({ + 'empty': { + '*': {} + }, + 'x$': { + '0': { + nextState: '!f', + revisit: true + } + }, + 'formula$': { + '0': { + nextState: 'f', + revisit: true + } + }, + 'else': { + '0': { + nextState: '!f', + revisit: true + } + }, + '-9.,9 no missing 0': { + '*': { + action_: '9,9' + } + }, + '.': { + '*': { + action_: { + type_: 'insert', + option: 'electron dot' + } + } + }, + 'a-z': { + 'f': { + action_: 'tex-math' + } + }, + 'x': { + '*': { + action_: { + type_: 'insert', + option: 'KV x' + } + } + }, + 'letters': { + '*': { + action_: 'rm' + } + }, + '\'': { + '*': { + action_: { + type_: 'insert', + option: 'prime' + } + } + }, + '${(...)}$|$(...)$': { + '*': { + action_: 'tex-math' + } + }, + '{(...)}': { + '*': { + action_: 'text' + } + }, + '\\color{(...)}{(...)}1|\\color(...){(...)}2': { + '*': { + action_: 'color-output' + } + }, + '\\color{(...)}0': { + '*': { + action_: 'color0-output' + } + }, + '\\ce{(...)}': { + '*': { + action_: 'ce' + } + }, + '\\,|\\x{}{}|\\x{}|\\x': { + '*': { + action_: 'copy' + } + }, + 'else2': { + '*': { + action_: 'copy' + } + } + }), + actions: { + 'color-output': function colorOutput(buffer, m) { + return { + type_: 'color', + color1: m[0], + color2: mhchemParser.go(m[1], 'bd') + }; + } + } + }, + 'oxidation': { + transitions: mhchemParser.createTransitions({ + 'empty': { + '*': {} + }, + 'roman numeral': { + '*': { + action_: 'roman-numeral' + } + }, + '${(...)}$|$(...)$': { + '*': { + action_: 'tex-math' + } + }, + 'else': { + '*': { + action_: 'copy' + } + } + }), + actions: { + 'roman-numeral': function romanNumeral(buffer, m) { + return { + type_: 'roman numeral', + p1: m || "" + }; + } + } + }, + 'tex-math': { + transitions: mhchemParser.createTransitions({ + 'empty': { + '*': { + action_: 'output' + } + }, + '\\ce{(...)}': { + '*': { + action_: ['output', 'ce'] + } + }, + '{...}|\\,|\\x{}{}|\\x{}|\\x': { + '*': { + action_: 'o=' + } + }, + 'else': { + '*': { + action_: 'o=' + } + } + }), + actions: { + 'output': function output(buffer) { + if (buffer.o) { + /** @type {ParserOutput} */ + var ret = { + type_: 'tex-math', + p1: buffer.o + }; + + for (var p in buffer) { + delete buffer[p]; + } + + return ret; + } + } + } + }, + 'tex-math tight': { + transitions: mhchemParser.createTransitions({ + 'empty': { + '*': { + action_: 'output' + } + }, + '\\ce{(...)}': { + '*': { + action_: ['output', 'ce'] + } + }, + '{...}|\\,|\\x{}{}|\\x{}|\\x': { + '*': { + action_: 'o=' + } + }, + '-|+': { + '*': { + action_: 'tight operator' + } + }, + 'else': { + '*': { + action_: 'o=' + } + } + }), + actions: { + 'tight operator': function tightOperator(buffer, m) { + buffer.o = (buffer.o || "") + "{" + m + "}"; + }, + 'output': function output(buffer) { + if (buffer.o) { + /** @type {ParserOutput} */ + var ret = { + type_: 'tex-math', + p1: buffer.o + }; + + for (var p in buffer) { + delete buffer[p]; + } + + return ret; + } + } + } + }, + '9,9': { + transitions: mhchemParser.createTransitions({ + 'empty': { + '*': {} + }, + ',': { + '*': { + action_: 'comma' + } + }, + 'else': { + '*': { + action_: 'copy' + } + } + }), + actions: { + 'comma': function comma() { + return { + type_: 'commaDecimal' + }; + } + } + }, + //#endregion + // + // \pu state machines + // + //#region pu + 'pu': { + transitions: mhchemParser.createTransitions({ + 'empty': { + '*': { + action_: 'output' + } + }, + 'space$': { + '*': { + action_: ['output', 'space'] + } + }, + '{[(|)]}': { + '0|a': { + action_: 'copy' + } + }, + '(-)(9)^(-9)': { + '0': { + action_: 'number^', + nextState: 'a' + } + }, + '(-)(9.,9)(e)(99)': { + '0': { + action_: 'enumber', + nextState: 'a' + } + }, + 'space': { + '0|a': {} + }, + 'pm-operator': { + '0|a': { + action_: { + type_: 'operator', + option: '\\pm' + }, + nextState: '0' + } + }, + 'operator': { + '0|a': { + action_: 'copy', + nextState: '0' + } + }, + '//': { + 'd': { + action_: 'o=', + nextState: '/' + } + }, + '/': { + 'd': { + action_: 'o=', + nextState: '/' + } + }, + '{...}|else': { + '0|d': { + action_: 'd=', + nextState: 'd' + }, + 'a': { + action_: ['space', 'd='], + nextState: 'd' + }, + '/|q': { + action_: 'q=', + nextState: 'q' + } + } + }), + actions: { + 'enumber': function enumber(buffer, m) { + /** @type {ParserOutput[]} */ + var ret = []; + + if (m[0] === "+-" || m[0] === "+/-") { + ret.push("\\pm "); + } else if (m[0]) { + ret.push(m[0]); + } + + if (m[1]) { + mhchemParser.concatArray(ret, mhchemParser.go(m[1], 'pu-9,9')); + + if (m[2]) { + if (m[2].match(/[,.]/)) { + mhchemParser.concatArray(ret, mhchemParser.go(m[2], 'pu-9,9')); + } else { + ret.push(m[2]); + } + } + + m[3] = m[4] || m[3]; + + if (m[3]) { + m[3] = m[3].trim(); + + if (m[3] === "e" || m[3].substr(0, 1) === "*") { + ret.push({ + type_: 'cdot' + }); + } else { + ret.push({ + type_: 'times' + }); + } + } + } + + if (m[3]) { + ret.push("10^{" + m[5] + "}"); + } + + return ret; + }, + 'number^': function number(buffer, m) { + /** @type {ParserOutput[]} */ + var ret = []; + + if (m[0] === "+-" || m[0] === "+/-") { + ret.push("\\pm "); + } else if (m[0]) { + ret.push(m[0]); + } + + mhchemParser.concatArray(ret, mhchemParser.go(m[1], 'pu-9,9')); + ret.push("^{" + m[2] + "}"); + return ret; + }, + 'operator': function operator(buffer, m, p1) { + return { + type_: 'operator', + kind_: p1 || m + }; + }, + 'space': function space() { + return { + type_: 'pu-space-1' + }; + }, + 'output': function output(buffer) { + /** @type {ParserOutput | ParserOutput[]} */ + var ret; + var md = mhchemParser.patterns.match_('{(...)}', buffer.d || ""); + + if (md && md.remainder === '') { + buffer.d = md.match_; + } + + var mq = mhchemParser.patterns.match_('{(...)}', buffer.q || ""); + + if (mq && mq.remainder === '') { + buffer.q = mq.match_; + } + + if (buffer.d) { + buffer.d = buffer.d.replace(/\u00B0C|\^oC|\^{o}C/g, "{}^{\\circ}C"); + buffer.d = buffer.d.replace(/\u00B0F|\^oF|\^{o}F/g, "{}^{\\circ}F"); + } + + if (buffer.q) { + // fraction + buffer.q = buffer.q.replace(/\u00B0C|\^oC|\^{o}C/g, "{}^{\\circ}C"); + buffer.q = buffer.q.replace(/\u00B0F|\^oF|\^{o}F/g, "{}^{\\circ}F"); + var b5 = { + d: mhchemParser.go(buffer.d, 'pu'), + q: mhchemParser.go(buffer.q, 'pu') + }; + + if (buffer.o === '//') { + ret = { + type_: 'pu-frac', + p1: b5.d, + p2: b5.q + }; + } else { + ret = b5.d; + + if (b5.d.length > 1 || b5.q.length > 1) { + ret.push({ + type_: ' / ' + }); + } else { + ret.push({ + type_: '/' + }); + } + + mhchemParser.concatArray(ret, b5.q); + } + } else { + // no fraction + ret = mhchemParser.go(buffer.d, 'pu-2'); + } + + for (var p in buffer) { + delete buffer[p]; + } + + return ret; + } + } + }, + 'pu-2': { + transitions: mhchemParser.createTransitions({ + 'empty': { + '*': { + action_: 'output' + } + }, + '*': { + '*': { + action_: ['output', 'cdot'], + nextState: '0' + } + }, + '\\x': { + '*': { + action_: 'rm=' + } + }, + 'space': { + '*': { + action_: ['output', 'space'], + nextState: '0' + } + }, + '^{(...)}|^(-1)': { + '1': { + action_: '^(-1)' + } + }, + '-9.,9': { + '0': { + action_: 'rm=', + nextState: '0' + }, + '1': { + action_: '^(-1)', + nextState: '0' + } + }, + '{...}|else': { + '*': { + action_: 'rm=', + nextState: '1' + } + } + }), + actions: { + 'cdot': function cdot() { + return { + type_: 'tight cdot' + }; + }, + '^(-1)': function _(buffer, m) { + buffer.rm += "^{" + m + "}"; + }, + 'space': function space() { + return { + type_: 'pu-space-2' + }; + }, + 'output': function output(buffer) { + /** @type {ParserOutput | ParserOutput[]} */ + var ret = []; + + if (buffer.rm) { + var mrm = mhchemParser.patterns.match_('{(...)}', buffer.rm || ""); + + if (mrm && mrm.remainder === '') { + ret = mhchemParser.go(mrm.match_, 'pu'); + } else { + ret = { + type_: 'rm', + p1: buffer.rm + }; + } + } + + for (var p in buffer) { + delete buffer[p]; + } + + return ret; + } + } + }, + 'pu-9,9': { + transitions: mhchemParser.createTransitions({ + 'empty': { + '0': { + action_: 'output-0' + }, + 'o': { + action_: 'output-o' + } + }, + ',': { + '0': { + action_: ['output-0', 'comma'], + nextState: 'o' + } + }, + '.': { + '0': { + action_: ['output-0', 'copy'], + nextState: 'o' + } + }, + 'else': { + '*': { + action_: 'text=' + } + } + }), + actions: { + 'comma': function comma() { + return { + type_: 'commaDecimal' + }; + }, + 'output-0': function output0(buffer) { + /** @type {ParserOutput[]} */ + var ret = []; + buffer.text_ = buffer.text_ || ""; + + if (buffer.text_.length > 4) { + var a = buffer.text_.length % 3; + + if (a === 0) { + a = 3; + } + + for (var i = buffer.text_.length - 3; i > 0; i -= 3) { + ret.push(buffer.text_.substr(i, 3)); + ret.push({ + type_: '1000 separator' + }); + } + + ret.push(buffer.text_.substr(0, a)); + ret.reverse(); + } else { + ret.push(buffer.text_); + } + + for (var p in buffer) { + delete buffer[p]; + } + + return ret; + }, + 'output-o': function outputO(buffer) { + /** @type {ParserOutput[]} */ + var ret = []; + buffer.text_ = buffer.text_ || ""; + + if (buffer.text_.length > 4) { + var a = buffer.text_.length - 3; + + for (var i = 0; i < a; i += 3) { + ret.push(buffer.text_.substr(i, 3)); + ret.push({ + type_: '1000 separator' + }); + } + + ret.push(buffer.text_.substr(i)); + } else { + ret.push(buffer.text_); + } + + for (var p in buffer) { + delete buffer[p]; + } + + return ret; + } + } + } //#endregion + +}; // +// texify: Take MhchemParser output and convert it to TeX +// + +/** @type {Texify} */ + +var texify = { + go: function go(input, isInner) { + // (recursive, max 4 levels) + if (!input) { + return ""; + } + + var res = ""; + var cee = false; + + for (var i = 0; i < input.length; i++) { + var inputi = input[i]; + + if (typeof inputi === "string") { + res += inputi; + } else { + res += texify._go2(inputi); + + if (inputi.type_ === '1st-level escape') { + cee = true; + } + } + } + + if (!isInner && !cee && res) { + res = "{" + res + "}"; + } + + return res; + }, + _goInner: function _goInner(input) { + if (!input) { + return input; + } + + return texify.go(input, true); + }, + _go2: function _go2(buf) { + /** @type {undefined | string} */ + var res; + + switch (buf.type_) { + case 'chemfive': + res = ""; + var b5 = { + a: texify._goInner(buf.a), + b: texify._goInner(buf.b), + p: texify._goInner(buf.p), + o: texify._goInner(buf.o), + q: texify._goInner(buf.q), + d: texify._goInner(buf.d) + }; // + // a + // + + if (b5.a) { + if (b5.a.match(/^[+\-]/)) { + b5.a = "{" + b5.a + "}"; + } + + res += b5.a + "\\,"; + } // + // b and p + // + + + if (b5.b || b5.p) { + res += "{\\vphantom{X}}"; + res += "^{\\hphantom{" + (b5.b || "") + "}}_{\\hphantom{" + (b5.p || "") + "}}"; + res += "{\\vphantom{X}}"; + res += "^{\\smash[t]{\\vphantom{2}}\\mathllap{" + (b5.b || "") + "}}"; + res += "_{\\vphantom{2}\\mathllap{\\smash[t]{" + (b5.p || "") + "}}}"; + } // + // o + // + + + if (b5.o) { + if (b5.o.match(/^[+\-]/)) { + b5.o = "{" + b5.o + "}"; + } + + res += b5.o; + } // + // q and d + // + + + if (buf.dType === 'kv') { + if (b5.d || b5.q) { + res += "{\\vphantom{X}}"; + } + + if (b5.d) { + res += "^{" + b5.d + "}"; + } + + if (b5.q) { + res += "_{\\smash[t]{" + b5.q + "}}"; + } + } else if (buf.dType === 'oxidation') { + if (b5.d) { + res += "{\\vphantom{X}}"; + res += "^{" + b5.d + "}"; + } + + if (b5.q) { + res += "{\\vphantom{X}}"; + res += "_{\\smash[t]{" + b5.q + "}}"; + } + } else { + if (b5.q) { + res += "{\\vphantom{X}}"; + res += "_{\\smash[t]{" + b5.q + "}}"; + } + + if (b5.d) { + res += "{\\vphantom{X}}"; + res += "^{" + b5.d + "}"; + } + } + + break; + + case 'rm': + res = "\\mathrm{" + buf.p1 + "}"; + break; + + case 'text': + if (buf.p1.match(/[\^_]/)) { + buf.p1 = buf.p1.replace(" ", "~").replace("-", "\\text{-}"); + res = "\\mathrm{" + buf.p1 + "}"; + } else { + res = "\\text{" + buf.p1 + "}"; + } + + break; + + case 'roman numeral': + res = "\\mathrm{" + buf.p1 + "}"; + break; + + case 'state of aggregation': + res = "\\mskip2mu " + texify._goInner(buf.p1); + break; + + case 'state of aggregation subscript': + res = "\\mskip1mu " + texify._goInner(buf.p1); + break; + + case 'bond': + res = texify._getBond(buf.kind_); + + if (!res) { + throw ["MhchemErrorBond", "mhchem Error. Unknown bond type (" + buf.kind_ + ")"]; + } + + break; + + case 'frac': + var c = "\\frac{" + buf.p1 + "}{" + buf.p2 + "}"; + res = "\\mathchoice{\\textstyle" + c + "}{" + c + "}{" + c + "}{" + c + "}"; + break; + + case 'pu-frac': + var d = "\\frac{" + texify._goInner(buf.p1) + "}{" + texify._goInner(buf.p2) + "}"; + res = "\\mathchoice{\\textstyle" + d + "}{" + d + "}{" + d + "}{" + d + "}"; + break; + + case 'tex-math': + res = buf.p1 + " "; + break; + + case 'frac-ce': + res = "\\frac{" + texify._goInner(buf.p1) + "}{" + texify._goInner(buf.p2) + "}"; + break; + + case 'overset': + res = "\\overset{" + texify._goInner(buf.p1) + "}{" + texify._goInner(buf.p2) + "}"; + break; + + case 'underset': + res = "\\underset{" + texify._goInner(buf.p1) + "}{" + texify._goInner(buf.p2) + "}"; + break; + + case 'underbrace': + res = "\\underbrace{" + texify._goInner(buf.p1) + "}_{" + texify._goInner(buf.p2) + "}"; + break; + + case 'color': + res = "{\\color{" + buf.color1 + "}{" + texify._goInner(buf.color2) + "}}"; + break; + + case 'color0': + res = "\\color{" + buf.color + "}"; + break; + + case 'arrow': + var b6 = { + rd: texify._goInner(buf.rd), + rq: texify._goInner(buf.rq) + }; + + var arrow = "\\x" + texify._getArrow(buf.r); + + if (b6.rq) { + arrow += "[{" + b6.rq + "}]"; + } + + if (b6.rd) { + arrow += "{" + b6.rd + "}"; + } else { + arrow += "{}"; + } + + res = arrow; + break; + + case 'operator': + res = texify._getOperator(buf.kind_); + break; + + case '1st-level escape': + res = buf.p1 + " "; // &, \\\\, \\hlin + + break; + + case 'space': + res = " "; + break; + + case 'entitySkip': + res = "~"; + break; + + case 'pu-space-1': + res = "~"; + break; + + case 'pu-space-2': + res = "\\mkern3mu "; + break; + + case '1000 separator': + res = "\\mkern2mu "; + break; + + case 'commaDecimal': + res = "{,}"; + break; + + case 'comma enumeration L': + res = "{" + buf.p1 + "}\\mkern6mu "; + break; + + case 'comma enumeration M': + res = "{" + buf.p1 + "}\\mkern3mu "; + break; + + case 'comma enumeration S': + res = "{" + buf.p1 + "}\\mkern1mu "; + break; + + case 'hyphen': + res = "\\text{-}"; + break; + + case 'addition compound': + res = "\\,{\\cdot}\\,"; + break; + + case 'electron dot': + res = "\\mkern1mu \\bullet\\mkern1mu "; + break; + + case 'KV x': + res = "{\\times}"; + break; + + case 'prime': + res = "\\prime "; + break; + + case 'cdot': + res = "\\cdot "; + break; + + case 'tight cdot': + res = "\\mkern1mu{\\cdot}\\mkern1mu "; + break; + + case 'times': + res = "\\times "; + break; + + case 'circa': + res = "{\\sim}"; + break; + + case '^': + res = "uparrow"; + break; + + case 'v': + res = "downarrow"; + break; + + case 'ellipsis': + res = "\\ldots "; + break; + + case '/': + res = "/"; + break; + + case ' / ': + res = "\\,/\\,"; + break; + + default: + assertNever(buf); + throw ["MhchemBugT", "mhchem bug T. Please report."]; + // Missing texify rule or unknown MhchemParser output + } + + assertString(res); + return res; + }, + _getArrow: function _getArrow(a) { + switch (a) { + case "->": + return "rightarrow"; + + case "\u2192": + return "rightarrow"; + + case "\u27F6": + return "rightarrow"; + + case "<-": + return "leftarrow"; + + case "<->": + return "leftrightarrow"; + + case "<-->": + return "rightleftarrows"; + + case "<=>": + return "rightleftharpoons"; + + case "\u21CC": + return "rightleftharpoons"; + + case "<=>>": + return "rightequilibrium"; + + case "<<=>": + return "leftequilibrium"; + + default: + assertNever(a); + throw ["MhchemBugT", "mhchem bug T. Please report."]; + } + }, + _getBond: function _getBond(a) { + switch (a) { + case "-": + return "{-}"; + + case "1": + return "{-}"; + + case "=": + return "{=}"; + + case "2": + return "{=}"; + + case "#": + return "{\\equiv}"; + + case "3": + return "{\\equiv}"; + + case "~": + return "{\\tripledash}"; + + case "~-": + return "{\\mathrlap{\\raisebox{-.1em}{$-$}}\\raisebox{.1em}{$\\tripledash$}}"; + + case "~=": + return "{\\mathrlap{\\raisebox{-.2em}{$-$}}\\mathrlap{\\raisebox{.2em}{$\\tripledash$}}-}"; + + case "~--": + return "{\\mathrlap{\\raisebox{-.2em}{$-$}}\\mathrlap{\\raisebox{.2em}{$\\tripledash$}}-}"; + + case "-~-": + return "{\\mathrlap{\\raisebox{-.2em}{$-$}}\\mathrlap{\\raisebox{.2em}{$-$}}\\tripledash}"; + + case "...": + return "{{\\cdot}{\\cdot}{\\cdot}}"; + + case "....": + return "{{\\cdot}{\\cdot}{\\cdot}{\\cdot}}"; + + case "->": + return "{\\rightarrow}"; + + case "<-": + return "{\\leftarrow}"; + + case "<": + return "{<}"; + + case ">": + return "{>}"; + + default: + assertNever(a); + throw ["MhchemBugT", "mhchem bug T. Please report."]; + } + }, + _getOperator: function _getOperator(a) { + switch (a) { + case "+": + return " {}+{} "; + + case "-": + return " {}-{} "; + + case "=": + return " {}={} "; + + case "<": + return " {}<{} "; + + case ">": + return " {}>{} "; + + case "<<": + return " {}\\ll{} "; + + case ">>": + return " {}\\gg{} "; + + case "\\pm": + return " {}\\pm{} "; + + case "\\approx": + return " {}\\approx{} "; + + case "$\\approx$": + return " {}\\approx{} "; + + case "v": + return " \\downarrow{} "; + + case "(v)": + return " \\downarrow{} "; + + case "^": + return " \\uparrow{} "; + + case "(^)": + return " \\uparrow{} "; + + default: + assertNever(a); + throw ["MhchemBugT", "mhchem bug T. Please report."]; + } + } +}; // +// Helpers for code anaylsis +// Will show type error at calling position +// + +/** @param {number} a */ + +function assertNever(a) {} +/** @param {string} a */ + + +function assertString(a) {} +}(); +__webpack_exports__ = __webpack_exports__.default; +/******/ return __webpack_exports__; +/******/ })() +; +}); + + +const version = "1.0.1"; + +const css = atob('Lyogc3R5bGVsaW50LWRpc2FibGUgZm9udC1mYW1pbHktbm8tbWlzc2luZy1nZW5lcmljLWZhbWlseS1rZXl3b3JkICovCkBmb250LWZhY2UgewogIGZvbnQtZmFtaWx5OiAnS2FUZVhfQU1TJzsKICBzcmM6IHVybCgiZGF0YTpmb250L3dvZmYyO2Jhc2U2NCxkMDlHTWdBQkFBQUFBSG9vQUE0QUFBQUJGaHdBQUhuTkFBRUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQm1BQWhsUUlMZ21YRnhFSUNvUGlKSUwvZmdFMkFpUURoM29MaEFvQUJDQUZpRklIa2lFTWdRb2I3K0VYeE4yWEszSzNxZ3E0TWdJbGlsSThlNW9uM1BHd2NZQVo5TTlZL1A5L1V0SXhSQlAwQVZSMDdidjlnenhFT1p3MEhDSE16RFdvdGg4U2dweUtkbFhnVlR5Tnk5aE50N214dG96NEtXb09kUjlxdU1TaDFsLzdzZFhtQ0RWRTAza0xlWkhlSHd3ZXJFVnU1eUEzanVPdlE5QS9BbFBrL01RRHIwRW5hQWFWOEUzSFE3NVVFeU96aE0xM1hiRWxEQ3k3eUEyMWwzcm5rUXkxQm1UTGNxalIyOEpyOFVLZS9QQ0xEcWQ3NHFaQXpFV1pIVWFIU1kveE5WMnJkWFU3QTl2Rzlra21rOWYvLzV2LzEvYU9oeGhReEIxZlFnbmxZOXQ4M1JhMndZS0NUdHRKTHlKWnNNZEdxVXdQcURjODZlYS9kMG51THZzdUU4SktRaElnREFsaHJZUUVDQWtqQ1dFdkFVRlVSS1lNeGJGQmNPT1lhSjA0VnRXcXRhNDY4ZHVxdFV2OXJXMWRIZHJoOEczYTkzaUlCNGdRNGtETWFNeEsxU0t0VDV2V0p4VWIvUzYzTmIySno3b2RybjQ2TE0vLy8zNy9tMnVmK3pXcVlwWG9ua1MwcXpWdFZnbUpWUC9vREVJbEZONVJUaXZKV216WnlZRmdtYlBBSUZtQjNjMHNFS3RtZnQxMzNHQmI0YkZzSnc2aDR5UkRTUVk0a25vUmZvY1Q2eHljNjlmcE90amVISUhjdHN0a2k2QVBia1BhcFl5OVFsVHNjZTJUaElSU3pMeDdCN1FwMEFGbXFRaEpNZmtrZElXT1U1TXRXM05MNjJxLzBCdjR3bWFldDkxLzdBUFpMQkNzcEt2L3M1aHh0a2pKaU1ud1R1KzZiMHZWeFpUYXJIZkszekp4WXBpWTBxbmFZZDV2YWtuTm9YVEhBa09valRGMms2SURXR1JCVTc5c21yakE2UktQMi8yeTc5ajJsR21CV1RNVFdFcXpBREJ3T3ViZGR3czF5NWQwdlQxSkl3MTRiUzhFa1RjL0RqdkF0MGVJTlVGVi9PcUs4cmdpZ3Yrdi9YYUh5RlkwUnZwM0VpRnJQVWdUZll0SkpFUkt4b2RYVmIvMUFaSytaYVRKOGkrMnJ0bFhiZjNTZDBmMjlUN2VkdFBkOXZBZVFBRHZrU0lKa0JZSlNqRUZpallGU1RFSktiSklwUkJza1dpNXBMVHUzN3BJT2Y0eWxlOG9WS3FjNnZKVHFwMnJ2VyszNWQrVThmNnczYksyT3RWMXZuRnJaVjZQNTcvMk9wdUVlK2hQTVhpRXNiKzVYVVhwcW5TSEF1T1FGczF4bi9VTktyUU9zNnltcUdKZXlIL3A3c2ZmektWSVQzUlNpVUlTWG9RNDBQdmR5My9ud2RPdWxTS0hsRktLTVQ3VE5mOGR4OWlzM0djNVk2Q1VEcE00OG5jam1KRDZlMzQ1bWlJQlZ4MWc3ajliNXQ4RFRSRHc5ZUlPOEJMczVTOXNGdGJ0ZGpKblV2KzZpTktQazlkK2hvMlVnZUJ5SXhrc0RBSE1sRjNRSGNVb0tlZDVWQzMrL3k2WG9BZXFIRlI1QnI1dXUwKzg0b1ZjUmlhS1NIbE0rZkJrWEptWExFZ1dMdFBKK1lkQWx4NGJEUnN6WTk2U2JiSkdSakxJVnBtNDcwQTRubFczNjdMdTB5TjZVVy9xb3hJbFM1SE9MbHVCV3ZWYXRKcUwrSnRBcndFanBzeXFXL2E1ZTc3MnJWL21HZnBIck1Td1BheUViOXZjUk5NdHRLbXJqNkxzeGZlUyt2eXN0ak5UK2RvVmRmbFVYOG5WNVFjejZaWjBhS0V1dEtady9YMnFSazJyV2JSRkZzdkdYOHRTbjExS3FoZjBoajZ5T0kxVkZsTjFReHJOMG1IUVh4VjBxaGcwL3FpcGZjcGZtUFRFczhrWDN6VFdlRXRrcDZmUnFSNVFENnNuMUpmVTE1NmlUMWxQUlIveC94V1k3MzNtOE1mdWorMGZXejRHL2ZmUHYwWC9tdjVWL0dQK0o0TDd2Kzg2N1c5L2VXWEw1cldqSTZ1WExWMDRPSysvciszcDQwZHJ2Y1habWVsUk04K2ZtaGNWSlRrWkR5NTRDRncyblRLVzRQR2Yvb2UvOHQ5dzQ4U0JIUnRXTEFBR2hLYlNXWjNYV1ozU3g5UWdLYm1UTTRtSk05NGJraUdDLzBJMzlHRUxPbEJzZmR1ZFNGSHBwZnBTZlFHQUwyVVZnYUxZZzVjWHpybjlEeTcvL1J4S3BVRFE4MjBBbnY0RGxwV25KTzZmUEF0Ynh1STRRKzU1UWtuL0thOEYzbU1kR1JHQ0RZSXhiUWFBQUVaWEVRSFZ4NndpQkNiZ0gzaGk3dE1RTStqVGp0czNuNzJvbUx3T0xyL3lRak1Zc1BLcnR4SUhGTnpPTkdHc0kxNkRKdFlnQmpuNFF4eG15QittQm1DVUtCT1ZnREQvc2lWUThzU1F3b2FJUGdOS01KQ254NlFWVWFBRk1Va1RFT2NCbjJIclNFQzhFUWxFSHJyeUdGUlVNQ240eStHK0lBUDZremZCdDA2aWtRd0RSTTQzN0lmeldveDZtSmg3N2VNcms5NEVyOEVTTk4vNmpSamRoUGw1T0RJT2t0d2RBQVFJRURMcHd3b0NqaVhDMXd5VmR3d1FqNkpPQXJJbXNoei93N3lYa3I5ME13YWt4YVo3Y3hRR3NpdTJia2NnQjdUZEJrNUdWQkJSSFlhSlNaVDZjdkNKaWJsVTRERXBGT0ROYlJvSmtpdzJaelBSV0ZndVhvWldHWkMxQnh6U2EveUI3Y1EvTDBZbDdBZ000VkZnNWNkRGEyZEZDNklIUUpWUHVQQjNPS1VIOHRlWU5zUER5eU9ncGlJd1EvQVA4OTQ1K0Z5V1NYS09zSUk2b09PSmVQck9GK3pUaExYSmhxLytrQ1c1NjdWLyt2YlpheVRXMkphMG9nQmg3TDBzbENsRW5vNkNCcjFNS0s1d0x3dy9oTWhDUVZST1VpRDBFT3VFOG9lWVBCaUpUanV1T0EwUmdIeUIzK0lET2Zia0hUL0tZTEZjN0RobEFXQkpHeUlrYUEySTRYVnlHK2FLNE1RN1Q4RTZ0R1RMNXlWamlUd2M0aTU1L2haSDgyQUFyaTd2RldzandVZTdaalZVdEtNQTIyNUZCd3B5QmpJTGdaTkZnY0ppd01YaXdNMFN3TU9Td010U3dNZlM4WEhBRXNteEpnRitOVW53ZjZYMlk0NnRLc29FR0Qram9oUFFXQWgwRmdWQkZnTWhGZ2RobGdBUmxnUlJsZ0l4bHBaVERoeURCMGhiSXNVSGFrbG9iUlVZb0FxdE9rOEd1NnhSanVxWFN4WkFEM2NZbXZZMW1lVk9GakFTRHJkaHBZbVJCYk5Sd2MzYStsdEJUenBrbnR5d0lqbmd2dEpLM253QzNLYlNpYWdNMmU4YVFQNEZ2cjd1TktlNlgzS0c3bFFzQU1mb2xRNFFqREhjWWtYWXE4ZTNEU3BSUUEvSC9OVFFNZ3IwRnV1d3BCTUUxM2ZzY2Y2dzEyd20rSkZKampmRXVxRms0THE4VTdFQ0FwbVlIQmh6Zk5MY25lYU1CWTV6UmJWTTFRY3JDRFpVNjBqaFNmYWlmcWo1MnYrRXA4VnE4elRkWWlDNFROaERFVG5JRXRBdWNsRUdGSW5aU0VqNTQwREdpUkpsbWpEZjhpWURKR2Fpakl5QnBPRHRZVTY3dEcrVUZEdEN1ZUc1VGpDaUo0b1VIWlBvZ0NBdXFjaUlxeE56eTZJaVM3MUVJZGVselNpSEo2alZBeG1pSDNlR0NPanhmcmVtM2tuS09vUGNPVFFsY2hscDNNVjZpR1pOQmwxdmNheGlaTWZ6cVVBY2VDOGgwcWpITlZpd1Y5WjFMTHFIV0c5Mk5kVm9wWVZJTE5JWk5YVlkxTk5DVGF6cEF0MGJMOGNIV3pLRzdIYm5EQi9xVlJHVEpUdmhGWS9zc2FLODVkRnh1S3BabFpIUG5GQ3VFTUorSnkwekUrNnhkVmJua0kvQk5mbVkvYnJQTlR5K1Z5amJ4blllK1lUbzF5RFNBbVdaU3gvcnZoR24reVVvU2lZZ2NSaUJuZlNMRHVKaUpNMlpLYWNqUk5VQlJLTzhOS3d2UkNzeUhadlpLd0JwZVZrRWVkYW9sRzlnYmx0QWJoVVdYUTQramFYTzcza2wxUENOaElXOUFTWUFHNGhaWkNGWTRoRE1NaHV5UXNoc1pOc0lnUTZIWUxwc1NBOHhhOWxWaE1BYWgyRFcyWkFOeEt4bk54RUNXeHlDMldaRGRoQ3ppWDJBRU5qbEVNd2VHOUpIekRwMkh5Rnd3Q0dZUXpacmpsSS9ySUZOamk5NWY5amFhazZZWVhDcUFoRmlUSERtcnJPQWg3VjFNS2l0MC9NVFk0SUxkK01EWE5ZMndGVnRBMXpYTnNETm5DYTRyVzJDdTlvbXVLOXREbnFVZWtGMzNFTEpvK2RnNnRhbjZOcm5hV1Vwa1FZSTZIOEVwSG5rNE9VUVFWRy82WGY3d1E3b1JhNEFvMzhCQk4wT0Jybm1LWnd1WUVVT0Q0Q3hLQjVpRUFENVVkWnRQTG9HbkwrQ01yaUZSd2x3S1dvQXBkdmNmSmFHUU1RcW51SEFZK2RyUUhaR01EcTFrTHhIU3NxUmtzWXQ1b3grb2lFOVJ4MUpvbGlyMUJMN3VVUWxUTXZnNXNIbWF5T1NwLzFhT3IrZm5WdURkcG1sUjl5OFZXREg3V1JCZHZDM0NrRVRoekxKZEFYUzlvNHNyNnBWbC9oTWxzUlpoVDZSN09RYzVYRFlyQzFGc2lYRGZTQnZ1UVJCWEdJRDNCZ3VFRnRlYmtwaUJqWW5kV2V6a1p0ZU5zZHg3S21ENHQ5Rmk4RkZ4N1p6VDg3NnlkTWR4L21wdmRSSVZBMmpaZWJ6Y1pjSkpZV1V1TG01SG0yMlZ0ck94LzduOS9uODFVcTFLRHRkem5ueGhCZG54YWJaTkNpS29veHlrMmJ5a2xpa0dObHUyTndhSGVib3VOVTZ3MUEwdzlDWlZJZEszNlV6aHNHb3Vzb3pMTTlRTE0rZUFpR0tSeTh2Tis4VEw4MG9OT090Sy9XZU5FNk1zdHZzSHVITktsbVg0dkhFNWFRem5rMW1POHV6VWsvS3hqSVJTZkpaRmJhUTF5RW9XVERpcjMyRXh3bVd0bUMwd29nTUkrYndLa21NenBOV1d1U3ROQ1B5akdaMSt5bkdKMGcxS1l4RzhXZ2l4YVJ6V1QwVFRQamp2cGJNdVJtUHgrUHpuLzRabDR2eE91SzI1Qkc1NzUxVDV1Snp5Ym0wbFdrd2RJZTE3UE9jcm5udE15Nkg3TEJaTFJaTWtoalJZcUd0QWFhV1dBMHdqTThySzZKYlZ6MjY2azBvQ2ZmQU0vQlNsRTloZVkvVkkvblM5V1Q5ZjdVK3o5QmQ2aTVMVVpsOUtGdkwxQ0lUNlk0M3BhVHU4RjFpaTcxbnpoMkRnZU5tWjQvRldjeDVWRmJYMEdzK1FaaHh4cm5BeExaeE9hR0NPZ1lwNXVzQStGbVlVVllnMUpZQ0lWcU9pcXJ2aU9FZ1BEam96eHVlQjVPaThvbE83Z2ZHYjdydE5yZXVOUUpVeERKV0JjVk5lUVRBdWxPQWdFYkJsaHdMbk9SdFBNTkZFTEpacndEdVBnb0xKeUlZNFZ3SmNRaGM0RGhNYUN5SEFLYlJNSGM0WXhtQTlCaFJGSmp4dElKbEZhd0MwVG5qWlJ1NDRJZ1ZyYUpsZ1FhNEtZNnd6MUVjOFRRb29ScTZBR2lsV0hBNUkrZ0NkelpIMW8zS25mYXF0NXBBVUNXRWxERGZUNmFwUkZzamhGWGlxaTRJV21vNFlGdzBidzZUa3crd25hZHNOMkFYb2JsU2lYVzU4MnNJaDJhTjZBQjlUUGlxMXlIV0NZUmpFVUN5SXRVNVN5RG9pUXhGbHNuaTdYb3hETVNwSWhMRkp4QytpK2tyNjNpODVaTmdDT04xU3NFUkJwNnlNZXNKSlQ3VytVRllDNjdpQ09FWUpSU29KWTlKeDVJbkpBUzFPSjlIekJqUVQ0clVGN2xEdTRBUitvYy9zZG44SkQvTVBacDlmOWtqblZ6OG01SzRRQzhFcko5ZFVna0ZFNEllUUxBM0FnbVNrYjJOMVdBTG0zbktqZ3JSR1M4eFVoV3NBNFQ4SXBBM3hTOEFSeUFQV0tkMDBLc2lNNHczcTBZbHhPNlZXbzZ0aHd6OUtRekJSVWlCVTljc1Fnbi9UWG1YdEY2dlJ1ZDRMYkpLTGpUT2swYzU5UWVERWhvN0hlYzlZdDBFaEZtaTloSUVNT3FSakljRkJleGpmYk81dlhSeU12K1ZlaHpyRElCdzlXeWpRMTRENFFoM05rMTl1Rk43dzIxM3BBbnNKWXFzTUc2Mm9INC9BMWRpRUY1dXNkVlQ3U0RZTHBiS1VrWW9LaDd6VVNBOWdrU2RlY0E0VG11WTJMY3NZd0g1bEIwUGszdkY3b0J0c0lBcVVqaUVtZDJ3MlZlYURTd2RTdEI2WndMc3pMM2pKVVh1TUVMR1g4L0NSZGtHeG5rdnpHRmJpZ2NYTXU2ZmhkQXg2eWt6ODBWTUxnaGhtWko2MkRxRkRsa2d0ckc4V080SzBzZWtYbml3bFpkS3JKU25Sa2NOWktGajFwT09wRFBrd3RiQWcvdkpnNnJDWlBRQXdrYnhPRUEzZStvakova3VMZEp0WU1OSElZVlBBZUlodGdFS0xqS0F5dzdvY3hrOVJjeFlxTFdaYmsxQXRoWjYxZGZQOGlxRVZJa0FIRWRlRTc5d1Zqem94azVMcGFGNGNreWZNVndBdG14NW1ndi9tZWFKUW5nMTJwekZyMURtMEg0ZzNlSVB1VkYrZGhqaVA5bTlUUnZURXI0dnAxVXU5YjZhL29zdU5ub3FKZlhHeThqVklmQTU3RzU0Z0xPUk1xMzU4N0E1MHVQY0Fta25EdDlIbnlERm5xR2xzSm9vMW81S2VSZDQyaE1jR242dTRqMkVIZ0pwWng5blhIR1dSVkcrTzdwYWd1QkxnRkRxYzBKOVB0WUhBcnpzS3k3T1hzSVJCZUJ6QVJNMVRtVU5SNEFlL0Y0eEM3T0ZNMTZDWjhFdllLV1hrdEhRVlY4aWJvRXdmL3FTRkZiN3Y1d1hVclJkWXBLZ3pVcGo5QStWdmkzVkRFZTROYVVQaWVlRithalVHeEgyd1pwV2tlcVZmcElYT3owWlg1QkVFcGFiMXdLTUxwRGFJN2p5WUxiV0RibzgyMGwrVmVqYzd5VVE0MXk0UVhiZFJ5b3BacnlkWGVVekhnVE5HYVpEZ0ZhYmNLa0ZoTHJoM2hpVGFEbEFKaVFSd3BGRURRU3NNNUpGaldyUjlaelpMUjdYY3NFYXpwQW1kNmkzNU03cEF3RUxGUjFsRHBkcnVSdzhobS9YbXdodEQ3RFhRNVJMZU1XUXF5eUM1YVNTL0hCWkIyRElBRjVDcHdYeSthb2dXVzBHSkFWZmZiMngwL3dFYWg1QXlFZEZnbHB1MDYxRnRTVU5ucllKSmxPbk1FblVDeDRzakFlU3hZdU5RN1d2QUdpbkkzZkVZNWtIK0VZTndxbnBYTXpYVlZYNGpqb3RMZ3ozNmVzUVdLS1M5b01aUUpUUUVESlovTkg1eHlHVVlLSUc1aWR1N1NLRW9IMWFIZURmTE9lZWZmVldhelVQS0Q0dVk5Mi9hNmxIL1BIUE9GRkI4VXVOSll0cnMxRnFQYzBmbjJvV01ET3hLTlBRTnBPa3Zod3BCdUxwdTFKV0V6WjZSa01wSDd3SUJRWGQ0a2ZTS2pMNzQrMGhQOTRrb1lRS244Yk9MSzRuRGNPRkJ0WjFXd1NYdnJBNDk4a2NOZWtqMVJSQUlvT3dxd29IRkhqNUFCdFE1YVV3UnE1QUkwK2xwQS9lMUtleE5BOXE0WUh6emxkak5KdXpLTnROQlJlOEhROFRrV0tJZ3BPTU1VeHVKc3c5RWN4d215blVhNDM2dVVDbU1UU1pwR2tGMWRUZHBMNWs0eEk0d2xheFNObWJ0S2tKNnI3TTVBRzd3SWFQOEhZTXdZYmlsZjlJNkJzQ2JNUlZ4RjJtL1ZWWTVMTHhDQjlwS3hLSUtiM1FqL3A3RHg0TlJrdm9Kb1JOSUowc09SOGRHQUR1Tmx1eGlNamZrL0NoSVRhTWtVL3pnNE9ZK05ERlh5cjk2SWg4a01iM3M2NHhwK0o0Ly9iZ3NZVkt3NWFleWxjb3gzWnpFQ0JnVndCY2dyTHhZOUlVajkvTTM5SjZCeGxrYXlTb3VOSDlZaU9OUVBFQmt3TzFYaE5IQmV0U3h6eTRuMVFBcDdHY0JFTzJjUmV0cUdydmdpK2IrVWFmNk1ja0twbFVnQ295Tjh0NFl0d2pJQ0I4REdGUUJQTGEvWXBkM1ZBSVVGSWhMMkg3UzNrNGtxd0l4N0hyWjZrS1RzQzRKYTFtRThaWlVhazliaUUrYlpFY1ZqbGltTHU2WUtJV0JsNU9JU0JVVlNnbDkrSEFlTnBHZVdwdUY5SXFHUDNYV0VsOHdsRVVpa3gxRG1IbVNmT1FmNDdoRDRtaFM2YjBkTEF1QmV3a1BwOFRYdTRtdzhNY25VZFhOdDlHTXVJb3hOWTVRa042R1YrU0szNFRzMmg3TDgwRHhsQWZnMmU5NWtUSUxqcnNRMlp3ckNMMFR2TlRCeUhzR1FzQW9UNHVOQ251OVIyaS9ZUmkxZmFzMHB3RjdQeSs1VFJsK3RibmwwM1NscUZ1cTEvd0ZxNFN5R05ueGY2OFgybmtDQk9rNnF0Y1FCWGh4SllvWEoyRGRiWGdCaEJlcXdia0x0dmVMRUtxUG9WMXJpc2h2RjFyUER0OUk3M3BOdDJUTm9RVEVZc3o3TTBvU1kwVkFzTUtCWUVHaGVMQ1FmVUxYeUNIdDllQUdWaFhzbTFRLzJyZWY3VGNYQzVNdytYWTJEeVE3NHVhMXdaVmdDQTRaTTJUSEtGSWpMd2laMEhFS3NPVlJhM1dSdzFFNUdYYUJxNlhsZkN5elRYdVhpVDNaQ1E4NWNUN0FTVDN5aFNEVVZUSHhpN1FxNFR0QnUxY3FOcFdTaFIzV1ZlS3owWkt5SXBoOVlKMWVFaU00Qnd5ampKV2ZUVmFUbFI3N0dXUnAxOGpPWWtqeURnd3UwOVFyNXhGRjVZdUFQcWlaV0MzL1dCTWhtYklPZG9ObGFZS2s0YmJCNUJlQ0dHWGVoaFFzWmV4ZHg3bmtNVWxSMWpreW5NQUo5NlBkZkp5Y0FSQ1pkSURYQTlBRHZOU01FRGlwZnRBZVk4VmNNVEZxREhnR1lpekF6WEVLUEJWSUtGRzJEaWs3WjdXT3NJdndZaURUaVpPTnJnY3lTRjhBc0t3OW1EZG9KUTFuSHR5VlA4Yms1OFNydm5xdjNKYlBscHNZRm5HUlFUL09UT2o4QkFMVWZFOXdyTWpaTXNLd3UrSVNTNHZPeFJSb1grNml0Zjc0SWU2cE9oU0lQMFZaNWtrejkzNVZUR0xFcUZ0d01KeGsxaWo2WFNZZmV2QlFIb050OFg5OTN0UnljdVhBMVRjRzYwVVFNOWNqWG93UW5BRjVkVHc1SW5KdGdxU3p5bG0yT0hhbTVnc0lURGNHY0h1bG4ydFVZT2ZEYUtWb1phR1VzQ1pheEM5TUE3WEc3M2dzWmNXVWVtUncydTY0c1R1Yk9rRXdzaGFVUU0yWHJqTElyMWM2VDVpK054bU0wbmpFZ09QQXhaWVovUW92N3hENk5UcllOSWlTN3Mycml0RkloK0llQWRZTXRZRGhjQ0tUOFVBZjZlTXgzYkdFRms2TUFkTnFOcW9aUU9pSXJ3ZmpjdjVjb1FPVXhsZEU1alJReWh4am5QOU14cHFCMHgrS3JHNjRla0sxcFZ5R1U3NG5xWDdXZnRET2gxcVM5WVNSRVZnUElLeXBUT1ZCZmlTUDFPaGJkTjl1WE1TcFl5REVyU2d0VjR0K1VsdGIwZFBOSXd1VnRoVFdYM1FYS2VLNmZLOFpEa2FyV1lzODg3dXcwdFFlcGI2Q3VNS1plcGNaZHNFMTVsRmtIS2ZGUUlTOUxSZ2lIb05ZWmVpbW1WRUpTYmJSN1JOU0JHVGFPV3A1d0tjM20valEzZlBhQ2NsUm5YQUNSTTE4OXJtaWU3Qk1qWWtpcXN5ZlVZUFFnanpNS25XOWVNVE13UzNOK1dDRGZpNXVJRkp3eEN1VmdmVFRnUWxvZTBGTisvbGFYTmhZbjdWeGZrbTdwb3hVaVZzSHVOOW82MUd4dHVxVE5JSktPU016eDJzMHNEN0taemN1VjBrWXkxT0tuQ0JtU1F3SVRzZ1pmQVJZWFZ3b3NPZVVwWDE4cUZkUGNvSTNhT3RobTRMcXArY1N2MHoxWlQveFNHWkVvZEFQbEVXcXNPZEFNVDYxWkdJZ1lTcWlRNGJHYzRMOVFIdjFDRG90dmlqSEluRFVJK2xxWWR3RndUTTZYUE5hWFFyTXl5M1lVNkRXeFc3M01hZWpBcVRaVmpBT1htUzEwblJITlBTWFVNL3ZHYThVdDFSL1hPWmthaW13ZEdyTzRFeUhBZU5PVTJ6WnNMY1dqcVZYd05veFUxRXQ4d1RQRHByQ3V0cVVqaysyMkJURzlGU1E3OWhpUXZLSG4wMWZ2YmFpZ3NXNlo1YmZmM3h2bkxiZGJwMVVGbDZMQ09CYkxuQ2h0cHN2LzdhdW1uRmZXN3VMM1FaZWZrYmszbEFtdWdNSzduWWlvVmNNU042akhXeXV2cVhIbFB6a0lhM0dlMWxHbkxhZFZySjRsS3QyQnhMU21ZN0I4c05SemtSTnRwZFErdzRxVDBUYXgvNUtjWklYRlY2N0FSeGtYSG0yZ0pCZ0hidHN5YjVTQW8xNjZsMk9GNW0ybEpMdVZPY3B0aVJuUnJuVG5TMUlSekxQdE5lMDJXaklPYmdzTnVNK05HZHZtUFo2ZWtOVGNYTVRleW96aEg2U1hhNWhHbnY2aEQ0c25xalljVHhMUXNCN1h3c3lmSUVBczhGdUd1bm9lMUhSUXNtSG9SeDRtQ28waHlxeUlNU0w3RXpyR1N4WmJWZndlaENrMWtxcXJLUVVTR0QxY1pwdEVBTUNIYlgyeHorcUVZNmZoS2pURkJ2Wlo4N3Y3UUJSSVhkbWQrUytDSkpxa2pXYXd0c3E1Ylc5S2lHNFIxS05MV3ozVExrcWg1Ymh6N1pxTStkUkJpcTVkOHdxS0d6akVuWlN3dG1ZRHhLc1hIM0JBTnBHMjVha0pXb1VzUWs0YWs3Mmt3bnhST09VQWk0bHp4L3dCWHozbVNkQkJIOXN4bUVpQ2ZIQjQ0N3ZYSXdEdTZscUNKWENvOW03YjE4Y01NQkJXZnIxUnpKWmg4K0hIaGYxRGxOMlhYcC9vbnNWb3JHM1daclAyOEdFbVhaMHNUUlc4aG9yRjFHNng0ZjFHVzdRbVpoSXZTd29oN2ZxaG43NXhiWjJjRjJMcjloSEc3VTU1Wml1UTB0dngrWXhHOWowVXpNOVdINlkya2FjVDFZM000bkV1a24xYlJzVVFVTzlrTnlyRHpwcVdEaHcreTFCZkx6MkNLOFk1bXB0VEZDWFZxd0ZkQVZDVmlSYldXalF1QXk0ZU52cytGSlVXMHB4Qjh6U25HM3daNEE0V0xBK3Y4YVVmczJXckhFOFc5WHdVVStiVHdCSHM4TCtMaUt3anBRUy9GSWJWOFZXaEZ2dE5ScmtsL3lHN0tGNjA4UkdoV3VuakZ3UVVFL0hjOVlTd2dJR2JPQzBSMkxUSjMwZXBBRG5tNmVRdU9sUVk2NFJsNXUwV3lPU0NmbnFOZGRKWTVwSkdqL2RiWkZ2Vk9XbHF4ellXYVJZbVBQT3hVQ3BEb2dORFBhZHd4UnZZbGd1cjdRY0tEcEpubFBXUWlUam5MSDFlMzVCYmExTXcxOENVb1JWUVBTSlpxQWt0NmNHbHFocUtlbzcxaGhwMGNvb0lud2ZxMjBONkZSbGpQZUd3RndxTmM4WmJHRjhOV1VOT3dFemFIeFl4WUtod2t0d2FXMmRXSk5HRXcra3JVb2pZejZwTERZVmoxNlFYV1VORkpLbGtKeHJ3L1hqYnVRMzV6VXZ0M1p5U1AxSW8yQU1ZVW1PazM2U1Y0VUxaaGxIY3FPU1FadXBhZU92c0orZENzZER3aDYzWUxjcDI4b0ZGSXhLMmlMMFRtUzVSbVRQLzJUcC9lcm1DemRtSy9wSUNrNjcyRHlmTkIyTEVOZzRtYVB5Ym5qVm5QU2JYM2dRNWdCNWJ3UjRGQWRIbmpvei9pK2l4WVVnUktKOWNFUUJzRTRDbWVPaDZqd1BQaEh0eGQ0d1dNeGxLSG1MQTdWRHRHUWt6TytvNXpURlJiUmZqeWVqaUpORyt5emZHbkM2TVFYVGoyamJtYlNiWFJXeXkyRG1zTDZ2UXFML1lKYWxPMi9GZjhHWWZ6VCthWWRvTTY3WW8xMFVNL3QvMTJrMkVoMXZQcVdlWU11TzJNbjRCS3R1MFZ4SmlLQkp4V05Ka0h5dURxcXE4R2dsbm9KenVaUFc2Y1FQdk91cktxZWNHbXFXdEF5YUZ2QVNPMzk0ZnpoYnVTdlk3WDlQc0p1M1VDbE5Sb2JOQWd2cjlVTlE2aWYwRkQ2aisrdk1DY1NJMGE5RVlxVzd0OHR1TEJQQ3d5SDg0WVp3TEtGV1F3THc3LytOKzZ0MUJqeDlCT1pQNFRxZzMvZytydUVUbEdzMlNMUFFqazFtNmhCazNSMTJFcVdkaEUvMzEzOEoxMXVwenJtUENhV1BqRVRnMG0yQ2hUUHZXT05ZRklsblJ6VDUwTXBsMjY0K3IzMGxQeUlJK1AwUkxCR2NSOVlneUpZMDVVa3RPY0loS080WEQ2TXJZY2tqL2JjckI2VFd0UCtJbU0vMENNYXZRTlhhNnJONmpKdHYwRFpkd0NxRCs2bm9mTFpGWjRHUjlmRHc1ZzhhRDJRZTJqZ0s1VFUvTWd6cW5DbmE0ekkxSFc4YXRxWHh4M05FWmlFdzJjWExjVG5NSmxSamZUaENWV0psWjVidkZoYlE3aXpjakZ4cEVPbDhoNElkVnNRRGhXWDdtK2NSVGhTMHhiNFRUOU1PMkdVdGJwa3JXVmU0N3RoWng5T2VMeDBnaUVKQVhZeFQyV3Y2OUJsYVhxUzRhT2NlaURtc09KeFNzYW92WWVlRldVNUVhVHlOTEpUYldnbUhFbHNkMU80YkU5NDcrVXpYazJ3QnlESGdObGNXNzQzNGlidm5qVFZ1ZUdaQnI3UlcySnFLUHZEL2VrOTQ1a1drVE9DSUFwaCtTQVBBWFZtTE5UMXBLTUpyaTRmdmNqSm5TUHhScUpYV1hHOWpVdHpLSGhHdGdJck5kZFlUVmJqRzJlcjN5S0FMd1pUcnlheko2L3BvNlFERFozbWFlNnhNemJtQlFVUC9GaTJWMkloWk1aRmY2QVdGQTlMbFZTMyt2YnEzRmoyQnJ0ZmJ5aEhxbU5NemsxbFZVL0xmSTJmMzdJS2M1TCtPbWVpRGdNamJZSG93bHZwak4xSFRSN29tMTl5RlNhVTV3aml3QlJqL3Q1NVlleXJyMVlGWE9aVkZObUxBVFZRYWNzWGo2dHhYeTdmd0QvSVBveGp3QU1Jcno2S05hSU16ZWFNeUcvWFdKckhJcHNibitVZWdsSWV1UmZ3ZzdRcllHWkZ0dWdDN2tjcG8wVm4raFZiSHRGK0syYndSUnZHZlBiV1FEWmhJejZTYlVhelZwUCtrdjhLd3JXbDJ5QzhubmRNL2l3RUFWK29jZ3BnZ3FhalZHVDRVVWlZd0YyOG5ySlpQR0Q5RTI0NWxWNHVyNUh2UlN5M1VqT0VudnlSVGJ3KzhRMjNMNWtWVVM0NVE0SjlaOXJJaUlvTXJWNkhPcjZDalBjcTcyNlJNMzd3N2NETUxWdTVUZmg4YWJ2MldnMktJRk1LUGZodmZIQUw5Tjl3dzZJSVZMMUVuUk1kbSt3R3hCQXNkRzZSYVM4dGZOVGNyTVFxaE83ajNmNXVheGZnQVFYSzR2M3gzY1pTVzJ1M2pQcm1oSFl0Y3o1YTVVSS9NR0ZQZGk4MDVxeU0xNWFhUzFjR3N3eGVZb2EzY3dyazBIZ1hROUFqWHZEWVpHK3c2SGt2ZXFaWU1XSWhRRGpWNXZkek45TVJjb2FjNThiS2JVcFZjeU5uWUdNSVRVdTl6bmZ3RGZBTWVnK0pCa0hXeVJTMzM4eWIvT2RJalI3Sm5YUENwNVBoWUEvUlFlTTd5dXNXbXpXaDAraStkYm8rbWM4WkZqbkwweTBlUk5Fak5kQ2I4QkM0YnVtcFJqR3NhQWhYYjdsaEY5WUpsZTlFcis5c3VQQm02VWI4UUFpdUkybWtzYzJCeDIzOWxsMWJlRDFrWkUzMjVFdzZHajQ5ZlBiWmU0Rm4rNFI4UitFcHdtbnNsbkQ3M0tpTG4xS0VCeE5WYysrcjlmSzJMOHhpUStYRTY3Z0N3LzBJVU8xMGZITUhVYTZxRzErSG1nY1dQN0dobkNaYklaR1JmakU1UCtmRCtZVUlyQy9ISXc1bDIzcWhueFhPVDJVejhiWGt2TXY0MjBNZjYrMzl3SGZlR1RhcXZVVDl5TjV3anBwYjNIZ0VzOGpoOHdnN2RLUmZRendzNlV0RUZzQkV6OTY0a1pvQWgwQTl4cGlaakU1TmplVlFoUU50UE1zUFJqSDRJOHN1M2N5eXhkL2JGdmN1MndPSUFuaktBb1ZzbGJoQ2xkVDc5S3BuT2dYaHJ1NFNsQ0JIeUVrY25qRm9Sa2FnVnVqSDd1bHJCM3lQQ0VHMTZaSm45Q3RHOE9xTVJEdzFuQnJML1BaQmRGMmNET0wvWGtBVHE1d2hkWklxRVNTWUVvaDRXZWF6a1p5OEJQOFhYZUlTaDl1OWV1a2hqa2pQc0l1amtLWS9zTXRUVFd5L29ETU53K2dXd2NZall3YWxFdXdLSkVhRWcyUklyYWROWW1SK0U3d1JoTXBwUWNXSHE2UzJFd3FaaDF3STY3WEF4NnJUN2EvMGJXTUFNSXN2NG9RTkFsNHdtYUVGUjB4WFdYdVgwRWlKR1lGV3VQWUkrTWNBU2xoeU8zYnBLR2NtR2VnbFRNSXU4WmEvMTVrWFlxRERBR0oxZUNvc2VFYm9DT1VOT1VxUWZuN0Nqb01QQTRsMG14RGlSTjd0RU5oMGlpUDB4R2FBeDU4RTBSekNZK0dFSkRTSitQUW9SS25wcEFDSzBhbHdlQ2NIS21hc3J0TUZQUFBBdmxId04xNjVlWGZjdTVzY2d0WEVOY2owdUlsWHh5OG9SZE9UVWNITDBWMWVqWURBK3R6NTA1SFNYV29GL0tvamM4TlczSDBGWU1tekRyNXI2OWRxeDdsM1RxSEduQ2xsTm8rMWZvV3RLcXlrelVhdDRhWDVycDN5UUlVMjRrcU5rSzdYdXEwSktHaTF5Uk5VNnZFNEhUU3FOUFZxQmJOSXh5dFRhSTVOdjFacFErb1NYdk5yeXE5YU51YnJlSU9yNEN4V2NFL29xandoaUJJYnM5T1VicDdhdWRXQlkwOWI3N2tuZE5wbGM4L09VSEpxVFNqL1V1MDV4M2RXSEUrUDVVNHdZUUhpRWRYUFFBaTV4S0puWGw1N3psaFFZcVM4S05jZEFlUSt6clEyTVdTTDFyVnE2UlFzWE0yL01qVHBmazVyU0wzaDdWWGZVNTZWdXNIOEl2cytPOFIrNVZDbjhzQ3BVOFNnOHJ5azFvcUVtL0h3ckgzcTBJWkRmckl5a0YrV0hBcWVOQ1dqVi9PM2VtQnBsZTdIZm1QNGFLL092ZUVxOWRwQkVPWmI4VWdUTlAybHlYU1FjRjVQRlJNNzFVTUtyWUdDa3JYUXE1czhxQ2o2aVNoT3FMZTJBVUxhQVE1U3dGVmNsd0ZVaWRVUGdMUzdua2JMUWVTREVhU0FxeVQxZU5HeWFyUVV6d2lGRzBBT1cxaVBVZWF2aTBGMEt4Z0Q0djZ6MUlWZW9VU2hzb0J1eks5MzV0NEJtV2E2eTFrWHpxL1V0N0Y2M1hNN1RaYkFEaXpvVEhwSi84bnVsK0hBbUQwODdnMjFRMjl0OUZ3SWtDeStEak9GSWo5SkcxUGRRYm9xOURTMVBEbzF4T1RsWUZuM2F5bmVTMU1aaEFPdEdzcVdzdEdRSEVyQ0RJVWNwUGJXb1Y5SXFVYzhZcUtuSjRtNzdrY1dTWE5zYnhkY3VsUytFVjcySnFzaU1kaVVRSE9iOGlKZXVqcG5xaWNSVzhEeGxEalB3UndTU1Jpd2ZrdXhDc0sxQSs5a2VTYWJuSUZ3MGlwUDNqK0tXTzRramUvdysxVFdpZXh2ai9iNkdSR083bTk2R2FZSkx2RHk4Tks0Y2JIUHk3NkdFdXVLV0V1MFU5S2ZObXYxVnFjOU9kVzM5cGM4R1hpczRlOHpxNzNQaG9jT0tEbC9IbmY5Ynd1QnhxMnhhTGFqc2xUVXpRT2c2U01jS0dTeUNKUkRJZ3BCVGRXRndVSzlzMjBCVkxkckdYVUl1YlpiRGI0R1BqTFFFWmJFTVJCQ2pkVGxhSGZZSUFFKzNuSm9aYUNCU2gwY0tuRkdXVWZXdHM4SUFhdVpNeVRLVTRvOHJYMFFrRUYzK2VJMm9sWklaT3R2YTJUYWhiQ2tLajlDalNEWlZqMFhmRFRNMmdWVVJRU2NVaXFQenlSTmV3Qm1TdkdCeWxXKzJqajRXSVNmeEZtNkJLYTY2TjMxUytBMk1HMm9Bb2s0bWdxR1lGZmRzSXV3cUpkVG9PYWRCM24zeTFwOGgwcTNmb0FONTVKMEg5QjdoQ3BKVy9veFdjKzFXajE5dUpLN2tRSm03R0NTWHFvU1VCVzVOZ3F0UTV0MFY1Mi9udzdsNjhWQnFMcVlxWjY5c1VOcTRpUVZyMU4wbmVYelJienNVblBudTIvdmNsWGNUMGZiR3NQcnd4VGI2aGR5NTVXdDVmOC9OczMxVjVpeW43VkUxVlZFMzlhejM3OTBzYkhyY2tGemUvYmh6eVlYMG5meE91TGp6QXE1K0xmL2w0N1VvQkpBZUM5T2toQWN3b2ErYUhDdmVRbWk5cDh1TTN2b3hhWTV6Vnc3NnNoZlFscmh1dGpNbDB2QXN6QnRFV1ByL0NCRHlyWk1jU0pNVEpWOXR0aXgyUzhIZ0h0cTJwbnVUZmpMQjZFdUt0d0NnMnNyWWhMRHJqYXd0U3ZNMFZFdTBwSVgwbXZhMkFaWFlnU3dubjc5Zi8wbVRNRHgrMUM4TEo0Qk9Gbm5aTm9pQUFGeEtEajFzTFpNYU5lWkJ3dyt1ODhSWWZOREN3WTNYNExsUGNsSUdFY2Q1WWNMc3RQdXkwV0R0VDVIYkdoZFNucThyRGZpM2M1MTZzREdqcVdvSlpIVWVFejlrVHI5c1p6Q3VyamxDUUpteHFHZjAyM3BJOW03eTltR2JPc21Ea05tdWJhTDdBVDl2Y21YV0l1cEVxaUFIWWI2VlhCQWdtVmR3ZTE3NGYxQU0rRkNjMkZxQ0VseXNpK2ZJSmQyVkJpYjdBa2lleTZVdmZZRTZYb2xNdFBiN3JiZ0VUcmRFazVnV2J1QitDZ0FKc2x6UFNTakZVM0lvYnVTRUpOS2pHVVdQR2lwWXNSdER4M3ZnZVFsVEwwQ01PckxPTHhUaTdMU1A2c25ia2V6amZNc1dLWkNBalpLdVRCNytYbVcybWZMU1puRndBd1lqVjVOeVVxdlhQU1UzTWdab3dyVERXNk9BTjIzaXdYc2E4VkJib096NHhnRUhodDRWWkl4UnpVd2pYUnIwVnhuU1d5SHpJcmJkY2FKV0RpT0ZHZVlBZUNKWWpyQ0FLanRjT2l6Y3RSYUlYcFhkUnYzQ1d5Znl5WVpJU2pCU29tVHowY1J3WUdVOHVOMkxaQVE2N2tZU01nbGdIeTlXaXlkRTVDQ2dmRjJMWmtDTzBsNlhkUDlRMjE3ajc1UEN6a1VZYVlZakNlYi9SQTQ2NGNSNG1VdUlPRlhBcnZXZ29BU1RCRTZuRUZsOFhUM052QTBveTdTWnA2citGWEYySzU1YytvTGdIbHMxdkt6NC8vTjFZdHd1MVhKcW02SHZWeUpVK2Y1U0RkRitPd1ZOdTBHU1BEQWRDanNpMzdJTzA5VnJGZG13dXpCMjRkMy93NUxORTFvNmlUMDRtakIyOFNpdGREU0VVeFRmaEd3YVNmL0dMUmZRMzM2azc0RVFmNDVaZ0EyV1dDTzV2THNQc0dmcXd4QzVyL2JyNGNocTc4b01jT2QzKzBmem9IL1RtUW93eGVyWE1CZmV0S3NKeTh4M1NXSG5FZXhJYU9ZQjFFV3F3aUdCN05ENkhPQWRNbDZHT2FBVDFHN1RJdlFwYnhTTUpwb01CejZKM1RwUzlUa1pmd2VhVDQ4Rm9sb1pZN1FldzRrYVZCRnRJcko3bWt4dG9YTnY4cFVvNkluRUtnQjVpdUprayt0YzZRT3FKbEtac2ZaTElPUWNxdUlaZFFkWHc2Qzl4eERSWGltU1laMVpJOC9WaWFIV0ZmN0toUEZVejZNdXEvVyt0M2d0OXFOeFMrbFV2TUg2a0Q3VXNHQm5IY09ZN3ZBYkxWSEgyazFjbElCcDc2UStNdEQzOWpkOFlhT2J0RWNiQzQvMGkzalBmTjNxcjI1elJJRUlPQkFpN3lIWHBKWEpZS1FIOVBzK0JHK1Uzb21jL0UwYnRwTHU4Z25ySENxekExTFhoN25lY1hCMTlhcFBaWm5yZ254YUVOaDhPUW9SdjBub3Z2RDJiUTZoMjd2dWlkS2dVUWI2NjhkbldveUpZZkZlNnZXeW43Y05LTHI5RG0wU09jRVFJR0ErcXlJK0l1RWt1dHcvMjZoeGZVUjRJeEdnbzA4dEhMSHRPdmI4STByK2pYQzBWVmRxNm51MGxKekExWDgzd0h0UjVGdTBoWVhJU2E3OGVyYWNoNXErd2g1UXRKaWszc1l6NHdrL3NranpSMnAwaVVYMkdpS3FuT0J6THc2aVMzbExRVDYycE8rRlowWHpHbGZxZ2xTVjN2TEk3RmJHTWVuWk9lbjhTaEJ1NG4yVHVFd0hqOWc1dmVpWHAwN3lTOHordkt4RmFLaWIvSFVtaGgwYm5pZytrYTRDQ3BHangwakJsMjl5L05pMlJtWHplbjRHQUNkUy91eUIxMWhjN1F5WC8rdXA4elRkaUNLT3lXeUcrUTZQNUUvbzZpd0lxcmRnZUQ5SURXQWNzblJ3SHViVlNOUU1EMWZ3NkpMZ25yN25HUTI0Y2dORlF0UHliT1BWUWlSZ1MzNmxVRmh6dEdKdGRzakxUWUdmUmVlSGM5aW9HQ0Q5amIyRlpHQ28vNURJbjkzc1IyYlJnRmRJY1hleEZGSUVzREtrWmtVRnJ5VTdBOXBQcS9OT201NUxXemNZTlA4cGplY3d0T1dWWlZTWkcvSEk2cXU2ajVaZFhYc2FuMHdmOGNJNTcwV1RQZ3Z2ODc4OTBOYjQ1VmFhU2RVWjQwazNFYjNRUFcwL3owb0xkTnYwM3N2dE1ocnYwL2p6NTloN25rQTZocmY2R25PNm80eHZlTVhDaFBwaXVpdDFCNmd1VHg3amljblI0OXJoZndVQWVaS1BFQWg2Ylk1Q3dMSGRaaTBDanlhckNmb2JDSHBUR1dCM0MyKzZxSmRuWTFBYkh1UjQzdmFrOENrT0t0TkNVOHJQa01VR3R6OFF0dDlXM0VmTnQ0S0F2ODlkUERnY2VqS3htWXgreTRqQW1IWmlIVmhlcm0vZjFaOFExamZJRFRZRW8wRG43TEY3Y0Z1N1lvNVhFVGdpOTg4YzUybVpra2lTQjlUMkFySFBwNWh3YzNjNXFqRWZEbzQ3R2tLckIvYkxaOGdSRmtDYUVLSXIvWW9aYXY3VXd5T3Jrd2Z0MHoxZXJFWDZJb1dHdlVpb2dOV0VINENvYWdvU3YrR0RQdjViVWRkV0w5bEt6a3FjZTZrVGNGT3hzb2JOMjBQSVEveEdHNXJJcVVMSGhTNXExeUZEYWx2a1VrbVQzRFlOOEh4NllFTjZ1UXNocmppRytJZDdnMk5oYXQ1UFJQRHN2N1ZCU3ZkNVNYTE9hSWI0bnFMQWowc2ZlRmd0KzZtQjNpczNudVFlMXhKRlBpYkpxVFBuK3pvMGI2cTVoc0xwck1HT09PV0s0a0FpNWFXYnVwdVFDUlFQS1M1OENTZFBBaER0YWtKYkhTOFd2UEJvOTlDeUY0UXU0NzVCaFFKMW5RQ21PandUNDJUVEU2ZnV1amlzVWxITFA0RkpTWDdMemZkYmx0OXZsbERnNEh4eU5oeG1yTjdkc1lqQ2dodTlYQXYyV1E3RjVJWVY2YTVvaGxpY25WbEhqY3ZQRTJWVjJHWWtlaHdHZEN1K284NWowVmZlMC9OdjU2VUNKR2FPc3FlQlg3Vk1XMUNSUUVDMzlXOWxWY0xERm5MYThwK0JMNnQvN2c0VUZoVzl1cmlGOTVDbUFqNzF5enBZVTVBYkl6V1BHc2VBYVdoUUdlMzJJRXVUQW9WS2N0UkNvMVpBZyt4bWR0a1I4NENPMlZGSlZlbUp3TjlJMVA2ZDN3V21KaXkwa2tpcEkwM3BPV3c3eDRDQjRnTHZiYjdJZ25wUzAxQkdtclRuYm1FYzNwdEE1SkM5U1VsSXFGL21GZXk2U0RYNnd4b2I4ZlNqWEU5NlZObjh3L1JFTU9jYkt1M0FRRmJETHpnUDFYZTBXYnpqUzIwd05hWDVibVhwRGRzU1ZoSjkyaHJvbUF3d1drK21MZHh6K2dRK2dpandtbVROVm1EYWZFU3EwZHRQeWZYNXI4MnE5U2VQOGkrVFlPdjBQQVg5MVJzRHhhK202TTE0RFk3YnRCeUJvV1JWN056UXB0azRwcmI4WFdzOUE1ajJIbm5wcytuUmVCN0JuOHhRT3pwaHZUYkx5NlpwRllDMFNZVVp6NmNwOHJSZDdNbnhlZCtxLzUwUXJKSE5uNFl5S1VyWnBKSktWbFp5emNVbFNZbTFJcnVyMWpZazZHMTQ1eERlQWw2bTg2SE1nT215Q0NmZnB2S3lCZ252UnZpdHpnaUFnbVh1UEJJRjBrNEltRHpQTkJTckMxMVl3YXhnb042OFFCSVdMQUtFUHVueEUvMnFZZURybjVxK2FiVGtqWmliQll0c09VZXVqdXI4VHB0aE1wZ1o4aWFRcUN3aFhDVGVZTEpTMEZzT2UyMWhBZWxxUDdoU2FCQkgwL3ltc1p3MnI3blRaU3BvTU5JTVdnNWUrZzA1MDZiL3NyaFlCL04wUWJRMnNxdXBya25ucGFLZU14dy9ycTEvSEFtenkvajMySVRzdy9NZFRZYzFmZ0hIOTdoOEQxMXE0K2NmaElKQ2FFVVVqeTRoTWNOQ2ordlJCeEtuTTNDbGNsZWdSR3IxVWkzZCtZcGJidE1GMDd1bzVsLzdCaEsvbnYzUlo0Z3loSVZqOC90cHhLT2hmSE9CSnZnYkNHMnp5M1JLbjRvOXhjamlvNVpJc0lPYTc0NW1UcHFRbHdjbVZsQll5VHoxNjdqSjErNW9PQjNYVzJ1SjUwRGZnQnAwZklwb1ZEbkI3dlUxc3lzQlcvMU5ndzhUbDZoY0YwVlArVVRVSGE2SUQwdit2TndTWmhkNmpHZm0rbXoycVhKcGZoUnFKa3RlcnZ0M0E0RzV3NkQvbFFacUo2aWVrcW5YK0R3UkFGUGRIcm9WRGM5YkY2YXp0bjNwc2d0R1dhUWdUbTMzWnNaUTQ2SzlmUkFaOXhzeUIvSlVLeENETk1OaytKelV6cHJQY09YYWVla1dUa1VFWExSMGJ3dG51dWNlZjZHeXg3WitvN0E0T3RmSnppOWx2QW9IYlBmek82TjlGRnRocVI3aTlPUVpwMG1aT0E0QnVSaEhyMzlKRFB3ZHVDVTRPQVlnZ003MnVPMWRqcmJyQ1VvUFoxdk9zVnN1akwxY2dxZ0ordVJEVG5EL1BRNCt0RDNVMzNqMG1CS2RHRUI1aVRHVG5nSXVibTE2K1V5V2h2SzJVSnM3ZzRCbEVDK3FTaFBhY3MxcGZKZ0pjV0Y0THBRbE1IeVVzMXkydE56YUhOUHZFNGpuTWsvRGswb29zV0UwMHlzYWFrMEM4clFoRXhMWmdJcWcyd1JzSU5oUUk5M0ZlakRaTzF0cmt3dk5SSUJERy9BRkord2VYTzNDRVNtcFdMWjF0NTRjLzN5VGtXb05NdmVrT1QrT1I1SlRRS091TEpPN0RIVGd5d0daenAvM1F6RFFoR3FaeFF3djVwQzRWeUVnMlhyT0VZTXNBQVRTYWU4TFhWVGtYYXVaQWFMQmhIRTVjejFrbTlJMGhsQnFXY1RMLzVzTUVpMXRTMlI1czNUcXlVblQzK3p5aWRxT2VpcnE1by83eEdnU1B4bXlBajJGcUtyL0JaK0w4ZEl2cUt0czdyaTU4VE1KTWZveE43SHQrTUp6QkI4cTdhZjJQNjgxdzIvQURFTkVoUTJrK2tEOHlNTVRnaXlwencxZTBYUEdqZ0N5S3Z2Rk40OFdxODIyenUxWUgvRE4rZ0lsZUp5SG1CRGVVTWZhWk5LblNLNlRSZlFPSUNlRDc2S1FSa1pLY3o0NFBiamhwazdrZUJmWEdqb3J4QklIaXJKejJQb29TRlRjYkhKZTJ0NHZLS0h4QXpCMGpsdDhWb0hSaFhJeTZRckVPdGZHaUdWMHVIVDZRc1M2Y3pDUDFQQjdDV0ErcDkyQlpjMVFaUlplc2ovU0JFS2lLUE5aTGtEWTI0bnZ0UG9FSERwRGl4N0VkZ1MyS0sxelNnOEVHeEE4ZDA0WDl1UXpMZ0Z3SC96KzdmeHNuUHZxN3hLZjg2cGJpMVF6aFExYzNlZTd1TEQ0QkIzNEVhTCt4N0c2Y1JCS0Fvb05JcGt2TVJFTGNVZ2RxQ0g1K1J4MkFlbHd1cHZMNVJ5bWR1Sit1emxCbDcwUmpKc0Z0aFh1MExPTTNXanNzS1VFaWZ0ckV1ekpRZ0ZXaTAvMFlyYndDUG5oODRJY0t3R1ArV3Q4SFRtTWJmcGNmZll0UlAvMTdTTXRvMGJueW0vOGVqYU1LUEVOQ2R3ckphVkd0VmVlbnFCb2JDUVhteml2T05ibjU0V0dnb0tsYkczUmVHdGxwUzc2N3pUcExycElaRWhoVkxKaHA2TVFrNjhkR1ZPNE0ya0xEa2Y0bU9yY2o2N3RNcnhsTWJ6YkNUU3VQcnhpbHVXQXRyVGxZN0xxeDBPeDcwajhPNU43NVRza1czYjNWTkZhOC9MUzdpNXBrV1dQelp6Tm9XbmZ1MngzTnEyL0VyViswdXYzVDlEd3lCQ01ZUEVyOG16c3Q2emFuN2ZyQmF3WUtFWCtOM1AyUlNySmJKWEFBUitrMTgrUlVxN1dDSWxVbW44NEtiUDF6Mm1oOWJOZE5vdXFNd2tNa0cvWFo0dHVWL0IxNlhWbmV5aTFTbG1qL2Nib0RqZFBIOWd6aGk0U1p0ZytTV1hjdTFiZ2lKUXhlczh6QWx1aU14cW0wc2RTa0gxN3Z6bmtqMnk0K2VBbkdqS1lKZW5yc0hFZmsrVmlUV0N0Nml1dUc3REIwU3FnOWdhamlFNEkwbXRlcTBBRUpTTmxxeFd6RzQzNk9ZYk9vSy9tRHF0UGtWTkgwS0tZZHlHOGF1SHdHZ1d3YkRRejcrRlRmcWQyQnptY3diTW0rK1NEdi9kV2VlbnBaU1VGZWIrYytmdU5OL2d3aHFWSmdTNjVzZFNLSWp6ZVNsMldBUUM5N1FGbVc0ZWRQMHp0NzZtaU5xRW85NzFuUkozc0J0anBzQnpzT0Q4QlFVMy91eVdFN2ZwUVpjREJQVlBIWUVHNStOM3N5UDR0bXhuMlNmZGcrWVN2eTYvNEtCQkdydmgyOFYwMGpuNHVGYVh3TWtRNjcrTjRvaTV0dEdZY3ZWMXNQbVlPNVRLamtEOHRHRy9UTkMyUG9ER1JaaFdSL2lLV1FaSnN0ZHNoVnJ4UzNqYS96TThYempQMUZ6WStPcVhOQnM3NUk1TUJtdEpkRngrNHlNMU9VNUlQVFEyYnlUSG9Rajdyc0xCb2VwZjNMeEhkNWZSaEtaQVVSc0Flb1dpOVNBZm9BV2FoRFNaTzMxYmZpUTdsUThxR0hQNU9ibjhSRmU3ejNlcm84T1Vxa0tWbnhHY0RvZFFOVTBWQmI5NkdSS3orcURYVXU1YTVqYloydlpFdGF1ZFMyY1p2R25nK2owMk5jM2ZnbWM0S1V6Rk1uSStBMnpZaE1mOXRSSmFBK05KRFk5Q3dSemFKck9sS1poM0I4ODFUSStOcnNYdzZkMGRzK3JtNTNJZFJoWTQ1cVJSMDl4RUtBcDh5ZHJBaUhnd3Y1U3lwdFZCU2lxWUh1c0xuZ1JBc1docXZVdXZuTmFJR0VGTmdGczhvcFlzZEVHOGtSRCtEazhTVVcvczR5M0E5MkNNSWMzSWJBbi9VRHEzZ24wY2M4KytubllZb1d4SU8wY1dwenE1ZkNYSENWcU5XSlNvSXlvckNGMGlwQ0JZR3cwMFFZMW5WS2R2bDFhZXlLQmk0UjMrMVlyMGVkNE5ZenZkdUdlVGtwRDIvUUw0cjErRHF6K00yajJ6c3ZTRU84MkpVbEFVZTZiTmVRR1BoMUJnb283NFIxTVdHNXA0NFBhSkNsSXNZSzF4S2lsZ0tQbmRIaGowTEE2UHYzTWdNVFMyVExPNENXeXpDRHBIVGpDTWxnZSs3VnE0NytkWDVhb3VtbzVHS3BRU1h0WnVMMG5PZi9tQ3oxTzhESjhXV1I4STIxQlc4dmdGV1BLaytKR3lsa21QQURXS2hoQUxmVjByTkIydjlTNWtJVEF1cmJEWndFaW4wWmxlbm5QOTVJdjFPaVJrenMzMFNyeFJPM2orUEtKWXQzRHYrVkthSUkrd1BLSUk5NTBQcjkvZ3RRRGJqVEdITkVQRGhDNVJKSzdsZUQ0a3FXYmp1Y0NVMnBxZC8rTWRkdko1aGhtTHZzS3N1b1pHTUEyVVJOdTRMcXNsd1BpcFI5UnFCcllHUTZnR3VJMXIxMmlHbUtTelA1TkhPVkkrOHF5eE1ZdmlLaWdrNldzeHhtUnRuV0Yvd1haakxsZlhGdFhISnJVY1lIcDhmYVNBS3V6L1B0cTY4RmJmcFMrdU0vR2VSUjkyL2wydzZJS1hQaTZtQWlJb0FsQVpCSkFDdzBMUjY5Sm14MGxoWnRiZ3o4ZC90L0lzY2J1MnlaTGFGR3hBc2JVWStaZW9ZblBLN09sZ2xqZmsrODNyNGZzNTNoUmhkS1B3SmhTOGRuSktBNlZGR2N0Ykp6K3RmVVhwcWJMemVtdWUvc2xlVHVqa1dNTW1qM2tQdkNMWjRpT1pzSWtiY0lOQmc3UHV0UExLUVRrR28zd29TRVE0UXZHSmdoaW9yK2ZWZlpOREJ0MU5COGlpRFdUSTFxbHZhQ05Vek9VV3dJWUliODEyQm8zMG5GMnBpakNDaVhobWRuS0pKNWRWaE50d0cxOXBrNzBoTHJraTdRMXltT1VBNTAyTStuS0dNZW5NOG1FWFZ5aE1vTFJDa3F1MndZT3lVOEVicTNUSlIzMTJFbEx0SWdnNkVZaThtcmw3ZitJUkpVazZrNmh6Z21QZlo5WFpJV1d4YkZFbmFVZmxLdHc0U0VYd1VjeFpqbm0xTjdhRFZDaHpSbmZqYS9PUG5QVWFteGR5N3k3U1ZHVlkxS2pHbVdOMGZBMWgxemFaaW9qdFRIWm1XVm1PSzh5b3pHbWNMZGpkOUQwYmMyakhIQTNFOXM1aVQyOHZaSVFHMDBGSEkyaFBoN1FSSkE4SnovcWpTaVJDUWdWWHI4UjBPWXJiTWlvZzBPc3FPNTdmQmZnZXlwdk85amZNQU5uWUhFK3o1UUtMNU5CTHBBU0FVQkd0azNWMWV0elg1a3oyeXc4S0x1NUxxV2FJMUIrV3Q5N1o0aWtJb04vT2pqVE1aUVR3U3RyazFLQWsvOWwxM2svai9WTUR6RkVVenFjeWVnRE9LODg2aEFWdzA2amJQQXRHa1ZHWkxqcjNsUWxrallma2VuUjJ3YlFVRUFKQ1drTG5rS3dMS1dhKzE1M3Zwb2pDOFNIK0tCK0dBZjd3MEpiTmg5VEpmeTgrTzZEVzlNemtIT2tMTFBsNlE0cXJNS0RRZWZUdDNlMTd1NlpQcTNBYUtnd1F1cGo1MDVPeTNSYlpQaGYyK0pOMk5ONGoyMmZtZW01UmtndmYwNDFZY09uQ1F1S2dYSFp3T3F2QTlQa0JVeDVudXBzbjN5V0pxSWpQcUp4TG03L2w3QmJjbThQUEFNTzV5eEsxaHc5azhEbmVmVXZ0bDFLOW85VlNRdGkxTXJVdm9meUhyaU5zL2Y2REFxbUZYdWswZys2OE1QeVpDMVVxTFdLU2VpOVFLTmw5d3hvZmtnOXlJN2JCNGU5UGVtWnBWTWNEcFN3bnJPbVFEeXpJQzM4VlBBRGtIKzIyWXh3anE3bzR3RWViR3lDRlRWU09sV2FvZnNBVzVRRFp3REFFZGhMby9ETUdVVE1tNXE0eTlRV0dKa2xscHhucEh1S3NYNE1OdjFhNk84MU40cWdURWxIZHAzVEZIdlRvNXdXQlZnMmJ0N3I2S0NuSUJqSXBYRzZCUS9FZWRpcVZUcEZhSi8rOUxVaVdKdlNtOEVNTlJNcWVYOE54eitwZmNpdEREcnBtZmZ5cElQcDdheWVkSVRnZ2RGQ2tlaHd1WlVBV0F4MC9qeFFVUEdjeDNwK244Tk41Wm5qZEVxWUxDUllIOVBwM0VpSjJHOU9WZFVkdFo4dXBNT2FEVWhMczVhNGsxZ1hNWmlVRWlLTTB2R1k5TzhoYUw1dm5NZGQ4MVlNMWpjV01vM2wwU0RzUXVZTDQ0YU1BeEhNVW1ON0VTV0g3aEM5eVRwaUdJcWJBYndnOXB1QWtGc1VWcytLS25ZTys3M3NsTFVyaS9WYXN6ZGNrRTcvYUdIK3YyNHZnd2h5cGorbXZRZFc1N3BmbVUrbmMrc2lDb0dUdXI3Ymwrd292eDF5SFdLbjlTY0lyODZ0WFhORE45RkZMNFpnKzBLSzNIM1E2RFNNUi9XZU5kVW5PVTVnS0ZXRlBzMXBJUlJrVXlKcENuTW5telVOUWZtSHVhWENhcGxxbkhFYnFhc0hjNy9EdjV0YURCdFdtWG1IV0NPcThsNktLNFR1enpINld2UDRaY3hLTi9vMUhaOTlmS0ZKSVdLTnl2ZS83SUwxYlN4UWR6UXprdmR3bVVoRUNCb3o1UFM4MFRCVXRvNFlkRUF5RFk2MkZVUVhSaThVcUNYUFFFZStqOTMycWN6TzVzdUpseWF3dUw1NG8vdGhhZ01iNnI2QXF2ekUxbnNlb28rOHdpbmJ2ZnRTWjV1a3hURzZndSt1N0VJOTQ4WmxiNjlSNjlYdU01Qkg4VmtkSDN5ZXMxU1NUU3ZHUjcxdnBxT3MwMUhvampvcWR3bG5iWkRNVjArVmNYV3l2MkFtcDJwNjBWdloxTk81ZmxrNUZsYTdtVWFsa2JxNVdCcjMzckhITDhXN0dFakMzb3RvWjJkNGx6WnJsUWc4c3didlpzN3FueXBVQkZmSEp1UTRJcEtNVWxZN0Zsc2tiaWxwNHZ4K01jb2ZvOFArRE0rS3FmaCtTdXMwaGRiMzVxYjE5QmF4amM5eWtRNzhQb2VGZlFQOGZScUU3Zmd0VDViY1VOY2hsNkkxVXllU3JzRTJzc1laUndhNHZ3ZTFKeWVROGoxZG82TmNlQW5aVHVhbjV4aTF3K1V3R0RpQjBjYlp0WTBNRVVvSXJDSTJ6NUQwRDlVemk4REowUC8xLzhpSEZmKytkVytEU3pRd3lBRjB6WDdPTUVjcmZ5aDhyMG1uQ2JGeG1Ya00xUWtVZ2hVZm9OYk5tL2NWRTM4elNFdGFhdXBzRjFULytzZVorUHRjWEtXSVRjYk95cmZVTm9ObWl3QjMvNmpFNEd5UjUwOGVDWm5LUVQwLzNTK0s2aFUzN1Zzd3hsOVhGVU41OWVYUGVXNk5TcWthTy9pWDZhOTEzOHU5WHFFMEgwbVdWQzFrRjlGZzZLLzhPVzU5THpvVm1wSGw1VVZpUk9jbWhKcnRQYnRtYUd1OTBIM3VSbk01dFdiaXIzQ25YQkF4YmUwM2NLdlQ2V01NQXlSRmJ0elBweVZON2FoUmtjWHB6WmcydkpyTTV2WmhVMVBSTVRhWXp0MHRkM1dRdEZuVzVQZUNCMHdON0FIcnRNSEIxc3BWZmIzZ2hlZ0Y5d0ZLdTBZNUZLdW8rL2FRUDBnazBDbnhnUGQ4YWtoR2FZVWNGUXBxdmZtejZaakxvNDIvdlZ3WHpieVdrK0pRWFRYbk9lbTdlc3VWMlNzSXRmckNxNzh6SGo2bExpL2JmemFBSWQvUkhkUDZ6aStCaTBEQWE5cDQ5dWZXdUpLeng0NW0rcXV5UjNxWEZHbHRRRUlKUUU0YUxnN3hMSXUwOHVmN1REaGtTWS9LZlhQcE0zMy8rQnRqS0tPODZrUmdpYjdidHIrNmNaNDZVWkFSZzhtMTFneXBmTFhEd0hFRHJxeHFzMnliSEFpUVo1c2g1bmRYN3Q3MUJvRmprWFlkR3Z2cFFHK3RaTGVZaGkyY3pUYi9zblpIZUh5UHZUSHFRN21tK3I5bW4raTU0NExsMG15S2x2Q0FySTllc1QwOWlxaXhBQ3hza0RmREJjQ2YvdlpkNDB4amYxcWhoYTVURjJVSnpDaTRmMmpPV21aN0FTSzFQOThTUEU4UFVuSjkvTG5ndlRyQlB3UVkyTi8yajlwV1ZsZVFwTlc0K0hyOUpGN0ZHY2lKWHFRRlZQNGxmTnVIaDc2dGl2R29DSWlUNnp1cU82aXJOakNIcEs4ZGdXOVdDMllIVGV3bUx4V3hwRzNTOGtnN04wRlNadHQ0cDBVY0UrRGFVaFZkR2VrK2xuYTdCcU5IeDVJWmdTMnJRMEVEZzBDZ3lQQ29kR0FwcURnczJ5SGs2amRHQTNrakZKV2JmSXJUd3NIOFVmRzMyUitNREN5TlVWWVpJL2p4bjdTSUFJVVFLOFZrNlozMFdMajhvdWNBVEFLMlE0T1U3NWZPbzFIL0gyM3RwTU1EaWF0amsvaTRJOWJ1dHJLNk9EZElQcnpyMHV5VUEwbnJieC8rbFVobktFMEl0MERhMWdrVmdiYzduYnZrck94clY0KzRxUUNzY2RNcVNyWGRhUkpjVXF2dTF3ZzNiV00wU3J2eC9FelM2R0dVYWpseElUU3pveHRINmdLSXk5ZjFRTmpraVNuU243bUV3YXVPeUF2VXJyWmtZUHlLWk81VGxOL3NUVG1NOHFvZENOd2FmdWY3cXN5RW1JYmUyRXJscERZVzhEWXc5MUVSMzBRakpEcjFVcHA1U1ZJL2kzUVdwaVJlT0dKaW9tRTZiK0orY0sySGtocVRrelI3aWE0VDZVYk4wMmlKUjdnMUFkMVNnVGU3ZXJzeTN0MWNoVlh1UTlUY3JQKytiS1MydTZoZTVjVnJTYkRNZkpIbGFnLzVMZFRwUlZEbmdoakt5aStjbGVsNHUvWUZEVS9EVmFjNXhHVld6ckptMm5KNzJQYnZhL1Y4cnA5V0dmSUloM3NuN1BxbWxTS1ZiNHJQVXZNeG83UzEwZDhmZVRNZkk3VlczM3pKZHZkMXZhVC85NmFnNkszNkxWRXFwL1dSZnNqZUNmVklib3B6Mmo3Wmw5Wlo2Wm9sRTVLNVM5RDIwbEVraStRb2E1NGZTeTNxUG9tNldKU1E1aE5keWJHT1hhL2swY3MxR2Rnc2FpY3BhV1RxejcvT2Q2Q2w0UkREVkp5VkZhWGI0d0F6b3c4Y0h1N3I1Wm9tWmoxN1pQVlZ3NU1nVlMzZlg0UEZoZXN6RVBjL1pnWjdaUFFBcHkzYVREeFRpN2ZWTmlEMFBHdm42WGR6SER3eklKcHdtWkxmMFdBd0h6NjM5aWdFSm9YTGZoclg5QThSZExtdFlNekJ2d1pJcnQ4TDZQTFY0Mk50TUFHMWh6UkhKRVJqUkgvalQxcEJrcmM2elFpeEdDcWZ6TDljdG1EZVAzTWJtM2lYZUd0WnU2QnRmTFBhcXdTTmxFdytaak5VYVEra0ZyMXppK2Q0S0tCaDJpWCsxOE51UlpqV0RQVGtBQmo3ZXFnVUprQm50dTdjUDU0VjVWL0I3dlhJenRjUGJkeHZ0c2dCZWVlNnRqMmhiVmFlS3FoWUUrNXJTMVY5eXZreFgrNWxNVVZWMVhtSXhwSDJ6ZDJTVTFVNm5zQnBBMzhCbzRFT1R6bGFtZXhnNFN0L0FvTkRiV2FQejluMmtJbUkwZEJQckJXUENUeVEvd3F0czdVQS9lTWFXK2ptR0JWRGh6WTlEZjl5MWw5SDA3bnZQYU9CRFhabE5aM29ZT0RvQSt1dFpZNHRab3lON3Y2RkJzYmpPSzZySzVLZnFUS2dmMS9ORzBRdC8rb2pGQ0dVc2FkRmdkN2RKeXh5WkVCd3QwQmI4Skp2NFd3K1NsK0c5QTl5YTFUNE5OZ3QvOHhFNnhXcllyMUJZVW4yUEN0VFlua042dWdjWEpZMmRLaGZwRVc1d3BudXdQV3NycFArUTVjTk5TTjJKMzJGUkloYnhicTlSeWcxbUtpNWdsb1diVml3MURuMjlNcE9SdWJMU2VYMnZmMzZ5blNtZ1U4MXlnM0pOc0h1S2g0WE1aL1A1U01RU0V2YjJQbU96aG13TEZtMVM1ZXdCZTBHRlhFVmxQNnVXcGtsVHRRYmlsT1ZDM0RBQ2QrNGNvZHZWUWRicXNKSDU4L0hGZE44bDhDVDAxRDV3MFp1cERGR0pOU1E4WWJqZmlJU3NJbGFGSUF2Tk1jdkNRNndsSWdiVjdLSi9VQTE3KzBUbUs1Y2oyZmcxRENKUXdFZStsZGdhSjdhSUxGb3ZuTlpKSE1uNGlIL2dJZjlzZURqZGZYN3JMR0JkWHBac2hWdXVpRk9XZ1dYejNkT0hONUVSbHNlZXJKYW1lVmdNRG93K2ppT3VTZTVKRUNCQXI3Y25KeTR6enZpWlNsRHBVK256end1M3NRVDQ4MW5ZOUxOWTVCSXczaVNiK2IrRkM0SGV0M0ljKzhvTS9LVnNnenk5MDZLNmI2TkFLR1BTSDhJa2ZxVTRDSkhMSTdNZWZ6WjdoOTUrc2RYeEpGSXVSNExFbFZESjR3SG9QOGxndlU5dWkweXpSTXhyYnRac1o3TzJhYXJybUpjV0hXNXVTMzZQaHIxbm5QamgyakVWQ3RrbHAwc3lrVWowKy9WVUg2TDJqRUFnRWpINEZJclA4NTNlREk0MkhUL0VwelhKTWsyZnJPK2RiclZiSEJhSTJHZk4yQTJ0Q0g0TlI0U0tuL3o2KzN1L3lFMmJtbTdwN1o5QlBEaTJOU2g4NDNkdHRuVEpaT0J6cCtlQks5Ry9UNTloZjNsalk5OW9EajhyTUFiclpzVTcrNmdlckZUcVlyM1d1Z2o5REVaMFczWGMrdXJZWVZGb2lHaU9NYkpOd09kNE5mVUwrRVdvTWVGZko1ZTFxbmdkekNYUFdieitHRUhEaGlTVEEvMElrY0kxa1JhMmdJKzREUXFSc1BBUjFhaGcxd0FZUUR6WGhML1Y4TFlYR2pZcG1aeEZsYTEyN2FMQXpNeld0UjV5WDFKUHBnVnFrSjRlbi9kblQwL3pxUExZNGZIblp3dkREUEdUT01aVEZHUkU1dWF1YU16TWlLMllhYmUweXVEVkdhTkhNNlJVZkQxR0VjTGl6TWpjbkRwZE9yMHVjaURlV2RLWHlmN1FLcnVXYnRDYy8zTmVCcVUrT2ZFVXg0NUZZMVAyVkRMYUkzaTBRKzJXMjdQU0VrV3YwMWc3VDNFeVBVVEozbnBHclVkb2JCRG56dSsxcXlsNGJKbzh0OG1xa2paOXlNaGNSaU4xNnF6dDlRbWN5a0QxN3dRcElEeFZnY1VNYVhGVVRqYTR4K1luVFNCTFVKZmNCdFIyWFhEZCt4ZnZ4M3poSStjdkpKUHFkV0RkZWpxN01VUnRpYWxER2lRTjlZT0o1NjhWOEJFcjRmU2wrRFpVME9nbmNjUXFuVEtHOE5EYlV3Uk9tbHkvZ3hzUCsxUjRiUzcyQzB5L2s1YWZIaGhVUEZGcE5HanovRzdmbGcvYXh0YzRDWGpuazZsUWFVb3RMNjhlelBPbllGcFZuVmx4MXNVVS9UekRMYVdxTHNXa3JQbjZJSjcwRnhNUmJoVEZ1Q1VmcHVCNzZLZ0NHTk1ITjhTYjJNRjB3THpDWkQ5RFVWSnQyRWFiRmNHdXdqNmNZT2dKQnV0VGk0a085c1BuVHM4ZkpIQ0pPQ3B1RTFkRUozM2Z0dlR1SUZqSXdOZDQ0Q2R4dHIvb21WQ0JLNFJIc3NZL0sveVZocDNFbVFOOU9nUjZVZVRacCtYWkZDK0lnTjc0Z1VVY3EzdG1FZEp3K0YwNVNEMGgxT0h0QWtuRGpuM2dqZGlKYnZ5cGtjMVlERnhjWWs4ZWZ3bjB0Q2lxcUkvV1J4MEUreGN6bUlzM2dnMnhxeklQMzYzWFJlMENOSHFYZzg3TTQ5Kzd5ODlqNFE0RDJMa0QvRmJId0dZYXdJNmR3T0RBV1FRaGZNRnowb0ZNcG1kMTlaa3pGRFBENSswOGRVMUQwUEJUT0NKVUpTZWFVdHgrNUxMQko4TmE5dXpDcXpJckVvZUZyREVsdTRka2xGZ3JrVW1jNUVFNVlXNW5NQmtNV1gzVCt1eVJPTWZOR0NLc3RyZE9yNzZ0ZDk4QjNKTlp1NVZjVXBWbXIvOHprSUhmZWJiTTYrQ0IvL3VrTEU1elZ4SVUrUnBKaXFuNjMyYWhxUG5IVFdFWGJuOWFRSldOYWJuTkZLdHpscExSdy9JZlhab3MrL1cvNTM5SlN4WHkzNDZHbDhlNXJFeklOR1lKemlUR0Jxa0hEekplQkVyUnh4NThBY1lIcWdKS2trUCt3VmMyVFlBeEIwUHJMazhrTWJuczhmQTFXbWVKbW1mS01lZG9aRFQ2T0U1VE10VXpGMGhxU1MvM1ZhNVVnNFo5SXBuc2ZPNzB2QlAwY2FSUklYbmJvY3ByeG4vbmx6eVpMdWRjdUJLYjVPbkdZa3ZGVGtTbFJRa1ZQaTNnd0lhYjQ0WHNPLytMMlM5d1c2K3JsSWc3bG1XS2w2VmVUZks3RTMyWGdZUVVoaFdEVUFaK2xVNkJLY0g1L2hrSVJaRk9EZitCN3NyaXVNZW5aSjVhSVhMT0xLTkNQN052S2x5OU91SC9OcElRVVhubkp2anlLcDAxK1JCODVaZTZKU3V2emtGY3gybmVpU3A5V2lyNGtVMi9tcDhMN2tSVU10NnpHSk9lVXpJRE02Rjl3enJGd3l1elMvaEJOWThjNlBudkNLOFNxOG8raGZ3dTNCRllxS1dXc1AyU0ZZK1VMQmtibTg5SlNFdlhZeHozSnRQcnk1bUpqY2huano5SHFmS2RycnlmeXp0clNrR3dvQ3A4SXNIVkl5SW1ZOG91RjFucVQwWi94Y012V3RWUk9aNUxuUUVyWk5HeXZXRWtjb0RxS3lZNkxDZm1aOGpxdmdGWmZwUUhIaDlVWnhZWmljeGZPaTdhRSt5dlRqUEVoS1VsMi9kdlIyWlVWbjZKTEo0cWxvUjk3aTBuTGw5OGQ1ajc3dUwxUnBlUUI5OUJLQyt6RFRQWnovWXpVbzNTRzdKVXYzaTZidjhrenMvVjBlUjhUK3FjeE9QL2pjMlV4cU14SGxvUHFWOFhlN0x6R1p2NUlBYkVZbys0Mi9ld09aUWcrZmVKb2cwd2VUYStVbzNqTENFa0xsd1haL1pjRjhlVE9IczZPZk9LMVNmNkhISHhDSllHcjZweXRpNjBjd0hKczl0cU90STZhdHBtZjhPSVZHWjcrdXA2MHF0M1kzTkxycWRmTDc3UHdsK1hCenlaNHJrNStxRHJsSC96NHVOV05hYzk4UTdka3lFSkhxV0p2TENuODk1ZDdFKy9wWEpveXJ6VFNhU1dpbWdnNWlCUEorVXBhYXcvWmg1S0g0UWJTQ1FXeG56K3kwdllydHJZdWs3UU5kM05yL0F6R0Y3cE92L0RyR25COCtNOTVnY3JhVHdTcldZc0d5UXJXSXlKRmw5VXAwbitndmtpNFEvSk0rTWsrMHBRZU40elhqRXB3L1lJM1lEaWlrRzNRVkFocUNEbm9Gc0NKWGswWmZETCt1ZE04WDlWWUI5ci90TldNTjBWdko5MTZ5bGhnd0Nobjg2ZVVkSU1MS29QT2JOeWRDZlkvWkJSUTZWSkpxRDB2VThlWGlwKytmb0NaNzQvMDFaYU5YdjNMb0NydUdtUHoxTysraUdmajVmLzdYOXRqNjZXa3VTaFlYYnYrajdVZVBMMnpsaklQa3BsRWd3cSs5aU1oWHQ1bmpVZit0NjlaNmIzdzcrbkRaMDZTZlYwZW5qY29tTlVCc0dzTy9xNVBTNmNUdWY3VHgvOTlHRFpTdU42VUpjM216Tkd5aFpFTkVkTzR0dHY4QVcwdExWOGIrdXFsZmVxaUFwQkJUaVowMWZxWHplSDdQcWJER1dUUEdwd2JYRE5VRi9aSDhreUZGdVA0VlZraVgrUnIyL1cxU2xGeWZXYnpWc3NaQ1dPYnNUd3paWjRoM2U0Q2FhdFNWdEhKcTJZT1NVdnJ0TmxIdHlWUGhNV1ZkKy9DaVJoK01Jenc4enkvY2pkLzVsU3BpN1ZIOHR5SHRueWtwUWNBWWVWYzFzWjZtenljWFBxWW9ublBwK3FiZjc3TGtBS2p2MkNMTllseXZDVjRvakZTRWlvWmpzTGQyaGxRVEd4STVzVU9SQjJSczZOMWlhaWVCVllmaGJDc3A5aEhRTy9pbFA0RnR6VHNmZjBYczVwQ3Y0REYzY0VsUVZjaTFrVnZRb3UwUzFOZ0pYRU9KMjJSTDlZM3hpNU5MZ2J3T3M3bmhvY09ITzE5ZjcvSlBHcWZMSUt3eDl6aC9hNEpqUVdiekp2U3Btc25GWXNpY2NyT3pEYnhwM1JROG1meUIxMDdDcWRBdnRkOUpGTFZFVXdQS0p0VGhtczREbkt0NmYxUmtjcy9zYmRWTnFNaUM3aXBNdVBlNE43UXJvUjhmVkdMWTArVGxSQ2FqdUVPZmVoeGJQUXR5eFhmUkhjWjJON0JmS1ZScGcwRGg1VzRwdS9Eb205cU93N3g1Ymc2ZmI2TDFZdytZZUdDOTkvVmRSWnJYYWlMUTR6dS9OWUhHbHFSdU9wREY3UW5qZXVkNzhNaDZiSVBYRGJGWFF4RnFNbjkvZmJSTytXeDBldzB6aGx3YS9sUXl6bWpQdDl2MUR4MHhoemU4NlQ5WkhoMCtkT0Fkc1BobUo3aWNMYlJkdkxid0VLdm5lZk0xeDJHTGN0Sms3UlMxMW55ZWFvOWtjN2NHOVR0TkZJa2owaHBUWE9EZTBObmFWWkhvMDF5aFBuQ1BuTUNqbjNjVjlZWHhpbGdiS0R5U05SenpiM05sQ0ZzN1pKTmI5TUovQUpvb25ERFJOT2lQeXJ5RDYrZml1YnNTTklrSjZNTXAyc2JUc3l2Zmc4UHBvYm5wOGZVNjM4dXJTcHVuNGtVRnZqWTJyWUtCSXcyendZeVViRDhhZ1ppTE4rTlVQZ0hwdkV2Q08zdDBQY3BzR3VZanoxMHU3U2FjN2N3d0Y4YTVXU29hd092SUEwdXQ0TkZhN2pEUVhSaURqZVMvZml5dEMzMDBTaWQ5bStjSitlS3oxSCtYd3NOeUZoUnNzckpyV0oxcFNreGZDclBSL290YlZHUnJMN0lueGl2M2pxWDRqdWFNeXMrazFzUWZYQVM3TmF0RnJqcFFjVzlEZHhabFZqaC9zaUZrK0lwVDFQTFRpbGpPZElzcWUxUm5uVVZ6ZnVzZE0zMGVhYitFa0djNGZHeTVrMko2VTRnbFB0MGNZVWlHZW9NT3JlR2lsdGFPYUtlRlRiRUUreHI2QmZpVlBRQk1ESXlEczA2VTNiY1lOKzB3bjJCTmZrKzJSRVRJY0VKWmZWakN6eThUTUw5Z3NycjFSOHJnMWxZRi9oRkNnb1BGRXdBVkgvMFVpaVI1clVSMUR4Y1Z3dVRjMTFLMWFzaXZzdU9ieUFteG1lSGluS3o2QUorQndUcElvcDltKzB0WWQ5THZDS1ZxR2JPUnIyTFVLS1NFUjhPLzFMOGZod0lEL01WZnZsZkdPWCtNd2tUdEpwR1lxeHJ5TytUd3IzWEtIUTIzS2xxWEo4SEtjU2ZVblNxVjFSaGw3WlFOZWxma0s1dHpweWVZQUNMeWRpRnNESVFLdm5TaVpyeDZjMGphY0ltbHE1NXNURUpNSStyek9iTTVRRDdHQy9LcDhNWlVSKytXZVNaeDNWZjFiOUNXVzd3ZGplNWx2RTJHYlFkRnNxVFhVenk1R2JYejVqTTY0OEFPbGkxL242RFQ1ZUVKK0hkVmZ4VW1zTnRaZlRjMmZWVUNIODRnZmNuaStaWExPMTc2L0gwSFFBU1dIdkdXYnJWYXJNUVJMczJlRnU1WnNEUEJNSmUxKzdneGdvOGZVd2Uxamd4VXV6bk40TmNETWQ3VWVUa3NsTzl4UjNNMENLaXA2eG1WZHFxa0RaejlXYTF2VVFXZVYzcEdUOXlGRTF2L0M3Z2ljQWtXZER2U29yWEpSZmE2Z1ZacWVIMm5GN2J1dllodWxNeUgvcUZlUzBEczFxSTlnSkVGQ1N3VytvWjZWakxoSnNvN2xrS3VkU2NPQUNzR25aNDY3WlVGSkJIKzNtRlNUeTlrcVZzWURZUUN1Q2orTW9UK0VlMEFwbVkwWmNBdncxZTV0dDBwUkFPcVd4QVZ6QmJkWHNvTXVUbE1ncEJXdm1sbU5ONjlmSVRUZk5PKzZmLzA1TktkM2d3ZU43YkVoNWNBRVQ0WTNneWoyc1FxTDZVV2VsNEtkd2lFRGh1Vythd1BTNURGcWQxK004YnFLaVQ5dmJQOEtNcU1PR0JlT1ozenZmNXpkREt1b1dBaDRhOTE0eTJWRFdwbTRyYTlERTlwYnRYU2JxSEh3dXpMdXBrcE9xYzZRSVpvUWtoeEJseG1SeldoM1BydkFQTHhUTlgwRVVIOXRoN1FsRDIvaDRXMVc4OWQzUmpQaXFPU1UzMUJMdlAvWll3cjg5T0w3QVJlS3k4TlN1REZYR3JsTUxoVy9NMmNiSFJiNGp0clhldnNlOGZOdlpIZDRIcVoyRWxvUGFaanFVeWhBRkx3YzJVUTVJR2ZRQW1vNG1QSEp4ajFTUHNZMGM5aHB5KzQrbTNDblNYOGsxYk82VWhTNlRuanh5NUQ3S21PSTY1ZUhOTk5yS05EaFhUbkFHaTFOOGtFSG56NUFCb2JLUjJkSUVCOCtSSUoyOVduT1l3MklrSFk1S0FhYWE4dTRSMVNyQnJsZ2dxSGZsdVpwZFNWY3ZneHZQemV4R3VuRnJBazlJM2hTRFJzTHhzMEJuV2R0SVcxbk16RFZqamMxOHczTmZoTUkwS09WSmlMSjNmdkNlM3haVVZjTytRYXhodXVOazM4TCtlZjJBMFFkd3IrTlZhaTZOZTJjalc0czREV09zVGF2OUF1cURNM3FxZm5ydGFUQ3orUm0wdXUzOHlCOWJEcTk3Sm5sV2g3L1hUUWpYVW1PbStzQnFoalUxNys3Z0RCcDlZVVJBc1puVnFNektlV3NIUm5NZVVyR3ZaVG5GcTN0Rno4cDIwSEVmNTN5MnZtTS9wN2h3alhHeE9KYUg3Y0d6d3FsSjZzVmdrSTQvOFZFNVNoMWxHY0QyQlozNS9xSXBLMUszRGV4NHlHSk1OblpOUGU4Q1Mwdlh4dWpNbFMzT1hEWnNwYXUvaHdjRTlsSDhNQVhNMUh0c1l0TFgvd2YrR1YxWlNXSWlsWnFtMTFrUktNUFltVGkwZU1XaUlUQmNoRzBmWjhsZThvYm1OSUtPenNyU1FCUmovSU9HbVUzVHA5a1FXOXhsSU1WRHM3c29yd3lVVVd4NU9kK2VMcThWK21aMUQ1Q1BkLzYxM1AwYUlwWGpFODQ5VVJ0YXdHQmYxRS9TWXFXd0c3M0FJK1RkUzlnZW1XL0VoMWtFWHVFOTFxQmN4cVJxNmYzMzJReERad2hyTkhsQi9PTW5pRjRtbE9qNktrNEx3U2NTVVdvK3Yxc3FHTXllbUlvdzdPL3Z5ZVBOTmptMzNMa2ZKTDAyVTZUdzlsU1RMY1o1MWxmeEVjZEtoREgycENGbmdLU20rWFkvaDZXbEltQ2tLREJlcjFXZlZBdUY2dkV0Vm1GQTZtQUdIak1SRnJyMk03cnJQa0JuV2xMQS82SjM2ajQ5T3p3d3huVDUyZ3dzYU5oeGZBL09ERjBURjkwOTg1MTVVd1Nvd3RHSEtGYi9mTWJyOEdrc3FzMmE0dDN5VzhPek55dWNudC8vUy9UWGZUejBCR055a3I2dDNudi9kcDJNWU9VRjFXeTJJZGcvRWhuc1NFL1U1K2w5b2NrYVJHTWtralBldGJ6Rmt3NUhaWUIwenlyTm9oSFZKc0V1QTBEamJvZFBoV1BVQTN5TnU0ZUc3KzdCMTJDWEpKUHN3a0pMcWhIYm9lNUJxNnkydXFhbXV0cGk4YURSVCtGVXZxb2dyN0NJL2R6cE9idXdxQ0JQeGFmaXArZzBENHVsV2cwblJOK1diSHpKNVJZV3N0OWY2T0owR1ROZFc3MTA5UDBYVXF4RDVaMTV3NnAxZk82dURqdnBOdzZYaTg4VWQ3RnZFeENoc3FxcTJzSHlkSlZDaE9hN2hkM2x0U1NuNnI5TUJ4NjZHYjlHN0FFUjltQ0xFQ0tRZnBVd1FIazRMM1BWcHB3Z2MrRFlWZm9vSmxxOWVXU3JnMlYvRVVSUXU1QTRKZ3ZYNE9ycGdqMzBtaGlYZ2VFTzJjT3VIRjIyam1LMkprTDB0a1RHWlFTZ3V3OC9IWWtRTnI5UDBKckRiL2I1aXByM1drcU5IbHltbTN0UzVkSXZqTUdtOEY5YkhsZmpscEpSTCs2N3RqL1lyT2NkazdqTkh6OUZod2VQdWNTL1JCcjkvQ0JDdngxNmZjV1U2cktEa0g0SzV4MmFSY1QxUlBkUVhpaDV0YUphUGo5WXdYbmpxSTlJekdJZFFxdUZ6V0FETm1Gbkp6aXY1WDRZSmlBVFNDdUZsY2RUVXY0ZjNSVTd0NVU0eE92bE8wbGwxU3VtaEY2L1RVZWduMThqRXYveW1NdEIxUDdlS1BqSmt0SU0xdlAvek1yT1lPTVVVLytST3lWam01NnptQy9HTnBkMmZxQ1F5cDQvU3plUHZjQnRtRDRnTHpmTlpFaGhGamY1K3d5YnBvUWE2NVVwL3oyVFRIYlNKR1lybDgvdUdaNHRrRFI4NmFFQkF6RVF6dnMrZDdZbTJkWVhoZXdLcDkzNHdnbjhVOFVJK2NhTlQ3K3RWVVYySjVvRWhkUnJWMWF5SlEzN0ZySXRXLzcxRFV3SXp4YnYyOWNnYVJoZnhFenBxTzlJWVM0YXg5TWN6S1Y0WWJkc1lHTGFRRFE1bklwZUIxZWN4MGYvRXR4d1p1dm5XUnczVlRTdWxUVHUyeWV1TVlRVkdNSnJSUHZCWHQrODdkbGpGb3IzenpGd3pQbm9GbU5FQTBacGlvZVVVbEM2bnM3cU9vOUFRS1hHSXhkcjFlZlc3a2c0ZmFoQjBuQndHVGYxMGJRWUU1WDVLKzJKWDVsQkI3TGNCRVpuY1BUWVluU0NoeTdsS0Jjc1RWaG1xR0F3WjBTVEhHd3BhbU5PUy94ZjVlWXpicVB1bnRON1JPNnpvMGR4TytETXlkaE13N0tFcFptdFJCR1EvbVRyaGxQdUk3SFZIdTZEMGJQeExpL2lKRTdyNnhlc2hrdjRuaHhQaGFkYUZ5aWwzT1BnRGpsd1FpVEFRS3YzZ0ROUzlQaEp3Z0VFa3Fhb3ExUHJ3cXpxVDRFRDU5ekRxYzVEN3FOU0E4K2drYTBScnFTNlY1S0lsSUl1a0srWlBDWTZOcW5jOExFZngwN1NzZFAzeTFFcmYwVmxzTndSZ21IcjZMUWhLRjdwUE13UFpnVXJJM3h4bm9DZVNmS2NkdWFLVnRwOXlVekc1dFA0T0U3M3VVY21UQW5obHgzQlB4ZGFFZncweGhnZy96Z1hNMmMwWW8zbVV1b041b3NwODNHUGFpYkJCR2RyRUJxb3VxMGZaaklxNVA3M0VEN0o2Zi9ydkN0V2NTTWQ5RE1xZCtIak9QT1M3UFhaOHRRQkJuNktIdTl6TjBqSWEzenVTVDdCMXhNK2hsL0QrN3JUdlVFZlM1dlV1bjRPeWFQNjN5Y3JHTXhod0tvUzFFNG5HTFpUOUt2T1FQTHJURHRMTHVpaWFsT2ZGYXdVUnlvaVdpRHVsV0Uwc1VrdldCekdMMXdSOG5mMDFZeGtZMjN0QjdyekZSYmZQYzdJdkN1M3QzZE9LN0orZDRqYjUwb21PMTMwRFF3WFhWTVpmN24xVjNZN01sWGltWDR1NHdKcFptdi9QMHhTRHRGVmVmNHc4SWl1TmtDV3RZbnc1ZEgzNG96cFAvNGVabXhTckp3K0hXYWRpcEt0ZXZrNjRwVmE1Y05uUXE5aXI2S004OUNpMmtwSWFPQWpXQUJCR0VIT0Z5bnRVei9mV3AzcXdtRWZiUzBVM09WWG9ycVhKTmc1cFU1SkZ0ZVNQSDdpZEFhK2w4N3p6Znd0dUhLYjE5OE1STEZ0a2FDT1FiL3BSTm0yQUQwMGtDb0NRZ09hWC9ZU1FBb2FYRW80cnZwVStsUVFzaEZRTjQ3L1B0ZUtucmxUYzk2ZkwwUEsvR3ozZXJDZ1ZSeHFXTTNCZzd1ZVJTVUdXdFhib09SVVZ1Wnc4TlQwK2hhcHdhVkhELzRmT3VqNEhoaXFQWFQrV2JETTgzUHpQTm1tbmVqNzJIRnBQTms0NTJnNnRud2VUenI1bGRkS241eEVEZXRsWmxrS01qV29LVmp1eTJ4QUtuYk9Pbkp1WkI5MEVDZUpLWFdCRFZzdGJ4djVDcjZaalcwd2IwcUIybGJqZXFRUHdwNG5OeDdmYUFtNUVkRVRjYUo1b0hGYVJEaEU0dDNURkVjY3NPZzJBbUJ2N0VCc2VZNFBvMDRTb1hkZnBDdjBoUWdROHVuZzlJVFZocldVQmsydWVxb0RwenMwRndzNVBtSnVhTGRCeFlMSEJUR2xidDVNR0hneFE0V3l6WHh5ZzFqdVFySm9WRWlBMXNpejVleXVpY2RTclUxaDl6S1AvOHM5bUlsUWxIblhJT1IyTXhUbG50Z1hIUFB0SlRwaE1kVzdtN1pJOTlqc2lZbTdYZE8wU3k2QnowRVFJQjEwNW9EMTJrMG5YZkhGV1RDMW1vNmY0amdscE4zM0RvYzludEY0UTJDa0VaYVZyUTIvRURJTVlGZGtiNHlIOXpmeXdYTjVaM085cDJ2bWs0NzBvNDlML2JIZ3BaOUtVc3FzaXd3ZVd5M0ZNcStwbjMwR1I1Skdrdkp5SC85U3k2aHJ6elhLaG8zbEFkQTExVFV0WWdPc1kyQlhNSHBMRENJV2xVK0JWVGlhS0YrVHZDd2hvOVExZFIrQlg4S1JlZXFaUno3emV0UVVNa0FQb1BVcU1wSTRta1RxSHV0L0dUL2RvRlhWandYYlZBajB5UDc1Wi9SNm5SWkNocVdOZnBXT3NiMlROamdTcS9FZVhxaDBXV0RxNm9adDRBYnByVTN2QjR1SzJoUk9CbUpHcVNKNUJScGFPZk5INGRqY3ZXUnA2VUNsTGZLbEt0NFVkaHduZWVnRHRYR2d5TUs1NTZtNForV3VhUEJNQ2tWM1hpUnM4ZndLNmZCS2ozS3h3WlRsa1NiUDdPbk9sS1c2WlRyTWJnNVpUN2M4MHlNdDY4bkIvamttOGVISHg1c2FUeHl2cmtjRHFZc1o5Nit5bDd0dzkvejZZdnVLZTRmY3V6NDFiSXBNczgxSHpsRVRMMmNLRnEwQkdPclk2K0J3SEpoNG9ORTdScVhGam15NDF0NTdLa0NYVTJKMC9ENVRMRzhHdzY2amFRZ08yNEhHWlJtU3ZMUStrcjM1bC9mV3RaeUpUbnVaY0ZoMitKQm13bWxDczNQSHpuNVVPK2txR09nU3VrR0c3dFF3bDZidTJHbjE4RWF0aFR5NmxBTTFON0FhOFlsOWh3N0xOTExuM2xWRlM1azRCaS9aY3FEVzVzNGRLUCs4NnBnV1Q2QXR0K3VzeU1wdExEUmIyMktYV3RFdHcwNk1wcEcyUE5rb0Jlbk5PdHZwQ25pb3RiOStGUHgvMjRPWDhWOHdhVDFPOFF2aUZrNWw0aFZncnFqWGxjWjhnVlorNlljamp4K0ZUM3ZUK0pwRXZ2ME9ib1pSdTFUMEFwN2gwc3N5VndRcWNPYlVjdTliMXc5aHdqV3BTV3NRNWdKdGJ4VWFjN0VLdzJkMlhyNldNSlVQazZtMDVLeWVhKzBYWkJxT1JuYURqWDBqS3hSRFF5Kys2Zmd3aHJ4b2dDOGNZdHIwd3pNbklONjRFQmJKQVpXcWdWUkhvUWQxN2V3ZUN1UkRoSy90ekcvTmdnSzljM3JBVWd4blVEUzYyVGx0R1paeE9nMkZnV09qMzlFSHJJU3pzenZ5dEh3RTh2bFBpVzcwNi9Na2s2ck1hOG1JTm1mdi9US0NTWXpmV01WbEpja1QyNDF0T01YVFhIVFlGYjN2ckRaREJIcFRwa2NRZC85ZjBZeGVPRTkxN0hkUUtVZ1EyVUljcjhkdmtoUnArQ1paOXRxdGcwRUhkaGxBWGFDMXY2eVVkVmRFSVVONkZVdVdaUkw5TWdYbHhHU1prNE5zTFZSYk5paE9mZURVMW1lbWo3VVhaSmpXb0pRV0Z4dllmZlIzQ244ZitoQmxmUDhWOUkweGdxd200YUV3NU0zeEt0djZyREkydDZ2eldPenRaaVNqOCtwNHZUbzlZVWNFMUhvWGdZN2Jxc09PbnJUREsyNVc1ZTFnWGRJRTBsdnd0WDhSVkhaZTF4VTU5ekhBdnRKdFovTkF1S094RW1mL0NCSzdJS21vSU5IUDYwdWQvK0VhZTFRZ2tyVjd1cGh0TE0xUFZIc1JIWmF0d2VvazcxZkxFRGRYdlBFRnViSTRzcnVYanBFN1dVWG5DOE13SHgvdzJOdWZSRE9xKzg1L2dBY212MmEra0R6NTFZOXNvVUlhOTlVcm96bmkvTXQxKzh1WE41UkhrbG10ZXd5djNrd08rTWtEN21QTWJ5UitDQ0RhTEZmM3BOSUNvK0R0WDQ3d3pkQzRIY2xsWWxjSVQ0Y2lCOGlHTHF1RGh3a1VmMUc1citjN0prSjdoWlNMZndpd295emZnR1Jiemd5NWcyekZzUzh3L05HVk9WaEhkRThxS2tnSzdjWWhDbkVqNENQMzlxdGNGVXpyTE5XZVA5UDNZc3p0SUVseU1lbERZUmxhOHFHQUhHRGlWL0hBeUR1Nm1PRmJLSXU0bXpRb21UUStrend6c2svK3ZJbVVkYzJJTlBLMmJSV2JYZ25rOWRKYUdzbGpiVU8xVU1vcSs2c0tCb3RkN1lwZnhaa0RXdlFzK2ltNWpRV3ZLc1hlbFZmUzJhdHNFYmZGbjdBbmpUa09qVElncnpOczBUaVpPb2FmYkgxSGVDUk5XaXV2QnhVTTFoQWtLOTFxN3hIc3plMzRTVHF6WEpMS2tPekwxa0E2WVVYT0w5Z3o2ekwvNzBscms4V1ZWWGl0dzZvWU8xZUt0UStva01xVTBTR3NvY3Uvbk0ybGR5SmFNd1pQbk1XdTRrUWs4QnBIb0ZuTlpBM3JCUDZISlNoN3VwZjZEMVJQUFlYTGptWkhHRSt3azVoSkxGSncxQVJ3bWNYY29SRUVGS3FhdHd1dnBZaHZlSWQydm1uM0Zlb0VoZXZTRVF6ZGVFYmFYVkpQeXdLaE1ZNjlTaC9xQUJLVTRlak9qak4wOGo0OUZidUtrY2Q4T3ZmNFd3MWFJcDZnaUkwZEdsbjFzVVg1U1FWS2FUcHhUVXRyak1rTlp4ZWoralZPUm1qYy8rK0hmdTJCOHBwanBPQW02Wk16ZE9PajMvdHRneHJic09neElEUUw2R2ozR0hoN2lJaXBwdk5ZSGM2YW1TQjdwazQ3NTFpRDd5R1B5RisxQnVJRW5TYlZTZU9IdnBQM0phWERmVEdpcHgvN2oyODZhTmFvZVJLUTNMSTZXL0FQTDc4SStYcHFLVUVsZVdpZWIvYlFCUVVMSlpjYjlKRXJoRkVyVGZ6L2poM09TTzQyRVM3bXQyRXIrWDc3VFpmb2g2eEhTZEdUcTk4NXdyQTk4bjhlUDNpSzhoU3kvMHFnQjd5VUtqZWtGbW1zRkh3UGprSCtzazFXWUs5cHVkdzN1aDNzK0wyMi9zZVlSTm5iVXN0dE5uQ0ZZY25KcnNyZHNUM2l1a24rNFdqVENpalFldEYwM2tvMU5kVlplaG1LbjZTanI5Y2xyZzlHOVVYUnMwSnRSN1NGeGl6ZUNSbTF3bkpRUWVtMVVOVHF6TnZyelBKMHRYeUtDdmgwaHh4QjFuZWl1Ym5EVnF5aTlGbVFuSnpEdWJTaTZKU0djbVBuOXl6MDR0L0M1UFJWd2ZIejU2V3dtSFZLdFIvN0Q4a3p6bHAyaWg0Y3BmcDY1am1aZVA1K1hQTVpPbmpiUUR6WHIyQTRiOCtPcmZoako0cXlYdEVBT3pUYjJIaWw5dHdCQS9ESVRTc3BCdWIzN0hKYnBYZWJUeHQ4YVZZc2lxZlN4NG5LaUdFeTZoQklWNWI2WHl3a0RUajNCeHllekQ2ZU5TMnhrWFZXT0xSWlZpb3RGUnVoN1g2bWt1OFBpTmQyUmMwTzl4L0lYWEQ3dkpLa3JhM3MzZTlzZHJiQXkyTzNTcjhqYytPV3FubGhSRVp4cXN2YmxqbVJnUXVRUFJrWlliTlJGU2dMNEVSbW5qcFhUY1ZQNDVoRDluWDFoTU8zTG15QXpBc3VRMUZYZWhYREcyZGVvaHdFRUt0aWcwdG5reXFYbWhqMms0U0RlTjQycU51bzN3UWkyUGhqTVhVZ2hvSVUrSmI0eVEwNDhUME9qenYwUE0xc2tDeUhFUFdIMlQ3WjN2dnRGVVgveVk5N1EyN2VoNzF0Z2VadmVRTzVFZGpuZFVZSVhKWXdsQ2l2d3ZDcW5WK2twbmt0K0Z0a1BJK1BLMFJhNUZHSVZBZENnb0x2eFpuYnJSOTE3cUdiUXNlUU9SR2QwYUNTR0plTUk4VjhCQ0xiMDNabnRIUkU0bmEyWkhLSExMWEVKOElFS1VkL01Jb3JLdmdKbXdNOGszajU2OCtNbWhzRzNuVHFTRXhHb0x0MlFTR0xZeVNCYXpmVnc2UHhyMm52NGRKbEsrV2VIQVdqaHRHVDROUVZKdVBLWitDaXpDeXp3QjkvZkNaNTl2aWRld0pwcEdOZjRKaVBhVmdwUktMOW5OZnJMVTJSV3hCVHlsZVBRSkNJSU1oY25xK0lSQVhaWWN1cUZ2eUFOdnlwS3F6YmEwdjRDbzErUmxXRXdYYWxTck9EeVg1V1dUWmFBU3JwQ2pTRDZVTGpGckN0NUFGd1FDRFE2V25ZYVp5NnNEZU9Ua29tYjcrUHI4TFhyZ1ZYR013cnUzYUFvSlFwcVk1Y01KVE5tZGRKWkNhaUt1WW5BZEt4eVdHa2VsNjlqY1JrWFB4TDEzaWs0bWN5TXlreGtYVE03OHpodFBRSWhvRk1uZ1YwaWpwejVrcC9yZDFkb1B4SitXTUVqZWYrdUw1djgzVitmc0dYV1o4QnlHZ0oyLzVmV21ZeXRSWUJ1UHFyU0dZTmR1UnhoaFJPN21aM0N6eDNmdUJTdmdCWFkwZGZnSi9VZ09YYndDbkI0U3pETkNLaHczQnlKbElxSXBIK1B3b2xPcU5oR0dLcEZ1Qmhma2xaT0xWK1VqTDVJc0JnTk9BY3ZPejRleElLdjZ0UlB4c0h6WGd5Szg5cGpsektPeW96amxQNUZCRnFCNW5GVGx5Sk04ZFo0TFp0ejUyZTExMDhiTmVnUEJJUlFtYzB4YVNtNkpGYVg3b1N6NjM1bklXZFpGWWlhcXRtZm9tTEliNUI1aUQxeVpNTFh5WU9WdWcxTkw0QTIzVUNDb1Q4VmlYM3hCT3E4ZkZKeWVTdCtDczIvZWZ5N0trcjhLS2FMeE10RnR3SXlKdnQvNEZMQWo0T3ZFL3lHQWJjRzdER2RLbXpLdStQcGIxUGFyVUtVZkZPYmpYVDBPazlDNyt1ZmYvdkFIaDY5TFZWb3ZNdDlJUE8veTc0UVh1SURtQlMvbmpscDZyMUg1Yjc4Q3VpSS9BY20remhYZEM3RnNTOGJmc1BZOHZycGcrUVlQSjhDcGhyNzNRd2RVU29HZ0VGZHN3T1gxdVRYSTE1ZHdONVpwUXpFV2pGem0yWTFuNlN6L0NPN3UyR1lxbU1vNm96TnR5bXRJOTR1bThqalA2RkQ0NkdLSkl4K3UyTThMRTJaU0t1QURuZm5vSVRRQ3FnVW9oeTJjbGJMUFBEVlZ1eG1tOU45NGNmNURMeFlxSVlPT3p2ZHRxejA2MUdXUGM2MjZ3TnRDbU1RYms2REwwY0ozaGx2VkdxdFQzenNCNVFlSWl2Z2F0MzZJaTV2UVBDcUFKQ0hXUjJoNmw0MnlDb3NOYk1OcG9UYlNhSW8wVFdmZDFiUnJ2N05rd25naVBuV28xaVBxajd2VzRIRTdBKzY2TU44TTlQMXYwb0M4azhBeGZoV0FRVmdIeTdWMmFlMTFWSWFjZHJxY3lKUUcxaHZyM0xUbmxWWThNVDh3WmxJMndHU2dqTEUwZWc4Zk9uaGxHMWx6S296MlU0dkhIVGc1SzM0WGxMQXFYZ1EvSVQ0d2hKUTUxblRwanRJdWRYQStrdGhiRG1ibWxFOFNnM3I4eEx4QXFXaGFjWC9QaVIxZ0I0NTVSSzhzeWpXM0RqNXY3Si90NzJacmU5T0Q4MUVSbGVyWXZFRUp2ZUZxSjJ4ejRZOVlITE1NUkhrbkIvMEhnWWk1OHFEU2dRQXRXTjVFVjF6dmg4ZTA1QXBOZUdqczJkZGN5ODFyUFRybUJaTUNZaEFoNU9EY0ZkejI2dlR3NTN0bnJkcFlWNk5XOW9YcWRONU5TaFRsM1lBSnRPU0VMbmFoaHhNUnh2di9WMnI0RTdjNHU3eXprQUVCbXhCcDdJbkZvTlZLZ0tSTlRlQWVRTFI4ZWNyMEQ2VHE1alF4S2dLNlVyaEZkZkNxekUvR3lVSjlFMFdLTGV1KzZyNm55YzNGRng4UERvTUJFTi9pWVdpdGxFMWtiYTVLWTF6SFRkRG1pUng4cEpBQkdONDZtT2FMWGtLdDRMMUk1aTVycGFRaU1GTmZOVlJ0NFhHdVJNdWJ5eVFDeHFLM2ZNRDdISWw3K2t5Tk54YzlmYTViTnlRMm13TDErY0h1eHVieXd2OVNiODRZbUlJMlRhaVpIKzJXQUxFc2U2WkdHTU1nV1FIYStsTWp1QkZBWjNWam83M1ZqdnJaajFVaTRWZldlQ3NrRTJ1VjJqS1ZIYVJQQmczZ0l2d09HaHZnYUFnenRoa3NoUk9za1JDNVR3R29jN2VLQVNqWWZIRWhxQzRUc1EzdjhaV29qUkJ0Y2p4a1NHeHBkWm5UbVMwOHFUYWg2eUV5bkFWU0xtZ2hlY0JYOTVaVjZFYWlzeXdLNElQWC8yblcrVnM3S0lmL3I4MDhkM1Y1ZUgrdzgycHlaSFc1VmlSUE1xRHFzazFvcGtiaG5HOVVvRnZZa2YzaUFqYkpIWXFPZnRjNzR4cUF6WktWb3M4eVFWWEJaMkFIQ0pKMTFkbUFVelpqYUZKb2RYSzZ3TjQ3TFdkeDRkTFRYQ21rdW1ZV2NKMm9qS3hKQXg0TW1aemNzazkxSzFTN1VpSS9GeXkzd3doY29SSC83NFk3Mm5sbmsySWtjK3Y3cmMzVm52elU2UGpXU1QwVWpBUnl2TzVWeVplNzRJM0w3dHpiU2JVZzlQQnlSS0s0ekFJNklFcERsL1laMGRyNFV5SndMUTR2M05zK3phT1QwK01td2ZpODlybEEzMVQ0Y1JjWlBYKzhBd1lTYzlRSVpiYXg5TmRQWkM3L2Jncmd2QURseVBHSjRzSkZFaTZSSVNLZXEzN09kcEplYldjQnRxazd2cHVoMDAyUlJrdzlhZ0E2YThKSzZqWnpOVW9UY1FHa1RjTzJHdWNObkpXeXJ6d3dVdGxFU04vdTdtZXBrM3h5WWlrb0hXK1drUGNVclpjOGE0T0ZIWEEvQm11SUFBR1owRUtzR2ROZkVYalZNT2hoQmxxVG5ZMzVqTUNtVXZnOEhETlVmc1ViS25Xa2hzUWtPVjE2dlM1OHJ5ZDl4MmVaNVcwdG00Um9WTWFmRzl0M0EwV1JOT0c4bUovV1ZDZ01uSWVMaXhraHNPQ0R5QWs0WWNHRm91ODhVV2dtYzIzcTk4cVZha1gzcjYrUHhzZDZmWEhtdFZpN2xNUEtiNURENE56Y1FoSW93cjUzcG9jSGN1Z3lrc1JlczBHMENKR05rRU5lZVV2ZGhIaGRUWDI5a0JreUlsdWgxMHZFWFNoVEF4WUx4Y1lacXNGcE9SbVVuU0RPbWxYWUNyVDJNcldUY3pJM1M0UlhOM3FSWGFxRWM5d1FXbFpZalYyRjkvYldLMFdhK1VDdm1oYkZyWGg2eHM1R3piWWtSWXY3MXFDTXAwN3ZEUS9sQkNCVTIyYlpZZHM2VXlSeEpoQzFXZHZMSGVhYy9PVEUwVzhwbDRTUFc1N0RhTnNZckI3K2tJVlc3QXRzc0kwMWFaNnQxVWpWTWJEV0ZBVnBMQXkweDI2UEsxTVowQVFsbFBuQ250dEc3eHN5Y1BkdGE2WXlQVmN0Nm9oVVpwRyt5ZmRkdFE4bWRZcGt3alVFZitrRUhPamlHeWtEeWwvT1NaTWtCWmJzVFZNVjVaWHdmZzhMVzhZRzRHVEE3YjFTd1hFN0VKMmlOdndlSUVGTkJyWDFQWW81MVdQdWRtck9Mb2JpNlZyYmJNbHdZMTMxNHhYSjFFbnBWc1RnazFmSDBHc0JzeGhYWTk3ak9mMFh2NW1Lc1Z1TmpnY0g5cm85T2VteDRmVFVicGdFdTVsTmVXNjdkKzZUUGdaWGVwb2tpMG8zT2tMczFhK0VhWlJPR2xaU1pyMDVKRmI5WWtNTUZadUNvTCtYTEwvSERWVnFwMmZQUUxYOUNCMXkvNWVUSUo5cFhIajhCdE5mZFBEL283VzR2ejA1UE5Sc0ZJeENORmFkTjFMY3dkSjVkTERseEE0N0FBbjhWZzNJQ0MrckpZTk5XRXJOZ3RsWG1RQ2RWZzU0TlBIeU80NWZYVDF3Y1BkN2ZuWmlkSDZ6VWpGUTJxWGtsSUhuM3AyeTBjZzFrQ1UwNEF0blFjUUZKaTdTVlM5MjZkYTk2WXFCMDZMVHZYNTA3WFVway9nN1F4QXZMVUIwN01USTJORnZOZ3lOQ0dTRGdXeXgwM3kxRkZuRDczVWFCV3VlUmhKMFpCUnNwdkNBSnlnY0ViSWVzUnRKMXlJbGFXRGRrcFdpN3pKQlVhenlwc3I3M3k3TW5nN09nZ0VuQzVZbUcveE8yeGI2VGowNGQrMU9sZ3hFVmlrVzJIT3pCc1ViZWFvcEN4RFY1RlZKMXAzVUpjbTViakx0UkZJNG4zb2Nvamg1QnY3eURNNkp1d0l3SjlRWGkwWEJhY1diREpyczE4OEs0T1BES0xaM1VER2hnSzFPT0x3ZWxSZjNkN2M2M1hYcG1kSG00MjZpdlJTQ2pSMVNSTjJibFY1ZmpTSWl6c0RHWEZZOGVoeG1mM3crUXFIT09lZWhUUW1XWlJlN0FnaTBWMjZxaTRHWjFvMkNJOG1iVVdBNW83UjZyK2orclI0RlNCU1oycTRybE5jUW81ZWFFS2I2YzQ1eWlNU2RhMWNNUEgrSnlFNWlMcUpKb05QaTh0aFNRaDFaODRaaUhPeXZmMXI5YUxtYVFpa2s5dmJ5N1AxMWQ3N1ptcDhiRk1KcTBKanl4bTV4Y1IwR0E4OXZXeitaMWJIUkxHdG9XVWx4TERtYUVqV1VEQXZSTUJRNGd1MUNCOUorN3RLZm9HT29xam9EQ01QdzEyNTdseXNkTnVBc3VNNytQQys0TGpmVHdtL0ZTQU5xSm5DcjdtWWFNOGlhYkJFaFdKM2RldUtMdjd2Z3pUUGpnOVN0MzgrZG1waVdJK0ZnbEhkY1lHMjJRNnNNekVoNnk4TTBWQUpaK0M3emxDZzlTZ1owQWdCVXYxc3hzL3UwNEUwc2Nxb3hnNzVaZ2ZqemxsaStYN1FvNXBuVmd6eDZ5UVZyaFE1SVFRSnpXS3hzNW10NTFQeXRnVFlYdEJDNFdzL0tqTGtBNlVVd0NDYnlBQ0xvNEVSTlJjTDlER1lXcUVHRnIzUUd0S1dWcUFpQkFxTUlxMklsdVA3QjV1Qm55QTVRTC96TjFuMmxCTVdxc0ZYRTRoenlKbXRWbTVEVWdSZXlHRTVwUnphOUtrcGdVZzNBQ2hpNU0xYUlPNmFzVStqenBQSThmMWNWNkVHMkk1TjhJWklTNEVPUkhoY29TL1NJb1NMT05PK0drdTZSR1lGK1pUT3FLOXlDclVERUJ1QW5KMndCVlV6a055YnBvYlFXcHNSUW5HOHRPeTVvSThNQUdVaU5VNEJYSXVpbXJxSnV6NG5TSVNpcnRaWExpeE5CTlArdm5MajI3TyttbFVOdFlXOHFRUEo3TmNVSmxiaFVrL2I5Vkt1UkJFNjhXcVpHRUlPT2I4dG1aVGs2Tk5MOTFzZW55NHlBRXRJR1prcTlFakV0b0xMa2xqMVhEcUx5aWNFUTE2cVR5aFZFeFFhcGo4V0QvZlo2UFZDQkxWVExhS0hBbDF4UzVSM25GVXFLLzlOT2Jxc3d3R2N2TjJZenlOZ3lxYkhlNlZEQUlZTlYzQVE1MEpVczVrNHFHQTA4cVJFSnYxckdIVjV4QVpnc1prUUtFN3pZL0k4Rm16VWQxcjQ1bm55eWNqcXR2SkF1dlA0MkhkTDRCSGFRbHJmamNQSDdKcE5DeUFVRXZqVjVLeDNVdXhwR2x5M0FubXowN0dtdzcyMXRXbXczNmU5QVc5NWdNRFNpams0Y0FYOTdCTmxZUVRtWjlQdXVjYmNiZVpTL3A0dVA2d1lLNXBNWCs2T2pzaUt5S2JQWU1OYml4RkNkOU1jYmx6WE9sVUlZdjFBb3N6VFNQc0V4RjEwS1FJL0ZVRFNwdWN4KzJVYlZiQlFvQzBMd0NHRjRsSFhJcnNFUGc2MmRFSEI0aWMvbk8zakFSZEFDMW41M3ZHekdiTlBxTFVvVzljbm5kRWZtcXpXb2doMnpGd3BnWThQTXFrYXI1ejVLVEZOS1grUXVqc0I0YlVrTXFTMGx5UzllZHJTeFUzaDFrYW9tRXJzalpHM1FqbEY1K3l2ZE9VenlnSTFZUWFxNjh4dEx5WUpsN0c5UWtuWHVZcUR3eFUwalo1WlJhaUZZL1NMcHFzSkgwaW91OWFTNmdpNkNra1FnRkJXRFFVVkQwQ1dxR05za2pQVHpzNDQreG8yK25OYlF6NXVxbjE0TURsbUs5ZlhjMzFCM1VVMVd4c09KT08yNXZOVDVuRGdlUmhtaDA4NkhaaXdIeDBmWHBTRUVJdkdLOERBT0ovWjI2MjR6VmtidTFTcUhhRVlqaEpQdXEyNDRqL2N2N1ZLZ0hjZ21ub2dZb1c0clhQZk9vd1RVT0FNQURONDZQanoxckZDaDcrUkQvek9lNHR1OE9KTTNOM2ZxbHBlSW4rRXY5VytWM2dnYmo2NE9Sb2ZYVm1hbnkwbkU4bDlJQm93ZkFhWHIvSWI4WW9PVFpPbW1HTEpjaFB1MVhxbVV2OVZMR1RlQzNpbUdxeUM0bHBJVGhjazVlTUI0VlhIc3BSTy8xN0I3OVd0YnE5UXAxMlF1MzJWTzUzTERYVzM1djdyTEtreFFYNGNDaktBME05RHdqeThFNlVFQjUxL3NRdk9pOFU3cTlXeWQxSjBEd20wRjhwbGJEN1RVaDVPRUFJeDBUbTIwTmRROVpnbDhobTlhVmhHbGVrV1hWQmtCOGYrRG9UbEUyelkvbWdXVXhFaU1ZTGF4WWRIaHpJUlR5MlVCbTdpUzhTbkFEV2UwTDdXNmQvSWlKL1hVakhyQmxSTDJpY0JwdGZWdXpYTkh0R2xzTG9hckM3dStvcEdNeWpONFZ4T09yZFBwclFVT0xPR0FBQ1VKU2VNNzlMV3N5Ti9oMW5VbDZBdFBwUjh6KzdTZElmay85cDBsRkRVK01mbXIwQkROamdBVWRobTBNakxmVmhGZkgySy9OUDhIK1FnN20wRS95VnhQMmtlaE85VWlLRklnb0JEdGR1b1NSTVNtN2xZWUw4Z25Sa2lLSlRQa1dhRmRrcDR4d1JDVFlIWjhobm9sSm02WDVBdmtVeFZzWG1GVmRQN2FHSlAyUGxDMUp4YUtKR3lCZ2FHZDRXU3drQXY3bmRvekVEUzVkTVRUcmlWSjNWTDdVUVg2azNlMi9VQTI5SURxdDdFYzB2VExlNDFyNVZycXNzTG5kZFZGeExyQm5zdTRxcXlDL01yK1FYUmpJekxXSEpzRER2TzkzcVF4QktlSnExNkdCNm9FdUgxbm1oczIrUnpJSjB3L3cxSXlENGtRYVJkQ3ZqSFRrcXJLNSs1YUtNLzJrcmc4TExES1NFbFZickdxMkl6YTdGUTdIRkJUTjMyU1ZNV2F3UVJ1Mkg4ODB2WmlIZE1KWVpZdDBCT3hHUldlTmVXbVJLcWxaZElTK3d0MnYvN3hocTcyUk00KzI2eG9ZWnFncm10Z3lxbm5seDJSZk5KZldIVHVGSWhMdEJRS2x5VDk5Q3ovc2k1VEI3RldOZTBqQXBWSDhFM2pHMm9xbmREaUJWTzY5ZkY3dmJHSnlxKzFPYXRsWnNYQXNEVlpnNUtEdG1MMXBhUE9yQW9QcGZGMEh2ZVBaZ0llMTZSSmVnVjRSbklvZlBoQzdObU1QMHBPRXA5dzJqSG5IRGU0M1hSZUZwSHdpOEI2SHRtKy9CS1BEbGV3alcrZE43T0l5TGV3L0ZoRXdhYWVKNzVIeWF0dDNXQWd2YWh4SmN1ZmJNclRNblR0MVR4UnlJVjhFY0k0ZXEwdWZLRVBkWGpUdHlaMUF1Q1hSNHp4d3BCNldTa3VrTW5yVU14djhaUXdwTVJaa3owcjQzTXhMc08rVHVDOC9NQ1V3M3VHNTN2SGhCUjN2eGlVZk91MjlOdWxxbDkwUzVIZEtqRUpySnJOU0tMQzVGMG9mNTZwUlZUMitycjdtcWxGZFNWVkZRVkZibUN0bk9qWlE2eEc2aGhpZlgzUmtYK2o1NDZtSFY4VlNtOXQ4bkxmSEdaNHAxRGJTaitnZjIrR0FLWTZkbjZhamFRSHJYdHVCalIwdHVkYVNhcXhiMVhXUnBQZTRkSlEwdWUrck1uVlhwMU13eGVUSW0yRVBRS2VjVlBXaWM2eTVPZWVUb1lRS3FacUpISHNFWjgxNWZjbTJ3KzQrZTl3R3B6Q01lR3BJcGNMN3M0ZDJxdnNlT252V012dTljU0c4SzdjZnFJMVlZUHVnMXpBanhyRndISlp0RGR3NUdUcmttR0dnbWcvdTh6YS84V1VzbXpSczFpS3RQNm45UmtKWGt0alZ1eHBnZ0xDSXFKaTRoS1NVdEk5c3hvWng4eHpNbHB2SjNualYxRFUyTFlieWloZm9KQ3I3UlA2R1NQbzFrNUJTVVZOUTB0SFQwRENSV05uWU9NaWVGaTV1SGw0OWZnRXFqQ3dvSmk0aUtpVXRJU2tuTHlESU15Y2tyS0NvcHE2am1DMEZkUTFQTE1CTUFCSUVoVUJnY2djd1VnTVpnY1hnQ2tVU21VR2wwQnBQRjVuQjVmSUZRSkpaSVpYS0ZVcVhXYUhWNmc5Rmt0bGh0ZG9mVDVmWjRmWHp4QkNLSlRLSFN4Q1VrcGFSbFpPWGtGUlNWbEZWVTFkUTFOTFcwZFhUMTlBME1qWXhOVE0zTUxTeXRyRzFzN2V3ZEhIUEN5ZG5GMWMzZHc5UEwyOGZYVDVSa1JkVjB3N1JZYlhhSDArWDJlSDErQUJFbWxISHFrTmVlR1pEd2s0ZjkyMWxiWjZCdkxzNFBqdzZ1THZaNTN3emhEdVFjMGdkUFJiZ0R5YmFLaWJMNEQ4RXZHRyt2bnV6dHN1bHcydmkxZVo3elNGVFpTa0JZYVpPdEFraVpWRFpYVFpoUXhvVlUydGhjVGFwTUxXelJNUUdleXQvVzBOM2ZIZDNQYmdPRjhrdi9ON3Z1Q3g2MEorZEhjd3k0SU1MRXU5UnYyK2ptc3hVdkczUzNzTXREbndzT2lFcWJxNGNJVTVZdVZjYUZ0cmt5Z0FoVHBvM05sUVBrUXFwY0JVaGxiSzRTSUJkU0c3dGNuaGNCSmg2WDZyM0p1THZmek1BWnp0dEFTS1dOemRVQVJKaFF4b1ZVTmxjTGtESXVwTkpiMC9sNWdyOUwrNStFR2docHZMM04yN1NSeWRCZjU2MWUremJqOEdULzZpbUJQNER4Nzg0V3VUdDVkSDF4WGt5YWluVjRtV2w5djBjSzc4NlFzUkgyV0lTN2ZJdGxzN0RuL29BeUxxUks5M0FSdlhDN2xGVlVNOUliOUxMdUpKOG1ERkRDdU5ERzVnb0JVc2FGL0svaUF2aURLK1AzaVA2L2RzZ21YLzNSN3dINlBnQXFjZnA1V1RQRjlNRGEzckQzUjNVcVZHaVVjZUZBRUQ5KzA0My9Rbm0vdlR0VFcyZmdZeU54RDRvTFREcjZnZE03cDJLazdSMTVNODMyOHBlWnBIZTByT08rQWdBQSIpIGZvcm1hdCgid29mZjIiKTsKICBmb250LXdlaWdodDogbm9ybWFsOwogIGZvbnQtc3R5bGU6IG5vcm1hbDsKfQpAZm9udC1mYWNlIHsKICBmb250LWZhbWlseTogJ0thVGVYX0NhbGlncmFwaGljJzsKICBzcmM6IHVybCgiZGF0YTpmb250L3dvZmYyO2Jhc2U2NCxkMDlHTWdBQkFBQUFBQ0RZQUE0QUFBQUFQRndBQUNDQ0FBRUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQm1BQVJBZ3FDWmNYRVFnSzBGVEdDQUUyQWlRRGZBdEFBQVFnQllrQUIyZ01nUW9iM0RWRkIyTFlPQURDY005R1VhUEZwZ2pnLzl0eFk0akFBeTFyVjZhRWk0S3FMZzZWRVh0WDdUNlZIMDVqVUkwTnM0OWhjV1hZYkQwb1JOOUY5OUZIb2MvU2xzK2M5Vk1VUDd5b2E3bXRmS0xINm9PRnFUT3o2Tis0WEFpc1RGNDZRcExaNFdtYi8rNjRvK0tJSXd3NmpKeVNvcUtZR01td2EyMStGeEV1eXFYZlg3RUtmMlY2LzdibC9MOHVtS3dqaTlvQUYzVXU0a1dib3MrN3JxSktIUjZpKy9aMlpyOVJZdEZ4NHdjcFRUaXdSQzVMTENwT0pHMHV6U1kzUXlVTjNjRS9kdTVTYjFlTm9Xb29tdDl4dDl0S3R4Y2dYMzVTQ05MSm9jSXFuT2p1MFo5TUlYQ0lVSFlJRkRtRWJUV0t1N0d0RGJQUVNBVy9ia1B0WFN6VWxWU1VIMVl6bTMwZ09VQVBEc20rdXlCb2c5SlJVYWVvMVZVamh5ZHpxcld4OUpVdE8yblpGRUtuQUN4YkE4Qlh2MzBudDE4QjhEVkFjdUxOVkYzSkE1TGxGSHpMZHVvdy9VU292ZCtBQzVpY0V6Q21rNExOY2FoZ05RMkJQNDMrMzFLVHpxeXZyQzYxb0N1VnBmTEtRbUFJMGZ5UjlxYXNJdTNLenB1ZGRWbXQrOXJ2V2MxTjlqVXByUlRVVVpldWRSa2xYVG1hQ2hzc0RhRFFzQ0FVQUlOeEJaQ0VCdjdwNzFNOTkvNlg3ZGtqeXdZWXBYMGJEeVc0ZHp3dFVBMDZ2dXdnQmErSVZOY3d6d2ZwbzQ0aUpMTWh5NnEyLzM1ZVZVSmdnSW4zdlNHQUU0K25vQUI4M3NjRHNMRVEraG8xR0pqRHljNCtDc0FQYk45K3A2RGc0RzBBVXRuNURFRmFSeXJURDA1SUFiYlVNaEtud0ZmQTRoYll6bm1laE83SElLeWNiQ1VzT1ZKcFpuRnh6WEMrbkl1ZjFQVmFEYkxnV3h0cVhDK2VyUitpSDNqL0JhVXRvZ2ZDdXpneTRPay8wRjg5SlgvOEZEbGRNajJsSmNxN1NLcjV2K2hFK0RYVHFSTWgzQkxPMmc5QTRKeUNKd0diejZVbkJlYmdmeUliUGcycGIzQTBHNzM1SEVYRmxRdjQ4bFVVb2psd2VSNWRuZ3hWTUpmblRNUEpMWEdNTVJSRGFmUS9EMHVVdTN4RU1rOVIwZHd2NURWamp1YVpaQ1p3SkxhVkV1SnM4YmJja3dGR0lLdDVEbko4RmNsSklDL0JpUmpKMDVPRmlncm1sWHNIN0UwWUFPOElKdjdMa1VrbHA0aGlEK3k2YW5WWVZyTGhkVXl2WEg1alVRM2g0ZHQ0Q2VkWW9uZlNaM09nd0lNQktEeUlaMElORkFRZHlxVHZwZkRQcmNpbHR4WUFRd3NWenY2eDdpZU1MSTNKcG16cXo5c1NNUWhOZHg2RmZRRWNTWnNHd0pZMWVjUTBWMGxXRTBZbGVBV0RvdVZDQmRFeE5wZEVqTXJJcU9hK20ydWpxSEFZTWpIVXZDQVZuSHI5UVdrVXN5aE94ZmFsVDlGUWFTSGpiMlRPVThlZEFjVGxFeFhrRUVrd2FsdndhNnlWZWhvQVRWaW00Qi9yZnBYZ3hWM01oUEtkNlNDa3IyZmk2YnRZMlc5dm03U2pmL3FEV2RHKzEvSHAyNmZqNUc1elZyZFJ3VGkvWjRaWFFmSUtOTXljaXFpR3FyNndOb1Zrb2lDalUxUUFNMlREZnJsRDExNHFTclhXYlNlREtQblN6M3lqeWdtMVFqS2hHK0Y2U0JCL3JBQWIycE9Rc0IrSTl4K2ZPMWlwb1BmZVJSb1NNS0xpbFZNV2ttZEErdjNydDlsYmRUcWNRdmxlN1lyeHo0RzdiSjR1S2pnSG5wNmE0SnRTTUVQUkxDVnpsTTFUc1VEVklqVkxoeG5Ra053azY1Qm5YZjVQdW80VmpzMU9OVlJYc3puVE1xVnRoc0Fzb1RraTg4UVdTQ3lTV3VyM0NpMVArdlNpUlBPSnpneUlxY2dsSkhvNWw4bUJDSXo5ZEM0WkFKalVqa2FvK1VpcDdOSkxLU2YycG9jaGFrd21IS1NWOCtOUnZHcE9wa2ZvMUJicEhRTzNiT1lUN0MwdzYwaVRtQUcxOTRYZy80TEZ6NmUvcDBkMTdNZjdubE9neGNSbVJUanJOOGh6aHVaODZ6a1h6NUZ3UnBtNlFLWE8zMEl6TkNISTdxSEg1VzZpTnNxSVUwZU5CeUpobUhzL1ByUHZ1UVRCSjZzdkZYdDlTanhZbEZ5QXRpd2g1MWkxbzlFVGtwVWdxY0h2TlQ4Vlg4ZWZkTGUrYzE0VTFoY1dlSEJFSkVyUVF1azQ2ZFd5bkVUaW9nVGh2dzVsbWlzWjVybVNnSk5kRjRtSHFyWnJOaXRBTnVnNEh6bWx5REdwVW1SUHQ1aHdzWlh4R0xzQmY4VnAvbGJYck1rR3I2UWFRano3RmROdDFCK3pqZFllK0pBT0x1Q2d0RDFLdFcxT1ExRUNnREYwQ0JFVDIzM1VpU1NxMjRLM3NVR3I0a2gzM0Fua1lIZDRqVnpsOFFpY0NGYk5MRTVHUjBncWk5MFBJRzU5aWpSRVYzWFRDcWZhUTdHQW5CcS9pZEY4YTJydVJ3ZXBoWWR6dmJvYWJGRmI3cG1QaDF5YTRzclRJVmRXbXJzMllXNnBMT2NDc215WXc5UG5MTVBCU3NOQTBVSlRSeXBmMmplSFZTeHBTTWZmeFNRRHdRWGdtQ2UzZXVRazVoMWlkb3A3Z0ZnWTlIMS9JM3A5UkkvYzFvT3hWNkRMQVdxeHY1RXJUdE5HSHU0ZkhWWWdnUVZuZmZ2NGtBdVhKKytCUDFJMEdKZWd0TTlZTGNOZVNpNHVQemtlUmIwSEFYaUdZa01rRVlISUVHa0M0RlZtaUR3aVVHR0lNZ0Z3cWpKRUhSR29NVVNiQUxqVkdhS1BDRFFZWWt3QWZKb01NVWNFV2d5eEpnQXViUXl4alFpME03VGI5eTViVUE4UEd4Wlg3OHFPOGprdFFUQXA0RHl6VzNBSk50ZGdqMjZ6QXU0ejU1dEhjSGdHaDFkd2VCZGNQc0hsRzF4K3diM3o3ODNMQ0h3QTFYZk51MUxqWlVhNUd4bFhuakVCQ0ZTTHdMQ0lzWXBMdkNJRnlCOUFmVnZVMFFIbUN6V0VXM2JlQWQzYUFuemY4bjhFU1BjeXd4a08rME5oZWE4L1I1R1oyeHpLalJQKzZLM3lsc1RYOTkvb3BWMjB2YkozV080V1EvZ0h3RjBmRTVLaXg0aklCbk9zaFR3Z3lVK054aER4bXRlYy82YVZOQ0p0UjVPYWt5cnBhaHdMZ2x5QkdGYllkRzVlTEpDVVhtUEJyT0MxelUzSVFid0hFY0JMalExWHdRQmVwUFZibXhwYXlwdHhCT0IyekxHMzZVZnc3eXJZSVFVaUR1V3hNZUlZb3pYbHFzaFFuN2VtN1MxMVF4WEU5bHdJQVBnT1d1UjNSRGVKbys5MjdyUjRLcWR4U0diR01pUEw1TGRXRTBNYmx6Wmh6WlhkQzI0R0FKRWFHbkxNUFVRREx5aHdrYmZJZFJtZ3ZTNHFISDZUbFVGRFpLTG1vQ00xblJ4RVdmVEs0VUpYRlNOd0JsZmhRSSttMDBiTXNZbWo5UXQzNE1Ea3VEWWlKWk8wMU1HanYrSnpNQVZBb09TalhuSTgxVUpFTzVrbW05cmRQTVdKQmw5OVhjMkNML0x4d3RXQ3lHSU9nV1BBb29pdkVrUVdJcUtnK2dSaW96MXdkeXNlTkVKVzBTdEt0UzJudURCb0NuM2YwTDRSUGc2ZVR4TnRVenRyTjZyamFjNnJnMEdZZkxES09sRnl0UE0xZkE4c2NReG1RMjBiUnR2Q2R3L2RyeG9wWjAxRURsanJrWDJES3pqcGVLb2R6bGFXbno2NWI3Y2ZSUkg2MkluVXlUTzBQWVJreTRVU3grZU1hSUtPNGVSVUhpUGR6SHZMRUFxNUs2Q2FZMkQxdDJtc2xxbFE0UkRiekM1TVIxRG9tSE9Kb0lyK0dxd1JTQWdKRVl3KytVUDdhSVJPU2R3bnJEenpNSk5CVWtPYjkwdXNpNHUwRmZtbUpBY3RlSmVQMm8zcFBZcWVCVWFCSksyeGEyaEJwT0V6T1dXZGNUQmkyb1VnRUpmZ3UvaGxPRXliWkVMMWJMUndGMHNVVHZGbWJsRTN0RzAxMlVSakFGUGtBTkhKQklaS3NVZnlKUlJEV3pZV05JV2V2SG5iSkxtWWp5VnFYWCtjSzBrL0hrTG9GbHBMbmZ5aGI3TElBWmRtL1ZXN2NoUDNpRml6NGpORUdyc25PcmpOY2VZN0J3dHNwcFFwWHdJQ3VTNi9aMEV5Mzh4TjZHSmw0eXkvTDg5TW0rZ1BSRHVlakhGaGt4VCtaZ1dzZW55VjBLVExIRXVJbUJncHM0RFhUazB3ZkNGTVlsY3krWStUSDVVN0NmRmgweEpjdG03S0tSUW9yQmRNQTJBTURGVy84T1l3UmlId0hsdU45VWFzbEsya01QL2lDdGdlb2w3d1QxTGhwemN3SjJsa1dJTzZHeWdGbGF5Z2VBcUg3UDUzQXVBRURnSlhZOW4yQVZ4NlBwYlhCTldMUmlEblpubnBEL0Z1SXdJWkYwRGZzZGxPdzVOVG01d2x2RFNaM2RIb3F6NE9XeTlyRGUwTWN5UWI5NXh6d05BL1BIUGgvNk1KTFA3WXoweEgyMTBTaDFDMEYva2t2SG9OQlNDN3hJYTJTZGR3UFR1bERFQVlKeU5XZHJUcUNJMGFNVmtFcVV1eFdRWlIyclNoRXNJa2NZeTg4ZzNkTE5EeXhtcVM2MG5rSTVNMXVJT3ZZZXVsWGNRVXpCUXdHTG1SOXd6RDBPanFtYUdIUWlRczI1S2ltSXRhNnN5UEluTzhWZlRNVmh4a2dsYjZMYnhIcndmckozbGpJUkQ2aldKZEFPME9DaFhHU0EvdG8wUWxoMFJXd3h3K0tydERWTEhoVGdCNDU2Mm1mTHNnQUI2NEo1d09oNmlNYkZOeHhsT0JlcTd1T3d6MnpFakpFclpxamhPWVhxSHAzcTZzOXVKb2JRdzlzSlp6NXl1Mm9yUjlicFdQRklmVDBCZTRuRTVEd2RMWTBISkVjY3lOZU1rcGRZU0pVTmo5V0hveTlPMDRMUW9ZZEVoTjBTalB4cnVvNEtQT3UrNjMyYStvQ2tzNFdkVGhVeG5pSkZ1dDFlNFpsaXVWMXdCaUs4d1ZKdnppeXB0Nm0veHBNTEo5MXB3eVAydU9uUlF5aUg4a01RRUI0MDVPWHBvckJCTTFLdmhuMVNkTGlwZVJTRnIwREY2VXdhSkpBSTVSVzBGblpJSUh4TC9zYmFvNGNUT0luNmdtakt3WWNpMmJCc1Jtc0hxZG9FRExJWXAvNkNFTzdaQUR2NjI2NnRHclNJVnM0eWxFL1ptbkt0YlVLWTZqTVFBMTZMU0F4WFBmaGxPR2Z1RUFSZThnSWp1b0lsMnFpS3Q0MXZvdzRkSFVwQ0txOWNySXlNbmlJeTdveGRHK3JtOFhQQ2FBMWJqR3ZRRVUvaWErekYyblVjNnFxb2cxYlh5WUwwbVFBUGpCUTVQaWJ0eTZmdWRyUk9nNDQrUUtrdXp0Y0tXeFNMdzFVblR3MWhyeFFHaXNlZTkrcy85b3NJRUNsOFh3V3p3bFIzTmxBTVpYVHY4SDBZbnY4aTg4WmRBYVQ3bEFBMTVTK0pteXBpeWJrQXN4QlZxM1UwTitCRkZHck16a2l2QTcvT0hGT2JVS0pmL25aWnJobFdLRG5iK2JIN0haNSt6a1RWYnQxeWJ4WnZBVnkydHZGNkxzMlliUmJZOE52WklWVGFjbTdYenpKanhEb05pbEhpeHFsSmZKOUxIekgrbWdJQ3NlR2FYOVFSVXZYUFBFUklQa2w3ZmdrZUp0VzBIbVF2WHdZMlc0Q1pma1ZxTFVhc0lrbVlDd3ZpcmNjalRsbXJmRlUwRGVSUkpnTE8vVHIxTlg2M1IzSmJKUDhrL0UxYzlKdWlYdDlqSnpkN3N6Q2lQUG13S2laczFad2F5R1VEVXB4ckdvL3g1WjZ4NFhjSENNUUYzS2RqVEsyeXBYTW4vaTlyeC9nWVZVcmcrY25PalhvdTFRenBMa2gxdUtweGc5Z216eGdnQW1zRHhQdW1pZkpJM3YxVDRoNGRMVW0zYldwWVhKOVZqcjRGaEhqSUk1NUcvYStNdzNQQml5RXE4Z01aL2J6TndpbHlMN2dzQXBGWlB0T3hqRTBjTXRmSlpTRTBuM1pZVll5N2pjOXN5a0x6TFRFU2kxL1hNYVViMXo4QS9iUlBtSDlIZW9GSFVSeGlsT2k3KytmZWNjZ21NRTBmMzFaOHZMamN0SmtCdUlSVDRBbitLOEZUUVdKTlIwKzg5MnZrbmJXcUY3NEJLQmd3SzRKK0ZmMW44d0FhTlBET2JrMm9WbFQ4Mi9FZm5ZcU14OCt2bTFtVzVpQVJrSU0vd2pXemZBREh6OGo2RlhOQ0VUdllqd2Z0dXRTWjRjMWtSNUZIa0N2UUFqSm83QmV6OTlLVUNZUTFjU0tLWlNTN0l1K3gySDdxV1hKQnoyZ21OTElJODZJemZkNm91cDRHU0kyZ2FMN1lVRVVXR2lEUzJ3dWpoTllGYUpjaGUwbFVYenlybU12ZThoT1BVdW1UWkhhZDZXQjB2Ly9vZThGR1FRK0lOTzA2Zlk1QTZVVkc4b0p4aXdmRlViL3pVS3R1VXZYeEtOQlIvZmJlOUsvTDdORzUvSkZyeVV3MXU3T0Rac3pKLzRZWDRSeUxLSU84ZWxnWkpJdHZ6S3E1S2E4MzRUSk9HZ2t0WU5zZTNCcVFNZWU0QTJvOGx5VWwxTGQxRlltTHk5bCs1WSs3S3NCQ2pLODg1MlNNMDFuUU8xRWhrelBISThITmVZMUl2U3RwSkRkL3FsbklIVXA3M25oVGpHeEs0MHdrL3FuaEZnbE5maThzODF0cFBZdU9tMGdpTHlFYlB3cW5hb3MwdnZlVzNQalN5dFJJZ2g1L1ROL3ErWVFXYVJYcXVqZmxqb0k3cGtZZ0ZQdmoyWVBFS1FmZEcrN1pBSXBoR0l0YUttRnJqdlZXc1JxSDhvdHViU0N6bVgvM2xSdzdjUS81UVdpQXNFV1RDVW5Qd2xoVHVUTWpZamhpUnBqRXhpTGtLRkRjYUVxak4vK3p1TDdNWFBRZzZDTjM2U21SV2RLMTBCUTN5ck1TVnEvaTB5YWVYMXdsdGU0S1VkOElyYkRaZWR3WFZOb0dudFkzSXhSc0J2MW5uV0Y4OWNBNTgwVDJjM3pLWUluRW5KZWZwblZGc1dOaGpLaXltVGI3d3BXSFY3TlJxUDJNdFMwb1ZvVEd6bURzMGVKT1BUcXZwOWpyUUhtRzM1QmExRXAweU1jMUNuSk9oTExQRzJ4UHdYTVNmR2UyN1ZISHY2cFptQ0pyT21XbDJ0TzhlTVNXOWJsaHJTQlpNWWdTaC9SQ3c1OFArM0Ryd3JIRjFlLzdkamFzbzFuc1NqdWlJNHRwVGZJSmwyelVjWEhqL2xOZjRVeFNGMSttWU5GOFJOU0MzejRLbXp5eVY4a3Rhd3BiQmFYUGhxN2FlbEY3SlBuTWY1WFA2eXkxbkJ6Y0V5MitNd0cxVVpuMDBhZVprbVlvaytrODJuOW5tMzUzZFJZekRMeHR5NmxWL1J1WW9EcjBXT2NzWXJQaFJ3U0FuYlhudjd1RU5RK2RxSGx1MjVsVXUyQkgwb1FVcUFad2g2bGs1VXZiWWtYaVZnRStTSStKS1l4SGp5WFgxbXZlYWtLTkpSRmRPemdwdFBxNzlTVVo2c0ZyQUorSi9ycTN5MFVVVnlNWDBZKzRoOSs0bVR4V3BEZ1FBZGFuUmMyWFU4ZFZmR1lkN3haTUtReHlSbnJzMUkvOEYvVXVxeGd1YlNwZXRXM2J5Nmh5M3I1U3M1UlQ5STVEa0pyV2wwK01EVXFBUkQ4QVRGQ01leDRsWnBpRXZoRnByQ1cxTlg4TThRa0svZTk2YWYrZVdkTFhkR3ljNGkvTDlZdjduRHNDbXpWREhsUGVnOTRwSnB1SmNEUFlrZTgzcU9qbldBSkcxT2VaRHpPR25qcDhkaVNiMmx2RFJ1OG9YS081clF3VU12QWtpU1B2UzlvMTRPRzkzQm1DMWFvVXFoOEFxTEdCNkVQTXQ3VXRhZThYemNpMDhDUUFocThWb1VOSHZSeS9FdVhYREpVTWs3eVJicDBjelpsRjArN2pHbUhMa2VMVm5KZVpMVytPSE9VRkYzUHUvdVg4Uk5nZU96ZCtVaUxncHYrc2FhdTcwaFFNZUVNYi9rR01YTEg5TVZLM09teDBLR3dkVGhHRTRaWTZTTFhWSWpUZmhOWEx2blRJc0QyQXVwbk1UbXpvWEQzODJtNUpFakNBTlJiOFlDZ3pYbFVUdWJ1T3N2RENhZWRlRVkrdnVJTDFCeUlIVWg0ZjNYc0VjYk16ekFEcjd1RkM0WFZkM2MzQzl5TStQb0k3Qjk1UXJVclhxc1l6SHZReUNjaHRDSEZOTWxELzlrVzBPcVhuSUw1YkFFYjYxUGZFK01zUnZmQ2VKdTRpL1IvSis4YW5sOWJ2eDYrL0xwVUt1N2s3MUxRdmdRMFE0V2M1dlcxQlVxczQzbDFZRksvMHFIUEl2WEdPVVNaOG54Wlg2cmpQNEZoUWp6OGxvVC9qcmJJMWRzelNhdFVKZEx2bng4Vk1ZaEpvNkhidmUrK0Q5MVpDaS9nTnhLWk5UMUQ5ZW1WK1RrTmgrSVpYb0s4dDBJS2Y3VEo5aFJIT0taWVNyR282TFFqMmd4U2I3V3pYK0xFeklUUmI3OEdvck05bTlQalMrK3V6WFJWYSs5MlRyM1RXZThKdWZ2WHRIWTlacFJ5SUh4dWxOSE5pVXU0WThIVHVUSVhOb2c2YmN2SC9pSnp5R3BKa0szQzN0V0VqK1BVVnFjWTBNQ0JPYnluZzNjbFZmUlBuT3JaKy9KeExITUF4OGZpYWdaVEg1ejFsUnh5aFh4ZTZkNU1kcS8yMEt2SUpCMkN1ZFNXSTF2NXVhdUtEUFcxa2MramhNdTNlVTFRVkVjeEliaUM5NDR1bGxFSzFzOGxELzcrRWpORDhPNk5UY05hVlhkUVUxVDNMaG1hd1d6cHNNaGZsZnBMeFkzRDZVOWgzT0lXbTZnY2NreEYrT2pjaEFwQjFNdmYxWklGV0xpOFJrUENiWVNJRFlSbFlxa3JJajFtdjBlamdmbDNsM05BcGNTRTNMeFVWa1cxdzM5TCs2NFpsM3dCeFkrV3hGRXRkdWIzMUwxTVRVaWpCSFA2RFFpYkN3OVVSUGxtNDdOU2xqMTZsaHN3ajZia0IzS2N6ejVhWEozMHFiWjJjK3oxQktjZzB1enVuaW1VZVdPYi9QWmJTc0dSaVVZUStsNHppbVZ1cEo0S284by9ORlA1OFh0MzFlYjA5VG1EbTNWOXcwWWo4MUsvdktXTFNEaThwN0ZlL2tGaU5sYnlydXlDbFJEMFR1N2kxckhYMVdSOUhaTXFJaGV3WG1DUms1MkI5TmlKcXkzL24rdHFUVFBKQ1poQ1NrVlJhbGNVcU8zSnlpc0ZRWVN0TFlOTmVuc2QrUE40bWltdlBucDBPREd1bjA2TndFS1EzaDJuSjBPblFLZzI1VmMvWmIwMDJUK1I0TVB4RGU5MDZDaGtpemJjS1Z3V2hCRC84aVdrOEtPcFFhczlma0hUai9zOWoreWcyamJsY0s0bTNKRHZZWUl2WG1RK09vbkgrL3VSN2txSnNyT09SYmI0RHFxSkQ5ZVFyWUlPTUsvTGxTbmttbVgrMjEzejhPTUNRVHk2OHFUV0NHRTk4c1Z2MFk3SExDcW9tZkVvbHlTeEoyNEovUWlDbkZtb1lySDVKUUlYeWJrOG9hbE9kUVpsVExjcmpxK21seUFKVktveER6TGdEczZoRkNHN1FOL1VGZ084RVNZUzVHNGhIaWU5cS9BNGNXQjEwc2doSmlKK2tzbTJMTml5bFR4R3I5ZWlMRkRlZG1Wd3JpMERRdVR4bXJzTUxsSVRvWmc3L010bmFhQmk1VG9RVG4xTkZQeVZyYVU2dmh2c3hCWHYyL2NQMzI0ZTJ4ZGZZMno4MEZXZkZiNkJRZEozc2xqK2s1ZHVTb2pTM2VsdStlNlNsQXVpN3lJa1BTY1M3QzdZcUF6ZlEyU2c0dklqdjJ0VkNKYnlDWnFpRmZyQWoxVVVmU3ppRWpwL29rZzROQXZGdGMwMUpaS0VSb0twbTdSWVR3aWp5R3hJMWVFN3BMQTV1NWJXK3RmaG1YYW9vNERsUW14T0J2Vm40N1BKM3RUZzR5ckdicHZrZWlFZWZxY2lFTjc5am5kdHU3eHJpbkxoMFFHTVh0TnBTUHNjazYvVXdZdmZ1b0ZBQ0JkL0NFK2dhOCtiTDJGRVhJaWxIZFJlOHBLcFo1ZEU3cmdCTlFxMDZvUFNucUs5L0NTWGpwVVNLbnlDemdnaUk4d0t5MUttWlQ1RkZjdEZTSWxDdWtoN2MvWlhZRDRTQnFzRUR5OGQ5aXRGc084MjJuVVBGY2JWWWs3dkRPTXRhRTdKK0lYZm1weW43RFhPNmhXWnU1djRRREV6Y1Y2R056UW9Vck5TbnNTOERib1BwNGhVOXRDMVluT3B6aHJOUVk2MlBnZVFWNCtpQWJONzErL2ZIcnpxcE4wczJyYmxtTWFMZ2NaVldNTkV4WDdoUXBScmRvRnZwOU1jUjRxa2ZFTTVJVHpBOTF5ZjJIeXNia24xeUpkVU5uOENTSFVrSHE2Wlo3YmpWQkZIWWdBQTg0REpTajRpZ2h0aTRucXlqR2ZQcm0ya1U0SWtCbU9YNkNITjZEZFYzdmRha3hGT1VicnIwQ3psWFBlM0tJRnVIRjFNWVZUT0xuK2IyYUtzVEY4WGlHVW8wd3Y4ZEFhQXZ1TGV5aU5DdW5KUVF2UVR3TnZVSkVvZmNCNEZtMXZmNVNZZDFWZGk3R2xKMWVUTHFqdlI3MHNmTVNFMXRPTjg0dmRpSllsSUhSbVl3TUdQTDRhU2R2cmptY0Fza1htSkdIQjVYUy95YlFvS1BFTDd0MDdMNThiNHZnOHUxenJnQ2kvYXRpazJXM0VZWXYrVEx6NUNKQlBHdk41UmZmcG8vUHBlRmd0cDZPZ1VmUmR4NVppWGFpREZUYVFjbFpRNjlsVmV2b1dPMUJCVVAyQ0VNSU9SQmpqcTlPZ3BoOEJsRFpaeWhoT0dGYWkxVzJXL25xek9Hb3h3VTNOZjMxZFBTNlFITCt5K3JNbnJUUnVsenRSRXNxeHRTZlhuQzdvMThpUVA0amhkVVdPTTc4b3pPdnBUcFFRV0Z4dE5JSWh3U0g2RWUwZy9VMFdLMjc5Z0ZLeHVKUnZzR1JZZkZTU1l6cUw4MkRLa0hSekNYZTR2TjRpbGpjV3ZlUU56S0pWOCtpZVRISHorZXhJQzh0UXNTQUZMbSsrdWNVR3diOS9mbnk3ZmZmaTJiM1RkSnozMHFoYTlteTVvR2MvSmlzaGV4SWVvTWFtdU93a1lsQ2JvRVNBSm42SlhhRUlXNURqV1ZEVlc3aXZYUE1wUnRJT3FBOEI1ZXRuMHcwV0NHN2QyS3hHZVJJRnJYTFJrbkNmOXlzRG5LMVVFekhoNnN3MkNJTVNQWThBTjVsdytha09qRWFRR2FvS3JrM2xDTzU0MDA0c25uV2ZOaWdmUEtWVm1rVUMvMzNJN3g4MWFmQTVGWHRaZEJNcFZpY1JHdEMrb3BiUTBpR0NyWlBrZ0JaSWF4U1p5SHhQUUxCNDkrRCtjZGZOZ3AvaHo2SXZKM1p4MSt1S3FlODhXYTh3VFdROTRGcnhISlJDelZjd0dIWElrSVhtTk1KQmFTUWw3b3FWVjZnRW1DVDZkVjgySGswd29jUDZhcjdCRXNHRGV6ZW56V28rbTR5eXVGa3YrbEN6Tm5RN3gwK1BLWGlhMm5PNnlpaEFZL3dNRHF0VHZmUjUvQzhRdkNuM2ljKy8vdGdHcFVadUM3RTVWTzZtaEc1aUNCcU9kbGFEVUNCK1FhQWhHZ2FDR0xGUElPTWw2bCtQUWdQOXRjVU5HcTllUG51eTIzUTdVVkJxQ2JFK25Cb2tydElwTlNBVXNBZGQ2RjA4cUcyK0xqRE16ZkhlVFNQTDBqWWZtMExiSThKN0RMa2VqSmdaTHFTVGxMSEFSS2hBVndqODllUmEwRjdibm5RTWl0L1FERGw0NVpNR1BZTGJOeGV6ZnJkVmRXMnA0RFJQYjdSbnEwK0l2SE1xWkFvTGErWk1XcHhNY0FrdGFiNXBRbjlxcEliTVlhalkveUpUMmxHWUVqMU9Uck1CWkpXb1huSVZzMktuNXVieHFBRVZWaFlxcUZ4WFhJQkg2MzM2a0NSeEZ2VS9KZWJwaDZCVEpNSTNwMm5KV3pTSEhDOVFvZ3l6TUR2VHpxSzN4K2l3WHFrYTJDNkpRZkpmNTFuclRtaFY4TlluUFJyYjdjL3YzNzYrZi9mNDRmR3cyOFNOZ2tkeWJVTnhpdUM2U01SakdFTlFtL3BYVU9rb081aDVXeklENll2VUI0WkxpTjVhOUZlWWtiT3JwNCszNjlFZ2pVMGQ3L2l1MEVKdURjcDA5RGpmbWt2clBLR21oWmUxditwSTBjcW1JT0NZUndRNWZ6VkxTUkRDcnh4dDhENzFVYTkxcmx6TyswSE50dFRxcUJPWHhMSFZEOUp2MEhDVVlzZTVhRDNkaU1JYXlZN0pvS0NDRWhrQWpLUlRPaTMrTWhGZkJMRkk2QTdhREVremwzREw4dXBnT1c1cVMrbGJoN0hnK0ZnZ1lqS0RqWTRLd2JNbjU2UFduWXpDZHRHWEMxN3d4WFJ3Ly9xR21MQ2VYVXQwd1RLQVFyQWhNdGM2VEt5UjFVSDMxQXN1TVM0U0lsOGk1dks3UWVYSWFkVkx4Y2prWW4zL0hveFUwaENVVnNBaDhHa2hmQUdPTDdoM0pDQzh2SW9ycmErL1g3eDl2WnlQaHBWeW16LzZHYTg4T0VRTjU0ZE5YTHVHTHFTYjhHcUNVZ0VxWStXRnRDbldmblZkQ2xLVW11bTd6N2Y3N1h3NkhyWWFVbURKWlpWZFczMU9jQ1hIUTRhZktodTBDT2pFRWFjT2VqbVB3Y0krUVRhdkRiTFVVUngxd2l6ZUlNSVc4dklwcSszMDVvM3RlanFPd2xwRmNMemt5MU9DaXJadmpFbDhQa1RkN3JDazF0UFVPZk1GbzdWYjUwZXErZTBCbmlKRE52ZjA0bG42TVcwVTRtbitkVUpBV2lYNXJaVlN0Y09CVk5YS2h3ZXZkdHlld08zSjd6RTIxbFMxTmM0djRFSmg1UEs4WG8xcFFSV1hlbFlqUm8zRm0ydFk3OEFFUjg0ZnZ3SUJyY2U2bXRLUzRxTENnTnVabmpRZEQvS2tYUzBYSFpVS09uc25pOGliQUFRQUNIamo0V3I0R3l2dGw1THdKUURnM2Q1L3EvZTdyTStxZXNnbStBR0VEQVphS1VDWVpRS1Z3MUtFK2Y5TlJwWEZTS3kvamNXOU5RRG5yeWNRTGpUeHJUUWFBUzlYQWw3VUFWV2FjV0tXUGQvaHhuaDlsaExFWnpQUUpvOXVXQXJVZWJhK1RKU1Jlcnc4WnlUZjU4WmpSREZKSGZmcjkzaFY2dHVJMGlZcC9oQ21uZG5mQUVBQWlOVnZzUWVMeWVIaHZrbmFIb3M5VWZRaGdEb0ZBSEFNZ1BNS2lOeXhGVEN5R3lzSUFoWldJSXhOdUFLbGJKNHJFK2NWak9pRFdqNFlwcmg4MjFtM0hzdjBhZFdzeFFBcHJRZ2RLVHVyOVpPWjFyaXRsVXA1Tk9xWDUxMERGWk9LRzNuRTQ4YW96Sjd2bXhsViswazJjVGlKNStKa1dETjJlbUVOT3R4cG1kd3pMbGxTcm81YmRPZ2s3Wml3SHQ2NmhsMjZkWEM0enorMXl3RGVoNU9OdlVtejdmYzFoVlJiMm5TLzNPUjVDQ3Y3a0ducjNwbERnbVJKNHNSTGxMamw4c1Q2K2pMZVFaZWZabmc1Yk5ZcFhMMFdiWDdYQ0RNYWY1QWxkRkdwMW11cVhmSXE0ZHVtdlhPaitzTmFiZk9saW53RDFQaVF4eHVtRmJ2VnBmS0ZkVjcyZUVHZStWUWxXNjdhcjRrWG4zQVRINTY0ejVaSzBVbEdjTVQ5dUFFUHlnWjdwTlJDMmFqQnl4YW9PcWhIZmxQdFZ3MW9RY2JxOGZkdHpMOVM2MnIzUFN3Vk5sUzhWaDBlV2dyYk44WklobnZWVG9YcnBPa1UxSGdSNVQyTlc2emZMeEpIM2pQOGZtWUwwaDJvdi9RdGdud0M3Ykt3eFVVdVRxellYUno3L3lHYnBxU2lwcUdsbzJkZ1pHSm15Ui9menBIL09BRW52ZDBVcWRLa041ZzAyTlZxdFI2dElQY0pkNHJLNXdpMGVieVNmL0VjbnVhQStHOWVYZnFWMFhrRmdjalh3eXdKU2RNbkdBTTRaU0h5NFpENStpendQRUt1M1Y4dTQ5K0xvZ1lqem50Q0hBWDFzUDcxdGx5TWoyWkg4eG9DMGZWUFhKd2lzdWtCY3pYWDRBRUFBQUE9IikgZm9ybWF0KCJ3b2ZmMiIpOwogIGZvbnQtd2VpZ2h0OiBib2xkOwogIGZvbnQtc3R5bGU6IG5vcm1hbDsKfQpAZm9udC1mYWNlIHsKICBmb250LWZhbWlseTogJ0thVGVYX0NhbGlncmFwaGljJzsKICBzcmM6IHVybCgiZGF0YTpmb250L3dvZmYyO2Jhc2U2NCxkMDlHTWdBQkFBQUFBQ0E0QUE0QUFBQUFPbHdBQUIva0FBRUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQm1BQVJBZ3FDWmNYRVFnS3pFRENEZ0UyQWlRRGZBdEFBQVFnQllrU0IyZ01nUW9iOURORlJiQnhBQWhRajFIOGZ6cmd4aERzVTYxNmlESUZYcDFkN1ZXVDF2UldhYytranhJN3lyQVEzRkhhUlZCZWp1Tmc5M1JXL0JaUE5IZmhGeUVpZ29EdjU4Lzc4VThJUWJjeUdFZU52YldqWTYxK1BzcTRsR043SlZkSFNESTdQRzdyM3paaXdBaXAwRW1KUVpSZ2p4QU1hQU05Q3lQaXdxdG9mL21yTDM5RStxUFNxN25NWG9DZkwwd2IvaEtjS2p0ZEphdGNwMXFWaDgvYjYrK0RUalBJdTFzNGtMZzB3RWlqV1JwWmJCdmJxTUpGRkdmOEU5Ui8xS1YzbHpFbDF2TFVYeUM1UUFHM2d3d1hCS2tvaFdEUE1HdWJudHl5Ly8rNVZmYysreitCQURWaWFUTU50Q2hrNkZGNnhISlMxR2JYaTgxNjdGS1hVWGR1andKVm5xaHJ2c1BwMHpUYWRyZGRXYjc0d3Y1WVBnZlpZWkMxRDJidnJ5Vm5YcDJqSWU4azIxVHMwS3Q5blI3cWpvUTZDRGpNYVduMk9mT1ZMYnVBQ3JST2dWbTJGaEFPOEZ0L2d3ZUU2UUdnWS8yZnpiVDlNNUp0R2FrRGtsTVorMUJiN0g3QjIva2plVUYzZnJPN2dUMloxN1FIcEF2cFpKS0QzREZVSnRRWkZZZkk5UG93ZENtcU5HMmFKa1dacmsvUjVQWDBmdS8rRFpCRkh1Y1F3ZDIvZUxIdDlUVzhtYlU5TUlJdFFJRGlwbis5bmdTQVkzN2FoUXpBZHp3Q2dHM1B6d3o5ekFlRDV3VlhBZkFEeDdmZk9UZVJ2ZG5wRVhDbkVEeW4rbEx0eElHZFRRZ0FBUHdBQU1jdXVDRDR1WVNPSTBsTUFqMGxMTHVxM3FuRnZuTFgxdXdYcnkvT2dyeUs5Z2lsVjRtdnIyRUI4T1ZMcXJUenlBRkJ0V3M4TVBzWHJPZ3M0K0dUNDdrTmRXOWhENm42bGVMeFg1WWVLTGRsUzJsTlVFZDRwTzRBQVFackNJRjR0bWNJQXp2Z0ZiVDBxVHVrbXFmWmJsWStPeTFhcGNRNUk3ZE9rM2hUb1BPNDJ2bm1rZGMyeFIwZXFtU21OQlUza1VrZXJrU3doN1FsM2dCZEJKNXdDYUovNWJtUWF3Vi9CSXBRYkJFaGlMaGRQT1dHQ05DQUpJODdJSmxMam02V1lWWnA3Y25Na0FUUm9yR1R2YkJZVHJ1QUgzV1Z0amRSajFwVVJEcFBiTDRmMmRRdVNVckxMaXlwdU9LZGVJMUxQTGlCVkdocCt6cGNrUXhTK2JRRGNJS0FZbEJESUNDZ0ZaRlM3S0VvUjBVL2poNFZjTFdFd2UyL0pnWDBGSGtyOHlzTG9UNGNDRVZISFFJMjlvbE9PZERLYzVDajYzS2hFREdnZWVEV3BMMDVkdkJ5Z0F6VGRkeTFJWUdTb3JvdVZ4dTZMZ3dRbS90NkdTcWVBTU93anJjYkRKQlNwNVZvLzBRYk1wSGhSaW5mYTVzTVdSZ3RBQjNPZ09qWDBLeG5OY1ZpakJrWXNqRkxUbUs2NEM5SnlyUTM3NTdFZ05SaXNOcEFkc2JCN0pyTFZ0WCtubkZFTFdkR0NyOWsyYzJ1M3RsS2p6RmVNaGNac0QzcnhqZ3ZJWFBMY2Zpd1FuR2JoL2VzNmtMT0JVUjhvWnhDaTFieDB3Y1lkNDlhek5tSm5KSUMwYlBVWTlSenlDRXJyMHU5Wkd4OTh0aG5CdUJKVFpJbzdpQ0ZIZzdPUXk4RExsMXpISnRGdzBFK08wc2psRm1FVTVML21zejYzQVJjMi9UOS8wY2Q1RVNvdTk2UWp3eDRUdzBGeU5BVmhCd0dFVWRBekVrZzRXU1FjZ3JJT0JYa0hBVUZwMFV6Z1lMUUY4YXk0RGVXRFg4djZWVDBXSVduTEkybE1od1BrTTVoVUhJRVZKd0VhazRHRGFlQWxsTWhnNk9nNHpRangwTUpHZWhiQkU0eVZJNVFsUlZzSWpwR3VpNEdVQi9VSDlOa0k0Qmg1WElVSlk3Sk1GdC9PYVcwbjAxVGx2Yk1PWTNIVEZaQjQxNzRNS3ZNK0ZSWXRkZ0N2UFZhV05XM3dodFRIaGt1VkY0c0F1RlA4UHJyOFg2VXNXeFIrc1RRQnBRWWVxMElELzJDRE8wb2JwYUd1UnNzVXRaTlhSM0lzTXp2bzBnZW9hTDNpdW4wWVdobkdPRzIxSEE4RWJQZEJRWEJ0U2VHVGhBSVNmSTV0WExuTFBoME41MHdvRXpPcHZQbE5hRFFWQ1RKckt3SHRuUjlWK0xPL1pEemtVVGxZUnJ4dkdYQ1ZTaVJWQ3JQVWRwQVkwTlJZS1AweU44RnpMQWpSQmgzSmlXTTVDRU16NzBxcWlpZ1E1R1N0SEZNQ1ZHMkZMK2tiTjFxd0lsRUNKRXBGTUFsZTh4NlhldlJVbHpTWGRIejNCTFhYY09pNFRmSWQ0RVEyRWhPU0NncHg2cXhndG1JbWdYUW9ra3NHUlV1cHNMSTRIb2dWTGtDcFdoRmVPNHVJRG54ZGpoWE5iMEhwL05Wa2NhWjhwQlZNeFc5SU9iWlJ3by8wcjBHbXMxb1dBeEJnWjFxVnh3ZjlVVTI2T2xwZFVESkJaYTJRS2hVbG54OEYrZ002S2diekl1RTgxZ3p0NVl1SFp1UnhJSUdnK21oR1V2WlNnd0VKU2cvQmhwNGtkSHBocllWK1ljWVVoQ0RRSlo1WkNsalNoZTlVWHRQcVRRbFdSUzRyMUhMR2FNcEM1ejVjNElXZFRwMG8zRlZUTmlPRjZkc1JUa3lnTzVzMnFHcEFuUmNvSEovUU5nSUNyUnBiNDZXRTBCVjJwemVOQXVpQThDYldJaERFQjRFQ1l0d3BhZ2tLK1V4RkJBZkJBbXI0a3JWZ3JsNERRcW9IUVFKUzNDbE9zRTh2QjRGTEJrRUNXdmdTbzJDK1hnVENtZ2VCQWxyNFVwSndkeThGUVcwRFlLRXRYTmQzZ0htSjVzNnY3TlpQTVM2V0FsME8wQW9HQUU5Wm9FSzlNWUxvQzllSVBzUGpJQUJzL0FaQnVPRk1CUXZoT0Y0SVl3TUtJTFJlQkdNeFl0Z1BGN1VZUUkvYUMzNFQ2ajkyS2RFYStyVjB0bk9zaHZUNndOZHpCYThEa3JyNVlxL3RrWG5BZnBaWWo0TUNOUFFHd0NDVlRNZnN4Zld1TGo2bHlva1V1bkY4WW1YVjlzMWY3d3h2RCt4ZUxyWkxuelo5UjgyeGo4ZlRaK09raGtrSlErQ1h1eUFENG5jMWg2UjJOQ2l2MUJZQUJEbWR5TGVwZnl5VVpkS2xrNDBQaVZpekhrd2tRbGlWVmczVlRkaWQyUFEvTVp4aVA5ZDgwRjMweWwraTFkSk5PZ2NBbmhtWUVPSWwwUDM0L2I4UE1vVFpUTk9UWFE2enVWZGdHclJvazBCR2lBVHZpa3d3dENpaUNtZHJZVGkxQkQ1bWluZVRoK29xdlZyaGFrYUY2a0VjVHNVOGQ5RTFoN1JSbUZETFE5dlMvWkhwL3FOdEM1QUhQY1JORDBEYVVTbE9MMHFlazB5b2gxV0w5VEhBRzhnRGpybG1Lb1E2Zmp6UXdoUUFERTZTSWtxUy9hRVFxRnN4dzhnOGxTajJoeUtFZkFrcXVFczUvSVZDSHhNTU9XcFFZb21pT0dibjYyTzQ4RElhdE5nWkxRT0lwb25DWUNvSXFhR3R1YUlLVzU3VjZWVGJRY3cxUnFRNDdvTzBGRVkzdDBheEdhQmp1NC9oMVJhYTY3NlE0Z1lleWFvVkEzNkExcGVKd1U1U3p2Ukw0aDJoYnRKRy9VeVlyZkRhbUdBR2xyTzlXa2lzWkRrOE5rbkg4ZHZCcTJSZTZKNjdOQjFqSEF5K3VYUTN1bEFZRjFTRUhxRlhnQklvVHo3K0JOQXN4aTlzUmRyYmMvVlRFVnA2eDE5R0pWbGQ4aGF2VEVVdm1IZzJnQzFxRHVpS0tnV3N4WStUNFdtODd2bVlwWXBvb0ZZNUFNcEhXSTJRclRYb2R0b2xsK3hzV0dSSjhzdFBGbjlMakZobEUrOEJsYWFHc2xNcXJUTmhLZzlKY0lBWlVXUkQxQlJsMGV6eVVjbE9qRXlsSGVzYU9tUmlRYnVmdlFTbmtoMnJOVHJ1QmJjYTAwNkNpcHFaMXlXUEZ1Sk9oZVZRMiswK3VBQ0p0UGlOMXRkcXUwc1RoNUVzd2xpM0Q0RzRPV3BkR09xWEpaM1QxMmF4ZXcwTUpKNmc2Mld0TmhUakh0aWRFckR0VXNOWVpNRHpkYjRUY1crclhVQzg2TDc4VHdpWDQxR2JiRy9TaXREbXVqdjZ2aE5FSWN2SXRUMkxEaUtteUJmVVQxMzZQY2VVNjlkZ21lSDcxcUpaR1o1TlJHK1FQVUZoY0h3ZEJsTURJNDlrdXZCV1grZFo3MVlMSXhYM0JrVFdVMHFFSFZwMXFXSzNKWVJIOXlvSm1yK3JPUldDNlZBUzlJc043c2txQnNCd0tqb0JzKyt4cU8xR3hWWW1JZ1B4dUttSWRaemt3ZUVvOS93ajNpR3lNcThJM2Jhd0JZNVY1Zk91YVUvV2J0RVpEdzV0Q0RNSldJekdsSE5KZDk0ejJmSzJXMk5KNE1KNTg1bXRHellpTkZoUmI3S3g4RXlzWit4WTQzYXRQWXZnSGZGS1phRzV2TjdWS0dkM3FnbGYvZ3F6clpHVHA0eWdaa1Y2WVk1bVNuL2xtamxXZ0hFKzhxWDByc3ErbXNnRjFpeXM1cHA4KytTd2svRGowTXF0ekszOVlnMktKdCtXQ1hTTWVuemxKaWVoVlhqVDFqaGVZYXlYcjlxL2Y5RDV0a25JZVRNUUxjc2FSczJmTXV4UGFLUlZMQ21aYU5CSmxrSjZlTFEyaU01RVZSemZzazZiMVpYTmt2bFJDNmgzL1h6NndPU2U5YkdnRms0SDdsTyt3c3JiYzhwQ0N0RFcxQlJiV1ZkWmNzY0dxOTZKMDVadFZYNmY0eU5kcER1VHVCYk8wa0U4K2JuS1dwSWxvbG5nT0U3WXAwdUY2bHE1UVZlL3Y5UWR4aDBLYlY1QjZkVXQxTVlGMTdDYktrY3VuWFYzYlRwT1JGMC9JbGlJZUZLVmprVVdpM2pGQWR5R1BqV3U3cnViREhlVVJnZGxMeHdyK0M2YVBTSFRCU3lZaTlSalhPY21sMWtiRk15b3lNb2lNcnVsSCtyVlhFNTZGZ3dDNCtJWDJWQlg3TVh0UGZkMEdicVc2SVJNWHFoZnVCNDFham03S3VjVmRNVTRTRGVzQklFckg5YVJFVjlCMkJ5dE93QWtGOGg3VEg0T3RGWUxjM09XY285TVQwbStsVVNBVDRVUUd2cFNEVTErb01SZFRVQXdYei9ES0lkZnZlbXN0cXIyeFNGUXdxU0lXNzBlUjhZUHVUNkdGZ0J2Tk9WcVdHNkxGdXczMGxXVzRFL1d5THpQL3N0SHhjeXBtamIrQy9FUzd1OUZ1c2dTbVMxTFYrWFdJMUdwTm9BdUJmOFRmUjJHcEdPQnVrbGgxanVKT0JRWEpuajdjR2JSYzNFZ2ZmQ0Y1NnB4WVREMkd4U1V4YmVxVEdSajl6ckhmQk5VOGt3ajhnbXZPY2orK1UrRlRHYUVEME8wTnBCN0RpblpROEZIUXBSNzNnUnNiaUwwQzE5TFRhMGdZN1NPaDkwK3V2OE5ZS3JMZm9EOW9jQUF3M3FFSytCbit5UXJjcE9sTFlTTml6TUFyMXEwa3U4K3B5b05tY3F2bjJNMXZhWFFsazJtOTMvV3JDcmZhMGxjMEZSSDMrZE1vZ3F2cGlSUEVhRS9iKzhUTnd4RnNFQlFadlpjcWNGZlVmK0daT3U1RzhWME16aHl0NGpidzhzUUNnMWhVb1NyVFNLT096dFo3QmpwT0J6cEQ0UFhDRThuOGJtc0JESDR4dHVLeFh3MDcrd3IzNjdpUXNoYTllQWEvbEhTNVk3VkZXWnZ5Rmw1SnRGNTF6cnF6YjYxNS9rUVhmdnZmcUNxWnJKU1BuMHJjZG1Sd0x5eTI5blVWa0RaZmVJN0hwTGdBb2JHelQ3M2hqVUpENTQ0WTFVakxSZlh5ZHFNMTZSUGtoSkpTR0k1TllGTHNiOXBDcHBTcTVCZHNNY2pNTHFFaUdibXpxWHB3eExXb1hCekJTNlpOM2c2K0hzbXplU25saGUyaW1SLyt4SUM2YVVCMUxlRjVuV2lxanZIeVpDK3BrU2pCUGlQZjBnUWVZZVg1aGpNK2o1WnZlaFNKbjcwMHJIaG55T1MzdXdvYmwwZzhkTGlkQTR3a3E0bUpUV28vVS85Y0RHWDZnTUIrLzhzcmNJbG1pTnpIaWpVbXVmTXZSMUxJa09oU3NzZ2V2ZmVkVHZjREU0RXhGNDcranFwU2hlRWlZSHVNV3RSVFBLVFdsYnRuUnQ2cndzL0xRV0JNMXRGTVovbG8xbng1cCtFTU9aQ05ndlpjcW5vVHpvanFlYVN5ZDZIWlhwYlV5WXpXcFNQaE84L3JKbUNtNDI3QS9LOS9zRmhnOTZRaGxleXorK3pjRXlDb0tUYTNYOUxObjRiWCttY0JqVW5sbVdQbDZmNW5sU2JycDE1bU14UnYvdS9ic2lqNFJUVlRVdml3MkJ2S0tvUFhoU3dlY3cwTjhzbnVQeFlHblM1OVJVWnJlTmVHYXhqaEdjd3lCVi9oL05VRUhTYk1LWC9nWVlQcGdwZlVEQ1lkTjNrN3BBS3dLdDVBWVZEZmR5NEdYTDFpTzZYYWZvUEN6L0hvM3ZqaE1Ebmh4dFJCQmVkRkJwY3dzeGhxZUNKL21VUnJLNzFQSFlBNFlIcFlmcllkK2lhR2RSeE4vdTF3cjNwNGVkWjRoeE1ZdEJselJsSHRKWGVrZUc1UWN5S3VCSC9pYlJuLy9PZ3A2N3pZK0tvRnc4THFPNFBYNXJGRXZoK2gvZTBLdS9KcCs0ZVB1azRsbFRWc3Rrd28rZ3N0eDY3MzlxTnAzeHdzdFhwRENGYzNTMHVCS0tCUTRoTlc3MU96eU1mZTd4ZmVFRzg4c0R6QlNVWUEzTDR0RFVkQmUvM1AzSXN0c2Z4SGtNWktOalQ5WXg3Vlo2RzhwbklHb05xbzdOelhsZExOclpubUdybHg5ZG1VMm5EYWwzSzVGQk1OZ2xyVmMxNTlhOCtCYVhnMEhhVzZPcVJzMXNKd3daMUZkc1BKaitVSjd1cGVFUzFaYUl5dUM4c1d2WndQUi83MDU3ZTRFWkFDU3FQUEtlbTR1Uk0wNUg4ck50TGVmQnRhU055bmp1MHUrOENvZzNsVDJWdmtHUUtGRFVKMVFFc3lXQzNGTzNkeW8wOTR5Vng2QTZSUldkR1JjYkVkZ2FBcHFEK3lra3paSnlUWEtoNU9QMmZ2bTRmUFFMUmZXeDZYTHJ4N2RzUXNqY2taQ1E5MTdZdkU5ZW5iWmliS3I0dGJOVHZrdEtnVDJrMTN5Q1c4YmUrdGpNaENVaVlXSEFVNlI1N1N0WnJlQlYzWVg0bS84ZjhwYURqaVZDVmFLUzdBNjlxZUNIUjBveWZlZmFhNm5td1hiNlk3dGhEaXV0bHRFcDdjbzZhVnlyQ0VMRFFUNGJJeDJ4ck1rNnJGcUJKYkZJcnZPMkg3dndiOWRqNDR4REtKdjVQMGJCSHpyVHZlaTlqcW1GdnJTMjVqT3VjMWpXbVoyUjMzbGNOdXJNbUExRW1IaUpvWEk2L0ZRWlFvVlZ2NTk5WkYyRllqN3RLa3N1ZlgvMTd4UnlBZXB6eU4vN1NqY284Y21IT1I1YVUyOWlsNk5ldEVUMEVldTVpY25UZmdtYkRtY3Y5bFEvOHJFb2RVeWRFRFpkV3ppMDZITFpERml3cE5PcXJ1Rkg5TGRuMStMRXJUWFZuU2c4VHlxeU5LWmdzcTVud1djN2RzUjc3U3BEeEEyOTJKOURXZStQRnRJcFk4MVNGc1owUFZQdnZrblFwZDgvbDZHUVVlbGs2bEg4RVhGcGRkM21DMWVETWFTdXBzMUNRU2Y3ZFI1SllESHo1Qk5QcWc0Njk5bTJ6UHBRSEpYMk40bW9PZTlQVXNtbDZvNzVjYTMzT2pFdnZyUDU4RlkxbWVQT0Q2VS9aTDN6cGMvS3lIQXdxRnE2NWdmNXE3ZzFVSE9GVFZWNlRiZDU3N0h2dFNiS2VLSFVHZlU2L1ZuRDJpNXJOUXIvaURCbldObVBidUxHekxjNXk0UVpLM05mdHIvRWhFQVFhTEJQU0orNFUxK3ViOG5vMDA5cFpzWWhiZ2kvbkhmQnZQUGJnMko4Uk15VlB6eEpKVlY3cTE5cUQrTEp5T2ZpRDBvY0s2YmNOVmNTZFRQdGxhTHk5UDQ3N2huV2VjU2xlVGUvTFYxUmo2T01Ma1BFUDdzbGZzb0x0OEFjaktSWmRTVnFIRGpQNkVMeGJkVUUzK2VzdTdWdHViQ2M3L0hXbGZVSEJNdkFYRlMrZHU1YmlPbTFIeWlmWFJkZTBwZGlMbUM3enc2Y2VrcDdLaFdlcEQ3VHRiUkZTRldnYmh1eGZOMnJ4NHRDc05ET2crbTNqS3lLazhjOUQ2blRUYVluTnl1a2t1OXFmOWVOM3JIcmJ4RjVpa1MrTXNoTloxSHMyUkFIWjhOdmF2NlprOG0ycFVzYUFnR3ZWOVN3WEJWK3JxcEUvMnZ2UzN3bSt2Q2pxN1cybk1XV25HVVROVU81L3BPdEVlWnJhUjUzTTdFYnAyRjlLKzVLRkJEWkh1Rk5wVi9Vakx0RlJFZHcrcmtxTTVPYytXTmY0UERQSXUzRnJ6clVTOUxJUENZWFkzeHJmV0FQVVFNSzh0dDB0YlgzNnVJYlkvdXVVS2Jyb3p4RnJLcTJpMVgyYkI3VHdLeWNuWXJoUERWNmg2NkZNcHJLL1g3eHNWOGVFeTF3TWVhSDNySUc0WGtGdzVUNEdNUERxY0V0d01KaFNTOUZXMGc0N2tYZkpFb05Bc3RCRWFSSVZmemEvRWROeFhDUVR6M0VSeTVQcVNScWxJajVINHRIdm55OHBDN0h6Q3gxenowWHA1azdTZ3JaNGcvZkxlVXE2OFZsQkRQTGZhbW5MRjIvbzVpcGY0aDNhVmR2MmVySjRtaUZ1dWpoT2FXUVFmTitkc1NkMVN4Q00yeXVqdFhhNkZOcFhHRmFqOVdmbmlRZEUvQzRvbzEzQlpjU1BqS0NJL0FWOTJkelAwbFRhS3hmZnA2OHQ3MFR0TUs5UGZLeFIyMnZ1eVFONmdxTjZ5eVR6U0N2VUVWNWZsSGVWODZQSENuYlV0b0dybi9xNEpHMS9TL1liNkJzanZyOE55T3V6dFBmN0JSN1VqQ3hXM215MEc1VFNVdS9VSVlMTjMrMVM1bXRVb3NFZnVOZFh4UXZyN3N1ajlwT3ZtWG1Na3BtZjdmTUZQNGlpQ0ZlZHU3WXZ2ZlpIRmFvUVdQbkNqY0svWVpMa0twbWVIZXYxNXUxK1NkV3lramVvQ0g5TXRkVnZGUGYvZjEyUG1lT0xTeTh2c2dUVDNOK2toVmNUTVQrRDlNNnhSaDBIdTl0Mm5sWFNUNlcrc3hQWExFZytxVVplOVh1c1RFS0JHdDIzVlpaMjFiYkVLN2prSmhpaW1JTDY3YWlaL05xdnQxTVluTWk0bWt1VytGcTlYOGhDWSsyczN0U0lFNWl5KzJKL2wySkk4UWdpbWhFcFBCemZFempFMVI0RjFnL1FhcVhjd09qcDZWTmhSekZwekxNRXZneVN1SGM2NmppcG9uUFBnNE43Sm45cXBFRSs2bWZPZDlJS1VjMDlzUGFqN0g2VlN3Uk96cU5OWEdtbTV4WW95SitVc2R1OG5RSVIzZ1lDWDRnYWM1UE5VWU85cUVyYUh3S0dYcDJkOE5EUENhK2xvMXhMaWtrcDJTcEdiOHgvUG1iNGp2SUVMV2VOdTE2TW9NODliMlpNWW1lUS9nVmdzOS80SldUSzJnVU5PaUFxQ3RpQTJYalBLWmFqa3dnbXA1VGY0c2s4ekx0cHNONUwyeXJmZ2FpcnBrOC9oRG5OTHdwVnM1bE1xeHFiNVhaV3owdVprSitHcng4eXpIRHp0TWNXVzE3T2tzaTBOd2JwZUpyNy9LbHJwVDE3ZnE2dDY4eXVMTnp3b1FLQlRJVG9SUE5pVFVzVHhuNVZPUlN4UFUzcWJyZFU1RHlkWDNwMkxwZG14NjRiR1hSc1FQbnRtMVpOMlZhT0krS2lzZG1mbXp2bmYxUXNzcE5DOURsR3UySjltdXZuZVl4cUtTdHFTdDlyMEU0VmUrbGUxQ0N1T2NsSGgyR2hJTDYvcmJPb3hEcm8zc0ZDQ2NmSUdjZmhid0hUTTdPakNROWVHRW5MVTVWQ2hpSFlnc0hvaGhxc1JXTC9pZ08xWE4zS3gwZHRDNjZNZlZ3NVdJaFlEMEFkL0hacDhPMzNHdDVSUkp5Q2ovbmF3bUMrZWkrQXNseXBDUk9hZXJKL29YUnRqSkw1UUNCTTZlU2V5QVlZRjh3STVEanZaRklUTjlCdFpyQ2ZlU3pHbDVsQXJVbTFyaU5zajZudHpVU1JnVldkTWhGRlBuWEp4c0ZYR2p4KzYrdEkxaXZYaGFvWDNTR3Fzb3pMYllqNHhVQzdTZ29uOStFb2J0QnNqWFRlVnVDTUFLYUdyV0h3aFFkRW5KT3Z4YWxBTFowbWxWVDVDenZnaUlqbWRXRTZlYUJJZVZlbk42V2tFSFk3THM2V3BNTjlUNXZRZDQ4SnZGaHoyMXhheENmSUNQRWtBa2JtS2dZT0pNOUJlRUM3eUNTd3ZxNUJEbmlqTGpRTzJDVzJBY2xjQmt1eTBDVHMyRkNrNkN1OEJFMHV0UlZOOGhiRkptUTJZSHNyK3dhRGlXK1FrYi9GQ2lYd1VPYTZieVp5VDFUVlBncnd0ekM0MU9oOXNnSUVSUUFOeUFGS1kxU2JDRDBtMjYvOXVlRHRRUmpmMTRYVlZXL29yRU51ZEtQUWZtRklGQW5tRVdtR0pRcDVxUWlwMXlrUFZ2UXMvbHFVSXBxL0tuRVpvQ1JTcXd5YmNxUDhNamhJRVNSUXVHNUdNSXRmdERBMW43Mk84NzY4eDFHNUIrWEZpblBINEtjbnJYSENJSVBhcjNDaTBkK1VlNitlbEZydnRNeGtrWkx3aTQ0MGc0TVRCcGNaMk1LOWlCQ2RBQ015bFYxSk10M1NNbVh1L1Vxelk1bXRkMWRVMmZZL2dZLzV6TUpnaXVEa2xjaDVESllQS2FiTmZVY1M3bTJBMDZSRnFDVUV0SVhsTXd3aVJVczBjMi9RMWx5cVNTckVqZVdMY3hSMGF2UGdoSXNWNXQ3dXRzY1pyN1VzdXhaVFZnM3ZSQkk1cGI4MEQ4UlRuVzBkUVNudmJXcG9TeEQxd2ZWZGJuVFU5VGQ1NERjaGdSaHFodk51ZG1SYm8wSWF6VUxRUGQzU013YWQySHVaVi9IQVQzb0pkN3dUUkdmNEcrYUdMdC9Oamt3Q0V4ZXBZREM3NVNBTDRQdUZXWTFvVnlsODU4SkpjeXhncEF5aHdpWG9QQlpTNlFBNEFIdTN5aThXTnQ2cU9UNERCbjdrRXZFN3RKakEramFUT2NqQ1VKbk13eHhpWlJnd3l6TkJGb2NFcDd2VWhNRzk3RHNSdGMzVWwzdkZhcW4zYmNFZHJJc1J1b2dwaXBtTllPQUdUdUFlZ2JNdFVFRjJzaVByTWpNeWlKSjZCUW5JcnJJVnpWK09xcHAwR3hrczZjQmowdWxyc3VzSzlTb2taWG5hNG5qZEIzUkloUWhjQStCd0JUWDlPeGdhbHVSUk5TdE90SVowUzRDRlI2M1RMemZEZ2Y0azMvbVlkV2x4QUV1KzFVQkJKd2tLcTRJOFFOQWlGTldRVTIxSGo5MGxSaUh3S043N2kzZnBrc0xweHdNSkFNb0h0Y2FsaEwycjhHVWZWd2Z1Vmk3eG5mMUF2ZDUvNmFaUTBBRWhtaFg0QVV0WFh3TERGaHNNcERpSEtwVTJyUEtzMGFrczRhZjNWSHoxbmtwMXlhU011czhJeERIUzlnamE5TE1BUjQwWDVoNzhSMzJBOElweXg1Yk5xdTlRTWsxZWJlZFVONTVEMVRqMkpiSEp0c1BRNGgwbVJLUDhSbkpCVWlSZ3VqSE1JVjNBRFlVajZ5U1ZEcUlKYkd1WUVxRE5KY3pmYjlyd3JiZEJta3BtRmFQZGJhRW9MRWhIaXYzbHhUZVhJOTlWVVRCelU2ci9jNGxBY2pJSnRBa285TjhMWWxsVk80aWZWZmNmZDRjVzZhMnBrZXR3T0ltUVQvV2pqV0lTWmRDNm1nS1Bka0VrTFlPSllIcFlsVEpaVk1OQ3dRV2dGNStUbW9BeDFxeVNQcGNWNHJsSE9mb09Vc3NBTmZZMWRiN3ZrN2wrQkkvNTlXMklOaTdJZkV5Q0U1NXhFV2JYekNaMlFGV1BzMmlvYTJESWMwWVg4T0VQdjhGNmhtbEJha0xhRkExSFRSeVptK2V2UVVzc0E3V0NYWWRjR0oxb0VSSVNxemhwUFhaeGpyYXRrNkxaQzEvbXdqdFdvaTFpQmhOZnp0cGVHUVJoRmN2UEM2N2RScVMrSFNRblQ5VFdacFB5R1k3aEVlelNZQ0NVQkpabERxRXM5Z0REMVJ2c1hXTk50ZnBWZi9XRXZSTFY5allOQlBpVS94amFreE4yRUMyRURjWGErcnVtM0RIVWltZWVSVisxS3Y0RXB2anpzSHdGZ25XMTR0Z3dHNmJoaWpJcFBnVUdVY2lDT1RJa1RpZWpNSU16azMwUW9SQUxDRDQ4TzZTTjRSRjJrcGltUmt4a1Fqa0lBd0JGWDNJdXA1d3A2cGVyejdWTFNFQndHLzRkVkVURGhVVlhGMHNjeG9IZnBucThSbCt6bzhTQlAwQndZb09hZXJ2NHBMZGlkdEtnY1BnVWFiNExMUDdjckN4Q3BjYnFTU2ZwOGRmQ2ZHc0pyUVU1dDZGOTJwRHhuN2RFQkZrTm9kQzk3NTVFTTcxREgwY09aYnNlTVJIUFhnRTFsbElzUnRjYjVHaUkwWUV3REJDNjU2R2pZZ0pIdXFUWE1MbHBJWmxWUTZydnFDZ0hFcTVHcEJFNTlWVVU0ekhrMDlRVlZpT0pnU1lzRnEvL2lITnpUTWpNZVpwZVNsOFk2ZVM3dHRQUkdGZktJd3JWR084bEwrY2tyYU95SUR3KzYrZDdWV3hTS2lpM09zdUtseVhhY0E2aVYyYkhyZWpJdjJzc1kwaVIrRERxL283M1h1YTJkZitNK24valFFQWVHL2szK3hMbjkzaXlKOUlCUUFLQnRRVVFLb1VxTStoU2Z5L2dOUGxyMkdhdjYwaUFhMDY4Mkhpa0ZscXUrR3hBVllRd0lzanRSZHhpNE9jQlFHMzlENkdMekQydDZTNzRIOG4rV3g5aHNqbDl3RGV6RVpMQTd5YUZONEVaYU9MUDhBZFFMTDNTSHFhcldQU2VwM0dZZ0ZBZnVUSGZkNDNUTWxpQ2dCSEdBZTlKRnFYN3dPVU5BREFHUUFtcGtPVXpreUhzZHlZam9oNVpUcUo1ZFRUeWRSWHN6bGxjVG96UnFIcmpiTFk5aTJQSWNObWpPclJwZHM0bkU2YnpNSzFNSjErSHYyVE1GTnJuRmVITVFVeG1DQ3VCOVloMm5LUnc4dnl3LzB6b09zZmIyWmpUbEdwbEVuZHpPVW10ZFBLQVRNcUdWWTFPYUdaaC9xMTZuRmswckRvV1VWTU03bkxoUDZlbzN5R0RCci9Ja1pGMldGVkdtSzR2L2x3clZ2UVh6ZTJtcWpoNUNoYXNhRmFWcm55T05uWU9UakV4c1QvaHNxNW9qYS8wTlRiU29NQnllQjI2eTNFTkp2RzVrOVNMTE5RdmRuTjk4bFdKUy9sOWtVTytWTjZ2Sk00Z25JTU56R1pYOTRPTDJReUxpUnBZTU9MaDlUejZhcW1xc2QwaVhqSk9zWFV3a2J6dUJ3SzJZWmtmc3krbVBDeTNTT0oreWc3ZExqbEFzRUxHMVlvMTE4ZDBBTTV2UlBBTXpOOFNQdFgyZnBLWE5La2x6MFVHdG1xZjNNY0pabTA2N3RFSjhKeCtlZ0YwNk1ZRHNKNCttUGFDaW1Ha3hhbHdRZXpIL2VYdmxHWVQwQXh6VmNqd2VMYWlxOEwwLzVUN21WVDA5REtvSk1wUzdZY2VnWkdBdmtXMWc1Z3VjejVkdk1WS0ZSME1IVmk4UERGN0pxNXloZFB4UktaMEk0K3ozK0ZMUjRabEgrR2RHUjlYbjMzZ0FTaDU4RWJkYWN1RHlrQUVCb0JPZ1RFemhWNHg3N2J0Q1Y0YTlPQ29KMGZpUHpQcTBCWjhWWlZicVhveDZGK1ZDUUl5eG9YMS9PM2o3bzliL2NDQUE9PSIpIGZvcm1hdCgid29mZjIiKTsKICBmb250LXdlaWdodDogbm9ybWFsOwogIGZvbnQtc3R5bGU6IG5vcm1hbDsKfQpAZm9udC1mYWNlIHsKICBmb250LWZhbWlseTogJ0thVGVYX0ZyYWt0dXInOwogIHNyYzogdXJsKCJkYXRhOmZvbnQvd29mZjI7YmFzZTY0LGQwOUdNZ0FCQUFBQUFEWllBQTRBQUFBQVg2UUFBRFlBQUFFQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFCbUFBZ1FRSU9nbVhGeEVJQ29HU05QOEVBVFlDSkFPQ2ZBdUJRQUFFSUFXSVlBZUNJQXlCQ2h0QVZGVkc5VGlRR0hneml2OC9KbENSc1haQ3V2MGVWQ0FnM2NYRW9sRzNkZFZCUzd1SDFrUVRldW1ycFIwODJZamlIT0h0MklrSEcrZkN1Y2x6OWVQYnNZV2pvVHhWVDZ1dmZYa1ZmMk0vNTQ0ai9FMFJVWWFQbjIrcUVjWGZzSTJOa0dSMkFMWXBHQWdHRWlVSUxTVlJTb2hLcFZTSWdkaTVxRy9XZGxmbEl2cjdxbDY5ZjFYUC9iM3ZpODhnbmRiTWFzV1d6RExJeERKRklWSUFmVWtPU0VkVVAwR1BYZlVWUWZrLzA5azNMMUl4SUdwSXBDN2NGeDIyc1NjOC8vMWhudnZlVnRXZDFUUmNrR0FtWU5VNmFTdEVtVkFEVHZULzM3ZFpYejE1MnIzcjZTWVFVU1RKbUVCMFJ6eng2cnB2aHZIc09YdzhOL2lGWDRrS0ZoUHd5RWdVSWxWZFQvei9UMDI5OS9VUGtKd2l5cTAyUWhqSXBZK29UanZwUEJDOVRObG51VWorQ3dUNG9lMmJGZVVkbXVZTmcxZ1dzWlBGc3ZKUnlhREp0NkttcU5OVkw0djdscitXbkRxbW1leEpYdE85K1QwcXV3ZE1EM1dIa3cxWE82QnJ6WGVKYU5kNUhGdE40MEdReUhvY1FrQ1BEMzM5N1Y4QmFEUGhKNzNWZlpyRU5VM0VnVlNSb24rdmF0bSsvMG5OVWFIZ1hwVDJITzR5MXluMHpyWEhSZXVtQVI0Z0N2K0RKRWlBdWdHQkRRUWxyVWx3QTlOcVFHeGl1SkJTNlJnN0N0akl6VnBIeVRGVklYWXhWWjZyWFZTcGFWeVU3bHJYcmwyMGhjZGRiczlrT2JFUUtocjhvUHZ2Mkc5OTUrYXJkZ1VCcFFhR0Vtcy9iMGxBVWg3VWFTb2tuU2RuQTdDZ0h2aEZuQ1RBT1FIMi9MT0EzMkQ0NWFuUGpudmZXQXVnb1B5MlpNcllFNjNIL213Z3dNSVh5UUFBLzljQUFCVEZKWmFNb1hhVmh6VXBQbG90RGt3eVEwdVJZcVhLVlpwaVk2UDAwQjh1Mzd3Um9qSzdMOVNxRUwrZytKQVBGNzg5alYvMzNIWEhiYmZjY094cnJ4b2lCZ0JBSDZ5TEFQQVRKajJNMisvSmQrTVJHYmphYm8zVzM2SEVHTVNET0JqdzlCL2NPMCtwM1g2S1hEbmk0RG10VVI0OFVYaitiNGlCdHd1ZE94RWlKbzYwbjBBY3BlQkpzSkhYbmdKWWd2Kzlva3lmcG9FT09DeUdONStqcUxqeStKeTNyNkl3ZW5iZ3h1OHRHOCtHU3Vya0pRZVY2VFlCb25NK3A0Zi9hckJHK1NsbndHS3B6QnNVM1BwTjEyRHRLeUNabVNQRFRJbmJjV1htcVFBREtEMHZRWld2SWxVTDFDdzRFU096cDRTS0NwWlYzUWE4eURMZ2R4RVNMb2ZLUnBvbVVaNVQrM2t6NnJ6SktOUHJtRjY1L01haUdsS0libVBnVGpkNWZOSHRxb0k2TDAwUUZnSk4wVTFSUVhBNVUyelh4TDRXSDlXTnB3N0lXbTl6OFUvS20wN3hzS0pzalpINjQ4RU1USHBmam9ad1VXd09acmNMVXBhb0FrT2JSVXBQcGEybFhiQ0VTNGNQNlhZMmpoaUIxRUxGTmZjRDNSaWJTNDZkV1lLK0owaWdYb01udTFldm9qZ1Z1eWpPNDZDS1dzVjNxT1BwWXNrQ3BQTUpGekYyTFJpZE5uS1hPRVhQSkRZMVpSa1Ava2w1VzlwbCtnSW1sRmVLMndjNktJV243MktWMzcyZEhvei82QThsWFo1L0haKytmY3c1V1NMWnhBd3Fra2Z2bGFUMGtTVXdHYWFYS3p6VXhCTnhQT1N1Z29ydnZOb3dTMW14eTA4WXZMQVJIVTYycEJ3VkNGRklMN1hHQkdkMHROanE0YkFyNDhZalZtQkhlNUxFM2tUVjUwTm5zVkVoVHQ1RkppMWdvQzJmbFphUk9TQjlVc3J2MUdTejQrSXdsRmYvbVZiTHo4T2RNczhjS3JLWFBQT292SUpIQlJTb0FrVXFvVVJWS0ZNTmZLcERoUnBRcFdidEVHaklkNHFNNERjU3dOODdkUTBiZEVKUlVpYzEwc0FETkttQUZsV2dUU1YwcUFwZHFrR1A2dENuQmd5b3FZdzF0SkJoMHBDWXl1Z2tRYS95SEVWOEhIU2VLVGdKVGpiK2dobUEyWlh3UjBuem5tYXpLMDluT3JGTm16c3BuTHVjYmFxcU8wTzhhbnBxUy9EMEo1b0xnVnRLczJYNERsZ05aWkVoUS8vT0VyeGYrUGpGNmUvcFhnM1Z2dWk1QXJTWUtXMFNSL3hENUxtSzVsanJ1ZGJESWJrWThxNmpDdkhPb1BFVzBRN3Y4dVB5MDh3ZU55UE9YWFk4RlF2VG1pbzd1eGM5TjBCNGxMNUpoSU5wOHRLcVRBMW95MGJYQzJ6bTBRZ3FVVnFndGNCWFNlK05ydU5QY2I2ZE9lbW03Y0JDSWF3YVJHbXp2SlU2SmtwR0ZFL0ZTTTc1QXdUVFVxa3dMMW5NNWJKelBCV2lub3htVnZEaFpOSXg3em1sd1pIQ0kybXNPMHc0Y1cyWXRubnlRRUgxTEhxU1dwVEpMTlZsaVhtU1MxM0hpSXFkc0JXREo5Q3Ntb0F3dGQyclJyWWRHaFlBWmRRNEpQcU03M1JLZWt6RGVqRGNqdzFhRlVkeGkwT2hwT1Z6bXBldTg3Z0d5KzdxRzhiVC91R0dacjc3anBBblgzaWFwSHRxbW5Tb3V3ZEZBOTNBOFUxRWEwMmRxbldwZXNiK0M3MjZub2lsUCtYclBnQTNncmYvRU55TTBqaHZNWE5IWmVOUVI4bUcwd0h0S013Z3pTQ3hxR2pCNlQydTc5ekhxRWJEQXlML0xHWWhpRnBna1huMVZ2ZWNlSDFXVEZGK1lwTWtDUFIwVnZSOE83cW5PVGxMNVdOVW41Q015azR6dHJKNVNjN0xGNmhnTHNkVjUxeUM2eEVzRGdSdlVEUVlTN3VBcGQ4Q0tuTmNaT3F1TUFHOUo5ZVVjRE5HSWJlcDhqdVVaS0Izc1lGN1l4UnlueXAvMEpDSDlDRTI4R2lNUWg1VDVYRkRPdm9FRzNnNlJpSFBxUExuRFJucEMyemc1UmlGdktMS1h6Y2swRGZZd05zeENubEg5Vlh2d1Mxcjh3OS81UWdPSVpKSWhLeWIweENIb29FRmFoVFFvbEQ3Z1RnTWpXOHdvNFFWSmV3bzRad2h1RkhCaXdwK1ZJdmlmYzQ3WXFZa3phRVUzYjdZcUx0VkdMdURsU1dBRHdEaWVHUHFZNnhLN0N6Mk1SZUFIUUVBNkRkOFBFOG1ZRCtSaENYUksxYncydUlYRVpzVW9zRzJUdXFacnBuMjVMSDNDRWxxV1ZBM3UzenNSMzhEVU8ydjRMcG10ZWlYNzFoZGZ6R2ZqTmF5Y3JHNnY4R1I4VWhqY3RTdHJidWQ2bW90clh0OC93M3F2cmN0ZzV6T1gyelY1ajh6ajM1NjVMVGpINGF5anZkMi9oQnVZLzN2NWZaMEsrNjE3MDNjUFFpTko4eDdFOCtYamZ1MEo2MTdEemlwMFdQV0JlZWxNR0llMnBMVk9odWNGSm9xWThQbm41WGEzcHVLVEtROGtic3lucFF6Nlg3OXM2cm5Jd0lSS1NHbm5hU0MyejZOMHFScmduenNxUHp3ckZDbWlTZUNXWHRpWWJEUVNFV0orRVJvQWh5V0JkcTVGSVlzMGwwclk1MFpnVlBSWnhxSkxpSnoyZWM4RCtsbjJVVHNaTUFWcUVFSzhGc0VpWjlrZ1hNQlJlbjlWWTJvN0xYQXpqV21GQXVKWENrWHlrVkdOTTB1VGxZZW5yOTRVQjkvY2luVzZDcVJoc3B5dFJlb0pmYjRmTGh0M1BGNTJXZm5IdVY5ZHUzenZJY3FGMUNJWHlBaFJSOWlDTVg1Zi8vaTJwZGM1NkMwNjgveVgyNGEvcTV6TUFMWkJOc1dwWXFhQnd1alZOVHNocDVzTEFSZDNORm5TY1IydkZmNDY1WnZmdm1aQWJiV2JWRmNremd3K2hoNGlsb3NYblNsZ053amk4NlJ3a3QweGFhc1dJbEkvTFNuZlBIUXQxZ1JQaE1jZmtYMFhHNWRGcDN2cER5RjR0L2p6OXdvOVVCSzV3bUNlVVZydlpwb2l5bnZ6MVZ4MTBLUEtTMlMzMUVIcmwzRjdKN1QwL0wzL2VOczY2aGZyU29hZnQ0clFLMXZGUGdjZld2WFUrSGhXbUE4aDIvNHA0NFRmZGtETU4zVFhZMkFSMldxckZzY09CZ2llYkxMSy8xL2UvSm52NWhJbVV6N0RiMTBRT1gzWDFSK2lZVUdMT3Z0T0M0N0R1UEJVYVFhNFgyZHJ3ZjEyV0hlL1RVT3B6WFlPdDRkdmU4b05jVk5nRzQ3RldqTzhDbDcxQTNJMDhNN3hzNTF1ZnBVNzZVVzVlODRLdm9FQnFGZnZQSWxFMWNqbFRxdHp4WDJYamtjaGwxSTVMaW4yc2VZbEZzSUk3czQ3ekJUczVJQ2g3alhWOGtoYnZCcnk2SjRHSmo2ZVRXSHVCblJ1blM0WmhVWmE3TUc2ZXAxMDhZWnVRSzYxV004STJrUjlIaVZwVUQzQmZqUGpjZklpZXh6SDlRMUNSMmNxR3BUYkZKSGhsM0g3bHNodTBGTERRZlB4cWNJWisvdmwxb1JqaXQxL2piTEg4TGhXQW5sQzk4N1pnRVFmdisybm9OTlNTaFFEclpxU1BRVXZDQ200b0dCR2RHRzFKWjdwc1BmSXhBV3RSbzRzMWd0RWRtUzBwNG9iODlmOUljZkdPQURNWTZ5cWlhL1dXczJGdXlycGRhUGhDSS92NUVENCtvbW9nS3NaMHRnT0NXbFhrL3hoV1BKREUwNzI4OE1YQnUxRUI2Tm9YdEFsUS9wQzNxbnZLdVByd2VmaWpZWU11NmR2NzBnd0FFSDllaHB5NVFvU1hLaXlzSERxdmZhc1p1S05vR3VuN3lSaGRISlVhRmNvdkh2dGpEa3p6TEZsbXlnMzRuS3lvNzBtTGU4a1Q4WXpxQS9OQmNyeUJKWkhJeG5PV3BiL293b1hxOERmOWEwd09oKzcwR0c4c0VOOEplR21uakZ5RWpZZ2syL3h4KzJ5cG4rNkloalhneUxQdXBIQ2loMjN3R0pEYXBlRXR3WXEvWWhsNnRDRXNNRXJXL09EWWRDbS9rTmg2ZisrWkVmTkdLZjR4aDdWT010NHhqMUZocmtSSVBubGNCbGRaSWlEVnRJeW9TWU5QenVzQXlyL0xyUHYyUGlxdHhwbjFIOFdqcDBUWXl6WjZVcTM5UWJlWEtCWGxlUzByaUFSRDVCV0ZxZHNveGZZWHUrUWVsZE9Dc0JZOThPNkFibTJ4YlNrZkkwZjN4b3JiR0xYcnRqR0w4U3ZxaWc3VnRLUllmZkdrVXh6SWFYOUx2TzMvL2lTTHAxZWJ2YkZOYm1LVVAvbGtEcVB3SkxKNXJHR0szWDNvcDRhU2c5a3RFOFZmMDBXRjc5dVd1d05sTXJlUkhmZmNIMDlKck01QlJTYWZvM2ROUkFZQ1B4VXFlVnVQaFQrZENYcXYybldpM002L1hncDJPS1Ezcjg3UnYvc29iSmE4OE9aZ1R3VkU0aUp5ZTJadll3YURYa0tKYmVUaHBkTWZyR2grZUNxVitOSWhMc3VtWFRYMlhuRHhkMUw4aUp1OU5Pb3Ridm4vRVBWRC96OE5lcytjNzhFRkdza1YwMWI0eWc5UjBIWDZ1YTRWY3JOanlNc2dDZFRQR0hSbWZ4TUZOOENKNG5Mc09JSm5xZFl0eVdHVlNsam9UNlFuTEFmQ1p3ZFZuN0pmWkNjVDlTeGgvK21leUxUdEI0TVBoT3NNbU55eC9LVThrMG1ZZmVUeHRGR2EyQngycU93elFMWmJibzFiOW5RejA0eTB0ZVJyUWVSR3IxRkxzakw4ekF3WE1qSW5DcUdVcU8yTDFleGxuUUJWL3A3cUhXNlVRL0t5eWJRbC9jYU5sa3c0dmxHOTlLdnMyWTRDbGdNZWRuQjdTVHQwY3N1MDJoUC9ldzhraUdIMGd4VGxzQkszNU9yMzZTYW9reDlQN1RaV0FqdHpKNXc0ZXdWcC9oTmNZY3ZCOERHUkRLcFl1QUc2aitycGk0dEpNSTA2a0xtUnhIR2ZTQzZCbFJJMDhPQzhSTGQrdFZNUWNlZmt1U0dNL3dsT1J5WFlaUEp1bUJ0dXVpRGYySXVtbjFaSlhnZHZDSzdpditmZEdPOGFRUDNiOXpTTkc1MWJFdU5yTytqcUlEdWZ2Ynl5VzBKdFlST0pGaXRqWGVnelVKSGRqUVVNT0JNWlZEZjM5aTlkYkZuby9QdTBIRTFDM1hvZFdzTHBEVms5UTFSN1JsVUxER2QvM0psL0tWYXdPdlYyK1R0d2V5K3pBbWREa0JrbjROMDBLelorM0RXaXcwVjBTVjFjVmUxMVhXSUtMQUhBekJ0TTUzQnVBMXBVczN3UjhTNGo1bEZielo3RVZDMHNRL2t0QTQvdkdhTkluZ2VmZ2pUU3o0K1ZzcmVXUHF3ZG11Z1Nyb2o2a2dVNlM4RXlFTjgwMFZ4RTJDenptcDl0Z0JkVnhsWFd0Q2xHNVc0MjlKaXRMM2I2NWFuWHAyUHpRNHNKaDlFT3huMmdEaDhTeVpDS3dFNWpCT05Zd2g1U2VMVEwwaWk1Zm1SNFljZkZBWlowaHlJb1Y1MWlOUFVxMFp1Sm0xSHVLMDViNFlXOWtHNGMzWk5tcVhVSzdveEgwRjJNUkxKNFZWRkVvbFAza3VkbUp4KzhTaXdudlNaSWZBb01BYm1Ob05ka00zbStscDJkU3RqbG1jVHFoY085anMrQUQyNWhDNmlVZUNRN0lQSG9WRnhhSEdLSjdYT0tEZXVFdDc2dldIbVB6ZFdtYzE4ZVpRS3lyRDU2UzhncFFOeCswUWU4eFBQSXphRE9KV095cXdMR29GbkswQkg0QllLcm1JbWFFUDVGMksvcWVZL29pazg2S3pYUFZ3c3g2VDNrR1FQR2NDVGRGRk5MRW1xRkFGMld0SkR1bFRxejMxRG1CcVdjbGU4QkhYelJ5V1NEZU5DbFRzMzJ3WmF6RjFtRnoyREdscEc5dXU4ZFZaRlVqWmhwQk5za05nMTV6V0RLNWJZelhCa2RITDk4VDY5YmNEOWZJaERuZXhtZXZrMFNkcERSMEdVL0hLUzV5NCtvOWtFU2xuS2phVmp2SkNkNHVCMzhwTGwreUFNWWkwSWxDSExlRkxQM0RhV3hCVE9hcmFWTUczalpFQ2h0OWZ3THY5WEd0YnpFZkJ6eFpvZHptYXZQbERpTjdyWW5BQjRUVFJ6Qk0yWFZkQVJkQVFINnlFN1VIc0ZYVmdONWtKdkhBQWE5WGp5R1dLdFhUSVFvQzg2cUJBU1ZoMDZ2Wkc4LzN6UW9FNjNScldibmVxL1VmR3dGTTlQbTNqQW5aTUFlUytGaThxY2gwQklxQ3U3cW1NU2VDaTdSdkNKcXNDdnVEd05heXA0T3JCcDNJRmtiUUFHSVBPemdMQTd6N0c2SE8zRlRsbFoyb0ZZWlZvRG50YW1KNUhNNHdLQzM4a0xBOGhhbWM1VnJPeUJHbnlDK2g0czl6SHA0UUN3RlRWdlZzMWh1aUhWWitVUWo1WDdjRmthck9VRTQwM3o5SnFsVHAzbGNKdm1YM0FsZzNQWkpXcElXUTFOMWJ0QVVjaWhUcG5neGFLYTdndXd2bm1MM2ovNWlQa2xHOWpyVzVGWU5hdTkrcjhCT3B5MlZ2czFxNXlpQk1GRWdKWjBSYUZRd3lUZXNkZUdGc2tlUmhrMVFXQjF6STYxdnVjMFltcm1yRXJ2WU9mcm9EaGpuL0RncGVqSkIxeVVueVI0djZUbUcxaGdaNGxmdDVTWmVBUEdTN3lqN1Q2eG4rK29NTFo5UTAxUnJRanVBcEhhZlBhdExFKzNVVW5YeFNYRlR3L21zZnNoTjFQaHRsYlRPZEFzVG1XMjRpd2pVL3Joc25EWGliRHdXMENHbWJYOTZkc1FwWGllS2VuZXRweTJqWXBDQzVWNkZUT2NvUXhkNkVqZjhBZmRudjcrdENlZ0daVGFsMXViZ29ySUkrei9JMzZXVStpSFcrT04wcnppWFFiWG5GQ05zL1g3RGlqUFRQemcyemY2cmFaMjVYQ3JTbFdFdVdUVVZtVzM0QThuWU1XdGVVSm41MzlHNWV0THVCUTZ2TVZqME5hanpsYk94SkdnbDZCVG9nbGh5VGdEcVJNK3ZtM3UwSE42YWFTWnhTZ21EOFJNVkdtbkhVQXM3S0p6T1JBeDlKbHVMUDlPZWNqUUp3Z013TEJaYWVoTDBqMjF3RG1LQWQzZ2RqNjVHQTVvaS9xMHJKRkdQcXFHNnZteVM2TSs0eXlMa2xWWW5GblFPT3BDb3ZQU1NKRldubUx4Q3ppMDU3b1JmblB2QU9CcG1mdk92Z3ZGV3dZWS9iVTF2MXpQK0k1aHFZRStiRVZDWEVGdmdyUDh0RU01eHVPb005ajhSdmdEQ3JZUlRwSmp1NkJMa1hsZjRDcVdUaU1xVUlGQWdOZU5ISzJ5a0h5dy91VHc4cFZxQWJxTmxEakI2aGJ6SjFMc1kxSXY2OHBsZzM3ZmMxL2pQaDF5aGdtdmgrblUxcEFwODZMeG9EdmZqWXdNcmtnY3BCMFR6TWlXcTJVRFgxbHlIM1U5djFmeGpxWTdqcTU3WThLQVVMYkRxeGdFM2w5bC9WUlB1NnMwVHVFeEQ2Rll4K2s1RlhTODRSZWhZNldMYVY4LzRyNkgyNThsbnIxNTkvLytMbWZ0Qk9UNXpXS2M3K2M5Z1ZJOFU4TmJuRElremgxc2pYVU91aHNYcnpwemplOGlGUmZOV1RpTUN0Q1pFUGZkSDcxUlJJWmxEMm45eWlHbnlwS2FRWndmeFVoc0ZWSnVJeFg2WitkdC9PTzFscHlmbUxQYXB2WEZhQmZZbkpDYk1MNzltQXVYVzBwa0docVltUzlwdFlQVDJGQXUyNUR2clFwdjFUNWtnYjlsdnA5YnczR3ErVFByUXlMcjc3U2JPdmtUREpsSWFSUkxXWnVGdkxNTEhnaVdGdGFWVkpRR1BUM1hGalh3TEtlc2hBckxubWhrNUM1RFBzUmZtM0V0ZVNKUU9leUxib2hTYjVIakZteUVFdVdEWnY2RmwzeXQ1dlZZY2Zjdm9nTVltSnBxeHU2UFNXR29GV3UyRlZhaCt1YzNTK1MrVmt6V0g2NGo3dWtSdW55TlNaTWtVUGpMWXFBUk9qR2hQZ3RuUjkvTmpZMWliYWtzMzlOVmd2S21GekRLQ20yeWtxaWxXYmw2TTRyN2FZNUtIb0tKTDFZQUxsQnJQOFArNVN1ZWVtS3NSTktYMjFScVZiYlhPMmpOa2VsWHErNllsOWZkMlFnYWg4OFloK3Q5TFJmTjhkU0JvdExHdDlSMHZPTWVVS0hQdUtnV3BSRjh4b0M2b2l0WTZxNVJxdGNzZHcwVlNPUDJFYytuTkV5cmZFcjh0dGdyYUtpRE5KN2QyV1J5emJUbnZOanZsTlZFbXMyU1FlcWIzdnpNR1lKYVpkNk40bTRXNzNMVUhYSHNqWG1UcUdIZ3FHTnlPQm1iNEpDM3JrMjBYWU9jbmo4RzVtVFRwNUl3RE1iYXFsU1hZVk83cmFyWmllYzFOempFS09nUWNTWlBoQTBpT3VZeFdZT1EwTE5rWHBNVTc5ZmM2Wld3OGpsaHpqdWF6TVB0Ty9VTkRkYWhWWUc5R0pUU1RuY3V0WWlNTER5NEVGbkx2dEc3d3BOUVdSbXRTTlcxSFIrblRic0NqZG85dTFydkVYclJCaGtpbHFEY1VXdjY4L0RKWXFOdFh2YWZVWGFuOHlsZ1hMeHRYR0hpZm12RytuWE9SS25mQWtWRlV6S1Ztd3BSUlVmQ0t1WGZYRXVHeVlySmRHTTZncDNwektxbmpqM3JKSlI1dmtBNVNQcGlMa1EzSWYxcVEwWkliK3A2S0p2VVVNOWNFeVFvQlk2bzRHZWVMa3czZDYwaWxZak5aaVRNbHovVVJ6Rk5UVTZZcnZDUFRvV0hFR2pHMVhaZ1p3NGk0czZua3RjSlVTd3VuS0xNN1FtNnJsZml5TzNLWFQ2YkFKVWZta0NiczFmVXh1b0M4ek5aL2NVcUxiVi84aDhwOUxUSFY4ZkZrUUZNODBGazR6b0Z6aVVvMEJMS051b1d2VkI0UzBxVjJsRWhmaXI0dDZFNzlQTlBwTkJtVkkvUE5FUXF2YitjSyt2TnVvWUdYUzJ0OFhHSGNJNjZqNmxsaEQ1b0NGSm9HbUpNaDZ1dllQRE5yYVJqY1YxSDMwUHl3d1hqbEFWZlducDc5RnNCNlFWN1lkeXBUZHdGaUxLYzBvN1dEcDA3dEpJWFlXU3NLYitYY1htYW5kWDlXU0NYZWFVZHhkdGFpaGVtNS9iSm9KOXc1M0ZTaEtsd1NNcWUyRU9PVzhXMFhpTXU3d2ZUOWZwaXc1K05nSExyUzdZeUxNUlNtTHRDelUyaTlWVWdzNXk1NklQNUtDT0Y3YW9MWGdYaG9RYkp1UnR5MWVEcTR3STFhWFA2WjlPcmo5STZLbFA1OVVFdGFRTlZua1ZjYzRHVkxDenA1cnNSaS84cTRXSHpiR3J6M1YzRk1ITjZHcWlVbFFuOWVYLzVLYVZaNTBnc1VrTldtcFg0UHFWRXgydTUwKzRPZDJzRG51NXBYb2k1RElGQjh5TTVaNGZxUDRGcWtoY1BFWGJMdFJIRkc3dkpoMnVrTURaQm1HZTMvd2h6c2J5ZTllc3IwOEVFekdicGE3V3kxMGxSRUxEeVo5KytrUHM4dnorK0l4dGk1WXo4K3Ivb1BWcWVDanB2eGRJQkk1VFpPQ1VzNm9mVFZnMVVxNUI4cm5QRVYrQk5vVGxHb3BHMzFCaGlra0ViWVRQSnJmTWwzUW5IZTNHRmU1UUdYL3YwYURROXhoSG5lbU03QzhXWnRKMHFYeUhXTHF0YnQxRWU3Q0MzMGJYd3VrRitxS1V0S2V6N1hLTmxzVG1ITkRoa1E5bWtkZkc5eVNjNzZVLzkySnIxa0JoaTBoMFZaay9YalJXSmxmeTRhUDB4blByQjBvMWdzcjZhbXlickpOY1pURXloS1RNYURweHYwZ2JWSXJJSyt1NHRLWjlwcExVcE13Sm4weW9GZktMUGlrZ0dSeVZ0WVpScjlVc0VaOFd4eWJuTlZsL2RCNElMRmtsZ3MxdU1mdnZsMS9XakwrMUhocXhrOHgxN1pWYUw2dy8wOXJsa0Fua05BUXFYTnNhWnBnZUIveFFnb2tyTEdiNVAxYXliaGQ0VHpZMkY1YWhRVW1DYkVaRndkMTVEcWU5emFTWFlTeEw1cmVSRitDdXR5SndnVlhqZFh4N1p2MEoyOWYvWXhMeVBzNDc3WUQwd0FNdEVaUmg0M05vczdnSDhhWjhrNDdCNjFzdTYyTlRNU2JMMGxmdVhVNW9sdzRtUll0czRpQ0RzbExpMnVkcGN1Yld5ak9yTXZBeUtXS1BDTGNPQlUwM3BmSE1Rb2xjcnhSeXl0YnhZZk1jblk3RHJhNzJwNzV6dzhocElhSlZsYVBScUtjcHpobUdkbTNOcFpDRXI2RmtjbzF4Tkh2NTg0c1B3TVRubXdpZHlSNGRaU0hLK3BvS0kvTGRYN00zQk5mZldIT1p6OW45cS9UUHhsTXIzM3JjcE12a3Z1RDlieXc5ellTWThiWmhQUzJmRWNuRmZiUW1oN2pneURHWWhjckthV2tub3RvL21qQnBkVTBsdy9WbGJrdUJjV1pUSy9IbXFsTkZPWmdCaEpNYnN3Y3hwSlJrNDE0QXo5RlhmVjF3NFpZempQZzdoWkQ4L3dJcDBQc1JVRjJ1L2hyUjVPdkVtaXQ3OHlzK21tZ0NsV2dWb1Fwa1A1NVN2YTJYVkk2ek5yUmJLVC9sb2dSYnFKamlwWjRxRFRNUGtVSWdHQTROVmx0YlBGVy9idE9MTFZpbXFNYS9LeHVSTzFnbVBNbXFRaDZQYjJMbEhJQ21taTU5TVp0Q09kaVN1bEJvOXVKam10bnVicnUrVlJiaTZGVmZhcmpxUG1PNGJSNGtvVWFSVkw2aUVJQVNxNWk0M2MvTXZKdHFxQ1F6Tk03VGh2MHFKOTVJZGJpS3oyakpNYVNUYUFhTE1OWmtHQW4zNGNzRlk1NTllTmVKQU1Wb0RUMG9LVDhsVldJTExyTmdicHBMTCtaTGRoRVJVMlpnY0tkTy9ZWDQ4d29IK2ZveHNYamJSTE8yM3JoNjhkUitObW5sNXhPUGM3SlgxRURGaTg3QjhzcW5rcm5jeWJiS0lrT1k2U2dkM0Y4Q3lNWE9SNUs2RjVIUnZiem5mMWc5TVluT3I2UnhmL3IyUTYyaFZNQWdvOHVGSmxLQ1llQy9MamJtUm1kWTdLMDEvZnBScytBNTZmVmZhT0k0Qm9PK1ZhQ3k5RG9pYytiVTVxMCtQaTM0SFFIN1ZXeFFFQzdZbmpmVU5DUTJzclBQZ0hiVnlnM1ZyWUlxaTBpSkxZSEdwSUhIaDlnQ09MdjRUZk4wYVdaZzVzcFZjSjd3d3BkbWl5enFCbXdzU0hkQlhEeURzWUVpNUZMcnVMNXJIc0FjM1JtaXdtT2p4VVhiT3lycVF5dDRBYmlUOWorbnVLWEVKam5CUkVwMEFwNDBabi85Y1NpS1ZvYTF0c3o3NWNkUjlvaGJFNnYxRDNmZlh0c1ViUXB2V1RxOXlURkwzSWk3TWFDb3BMQTBGTVBiTGZQRDFoeTcrTUhTd2I1SmlUUUVYZUhEc3cwYWo0UEk3QW4zOEZsb3c0VkRPOHphVmxQQllUT0hVak5yNjZHcVFDVmZsWGFOakgxamQyeVExSHFOVnhqNGhGcGtadWQ0OVpQUzRLWHhqcnlmbVNYNlRaWE56Nk8xeU1ReE5ZL3hGUkhVNisvS25Pdk9iU3FCanR4YmxnbE5iMDZ4Wi93OWpvZmZsMTVyTjAyWjBSaWp5aTZhaGZRNHNxbDFKYlloNTBGWDVUSmRWL2VqRHJ1QUxlREphMXhFdFdDL3ZjNzMyLy9IZGVMM0NZai9VdTBGQ01iYnVPOXM3bnlaQTFaWlI3Sk5qOWljK1NMbGNIR3ZURnBhVm56aDN6NTNvMkppQVpjSmRWQmRSckdRVDg3MTMrOThBdGRnMUxUd3J1L3NEV2xHVlJNcUh2WUVCdS9FekpKeE10Vm5Xc3dhN2t3VFJXS3JKMlMzZHQ5b0ZQMURGdklQMXQ2cE14d0JTN04rcUVybkVhdzc4T1kyZ253cWM2bzJEellWZkc2aHhNd2dNSmV6blh1UFRtbWxrVnk0T29vTi9NekVES0xFRzJ6MEFvUHFpckRIcDNJTDhselpMbUVlM01JMG1yZzcxd3piL3Zhdy9VUTYzTVo4QTVlUVU4NFZ3VTBQZGNtekpQa2JmNi9jQWVGOFptVktOWG04WXh4MmhWNE53OER5YnZ5MENLYkdYQytwWEM2VDZPYVVQdnYwVG42b2RzbGYvNDNsSTczNUxxOUlRYmNSY1RjQ3BSQXkrOVcxSTQ4YTR5SGhlMGhLU09aMmI1V2dsSG9xZjNac25VUWpuOTMwMVV5bGFDNVNiMnowdU5KNlBHbWE2cDdwN0k5ZXpjN0pYbEgvYlJ2L05ZaVBYem43Uzk0dHJHNzNTTnRjOFlKNWpWV2NaMTRXZGxmZ3JHZVh3M3VwcmJIdWtiajZQajNSYm0xV0s4TzB0OEN0T2pvUlpJR2RlMDdXSDQreWhsVmVhNEdSYzM1WWxrZDNLUDNlaEpzN2I3ZUpWK3pRMFJLSmszUlV4aC9iZjhuZDArYzJicTE1dGZKZ3FzaE96OU8zZDNyWjVJTlhkejgremMvZGRmWGFSa2gxT3Z2YnJDVFlBSnhJWDE5dkM5TGpoS3lxekl6ZE1KNk92RFRIRERHeGJOc3VoemxhV0tiY2JjNk1oNHJ6Yll6eGJJajJtRkI3RWliUGhHMUNQYjEvYlg0bWVRSFMxcFBlOE5VaS9ycFRDOEc0OGI0R1M5b1FlbUU2bjNXSC9QeURmNzZHM05vRDZuU2Q3TUNWSnJONmtBTWVlcmZoVDJlbWx0QVAwdzRYY1VzZ1B1Mmk5cWR6c2NBM2NzUHM3S1FrYTZXWE1LcWJNOStPUENsblVDV0RpUGltY0N2VG83aWQyZkZvU1dhckhPZnNNRFc4K29EalVyZnNaT00rVEcyN0FQS1lUQXpyTkNGNHgxNW5WWHJWV1hBZE85VitiSDRUQnozVGE2Qy8xdXJsdHZMM0xqWmJNN0dBRCtLU2o3WjZSZFl0ZHFMWXU1RnZVZHFrWFRJZUovbUx6M280aTBPQ2hNeGRHRmVPNVUvcDNOc1FrNXFMNVlWdVVQbC9oSTB0Q3dlZGRvNlJaelhZUmNHc3lPNm9OV2pwNFp3WlQrTk5iOG9hV1hqOFFlM3dwdkZJOGI5enpxR3F3YStXRGFjOUcvb0YwVjV3dmZOVEthdGFPMm9JeE1HUHJTNWtEVUlmaUZtNEhvc0hOcDJaOHhPV1ZsSGVNdDE3WmU2WWpZV2cvNHRsdjkyd3N6c1dTN1d6RW1oUGo4R2RGMkwzQlppWDZPWjFFczV4UmxWRGswdWMwVHF6TEJSTDA5bFN5aTJRc0FEbUNrcTlCZzNuNWd1Z3E2aGMrNG1ibUdReVhNWTAyWUs5L3VsaE5VSVAxN2JaTXBhZUIxZWxhR1JxcXl2NXBWNStlakQ3eHNvcGtEWTIxdDZzZVU0cVpwU0IwSUhEMnVwbWw4VUQ4VllVcGRqODFZVEtrbGxMZTFKaURtU1NBNWwxSXd0eUdSRzFtQ2RjdHJzaytQVFp0U3BvU3N5eENUZTFwcnJYL2NlVE43OE9ZWUJoZEVZNlJDSW1Belh2djNCYnBnNjV6UUY4YTNmS2VjL1hoci9UbjVqeE51RHBDUHMzQjlXakxKUHZVK2lwdU5zZVgzWHE5cmIyaUQ1TlQxZ3c1MHIzckNvUWNlZ3BCVWZPWVM0WW15WXBWZ1ZXdEpYYWs5TVQ2MmFuM2FRanpSYXhpN256dTlGZmw2S0Fkc1JjY0dBeUc1N3ovOENTU2l2RmlNeHlSaFVweG1PajROUmw4Q3RHVnFXZVBIcWp3NXovd1kyNHRKUUcxQ3NzQXdHVkM3aThRUUlNUExYQ0xtaU5LZDVDYTVkM3dZS3B3Y0xrdU9xTGo0YmpvaFZpdUpldlUxVG1uMkp5djU2bDFuVkVVcTZ1ajZoQlkzMWJpYk5qZFh1eUtUYTJYWUd2cHFkWDlmQkJEMUZKTTkwMTdzcjE1MExjWkIvWmlyeXpCUFNhR2hBZFNtWjFvNmMyS3hJbE5uNTlUcHA0Tzg3VFpFd1FHanR6QzY3NWFBOWJlTzFXSmZpUGpaMkNWQkFvYVRaRWJqbm9kK0pCeTFkS3pZWUNOcHk1aHp0Uk5hd1lzdllUVzB3TXQvYmdDYXY0Sk9KQk9GUFArc3BpaERpdFpPclQzWjFzVGxicm5sTXQ4WG9LclI0LyswS1RObWVYa1h1TVdPL3lOeS95cEg1MmVEUWRuTEVUOHZQR2ZXQ2U4NXZsQTlLUU9oZkVMcnh4OHNtTkNBbTl0K2xEbC9LVFJKSHFJL3lwZmVOUmZ6RzVPVXJINzB0NkRwRkdzMjZhK1Q0UVhIai81RVJDVUtVZHpWV2MybExGSlI4WjV4b1lneHdibzl4ZXd2ei93UFNhNVhqK1FNekJaNVRkSnFUN1A0cHV2L0w2dSt6NTc5WnI4NlpUNlFEYjMvWExVNnowdkY3Z3BQUlFPMkJlcXZXK0YveFl2MnhiN1JuOEo3ZVB0S1E5SytqYU1aai9MYXp6ZERyUmdBQ0xrYkgxQTZXTmJvckhGcTZtUzYydElvU3ZJRUM3UGEraTNpUk5icEMyQ3BnZW1vNytEZlVyMTZzV2hJazd0MDJjV3JFT1RhOE5TNUc4MjdtTHcrTnFpa1JHZ0pkZlJSSFdmZzVGSmZtdVluSG94Y1g1WEFlTC8zL1hqc1IrQnhHNFBOMlc3ajA5UTFqR2J0UmE0Q2NYd2JPaHhTWTZpRENGazY4QnhRU3Rya0xqWm1xTElMWEN0QWVkSmxYWjV2enhDSS9WSy9QMnQxazFERGZSbFFNdVNJK1Y4akhvNlJKdktyaWE0ajJ3d0pxcjN1NDUzQjYxSEZsQ1VLL0M0bU9GbzlSU3NpMTZOVnJGcWZudWV2Yk1seTFPY3JtUzBSRmR1YkxNcHU4L1U3eU5qb2tMVTVKdVlmNkdtY2k4TjJzWDZxNk1VU1NMbFJkcmFKQis2OHg3L3pqZGxsV2JaSkNEdGFBbzZlYk82N2pCNDVYbGFqTWU5MnQ2Q3BRWURReEkwV0Iwbmk2Zk16K215d05GNnRkMldCS2MxYzJySjlvV2xLZEN5alZzREtHT1NCcjQzNkJ1MFB5dWw5TXlYNy93YWJiTzZwbzI3UWhCcmtYcFIxR3FVR0grdHN0UEZYNHE3bS8zSklrSXkxVlVNckFjWFRTN2JPd3A0bm56TnhWemhHa0VnUHNUMGdKUC9leUhPVW52VDVXYnNJQVB3emltUGttdm9pZWJwOVMwVUI3aFlMdVhWQWlpMDdOM3orTGtieERNMzFKWDdsWG81YWxhdlFOaWJBV3RuR2hKZkFERDIxQm9aVGg1TkRzTzliYngvR2ZjMmh6Q3JoWjdFdWpGTXRLQnZ5TmJIZFB1Nk9RalE0R2E4TThXUGlpakE2ZHpHV1F5WW81clR5QWd2Y2N3ekFDOUZxMVlvczFtdExzTTJwcDBXN1ZBUlVEZkpyQnhVODRQM3VVQjliUytyS0Jrbm9FVlFYbDVOOG1tcVRPellrMFo2Tkhsd0RzdWlKZGRHNUs0V1VQendkOHRtNXIyck9VZDNvVVRENmxkSllGamh4ODhzNEVmRjMzUzh5UFI3cEM0NmY5RytmMTk1dG1XdS9IUFA3NkNlZXJBSUVINU84MDF6alpqd2FSL3BFS01mTi84bFdmK2V5Y3ZCcSs5bGRmWWRnRlpwcHNTVTBnRHBFRklvM290SXFybkxmTmZMaElOcWx3OXR3emNIOGNWTE5HeGdNRWUwM2xTNkljOWZFYjJaL29uemI3S29pdDlWSkFXN2VSK3d4YjlSMXdtL1Y2T2pvQmVOaHo2TTZZdFVRcWNoWEluZXpIR1VPSXNXQ01YZStBYWhVRGYybVlEZytaK3FTbXNHaTkzZGxTTzZtdzgxL29GSkF3NTJLZ3ZrUjAzRkMrVVNiVW1URVFtL083NlI0V3R2SjI3bnE1QStQaWsveitNa0ZlQ0Q0c1Fqb3VEOWR0aERRd2l5ek1BdkFweXpjbVBzZ0FRdk5sOVdKbmlZNE1zVWVQdVFBcE42blNUU2xYWm5yS1NObzR0dkV0T3RnMzBkOU5rZW9ydUkzMUNpNHVCekprcXFQY2FFQUFacitaR0tyeUhsaTZESlVEYzVuOTVCVG5VTFZUNUZ1N0xNMlJhR2pkYngwVzBSbld0VFA0c2FiL0kvTGRKb0tJM2VDanl2TjJJdy9MV0prelRkc2ZIdEtZNG9zbnNEaFpOd1ZuK0J0RWVER1N1VG9LajBOWUF0Q1JLK1dXUnIwaHFMRWNpSzRIdjJoWVo5MTJGNndxRW1VeDV4dmh3M3M0bEpqK0NnVm55MDJjTzV3QnhPVDVhUzBIY0ZVaU8zUWtHVlFvM2dCSS9RU0ZVdVNWa3VlRGxkQllqTGU0SlpHYUcyQUQ0NjVCZ1h5K2F0NXErYlB4L3NLa0UyWTZ2aWJUcFh6cE04QnNUeVM1NmFjSlhBc21uOWI3V3VxRy8wQzE4TXpicXVoOUlvUzBaeHJGUW5RcDI3SGp4bTZaY2djaGFUSDZFQVJRSEtLbC91dDBRSDc4RHZ3L1hxdW1yYm5lcWRpTitPdzRVRmVvTUx6NWVBRlJ4UlNJVERRRmlCOXlhaDg2dDhmSHRMcmZQUG10Vk1PK2FpTW9JQXVUVm14cDB4anRZVEhkd2p5MGlqZ05NUWo4OTFkalVDUUczOENUVFZpSGxGNFpQa3ZEYldKUGU3bUs3cExnYjl3ZFNxRktVWXo0ekt1TWdDTVZHVzNvdWFSRTluOHRrSEZQV09GUnpIeCtxbWtSVHU1SnFnUVJTS0cwSHdNODF1Z01ncFJzL3RhWkdJSHN6Q2xHNVFmMGx3aFJyb0FBZXhzT2RrdE84Ry9jR0Vsb250Tkg1Q00xMVhCenMzZzVyaUNEYzZEc0FxVlZkbnNGc0pMS1cvVWtJZ0N1dzY5TkZlQWNSaFdaQnhINTVYcEhKTjNGRUErbU1YQ29LSSs0TFpJYlRDY1VKbWt2Q0RBd2lPRjRQbkNKTkpPMlB6a1pPOGJITUtpbzk4NStkUEpTc0YxL0NHeThIT1lqWFdjVWhkZE1yTkVLWFJFUUZCd09JL1liM2dGaTJFdUEyR1Era1hUNjRQaUxCZ29qK1FzYlY4SDJjbUtZZEo5ZmozaUREWlRDUjlPampSUW96Mlh5RWxDdy9TVEkwT1ZpVFpkWnFVU2ZtU1JicE9jSEZseFhaWVA3YWZ4RGI0eXd5emlhRFpVSzlKUTM5aVo0TCswaDZEMGkxRWc0TldvaHVqVUhobnM0S2RLVWkvOXdFM2lENzh1c1VhN0tKWjNvRUdZYkc1ckVPWkZHNEJzb3JaVjlWdnJ1eG5HdFFIWGVOcEdFREphaVlBZHdCeWMzUGFSeHdLcWhtMHNlN0phS2NxdDYrdXluYTBzb2Zya0ZVRWJRUlRWeWF6L0FTcnoyaXNpTHRCY2xLNW9qdXpMNE9WSS9PVUpYZnhYUzV3QXJUNDZERFpqYnFkWXExelhyV3pQSmlZQ1ZIMHVyUnZkOHZsbjFWS0tPcnEySUFpQU1vUEhEZFphVlRPRy9yVXRKSlZkbFhQaW8yd2wrZWdwcU9nSnZEWmpoTTNYWWE1by9PZURSNUxWZndXYUN2WEllbDZjZENZSS9VSjB2VElIVGNDaGV6VnRjQmFvb2Z3dzZLSkxrRnIyVGFXRjh2YVpGRWxaN0x2MkZxeXB4SmFueS9LeW8vTzBpbW9Ub1R4WU0wOFJIcDVWa3pDbDdZQStyUVBWUmNaUTh4Rjl2RFFPUE5MRGY4eUtjN2JvZk5aTkNVOUV0UmlYNXAySWxBS25xeVBPUStIWTN2dzhaY2NtWHFHQklsUXk3WlZUM0FQVnkwNDF5aHJpS2lrNjY3VE9BTGhsQnd0TGQxMnF6ejFITk5iUHo3dE5NYWlzcVhIY0ZhRUF5WEFKbnhHajFqTEpkc1ZZYkZRZlBieHhvWlJFMytaRG1YaFNLNlFxTVFlMlI0cEpkb3dXVnVJS0xoMkpqOFNKb0NOK09CTlRUV1pHNjBTT1d5bGhWVC9vR2h3L0t3cVN0ZXAybkIycE1JSVNpWXAxSXBCQ1JCZkxYRTBQQlVYR3VJZG9NWUM3TVBFczZidkdKNldpb2VreWpnMDRsZXcrVmRocjVEQ3hzaEpYM0lyRk1ER251RUlSeUtHTHoyVFpGRHZmenhzRnJFb1gxeExybmdQYndQSjRWS1YxL1E3TDQ1VjlHa1hmaTZld25zZnFwSDVnOHVRZXVrYXpKRHJBdVZCb2ZDTlp5RVZrdDFPMlNpQmwwdHg4ZE1ucVdybTZMbWc1TWw0UXkvTGk3RFl3M3MycUhYNW8ycW5MaTBaRU80QTFCTFNuZ3IwYWlaTzl6dEFjV1dFcnR6RWE0ZU9zK0JVSFF5OHFXNjJKWERmaTQ4ZUVaNnpBak4wRDFqWjY4dlY2eko2R2U4bGhXYzllS0JRREsvNld4TnR2UmZMeXVsYm9SMy82dVhTejBCczk3RzNaMlQ3NWxLY2pORk41b0FuYnAyK2NvUzliU2I3Rlg2VW0vYSsrejBxTVk5Q3RxVjdoQ1JXMlJLQVMzWFdqcGRzMjY2a2QrREplL3dSSVF0Qi9YRGQwTURQVjBkYmMyTjNLQ3U1NklkTFVoT24zb2ZhTXJHRjJ6dmI0RnhRZGpSVWh4UDRHTGVwVzVpZlZnUkpGcEltd0Z6ZzFTVGFxNGRZMXF0WFZEOTFXdUxxZ0RCaVV3ckZ2RjJhb3pHUGEvK3I0aU1JZmM4VHJ0Um45Y2lnbnIxUVA1UW1FSGk1U21rUE5vb3dYTFZWOFZMMW12OXlkdGdZRGlQeit1K0lOOHFNem0xMVVaQkZSWGNxNDA1SDBsN0pnaVRrdjNmZTc0UGpTTWE2T0swWk9keXptc3M3RjZtL1VqSm13dndPbC9RbWhwOVNncndrbW1ZUmRvb0tPR0haeEVpRWNoNDJ3a2tzYXQxT2ZlckZMRUs0MGlvQ1hvL3BJZzdISDUwcUpNZmllSEZjczRYYVRVMEhQRTI3aTZ6bUJScjYrV3M0WXoxUzg1cThDZEFQUjZ1Z2RlSkZNaXg2VXJZcUpNYmx5VnFGSWpNU0ZBM1Y3dkF4RTFLQXBlNkJRUWFWUXAyNE9TS0ZUMW55aEdmdkt3UCtVakZubERwV1g2bDMzdStvOG5lYTZmRHB0Y1ZyUzNjWEoyTHZGazVyUmVDdGx2QlVQVmFMaVQ5clJBc1gyZVlqNmRHalV5NmszaU5hcFM2VmdwTXBucXkvR01RTjI2b3hSNXlZa21odzVlcDFiR1FnQXQrUHlxQnRtcGEzeWU5bGEvM2JLS2k3U1Z0SzJ4VHJDRVFWNStFUTFhelFhZmVXaTlabVVTKzIvbVpEKzM2RzJZaFZxQ1M0LzdlYVM1RU1wNlZabm05NDI3T1pTMGliNUVveDBnTXZGMDZBYU5Oem5tVi9PU1ZBNWp4dkNydzE4TmFOVHlldHlaR2U3Z0dvajd1TWdaNEloMGNMeUNNczZWZzdOUUJsdGo1NURRaExSY0UwOVE0dWRNV1dibWtIRmF3bXZxb1Y1YXoySjJ1RnRJNDFDa2JFRHhBSVZDWkFjdzJmUDB1TGFLQWFzdW5aWFJrMDZtczl1RU9SRDFPemVuVjl1YVpDc0Z0MnFQQ2E1S05PQ1RhZWxJOTFTQ3hwV1A3bEY1RytHa1QxNStWUjhzY05xdDRWbVJKTkZtMHhJWm4xbDNHa21FVklNOXhvZjk5RzZmNGVyOElsRlB1djZPWlh4eXBGSXNZb1ZoQUZ6TVcvV3Rxano2UHNFZkxMbHMzbzZ2c3BLT3BaclpSZCtHUmNkd3RsYzRQZVhVRzk1VlB4SCsrSGVodGEybHVDdnJOSnAybWRQZTRyemdycjdYZjhNWTlja0sxdXV2UnN4NnZtRVFUUHJiK0d3b1dMeDJxMTFWckExcTFhT0dWN2NDSnp4bmQ1QUJxdC9aS2NIZ1pqSWFEMG1aT1M2dnJ6Z0FOajEyVnNqUmVXTm9YKzhMejhpMHpxNmRyNk9pVEF3d0pLNkFvR3ZRK080TnpxTHhjM1ZndGovamh0dDJxMXlyVzJwcVZudE81d2llbU1UdWpEb0U2M2w0REt6azc2RDBVdDMyanVRSTBtS0dRZGM2SlpvVnBKcDBNWSt6cUVKZWJ5SFJvWjJVZzB4dVc4Z0RyclVRV2NpZHFac3JyUHpET1ZlN0k5enhBU3IvQTNkVFZ1bndiUnpRRWdTK1B0YVY3dC8yMnJZc3NEaDNjVDR6aUlweldjSW45Y05RcGcwWnV0clFMSkg2YVRUV0IzMmJUNVAwQ3Jsa2lUYU1rQjNXZFNEQnpqNGFlSzVkSEtMSmVFbmVuS3ZLR2IrRk1UamxCeHVQd1VzVlo5TTA1RkQxRjQweDB1RDV5LzlwdWhjR3VuM2NkZkRSOXN5K3ZiUHRDNVRYQjRDYjZ6dTQ4UmRJbXJtRkI2K25nZzUxdXdTTUkyTmZXL2FHR1Fzc011MGk0akZhdElMbjNRSVVId3Y5eWo2c0Z2SWFJYVdoTFRINUVvN3lUbU04aHNTYTltYUVrSkdndnhtRTJNemdQUGs3NnJBaHN5OFFEOW1zL3ZNdFRxbGNMdUhNMzJyY2JKS0VXaDBnenU3Rzc4eWFiWjVVS2ltY1lGcWxSNlJoS25OTHU4WUkzUkJYZzZhTFBTQTdOZW5SdE1sR3h2NlJPK3FjNS9iVnVlV1NuZnRmM1hBZmpqZjEzalNtTytsWVpHRWpSSHlWcjkzbzg3SGZidGhJczlwekpVbm1LWVRhWnBRbEIzZjArTkdFMEllNkhsK29CWkIrcFIwdlhzYkUrdTJKbzFQb2xqRFAzZXJrZmxmeUU1SDdlSUFrRDMxWFNVb245ZWh4cEdCYWo2U3FEbTJ2RFh5Y0JwNkRVUjJiT1Z3VFdVaFJQVTFUSHBUSFk0SHMyMXJqWnprcmJRQ0l6ZTJSMEcvTE5mRnBrZ1djUnZMRTlqaVJuRXBXRHFic1lObERkbnZFY0F1QiszRjhnc2I2NitZMjJzeWp3UFhjZ1JJK2hsWjR1RjY2ZFQzSjdBZXFxNnpTVlNVOFhpMGh1QlBYeGw0cEY1Z2xmcU0ySHB6eFVOVkhIaFJ6RCtXSmVVNVdGb2VvSHcrWmlBMUFNWVJRRXJTdy83OXNmc21OVVJoUGc0OHlLTkhRZFk3eXBXaVh3RWVocm0rM1dUaWUyWmIwR0ZyN2VGM2FaWUpYeWxHWUt5cGlqRER1WEFQbWVsOTNMNDVoK3RVd1N3NTNjTkJRYmMwdE1NNC9IV21WQmpqVXpldDd3L3RSczRJUDFLaWM2OUVUQkF6aU0xcE1sQUJBdTc5ZjdmelZuTjdMVmZ5VWwvd0FBQU56cWVoVjZQYW9mUUNmZjFtTkFCZ0ZzUWxGb09aNU1yaVB3ZytoN2ZGU3J2RURZbzdBRlVNQkViTy8wUDZ4dXdDMVdidWREdzZLOUtSYU1uazZSa0dwU3VEVmVIcHlHRkJJZVlaQXNna2lMT2dtL1F2VEdVUXZGTDZvSkpudjEyNFU0UVZIVWw4ejlEVkpuQXk3cUMvRGU0TEo5QUVrMzZyVmdQRzhacWhGSWsrdnczVFIvSnFNL2gvd0ZYNGk2NG0wTTB1UnFZZjJObk1tWHlPMUI2VzlSNmhwNGdJQk9nQUMzeW1ZWXpHMVBJWmpQaE1vNTN1U0RPM04xNndkTFZvTnc3SkxpR0UraGVtbzhhbnQxVTUrUXBUNjVMUlViL3hjV3lrY3lLK1BORDQyWmxST2tZaFM5d25vK0JDNG5kVDRlejhHY0F2eVAxY052bHAwbTQ3cDVTN2M4OU5OaDFmNWFUbDBmTDlFUGE4YWhIQlVBU0ZMQk13QUEyRzZ2OU1FaHFlamdCRkk0WnpOd0taVnR2bFF3L0hXcDR0amxTNlZPd0VRZDRWMWI2N3pVcWVhNFAxVFhTci9yU0lkT1E3bzFhZENvRndWYkFpY3BTSWdlVzRuL3lzVGNUR0ZRcDRlQnRYdndpWWJVc1lSa3ZMSnFQVHcwZ1hUeDU0bkppSUxaUVRVdW1tZ0xjYlZjYVRQRVhpRCsrckFxSnQyMjBxSlhuNm5vZEdpRm1TazVxMTF2dFZ1RHVuRlVnZElQVm9VaVJ1TmZyeklsQzJxMUc0Skl4ekNwUWtvS01nWGs1RzFYSjZnM0YrK0t3bXkxZ2MrS1FKdTRLVFJxVGpZTUVwai9GMUtDQTMrdmNkTXR0TmVKWDdySFp6eTJ2OVNrbkUzaE5RNWhZLzF5VXkyS3R4SXB5c1MxWFhrUFlTQzlaOHQ5alpyMGlHQytsTlN6Z1oxMHB5bDByRFZSRXdDU0hsSkwrdFJhUlZTS2FtcWRpSUtWdzFndW5RemQ0cS9yRUFPODNwNjN4UVRmSlBYNmoxNmhpT3ZYaFNhdEVtcXdveE1jTlQ2dXBlVnBFYTJsa3U0cGtWbW5aY0xIN3BId0lPdjBnbFVGS21pdGNML3pDN21ZT0JpTkpCK3Y5TTJaVUticXY5ZCtWWkZVSm9zdHdlRktTb0ZEUUVKQnc4REN5WUZIa0l1SWhJS0dJUThUQ3hzSFZ6NGVQZ0Zoc05rUzBtUWhoVVR4bGExU1JLMVlDUTB0QXlNVE13c3JHenNIcHpJdWJoNWVQbjRCUVNGaEVWSGxZaXFTQlBnRnZ6SVptQUpNQllLQWFVQXdFSkxXZDlBa0VtbEZ2NTVvbElyRUk1SVI2WWhzcEdCRWNUUWx3UDZLLzczaFF3Uk9iQTd5LzJBRkhCNkN0cm92RWpCNjN2SmdnNEhnODNoZWE5ZFg3d29BUUNhRE1ZRm9zQUk0dnV0d2RDOThNQnA5ekZCTGV6WHZHck53VXJIZStzT0ViSE1mN29nREJtWnlYL2xtM3EyOWZTZlRsbHE3Qmc9PSIpIGZvcm1hdCgid29mZjIiKTsKICBmb250LXdlaWdodDogYm9sZDsKICBmb250LXN0eWxlOiBub3JtYWw7Cn0KQGZvbnQtZmFjZSB7CiAgZm9udC1mYW1pbHk6ICdLYVRlWF9GcmFrdHVyJzsKICBzcmM6IHVybCgiZGF0YTpmb250L3dvZmYyO2Jhc2U2NCxkMDlHTWdBQkFBQUFBRFZZQUE0QUFBQUFYY2dBQURVQkFBRUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQm1BQWZBZzZDWmNYRVFnS2dZNDQraGdCTmdJa0E0TUFDNEZDQUFRZ0JZaHlCNElxRElFS0creFJCZXpZQzI0SEVFblV2NUlvNnNQZ05CZncveW01TVVSd1A2QlZRWVlRZDF4NWpLYndhaDVWZ1JaV0VKVkp4TkVlUWNWaWwyTFZqY0EvM09UTkRiczZYVjNxNHRDNFpkd3poeTZaNW1KeGlzektGaTZwdmlxZVBpdHkyU3JWZmpLOUlmRTJiZGIvSGFHeFQzTDkvMTlhZWU5OTRmLzZKZldrVHBPMEtRUlYxMmhUYUd1eUhEYnF5SnBqdnRET2dFV0FUT0FDWmtETkRPQVlwTE9jWFVrbW9mbHNHVUdXa2Zhc1k3Q0NUeENrQ3JCTG02YnloYmdxVXFWb013RGJMRUpCTVFHcE5BZ0ppVFJCS2lUYVFqRWl0N2x5bmE3YVJjVEZkdGZibFZkYi8vMnFMdnh4enFlOFpBWGdwRURwdXUwVDlnQkEzUWtIWkVGS1FPbE9xT1AvUCs3NXJYM3VHMDh3azBRelNDWFNQeE5FV1V3SDQ1cHltUGsvM1E5anJyejhGbkxHSFJVS2hldTBtTzFmU1ZHSzBjeG1FNGVMMkplTGNSWVZpTUt6b2s1UjAxWER4WGsrZDkrUGorMjJCQko1cHBuWGdxS20rK0l5a2FGNnBFYUNrbGw3cDVHMWV0QTRkTXIvZlp0OVZXKzk2ajJubTBCc0RBc1pjYUl5NHJlclpvZm5lV2ZaYzdJdXNQc0h2OURFQkR3bVJJbU1SNnk2ZStWejkrK1hWdEwzdmpSQk14ZTBGL05VNlp3Q3Y0U1lDVkgvbnVudC8zOHI5YVFPbTlTaktrKzMxbFd0VUd0dHI4WTFraTVvNWtKRURqR3d1ZFpHYWFiSzBtWHRPUVowUmlFa0ZKa0pQa0FNb0JuTmdFUUFEUkF3TkR4bTIwUVM4eHFiWDlwYTFiTXdHTE1ocW1XNzNYSVNJY1p4d201L3FMTlFydVVZakxXdnJYRnNXVmFjL0hZdm9MN2RZZ0xvNjg0Qy92ejExdm5TRElUbW1YOEMvditHL3ZyenlmbjRNNnREQWVwUEFhZVNDKzcrZ0NtQXY3M3dBUEQzZmdUY3FiYU1HN2E3QlRsdGkvTG8rUFlsQTNQSXlKWXJYMGlwZnB0U3lkMHltUWFSMFhleFNvbHdQTDZMWDFZbTcvWjd2L1ZyRS8zU1R4MTc1UlYrUUEzN1dBQ3doYjY3ZjF0M3U3dHhaeisyejJHTlRsK2laSUM0REVNRW52NkRjdnFVanQrbndNNlpwWC9hb0Z3R1Vzdi9YY0dGdTB0ZEdCSENKaXp0WnhCWXlUc1NNSlZ2SENtd0F2OTVSVDUrbWtzeTRyQWMzbndPb21MS0VqeDVGWVFzR25Ed2FPL2dIMUVGODNuRlVucm1tU1NHTkhoREd2N3hZWVB5ZFE3QncxS1dwL3E1NXhIcFFTK2JvdGxqU1B6eUtYRVMxdkpSZCtTQUFlUXRyMENCcndJRjlYU29OeUtSSkhma29hS0NWUlZLajk1cEIrZzlDWk84OCtRK1VqT0lZdnRiYlh0dGRJMThmQjNTSzVQZnhLQVJZYkszNGVBbXNHbnJFdGNMb0pPWFoxQjR3S0U2eHg0Rm9jdVp0TndJcTVaQjRiTXNKK0JvUU9UeUh4L3VRSXREbHFMWWJPcFBSM0poZG82RE5maExBamk0R3hPZ2VUU1p4TFYyS1BLV01KUnNnUlZhT25SRWsvSHVNeGxHVmVSTWMyL21wb2dxY3N4MUlscGVRS0pCYlR5TG8zWVd4S2pFUzhJMU1pcHRaYnlIekRzcVdGbEFkRDVSSVFXZStrajVHVHhEdWRKUkJhakJVb04vZkxoYnNrWHZSVXdvdDFRRkJMcVJocWZ2UXVYclBwNXBpemY2US9WbUM2L0QwN2V2T1NNcjRrLzBvVUsxM3F1cUZ5QXBQRldQUVhuVWZKWGVVSzRoMlNuSTJlWVZ3RXJ5NXN2WE1HMStSRnVWeDVxUkFTbzk1UTkvcE1TSk1rbVZKdW02Tmtuc1dZSEQycE9RbU0xRTVQM29mbXhWZFBOZG9Lb2VBMFh2S2VsUmt0d2pmVlNLMzlwaU95aFkrSEtyK25yT3R5K2IwOUV4Z0FyL1pjY2dxaWhZSUNRWUlrSWdKanRJaUFBcEVTRWpFdVJFaG9Jby9tbWg2Njl3UlNJei9EYkNZZnR1WFM3b1A5TkgvTVFLWjBoeGhneG5XT01NRzV4aGl6UHNjSVk5em5EQUdZNDRBMEl3b0lVY2FtV0pXbzdPRFpwVG5xY1FOemhYWTRJMVorMHpMNjREbUZwbnc5RzhxNlhzMmtzWlJ1SlBwcEdpem1USCtxY0tzaDNDMVJoa1d4T2RsaUpMTGVDV3o2eU52Wk15UXhhSk85QzZ6MEhvRjVxZm5QNmU3dGFXYkM4NWRvQVdrODhHWWVsM2tXTVh6ZkhXc1NlZUphbEtMYlY5VkJwQ1o5Rk1pOGl5ZS9seCtYcFM2MldFaFNtTi9haWZleUxiSTVjY0J5QkM1TzFIRnR1WWJsNWVGMU9CdGd3Z1Z4ak9vZEhqa0ZjdmFZRzM1bnd0ZXgxKzBzZWhoL0c4ZU9nVWZSMHN1b2lTSFdIb21IUmRUaWVlYXRGMHdkK0FNcTJVSFBOS3BVUXVHK2FwRGpVZFoyU0ZIb3VQT3VaZG8zUnhKRFZYdG5TbkNSZE5LNTdLWHhSL1U3RDhiQ3Bva1k5UkNYTWtaa211ZEt2c3FmR0NrWWtRWkY2SU9CamE3cFlpNHp4ZDZ1dnFTTlFnelJpZjlNbEJ6S0I2SkZ5RUJxMktJZTNnOEVlQlBlZXd6SnJIVytEcFpMVms4VXgydUZJNU5mVWl6ZlR3dElsdUdxYVNUbThHUllPY01iNEpXWDlzcWltYnk2V3grWlZlcmNOR3RKWjd3K3VXQTFmc1gxZ092YVN5bERCM1ZnYm5FdkpzYU1JekZzc3dLalRjbzJpaHFWMlY3OTRidHJvNjV1bjRlekV4a0laQWE4eHJ0N3BySk9mekV2M2ttNEFvR0pDdGVkRnJzcTFKMkxpdEJTZW9QbU11cW9kcjQ1M1BOZGxaM3JVVkpKRmpsL043bG4wWDlnK0gwS0JvTUpac2daVitZQ21aNDM1WWNtaG5vSjk0UkFnY1Q1Q0FKMFQ0MEE0NUhJZ1R0Y1NGV3VKR0xmSGdRK0pGRmZHaGl2aFJSUUxZa1NBV2toQVdrbUlzSkNVNGtsSjZoTElKSUFETENaQ0hqK2hKQmFwSkphcEpCTlhQcnVJY1ZFTE5xNS9JSGN3N0lqV1lEN1U2QUgzRUFxakxaSStBK21nMk5FU3pSZU1KQzZBcFU1dUhML2dxK2dCZlJ4L2dtK2dEZkR2MkNOOUZIK0g3NkNQOEVIM2M1MGZqRDhXMUE2VXU5cjd5cjFiY3ZlKy90MTBKcVNHQ3R5ZXBhQ1p1WWZ3ZndFRStCN1NmZ2VBRGFwZ1U3L3pQRmtiMzh6ZXdiZGNIYXJkQngzYU9BaWs5alVmbDhYcW1lVXhyYkxSeWFpVzlQbndDL3UxMWI5Tis4cEl5bVp3SlMvM1VycFpKdXpPZlBQR09RdDA1Y2FnZUdLMjFNenR2OTcwL2M4M1hIbCs4S3BjdkIydXZmME4rNytOYjU5SDNuL04rdmxnTTgrZllmMi96bHVWcGVXRE16TU1wa3U4cGxQN0JXRDRyUHFqc3ZtQ1lPdzlxZS9yZGwvR2EybGZleFpFWnA0UGM3bGpmYXZmRWl4YTlmeWxhKzhadUJQTFVhLzVWSXY1c0FNZEFsaG9wZXVwbEF4anFPTXJRaDh0TTBIRTA3NDdiK3FZMmRva2V5OEs1M0RIRzRETkhwVDVDcWxQaW5LWnNZMnJWZXIvWFNsOVFPNG9XeUJvQm5RbEVvMzUyb09vV2g0ejFJVjdXZzBrYTdhTUF2a0s2em5RTU56Z1M3OVNhZVp3dnNQWk9MUjVoMTdEQkRSd0RiR1JHOTVMQnM2dmxGMVllTEZlNjExeUJFdHAzYXNYdHlubjJyanN6bGNPKzk0cmVmZWZhdDk0dGZlOWJOeFc3Z1pHWmlsbWgxaER4OC9kTFB6QUMrWkJEZ3AzU0grenluODArcmJPYVNNY1lMaTlkak82VFFtTUlVWU1YNlFBRDhQelZwem1Lc2ZZczNGREpOTlFod2JzeU56REhwRm5EejBQWUZPNHE5RndLcVhWWm9VYU55OWF1UzJXTXhPU3JuZnlnMkYwWk5OblF3MUplM1N3aS9tS1FEVnhpMzFNYVlFVDQ0TjBnU3U4NHJvcHQzeTU4bzAzVnVmTkQrN2M2VDVrU3BBRUIwa1dPZ09zTDR6bnRtL2Y3UTVUelVmUi9JTExoRGNydmNzMmJDL0I1QjNyV0UvdGNjNjNGdmpSN2txOVM0V2tJWkUyeEcvaHNoNHpBTHZza1Q5eld1NThnTWNSUDhCT1RZZ296Ty96Si9PZlIySDhQK3BtOGpiRTJxaGh6MU5tS3NWKy9YL29CSkVJUmpIUFBjc0dkTmZJaDF1MGlMc2hnb25NdmRSM3VibVNIOVVHb3BpYkt2eFZnWE1xWnVsQW8zWkpHWFNYQkZnTWRWaCtQcmlSWHNtYld2SzBMeEwwYlh4cEhoRWMrd01qcTFnUm1NdXZPNnJaK0d0Unp0NXNiM0RLTnNreE81akZiY1ZOUmR0N3A0WXBvR3IwNlVIZ3VGak9CVzM0aG5rVjNraXRYd0pGZ0krdEVRSENwckNTcDVwcm90QmxjaVJnZnRRT0phOGp5V0tvTEtaNU9wM3ROWHF5TEhoN0cvRnd3Z0duQWFvNVdZWTVvQWhRNnBkU2hSK29MUURDVVRHYWpoZHYvWkM0YmgyQy9xL0lHdGFLbFJLakxrZzF1L2JmcmdycUY2WXlOSVJZTnJiVUtCUGRWb25NQzVNV3FyTlVrWDJGWGNJK1ZpK3ByTjRyckorYnV1MHA2YWdReFBhcEtpcUNIN0RTVUF1SmFDemsvY1VITldMV0hKYXoyd0NXS242cng4aE5KT0dQSnA2aWNPMFIrRDBtOVpnOHNOWGZPNjdYRzd5V1hkaEhsemtIZHdyR1ZqeDlSdTcrUkZ3MkhSK2UwQjlENnovbXJWNzlxR2orR3kwOGFvemdHaUVCRFRHUENkRXhVTFU4YTFBM1owdThzUHlJV05WYldMT0dVUDBRVVRpbTRoZjllRXFDK1RTd2tpTUR1NzdneVgvdGp1ZmNrYjlha2RHam5aMGVkS0JZYVhCTEpZQTg3Ris5N1krTjNvUEowb3NPblV4bkdLUnJLcU5uVmphdEV2ckxNbVlHQmpPLzdOYUNRTGRLc2JvcnV0dmpFZWtycWdtem04UmNJZU5uSTZUUjFkbkF3Y05uT2MvYVFTeVllczh0ZmM2bmJnaHZVL0tYUnRsT1Z1Tkt6VEFUZFA1ZUVlRTNFRmxDZmpmYTJUakpwcGlTV21GOVVKaWZySWhvZ21CR2p4VWZLNlphSWkrWG5OT3E2YkNkd1hHR1Z0VFk4emdGenQzb0t4YnJqMENvRWpBWDRiSjJhS1ltRUE0SWNhaGt2aU55Nmx6VXFzaEVZR1FDa2w2ZUJjMHhVZnZRZVpkMVp2a3hQTG4xNmJTcUs5bS9YcXE1bkJwYWV6UzlsbDZPZGt4Y25YYkNLKzlQWGVLNDZadzlyNDhjdGVLQXcycHVOWnMwSnE2cXZmeHRkSmlHK2RTdW4zV3ZLNndMMkxtVDJpWUJ2YW9mZUhrQW5CTjlpc25rekRTTktYTkswNmJOSFhVZ2tWa0s5dkYyM2VNSlVvL2JJRGxtZFlMTm1TMzlnWTlVWnNzVzFGeE9jRzhzR3g3cnZIVHF2eGVmalJEMlFDOHY5ZThrR0pvSmk5ZHEyUWIwNVBFM3RrSEtDQzBlek03OFNCcnVCLzBTeFdOaUVkb1ZUSm12djVQWU90UTJuYXhmQUxmTnpWem5jNkNWNlZaRFFkZk9UUzV0MkxPYk9wZE5MK1IxSTkrMjA4WVNzam94TUVCM1dkeVBzaEE4T1FycXpVUzRKQ05Wb0ZzWkxSS3RBdG14OVZyUUxTQWxucXZPOTJmektvcmp3eGlUY3BOM3JDcDgwaHl4UmI2MjRpRXM3TGdGR1pDbHVKYVk3RlRzRUxKQmVyaDRpajQwRXI5ckhHRCtYQkM0QytZUkhkVnUyeU8rd0dueU10V1o3S01pcmJQaW9teERFSk5lYUtscWJhNXM1Wko3TDZZQVVLZnBRTHBLOG9mQkZKYTJnZWx1bFVieDl5YWZFRUJMUlAyTkxFWFJBL00wOWlMV1NIWjJrVnFDVXF5clBtVXlybC93czQ5bDdTM3lTR1JXY0VDT0ZtVHk4cTdoTHNQN25KK29FTUpJNzNkQXVGbXJ6Rjd1UWlQRVQ3L2FoMDkrUFc1M1hmTUwzbEh3K3pwMk9nbDMzOVQ0UldsM2pDWWFKN3U5YmxzazZZLzNPTnpldm5iL2dXazFhOFVmUTU1SHhsSGRCa0RpMjdncFU3UUx6YTJuUUg2Q05oRTluMVllZVhkOW81Tk03cE03QWFxNmpnWklCNTIreWo3MkZ5d1lmcjduVXkyQUpwLzFIMUR0VmpBbG54emZDdVYxbk43b0E4VXBrUytGVW0yU0hpTzJuZXI4OEQwbUd0K2xWaDFobWdvN25aSmIrV0NpVFlLOGFwQ1MxTWp5WjFSdElHT0NlNGxOSUkya01rSStScHhPK3E2Qmc4L2ZtNllMaDBNM3Z2aVNSZjZGNEdOZzdZTDJ4QjhQNVVrdWhEeXV1U1Rva1lKZ0ZHUXR5aXMrU0w3Z3Q2RndGQklaUkczZTBXLzBtTGRETDRzZzdsQjhpV0doK2RCUlRvckpZQit4dlR3Yzh5K2Z2Z1Ntamt4ZFNuRllXZVFmb2pRTjg5NnBzRytWOU95N2pyajF1VmlOOXkwd2VlWWRyc2J3ZndzczNGUUYrS2FMWlFra3psMFF6MXZvVTQ4bCtiZURPZWdIbTFvT2R6dzZibE5JTjNWS29Ca3hwV0hsZWlWaG9rWExBTituRkpZVkxZOW14TElxWUVnN3pkQWhpOFcyYVp3UDhsaENGMCtQS2t6cE9jZmNlZ2dMTXM0T2FGWVBPRGdXYUZiNld4S09EVno1NHN1WVNLSVpkb1NyTElSaEhhOHRpL0dHbU5wbzNUMWVBOWNEOVBrb0ROVU9EbXB3eVQ5ZjRvZS96aDJyamsvRU9LUWVzRjBmWGo0ODVJdjNkNkZkSmgrSlVsQjhoVHFCRkJLM2RpNjVrMmM1eVpVNmdMUW1CMXQ2azJ3SU04WHZrNVh1M2tGR25YUjlDT1ZMU1lRSDFzVTNKcUFrSklSOG44bytmaDU3YW5GZ1pkcmVVcFdxYzlUd3lQMEpLZElIMnkraGltTjBTQUFjUHNkd0s0SmNhWUtKTXZOeWdxd3BGdndDbHBwRXN4Qm9xbkRSd2ZNa0VOUzVTUURad21YeGl4bXFQQndrcWhrdXBRbzIvNEFDMjRDWFliL1Y3VVVoSm1KTzVIZnRxbU4wQTQ0TDk0TTExMGhrc3lUUEJsMndEa2Z4ZVByRS9ScWFIWWFMSEtSUHlLOXdDeTQ5SE1hUlM3ZGRrbnE3Z1BPN3FSMDZKdTN1N3k5RjhkbVd4emFwUUludjBRYUp6a0xrUU9NdWY2cmRLQXIwMmhReFRmeUU1d3lhQnloTGxxQmkwQUF0d1VrcnhEeDU2aUE4aEhzcGJKU25oZEtyTG1XWFp3QXpNMkQweUg3ZjUrYTRyL0tieXBRZlc0NFF1cndIeHZWdVRQcXdBMWg4YkhQb3FRbnRUTHNudW1VeHJ6QWVBL21CZlYxOTR5Z1E2Ni9McFJMR2NLVzZTK2oyWUtSdkZhVnRsMFRQcTloUmxYZFJqSm9CRi93YWxZOG15bDZmdlFXSmR6aFgwK3NFamVqVS9zTURFL2M0NDJJTHU2cGp0U2JlOUg3QmtnaWhIaG5tdVNWeVhqZHRjdTk0dlZJbTdsVHZ1RmFaWEw5OElnbjlveUp2ZXhsY1ExRXNHK1RaL2V3UHpGWXoyeUh1Z1RVeWdWUXdqTExWNk9JVTA5bFZmWE81R2N2MndQbmt4aDQ1WE5ncDNoQXVOUGR0TFp5cTE0UlBzZEhuWjIrNkZaYTlnN09CSG1nWnNZd3BSMGk1TkEzVHZ3MEkwSTFwRmZLZ1NuUVhQV1hmNEMrMlIwUERIQXVYbGVTZUNNS1ZlanhhUmY1UTNFazRtcGRTVnZxZ3JJaUd6OTZrb0doTmpKbnV2TVlxUEpRMHJSTGNmWHd1NUpRaTIrclZpV3ZSVjMyMHhiZ1o1eFFHR0FRc01NSklvUzNtSU9NVFkxUEttRnoySzNJSVhvRjFjcE1KQW00S2VCbjBYMTI4Um9iSWRvSlB0ZkhZSmwzaVc4KzlGQmlkdGRuNWQ3cXdiZlppMzdRWHN1SjFCYzhPdGZ4Y1FvbGFpS2dYQ1E4S1lBSm9IeDJLQVBDQUdDNzJNSCthRG9lbnpjT2s4aGoyMVhVWWJSNjdTMHpMbzhVYVlPT1Y4QkhWUkhZTmx3Rml4MUVURUpkR2o5Z21lbXpKYkUya09YV1pvUHlzMFpkdGFxeWU4S28zVEw5bVlZd2pVR1JsM21ab25BcWNJcTFzL09NVm56eFJOY2lsdWZQdmk0Vy80cytwbjR0eExGODVZcGg3ekt5dU5KVzBHYStXeTIyc0ZoTzFZNGwwdlNibllkd1IrRVpPUWFFc2kweElWdUZNNHpoYXNMdG81UGNrTHI1NDNncXhrM2hwR0VXYlRsdVdEUWtsRGVLZFlCZkZjSlp6TDJDMEdPMkROT0JmRHB1SnR4MXFST0N1dDZrdjZIRVI0TVU5a2dLeS93SnlTK08xZHRSUFljR2V2NjNaK0s3TWl1K1MzaFVmUDF5U3QzejlNVnM5REt2UmlmcmRVV3NQUDZ1STBmYzFWZWJQd1gycWR0WEJrWTBxcWVEZ2pqZTlNdXZDL3BUVkxzQmFNdkxyOTl4MzMwODlNV05mVWQ5eUMxTjdOa0tpL3JPak82YmFLTlFjQ2p1Q3dPelMwK29TWk8yQXM2dGUwamRscmVqM3M3MWJGaGViSGdKSmFVWG85S0xFWDRtME1RN2pQeStPMDdpK3FHelhubDVlRFlhM09ud3dyc0twRmxNcVNGZk1kTkVDbWRQQ1hWUHZON0pzc3o5a0xlelVpWUV6eVAxREJtNjh6ckRVZWMvbktLUlY2UGZPamlpNndCMnBNR2s5aGJYUkJOUDNyamYxa0NzdWJTVW41NkxEcWhWcTVvRkk1OExNYXZOMFRCSVZlSVA3cW13SzNrazVDZEMxTURIWGZ6MnZ1dm9WWTQ5c1dLTFo5aXJHRDRmRUhaL3hmYmFzcHRWbDlkUU5zbzJqMVNSSFhGb2xZOHRTdVBBVWFiMjM0L1BCbzVBelZDYkRvaTczQUErVitmYm5hVlZIb1d6d2FGRnE0YkZPS20xdGVPSzVTZFEzMElIbTc0Ty83ZTVmbStEeks5RzkzTUQvOU5EbHVvUE9sa1ROODhHRUxVR09oZUR5eDlwOTlkNFJCc2RJU2x2VXVRejE4dERUK21GRVMxdmJXWndjV2RFV3NWUUZyK2diSGMwMkhwVVFkc0p0SVIrT0hLbGNwNzlIMUxKRkdkVUdQRFJoRjFWVXVuejdYRXgxc0t5cW1lN3FrMnVqZG8vd3ZkcmEzVjFZZUk4NHlHTTRzblJCNGJUTWRzVmQ1cGRKOGJYMU9IZ1BGL0VYYXlNUGRKZnplNjBPRXBQejFWSmg4RGJFZ0hyL25Kd1NXNTZNUmFab2x1WjhiVnRhc2lQN0xzZWgxYUZrT0gvWFBTdDdFYjJ1NG1ER01PbjhQWGN0ZzVaalNLUmhxMm5zMTR5UkgyVzFWS2ZRNlJjWTNqejgzZGFDb1cvV0w4ZzV0NWV5WnZEamkwcEtqeW9mTFBuSTczNnorMUJGVmlXYlMvSWlFUjJXTFdzQ2s4R2NyY212YzdJY2ZXOXlSTWJKWnZVdlh2N1BPRks0M3NqVVNWdEp0L2k5SXBrYjc5dU1yenJzSDV2OVpSMENtUytHajdYS0pzVHBzcUZUajhJcExwalNWbFg5MHdEQzNDR3FCbU16TmtRNEZOenN6eWQrQ00vVm5QeGtjeE1RdFl4ZVMrSFNQSjh3cVVvZXE2aUtabWowSlRxeUtxSzVCdDFzWjUzZjY4eHlsUGozWFYrMHR6ZXZ2UGZJZzQ4OE5QZGF6c2Zxd21lcWpONjl4b3RMMmRBYVVOKzJwNG15M3hOQllZc2pNajFiMXcveVQzNnc1SWtLUlhIbzhGck1MWTZBZkd4ZTdKVk0wc1pTcitTU2hSR2VnUExnd0J6RFpSWko2aUVrOEFDdkV1TE5XMk8wZTd6emVlZzh2NVYxWnJveTNQT0MzV1ErV3NUMWV0YXlGOTNIekFBNFo2TVc1bHVXWmlvaGQ3SXpIMFBINmQyTklvd2tFZHF3M21TZWk2azNUM2E3LzVsODIxQnVRTGt6VDBrQ1BkTkhHL2ZrWVAzMitMRmN1dEUrYVNhTlFWZ2w0NmEvUDljWUZsYWZicGRmUXVQbG9qYVlxTitGc3JEcDlkeWMwaFl5cVhJMmVSUDBLa2xJcFEyUjRIbURkUGZCdWxGdWVuUklLS0hZeVZyaE1KK2ZCVXBZM3FERlNKclhSK290M0oyRjcwQ2IrZjBnMzZsclMranJuWE5zemw0aUdlbHVzeXBJZHN5dXo1ZDZjWklnS2dTMDkwdzRGNFlFYXUwU0VXMFpESmJneHVUZEJsRng5U1Q2R2JsanhhV3IyUU96Q0UwV0ZNWHgvOFpFK295cElXZk14SmxEWjUyZFdpOHZGVlB6R0JTci9PenVjT2pveHlzaXNIQ3NaWDlPTzBsNGQwbllXQjlmVVBFOXRHT1pXdTF6andjeGliNTRwZGJMRnROYkNKZVBxMlkvUFdBNFhGK2FESEdTTTUrRFlaSzFZeFVrbDlSR1QrQzhMOHJGTy9ucW4wKzhQZTBNYWVVZGVnN3NBdThLditYeWdwWDV5dzhRZ0VtTXNLWXlVNytlYWlzb0pHa25HK21uZTJzODZhQ1Q5dVg1VkhvbHp6Y2htUlc1SktxN09PYU9CTEcxL3NtdlpxaSthR3lqQ0toRmdOb29hRGVhRzNHYWRHRjlXTk1ZdUcrN015TFRFYmh3M2FvclliMWI4Wi9ENytGTHhZaGk3M1ZxajRqU0pLVWoyMWw3N3QwMXI2V1QxeEUwbTVkdSsvYlFzK0JKaVBFcHZMMlFkTDFES2VJbHo2YTE3SXBGV2RwbE04K3d4UWVkdmxKQzFZbzJZbUQ2SjZzNmRmNjNPbnNycWE3aHI4ZWlFakdITytuS3gxQ1ZYU0w4WHhjRmxWb2ZxQTkzSzViUCt4SHBtejNYcW5xMEppanREeURSZFgyaW5HVWhLMm9QUXhxdWpjZXgzVk9ZVjVDYTBjTHB2NnhMUW1mZjVkbnRmR2RtbGthdGd0NWpvUTVlejBCTGxWUm9pbEJIZU9oU2hGQ1Q0aU56bzE1b2FuVUVmdHFseWM0ZG1PTkhFaVF1MDlMamx6cGtxa25IbTltSm9UV09LdnFCeFQzdm4xWS9KM3F5U2ZrSDRwRDJmbUpybE9nODZaL3A4OFBibkNlZ2xsTWVUS2JsNEs1K1F5akl1TEhJUGRpU0MxVkJLSWFLSEZIOFdIdFdDRlIzbXlZcFVjdHlHWE4yS05IUkZKRzZzc0t4SWMzUG1OdmxEbjVDbkl6bUw2N1FDVXN6bWs5aVUwYVRuVC8rYjFUbm11ZzlIZENJWE5jcUhBSHNsU2Fibkh5NG1YTHRqd1pxRWpnUjl4b2I4bGYwcmZzS2t0THk4WmFpaG9HZjI0S3JBYmcyYVJKRzMvcVFIb0c3UzlLdll0L1E5dTVlbjBkTS9qTmZXU1drZlJqS2pjRnV5TGNqeStXZHArVEk4NFREMU5DcFRuVGZ3WkVNeEduYXhMaFo2SDhtbkEyNmNkRWFXMDZhbVRlSzNwL3haNkhKejJlSXR4NWVsSTNIS3hQbjNiUWRKNWdUTlord1Y2RVcxK1RNQU84cmR6R1hTczN2cHlHOTBTQldGNE5RTkp3WnlsckJkZCtEVHUvallQeDdLQ3VKblNmQ2s2alZML1BRdHJhV1BhQkU5SmRSQU1rMXIyUDhlU3R5K2UrTXVENGlFeGlBYWkvcVNOOFFUUjJJZG03VFI1VnloRWVXU2ZCTzBySnpiNFRaVFZHVVpEeEp5eEpQL1A1TWRuUXJoNHFaa2JFWmVBV3grdkxRalZtd3RiVE5waFZ4cTJYbzZrUzMrQWU3ek9rZ1VReUpkYWhPd3ZNZ0twWjNhOE5DTTFxMzVaenBtRzNHUFdlcDQrcU5VV25nK0hUWDUwd296eUlyZHJsT3hjOUlHWm9YVUdVS0NHcC9RWTcvajBCRkpwaTZHbzJlaElVN1N4YXFDdVdkNTgweDQxYTFXYkN3bzV2cC9xLzlYRTQxWVBya0dKZ1Y1NDJQUU8xdGZyYWVWMUZBQy9WNGk4dm1YQVdMMWFXS25OWlo3b2EreUpJMm1kN0RzQ0xZOG9LZ2dxYnBmaEFndUU3Vm5EUnZJZ29TNVNGMzgrYWoxVnRCQklrbEc5eURvL1lUT3ZDcnlwdkhRK2RxcHB2QkVkYnZqSkk1OUJtcEkweVp2NTRlTmVWcFlLU3dwV1QwTm5kalIyTXZ4NjVSSG9mdDY4RzQ4Vkp3OEk1TGZHUVVxaEVkdTByT0lMNVAvSnJRK2FvdFVhb2lzRCtNYUZKcUtZQVlNQXZhS25xRisyeU02eDhSQitxdkpZVDhXc2REdE91YjgrK1ovMGlJZWlYaUVuYTYwRzNMTTZwNW0vVWlhZHJiSlJKZU5HcGMxTlBnQysramJQNWhtR1AveGRNS1VHQk1iVXo3SUU1NXV5M0szcmk4cE5tNWNHaWticEQ5MXlWVldCWDBkaGNIc1M4OVVGV2hMWjR5WDFKdlR4SE8wREw1Wk9hYlh5QmFuMER4ZlhjR1RPbWVYb1VvaVlTdmVtUk1RS0lqVzA1TU1KWVB2SVdzS0dvVVgwempaOTlHYXdGaW1ic014Ym1FYVVyZmZpc2NkNklTVmM1aktvdXM5YlVQYktMeE4rUm1ZSjR2dGdXbm50UG4weUJFMnM3cHI2cGc3VjVYbUhoZHdua1phVWhjcXNHa0J4Mkc3OXJQWVJjT0JEM3VzWmhrZitsbUNMVTRQeEpEdW94YlZxL3QrS3pac3hqcUtGVUppVjk0UkRCa1BHOVpzTWJvcGRScGFBbkQxNmxYZDY2Q1hvUzAxRXJiN1FucmlzZVF2Wkg0UkE3Y093Nks3cm5XNjQ4bVFYSis0UHArdkxSdlAweFV3bDM0QSt0elQ5UkU5TFZTTGRWYm11ZjVyS0h0eC93Q3lUcE9hbmUwRnE5S25FY05pL1ZyeUpnMVZNSWZvU28yYlRTK01oV0VMR2Q1Q28wZXJYWDNKSnA2M2VzUUNxb0VTWTV6MEVVTE9XYm1uNEc1bU1PMXpBNWRJYjFDUkVBdzNtR3ZLUzA5Y21zNVo4SFIxcDVLMUlaZGhwSjBESFFRVlBOaEdZSHo4VFZhZ0ZvRWxaajdUQlMrV0FYM3VHZnFJbHZ6VGJieVBqNW9GYnNjQ2ErS3lqV2JRbVFtMEZ3Z0d5cnRISkRMYmpabEpjWk5lbFdMdjhTNlhlTlJ5MFBoOU1UQXp3VU1wMWlvRW1VL1F6UUYyZ3NwZW4yaGNzMmZ5Q1UyRVVtcVdTOFJQOWZvZFhUdU44L1NGRGRsbXBpcmZVVmMrUCtBWk5WUVpLTVYxQVAxeEYvdUR1UHI2QU91ZFNaQmtIMzlVTUNoeEhPNTFaSnJwMzZoMXNLN2JUTTY2SkhVQisvdWxNL0JGaXJKMitBQkdpQU52VWxWMmhGTHFST1RNc1VWQWhWYytpcmp4OFZ0b21vNTVpTW16S3c2a2VjM2lGNk8zcTQ2S0QxeXBxTTBqL0VvaXRNWEdhTm1hc3VRaFdQS3pRMWlZTlFvaHVzSHMybHVyZW9QNUZWZnpSdmszZTNkVGNMYjRkQWJySWhiT09tL056NkhFaTlKdjBnUW1JY09KOFBIY1ByTmVzUHdlK2taM2ZHSjNQTFpCWEJIcFpYV2REbjI5a1FweEEzYit4d3pXeDE5ZnZhLy84dnZmZjJtQUdJSUhodGZmSU9WMkk0Tzc2dmJoMWJTdXhFaDFLQ0ducDZTeHY0bGpNQUxlZEovaTZRaHpLb2ZCbEtnU1VyaXp2RDNlZU9DbmJiZ3FYUUlvUHV3MzhRdlNaUHlzUG1UcGlLczgwMlZhbTJEWU9nR1pCMDYyQ0NGZjI0cUkyS2ovKzhnRytRVUQ2Ulg1UjZHOXZmaHNiWlFwTzNCaVcvWGRHeUxieUVvMW1hQnVVOVJ4QzVRTnFnenM0c2JUQzhzS2xuQ1lzdWcyZDlIVDFLOTVGN05USWE0L3Y3K0pqQTBJYk5QVWNhcDVTNnRRMUsrL0pQaVlrWVFEQzlPTnR0aXlERmZUNXNpUGV1SDhXWHA1Z2dXdmtkNEUwcWpHREx0Zlk4K3lHM0dtSEsrMXlKRWR2UDRYRUlUSDN5b0YzNnBIVFNVSWZCbTRRTXgzeXdEbXdKL0dHSHc3c2lpaTg5VHdRb3NwR3A2SWo5UUxTdldDOXFQS3BCRzZVNi8zZ3d0VHF4SXIxTnhTMlQ1ZGtwN2hhUnBOQVZ5Z2tWcmlVS2hkcCttSkQxVzc2TXJuWmVvS3Q5MnVvWGVtRStZcld4d3pZc3E2a3RZU0EwVjZLZXZ1aWJkSFNocWlncHBVZk93c01JTjJNZXEyeE9Jd1NZT3dDd2VXUlpjZndwZzdmVkd3RGxwSUFuaTVxZEd5d29pR1ZPcFA0NE1LZDUyRGhHKzlHZE9STmcycWFXVm4zcWxHc0NzcUMwVVNjNXEzRkd6VGp5NThQRzlXdE9mRXVrV2tuWmxxUjRuNWhmK1dDTmtFcm5MM2JNajJRNkxwbDMrcEt5MWY5ako2RFNWcWNOM1ByWjlzd1pjMGZVR2FHYXd2RFpHUDZVYnkvb2FZd01wWkZtQkEyLzJoSWxXdksreHpudUxncXozQnNtL0dqZ0pBSFVlKzJ2b2lzeTJYUlgyNERhdVM1a1h0LzBjbEo1WlBEUUNQSExxZDRsZzhwVjVvaGk0NnZpKzZEWnhzaGV6c0dhNjRQOElva3I3emNGbnk5cjZ5cDZmNEI0ZnFoL2t3eXh3V083cnYvYldlTFN1QjZzTS9KU2EvOXhnK20vL0xBcVZocFMzbDAydXhoS00ycHpiTFhwTmZDM2F0MlhtbXVSZ09nb3czTDJxQVA0UjZiT0FoNENML3ZKblI0TWo5QzhNdFV0ZXd5eExsWGJHdElCUDhCYksyeTJ2eTdqOVFMUWVIdTFNL3J3cUFualZDdGhFS0ZmYzN3V2RiT1dHOVJhUkxzQWs3VUtWOTFnVEl5ME1mVkJSck9vcVo2UVZEWXBkQ0Y3TnJ0ejF6Ujh6cHdveVYrMy80OFozdjI3NTVVaWtZbE9hMC80QktTWjJBWVdoQi9SVURJY3FRUGRJOHMzNEJzd093NndYL3A1TEYwMWpSanVldlQxOWNjZGpKMlEyOTBxS1IxYXBjSmV6TWZuUlphNjRpZjFUTDcwWTAvSDduNklDeUtIdHBWT1dSQUQ1L2xnU0VKUjhiYXZSbTZBaHhCK0ozN0VaVHFkY0dybWNZSkg0V1lrbk5vdCtDTFgweGdObTVaYWNpeFpObjFvVlgvYktlZnpQc2xvOTF2Q0tiUmNBZ2pJclF0Tmc0UkhwNmtRVEE0ZXZMeFlqWUtvWFI3L2VzcWlyaUhrdkVUNVNGckRYUi93cXJhWC9CNHgxM3AwN3NyaWJFYVljYlFVSEFYUjJCZVpZK1dsSHV2QWhZQmw4YTl5bTJmVnNWWHowTE9wdVowRkdxbFdlMWxWMy8xaUlQaDJuN05sVmRZUmtXVVpDZ0JOZmpadCtiUEFxTEc4TVZSL2crSlZqdHRJRWMxTElIV0hBQXRnQ2VhcmQvK3g1bC9wYStzSHJsekdoVkhRa1pPWHU3YmQ5QmlNVjJFM1FSeEFZMW9JbDBQYXlJSlIzOHFmN3J0TTB0QzZxLy9iZTZBaU5uN1ZMQ3dhY3JDMWtxQ3JURXFTc3dDbE5pRnlpRkNZRDVhZUxISnRPYlhZTWhYaTR2dnQvSzF3YlZJWjlkWWFEYUVKRG9uQi9zeVVBamxjRktjUmk0ZWI5a25QdVhuZTlwOU9lRDFFcm9IQUQ0clo1R1ZrVlo2MUNpNUQ5b3kydGZTVjd6bUFaR1ZPeTdWbncrbHZmZ2ZMbGJhcUQvSGh0RkEzM2lWY2wxZVdxVy96K2dFUkxmaDZUYit6TzNvTTZZdVJCV1VyeVVWTHVvcTNkdDBhLy9KZEg2b2dLV1dEVXM4Z3NLclJhaU1RbFovRTlNcHJjN2lacElxeGp0a0liRlE3dk12c1Z1OXovZnhSblVGQ3lSaUVFV2lQY2FEQVpobFVpQWVscmtOZUs4dCswRy81eS9PNFBiY1dTRE1JTkliRlRsc3J1Unp6L0VKdVRFbzBYaGRFeWE5dWozRTFrdU9pYlpPUGNqczltS2d5WGRZTjZHeGVYR3B3akROSFNXdlF1KzUzak5hK01VeXNjK01tZ2U1RlpDQ1U2YjhkUTNtV2x6UyttQmxEVGpESDVGckR3VjRzcFlvdUlteFFRRWxobWltTWVYZDNCQzhpa1VuV3lYMm5LWngyMXFuYzRpUG1sdktFVDh1NDdKMkp3dEdRMGFXWS9aSVBVVEZlVnhaYnRjU29oemRiTE9lWmxHeU1OTDhtdTdMdGRNaDFlY1JtZXRtS3NvL3NNT0RmQ2twam91cVZFVVE0ZDhXUm9sQWVxUEdkak9ScVREaGJDY0F2b29TdUJEWmNrTEt4dy9qY2pJTGZncVMxMnROakgycFVvV0NEc1BRdzZJQitkRzg2c051b3pTVFdNQnFZVC9peUFkM1hsMHFyTXNuaFNWM0psc3J0QjhsYWhLMURJNmNRVVRHU2t6a2ZDdm5FM21DSFJLTU1aR0RKZmxsaHlMMlZuNitHQWtDcVNsUSthVWhUelJQZHZ6UWZ5VGtNaHI5TDFxYy9FSHZ6a3FFbkZHcjNvZ0hVVUplMXRCa0pCcnRMYkVBVVQvSFhocGpGQ2NtVnNUWExWZGlNcXZ3TzhTSVpjZzI3NnRTdXpxZ2M1bUozU1VMckVZMnZ1V1RWaEZJKzM1ZHNIcytmQ05WbVFGdU5ZcVlVOVA1RWNIbW8vNmorbUVudVIvcVlRNDdxL005NWRNUTlMQld3RElaWkM3c05BM0dRQUJyL2NlR3kvc0g0dE5UZktBYjQxQWtUVlhDRFRUOHZKNTVVbGlDU0FNQzdFSHhIMXQzN0hRME80VmM0OXYrbG1Ebzc0Q0hzL3NPOEdSVXhiNEdJTGdrUS9IWnlLUFRQOEdnVVRrQ3JYYUVtYlRTeWpGa3RqMWNIUzVibldRN1BDTlhSdjdpOFVMbTBuM3o5N1pPamM5VS9JaTMwTFlhUVkrSlByN1NPN3hQMVR5eVh2M1ovUG5hUklGdm12MHJXYllEZ2dZRncyeTQ5eCtVeXowVFVOay9PSVRoMk9BNEFQN2g0U05jNTl2WmEwSmcrRGlIT0NJTDJBUVRUZDAzRDM0Z2RkejdGMGU4azFpbUR0NFhBSFUxZzBxRnQ3YnJyZFdGUjl3T2JsREtwbjY2bmtEL0doTStGMzNEMUY4c2kwaXpRU2REU0JLcHEra1JKUXI1ZHNDd1pHdnlOTDR5TFoxc3R3ZFRia0lNSTZFTnFqay9CWFkrcGFFa1VEb0c1aWQ1Tk0rWVdTd3JjaXEzZ3I3Si8zc2RBMUNUek0ySm1Za1lScEJKckR1NUZzOHpYRHNWZ2dBSFoxcmVsZmdZa2kvWnRpamRkS2dMdUROOFEwY0pyZVZ0V09JMXhDUlMyU2VGQmFkUFMrYjFwSHlQTjk2RXVHbllIa2c5RUdlOU1ZNUhMNkZtWTRKRnoyK3lPZUZidERqTE4rOFlzaDVNWGVKNlppek1TbEJIUkZ1OUZXU2s0elh5b1ZqQnlOT1lzblNVUTU0dHExRVFmd3JnM3c1cHV0SE5sTUFKTkNzYmJxV1NSOEg4NGhFcFlmR0xFTGNRemlEN1FyTlQzS1g4M3J0QlNNN056T3ZiWEdMeWp5WFE2NXJud1lXdHVuRTNQZlVZOXMxWXA3enRFN2dLQkVER3VBTkYyOUJ3cElqb09rVTRlZDFqZ3Y1aXNJOE1QU0J6V1NpYitHWWN1cTV5Ly9jbDFqWm1oYlByOG11WnBKeDBGeXV5Z1lZUUEwcmlOUlhrRVJCVHMwcGFvWlZ2N1ZLdjc5a1FkUXB0Um9zVEh0dis1RXl1SFpsdkcydHA1a1RodjFUMXNnRzl2NmhVZk5WOERMdHM2QmJsUlRUUUFSLzNkekUvSStHcE5wSlJJRVFONGhCdmlESHlUM2l2RFhQNDBXdXRSQnNETXBaV2VkQUYxTStJVlErUXFWN3gwL2x2YWNQYjVzLzNMdUt3eTdFb2drYXhTWWYwa1c2Yzg0ek54UkFDYTFnNWt1NEg1REZ3Vjd6UU1zb2NBUmgrankvZTFEdlNPaTlCczQ0Q0hGdFFYUStJcFNLM0NDYnNxM1JxbHBkbzFLVmE3QVR1elZLc2R2K2x2OFZYWWVsU0JINkxCSXM3Y3Z6czlQOTNmN1ZmaG43aGxsVEYxNkQxL1RjNVZ0SEkvSFVNYytNRURXcXplZ1lBSUx0ZDNwck9HekhGcjJMZGVtN1llZjBXZjA4ZzJWMjlIakI0YkhCUmdRYzRTdVJrVkZVaGxjRTZtR2Njbkwvemxrem9ML3BLZ1krT1BpZTNxZGdvNGZnMGZUNjhtTG5BNXkrQmNFdFFPRkZzZGIxeGFvWjRiZjI5MmprSnhOY3kxdk03Y1JhWFlhTG4xUzdBSzRjQVpkVjJBYW1hbHBEWHVWckZGU1V1b0tEenV0S0JnZGRUbzUydHpldCtlUFkzUGFkTW8wWkQ0UHpYdDhCc0F1Z3dEc1Y3T0ZqcWFualNOREEvWGdHU1d4bGVBWUR2bUVvcmZ4VlMwbDc4b09TNFBhMnNYMTNzWm44Tmh2Zm5wOE4zVkNMNVRmTWJnZkNYelJsdkFOazU1SUFIUEt4Y2VnSlJFVkVlcWlpQ3NGVnRSUGxHa2hWL2lUQm9oQk5TVHNOdFp3WCtOWUJpczl6dmJjd2FJUUlDaHVLTHh5OFJjeVFIVjkwejZlNkhRVmVEMlY1S1BTazd1OE11bkVYUEgwRVpTS3lGVWkzWElYTm96UUFNZURBd2RWeGJkcStidlQyby9KcUQxb2Racmhybng5U2RJZmcxcE0reTdFaVIzUU5yZk9ubG11d05jd2J3L29lc3puWENJcWhEU3BhVjlzS3BjU29uN2pFTXUyeTluWTVQOTNlekkrY3hYM1RNYTBKcVFtc013NG5IWHhqa0VrdkZCLzNWL3I3UzFaQjRYS3pDUUtMdUpCWk5KYjd5ZDVtKzhydGJRYnloSnB2NnZENWg5YmpHb1owc3BuNHlPQUdjMTYzdUg5V0NyOVlZU28vdnJSYmJXeXgweXpQaU9WWEkxek54L096bzk3bHNaQml2citLdkw5a0ZvckxpMnNmZzg3VnJXRlowK29HSFdwMGRRVkF2WVRXK2lYNjNsMGxlZkhNY0NKMkJiWEY5TTFpNW1oVGQ1ZmltdFh2RGJ1RnZsbUEySHEwemFPMXBZRnNtL2wrK3VqT3JhdUw0cjRkMXJscGFqMC85eUJwa09Xakl5TXpJM2dJaWhlSUJaRndIa1hCNS91cndQdEwxc3ArU2NZM1VQcExhNWhCTFlNT05WZHJaSkZuSUpqMXk5eWxGMGJTYmQ1Y2I4TVAyTHFJSlFxempVVTZyMUhaUGxhT3Mrc1h6MjF2VHU5dmJ5N1ArWlMycmJMd3FGYXErMEFjZHpPczBBaUh3V3VHeGVQd2F6QUpKMWFJYUIrNlU0dzcxaUpWSnBGUzhTbjkxS25NUTZkYnNISzg5Q0ZQV3hCRnZnaUk5NXJZRVdQdjhwemdoU1kzVXFGQ3RpdHZvOGVXMlJraGxpdWM3NGFiS1E5NUp3RTgvTnBJVG5RcDdKc0h1WFdlbTY0WUZGNmZsV2hxSitlZnRwVi9MYWxZeitxcXQwbmgwVE1FOCtVZTZvTTVYZHlBNXJRMWlzRUtOZ0lKMGxZMWZCdXpXbk5iS01QbDVteEdyeDQ0Z00yOG4xM3ViQy9XSjdlbkE1ODdKdXZtSnlaZmhsalZhTG9Id3I2K3BpVWVuU2ZpNVgyU251UDg2WTd2c3V6S0I4Z1VzclNmVlJRbDYwVytRYzhOQ3JMa05YMk0xOXJOZHQwQ3k4KzBYWlVqcEdwVmFTWDFSWTlTclhTWEZ6WlRmbk8vMUEvYzkvdHhVcXVpMDZJRW9uRXNkS202YzJ1V085QzJJTEl3WDJwSUEyZTZUTy92YnM1UHk5eldQZXRrMm4zVUQydmc5blNFUTNkV0FXRzhidEdTUGhwZ2c3dWFZWTRrbU5qQUVmU2Zsd0VJT3BwS3MyWEwzSFdOZlAvejZ5dnNxZ1ExbHNjaDhBVU5BOEFjRlczclo0NjJib3ROWW9MZGMxYVZCYXVGZkh5TWJoZUFQdTVHU1NHWjVXVkF2Z1NCR1BOUWVRUVg1ZHlJS2kvck1pVGtkcndiR3pGWjVtQ25rMzJkOWV2SGVtSnBCOStIKytzUlRLWlBOUmo4N25GZTlCN25RUDBYRjdSWWpIV0lBVG5oSkxrQlNFLzBmb0w2MXFzbFZEZXYxcXRIODc2dE1KbE1sNWhPNVVHZVVLNkRzckpFS1R2S0pqczJ6RTYzZVAvZDJ5L2ExK2VueDRlN085dWJ3K3VyQzg0aTVwaHIzY2ZxUWNqQkRlLzZYY0hGVjg1NTEyYWkyOU9ReFVjUE1ZQk5qVkpRWXFXQ3VXd2xNMEtqTEhIeXRFcmNuYTMxNllQWDlHM3JKb3Z6R1ByT3FOQjdyemphc2Jpb3Y3QnBsa2ZBemNja2NFQlVRajdGQnBhZTVtK3RvbVF0cTF1WlJGNUQ0ZlFBenorM3ZMbzQyOTVzSHZiTE5HUkpyVFI0ZzY3cHBMOU5hbnZ5Z0x2eUVHVG5Zd3h3cW9EdXhDNEtBVUpaYmFXcjMvUWkwZWpscVdpL1JJZnM4anBJOGVFbnd0TjNpazZqMmQ0ejhHcHB0V3p1Tzl3R2c5OUZwV3VsVzc4NDFmcmcyZ3Z6Mit1bTdmdFBmS3dGczF1QmlIQ1FvNU1jSEJSdnRpOTMvVDdPZlhCTXFlT0V5eE9EOEcyNjY1ZzVGNGp0RUxKVkl0OXpiSUl5YW91bzNyY2FHRUlCR09lTmRUbS96YklreGxLL3BUNVhaWjVGMXJTSlBZYmdZVVUyUXdWM2svMmdlRGpRdGZpam5vQVVCYkl0cmRSaUdRQm1va1RuOXVuRnM4RERYNGNPQXpGRUlzZHNZZVVXbWUxTy9RQk1CK0NtTWZZNlR3WThiaWhEK3NwMkxuN0NGZ2NpZys0TzI4REpXanFya01QRVcxU0hLZ3VNU2QrWmVnNm9WY09IRmhtYUJuZllLS3VTMEJjd0ZBdjdQMTZPRmxiK0hTVEFlSm1wUWFVK2pBMkQxOFpYUkM4SkYza2FlTjNjdCtKTXZ5TzE4UEUraVh6UG1CUU91YnJ0ajgyeFV2NE5LUENMTFdYYmpjT2RZSGxrdENxZFNNZ1E2a2x2b2dkUyt2NEt6WVRVMnd5dUtMSmZhSE9zaWl3SmZMWGpVR2lsRkZIQWRTcVRqMTN4NjVsalc5TzRuemdzcFl1WWpDWVZoYkxJalY1enpURmRIVG9HU3F3bXdHeXVwcFoxWkVLcitvbkl1SnRpbW8zVnVTTStLTlBPTkllSGJ4RVpXNlRscXZCeUJkMmlhV1hlbStxellWYnVwK3laVFNMSHNBd3NhSHFMRER6L29oY0JOaHlSRmNQYUNZVzlUc2grR21wYWxZVlppVmdvUmE2Ry9lVGp2SjFPRFl6ZWxqS3B4aUZFdUhQTEk2dktpMXVUbFNnaXIrMmVIeGNJRE1uTzFWZHhZeEh5TVVqRmcyZTZZLzZORWhxSFJKRkhnV0EzVjUwWjluaGt6cFVZeklWM2NXUWdQWmxyZ3NYTTk3Uld2UVROUXYvOWxPMnFRa2F2TXE0T2dmcXR5RHRUWVFnb3BSd0lPbUcwTU9pV3NMKzZ3aGYxSlA4ajk5aWhwOWE5V0NKNWYwMXRnbnNuZkZuVDJJT3Vnd2lzNzlmSFArdjNTL1hWLzJ2Wi93RCtiZnZuM04rMGUvL2xGYzRzdUErb0tJeEs1cEtWUFlJUEhqSGVrK2NEcUNsZTk5R1p1UGtCRVBlSCtDN29EdmYrMC9TZGpXWjYvYm4vVldqRXZiYjZJNFd1dGJjR3dnbFh2TWNlVlR6Z0hqc2NMODc4b01VYS9aL3BxK3B4OTRYUWRSS3NTdllWSUd5VnExV0tPeS8xdnVxOTJJaGo5dlRXMDdaVzZzRWpYQUhTWmVOMlRrRE9YenlFeXlYZkJtdWIxWVBQUjNyUVVQdVNmaXI0QmNVVjBodTZTZUJyaVA5ZUdCQlh6YlRUSzBiZjJFcUgzSHYyWW5FUVJtS1IzbkdtdGJEQ213U2RDbCtQTFFiVGlZMnBQN1hmdXpjci9LTGhHdkRNUVdEVWdwNyt0MjkySDdBOEMvOVZrNmowaFRkNnFlbEpKR2RFN3AwVWQ4SmxRK29OR2NuRXRoRndtd0lTdzlFRGdMK0xxelJiZGQ0QVJoNDNnSDhEcHlraWFzc1VwZmhqaXJQbnN5bGVwWlFwUWE2OGRrZmZsTHduVlR4VVJlZHVzclJvMWFkZG5ScTFPbEV4VldMNXBsQ1d0Ulc0bjB5Z2xWUWFWVHJrV2ZNRWx6U3dpbFY2akpNVnh0T0x4Z1BUZnFhQUdPWE1qQk5oemFneXd5THFOdWxqanRGZDZsZEdwLzNqTnVqVU5aaHpYMXlqU3lOcnI3T1dOL0RPKzlLQUd0bVc0SG5PbE05cnFXc2Q3bko5U1ROdlJyUmpFbW5wSlNLbElDY21JU003WG84bzMxS2NoZXF5Yy9TMExrK1Q4REJyMWZzN2UvR1ErQVBKdzhwSE02dXBCc2sxNFZ1amRjWlIrMTExdmdrcWZxSUROZGJ0N1k2ZzVpMlJ5aWFzNmRybmlqTHJmWHRjcUZXblF3TnpSVmpOZWphMnA2aFNyTkVYbGZoaEIwUml1a1RFRDBIMVRGUnBNSTBzdzdKcmxXL1V2dGFpQlRqTjgyNEw4TmF5ZDYxdHUxUmgzU1RxbUxPdFFtTTdGUnNSdG1xck9CNTduWlJnZVpMc3hhMlUrSGFIeXZ5eDFtbDM0SG1RYVVUNzNaL1BUc2N5Ymhha2VaYTNwMnRKWC8vZlJiK2c4QVJ1SGlLSlRLSFN3Q0VncFVCQnc4REN3U01nSWlHam9rdVRMZ01ERXd0YkpnNHVIajdYYkNIUlhFRXB5aC9wU3RseTVNcFRRRVZEUzBmUHdNakV6TUxLeHE2SWc1T0xtNGVYajE5QVVFaXhra0Q0Q1Q4VE1PR0pRQ0FuQ3lzN3R2SzdWeDQzamk4MDdwK3gyVDZPbkViT1JzNUhMa1l1UjY0MnUwYVhmc0hQVHY4TjRWL3RGZm1maTBKZXUwRysvZDBhNE9IM3ZkK0xsZlU2R1JTZDh2WnZISVNzTzZHZWdEYzNKbmozSmdlL1AxMnUySmRMaFYrU2d1ODMxMCtncDYyN0Ywc3hacHRqZlM5WFRoNytGMjdQcFAvbFJyMlc4TXNOIikgZm9ybWF0KCJ3b2ZmMiIpOwogIGZvbnQtd2VpZ2h0OiBub3JtYWw7CiAgZm9udC1zdHlsZTogbm9ybWFsOwp9CkBmb250LWZhY2UgewogIGZvbnQtZmFtaWx5OiAnS2FUZVhfTWFpbic7CiAgc3JjOiB1cmwoImRhdGE6Zm9udC93b2ZmMjtiYXNlNjQsZDA5R01nQUJBQUFBQUhVd0FBNEFBQUFBNjBnQUFIVFVBQUVBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUJtQUFoandJT2dtWEZ4RUlDb09QZUlMRElBRTJBaVFEaUZ3TGhESUFCQ0FGaUVnSGpnc01nUW9ieDhFWDBOdDJVTnl0cWs0aVRQQXdFcEhIQVNqNTNpYmNNZGc0SUl6OWpsWDgvMzlPZ3Y4eGhzSFhIbURtMm15N00vQkJJa0hoRUJVV3d6aXJMTjJJakVpYlJFVWdNSHBQNUVCQ3c4UmwrOVZ4dVhNTWwrbzFtdTQzZHZrcmwrSEw0eEV2OTdIekxyMTdQSlpXZkQwdEtDelVOakxTZjdxdHQ4T1Q4dWtSaDVsTlA0SGVoL2p6dzE4MkxWcktNRG9sclV2KzN5dW5ML2tnUWFMdm5sdHZDUnhrRWRpNGpKR3NuTHdETkxjdXR0c3RpOWlheUcxRUg0c2t4eGlWZ3g1SWxFbEp2QWtTaWdtSytmcXYySitLK3ZxTjlXSDBaNWdmRExsWkRRUjhrR0FSaFlqWnhIVjNUMHlML3JYK3BudXZiNzV2YXM0ay9acUppZ1BTeW1aSkt6S1FEQlJVaUdaMkhPLzFGUFpyWGFJY2RiZGxxV1d0WkRoQTR5RTVQbUxuY2d2SnVZREF3Ly92NzJtZmM5NGZ3QkZNVEk2QWsrdDBPRTZhRW9RNHEwTkpURjU5YnY4THdBTDAvLzlmcCsxOXlpMnZpQ2FKb2hRd3VNcWxkUENUSEp4SzNqeVhFS1dRS1N3RmYvek01NnlKL3ZseStqbGpKOW4zSlB0bjJ3b1dUaVZreXdvY3ZJQ2cyYjFwMnR1QWJJVitZc2tZTzJ3N0FEalNsUEhZWXJFdHBiWjdGREg1MzhHaHZ0Lzdxdzd5SHI2RDdIRTZKUWRDSWxTRG15bmNDOFNrVTdCaHkvOWdWKy82NGRyblhTc2tVTXliemdIdEZ1Z0FzMVNFVERINUpIU0ZqbE12V3paenFwMk9PWFJ5aHA2Zy9nV2dvckJkQUFHTGM1NDZJS1hQd2pIWkZKY2grUHNaVzNyVlhkVTBJd2p5UjBDWjFFbEtzc01HeWJaa1FJR0I0b1QvMlYrRWlXN3ZzblRpdmRQV3V6ek1jQk84djZ5YzQzbHNZeDhpVkFpa1JDb1lTS2I2TlBTYmQ1dHVobUtJbG5aZ2NHSVh0THpiLzZsYWxWWkI0aTdIYUZhOXhwdDhadDV1dmtGdUw5eXc4QXNrQzc4QW1nS2hKZ3ZVREFTcUhUaUdBdFN6YXJDZFJQVkkvcW5QT2F1R3BzZFFZeW1kOStGbWwxbWZYaFJka0YwUVhwamR5eTlKdy9PL2xzNysvMi9USHFVcFRtV2lHRFRqa1ZFYW9YSkwzVjNxWGFoTDNkQzdBaGNqRVJZaFN4V0crVityMWNGblVVMlFyQklLTkJvcGsvN083YzM4TlR2RDdVUjBNTGM5ODVuQmJIY1IxMlJxUFpnMUlvK1FPdjVhVmcwS0VqdVh4b0FIMnZPYjM5WU94OTcvdjdhMm5kKy9sVkFJVVNsQ24vZldaU3BNcnhzNUxtSE1vNWJsc2V2OEoybTZMRmVOMEZVWm93NUZFa0tROXZieHVtVTR0ZmF1bFhsdEVPTHczeENBNHZ0OEJ4NkFMb29YQThDNmhlQkg2b0NBZER2cUkzc0F2dzBEZnY0Si9tRHNSMHdWZ0lUa293U1dMRWJMcjlOQmxnREF4ajB3QU1ESCtRWUFrTEF1UlpqTk9TUjJPb0VyditmNmdSNklpUktyVE52amdNT09PZFA5My9zcjkzM3JCVlNEbVJrRlhjNlA4MWJlcmZIYVV0dHFaMDNYbnRwWEI2Y2hFcEtFSXhGS1JMd2xVb21QUkNQNVVrcVhNcVZzcVVEcUpZMlFUa24zU1E5SjM1V2VsMzRpSmNoTkdTQXJHVjZHeXNneXRzeERKcEpKWkhLWlVoWW9pNWRwWlFhWlNXYVZwY3Jzc2pKWnBheGV0bHpXTHh1U2JaQk55ZmJLRHNnK2tGMlIvU2I3UTA2V3k1WEhsV2VWN3lvdktIL3pjZmtzOGYzQjk1WHZ2d0dKQVVjZlNCWVhBWUJFV1dzbWprNThTWjlUeDJmL0lEL01yL0pPamYxajdhMEQweUFKVWNLV0NDUmVFc2svT3ZLR2c5SjNwT2ZvQWsxS2VBdEx4cFY1MzczdkIxYkk2bVRMWkgyeXdST2pzdDMwSXYzMTQxRzU3RjFMZlJwOHYvZDlhcno1L3dWSisrS1I4OTczdGpOT2V0TnVtNnd6cUFQV1JQL2ovanRTSHh0dFkvNzVudXVZYUhDcnQySkwxN24yTmErcStmdHJXOU5yRGRSWFhZeXM5TmYzdnJuMzZONFpwNTF5MG5GSEhIYklRZnZ0czllRVRVWnN0TjVyaG5ScTE4eWxTSUVzbVpMWmhMZ2tRYkR2M2I5MzM2enZ1MjI1YmJ5ZGVKdHphL2JXdHEvbFh3dSs1bjl4MHo4cmVRNUFIL1pHSXVFd3hGNE8zWXY1TVJheDVBSUE5TEE5N1hSY2FxOU1EM2p1bjFGS2crQWNPZ09lL29PNC9pbkQvMVBnelJ2alJWd2dud05GMC8va3hwcXBUcHdJd1EweDFIWUVodEZYeE1CZ3VhaUlBelB3eC8wV1BvM25QR0kzN2Q1OERxTGk4alc0ZXhXRXlCeFkvMzFmdlRDVXNuTnB4bk9OVWg0ekdYbk13NU1mZlZnZy81UVV3dG14amtFSTVINGJWVkt0UjB1aEkyWlhTd2s2WWk3MnJXZ0RPcERRTkFzaHlWY0JRL0VNT0tZVE1iS3NTRUJGQmJQaXRVZXhSUU44TjAzc25xTm0wbTBST1QrM1A1ZXQ1QWR4RXVGMWlLOWNlbU5CemRKR2I4T0dqamVyZVJNdkprR2EyeVBBQlVRb1FidFFRY0JuRTZWZWtOM0VGajJMbWdab1hjaHorczljUFdpZSttazA0TVgyL0ZnTVJucm0yUGt0RVRsaGw2dkE0VWkxTWROdVNOTlkwM203VWdlWGx3SWVtYklLZTlXUWxGZ1FhblBhbXJiRFkybHdaNVZpYUVZZzhMWC9XRTU3ZVJDbllsdUNTd1FVUE1yNW0zV3Vvb2dvQUJyeUNSYnp6YWEwc2M2akJ5cXdzeWNCbEFXekJQOEkxK1R0YXZuMWpNZ1BoRGhHZEQ4WlQ5K0ZZbTFySzVQVG5mNFE5dnI4Ni9EMDdXUERTWnhRMzE1QkFkS0s5MktoekpDVjU0SVpYN0dvTDlVVHJaZVFSd1Z0ZW1BUllaM1dDUEwvVVhzN0U1MHU1emdPaW9oWjhKZCtZOFdCTlppOUZSUzJHalVlV2dBdWFFdVM2STZZOTgrUHIyS3B3RmU5QzF3UWo0NWwrZjRzaEZqcEVYOUhxdCtMNjJXdzlQUDVsN0k4elBEcnRydGhGVFVVb0c1WDFGRVV3bTdFTUhEaU1HekVhUkRFWlpERWJWREVZOURFYXpERVo3RDlpYUhZLzQ5OW9Ja2srWHNpUlU1K1ZQc0g4Q2QxekNnTjBJd0c0VVJNQXllV1lTTWhneUJoZ3lRUmd5SlJneVl4Z3lGeGd4WFVsd1p5U0VxU1NVUXVsbWdvd1VZcDBJdndvdEhWczdBSWl5S1RQeXc1QUFPNkk5SzBxNlBaNnVGYVRteXY1WlYwTUkvTVpZWDBoeTdjTWo1ckJma01MOVV0QXZlWnRrdm1MMkplbHlhTmh1R0ZFVEIrZ1k5L091Mng3cGFpcDFzVnk4QWhCcVpieEpEWmRyYUMrc3JoYTBmVkNzeXh2am0xdElhQ3UrTXE2cUNKa0htZmZaei9QMmlPRThMRWpZN1BSZng0MjhQRHhhMktkUkFZSkhUbkpmZG41dlk4WHhiZ01OZUg3dFJvb2liRUZDVWVsaDM0WU5BdlJhL0RUM21mYjNCWEhNNXI1aTFoanlGeUNLV2xmZVJOMmFNd1dpTnJ6TjhIWjV3cGJVd3pOclZHY29qUkVxMkkyRW9LZmlJaWFKOTJuWkpoVDlFWGIrcUxqTWdjdFhtQ3VLQUQycVNtWXNXeW1oYmFKWlVXczNSZTJicXJHaFIya1dZUkdJeDhNa2lpcFllN3pZbldIQm54QnRFRDFBbGRodTFDemtkSnJoZmlGR29jcXJoNitaRXRyeU1GM3doYTIvQjREcmJVTmRTTVM5MkRvczF1aDQwcUZZUnhrbDZSMDdKQzYxMUlyR1hYNnQrRWVyL1ZKZXZKZHZQNXZidTgyZ1NoRElmOHJ2czdyQnR1TWVpSERjdkJPdWpNRjVYdlFpNGd3WnJaaWRyUXpFaW9hR0tvT0FTN3UxakQwVTVoUHJ2dE5mSy94V0NDd0FWWW1XOS9xTHRPT3QwWFl4VEhJaWxMd1pqNG9oYzJ1UUxuZE9yb2ZWU05HSXppUXJkWXpqNmEyZG41VGdHWUZtK09GWnhZR0UzdFJjdnZnTkVwYXZSNXU1Sld1dHJSeXRwSW9seFk3NHl4QndER3dHa0NVanBBZ08wQzJneElOb2xEQThvRUNMQlRRSnNGV0NmWkdsQU9RSUJ6QmJSNWdBMlNyd0VWQUFTNFVFQmJCTmdzeFJwUUNVQ0FTd1cwWllEMVVxNEJ1UUFDWENIZ1htWFEwVmhoMjZxLzVvNWlXMVROaVZTVEFqekFXcXFkaVVPaE9oVkg5U3JPdVUrc3BZYVorRGRxVlBHMFJNVlRrNHFuNWtJQ3RhZ0VhbFVKMUtZU2JyVUhIU2ZpSUJkcVFuL2NSaXR2cU1MZU5iTzhFUXlBc05iUXBTNXdIREFIelpkTHVuNDljaXNBNkpZQ2JzQnFnUEpBS3E3cDBFdW5SSE1pVGxjNVk1Umttd3JnaHRkSndXZnVTOE5EY1ljSjczNEdrVGtoTTFieEN5SHB0aVp4SnNRc0Fsd2VYQmZwczRRaDZjR2pxM3B3RTkzeVJCbTNVakNQT2ZJUzREUHB2RDBWa0ZjQnBQQ28yd3hFNkQ2QlNINTZkU1pCR3UvM3Y5QTJIRmRRN01wT0ZIMStheG95Q2NWdU9OMW9IaVNQZVMwZkZhU01MaXFpR1JDZ3B3d3ZNRWRia092cGNpVjF6T3Vaa2FlYWNtcyt1UjVnQjlJNW1vcnVCNWtxb3pJU1VSYTlTanFseHJuTWlwVFZCdS9ndzIzSHNhYXJFU2tncFdhSmJtYmZyNWV2NFpYMWhJRzhicGZqM2dOZHp0bkYvWkRWb0FCTkVUcVdTbEpsdzVKT0tKSkR0TXVXb1pnRzU5Umh3Z1VOZjcrWkZPRHhMa0VFZlQ2ZjM3K2ZEaFJGTWF3SG8rMFFudUh2NUlzVzhwblpvdDN0ZEZyVmdXV1plZFlQeG9QRFVWd29QcVpxaER1cXByR05scEMwcTF1TjBhSTFQV0VZU1NpcHdXQStneExwcHBvUVlEb1hqUXBRa1FYVjBsMjZINEJqbklubHNqa3RDek1CditxRkRzMUZjd3psNGpnWHowbThTMUljSGVoU2dyclB5MHB0SmlBcmlpSkpFa0tuZU5FdVIzMmFvTWxPUVdjOGFqY1ZUYm9YYmpuRnY0QnVUN2EwWHMwT0pockhoWmlnOFNSUGVCR1RWWk5LRHExaHIwUW9jSWtRUXBBWWUvdjlhQTAvcndCUEZzLy8ySDRXMndOcWhDQkZ4aUNjWjVyTmZCWkJpa2dCREJVVDZXblREd0xPc1JkQkZRNU9OUWUxMFhvQXhESkljMXlYL0VPSnlET0N0QVF4RVZLeEhFSjAxZVN6TnhVQWVlYmY4TU1uam9tNHYxcmVhYmtDSUtndzFQY2hxOWdyd0oweGw3aU9ReXM5QVRhTWQ4VEdKaVRZMjhJSlZZRG40NUhNQTAraUdjYXFrSHQwazZlck9RVjRucVRqQXFDVE8zU0lMdHNYY3dCanZaUjU5amtrdzlBZEIzd0FVVVJpelhHUEV1eGZLeE5nSzUvYklnajVCdzgwSVR4WFFVcDZRS1NDbldsQ1J0aVptZXEzYkpBZ1Z5WFJUd1lma2Y4eXVBclErOWFmdkRDa09ha1Q3RmVqSElZS0xETGtJaG52MEFTUzM4UWdiYjQ2Z3cyTElYd2kySW5uOFUxODNnT1M2MExUSTZtYlYrcURDc3lpKzVISVhBMEJPa3c1NW5qWWU0M1lZQldtMVR2Y1JzUmdrVlM4eXFGS2hkU1l5Nk1VSU92dEFqemw3NUNnRW44akdvRklQdE8xak55NGExU05nWjBEMVJMT3lKUERSSHZMZlRsN3VZNVc4UVNBUWZZdUlDYVFYbjc1SHUrY01tRUFEMWp3dGFlZzE4bmRDM1dKL2NvckxPSTlDdlJMR0UvNnBVandFd0pNQ0N6aWhYbU53WUJIdzB4L0JaeCtmc3NrMEM3MkxTYmtmSzNMNnlyUzY3bG5GRXlEMFliT0dmeHV4ejQ2WFozMEdndmhwVXRaandhV09QLy9KTVowRVNpc0hjNUFndlE3dXBQTEdhUmsyUlhBcXMyL2p0M21LeFJBNnNaRHQ5K0lLVkNGQ1AxQkJhcFltV3IvVWpZTXFjQjBua1JxWlpCb004RGVmZEN5QVJ3cWk1ejdQWXc1cjBlenN6QUIwaFhZRkViWklJaHVkZmtRWCtVRm9OQ0tpYVpIZUkwVUVIbzB2QUtFeGJ3NnE4Mmo0RTcyYVU0OHdPN1l2VDhkNXNrNHFxcmphaFcvQnBDN2tmb09EaW1kZjljdGtxTFpGTFFsTjAxY0VveUxJRFlxRWtaU0NFdjFITzRpdElPSFpmaE5OMEhZeUF5ckltMG1Ga1U0VTRWYkE3SENWZ0xBZ0FMRzR1R1lCdUc4eXBlZzlFdHhrWElMZmwwQnNqb1pJaVhiV2E5aENMeDRUUzl4VVU1cXQyOVptNFV4SkZDNDJuSHlxajVYaUtnaTNSNzJhanh3UmFheDYzM1l2MUcyQVpYUDFWZW1JZ2h4d1FUNXVvMFRXSlR1d0E5OWhhaG1OMHVFOE1RWUdTdFlQSi9POTBUU0NZL3QwL1NPWnowSW1HSm5JWXJPS3ZQdVQrbkVHY1JnU0ExZWlzcitVZ0FaWUQ1U2NobndsTk1YWUErV2VGTkJsNVJqUmFTQkdJWlVaK2lnMThseEtjYjhtNDhENjc0ZXpQZVVEOUVLanlmY0dDSGV1djBXempZWHVGdEJDbDBLS2hvWTBCbUtxVGVFNDZoYTVXdHRmajFIdlBpY3M0REtIWCtLdHFkKy9uUGNza3ptK0cxMy9QU25sSm9rYVBJUVBXb1BuV0tvcmVMU1B2aGtiQXl3YUJuWGY2WDBRYTdiTmpFUllCK3VMT3E4YkwyRzFIakpZSFFGbmtXbHFrRm1aaGhEaXMxQk1nREdVb1RsTWUrakdLMzlRZ0FlemhuM25UeUJoT0M5UGJGdk1OZXByY0J3ak1oMHk5MU82T3dJcXpERlNOcjhUbTU3YUVxMWo1ZlQvUGhtZm5MZDJsTEIzcFptU0lSVVp5RS81anlENVBrL0JIQUNoT2tzaG9mYVI1UVBUWkZMZzhvMmZOUUZkemd1TmlyMFNHTjliVUhwem5DRlRrWTRXOHlPMDMwN2VpYkhVQUZZY2ltUmtVV0ozemMxWllBdGFaemlGUjRnMmlLYVR4SnNmQ1EybU1iRkw4aTZPV0hUR2Z3ZEJiaW9jYW9BMVFnZ0o3M2tkT1pHdW43RnVoeTlnbWl4UDdHMzJnelZoQUpIN2s2RU9TV2g0bUMwcVNhWVlPNFNBZTljeXlxd0Z6Z2VCWWpUMDc0Um9nSkNPaStUaUZIZCtNV2NxT0pCOWZqNVBKWFZSOCtwQmJNUzZoYlMvMkFkN1AwQjFmVTZwNDM3QTR0NDNySnJlY05SYmhFS0p3UHNGMHVQekp2NVNtQk5FWUlhbjZsZ1NhRXVzU2NFVDYzcjhzbjl5VlJDQll5NHNWc3VVazVTajFZelBKanM1Q080K1VlN21RaXhqYlMxQ2dPdGxWNEgwODRudUxJYk1aK3gydnJQa1JIRWRlN3lwTldEaWIrNDVtMTBuL3djd09DVGZlQ2RjK0hlVzZ4TU5jMktaSXFicDNWNm4rd1BsSHlBbStubHlHK0kxTUQ3aGx6eGk0RFF2ME9LNStXdU1CcnpjL1NYTkNrbFRkS2R4R3VFSlM5SnMvR0JkNmlnZ0h2cmhRaWZ4WTV4VDVCR1JFYmllZXhleVJ2TGx0ekVYalVTWjRVb0NxOTZCUWJSanQ2S01rem1hdXlBcjVzc01vY3lDdm43Nnh1SXpFMXpNbEFLRUJGcGxUaWxVS1o2NVNwU1g3czlZQnY3ZGIzRi9WZHZjWmhVbFdleFAyYzhWbGhDQXJEbkpIb1FWNGdVRHJWM1VZcWpSbHA3UFRKM2xxT2NBT0g3UWplaDlsYW1rZExHYVpWS3FLQ3dpT2lsZFFuVjFxZndXMXdyL1RvbnFwOE5GSDkxOSt2dnJmV0UwMUFEKzVsREJ5RmRBRmJpMUtNSHoxWTVFaGtNemVJV1o4Sk9WZlg3WTF5ZWFjTmU0SUVnR0xjMjhtSGVmMlgxTDk2T2JXZnJBcUJuZlFTdWdJeno5VGViR0FSdkYzZzB5bVcyYXRGbktSZDk5czlDTk1WZ3ZqSFFiMWxSUCtpeXZmRWlCeUUvZUM1Z3pLVVZobGc4L2d2aGFTekpvazdYSTcrZkg5Z0ZwNjN1cnpzZDQzVllvTTBwRWQ4Y1luTWJzdE8ybFkwT1FpVUgvUGNEdHJ6WFl0MkpUenJNRk5JMWtOdlh4NzBJZlVha2JxdVpEQWFGQUJpWWR1am5QTDNFR1JOdTluMDZUc3lsY1RCWWltN0JuTnE3Uk1DbW55V2Excjh4aXlMR1dRSWliMUFCNmdHa1hLNGJmcGNuTDErbWRBRkpRLzhyUUxWSGdlb05PZHZ4cktlUUR2eGczMnRJejRDaEY0VGNoeDllMHJUelZKbDVhMUFxRmFKbkZDNmxlL1liUFBzTVJPZ05JRTRQemg3NnBMUENSL1lzWmNXdWRnRUhPQk0xa0FmaXhySEt5YmNweWRkWkFSVjQ5d3pTUGczUXlaSSt1VmJ2UXlaY2hJc2RLamlXcHdzZWdtUU9ENVRuUlBKdHpRaElWR0dUV05rNDhSck5MNXo1eFM0U0RPMjQyQ1AxcUhEcVFUU2U2cjhzbXpQSnNndmNjQVBOVE1xOGM4NStVaVF4cXl4b0RsOVV4YjlBWUhtU1lvLzZuRmVOR09uUnNOY0FjRExEcExzZTY2M21SS0R3UFA1T1ZkVHF1aGRxU0RWR0VyYXF0OXRNby96MWpGNWg5K3RyMkxRRExDQXRhUVhPaVV1S2hINkV3TkI1TnhvZ0FObHplM1NuUHdMNnhEZU1SWnIvY2xiMWV6RkRJNWxiaXcwRXhJcFhzSnM1NU5nN1hCVERvdFYvRUZLak5VZ2lOSGdLU0hOTDhaSVRwVUtsdVp0L0I4U0lyKy9XRUhHdDVJMTF1UTBqQ3NzdWIvMkZzams1eEl3LzdZVkZaWFdtd3lyZE1nVXdXS3VTNitYQkVLZ3dmdlE5c2k5Nm5tVGZsWmsxa3UrYUV6aDM4NjZMT0xqQURKZllIWG5nMXlyMXZQWnI1WjUzcDlabTVkYlZlemNXczI3Zk1UaUYraGdOY1RPMmI0bE1ZdTgyeUsybVUyUko0ZEE2Sk5EQ0ZrYWZ4NzY5aWpTa0FicUJQYmdGT2RWK0NlbklKU1FOZklNdGVyUE5JZ05acktBeDhnYWlTdEJEZjEwQVJoeUtCUHFRTVZLcXJZSmFXYVFxaFIzS3VKT1BFKzRrTjN0YjJQM3ZaSHkrWTFGR0RRL2ZTQ0pxTjZWQlNBTkxEeFJqWHdjejlteEFMcWJEcnhlZ0NxSnZ5NkY1bXVXbG05U3N2dEN0SVJvSDZBVE8xQUxUSGdKejlDa2NTbkhpdGFqSm14RmsrOUQrZmxudzZ1WHVvaEN1RzFrVFplNjl3Smw2SDNHejRrN0s4LzNjYlRSODlvR29tckFpVE1BaGN2MlRDNjA3NWJhVEoxRWtMRUo3blJtTjRyM0VWTDc4ak4vWlNkSzZwOHV1ekxEUHkxRzlXeno4U0ZOS0I3UDd3ZWJvRjJGNWtvYWZYOEd1WDlWVDkwZUNSUFJsa3JXUXowTHhTL0hMcGJPSWY3Znh0Zy8yUWZrWDRJTGJiSmROTWxyc2FpTWtENlhMSDRzQU5JZ0NHQ3g4OEJkZzN0TmJ6OWI3MFA0dVpSY0V1VStPbE9US050a1dIb0RLZzNpK1hhSWNFKzNrZXJ3L2htZ2pnUlgzZDFacTZSZG8zdHNSeVAyRk9yQllaMlR0dE4rTURZT3hRcnFhQ09uMUZidXFsekREQ3dRMDJMT21rWEJGU2xkTHZzNTk3RWlQVndEVXVVNnRaYVg5cGkwUWgrWlRsWGhBcjNlc2NxeDRZYkljSmdSWTFwUS9IeHphVU44eEVySy84N0tOM3dUaG82dURTOWh3ekIySW05Z1hXY0hQaHhWNFpISWFwRHJZbythN3FVTjFRZHBJdEZZUGFlOGhSdm1TMTZRTFl6VFlpYW5LYzNqUFMyN2lIS2VKQWgwUEhpdXA0WlZHcDYvb0wrV3U2aWFmWWVOSTdaNndrL2pRNFBWeDNWVEQ5OUVkODdHNUdzVVdNNUQyd0F0enVYZG94NTJNT3ZudGp3WHN3RjZPYnlSYUxhbWsvdHZ6Smc5MWdyWHFGNnlLVzFKM1pBdXoyQjBVZUkxQXZmL0FJa1NpT1NTbEJVazdYK0pjK2htMXBMRUJodEl5NzhOSnZCM1RnaXA5Q0NlZ2VnU0l1ZTJLQmxXT2o5bVphajd0ZklOdnluV2xLaGFNKzJsV21jdjRNR0ZMdGZSUzFJY2FZZzdsd3h6OU9MaDNyMjFOK1Z5VU5LSENOand6K1RiWVFMT0dqR3ZaZVRCSFdGZEpiOURrZ3BzanM2WWRONzZRcTdKY3h1Um5VTnNIRVZMNlV2UTdTbW94ak5PUGZ2RFQxb1luODdvSmJ2dEM1bVlmZkJ5MEVIV3crcmRwd2NxdXpCMURmdXRmaUJQdllLdlJRT0ZwZ0tLamRCdXF2bXk5VDZOeFJUbXV3ZzRnVDJ4aWtGUmJsa3A5b01wY1B0OVliMlZxa1BOSWdtd1k2c1NWNHB5SnRrcnhNN1poSWZsZXoyTVNmT3ZtaldNeGQ5VEI4dW5CRVRhUzRHOXVYZzZQY2dYQUVMVTJUanNyMklOTDVra2RnaXhFM1hZeW9mYkZteWd1aE9zdUh0ZnE2Mi83T21jQzkzeDEyMk5BKzVlbUZOMVFHTXdaWDlyOE9LVHJxMmpSa3VnRnpHOGZBaUo1dkNMSEFteFYrZDB3a0x0NUVIM3h4Vk9OM08ySUs3Y09QbFZmYmdlbnhQUkhxa0x5QWQ3aW5DOEhvZlhuV1k3bGhURUdBdlZtVDR5OFVlemJkd0Y1UWJud0JJcGoyM2dLM2JqcXc5Y1M0d3dCY2xUTE5OWS9wQm5kcWpWT0RkbE9UVmF3RXIrc0YzamFuMXE3OTZxcTQ5QThJZTU4a3JFQ1RJUEo2eEZKSWpMcEt1ZEh0NVk1TDNUYkg3YzhKL211N0tpb2pxbWdIblZHaHFkY3BNU3VEME1Ga1dIdng4SExEUm9nWlR1YVBFWjNWZ0Nmc3laN25DR2hmUjBQaHU3VU90ZlgvVWgraHl1ZkQxSDJBVVNWc2FINVJWMHF1Tm9oakZ6KzN2K29KeVh4MjNqVGt3MmZaVjFoWVRzWGFNM2E4RkhFanpQOHFJRm9DeWRyN2Vpa0x0VW81SGdLdGlFNzBGT2dwTWFORlpQWFU0WFJ5aHBsd2g3Uk1JNzliZkR3R3Qvc29BSVJHMmZoTjJKRlJPbEpxSUpRbjFFS2lDeGlpK3Y0aGpqOUppK0JTYjArczQvWHZ3Rm9ORHQxbmZoa0g0QjY4YXF5Uzc0UmUxdk5VT2R3T0s3L1FSWWd2dXhIaUdpN3NiZnBHQSt5R0oxTG1BaG5ibUxwWmFRTy9haFVKU3Eyd0NFS2crRHN0cGppTldWak5tWnhaNXlIYS9obFJUVUpnL0EwcERlWXdwQmhyanl4OWdtaEFReCtabnRmVC9BVThIUlBnTGZRTTNuRHRZNjlyV2M3RWdPc3g5UE1NdU9lVUhKZTBscG51T0xLcm9lZFAyTEN4SHdwVUtOYzluTFVGU1MzeWczaWFuVFBzUlBiNEE1dmQzWmQxQUg4eS9tUXV0d2tMaWR5SWgrdGZoVHlEYUIyV0VadWZCWXlJMlpRQmM1MGdET3FJYTNLT3graWxtRDBBZ2pmQk5hRFNTWENiL2JVdUU3cUoyV3dPTHdVVnA4SCtJb1R6bXRkOUMxYXNiSmJnemJkdnRoUG4vakoyaFdTSUVTTmV3YjdBOXhxYkliTmdNWElXZ3Ewa3luaVVhd3ZZYTlBOGFLQkVGWDNlYjVjSmt4UVVWeFdDSXIyckYxTm1Wayt5OXhZMit4RE9GQy9nOUxYcDVEV1RudDFnUFBLQmdDMVlneThZQTNaT2dicGFrS0llY25vR2M1ZnRsZEwvUUdsTm15MG1YN3U4WWhUbThDcno3RkFpVXlEczQ3UFViMkZ5aEE3UGZXNUx1QTNDeDk0NlJwZkdiTEVPaC90NVZlbTV0aWh3LzNCREZQdEljRUloN1BKVFNhaDAxWHZZelI1cERSVEh5c2daT0paenVkVkxHazRZbytIM1BCTlRqTEVlSDNETVRFVUpnQyt5OVRxbzFYcmJOMk5qeXB4S0phWkF4bVNiR2VKUzd4Q2RNdDNjRTkyOW5lby9uYXNsK1l3YnhuRnlzS1Y0bEdZWG1YeVNEV1FxTUhza2k3ZmloTWNLWDdzS3FxcGp2eVhzc3pDb1ZXRHRGRUh1enRDT05wWDNqNEg0R01hcDRrd3dTcndpSE84cEFjdkwvWjM0aDJjZnBQTmFJYXhnVU1hRUtwelZGVmdNR0tVcWZIM3BrSFJ4Y21sWG83ZFNFc0NRdVFEM25YVEdPQTRvQzJpRndQUG9kaWk5aDFEZTlhNDEwYksxa1VtLzZCTzRTU2o2REFUVVdvQ2JnNFc3YUU2S0Z1K3ZBaUV4d2JuRUhab29CbDdGbDUxbVEySW1jQjNTWkc1UEtJMFhkbFpyemFma1JBWGd4N0REMFNwVExGaENGQm9lVHJzNldjdVhxUTBway91Q0RpL3pVaXpJUnc4Z25RbjRLUXFrUXJvSm9vMXlLSVZrTUJmUGwxbzNZUzA3TlY2UFFhZ2FqdzVDemRPMzE2MjlxWERxZmZXU284QjViN3Y0UURiOVp1WUQ4TWlheDBxUDhxblJxcWFkaHlPaG9IZ2RoYTVNWkhWcHlYQWNrejFJL0NCMFlNYmVKUisrbE04anFwd0FHR0xkUkJGL2VXdkUzU0Ywdmk4T3pnZDloYk9WbWFZQVdUR0pYclVxQVByRzI3M3RRYll6MWlGaDlUYzR0UktoRnBrQ0hSZ3g2QXdGa05hcXl4YlF5Y2dNVGFhREMzZ3kxZFRwOFNtU09iTHNVYmxQQU1qbUErWDNqaGhJWmJKN1NiZEF6TUhuc3dCMkhLMktnUDRiU0hsZG0zMGJTRG1hbUNqVFpSV1NFL2ZHWTZZcUh4Z25VZ1RodnlKRFRLS3ZUSzZyWlJFMHBuVUhhRkNWWDlVNmtxSGpWK2tKL3BOcVZyWXlNSllveWFTZ1g4dlk1T3pwQyt3UW5aZGJMK1dseTJ2OGJtOGJadVhXdldWWlRaWWpMeUFsS3VRYnV6bTJqM25yZmYrWCtBQS94dkpZSUZSWDI5QjZpMXQ4WUZBNEtsUHNTcWVKMzFpK1BOVXVuT3NIREFoZjUyeGtic25mR05iYVQzZkZJUnNEdGhDUFM0UTExcWpxVHRiN3FkRXpTMEVFT2YrbDBNRWU2QThUZWxpVTUxMUlIcUFMRWxlbVlQNjFHelY5NDNIL01nWDJOVUJ4eU9jRnI5SVp1SjRkL0I2dUhzVm1nMVJRN2F1K3VJeWNSMlpiQnh1Q2UzWmdITXpqTm8xME5Yb09yVmdLT3JGNTFjUXlkRE9pdmxOaDFwcmY0T3hSbjRCWWpxdjk5VHFLZTZHS1FRMWk1K0I3dTBzMFhudUJUcFZZK1oxOGpBMDlEYUxRMmVwVjZUMEM3a1JvUXV2UmtXNytZZmxWSEh6VGhxWHhxYkcrRWh1UWtQU0pDMEIzcmRwTXpSNVV0Z3JXNHhCQmdwSWNYK2JQR3FHckRzeU1lM3NIQ3Z5UCszZ2Zvc2hhSWIwUzZpMWQ3MnQ5VTZPUXcrR2RVNm9zN0VsZnlYTU84eWJMemY3WjNGN1VlczJWanNOUXFhR3RJUkRYQ0pCQTg1cWJPTXVVblI3WDF2am9KZDRKVzU3SFNweEtMcDQ0SFRBY0Rmc0VLODluZFhPZ2s5dERTU1hEaG9senRlNTRSSkV0N2ExSDY3YVhOc3IvblBtT3c0SGpJNC95VDlnVVQ2M1lRZTNKbHpTT0F0VHN4NU9JUUgvQVN1MDlrQzBTL1g2RDRaMVBHYkllaU11YjlSWE9uVE9aMGZOQjgwalM4YUVsamFxNllhZ1dlQ3lLUTF4SktxQkFRM2c1Ynl0bG9uMFNtclNNK2FpS291Ykd3clBSalkvZmpwaVhOT1doZDZSdlZlREQ5cHRUblNTZHRHdG43WVpEOVlHajNUY1VJc1UyV1V3TU9xR2w0TDd5bjM3TVRjZlJQdnprTXdncEQ2a2RlbVZMWnl2WmN6dVhyMTRXbG9oVzBaM1VUaS9pUXFlR0I4NHdUNVBicW1KMHVzdC9EWStDaTk5cENGaURzNXJYa3A1eFFxdTBGWXJpaW5xU2NzeEp6NnQ2bWNYbkRmYjR5YmNvVEx4aEREYjZxYVBmRnpLY1FhMHk0dWw0aWJtUjRaYjNvbm9yUVpMcis0d3dLN0RBS3BIZDRpc1F6blpNQ2tUemFHZGxsQzdLUFpWd2wyVERnbVpLaWRWQ09QeE9HYVJjcWFwTHBYTjJCV1ZPT1J2amE5OG00MW9xR0xETVNGeU5uOERCWDVEQ0ZFWVkyOGJXWnRaMDU2UEdWN245cUdHbWRLTFI1b0NseFFIL0F5UUw0clB5eUxUM3U0Z0hraUFpYU0wTU1YNjhjaXZ0NlJpeFlHNnF1U2xFY2VINUVPc0o2UE4rSHZDUkdpQURTcTFVTTM5L0FvWmxEZ3N3N0NXeldUeXZQTE9sMkhyUEE5TWlOTHEyckdkSGlGenU2eXN2N0M1a1I5RThrenJaUm9Lb3JlNlQ3NzJ1Smp6MXQ3REhMdmxKTDFqK1B0YXdZZnRQaURQUE5xV1BuUDRBbEQ3VC9HbnRsOTBMYThFNFNnVWRKWWowdWxqLzFaTjV5bHcva1BnbUNIOG5VQmR6WFJ2WFpTSlNEQktGMkFRVWtSYlVOVU0xdzd5OUo0ZExlK0FjOEVUWmlwKzJ4dW9iT2VPb0NYL3gwUGJhSmo5UU41cy9zN2RQeVlnc0djWFIyNHBLVm0wTmRnNmg2U2w2RzUxQ2IyZ2ErOEY5ZjVIV0FHZ0M4aU9BQ2M4dXNoaWZEVFY0RXZOSi80ZW5CN0lVVWpjMlQ0WitteVpnZXBKanpVNWpHM2NFMUY5eVE1Wlp3NkMwanBJM2Vidk9mY0lxaTFXWE9PREl1RVU3dHJMZU84SDVMcGxwK3EvWWFXUy9QUUNWSkd1QmhFY1FjaFFUUXFsUTJuUkJVQ01TM0N6MEVkWlBPYnVTNG4zV1J3QkZ1alVCdDNsTG1MTXNPZjVadHNVdUEya3o1cjVGK2hwbitPQ2hwMHAxZnUzS2FrbStFVFEyOEordXBhYVp5Q2ZzdkFRS1ZtczhXNEdndmplNUxsd3M3WHcvYXh5WjZoMHRMdHorTUo2U1Fmd0NIc2EvTWNvc1dmeHlHVjZNYXVoYXdINUFGeTVURStGdmE0WHB6aUtSa2pHU0N4UXNwNVpRblpHem5XdG9oUGx0UXNjL0MxcFAxM2tjYko5QjRJU3E2UDFZTHZpWXphd09IQ3ExSll6VmM3blhGdFhNUllmS1NuY0dWeGZLdGNHMmR5VzJLT2pYaXhWZ0lWTkVPY1d6TWViSWwzUFBmcEVmRWVlZUQycmlva2x5T3d4UEpOZUswbzd5SEJLWDQwWExVTDZESmZrNUtMRHh1Y0VmclBTUTVuYXA5d2xtL05CWHVyak9FbkhYZFFkU28xSnBGY2gzZkhORXFKc3RtTHpCOExVY2dYZTJQSjNVOXZpUDVLc3ZyaTdDVXJQL0owL29mdVJpWDNzOXA5UytyRXdYRjBKMmJIOTBkYXBPLzkyTGovWXRLVkFrZi90SFg3V1AwSkZUakdFUWhQRUNkeitIMkZrTGpncjErUDhaVWNxcDBDY25QdmloNmRndU1lZDB1S3Q4Y2xkZDl6eTQzdkJ0bWF6WHhOZmltYmZSbmRIK3lPWi96SEdxUFo2eTRhL2FJNlE3dUFQd2lQYWlrcVZrKzlrZStJRHNUSE5jQjZ2ZnFVeGlCRHpTYVEwdVV5a2FPejJZb1g4NmM1RnFwTlJxYmtUZkQ3WlJiT082NCtGYXVVbEhzWjYzWE5hQXRuRFoyTzZwNUd1bHFBM2Z2QnM1MCtoN0R0WFRFc09Hc00zNTdaTlhiM0dLT3pHTjlMYm5icURMKzVrZlBscm9hUlYyc3RIL0tJcktVTlE4R2Q3WStwUmM0ZXhnUkZjZDZXN2Z6ajJpbXdsdWNmSUlRZTAycWVTaXBiVTdxMHdKWkZPUmtaWEpGTzZrSC9ZN3VXcGhQcG5aUGxSaTJ5ZXlkNjdjS0RNYTRpcnE0S2Z6SEtLNEgvWVVzZU5oWEFLbi9uOTc2eWdyME1aS1V0ZEVtSzBqL2N3THRvdnZwa2xGcmw5eCsxNFFsdk5TZks3b25JN1VQSmVKZFVVbVE3ZzVkbjFEcjRqWktZM3Q2bFZHaWw4Z3R5VTJWL2IrcGFYMDZYT0tKVkQxWTZFREVESnFTbUpIcFNOeUNzMGIwRGtwVUJwbFpsME5sRUszYXVEZHdrVHZZVG5lRHRlcnpWY243RVU5Tjh4NEpieDVLR01KNld5N1dmSHRvdWVtWEE4c0xlQ0VIdWg2NEpmU3dRVWppT3g1SStVM1NmeFdadXNUZHNJdWcvM2VqNk9BdGlXcmtib0wzRitXam84WHNjL005cXVrQkpxWnZLZndTbDUzbTc1YzY3aXlHTS9nd0x3T0gvaTFiVzFWNS9nVWNLZGJzeGZpZm50bjNyYi9Hb3NPejF6bGdXMTRQeTFrRVpQOWZiM2luL09sZTMwbEoxWFFzMnlCVmFIK1QyZUhlL1ZNbXNaT2ZYb3luRGxGVzhmTnoxandiMmFiV2U5eGI5SWFsTG9scXhPWmdPbFNCVGFVNzZhQ1pycjY5NFIvdlpwUkd6Z0diLzFBNHlRc1Q0WnNpbVpkM0Q5UzZVYnYvakN0SHZsallPYTVrZ0RxeXhqeE8vaWtuTzhpSWIxeWN2VXRkYlZ3MTBvZjZVdkVGbkNYM2VjMjZmZ1RFRitVNTU0TWgvZHczM1VOcXRwcDNOVzFjNE4yMVBDa2xHL0VSODJCRkFCeVU2TU5TakRROGtKcUF5RjZ5SVVjMFlPenJiVVdhWm84bXF3R3V5UHRoeUVTbTBuSmpwNkZTd002TTNNR2ZidXlOWW15OGxsc2Z4VVZoRng0NW1SZUlRT3hkUUFyTlBoM3hqNTV2QkppMjQyRDhwUkNmRno0YjZtN0lVYXI2aU4rSGtXSnJZaTB4TDlpaU50Yk1kV25Ra3dYZnRCKzFXNXpzWDNqVWh0V1JuaTRGMWRZVzZwMk1UREg0eVhkV1hQK1dMaDNJVmJYdXZGL3RsM1ovTzZLS0xXZDhIMTd2Wk51WWhnSnc4RWZLOXhCeU80K2Y2WCthcGllS0lFby95c2xyR3dvMUx4QW4ra1VIOEJ0Ly9QeFBLTStZZWxSbVdqMVdqdVFSL0N6eUQwdGlzalFFblpzdGd5WnpaRkxwTVZySndPNUhwOUltZm5DVzdIeUsxM1hzaVh5M3UxUkE3c25yRHdpRkQxb3lSVDVOeitQRUJNaEs3N3ZyVUJJNk9VUFJzOHdQMlpUSXVNRlowRjU4Qm5ob014U1BQU0FrS0V2ZXhIVWtuWU4rS3JVMDVhVndCYjJMdlNYUVpXclhxa1JrODdlYkNlcXNRazZWcWtCNmVPbkpiczBTbkdNbmZHZlBBV3VaYUNFZEx1ZWs0a3h5VmpubFRadmV4NXF6d3B5TDAzVi9qa1JCdmJIcGxMM3E4UUVTM2hhTEdrZHpQNUpIbnRkTVB1R2VHb2VQWTQwNHFMUW1JUGxzNDJzUkVJOW1ZY0JKK2Q3aVVudFZwQ3REdGtQaUVzakhKRGZYUzU1eTQ4VUhnU2s4NHFiaGdkbG13M1ZSY25IMHNSQlZuK3J2Z2Z6eDIraUEraE5OeEpya3V0T2FaQmM5c2taS2xzWVJtRTBpTmEySkJPelJaR3NpT2JBYmFTN2lQOVZhSDhMZWwvcFRMT2VsV3BpVWlMRnZjTGlwUGo5cE1PZlVKQUR2eTQ3ZVY1UVJnSTlndjRpbHNxT0lyaXhGcEdJeWRnYmNBYVNSM3JNSTNLcEdsU24wT25ud0dWaWs3ZnZqQTNrOHFpNW11eXFGakQ0N0R6cTRIcWVKQ2J4RFV4b1o1ZURaL0R5bDhEcklrUDhPdFY5c2dhT1ZTQWdtYmdmd01IeHRsVUJvV1VBUVBUUXFDV3czUi9GZzlWN1UxUDIrM25CL1YwQTNWOGFnVU9WdXkzYnBsWmxmcmZWMldWVFhta2NDd01ZbExTeUtVVmRxbFpLcTRTQ1U5VUVwaWdaWTNSR0hnWkJWdGI0QUsyeXNxTU4xb3FWaWNJNEtsZkR2d0FyMXJDTGxXeHBYa1M1dU9oT2FWZ0xMdVhPZkgzdDhoSDZaUXFOeUVaOS85NE1kdkdhV2o4V1ZKY3ZQTmYrOUxoTHlGQ3psYU92YmZVVytOSUZzK0NJQjVxOWgzTkN6Z3NwcFphU0trMVdhNnNSVk9pM2hsZlNsUHpsMW16SGpHb0FCWEg1V2gvTkhaUVJDS1VVM2poR0pGSCtMS3k2a3VDVU55OVA2MndhcXB5Y05FOFhoZVJ2UWxiemQ4TGhmNEp5Q3A3RzZyTm5rT3drdi9YVWFqUW9qM0pDUk9odlFWRUdNUGhoSTE1aVZoNVNXajlSSGFFeSs0Mi9lZEo5UnM3cWd1UEJuMTh4V21abmw1M2N6SytnaGpJQ0VyM040UW8wem5aRmsxU29MMjI4ZENXcWFUbS84M1kyVTc1TTA5cWYvRWJmSS9VYmYvbDJlL1o0Z0lzUWNiMC9UbW0rSnFJWlpENWVybE5qMGFFVjJDTi9RbkJiN2xMMjZ3TjRXYmlnM0Y0STVuLzlVNUczOUl2aWpBNlErcENrVjhmejZWdXZCVzlieDhJazhxbERIcFJ3dEl2R1gzbThEQkIzZ3NQT3U3ZS9jRHFjWkRadm5uNTYvZHd4TlVRcEVKZ1NIV0hoWnVGbjZ3WWVMMVc5T041MWtzTzkzczJsMU1ETm1hcVcxRGVOYTZvZGxrZ0NCMS95cXozNWo3a0VzdlY3VUlncDRZNzNvUitMMW1aUC9ZWlY1dGZQMmQyVDVLcEdwOEY5dTZaNWZSSmJsOERLTWFCR2c2WHA3ZkJqMlJoTnFmUGJ6NEJQWC9xNHpJOGRCdk80SkhodTdRaDlkM0pXRjUyS0FocitGeWZoMExLM2laMWs4am1GRGQ4cVNKRzFGd1FlbEhPOU5JbDVkK3g1Nzl1NWREWVpQVEExc3VQdlh0WlZDblZTd0R2WlJMWDAzeWFqbnB3UGsrSzJpQXpSYmRQemdYdkVkYllXRXVDQ3h3Uko3V3R0VU9KdnArRW93WmdoV2JMc2hLU0luTEhTOUNOQWpiaEVxYXY5ME5Ia29hSzlmLzVKOVJrWldMTW9lU28vVFJlTEZzcUhjQWl2dmhQZVVpdXFsN2xvWHljN1FyUm11Mm5uK3pXRzRZUGFZUG5rbUxydFJzdTltMkZJdVVnaG9Ob2plUUdCZTZJMEFOaWxaVTN0Lzc1akxOZEZHZE5pbWZidjhYSjlIL25wejh6dG1TbU85dmlYZ2haVGYzcXgrbWNkSElvTDV0bEdVaXRhdFpxaHdYTVQ1VSs5NVdxampiYzZWdCtPcitPbUdpZkJRTEROOFBKWmswK0pQd3hEY1VwYkJGQnp3S0RxdVVSaWpOUzJTRUxHeHFZWUZHeHBpSWhkdmlydzE5SkhDb2NGUkdxOVVyMmpSdDVXRjVSSHZiMVY5eXgwOWFDQnNEVTkwUTZKWTJVU0F5Tk1lRjMzem5JOFZPU2VDdmZ6cFRhSjJ2NmFmd2sxcUVDcHg0QTFSRDh4T3poVEUvMTNJTW54dElaYTFPS09oZ3lieElMQVdOaUdJQ0Q4U05XMmQxVTRocHZhaFZIYjlkR1gxakRseHlxMnBSOGgrZTcwQko1djlCZklKUEhIRjRuWHhGVDlIOXFwS2VScnJaN0p5Nk55NDl5VG8yUHZqbEFCbi9IdTBXWm5YeXBSZUk5SVFrRXJoa1Z3aU5Ybi9hc1RBQ1NXOXVpdEs4cU93R3JZV3lSSkttUjAxSFNUdmZvK2RROHFXc0V5S3BrNkZnWURwTENrRmdNRTFkUHdpZ1RENDZEcXdVQ0ZtZjY1THBnQzdvaUdERGl1TC8zbFFhNGl5SzBnZE9yUzlKODRFeWg5dEtKMWZ1K1RpaUlaRnRwdHBYT2twek8rTWpOY3NscmpUYWZ0a2xMaERPWTFMSW1PaVdiM1NRdFgwbmUwMjcyb2E3SGJJRWhTaHN3VXdBaFdiZmpZMU1TZ3I5YXFxdzlKQTNpaCt6c3gzV3hRYk50KzFwZjNhSEttQUVVMTF3WTQwb05iTnJBOTluNWsvM1dCdWZUZEV1QklWR2NKeEYxTUhtaDhYditaTm5YMDh1bGdXa1pOVXZPK3hhL0xOb1hhek1hSWJtbVM3c2I3aGY4anJHVGlrc3VxZmV0RDVTMHZoL2t4ckJFVGV2WmowaGI3aEpBR0FTTjFiSXN5bGs5QkZWU0ZDcjFRalFGb0tMR3JhWnRzbUVLZFRqTXVFMjNCY3lBYVNDVk1oeW1YS3RZSzVTdllmY1poL25aS2JWc2JwU3V1cnJIbXZ6OGZnMlhreXVwclE5OG4velFWVTRxZzhxSWsycFpYVEZqWnUyVXZKcENaZEI2QmlQQ2hxbVVZWmxwbTJHcmFNK2Y4VlVTSnZkQ2JhUnREZmUzWUszT3QrbmI2UUVJVHhIcTFXeU9XdThwcFpxWTlnUkRWNGlBUFgveGlWUm01Q0VBNDRHL1dISnhCdjIycVBUNU5mTGNtOWpzeHRmM1R1ekl6RTlMRzFnL3Qyc1NIY0UzK1E0cEZPb0tBc1RtdlAyNjRZMzBhMVdlM1Zzb1RkNGVRakc1T3NMMDJRNmJLK0pjSzJWdmw1Y0xOT2ZHZGh4UzBaT09BUm9BekNvNG9TbHVxNkE2Q1Y2WlorUHRLM205V2ZRZ2l4ZU5hV0gxOUVORlEvSWV6R3Z3M01WemsrS1lrWEsvK2M3S2EwUXNQQndDbjljYkZCazZ2bmVYNFhrOUNJV0hZMlRXK20wc0toWWNCRTY5bTdPWWZRR2dvR0FzOCtkS0lHeVZlaVZVOFErazgxbUNQZnkyeDRsclRiN1A1ekh4MDBXSkpnWnc5Y2ZmTXo4L0VPSzlPNkIya3FINkgzaUp1VFc3R2tPSHc4dUtDLzkwT0lEZUVyYkhqYlpzYmcxUVN5MHZCM3J6T1RwZEgydlRqZDJIMG5YeXk5MUdLQmxqOHBoNmhLaGFvOHNBMUNIcUNtSURMZEJzcDQ5NnJEUFpJN1N5Q0szSnZ0NnJxWkFlYXJtbkpydWtBVklYTGNkZ3lNdGJlL0NMV2RZb215K0k4Rm1DZmZjVG9Qck5PL0xMT1hXZkplL2FaVnRNRTIzNkIrQTd6SzBKQlpxU3pacjBQT0NTaWJhQ09Ka1lTQzRvS1FsTTd6NS9Vc2pHL0RDMndIWUdWeXJCV1Z6RkxPNWpSc29raE1ITUFCNElyOExjUVc2c0FjY1YvcVRFWVRqUW01Z2ViemVneDdQTVArWWYxQnpicnFlVVMxK0VVd3F1bmNUbFcwTitmRjZtZmU0OU0rbkx3Znd3anUva2pQZnpJU3gxS0xpemUxZVZyVG1ETjhYbTE0UmxKcmc2Zld3ZXFmQUNMdlBwem1HMUZFSnl5SWV4Z2xQbHdYMG1DOHVyYXZZQldXK05EL2o5a2c5RmFFWXcrdHdSQ3VEVm80WEptTDNTeWRkTDhUTTZyKytUUnpSYnQ4c29tRnZqeGlpeTdWczFiUGl6L0djbUNvbVRBU3BISWJLQzlUS3Rob3ExYUZvd3FtWjZGN2dlcVk4MEsrL09uT0FZUXhNQTdhVy8xMlAwM1FjcHYxTnBQVnFZaEdVOHZVakd2SkpMdlNteC9mOWFmVkdwaDk1RHFxbC9yb3ZVZXpiTXJETmhaTllnOFhmV3lBSkV3T3BhWFhVRlU2QmxyOFlZcXVzL3RoQ0trVmpMSnpaUk1ZVWNCTThqb01veE51L0djdnE4UkFKY0FzN3gwRzgvcXFkaFF4cFZoRDBiOWRQZElibndtRy9zdFYrdkVhRlVrdlJ0QjNNL1hRSm9XdVBYaVdJSDJ4eG81VUpRQUVRNTl4d2Y0T0QvTnMxNDZmcUN3enE1dk00WEdXU0l5bkJqbVhpc3J4NW9sYkVhemdKRGlvQjRiK2FxVld5Zk1UWkhMWUhsUDNqZUplUGRtUGFQOVJuK3BlcWpnN1E5MDR3Qm9rSk9wSzFJU2F0eUhmS2RIcVRQYnFQM3NZVVpIdnFnbG5tZkNJS1ZoZXBBNC9ySVJNYnJIVDVFVHc4aUEyay9NWEs5ZUVTemRadEdUbmVQTDA2WndadHJQT1gxdXp3amZsWlpHdGszS29DU2gyNHNwS0VwRlVnV0pqbUJ5bHQzcnNRbjFCUWNqRkdyV09RR0prdmh2RVN1SG1XYlpNWVBXV1k0Q0lhQUI5d1FIeXE2SjgvZlh0OU01TEc2TERpOHJhRjFiczFzUWY1M1Nwc0hldkE3MjkvSjZnUC9qa2wwL1M2L0ZiSUtFS0V1Mlo4Z3cwUkhnRHRUc1paSmRSeTV1K1huajE1NVJjbWxFY0hUYTJSVFc3cDFOYnZ3RmR4TE5TUUZSTER4MDdPQlk0Q0RxWmY5RnArK3lzNVdjZFp6S09hSW9BZ1RsV2VwVW5GbmY5dFpydFZpR2IvZnYyR01CZHhZWFE0LzB5ZCswYmdHakE1cURrOWxIOHJMMVVTZERFK3lDTlc3WkJJZE03L09KdDBUVzI0eTRkQW1mUG4vRlpzUE1JYUNsb1Z6QXIyVXkxRzdxTVFrVjVaRGN6UkU3UDVSdDAwcStTT3JtS1R6NXZqOUdsQy9pdE1qYXpVSFZVQjJOR3VDdkFZV3A1bkJwYm5DaEpNN1UzVitCemgvSVhzUXpwOGdnaUhFU3AzS0M5Y1U1RHVGTVZyT3ZkL0N3S1o4ZzVwd1hpb2RzUkxCYkRGSHVuK25QbGtnb0s2eGxwYjM3YlhsaGNqbkh4dTArbTREZDRpb05qazYrYWhnQXdwY3UrckdFdEtQNkplNDVsRE10VHE4L29XM3JrMFhsdEhtclZPQStzemd3c2l5b2Uwc0twWmJmS2ZrNjdmZVZyZHFpMi9sWWxFeG1qZGtIU2lEZGZRQlJNQUtXa005ekhFN1JHbVFyVTNINC9ldkFObGV5UVBGUzJSQlhYYVdoVDFPcFBIVXEyMlFLQzF1aDRlNUpiUVFJN1A2eHZxb1dIQXd1SE5Yd2t6Q1ROWUhCV01EN3RnRlZtSldTM2xmMnlNU3B2dTlmS0I4S0ZnWGxWNHgwbUJPdW02YVpjMFdreG1iMFRVTEx0SEV5STllYS91VGlQVTZtakthd0NmWEUzTDN0dTV6UTh5MVRKUXFVM25xa3BTYWxGb3dPQVRMS1pKdG1USUhZemZkTTh2ZXFyL1N5dGJOYUd3bHZ6a2RiRXJJNmY5Y3MwR2xEMFppdFZiTW8xaDkvWjRWWjI4MFlMYjBJK1RGZ3FRZTV6TUE4SDJ2UUsxcnZMVlZBdEVwVklZOW1jUmI5Ynl1QWhTTFVZakJBMUVXU3owcllMSlh4T0pBUXlvd3NWeEFHY1dqVUdUM3krLzNKdFEwMWlUNEpYNUtnV2lVS2wyMDkrWEhCN2FpRkJhVjB3OEsyUHVaZ3FNSmU4Rm5pOS9LbU9ubGpKMUxJUVlJbi9ybW0xTXd5RUFTd3JBdzBSOFMyZElvMmNITXdxUnFZNnljTkpNbTZjR3pPREYvUjhwbCtHOTVZck5VYlBDS0tROHA3SDNYdkN3Vkt1MjhxK0lNOHdTejB1VmQ0c1BZbWh2c0ZyQUJMa0pvSzYrS2o0QW5DQ0RpYmZMTHFleGFVVVBBVmZXMHVYTU1zY2s1RDhUQndSeXRtb3J0eks1S2l5TjEvSlU5VThEakIzMWFuVysxZC9xczE0YzRuQ2xLaDhqSGl4am5wVTF1NVhDbDVVUW9SbUN3ZVA0YWxIK2xKSzY3K3BiZTJERlhJVFZvblJMaGVmd2ZIY1ZId0h3NEgwYlVhdUxqTFJhRG5HMTREc2pqVGY1dEhZL3FLTVY0NVhSVGVWWXlxY2N6NjBSSkNRTmZmMWc2WUNHVWhWZTRLRHB0TVAvdS9ZOUR6VGRyc2o0YlpIMUFwcWRFcHExeUQ5YlE4c1hzOXV6RVgvSURtc3pFL1ZNVnpBbjExQlppV2tLU0VXOG5SN0FmbGlTTlkrdElLd3M5djkvelVrNzl2Q044Zm5uRE5TSm0waG93UXh4SitMSUJTOFZEVGNpbTdseVp1RDIyVGV5NUFQdXJ6YmlZeUJiTXJjM0dCaXBvZ2FFaExiVWRWVEd4a3hWdTIvT1QxNkxZTnpYKzNIZDQ3REdWcnAwaGdiU0RUTmI2Y0xkUGlxY0lUUmh2YTlWL1VVL2hGQ2t5Y2txQnBsTVpxWUY1UVNXR3FCQkRWSEVRRFZjUlZ0VS9RQUJneVdUTXhKb0J4SWNjS2RiN21TYThKV0tvNUlHRUh5cmhic01RVW5MSmhKekxyMXUzM1ZkSlRHZUE2YTdtVE1ZeGdka1N1dDhER0I0MlljVDVwa1p1UWxrOWpJUTJlSVpaRW5ZS2dJYUdidkpyalV2a05Kb1k2M05heXVJYmhZUzllWEMyaWF6R01IU3V1bUhldW9sakUzRGdnSkNDWWRXWGFmWXVBZm4vUTNxQzRXbGNVbnRzWmxpSXFFYnNQU0JtL3dPUGttTm95Y3hQYnRZWXduazhiSVVKTFlqVDFabXhZb3o0cUk0d1F0Tk9nd2lXL2VhWlhWSG1KUnV0R0RwWFVNY2lZdlFIVWVab1M2bTIwWVNoOC9WMTExQTZsZmtCcHNIcmFsbDlEZTc1RmpCWCs1NDlTZ0NSTENibVdGTDkvVFFDYlkwMDE1bHRvWnZsd0dtdkc4Q0Q4a2t3UGk1b2FLSnJCUktMeTluaXhiSE1rUUwwQXMydmRBY3phUFdIU2l5eS9LT0Jpd091Mk80OExNWllUN0ozT0dmMC9oZnRQeVlaaml3YzZ6aTJjTVNnM2VJblBpdXlQZUFQdGVubHpvcjJsUjh0RG5wdVkrL0F2Z1lTUEgxNkJxMnlIWktiUCtyNEk3cy8wVGZrYXJtclpLdi84S21ROHY5ZTFWWTF0NVVTNmVJL3pPUUh3L2JPYURQVlovVHlZeGtxK3h0Smp6YjZZREZsS2FhZ3dyVXE4NWdDTTNzemw4Z2dkRXpMSnE0a0ZPYmtZS1Q1RlEzY1RHMzRNQUd2Z09YbThSLys4dlRDdmdtdXdOajMvbnQ2S1ZkUGZzY3NleGRIRHNoWXVUeHNRZUJMSkVuMTRuenhKRWFmRnFJT3JvendrR3NybUxvS3ZqQlB5QmsvazVMOFJpRnBkQWxsTm9FVGJLNWFVM0xuVFA2TW00YTl2eFpidXhFWTl3UFp0N3lJcnVpei9oSk1wdVE1RmZuend1VjhwMjdzdGFRd25QVFp0OHFjSnQrelJYQVJEQ0xIVGlKQS9kaE5CVjJSdnd1TW8wT0lCNk9CNGVLWENLZFAyNDlTN00xNE5pZ1d0ME9uVGlra0tRNi9DeDVBZncrL0laU0tXVWZlbnMrd3RDQ1ltUjhWdXpwZTA2Sm8wNFdvVnlzMExmR3hxNlA0Wmd5ZGN6WE5FekZWZUx1bUJRYXJub0NnUUlENUwzbjdtaFBwMFdFdDRESHR5dkpuNjRFd3BveWhoblY0QW81ek1aOVorbWVOWnRWeUlQK2kzK3pTVXFaMEdHR3UxRTFzc1FsMEI3b0pFbmt1NzZCUElrYm95eTFub1poUUNNRkZQNEJPRDExb1Z3eVc4SGkreU5sSHdFcVBuZk9QMGRsTk9aa1lPbGRXd3lKaVhnRkNFSXJ6VTJMb3RjTDM0RXJpNHkyeVJMdXpzc2Yvd2h4TUx3Y2lsczlJV0ZoU1ppWDNJSzg4YkhrOExycTc5VCtPV0JWTEFoWHdvWm9uclorRnRhb3dnZ1MwWC8xTi9VK1FYUDhPUTVFaTFCWnhSVEp2WWJvZmFtd054VVBrZ3hCNjdweDZENGNDc3ZDNVlHQlRWcEpxU1pKSS8xMURBTWFIRUhMNXNuU1dTMldCdmE4djIvVm1nUGJ2OThoR3N3QVNpNHNQL2d3dG9KNXFGNXRWQlpUam1wS1kxVDM0dzFvcGd3S0NBSVhCakhIZ0FVNlJRWkVCVkxIWUxyV2c3OHVjTTBKZ2tDNzJFSUkwSEE3Z3R3TnRYMFVkQlhKa09jRFJxSy9hZ0hZK2dNUFJRS0dIaEE0TThxcFNkcjNnRGQyK3pXN2pjTGVjR3BLay9pUVY5bjYzWmljd1NKZVFpU0VPVDNqRzhsMnZkNFNjeHFwbWxPRzdxVXpsaUdFbTFVb1BvOXpDbzZ0alZMT2dwSyt6ajEyVi96SEVsOG5lbG1pMnlKZ0pwaXFSeENQNzc2K1NEMVduQ0JrYUpOdXU2cFA4aVF2d242S0IwV29YQVo0QUl4UVNHTzh6N3N1elptdkRzbkYwckVZZW5MNDBsUy84a2UrUk5tRUhUT3cvb3JDZnJuZzFzUjBBRkJBRTlCTHgvZXp3c0dCMkNzZk85ODk2NDFkLzBSZkxBc2I0M3VFUmdSeXJQZUNiSUsrdCs5ZjlPUEVrTkVQb1JZSEo1K2IrSHpiK0lwSGRrY2NlYjZiOTRVNlYvYUsvSzFQOHBIajRDVCt6U2VmblMvSGg0RGd1SHRmRmFXcmljb0JhRHRkdjMrQmdUdVUrTjBNY1lKcnhpdUZFK2pEZXAzdXRVQ0MvUTRJTXZzZVZ2WTY5Vjd5eVpVazdzVHg5MTZPOXdPd3NYZENTNUJQSWY4K3grTHR4WWNMU0J5US9ITFE3azZkTm02S05TV0lmaE9FWTRxMmg5RU1zdmR4L3ZxdDZEc1hDSmNxaXFYWmxvUlpiZ2VNS2YxWXNLR2ZlU2cvdDA0RnZUWnNXcHM4UjlRVEZBcjd5MHVVUUN1YUhVYklvK0FYRkx3bDVhTW9Nd3F4aHMyRU1CZ041SUt6Qk1uNDVFTnFlS1lOQlFkaWJOUVI1ZUY5RUgyaHAyZjhYNVE4eWJrN2RpckFGQW14cmswaUpOSkFDOHptdFFFVWJodzlUUUJwUktqR3diV0cwTmpldFpzQ0l0d2psUkRRMy83M0d0WFdBclBpdkV1K0NnNE1nQ0QyYkZ1bkVwdXphME16ZzlJUFpuYVZtVDRZS0JPZS9uYnFMMHlTZjdrem5ibzJXOU5xSy9uNVZsYmJUWWVIaG1QQlUwR3ZUZWtZck9kUnBwQlE0c3BrbEo2QnRmKy80aS9GNWh5aVBWVHNmOHcwSUg5NXJuRkozWFNOWVlyZEFsZjRGTkJWdks4eHFYNnlwcFlSTnIyRjVGQXBZd1MyT0w4YmJoTlVtM0liQzVvckM4TS9ycFVLNlVMQW81TU9FeCt5T3lEVFA3SlFuTWxJK2pXVlVNL1h0MjJORFY0UTRFUk1pOGNLQlBiK1R0UE9KWmE1empWb1YwL2xMVVhtTnU1STVvZDZ5aFlwTFMwcEpzL0dOeWVOTXJrbFBaQlNISjdmVVdDTENBb3BJMTBWcnBjV0JOSTNFcGptTTU4b0lxUmUyMXN0OS9uUVI1NkFrbTUxV1QxaEIvZ3dYZTZEcEM1RmtWaDZockpJUW8yNEtjVENvaFNhSXE3Z25jbjROZURzd2FJTnYwQSsrUVR1QUgybzUzRWhkZFpXQmhQenBGMzdyZy8ydkJQc2RDZlQ5SVZ4NlVUM2hqLzltRUhxR0RMZ1N1MmRFMCtWcTYvR0FnUDBGRjA1dS9jbzh6Snh4U1lmNDBYSzJYODFJMytXRWMwYWQ2d2YwNy9vbUxqYmUvalVhMURoVHRNRm5IWlFwcDdHQUlxWS9qd25mKzduQ2dSZFc3dEgvWjgwZExQUkpkZTYveCtDSHBPS2NUNzdBS1lVL2N6U1FEcWxBZWFqcnV6TDlMMkZOZHU3aXZaWjJDV203K1NCSjJvUHZMZXIycGpCQ1dpWlFta3cwcGRCVDkrRXhCV3NqMkxUM3Z2NjFzcU5PeDlLc0dFMWo0M3Z2N3BQajJ6ZDBOVVFYSjZ4OGZZazV0cVBERUZzcmVlbHlZeHJ0b2dtN1o1YkF0c09Rei80aHVaSlZiSUhqWFMrYjVSUnhONDM4MUxQZVZhV3FuUCtYdUJzNmJZVUtRTXQvL2x6TEJ4U0Z0ZzhoNmVZc2FIT1dGRUxTUFEwN0RhTEI4MTdyUGJ4SEo2MktaRi9mYU1Eby9WWFdqVkYrODFLdmJaN2MwR2hEbElFWjdadnNhMVZNampCRnVtaHJmQ294RE85OUpLREVITWVGMzgydU5PSERpTks5SmszQUJVclNvbEphLzRXRXFDcVJ5WlRzR1B0U3gxK2ZzS3BINGV6L0U5bDZVTDZDYys4eTJBUVRSLzlzaFJEUUExQnZEQ2cwRkgzcDJmSHVuWWdTSjhTcWxqUWVHU2Jsc240MTkrc2pnb1l3Sk5wT3dRL3UyQ25OeE1OdFUrSFdZMm9QRDgxbGVmTW1IM2JDODZ5TVRSc0RranUrckQzVEJEeHNBdEFsaktDVS9Ybk5tdWx1NnI0MWdwWXRxajViL21DSE93cGQyMEF4d1JuWk1pTDhxNWliTWRGalhPYzF1a284S1pkTm5rRDZ0ODdZM3Z2bEo1anNrZ3dENDByeVpqV1h6Qkd3QXVoK3NUTDFCaURrT1ZZbis5NHZoQW93TW12MWptVGp1Q3drZjV6OEVkUDJVZktIQzhFWUVyR0FMb0E5SUF6WEtEbGd3dHM2YXAwTC95aWNuUVJyZG1vL2Z4K1Q2cVhZbWVyc0lub1hnMG1vL3dINXNaRzU2Um41QVgrVUJHTXRFU2toaFFUeEkwNGl5dDljT1U4NDhoOU5STUpaVStWQUpoRlJON2xHZTBGN2lUWjUwMkdReFJUTVNtM1duckxhaXNVZ0hxRmtwVHN5RVVON2ZzdGdRMTlWdEdXNHY0MWlUaFNMajlIck5vSndNYUh5YlVxMk55Z1dJNG1CYVhtcHVwVDJabUc1RDFSL1FMV2l4UHVnMUpEamlHSHNEOWxINU5zNTJtY0s2d0NzSlV5TkQyRTBSM2s1RFJzYW55TEEyZ0dGOVptV1krY1Q5NFhzUjhvMGJ4eUplWVZXdHpCSVNvNVBhVWxKamg4a0xkU3R1ZXYrcEFsdXZZQW9uZTh5dzhQQUhWK1JrT1d4enl6WHdCY2c2YXNkSUtuMy9Rd1lzYUhmdDhjZ1kzaXdhbVJtS1kwMUsxMVc5akZxd3JOckZwbElQZzY4ZkxtWlZYa0RBeDcvTXpBSHRMRVIxUFJ4MlRMMU9JdTJkR2FrQ2tSd0k1M3hZeDJUZURBaVlnaEQvdDJ0NHppejJ6d2FDK05JRlRnYUw0U2tpMGh0RHRUMUVVQmY0SktEazc5SkRNQVlERENyNkZWTTJPMlc3dUhRVUp4VzBlMVpXTDFTK1hiajI4cVYxWjZGaW02Y0ZxVng5a2pkYnBnNXpxOUNaQ3plbE05eFhBSjhRVUtmVlYvNHJxRWl2YW5HdUJSNXVhZ0hUMlRsQ1A5OTN2cTdndXYzcVZRZjBPRndkSjVVdy9pcE93UjBjdlV3QVI1WHhmSVZSck4rTzVSaU9WZEE3bUNUbVduOG5nRWpReFRxeFVETFVBQVoyTTd6ZVVTU2xhdjJrcFh3MWF0N3BZanB4VzNpbHRFOXh1N2pBVGUwbHA4b0ZVUGV3VC93bU5KRS9scklEZUR2ajNsNnN5SlozcDVqbnlNZ2lhMUR4REkzMmt3N1AyNGgwdWxFeXpqdFBOcnNwb0JVYjBJYXI1S2J6a0UrMytOTXZPT3A4VVN2UG1ueDB5M1BVRU1JV1NNUmc2UlZNeVFhTWdLcE01WmY1SHVwTndENUVkWUZPQUZXdTFEOFpIQ0JoVUJFd0tWWVNlVDRPeW9HQ0hsK2xvdGhpbDZjKzVrbkJESlU3OHg2ZUVwaXBWNGVKNkdJQTVIb3VWWGpIbDdPVGMwNi9zN3Z6bmR1bnI5cGZOVlNVbURaaFc3dUNJcmJyNUZCbFA2amtRKzNxcGdIVHBJZVVlaFhDdjhxaldYUjNuNjJZTlJRcUNDRjlsWVk4OVBEcndNMUhKYTdKUmJwSWdTOTVGTE1YcHFQY3d3MUdBNXNPa2VpTklGek5Qd0tWcmhEQzFsaEdpc0VZZWhPRzY0ME1ZdzZGVHkwTEVVcEwrZmtzY1FIZzl0ZlNYRUVNdFd1dWZ0cmVpVUQ0VXBkS0pQcFk0d3hIU01xNzlrMDdNZHgyNk04b29TUnN5RnFSY0l6N2dEeThoM3dIQjczQUxZU1VvUGZXaFMwalVrU3M0MEJRcjA2Uk8waUNVWGI3REJlUlZyMmNzbnJpL3Q5cWNaa0hmV2tVejBuWEdZRC9DQ2J5YzZVVVFyWGxGR3RmTTg0TU1SYVJGZk5SMlhWaFk2NFNGY3Y4aEEyWjVURFgrMUplL3VRbGFWanhBb1dSZXhyUDd3V2pQbGM5cTRwenpHU2k3V0dqMythZjFETmdzcWxLVW5Bc1EvOFgzaW1GdnVRUVdia1hRM240ajlYanJiTEpoV0JhRVhyVTFCWEppbEVZRFkwK2NNdkRqU1lwcWNuQVpHeldVWVBIeFVwNFY4UGdhU0dZUXBMRDlRNXM2Y2txeFY0Q2dyWmtuaXhHVWFnU3Vxd1M4UFdudWQxMy9oTEd4cndQazNuOGErQ3k0dEo4aWlSclRua2F2S29YSVd1M2FtVWM4b1ozQmFsVldINWc2Njk1YVcxZHJpTFN4Sm5FT0RKazJBRnUvRkduUG0xM1V4Mjh3STVBNGVqRUdDN3U2UWp0TkkxWnlJZmd4a1VRa0M4cVRjaVlpQU03N2s4dmFZeDBobVFwODBMLzc5R0lzL2g1NjlycndtZENESHN4L2pmZTVGOXJFN2tMYnZRZ3NZdVIzNUQ4ZGxRUDRnRFlCTlVtSzN3OFlWY2JadEYvbjRDWjllQmdXKzloVDk0MDNiS1BydEZVK01tYzd6alJUU25Pa0R0WkxDbDZ2VWU0WUcwdCtldkJVY21KY2RtUnhrNkc2MmlRVHgrQWVHcDlSSGxPVHR4aElyTUZtd2M0SDBBSVc1Q004NjV1RytlRms3QzM2NERSZUpla3M0d0FsT3VFSEExNm1nQ3N2SmV4eE5GVW45RnJBcmRjcGRBbnRlQmVCRHlua3BZZ21kOTRhVDhrQlFISkFlV1ZRTnpnR3AzQ1RTSDFoeFhRMGdXU29TMFdGandEeUFFQ1hDZXl5aVF0blRDTTNTdFBZV0laQmN3SXlRRzZkTEc3bG9aY1oyOWdFaG9ESmV6SUxXVDN5OFlUSXYvSWtDa2dYNmxackhva0Jaeitqd0JvYlRxVUVGZzd6OHR5N2tlN0h0R2dqWTFTVm9PY1NxazlJanRMQ0ozYzBoc0g0OW1QcWxLMmNWa3RzdVNZeTRGZW12V3hnY2crbVpoTW90djNnSHNyTXZqTmpsMjJGbHFoNDZkMmRHWnc3dE9vMTdQNFhWMHNqTzFtVksyZlllRDVZZ3hSYjdjQVNDU3RlVGxkSzRwbkozT1hOSEtTUVA1a0Z3dFVjT2o1bmhCK3RKbU93c09OM0hweThrMGQ1a3N3Ym9OS0NvK3llSzF2K2JWdGtScnFjeU5hRit4Q25ic0FJTGdTQjNYcVFiSnN3eUYxS0ZqWmJRdGRYSzRCQkRCc1poNmlZN2g1TFF0WldYb0tyZ1UyRDI4ZXVkMklBZWhwdFNRdEZTV2FMMjRrTnZDa3k2UmwzQ2hiUW5XTWhUUTZOdmpzSjIxdEZtUXJtU1FhdU00YVN0YTA1bnNjQk1TQjNQajN3SEFlUkFJZ3VPdDlQUzJlNXlPeXQ0dnFnS0k1SVp3dGpCcldUTS9QYjZHbFpNZ0ZzbWJwUTgzbVM2VmtNT3FpWCtCYjBqVFdNNGJ0N1hDYy9MN204WndiZzhOb0NJTzFjUDVUakVFdzdCR21TMnpubkgwTzFRdWlFMXFvS1dVYXF3NzhTd21VbTZXR0Z5cDBRZFpkMit4dmk0d3RrT0NodUtKZ2RhTEI4M3h5YnFaZFdOOTFValJPU3FkU2w1YUZRSVhwSVdoVjViVkNvMnYzajRleFlYd3VSbHFBbkxBSGxOaDBrbktFU1lMdjFPVFZKeENheUN4SVpmSzBlODRrOXhuRGdqSkJzK1ZiaDFRcVJIdEpPMEJGWFZJSHM5M2lDRUlCMnQ4c21VVzJnWlpWNUVlYzRhQS9uSkIremQxb0h0MGlnWmNFdFgwOUg3aXJWOVF3b0VNb01KZkdpZTQ1QnBkT1pueDAvY2ZIT0F5Ym81MjZ0c3BuQnNBSlNCK1pDK2IxNDdvejc5OG5KMk1DVEt1cE1kR3dNUGxNR0puWDQ1UytKN3BCc3RMaEt5b2xkYzNiOWYrVVFKRDBsUi9uWkh3TXo3S2lJbUN2RHhaSnlyZkc4RFQ0VWZnRjhZRk5nN2hnSHFwUTdyQlpodHJNcXBaWEZPMWJ5MHYwUHA0OHlFQVB1UkR3T3VibndaWWVMVysxU1l1bnRiZVRHQ0t2V2hYRDNJUUhITlcra3NQTlg3b0VMRDVjYURWbWo3NGxJU2xCc3ZvcGhScFAxT0R3K001RnhnLy93amtJSms4M1U4L3F2ZXc4WGljaHRrdk5ZR2pXSE03RFYvM3RoOWdlYnI1OVlOQU5aZUhhT2VKTmpsV09zN2tHS0NuUmExTW5pVUtSRWErM2JEVkNpUnBlRnhXZmgxUWQ4dnM1M29HUSsyUXJEMzJReTFRdjdOajliSUJ0cTVwOG9ndGw0ZW50bTlNSGxwalhmZkdUVjgyVEI2RTZ2akhoOE1Ma2MvT3NmY3QzeFo5UFZldXVCNjk3YnZ0QmhBQU5Sb2RrZHZDOTVLQU1rM0pRdXZvaFNKUXdaYi9hcjI4cjB4M29vaENUVTM2K1AyQ1grVnNCVmgwWWJSMW9hUVNsSGp4VzVBRGpYN2FyTURXRGQvTzhBUXVOYlBtc1F3dlBENWN4Lzl6a0l3SGZXK3VsZFhVTUxHTjdWUThMOWMyUXA3dVdzSDJKMDVvWFIzdzlmd2Foa1BOWUt5M2JiaFZWd3ZVSVVkM0FrcW1RcXJSMldHS3N0SVk1andtamxGa0tYN2RaTTVZYjlvS3BTak5EcExEckV5QnRwcDAwSUsvR2pER01oMmI4SmdiZE5yS1A5OFZoaE1QZjhmbmJFRHFKVktaK29QNFA4TDBiOXgvbzRDbTdZci9RQzBiNklaczlWRGV6QnRZSkllTkozMGdBYW9WYVRhOVZtRnZIRUR4TDMxbmFOTFMrMHVUUW5lbVgyQmpHNHlBTVRCNlZ3TWNFZzBMQitNNGJJZWtqL0VHbGpkVDJDS2JPWGtnd3pDaDl6bGwxRVprako1QmMvNVZ3emdYTXc4b1B2cStXYTNKNURFNU9XUmRtV2JURldDSUFReGR1YUYrVDByTzRUQjVtUnIxZVV2cHFWcFdiZW1wOHhiNEJyNlNmUFg4Qm4vZlZIeXMwRG4zY1l3NUxPSzA1TkRnb2JmMlplVUdkaHdhUklITmxBZGt0TG4xSzBWbWNGV3VYN0U4ZGNzSkZwVk5Gc2cyR2p1cHRNNndUYlpSMjJkL2pJNkFFcHRQcm1YR3VodHQrRFRpZEMxd25MZytJTmljcUUwY2ZodVB2djBMWHlvUzgvL2xFZU9ZUmxkMkNrTDNiNnFpdGE1czBEQU12cnZ0K1k2Q2JwS2RzMlJDYjVIdUU5VGs2TkxqRENXbUF1MnZIZCt0cURCNDV6NERldzFaUVBmL2JBZkhnZHJ6OFh4MkZuTWNtRnE4NlFKcElqdmZmeWRJWEszRFg0MHlhdTRROE9tbmFqNnkwMHBXVVpDZE81a3UyWnVpNFVZWHAvM3g5dEp3clM5eFBqNGxKYVdVVFpGWTNnaThOQndiY21IeFBtbDdoMlAzSmNtUVdMOGY0QXdHRkJEUGRpbVhqVzM2dWdYeEd6QlByQ21CUzRnQnFNQ25KWGhONWlMalFFdjZLYVVpYWRkV09BdjhudTRaQ2VGZ0VQUnBEbDVIaXBwR0J1TUdFWHo3N2h6aldPU1kwL3lPczJCLzRYNFEvTDNqeDlDY3lqUDZUZ2g4T09XRGxyNGFmdXZ0MVNSUTBlUFpHT01ham1hZ0hTaHQzWjNNYXF4aW0xZFFvTCtmM28zMGIrQnZpTExDdTZEMCtTMTlmeHZRTWp4bjN6SlhOVzFidjJmcEFhVFZxbk4zdUpwb3p2UXNKN1ZWek9GNVU5dWNtWVY1ZldqUGtscWdoc3ZoNlZ2YWdOTFd1U1JXWXpYYlJGN3cxOG43MzhDb0ZXZ1daMTZrNDQ1UTVSWUxEWmhWcXJHNk9rMUxITWcwOEJzQUFuWlJjamtSWG95SU80NVZWaGF5N2F4ZUpPTU5UaDk0L3ZVcmZRVHVQTWM3WC9MTlo5K3VETXIzNHN4eGtSN1A4NjhsSjVPMEpNYVhQbDh5aUNTZDVocDh5Nzc5N0J2SmJRem9WN2pzWjhqY2VYNElkQlF1blhwVlcxeStKSnYyQjdmOEk4WGFhUGVkTFZsWld6SWZpTE55VU1nUENWSjNqSkJGSUdaV2JhbDhTVEl3ejVaR2lJYi9Oa1FPa3hnTUdoNENwV01uWkowQTB3alVmbGh6Qlh3VnZ3d0plN2k2aGUvUlcwUTNGaUY0Sk1LWTRMUlNyN0daRHREWGQxUk9wN2ZuSkZjK081SVRrNW9odnFwS2FTeGpXdlAwYm9hdU1OZEFVaEVvQjV4ZFI1TEtDNFZFUHpuKzZ4WkVTK0xyN1hHSnNUbWlYaDUvRlBQMG1VWHZuSElIN0svdStyTTZ1OExOOXhGWi83TkdNb29KZkpHQTV1eDJGMVJsQk4vYUJjRkt1cnZrbENCOCs1M05VY0E1TExVZzl3OVNRS1BkVyt1b0JsTS9OYWVZcUk4WVlTbmh6d2NlN3VsbFJ6dDBPM25jc3RSZ1pRaFRVODF3MW1YQnVJWkR0blNTNVFVWkJDRVEvaXFCdUtOSFA4SGJlaHhHRHhHaGNTcWlBRXpwd3p1MHRuOGx2d0xrSzJSb000bjZMYkthb2kza2t3ZVRIS0p1SWdsMyt4YXVsY2ZZT3p0VHJrUFE4a21iOE9ZWHdxUUpIY3FkUkl2cHJ0ZWNBOTAwS25PWWZyNktTYU0wZ0hnYWswcmF6Q1FDa2JXdTgrR1ZmdlovbVoxSVlod045QmdHaUVCNDVUcW1hVEJzTTRuS3BPR3JMeVJBSnFBajBqcUhSV0VnakJUUnVVTWZpbFJBWHdJSUNvczdnZ3llRnRhS041MThCd2hpUUQzQ1VJaytIT0p5K2pqbkwrS0wwN1QxSHRwbGxwQzJLdnpGODUzNmtGMzU3M2E1YVlkemYzNDloNWVPekNMUXk4RGpiRTcvMlJGcUhETlhkTmxDd2ZucVkvL1lYRzg4MkV2ek1vWmVhMmxCQzlqdmhkMC95YjFoemxna0NRUGF1ME80a21xeHFLdXNmMVQ5eHNYT0dSUTgzaHljYVFmSmNoTS9ZaGVXclRhaGZ4QkRrOWxQNXEvK0dLeDN1bXlkRk9JQktybW9abTEybUdjUmVpdmtmM01WdGlva1VVVTh6RFFubDZVeGNQclF2SjkyN1JEN2k1TUtIS2oyN1RBY0w2akFyK0NMSUFWWC8wOVFUaHFPRW1IdUdTVFN0eUpaTDRrZnNRUU5kL2FIcGVJWHdBRkxzUDZJNXNiRXpLVjF1S29NU2tSNjdJY1dxSk9DbnFhU3NHc3hCUlhtalBEeUhkRzRhK0VSNlRFZndqNGliWmZuWHdIVkNNSVgreTlFUmVzazFTVTBWc0VDbXVXaXYvWklPdytOamoxSjAwTGhCYWZYdVp3ZUEvWDU0U2tCS2NteU1IdndWYllIWEUzRmhYMGtHamcvcG9zbUNnckQ1L1VlaWxLcStWdXFKLzExRXBtcjh0NktLOVlidkFaaUM4S1QvRmNXb3FiMEw0a2NnK0IwSmEyYnpMcUNxaDl4YVpCZytOREtabWU3NEhKd3lPVVNTSW9yRHcxVEtTQVZwMVVaMUVQUzN2cGU5ZzB2N3hzQUlGL1ppZkJLTk5aV3JhUGFTUUNFUjg3aGdvYmJlQzFaczJVRUVJMVVCaksweW1XdGxZc2VqNjM1TzlUNGpFbFM5anUxSEY2RU13bE15ckw1ZUZiRWJESk9wQmJmZnRFQkVwSmEwNmVBdTNVdHNJUW5Xd0pWVncwejlaSWF6ZkxZRlhGVm9ZczBTZysyMlZTOUY2M2hFczl0d2lNY2IrV0lKanpPMjFMQVl1ZWpxd1EwK3R4bUNyZnRBSlpaT0ZYREhpKzFwdCt2SE1aZG13Q2tFaDJUanMyWm1rT1plNFdIKzJ2RmRNd1JDRWNQcFhJSUo2a1QzQ25oTnVGV1l4VUJHbTVtRDRCQ2FHSXloVXdoWS9wczJ3RUc0QUZSU2wrT1VobXhVS2RSdUhWd2lIS25xQk1uMFdMV2pSbXFYb2loTUtnZTZhamxVbm1yaU8rc0FVczdLNm05czNRTitLSEhzWlEzNzlTVzhPaWhWRklCdHpyYm1CSzNYS1JZbU0vMCtqVGJ5SzB1UUVyRUFwRHFxYTI5ODZZZGZQYm9XbEFWeEJDVlhvU2FoV2dNLzJ5Nkp4V2dJS2Y4WTlRSjBPalY4TnRUdXhtdS9HaTA1eHUzOFRzR0hBZWFER3dUczY2TXE3c1VMQzVnWkdpajlRbkYxSjdqd055VTdwZEg5amNaalA1eXFWVnMvQmZDOFdQQXd6cktqaUdQMGdNaElqeG1uK1YwcExZd1dzOGN0SERzT05DazUyRE5KVVRFUHpvUDE4T1NHa2JDRGY4dUp5Z2JqN0E5RlUwcWRZeG5OSmxtaXJhZW9pOHYyTnFwMC8xR2RIUXltMnRCcmE0bUZoU3N3aEdMbWVQaTZ6STRGdk5JRXB0cnRRRFdrVXRNWGp0Z3NWb3RRTTQrM24rNVlvRG9wdXQ0aE9mdE94MGVtU0J5RkxLNE9WcEE2OXRUeE9CWkx2VW5DVmRrS0JNUmlZbXk3MFQ3SnFxTFpOVjMrMm9UZ1p5Z2JBUmZQVXhCNGhJZGZuOE85N3NjSHRlaXlzNGVlaUdpZllJaEhtY0E0UExZYVJkME1xbVJINURyRjR4N2dad1c2UENtcHlVUTJJUUlrYnFHaG5qY0lTVHg1WUYzcnV5aWNNT21rNGsvNzNNSXJwSjhsQ1RHaHNPSFIrbC9lRmM1L2ZiVHhUNjNKRVlDclNCTmJDSXR4N2Q1UFdUZ0pSSThaYlE2NWJORExWSXBRbHFkWlRNbHRGREtoWWhFaWdjbGRNekQvMDRpdVgvLzVOclhzNXJEaktnTDM1MVlzdWJnRmxJdjB2M1E1Sm5KSkZMcU1vdlRZcGZXcDRadWhINmJxcW0rZXJWcXZMWUd1U1V6NVMvVDVacE5hOWVHZm1RRy8zK3RKc0ZxTmtab2t1bUxpbW5Yc0pmNWFPRnpHTnFWRmxMVGxSNDdXVk5EdHpROGNNNWNUS09FVlAxeEk4YS8vKzY3NzlQTWVnSlgyVlhkM3o3QmtTMVZPWjR0czVuYXdwek1zcmNkVStBOXo5RWpSOFltRGgyZUdEbjh4dGprb2RkTlVQZy9ZZW83NVFuYUVQK0dRWkoyMTZHRXNEYU4vZ2g4eFVvaWNQanFwMDgzYmZKMXcwRVN6WDVydmg4RE5vMHpCSnRYZDdidFRXTS9JSW1ieWZkN2JYTDhuaERDVmVhWHdzbFVhcTg0ZkVRclFsZm9CMnhxUUQxTEYyd3VUWHF2WVNIWFk4cVhlZzRqWURRL096ODdxSDgrYlFqaEV3RjBUdi9pbVM4YkpiQjluei9OVkpPSVV0MkxGMG81a2FEZ3ZIeWhiajdnS3Yra3N0elYzNi9YWlZCYVFXMnZwSC9KWmhSTit6NUVRbi9FSzQvRHM3TWxKQkpoRFVJZUIyY0tRVjZhcnhNSHdJUzNDRkJHbHRxSS9kT3VqLzg2S3dQU240VmhBT2ZyOU1uYVhUUk9SdFlRU0NXazJWbFllUnlmejBPaXAvLzVGQ3ovR0FETFA0RStHdUlTOWFxc2l5bkJ0NjR2elZJNVZReUUxOEhieTRhMDBJSFNoUzZ2MUpDY1MyVTR3V1k4b2Z3VEFQejBNL0RVQjBOcE16TnBRNmRJdHNoSmovekNjODdQa3BMNHE0UHZaQzh0SjZGc0pxazZmZzk2RDRkMC9zdFZOM0V3bGNFWGlHaXJSc2NpYnNBOTJxbDVrZmo3eWkxMjVTTjNmZENKZHErQ3lCeG1HUExScnhITUxPSzBpTy96SUxUSVZ4OEJINnI5OVhGU2gwZUs2NmwwT3VYOTVOOE1vM3VYZnhuLy9MQlhiLzJYN2cwV21FaW5VK21QV3JjODlxclVHeXIzY21xelY3MTZoT2dlRGxNa1ZKSThmNnRlbnhFd2FqdmgwRkt1K0t6cW42cVhrNmlBMTRQUmFiVnFkSG9CeVFkeXVhZ1N2QkwvbVRsaVBZTTZITDVqTGN6RnNmTi9kN2lMSnh3T0lHTHRYdnd4L2h4UWNReEFKWGhGbFFGVkYzSEdlYkNJOFgyd1gzMDEzVVhxS2thbzRTYjVmaS8wRWZEVkhnMmZnK1BDTzlhR2gva1VaRk1EVnFYNmdRQlkrVjBsZTh0WnVYbEp0Z01mNCtldVE3MnBtakU2NzBOaHcxU1VBNWlBclBmQVBweGdjenhCZDZpalgyUzgxdmJXMnJaVktTUGs5TGpjT0xJOWVlT0c5VzIzNy9aTWZCR2JXc0pjR2NBVHZNY2tOb3h4eW0vcDdUVjdSaXZDaHZTcTlWempPcTdSZittZE5YdG1KNlFCeTlTUEIzU1NzUXBHazFBTkhkSVZ1d2dzdnRyUFg4MW5FVnpGa0k2dWtmNyt1eE1TOXVSUlJSMUhRcGdJbWhSanVwaVppT3o4Vm1SS2U1ZEcvYWpSUzJmTVdWdGxIdzZ6Wk9LaFRFUG5sUjVFKzZQN1hhM1JVMTFDMXhocU5qMzl3QnFUYUkvdGxRcko5dmhrMzhYaGc5cVJ5NEV1SHRjVmNMazNQWEZxMFRjK21Xd1hubjR2UGpIUFRFZHN2blpyeW9tbkt6NWV2K1BuK2xTc3l1MnVpZ213Ri91SXZQKytkYXM2SlR4bGpUbkJyd2s5L2trR2puWHBSWVMxZXYxQWJWa01pNjNNRU9CS0cyRjljWUo3b3ZzQ3FXYmRvSnQ1Ulo3TkovR3o1VmVZNTdldXVIbThTOTZzMk1HV2dXRGswbmRtMTA2Rk8vZXQ2M3c3RWdTUC9GRzNaVjM3UWFOemVoQzl0Q1R5MUxhTW1GSzZKOG00cENURG1HbFdrOU53dkZKN0RMZ0dsTXFHcW1FcDNsanVkbWdySnZXTUNsa1hUQkRnS29aYXRmSVp1Zk4rVnJYWmxGVFY4QmV5OHRMWm9QaVV4Sk1DZmw1TU9wWXpuTGthOEl4aFAyTDdibUlJa3Rqcld2SFJxZEt3U282SGxNRElWUUhQYmE0anV5YWoySFdtQ0tMRXNIRWc4SFhCaWxqZHNZN2QzYk1Nc2xocVNibmtPendsbHZVV3VHeEpVOWlsZmVuYk83c0NrczVmQTc4NVhhVUNKcTZ4YW42MUNzMjQ2T1VMZUZReVRJaG1jYis3bEcxRktER3M2RGdqODlTbGlTakJDNFJzSGFYVHRmWXZmcUVWSjhYQlliSkZIQ1dHcmRlNVVISTB1N1Qzb0p3aUJnOHJBVEhsU0IrbFVZakdtRWxpV2JwOXc4WGhTWDQyZnpHb21BcTY2aCtqNUxzcWRkZDJ0azd2NTRLa2t1K1pFVnp5cGVSN2hNcWdjUHI5U3I5QkVqK25nTlE0aHNGN3VWY1hJR01Bdkl5LzE3dE1rVkx4Q3JBYmd3T3F3MWVWV0p1aS91VGRaSGE1QU1HRnhRa1hJZ0pUTFo0a3REMlhoYW9mVng5cFNCam5IL1o3ZHBEalJ0dzRyOTlWeUZPRm42ZU9GZGxIUXlUeXErbGJ3Y0pTcXFvZVRRYTd5bTVWSGNQak1kcnJvN3BlczV3UEJMZmZYQk9HRU4wMjZTWjRCblJ1c29yejl3eURWMCttajg4RzdtdmMzbFpSTFNoZ3VubnNDWFZvcU1aZkV4b3EzY1BsdVptY1NqRHRNTWdwWm9mdzFTNS9rYjlMdlc3OTFoNFdnY0RhSTcyeXRZWWlyKzRIUVU1K2ZVajRMb3hLdmNNZjNwclFxYjVLMUhSdHdPZHhkNXF3SVdqSjFYQnBYeW1kd2doU2ZRbDUyckRDTzdXbEFtNHJqMG52MnBlVU5RaXk0M0ZaZitjNUJhS2dISmFrUW9mbFpsRUFvYi9DTGI3dWxBMGpBSkJKTEZBMlBjakR0RUR1UGZ1QTZMUU5BZi9QWDZuYTAyalF4bHo2cFQ2S2U1R3JQMkh3N1J6elN6eVBUM0plaTBBeWdBbE52MUR6RkJVQWVwSjdnTzFQM1U2WENINytUTm85L1hNd1FxNk9aMnROZ2cxSWRBQUdFeUNpSFZSN091MVhuZEREMm5qMjZxVHdzMU1FblJKWWtPa1ZYMDYrV0lhTjhiendtZ0krWTRyU21BaDZ0WGlBLzdwUVNTaXp4QlRpb1pKY21CUk1hVGxOUDc5Ly9lTFo0MXMzNThObU5SbEd5MDhpbCtwU09YajU1WGhlNTZIYUQ3dERtMnYzeHRMbzAxVWtEQ29RYjJCcUozQ2xPdWVnS09VNFVLQ0NZd0IvUTNXZzA0OTMzRTY4YjcwVmpTRWNJakNJRTNxWlhtcEVPSnVEU3BoQmdhaVlJWUlwY29hRW9rekZieWpkeXdPVXBYZVpYdnIrcDd0WDU4MHFYcFdOVUhYUERWcnAzU2VJM0pGNDh4Slh6aWk2UlFPek5aVnZvSEZHQUg3bEhEcUVNSmpRRkxMMnlpVEE0b1NqaUhWT3M2Rk5nVjkvT1IzQVB2YS9XYmVmcUZLMUxqZW9MK0wxb1hRT0puMGVNajR5WTVkcW9ZWFZUYUZqNVNGUWRRTHlyMVVtVVJoME0zb1F3YVdlNElrUStJc1lyZ0NUV3hyNjQ1bDlPZVpyRTdLWi9KbjhjSTAwQUtEUVQwWVJkazZ5bHVsb2RnTkF4WUdUUmxIS0RJQjJVT2graTlqTDU0SjlvLzVBOGhxSkFWV0VIVk1xK3RsSk11YVAxcVUxQ3VYdFRzcGJlcVNpY1FNQVNBZEVleE56WE9iMFN0TXVRdzhBeFBvUnNsWjRGTHlsVWJha3JlUEpoV2dIMkZzcmRaYWlNRVN4S0FMNVdmNDN6MTBEb2tWblVFVkFoa1hQOTAyaHlYZjZvZ2ZOMmNETlQyQlFNWjBGaXFCTmtRcjBNQVd3RGxkZmNFVUkyWVRSK09zL2pYYy9JeE1BVjRmT29TTDZCd2dIZlFZUit3Zk1lallkelJaa3ExemwrR0t2UmV5bFlUWFU4bFBEcUkvRlVOQjhpeXJhSUFZUDBlY1FVeE9kQTFBelE1akJJWGRyVit6M29aNXVhZDJVazFpSEw4Ynp3R1JESm1FTU5nV2RiMkJ1SjJVV0QyVEZPNHhjVGhXcHZsblErMjN1RkJJRUVFaXlPeUdFRzdoK05ZL2JNSVpIbFBnbFZaWUlkUkxlbklHK0p6UXpnSFJIempOQkFZOWhDTkhDOEhtNWFKVGZPVEoxU0dtWXBrU3pOeHJVYjlQWXQwbmswMjNDZC95dUNQM3hPNDdCalk5dXN5RkFnRERPK3lJMWtKb1J2QkIwSkJ6S2REUkI2ZDFIUEhjVlg3YzRlYjEyekdPMU40ZzdmN3I4TEQ3d1ZSaU4zNUdFVUVZTjdRRTRsM2lFalFqNXJhMzFaRXhqQ0lybzZIaGZ2MmpNTCtiUU4yb3pQUUVXQUFwRzJpRk9mZUY3QkJzWlYzZ2xWRDhzM3k2NTU1WnF0UUh3SG1PVGUvTjhmbE4raytrREpoWkVPSHB0Unc5Q21EUXdnb1k1Qk1FRkxRQnlIcTdrZlo0T2RYcStWRTNkUzMxblptK1V2OHZpaTgreU9wTnBvY2ttdGYvSzdRQmdvN01uSzJRNXZEd0VrbzNGSGkyTU5UYlF1K1NRa0tRaFcyVEJRNGlxL0RpaWFoM2wvVy9yR3FqWDhzdjdCRUR2Wm9oWWQ3OTZsOFVGYkhDaldYSllVRUd0dzhPRHhlb3h2WTh5bUFBejcrRGU4K2xPUDAwVkRxNkY5eDllZGpiazViZkxCWXFNT1EwUy9pTUR4NU44Mkt5c0JKRkJFWDRET1V4dG9QR3ZlTjJteEM4ZmI2NjJxL2tVZFcvbnJra2lCMXRsTVB6SUgydmxjcm9XSzJTcDhxOFpKNTV3eVpScTZicU13UmtlaTFlbnZpdTNEaGVRelc5M3FhMHJUaG9mTzB1MEU4aDRYWnN4c0VGQTFCQVBQWXNaRWtvVzlTUlJWZG9ZT2E1TWdWczNUb2UrYlNwZ3d0ek1zNWp3cmJHeENLOVlhSE1ZWmthUGN3UU8zTVdYMVQvMDZiMzdjVnJ2OGR5bnd1U3B1QjVFVlJxQU95TVNVMjhFak5ZREJqUm9TLzhhRUk4L0Q2NUFRYkRBSEs2aUx2T0tnRHAwQmtLTWNsZ3lwZHdpNTNvU2FSYmVJNFJlbUJJM3JuV05ra0xsUXdKRzdGNFZneHR1OHFZRlJ5NkFwNWc4emhGUjVGaGszaU5rU2Zrd1VOVmFtTldUa2taZFZ6Rnovd1VrenpkMUdiWExxMGROdUdYTjdFeUhvbmRwSHZ5eE5oQk43bi9lZVdSSWRBdCtpQmdDVHNiUmIyYlQzcXVYTDU3ZkxtT3Z4Sy9teHE3NTF5QmhSZExLb1pDanFwT295aUs0enlyb081YVgzektrQk5zMmtIRjBlUno2K2JtVjNwazBpY1BBWjNTZVhGWEQ5N0lyRFhWd3VaOUlMeWd5V0wvYzdsYXk2K1lRQVpJa3dZQzU1ZEppcDBJKzl3SHc5MDVPRDJ2eW41MnFFRC9vaEdLdUpzcW1Ta3Q0VFZjVytVWEx4S3ozWFZ6WDJ6L1JDN3g0M20vY2I5Zkw2WGlZeHNoUVJMN3FQUmcwQ0M5SlltWENkYnlPOFZvTTQyeEpTNTZIK25aaFdPdkMvZmExTktoVm5DT1hLbWNUOU1vNER4WWVVaFU1TVQ3V2IrR3l6K2owQmhRazNSWVRtRjUzYXFpVnppN2lUSXBCNFVHNnIxalFtam1lUitFbG1RYjhzTkZMaXpLbmRzLzFQZWU4dkNwZzRaSjlsamdKNHNHcUIrNVU1RzF2RlJud3dydlVIVFhHbmJtZnMvVE5TenluSzBTK1JZRTJrei9ySDY1UllhdEhraUhZSm9ra0w0VGU3eHRNUU00dUpPczhlZkc2NTZzN21pc2RnUU9nUGVmcURjT25YMUdMUEJDZlB0eStXUzFhVHk4bkpYenFPYnVNTzd4VEJsbDhjY0JVQWVqdmtpUUZtYU5DQ2xXYWxkcE81TnZxQWtZZzhkY1E0ekJPZ1RlN2t5RjRobWRQZTY5ZW5LYTJrWndISEZsUGRCbFVNdENJUno2SFI0V3JHYmxMRVpnR2gzSDBGNy8vdHZleWI1WHdNRGp4aHhqNjQ2Ly9GRDljSXhlU1R0WVRoUEYwbEJyTkRnQmlBdGxrZHIzVEloNXprLzFEbXhobVljTzl6NnZXMVhhcktUNGFTbnlCZldpSnB0RGxEQmJBa1JGN2RJRTV3VjZaRDFLT3ZURk9JN0xhT2FiSzVUdVJHQy9iejJMOWNhQlg1MlJPb3JwVkZ2SEVKTGliK1VHOHk1bjk5WHc2R1k4RzVvVDQvMDdCeTVEOFlmOEdwMWE0SmV2SDFvSlRhcDE5c0svS2U1V2Q5MU9VZHllSmNOMzcvVHZROXNvUlFCaW1LdDd0NmRXOEl5N203TGlySzhtTC9ONWYwU1dHblNrdDZTYm1LZmZsTktGdmxqWUNrTVVGNmZFUVRwWmZSMjYvbXl1RTJCRjl4Y3cvWWYwQUl1SlMrZ3FMc0ZxejMzWmVrQWhJTjZEcnQ1RS9DTGFnVUh5UEhpUjVkWXVRa1ZacCttaU0xbnNxQm5vYVcwaVQwMnBIa0IrcXgyKzRWajcyMERGSktNekhpUGRuZUxsSGhWTnMvNlVsOCtaTE1PZmErb21DVi9OcW5YVWhjWXh6NS9aK1VDSkxtRWZzSEhHRDEzTkEzeFE2bmFlMnNCWmMyRWFhVGxHUnpVcVZNSWNoUEc4ZGtRVDNXYmRMaURGYnBDT2NPNU9uVWVBNWRwMENSakpLb3hlNTh6R1dTYm1qMjJjS0F4SUNQNElRd3VOd2wydkFvQjFaaDhuNXpWOThtVGZNTFZVaFR1L3dQUFRET3hjUlk0N2pFUThoR096UkdER3NVS0kyY0VlYmRDbEhSeFBLMlpDYnNuUmlYSmtxSHR3Ymg3WVdoVSt4VVNSYzBBdDUxVkdJVTBOSFY2elEyY25RRHB3S2VnaDNtK0JpaFVESS9iTGQwZTEzWGdNejNoRGkydFcrRlhrY0VQdm5oV2Q1OWlIdlE2YUVPRHFtVjB1dEdJN0lqZVdtWkJNa1RmeWZBWnpIZkNxTW5FeXJMN25ObTN0YVpJUjBxbTJvb3AvbGI4TWNKYWUyQlB4QUNkZGROQVlGSGNNeW9JR2RxRE1vdXl6MUt5Y2lzOVQ5U1lORFJ1WHorSndObjQ3bWZHdmJMSjFzYVJBWGdsdG13MXJMOFZ2ay9PM2gyOWNQUHovN2ZPZlc4VEFjTk8vSHFaVDlMaG84OHA2aVJEYW9hM1E5Ui9zeG9sWERGTGJad0k0c1NPZWNTSERuVmdTVVk4SEZ6dEZ4RkFZKzlWeUhHQVh1eXQwRkZEM1FJdEIyMXk5eUFTUzZUWFF4TDBoSXg5ZmgzYnJjL0twT0czU3hYVzdWVzMxUkFsN09uYU9GVGJvaFg4Y0FzVjkvTkd3eEJDTUN0VGdmNGdiNE1TNy9MUW5tOHdid3I0eXk3TXhMSXVidExsZVgrMkt0UUh5VnFJTkZYam00dVRZa1d0aEZWYUdyaitYeUQ0WUwrWncwOWtKWFJDbTM4d1QzNzE3T2gzM2ZsUlVvdTBkRTFNTzdJcWVJQi9LZ0p6RHJLcWZsZzlMZk1iNEtiV1lHSy85MXZ6bVZESTZoRHRQOTRhOElhcDlOanltaEV3N1V5M3Y2L1ZOc05hcHpkRmdHeXdUWHlUSlo5MmpFdC92THhYdzJIWStHZzM3NzJmVnlQaDNmOWxTcU9JNndsY0lmQWpnY2sxeDNJcngwck1kNEIreUo4T3Y2ekhCSWRJbGJOd2FIMnJ0YXV2UUFoMkM3U0xqTjIwY1pBYlpobFJxUXJPY0x0allEWnBUb3k0cExIL2NPeVl6eEJWU2taVXFoRFhKR3gxS1pQU21kYVJZRmU2NmYxUE5QdUd4VHdaVkxUU1Y1bXNSUjZPYzhGMXY0eWgzWnFYQVl3NU1wZzBneUFmTkVrRlNHdzFjS1lOQ0J6Ynd1ODdFWWFwNklWV0xYL0FaYWNpRWg3aEJCR29RUXU4UVlGeWNBT0xHQ2gzV2ZxMEYwaWpkc2NNd3JEUnAzcUZZeDZtMDZySUdaT25MTlJPVVRFSGZxZ2VhZzg5WW5ESUZQaWo1TlBIL2FmSEk5eThuQmdMa0VUTG1iV1IzTm5DeXVLeE10R3N3RFVSejJGRDdnOUxLMnFFdHhIVTQySnBvVkVVL0dnOGJMV3dENUMvczFyVWlTMDRYaktqUzV4R2V6aVU2MG53UzRyRTNtbG1McGd5UDZWUER3L3ZuWVZVcGthUkpaSlI3Sm8rUTZHQjBpWGJFWEF3Y1UzVWFrc0hqK0dpRXlxRjIyZVpTbnQwd1RzbmszN0E0Njlzb2l4WnR1bDRPSFJBbDVFL3ViNlhpQWV1alI2R3Z0UjR0WVR2OXMrZ1ROVG02cjdqM042WXJkdEFyMkMwWjJ5NzRSQ0ZYZWpHWlpjQWoxNUNYVlczbys3Z2RYTWtJS3gxVzZ4MUg4dFdUZXVZV1FwZGxLREpVOW9NbzFBVXNnSDBVM0JZc2dxRVNlYVNCRktXMGtaeEorK2Mxa3hyeG03bW5GRzljcVYxSk9KQlBiOE9YWXBlTjhjTE9qNmk2TE9LSUlGbXRjN1kvQi9IeVhXK2MvcnQ1VmNuN0JBcTk4YVM2d2l3b1JpRlhIWWdCbkNIQU5Qb09hNlFHMHk5SFU5ZzJ2WC9aN2ZhY0U5WFlaQjNZQVc5K3BiNXRGZmFSM1NRRWx5eEhURlNVTWwyaHltMnJrUWxPNHpRaUpYUTJlQmhNWlU3VURtTEdWMGFiNzMrL0hvK2FUOHpHTkErYXNTS2FYeWNZMXI1Y0ZlK3VRdTVBZUx1RWNFT0xBbjA2Qis2ZTYwdEIrV3Z1TWlUYk0vT2ZSUTl4UUNjYnhHb0FNR2x6bktJUlNjQjZwQ1lKUU51bnJWMkNsQm5sY3VxV3NjWjBqYkZyckFieFk2a042V3Q3K1Y5OGlFK0xQMys3ZVBoKzM2OG1vOCtKNmJtdWUrZFNxb21QR1dkSzRUOU1JUklWRkM1anV1S1U5bUlvc0tLaFlvWldQZ0pyK0NGWlZDdkoycHpyczloVXVZQU9WbitUM2ZUSWQvQlFUM1ZHREVpUlN3TTZMVG93U1NySHdNQkQrN1I5ZmpMRDFCekR5K3J5emlDMlZubFJjYlhSM05hZ1Vxak8rZ1J1K0ZhTWUzTS9xUDZxTFg5OTdndGlGcFpnc3psRVZ1YWp5Zk9DaXVNQ2lNcG9vbmVVd2xLMXpxeGV0OHE3M3ZrOU1yUS85N2dEdzNjK0h5K2tJdnNXMzc5YW9Gby82cUg1TFBEbU1IeU5LWE1uWnNabUZWeTRJaXRlSUxxVVZFVk1DQ2F3QUE3ZWtiVUVHa00vZkRXRFYvaC90VytlUU5HcnNqbDg4ZXBicTI1UnNSaEhDSHBmVlVOL2l5bVpXUExTMWczVWgvUjlDK1J5enZNRmtsd2pScHNHd08vcUtoZnNDckdrYjFXdkFSV3BKeTFFbFhvbWdRbFF3Q3FURDRnWWlZZVVqV0hMYkw3V0hKZ3pMVW9nWHVwcnJ3YVNJWHIrczN6aTVoeTJTa1RZVnZPUGJzNkpEYitFK3UzaFFhcnIwUzZTMlRWZm5YbS9mQUd3cVU0NnMxanBKSEtHRTFLVWNJcUsyV005NmhXRHpsUVRXSnBDcVdDVkd3ZXJoUzdtYzE4ZHZrZnVUNGQzYnU4MTBNbHlPbHYxZVZaV1ZsQmVsak4yQ3hpVXpEQzc1SlhvSWM3Y0REL3BROC8ycTJ0NUtZMk90V3J1dGhJdDZhR2NqSXJoRGxWRWZVZFYyd09GWWwvbkpYZGJxeEY0c1pSY0V1WG5heXkwcHUwcUlodG9pSCtSWUZRa0dLeFF1TEpJYjdwR2h5UklOZ2NYc0RBaVlLS2VRRDhGRnFZanBFR1haQ1M0UTA4b0hYbEVzTzlLN282cSsycU81MzVmZm91SzZiaHYxemRGYXBQMTAzcXlIZmRRaEFXOG9meUkrRWtoSmhtTVJ6NTFDaGdDL0lGZFR0emtLcUJkNldHV0o4UlJVajhEdWxIRk1QMzRKelpEc3A5MVBLK2V4cTB2eCtncnFXTldyenZOOC9iNWZVbXhBZlhzZndFMVZIalJENjR5UXZzZU5mYTM1aTIzRGYyWVZiTjV1TS9TSlhaN1k1bmJKanZnYVBtVE5sQmhIV3hPN0pEREFpc0FTbDJSakcyTFJsS2pVS2ZqcmJzekZHbzZkVzRiY1U4d0k1QlBJaGFXaHFtS2Fwem0wcTdPZ2IxakZhaFd2R25nZjNyVEZZNW43LzJhcjBkdTNpYi9hLzBUUlpaYjNmWERnMWZFN1gyU0tpS1Vmc3FNS2JkR3JYU0tZMVhlS2xsRzRjWVBURHdPOUppSFQ4RTZkNTMzbmdZbjR6a0tHempYZDYyQUVCNDMyS0h3ZTZLOEVzZjkxS2piNm12TG5nVTlqYzZJbUtXSDkxK2p1amVkeEtQUXcrTU9EOTA3Y0NrdFc1eGthYVVIZ0JFck5EQ2JDK1FkbEFCTCsrVGVPdUNEdi8raGUzMWJLcDVzWHZlTzdYODIrbHlIRmY0dnhIS1JKRENWekVmTE1xYnFkSjdGaVFlQWc3cExpVjFZVUc4WDBmLzh1L2Y3NjViTW5WK2ZGZkR5b1hycGFjdW82eHM1RVRsMWt0RHV1VWpoUDNnVFowaDE1NnNmaEhNNGU4c2c1ejJoM3ZoelhreThqNHNXb0U4UzN6MlYrOE1kSS9Sby9nREI0ZDJtTWJCQTJZUmxJUWxja2tFQlZEaEFaaUdGbk9rUXNHQXVJZ004bzJlSFN1eHcvV1Npcm5RQ1lSb29pU2VIdUpRNEYxczhyRklkU1pvVlJyYyt5VXdkK0tsWWU3aGZkNk5JN3A3Y1ZFU2xMMkdGRXdnSEJFWUtrUzdOTXA1dVpPaUtFbDRSK3ZTOWVEdjNtcURGNjBDbE1qakxSKzUxZEh0U05jekk2bU9SY3hxSEtGbWxzRHQ4dEJ3eFlqMXEzZmJzU0c0cUREalcrVUczaTVXanFtSzVmZ28yUzRrVmluQkcyK0hCb1pyek1GaGloN09FdUR4MGh0bkt5c2FFKzFhdGtYOXBqNCtKaE9kejVYR1d3blpsSmFJc2h3b1laM3JyeEZYdy9DaDNOOTBjZllwTEhqcWJuenoxL2RKMzZra2JjY3FGVmtqVkxsaTExaFFXQ0R5dU9OUzBidE82eFVndThsSnIrb3cvcEp2U1dWTlpLa3pQVFhrMFVFZGxKeER4R3grdmorQkFzY3R4d1RiSndvNjN1b3Bwaks4U0wyTU1Jck1mU0ttY1p3NkR1UGpWOFkzSmpLR3lPUXFsblNtMFFvY04zTHg5a2tRaFpRUkxuSktyaUJIV1l0bXNlLytTUVlrM1ROSUlpOHBtR3lEdE4xd3lKZ3NDSUFFQWcrRmZ1ekkwN1A5SGpuNkprK0hzQUFOdzU5ZTlCdFBTbi85M0hWUGcwRUFPZjlRRkFBTDYvYWJ4Zm1xWFl0aVhoK2VFS09BWjRCVHJ4OG1vQS93M0FQZkt5Z0UrOVI1dUpnRmFjMDlVcXFvQ0RwSGhmZGFHanh0YUp0Q3ZkcUNnQjdVMndZenZIc0twWlFreHAzVENqRG5wcmxWS3JoVWFLQVIzeHFKSmZhUURmRW5EUXdqZi85dVpHbFlkcDlSdHhnakFkMEJybzBvbTRNL0tkcTFMVEZBdFJjU203RHlTQWY3WER4VXJDYmk4QkdHbmgyZm0rQXRzampJS0ZaaFVjQU5BOFFMTHN2V09SNWFGUDBvRmdsUEJ6UmxRdEt5TVI4THZVemxiQ213ajFIbVdVY2pnN2dTV2Z1Q1ZLOFlGL0h6Y2hsUENvUWpVdzNNTHpZOUdZb2ptNGw4MjIxUHdBSDFaRlpMbm8zTFRKY21IdURkMGRDL0RIQStUWnpPSmRUa3JBRkNCa3JodVZXWlVTY1pZYXQ3OFM5QmZhSXFzTWIxZEdHTkJTSERDdlcrK0pOcDM0ZEhPUnlraVNDUDVxOVAyRnhkOEEvT05aNGdQN0E1SUp4Z21jdVFpQU12YVpvQXMwUG9PS3NRakJ5QjRkV0VRdU9lQy9uK1Yyd2V3WHhLMjJtRTlZS3hoUURhZFRaTk5FUWZjOGJTbW82bHl4QnhWNzBMQUVpKzNMdjA4RVJ1ajdaVGN3MjhSemRneFJQNWVMeWdxbjlGbklvMlVndHNxem42WkpuYmpaYTZLcWhxTlZXaDZRR09zVllhRmI2L3VWd1NndGl3VmlLQUJyQ2FnZXpUQ01FVlRhT2xjbUExMTlKdENsdnhwalFYMVBTalI2OU9JZEE0SGVXRFJmWmFLeWVYUDF2eDFMdHRRWjJ5RnExRlVPSmE5NnM2QXhOSkYybjlEMnBaN3pCMHI3cFRrN0JVemdoUFlIZE04bVVHSm1uSDdQZVJkd0hMQUgvaDBoV1pqTytmNkJteDJlaXVnVEdOWVJBUURzQmhCdndXaDJ2d1hIK1A4dE5sM3Z2WVhnOSs5YlNPSHdXRTlGM3lKa1JXQy9YRkc1ZGpOWW9zbFNMV3BWcTlGR3dvK0x2MmhycUhJUUkzaHJxemt0WVZTcE5jTnVEQ1RUZExUU2RwVjJVRjNhNEdzV2dzVC9NclZ3R20wbkcyVVpOOVcxeTFUd3VRWkxKVmVvOHcyNVNxVGF0ZjZjM2hMdVlKdkZpeHExOVZwVXF5d2dRK1Rhc2NWUWhtcncvRnBTN095K2JGRThzV1JVbUVneG9vV0xFQ1dLZWpndDNLMmdPYkFGOFRxUDUwSTBLSFB0R25WaXRrdUk1djhoR0grelZ5cnIxcWR1S0pzdEwyOEhjZkJkcmJoQXdxRU9iTE03eW5zclNDcjB6aEpweWpRTXR4MmVNVGJYczJ0cUcxdXo3VXhKcSt6T2c3YTRralRiN2FYTE9yZTFvTjBPc2tLQ2xJaVNsUnh0bXhTbjB6VmxMT2ZmbU1KRGdsYXVlYUFXRW5CNXc0djNKTXAwS0xIZEpTbUh5TlcyeUxLeVdpZURNTDJPUmFxUnlYYVR0ME56ME1wVk83c3BNQzlESk5UY1J2L29ENVhPTENYVGd4dyswV0wvTkdiQWNUM0xMM2N4SC80Qi9MekM0VUVDa1VUR3hNTEd3Y1hESnlEa3daTVhieUppRWxJeWNncEtQbno1OFJjZ1VKQmdJVUtmYzRGRzJHdUdSdExvL3d1TUZTZGVBa3dpTFIwOUl4TXpDeXViSk1sU3BFcVQ3Z215eStDUXlTbEx0aHk1OHVRclVLaElzUktseWdKOTJRYXorbzI1WjhUdlJyMW0yaGt6dm5UVXNYQ1hQR3lHMmJvNzd2cmEvYkdGcUdVN3J1ZnpkMU1Hd3VIeVVLdzV4NTF5MmdrbjlabjNlcjVBS0JKTHBESzVBaWRJU3FsU2E3UTZ2Y0ZvYjNKd2RISjJjWFZ6Yjh0b1R6M3ozSXQrKzhncnI4MUFVSHNiMnRRSWhjRVJTRlRiSjJPd3VQWlBKeEFiTjlGQnBsQnBkQWFUeGVad2VYeUJVQ1NXU0dWeWhWS2wxbWgxZW9QUlpMWlliWGFIMCtYMmVQMTgvbnIxNmJmYWdFRkRocjFtamJYV1dXK0RHZXQxZ2xNS1FpOW1XVk9GUXZ5T0o5bEJIQU41KzZJQzdRd3g1VEQ2NCtpTzVJVXFCdjJUbFJpbHlmanU0L2hlNHAzcmMrSVNGS1F3QzFxd0Fwcng3S08rQmJmdDlSc2ExTytETENDczZzSUVpYmdlN1BpMEFrVnh5NWRQbGFYeGJMVXZnVWhrdmdsRmtiYnlSYkxTZ3pxelBPOS92ZFVHK21CdkFYVGgzalZiTjNueXNENE1GVmJCSnpFVlZzQmtUR1ZON3FtaXdtaVdJMEhSU0dMVmFtSjMxZDN3ZlNVVjlwVW0yRnRzOTI2OStzeGZzMitDVmIzQ2lqUTZ6OThyUldXbzY5STFXNjdlYjMwME9zQVAzZVhuOThQSUZWVFF0bjNoaHFFQWlOam8rdE9DWDRkdjUvNFhFVWhVSkdZK0MvTDFIaHdERy9HNXVXZHpWV2ZrYkVyZDY3Y0pzZExWQnd0Z1pmdVQwSXhjNDNxRDlZVmJ0TFBoeXlsOUJ2THJzdEsraFp2TXFSVXlML2VrS0c0YlQ5VnJnbE4yMXB4eG9uYTdCTXVYbmNYUzlJT21NQXBjRUgvZW96d0VWd1ZRT25PNy9jdFhJK3oyWjEySFNVd0ZGK1NMbkwrQmhwVldwZHp2cCthTHA5WGo0WmY1U1o4Nlo3SnY2L0VTSVMrZ0VKTTV4VHdoZDJnV01MbmRpaTQrVGNvTXVQV3paZ01VUHdaU1VHcHl0eXVjaVg3Sk9qNkpxZGFDRlZiQkN5akVSTDA0cEF0eGEwNlBzWmt5UlF0SWZvQXJHM0tQRWR5dy9TaTdkWjF6OGhpUTViZldRQ0M1dnU5MmlZTG85TFlCY2lGaCtTTUZBSWhsZ3Q0TzdmNHdNTHE1QXc2dnp6MzZkUGhuSmhYR21iNjNVZyt3YStpWk03ZWx2VDA5MGZFYUJTa0JaOERGTTdmMU4yVlplOFZLQUFBQSIpIGZvcm1hdCgid29mZjIiKTsKICBmb250LXdlaWdodDogYm9sZDsKICBmb250LXN0eWxlOiBub3JtYWw7Cn0KQGZvbnQtZmFjZSB7CiAgZm9udC1mYW1pbHk6ICdLYVRlWF9NYWluJzsKICBzcmM6IHVybCgiZGF0YTpmb250L3dvZmYyO2Jhc2U2NCxkMDlHTWdBQkFBQUFBRkx3QUE0QUFBQUFxQWdBQUZLWEFBRUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQm1BQWdqd0lPZ21YRnhFSUNvS2ZISUh4SlFFMkFpUURoQVlMZ2dZQUJDQUZpRzRIZ24wTWdRb2JEbzhWN05pVGVCeFFGWFNPa1lGZzR6QU04UitXK1AvamNXTU1QRkRML2xpV0ExT1NBcU9hRXJ2cEpHOW81K1FjOXBaR3B3c1NHV1BVeFJFU0pUckNLTGlLMGpCZTNDclVwWXVrMlFYMVNlR0xHbUt6V0N4Um9iK0thMFdqZDdDaE1CVmFqMHZoQ3dXSk1teGMvUGdodGg5Ym9kZS9mRmx4aitCSVo3aWR5R0l2TzBKam55UzVBNlNiM1YwdVk5OUlMcmtzTG91RW5RdHJaVXpZZ1JEWWdZQ0FvTElFRVZHR2E2RGc2clJXZDRlNHErMVhiV3ZIMGs3OXV0dXRiZTJ5M2ZyL1gxUHIza2NmU2xKWmlleUEwOEIyZ05nUVFFY3RCOVdBT3RYSklLMXdQZnM5TDlaRC9NZCsvUGJjKzgycW9VbXRrVWphelR0REZJc1dJVk9TZWRPUUxST3B2UDl6cy85N2h5and3Qk44OFJrMFN0aDFua241M1h1M3J0U2MzNWYvVUtkSnNoeGl0SjBMWVlsZ0dQKzNCV2d0cW5DQUk5REx4ZEwvcldrbFhhV1pXVkpKRlplVDhwaXFzdmEySGVKbVVQbm50WmN5UUY2RzVKcGNiVmVvZ2NJREhIWWFXYmFHNGJyTDhVK1FyWmw1b29KRzJLS3VrSGRmVFQrdGE4OHV0elNMZE9RQTRjZ1RBRGdDVGwyck5qbFZkNDNWTXJ6VlNDTktKS1BrMkI5US9taS91S1ZPK0Jpb1I5M25KU2xLSURXcnZBRFpBVHB3U1A0UUJDa29meXJxSzJwMTFjaGgzMC9ta29yakduS1YvR0hHUHJLcTNSWDZkbGhHdGcwaWRhc1JMRUVMcERqTjJ5NnB3TXJ4Lzl1MCt2UytzbWNsdTN1QVBZdGU2RjB2ZXpsdHovWUNCUkZndE5tR3F2ZGUwU3VWSmIyU1BOYVQzQ013bGowOWJxbkJXMjZTeWp4RVh1YnVKV3pMalhLajNIRGF5KzRsaEk4UUVXVWMvaUQ4V1VvWVJQa1BRc3ovVC85alRRcGxaRk5GUnE4Ykp1S3NMdVVGYkJOWk1BSXJmVFNSdVVUbDU2QXp6Ynd3SlFEbG4vVWlKZ0M2LzV3Q0FPczNEdjVFQjhMMzdZNEJmbHh3NzYrM1R0a1ZkeXVnUmEzMU9XcklVRnNQL0xVRGJEckRBQUQ0dVE0QUFFcHA4VE1Za3NzRUR5ZkNXZnJESEE4YkpoYTliTG5xTkZsc3FWME9PT0swaTY2NDZTdC9vRk5SNmZWNnZWMy9yK3M5MHp2NjBYNmlkL1hUdmE4UHJqVkNPL1QvZndnQXhTaFh2Z2JOckZEZ29WK3NOK3RLWGV2cHhudjd3Rm85dEVMVjNyOFhPZnk1Yy92V3pSdWZuVG4yek40ZHE4WkhMbk9EOWorcHA5clZUTk51ZU51YnRBcmx2bnQzOFhQemN5V094bldDUFFkVnoxSFBjbm40UXlncDlYNzRjWlZrVS9hZnBJQkE5M0dhcEdmL01IMzlqTGo5SERtNlFlUTViMVJXbmhBZi9tZmhTR1JwQytjOTZCblFKaU5VbkRrMFlPREptd1lDcmNUWGF5eDlIcGVVNTNRNWZmc2xldk91ck84eWZ4VTk4T3hFNVVGZjVUbXAwcVdIRlV0V1pVVWdSTXF3RERGODlkTkc1VnRaZ2N6SVJ1Ylp6R1FQcEJLVXdqbjFsQVBIMDg5QVYzRk44OTdBUUZOQmZWZ0pscThqYm9HdzRMeFBNRGRRbURldlZmVngwRVlxSDkrUlF3SnZHaDE1MXhGUXhmUDNRNk5tNmhrMHZZbjV0UnZlcG1ncElBNjNjV2ZPTGlwY3hqVlpJT2JsRVZYUUFaRXVMaWtUU0xvSFpMd2g0NnUxNHFNbXhKQ2V5OHU1L0dmU0VpWGY0bUpPaTUwbngrMUpNSXE3NHB5R1N4NXZrQmJpSDVReGl1TXdWd3J6YkJEbGcrSUxBaFpIRzRDZXk3c2JBRVFCYm5jYkpsTE9qNXhEaEE4VEVUeFBvTUEyYlg1c090WHo2SjM1ZE1uakFDVXFxWnp6V1dDNlFZVVdsb2p6S1FkWmR5d2sraXZRRFZMbU42Z2hOS2dNMEQ4cWtmSkJTYTFuVnRuWFZURmdtOVBnMmZ0WTdYdVN6bVNkTUxGOVhXdHNmeE9mdlh1Sk9OK3E5QTExcUdpR0gvUldjY0VTYUNQWjVJaUgybnRGL1FGdzJRVGpGNnY0NVdNZXlwOTNKY2RIM2pMVmgwRkhBaUpQNUwxL29zYzVmWkFkUmJ3MUZCL09XQ0dIYlFJSkRrZXMrclpqUGJhcXl2bjdTR3RCVXhxbE1kWEFPU2pQUy9rOU4zc0FST0lJNWNYL2xrYkZTN2pUcWNHQWloWXVOeGhSOFJWTUVnR3p4SUJGb21DVldMQkpJckJMWW5CSUVuQkswdjVScUFXL1UxVU1mcXJpOFBzaTdjSVdzNkVtY3FOdDVNRUg4RW9FZkJJRGZvbENRR0loS0lrZ0pJa2hMRWtnSWtuMUJFQUhHWkk1bWJtc1hndldld1JuRU5zYmNnTlNJQmtrUjF5ZmhqUWZnak15Tm14YkpSczZVYXZ6NlF4bFBEUTlsMG1QS3F0TDAzZ3QyUmpLZ3VOTmtjMUJ0MVUyOHFHWGtYTTVBb1o4NzAwWnRQd0xmWC83VE83YmRzMnBlYW1oQUhXYXE5d0tLRjlCRFVXMHg3dUdFblVhY3BreHNJeEtoRGlybGdXdzRPNStVdjU4YVlNektDNWNaZHdmQytOeHhhVWpseG9xQUMxRC9TUlVtNmZ3OHJyc042a3JsWWpGcWxHMWJESlFDNFJDN0tmOFFMaU9QMlgzVU9nVUp4MXlVaWlEdlJLcStBZUViWFpjcWpHYldDNUZ0TjV2aGpDdkRJYkRTdVIxWkhESWNoa3FPbXkvQnBPZ3lXYkR0ak1rbkNHdUVVR1htWFVLV3NIVDZZUGhqN00rVEVYcEJKYU1NbDBQdjJkYTZEWVFwNmNIYW5HMExJcXhDWVpXNjdZOVZmVkdTQ3hJbW1GNTJHR00yMFJzVUVEZEh1NWlxODY4ZzZ5ZkpGalVJK0dHY1U4bVlPb3J6eUJXbk1Nc2tobi9SUUJYM2xtZW9ZdGFxWHJURVdCcWdkdnM3RzFVbDlxYVV1T3l4OWRvOG5vQ0dPQk45c3lib0JKODNTMVFqZUxaN2VMbHNzckp3M0pRdHFTaVErMGdnNlJGWWNyVVNheHRqcWtPcXVTNHNFTTAvb1dZQnlDMkFKZVlwKzFzMjNtWHgzeFNrM1h4VUNHUWVqRnZaOFVVeldyVUR5N2xKeCtSaXZwaGw3OHdXcEpCOXhBVjZUcENFZE1qVU5aZ3ZDMjBQRFcxbXBVUENsUjhnb2lNc0xaYWJvb1J5WVRSVEZLWXE4V0M1aVZsQzJSb0tpM2lCWlpxc2FCbFNkbktnaDVKcTNpQnRWb3NhRjFTdHJHZ1h0ckVDMnpWWWtIYmtyS2RCYzJrWGJ6QVhpMFd0QzhwTzFoUWtBN3hBa2UxV05DeHBGMG5vSVkwTzMwY1FkaXJ6bEFEempsYVhoREFSYWcrQUM2cjYzQlZYVGV2RHdqZ0pzUjl3bTAxQjNmVkhOeFhjL0RnNCtHeG1vZW5haDZlcS9sVlh2QlRXcXYwVDNxNFZwVk0wSVBYKzlOS2lRYUM0RUQyWFhQUENSbXhoVmVzdjh1R2c3NXZhdytidnlETU9qOW9JemlSUEh4NEpZdWxtendXU3FCZHZEKzBPOW9EN1RpVXdTRmNrSnA2QjhSVDlVSlNSR0JSSmY2NXRFQVlWb2xJc291WGloUG9PcHpRcWN1TkpDcVNTT1VwdlIwRmxhbUVPMXFDcGVHSUNGSHcrWDVScGRqSFNST0xpbVBJcnZhV2tXem5lRzV2RlVIbVltazZmcE1zWEZIcFdidzU0b2lLMG90U3BiRUsyaTQxQ0VOV1NZVW9UNUM5SUh0Sm5Veml4NUtsZWZuK210eW0xdHJHR0lFQWs1dFc4Tk5GNkhDbUNNMUV4SG5kcElVUTUrbjFxRXlqUXpCNWpFVGlJamx5VlpMVEhKOVE3d2syU0lqaGRwRklTVkFVU2ZLWmZMaUt6M0NOVXorbUZZVHhHNHp6QkxuRjhMWndKWWQ4MzZmeWZWekZGZWdZVUxMSW5ad2hEZmZwTG44N3lmTzdlWEFHZ3dBUm9LRGJJaDVlRFVWamhieXpjVWRvbEtidzBzOXBzVDBtZ05NWkxYcVVRb3VDZUhoKzdWUUZWRGZQclpsSlBRcFlOWnFvSk1ub28yVitqakhoVDc2U09TQkU3dlQ3V01FQUFUaW5hdWVVZEpuWWlBejJqM0ZzOUp6Ky9IUDg2bkorZHRZUVNZOVZhNUVGZ3F3d205SWpsTVJFUjl6alY4cUk5QklaV05RM0JDaUY0Y1Z5Tjcvb0kzUlVFc2ZnL2t6VFNEcDN1VE16THFBUzlJcGc1bThZckhBb3JsbDh2K3dwUkRKVW9GU3VOa2hyUVZDYTRYa25CbTRKSnJ2eWtSbDF4QkUzN3hDTTJ5Smp4WEtrOWd3bHVBU0NZV09ONTluVWpCdWFyb1RncllBTmdHaEI2ZHhJaU84VGkrb1A5Zm5IbENYbzhoVExqQlZLUWs1eDZ6Wnk0MzJadHl0Y3Q4QVFDVzlES1NJeURNSzZuWWVtdDI1S3orU0FVc0dZZTB1WmJUbXB0Yks0Qkx5QUtnZ1lNamFMTmVDSXBwMXJFOXphTW0xcG1KVVY3T3lXY0V0YTVySWtsbGx4N0VwM2RSWmxSZHBYejBpWllTNWFjTHU2aWlhNHdmSjVKQWFMTk9HK0x3TlpqZ3NjQVFIRUlaR1V5WjMrUGtsbmIreGlBdUErSmNUY1VMdVoySEtDdzBYYmFsWnN5S2c4UzZLWWhLUVE3amF1QVJZZVRuaWQvVjhmTmgwSjhCL1JoNWhGUEJ3S3ljRzVxNC9jWjdJVlN0cmVobUE3RE0vRkxWR1hPN3ZTZzd3dGdPMDBmaDN6eXZwaTBoRU92V2ozZmlHVERhYnpwZzVCRlpYNkdBV0UweTlyNGVoRWdJbU1uU0RKcG5zaXB6ZGMvU3cxMWJoVHpzWXgwZHhoRy90dmJnNnpmZHpiZWJzWkdRYzVUenBGYm5FTWVGMzNjSDdwaEFhbmNZejJLUGN3eUZSSldiSWNobEVNQ1RGNlptNE8xWk15NjN0T1FTanJDcWRaZWFtcnQ1RGtXQkdNNzkycnNSak9ZekNudVNpamJhekUycFI2QnREQ01CTi9FM0xMNFpnUTVUTFBqOW5yQXJuc1prT3BXUXpjQ3BtbFZCVWxmTnpudG1uZ0ZZanZ2VVl3eXJDc01Ua3BRdGJOT2hoUGJmbzM1RFBWM1IxdG1yb2xHMyt5VFM5MEROWlJpVjZPcWlkNGVCak5mQjlXOFBwNVNndkljcFFSbzBlcG83UUdZTzZrNkU2eUptR3M5R3VUSDBvTWVGcHpubkUzeDlBR0l2M0Fqc29yZVNVTDh2L0VLamZJZGxSeUtETnhqdExWNy8wNU1YczZlWmxORTNuN0lpbFRFRFRzSEozYkd5alhDTHFITWxmSXN6U2loQWdId2ZvRlZvakMzVk5OQWtrVzl0cU1abnlEVXN4VXJSakdtWUNJOVFmYXp6TFQ5emFYQ2RZQUJ6dkF3MHo0eVo1MkFVaFV3amFINWxUckJ4b0RWdENVWXRjZU5raHl1Tnlhd2xZc0VpOXhLR0hPdEgyU1NtTDBXQmFPVEJ6Z210bjdLV1JUQmZpbVdqbGZDSk85RmFXY3RoN1dod1M3LzFyVHdBQWozWEVqMm9qekNSeTRFbTVsVi9ra3pCQzhZdWExTFgyZ1lmM0hFOWYxcEVQK3kvamVqQ2xLRUpTWmx3bDZlUUpGejRCejNEQUNmTzd0RERqeVlLS08rY2tqNC9zL0k5aEdCang0MjM0UWx0cnRYV2FNb0xVVVkwbGJLR2IyV05ieDFwUnpSSWpTc2plM1QzQjNZZXF3R044Ny8vVC9NMVFJTm5yL1lOZkxjU2lqQkhtZktiQkNBdHliYWRSQXhMRVloNUxlS3FJNnArUnF0ejU2d1I0NTU1aVN0MVFxM0l3TFlnSWhzK1Y3bWZVWE1YTm9YVCtZd0tWOCttWWFEVHlsWG56UldCNGNUbTdPaWh5cEZTNGpRckpSS2ROS1dibnMvY05nRE1PMEVPd2l1M3hlSnE5RmIwN2w3YmNIQnNmS0tZbTVNUzlnL3UxRkFNTFVXRVNkUVFHeWlqeVNPbEdVKy9OalMxVGkrMmtRQWRZeDlFSnp6YVF6eXFieFRBUEI1WWsvOGVTV09HMlFiMzJJQ0twS0poQklUc0pnNDdSeHltM1Z0eDhFcW8zTTlBRml4YlFtS29iSVdTbUdMTVRFZmtXR2ZkTGloN1dxeTZndi9OMVB6c3dqa0dQK21EODJoaUd3QUhoZkhHamxDOTROYjVDc1pLZktLSFJJN0lsU3hJR21GMGp0TlhBTkd6VFJqRVExVmREZ3RrMGpNRWh6VkF1N1M3MnhrYlFKckJNKzlZeU9Ua3k4MXFaUU9wTkpOSEZLYUpnUlhvQ2NoRUdreUhOSm5ubHBXdnVNTmhQTWw4UitQbjhuK3Z4aW1ZQlQ0dW5Mbkc5QXNCN0R1clZyWnpIdmtxZTViVW5rYVVSS3ZkYW01RHh2REdQbWVoQ2RMdzF2QlViZkZVcDZTSjFtYm1Lc2xNUXZmb1RLRktKbjRvcWlCcWd3dGNaZmhwaDdUbitCU05jMHBHODRrQy9PY3VheWhiR1R4bGVtdEVSS0s1UEhkZk5NazVIcjAvSytMVzhYNjBLV3VCdXdrU1ZYSlJ0S29aT2hPZDE5RFBmL1hMNEZ5L016Ui82OFRMSTNDMDJxS3FkNDlGWDdjNEU5TjVWWVhuREluMlpUeHBGS2FPT2g0eXIyYXZIckRXV3JSMEZiZFdLTkxwYzZkQmw4TDJ3VEhRSDRNNXF2em0yVlozZkVZQkJMUUhqbTdCUGd4R0dtRk9SVkVRVHgvTlhMVzBYMTcva2YvcnM4SDBoWjZlKzFTYjFmTUxZUytJdkkzUEFremhoZlZDSmh0eUptNDBTWTRFdldzK3dkSFI0eDhoL0ZDK3NyaUdxbHRBNWt2UEEvZ2xCRzYvTkRUYVI2OVdhMjY5dWhLbW8wUldpVllBKzdUYjUxeG1WSmZweHBKeFhOSjFJNUlsNmlQVXUwR2duZS8va01iamZOdmhxbjJjdFIwVHNFSy9WYmFzMmdsN0tDc1RKTnlaYkRVUG42bFhHWUhLcGdqblJWNDNWTnFXNEZFZWRncENPdTl3MkM4MmdvdGNSSzgzbXNrNVdqcldSUFJLZkNhR2xiUkNaaHMzRW15b20wTUxUYktuSUJsRm1CVTd6QUhpaUl2Wk9TN2V5SlN6eEtlbnl5d3ZmbUczYWpFVXNZK3ZtNkFRcVJCSzlBYThWVUU5ZGJhd3pqVFFvc1ZMTURzUHU5RnIxVFUweGhzY2hUZDJ4c1RFVzhMeEQzZGlrd3E5TXRKeDVmMS9CbGRUbGFURTNORFFkOVR1ejFheEc3ODQrVlUxbGZ6bG90RE5lSStzS0dDajZpbzFvVTVYeDZxOEVRRmtKRFRpUnVNbUM0MmNaUmQrOXFHRWQ4a3FNSGV1bHoyWnZvU2xzaXRjM3ZJZi9DVE1udUtETCtnakY3Um1hdlBmd2w5YWFDSGlqVnU4enM5RGRLc085ckZlWEU1bDB1V0ZxcFYrdHpyeDRnZld5TjAza1dTanZmdUREV1JQVWRmNjN0ZHN1b1FYSnRlZU9OOHdxUVdXRG9mZnk0ajM2UXFPdngvb3FGSjM2Z01LVFJ6WHdXVWxValRNT04ySEdSZDVXTnJKUUtXQ0ZBU2crNWtvbUFkQk1ZQnVCK3kzZnV1Qzk4RVZTVnRISkx5MC9KSVovR2l3eFByYm4rRzF0cGNTUnY0UUM3V25RTDRjSHZUbjJsYkZSanNiY1pCNHVFakdwQWpiZkduY0JsMGxIZ3lrVnFmMXRPeEN2Mno2YXFSSU1UOUF3S0xNak5kcjM5TnU0UWZJWGt2OXl0d2VoTGVaYUR0MCtJaUF3Z2o2UDhPN0wwQk9zd1BrM0xtOFA3b0Q2THVnRXNJUDFqVk16QlIzazhSb1JrV3VXSmdYVUhPTjEyaHY5MGxhWER3b3FybUlDWWRxcHoycXF4aUxJN2RwUmlqQ0kyd0VxUG1UQ2JibnJuZE91YjJiWG0zZVB1N2d2U0k0SFY3S1o0WFRQTFdBN1N4Tk5nazE4eU1iYklaT2VDeTZObkNQcTM4aFRUUEc1aDQ5V0ptUmsvZndRQnNoWDZkQU16ZGk5SnJ4OXQ0QjQ0d0VhdDJWemIwV1VkZXpVZDAzUXVpN0E5U0l4N2hHMVZVbURCNUxZcjcvdHNEUU42b2tpdFljeWRUMW9FdFEwd2RvalNUZGdiY0VlUFpFU1hyeisvQjdYZHVZeGlrTzFBam1rcmlNeGoxaW55NFllTkJVYjRFMVkzZVJtUE03MVVJR21kY0VJK2VYNXpkK05nT1Avb3RJVWFoTUZ1cFM3cGJVYkdCdFJCNWpqTTlWMUlwTE1JeER4YjdPVnN1bmxpZWwrVTJCYkE1OS9ER1Z3SldXeXc0VWxmSEJzbk51UVlLWkVZUks0MHAySWJxdXZQeFRCS2NQK3dwSXQ4UG1lR1ZkcExYZjZHUEVmcFpqWEkvbTJrWDh4UkYrZ01aRTRnTXhUY0U3NHZkdnhXZFZpS1pFZnVDcVpGeU1FWEZHd2lFdEMrRTBTRFVOeDQ2dEt1QTBwZEdKeHUxbFNiQzBwK3Q0R0hiWGRNemxNUkJ3M3k5cGlobnZHM0t6L2tESHBPRXdjdGhLdzFLZ05LVnl6QjFtTC9KOGpZamUyZlU4ZVJ3dE5GQ1dkRGtHZjY3VXo0eFQyR0lkNG50Y1JuSWl1aXBFME1JbTIyY2phWnVZbHJnVVpXZWFsRG90SE1QaVVReXEwY01DcTFDSjNYbVo3d3hUbEhuMnRxWGNjTnQ1Z3NmaWVsQnZZSEdqZTVCeXlZRFcyamU5Y01CK2lhVWllNUdrY3p2K25KZ3hSODhZOE9FcFE2Tk81MjBEdU1CRGlDeFFrWml6Zm5aWlEyamNCaDJFcURGTjR3aHpKL1VqZWNTQXc3VHdwT2FtZmxXdlVPVmdZdkVBdWRvbEdVNGpNNmRXd015N0RUMjRuU29aeXUrSmgrWVpOajJjczRCK2RyN1RNdE5jR0swZ2hMN05sMDZVd29GZnBkZmZ1NWpqbXE0bHVKbk02K1FYRFpIS3FBNTYxRFpnVkFTcXNUUlVzTDZxckNJR2VYcTFhVUVlcHZva2l5VW93Y1lXaGpUb01NOEViY2tZRnowVlJnNU9DeUFvQk9NQWxSWkpxdXhHN1I3QnlDelh1M2NDWHhaWkZyVEQ5WWF3NUE0R1RwKzNXRnlUbzZxaUFCbGxpcGxrN0NGUVFkWnFmQk9RN2Jzem5iN1FZZms1WlltSUs0V1dYZ0NEdllsSHhiWmdFaUxrWTlkdmNicUpHTXRjU2htL25PN21rTWQ5ZVVhVjk1RXU0UG5VVkNUZE1EUDBzeDZLNThvYlFJK3c1Mi9FQmRSZGNOY0xqUjN2ODFEdEo3dGovdDB4SDg0N1ozK0dFanA2ZHo4MnhyV3crME5wUW5HaXJPdU5ITkxvU0lUMVBhYld2VUE3UDBMNUVTQ0paZ3g2MmVia2x6bXlEMGJjUzZGckw1N2tQV2NrNjhKaGl5eHMwOTdpWm5pUTQwTnhzTDgxQUNUS3RuVzZ2cDhSN3l4QzIwWGJNblR0ZjFNZXlXN2lhTDQwTDFJT0pPWGQybUJBTWJmRkZoVm1RajF6WGFiZnV4S0I0SVBib3JscnRlM25SUFVEU2RMRmsyUDIyTzFlYUR0L2ZuR1dNRnVpWmZaUTZkZytQRU1kYVlKNzd4TXNNb2lpaVJjbzl5NExiUE9TTElwKzIyZlVvOVdHNW05elRLbUhsdXZFK3drM1BMWks0ZzVCZGZoVC9EdVhNb2pOMXBoQjFVL2pxTEFhRDR4UVlsdkhMR3c4Z1pHcEhsSHMyR1VZd3JpRG1VbDhrSXVDT01sdUtOWm16b0ZKbmZwVHhLd2NGTHNwenZ6SVJrdjJ4Qk13L2VhWXkxbmlDUGhwakp2dFFaUkdMSDRCZUNneHBybWJ3TVlubTZZNUFhd2JQcTFGdDMrS0lpRXAzMENDZUcrdyt5VENsWFUvQTNFRFlHTDF3dnNOc2xURG9RUmViNWtxZ0ZGTUVGMmszUTZIYlBKVVpoWkx0aHlWazBhWUJzOWxlRkozOXdCc3lXc0MrTklWQnppSHJXS2s2WGFHVmRneE9paDF2RSs0b05uVWxveEZrdXJ6VU0wOVViR2FreUZoanVVU1BKM2JiWjRRME95ZVNCczlUSzlBeUUwNjdHSW1MRDVKSXMveDcweWZPU1pkNWlNY3lyK1FFQ2xOdEVsL0Nxb01Md1crdFlTUlRqd3dpcTd6Z2tRN1ZPRjl2ckxHNWhoaytmQklLTlIya21qZ3grb3BTNW5YaDZIWjdHVnQ5NUlwdS9INU5ZLzU0cVBrNFZ2bWkyN0RkelF0S21UWVdmellBVk5rbEp1R2RPa3luUXF1d0dwZWNrWjVWQUZTeVEvaDBCMEczblFBVmNkRmwzb3VTZkd4TkV2U25KbGZtQTlyNDlnYkJzNGwvWjdOZHNBRjZudCt5KzZvVklob2FvcFk3T1hIcU5jTmpOWjVOOE5wRVBnbVVPd3lJUk1zd3ZBdG5qZW0wL3RRN0Jjai9GZzVxWk4wV0JENlFqOHNKRWpPUEc1YzVtdFlGK3NxLzFTTGFGZWdiV3lKRGhMRmJXQjVzL2R6RlRYdXBuWlhzdFplbEpLQkptMDdSMzFDN1dkb2RYSGU3UGR4Z1c0OGROeDFCMUg1VjNOa201WEZDSkRCSS9odWNOaWJQanJRU0JZUG9SQXFiUGR4b0Q2NVcyVXM1aU9DWmlIQmlQc2w5NHZrRlZLQkd3dUFkV09uVzRsL2lMUmlaR3ZwdnMyU1FOQmZKaXlGQmptVVpNaUkxekJybEpTV0dMbjJtTG5tOUJ6Wjh5YTFIK3ZkcTVjSElhamcyeUplcncyWXlRWDVhR2Z4ZURaWnZlV2NKYXh3M2VhTFpiRlU0ME5nT294cEU5K1l1SVZKVEx2TXN5eGcrOHJxZEx5OWRDZnZCUDdHVlNwNXhZOHJwTjFVTUFlR3VOT3dmMFFXV3JKYmc3emh3aUhYelB2cTNJVFZ5Q0FPcy9KbVJmRklPUFZlbUdZM3VKVnF4MGxrY05hRHc4L1RtOUsyRmtQcjNGUWNrRW5sZGd3ZVlMYXRncEhSRS9iaDZpQ3Q3MDZrdlJyK0VZcmloTFBHT1R2ZlJDdEFTL2tUNTRVWElDUmsveGZ5dzFCTExLR09CaTBaS3I0dFlXclpOT3RsNXZqbHJlYlp0TnRoU2RvY0VSNFN4Y2ZtSUtrYXR3SzFtQUlVSzVwT1p3eW8zN2pmeURSTzV0eFZoOW5rc242c1R1T1dkSDFGMGRkd0RMZlN5V0Z3RjdUSWo0UHB3alNKMGE4aHkzUm00amNZdEZWcjJVdWlZU2l6dHlucXZWS21LNVgySHJ6MHNlVjJiQ1A5Tjl1ZlZvd0pZdktqVU5EK01GQmJHY09qWkFzRmVjRVhRemJZd3JUWFBQaU16cXJWNnFKelpRRnhwQVRMZE1TbHJUMWNubnllVTdUcFl3ZWsxWXovSWNHYXZRRXlobTV5TTI1K0tOQjJucWpqeGRXMlQrZ3NFK1NGTEN6QmRTbkl0K2dTSTdEQlp3T0oxNFJWT3dZV0FXaVJUZ09udWFkVVA1Y1hXOEd0VExmNDhPSkliM3lEK0RFOHpTREl6UHhqMXAxeGJDVU5PbzM2SVRWbUFrRUpsSkR1V0plWWpRaVNBaEtTY0FIOEViYll0SzJxWWJRa1F3L0JvWk1YL3pqNFYzL2NuNnZpNEsrT0c2Z0V2NzJ1SkdOZUk3SCtWWkFSdXN4anorWGhpS0wrUjZRUjRrR2VHL1ArUVBhK3VyMXZnMkwzWGFPbXZsekwyb2JWYXlTR0xEQklhUEUwcTBrczFNbm1DNFcxZUNGb29Da1VjdktzZVZMUHFkSTdLb0txWHBqWElGcnFIVmNhZGl0Zm1td3Z6cXlrVnZDSXdjSmdwZHJjdFgxVlIvdGZsSTAxNDVwOVp3b25YTC93WkxTd0l5WEVvRW1zSXhaYkZsNElKNUFVSmllVGN1SFlzcGVTT3pxKzYxMmI1eVdCQWw0aHJybk10eWVzRHovS3dBVUxneHQ5L1JRbk9GRDRXekhpeFY5RjFPYnlsUVZvUVMzakRRekluMXZPYU9BV09GbTNJSHFGV0oxcjUxemtVK1dGRnhJUGYrVFY5Y1ZwdVI1ODRLbVQycExXSEVKbEJmdDNvOFhVTWxnZXRiZWNMN0FxNGs5WjN2VzJpdTZMNlFZekNhVXZTSjBOQlQwYjdyTXlDUExvcDFsaytGMmxST1BXZG04YnRmb0g1SlNLcFhzQ2h6VnMranBaVmMwT0xJS0tBOGlYTXJwYnN4U1ZtRHBDc3gzalRRYUx5eUM3eEZMWFFhNU1VUHlBNUpRQ0g2a0k4SE5OS2VocmFVaVpQeXYyVzZDNmQ4K3JQZzNLWEZBdTJacUlYbmhyWWZMdmIyRnN1V1NRS3FSYkdrUWlCZElBbDJiQy9tTUNoNGpKRmtUMW9oUnpORFFvWDhub3c4NVd0aHZubFhicGlReXh0WGtJaXVUOHRTcjkwYUgvVWRoUUFvVWEzT0Z0RTduMXR3M3QydDQzdlpzaHlKejBsYlE0ZWRReFI1VDA0aHV1aUp0VGdnaXYzR2w2amNtRGdVdkxrVzFOS0h0ZFJQYTRRUUJja0hLa0dJczNZWEthN3puV1FBRDU5a3dvV0JIODA3STZ0eWVNZi9MRU8yaFIzcTJtcVQ4enVSVW9qMEg2bm9PbWlmVVIxNFJqZFNGYmRTakdoYk9jeC9IYUhZSS8yN0xYL2k5UlZyTSt0Q0tIYUhUNG9paDFxNm1FZEVrYXpQVWh2cW45N1lmT1VTdG9YUGZxdXBhc2tkNDJQYWxWMHIwMEtqc2w5WGFWM1IxNEZsQ2VEYUhZdDFReUxpVWpSeGt0cFpCU3ExZFlTTkpVVmJ4Q3pJT3RvNjhNWHJCMngyUHYyaWVvazlHcCtQNGJMeTRGcS9Wdzl0bVFJNnBMZ3NzR01IWU54cW1JcFpxa2tTc3ZVYjlCc05JZlgvdk84RWRqS1lCVVpzRThwUFN1ckxONjh4KzZwa3Jtc2xmYXRQczVsRzVKVExhdkI4eXo3SjA5REo4L21HdnFWTDdaTmNKbGFZM3I4RVdFcTlJb2c2cVhGcStPYU5SVHBLSHlYN2hMYi9JUktMMkFhSllmS2dmcTFBU0FtNUJyK2hmejg2cE5FaHA0NnAySUxoeElXNm1oMmxCZk5NWXo2K054aklWblNhbnZLVmxOVzhObWE0cWpjNjZGdTJtTDNxUmM0Q3FmS0xoWmRSM3FLRVpZbG40bys1LzhsTmFIOFNCTDk4OG1mTVZjbm1aT2V2dXJYN1VyRW0xY1lQUzhXUWwvSnBvVG4yU1o1azNOVXlOZFVWM1F4aU1veTVCRXRHN0duaEgycTBaN1JxUTFGQm1yMVEwYVV4OWN6STE2ZEw3Ly9YWHVPN0NGNDcyQ3ROYm1FUituWG05NU5pT0loYzZVa3BmdWgzTDdJSlJaYzdvdllBYVBzN1ozSW15Ykp4Q1E2QkVMUWp2YjVTNHpTOFBMOG81Zm9NVmlmMSt0R3lCN2E0ck5KUnFWQ0MwTTF2dk1ObmY4UGliajY5b1Y2VGRhM09uZnhKMUdPNlFTcjU1cnBPc1kwanFwL1puWThueU9hMTBGdUJoY1dKaFQrczhOWHBYTVBORTlsNDNDbVQvQjZoK0xhOUhNVHczcXdHWGFadUNDMzRXeTJGakxab0pDTGw3SFhRaGlUcGpzK2ZQL0F5WWJXRk1sd05VU2hQdkVyRWY3VFRKSG1hS1RpN0VaZXVQL2hrK1huWklTbFhiQk5EUS9WTXZUdGhtK0Q5bDlrYzljdVZIKyt0RG8wK3ZuSXpTM3BWeXFuU1RlNkZPMnMxWmVlZVRnVjFxL3JNaVc5TERJOXBoaXhNQm8xVll5OHorWERVSmNIcmNVdHRZSC9udHl5eHM5aG5WMStKVlFoSlZmSHFYcEQxUTFCcllncG1lZElxb2tZV202aE0rL0dMNFFrVW8xZXdXWldWV0JDR3Z2aXV3RmRJU21GbU81UFhpcUZCWFUvN0NxTU8rdlJyc0x4bFQxSkpRMW1CYWk1V09kcGJqWHlFci9yNm51VmJuNTM1cTEzWHU5cVpZVEpJQ3NDRnJ5ZGIzeTQ3blVTOWlEaFZIYnB0bTV6eTlMd2FaOXdBaVFybjY0S3hrY2ZYZFhMRnd4SEk5b2xNTExjdlhwaW9QV3VRMmg4bjJoSGlLTUdPZjNaMStqVkpqYXcrNXh5TGhaWklCUHlTbFNHZGI0SWd0UzFjT0tVOXZSM1VRUUNwVHZYTW5GeWNJaFB0ZVBkMDVTTHIxMmwyN0pDV1ZLZm9PUUIwK2RrN2xhWE5iMm5qWG9HVnlHN3JpVU4zajFoZ3k1d2ZGL3hpOXNWaDh5U2htWjVJUVdQR0thVkt5YmNWVnRpb1JYT1dSRktDbUgrb05aUjk5NCtYWUg0c0F6azlxRHFRZVJDNnhRaURoQ3VmeVNsTVQzcGNEMGRHdnNzYUNOUm1sR1U5eHVDaEdjVU94aUQwVDVZbFVQaitVZm5mOG9SVkFVd2IrQTNWdWMzbHMwcnYzTVVMVDZXNXd1cnRBVTExbk5maXlMYXpzb3poVWRrakdpNnZTMThkM2JabWVSRGJHcFhwc3RBT2xpeTlUL0dlVENQWSt6YlpMbXVTeGxybGZ5NU02TWFDano3eE42a0txdUkzbGdYQUtZWXFvRGFkNlNiemQrSlNtbmw2L3FwNWpjbFJycGdteXZPOHRRajc1V3FCUm5IdWNUYktaU3VoNE50RkhiRmVTNEcva2x6c25ac1BmeTlPRXAyVi9TMFRyeWJzSXhFUGgzR2ZsYjl4VTE0Y0hvYUh2TWN4MTZnWWhFQ1l5U1BxN250NXVKeU9JUFA2cTZRMllYRkJ3czZ2NHJUclE4Q0NmeFlRaXU5MU1hbnNrZ2NkQUtYOUFUdzdUZjlMYVZQeStQbVd2dmFJZHNncmZEdCt0ZE5KTmM4WGZYUnpmRExwTjcxQkd4RkhHZ0x5Y2RONDc2WUlWdTBEMHovRkEzUmhSc1hobGc2MmhONjFSNUl5OFhDL1UvVGMvYlRZbzdxdHBabWxOWUZDM2JScXNDbjdaR21ydG9vaElaRE5QRmJlWHhxQmQ2eE40WStlOTJ6RWxhTC9BYkF1dFVYUHFyT1VLMDA3dHQwbU1jN2pXODFUWUtMTS9kbm5YN2pTU0JSYTV1Mmp5Z3I5OThQMmxoZzQzN29kVDlvYUFNQVgwazZuSmgrbStGRWFrRjBpM3NOeXF6SkFPQ2FHbnVFemwyQzYvckp3czc0UjFIUUlSdVV4cm9MUkk5VkhCazdtTXQweUZ3cFZzOUtEeDFHOGNnWFl0Wm5vRWFRZUtmQmI3d2xoaW1MMGJxNGlFa3ZFQXRWcVJaay9VZVBqM2I0TjNINDZhOFRmSy9UdlNlWEJDdFdRWmdqeSs0SHhDa3hWdTF1ZW5kYVo5Ny9BaTlSeWFqa0FWVmNEdXhQc2JXTVcrNjNDbkdEMmROWGNmTTI0dENLNXRMRXNGUFI1b0RqSURCbUJaS2lNQ2ZQbGRJVERYcW11M0wyMGtZK1IvZFBOMHREU2hMU1hwUlVXZjBySjdraUxzRitmNVZVbXBvd3VwYlY4cVJET0J6RERacmxJVUc3RTFad2VjOXJpRStqem5ONjUxTlBPSGh6dXNweFFUeDQ1TlU0c1B6Y0w5TFFiQURBZG92WHZkenNwN0FGY2VYNlA3TFVQMGhza0dhT3dOQlN6SXI1R0tRSDR1cFZ5ZS9DVUhWeXRCNkQ0Qk8wZUdEK2Fvei9pc2NocmhnNHhuNkNYODNrMVhOUHFONjQyNVdSZUZZTkkxb1h2YkZzSmpuZ21Cclp6ZFhnL1FRYXJuRjBTNGtXQUNha1B4UjQrQkRtOVFFODEvN1ZmelkxNExKWks5Zkk0QkwyY3grMzQwNEJQcHFIYVFoOFUvTklzcmdvdU91NHBsNTkwQXVjZ0hkMnpJWDNMT1JVcm5jSFFES21IWkdYbTY2akx2bFZHMU1UWHd3Wkxxdm1JSG1IR2RrbmxieG1tTkl2bWltejVlVkJXVml6ODZudi8wa0RzSEJBblVEZzdQNk5jUzlNN0oyTjhtVDFiU2dwZFQ0eFdVanI4a1k0RjZZOFUxYkREUENRbVhDSVl4L3oxalpLbXhkd1h4Skk5bzZnN3BJNmFrRllsbDRSTGdxYnovMDgvUDV1Umlka1AvYTFMblJIa1NJdkw0N0NuVk9xakdNbHZneTQyNjZlVndiY3lqaTVRdFlDSlUrbGdtbFlOcUJhQ1krUDNLTjNBWE5OeXZyUWRORVhMV2Jqc2JMbzgySnhnWHYzVTFOem9TQXd0d1RPSFlRT2hzZkU3eGNrV1lubXhsYytyRTJ6VU93elp6NVhvQVhVN0tHTmtwNlRJV1pwa29UVnBSSFNCWmxNYktLRkQzdENVMmtjdmFNclY4cGRqRVMwQVZOOGNLYU1IK2doOG5SaitIQUp6Y2xtdklPMGRDcDNidGVhZFBvZXJNV1QvOG0zVUtPSUFTVytoeVJaVldmZm5XV1Bpd2U3c284K3hEcktaY2M3dEtIMXljMWVqZjZnQjNOS0VqbFBhclJ6WkU5QU1ZcW10bjdMQWVtRE83WmordzlTbEtjc0FzTFgwbjh5ZEdGRXJhelQycFlHMFpRaVZ6Z0dMQUNtYUpDcnVWTUdmWFZhYVdxbWRoS0dES3c2dllQWWg0dlNxaWx6VEFCZXZCYk4rR3ZEWlNvT2FCYUJTUGo2YW40L1RJZVlGQXFiRXlTZjZhQ2xQME9LUk1CbDEvNlJQL2hYZlgvZjhTaVFQem5mdWk2SWxMdHlGTXRRNTFOR2wxTzVZS09xTzdtOGo5UGhmano4SUtZZEhrQXNpN1FwZGZZZHBZa2tFTEdnQXNiZnFDaGw0TVhYVng2UG9uQWtwUWJkS0djVldnR1dEODZHWmJDS1lDb1BKbG5ZaEdnQTE2bUp3a1JvTHRRYk5xdjBjYWpxWGZPM0ZwaEVRZ3ZwQStWRkRWVVZWdVphVG1SdWxLUUpGVXN4eGg4Z2o3S0FMM0Y2UFFRNmFWS2Rnc2NOZkNseFlkUDF1UXRuSzFjVmd6MzVrZU9PeXdSeHBnV25kV3k5TTgvSDRxU1orcFN0dktPVlZzbnRrWHlpK0p2NDhQTDFTL3FwRC9CVkgwMHJXRGJRWkVqKzhmQ0thUEJDT1NoK2NrRGZLSTM3TU05TThSbTZ1dmw2dnZUSGxnV3Z6VGpHbENya29zTEluTExoY0NWUXE1bGQvbDV0Yk1EYWUyamMxTmRmVWt1Qy9RVjVOQ2pzQVcreCtaVSs2YTVLOXdZeDZ2eVZrMTRIK0gxSE9BUmFEbnZvK3JzNkRqbVAwY0paWnlqbDZVbGU5eFAxc1M3NzZmMkJROUs5NnRzazhDWUlNY0Y4dlVYYWlFYi9oY3hXNzVLK1c0Wm5sVzFVODNjcEdYRkprcURKTk9KTE1hVTk5eEdXOC9iVE9qU2NjY3VEdlB4bEpJQjZWbW1PbzU3U1BHdmRyN09VM0FRZlNzd0NuSEo5RkpkOU50TlZwdnR3YXllTmlrM0NxbVdkZmNqT25naVlVeVVHMmRPczNMMjFXbnUwUi9QaFFiTEFUdGxRR2xQc0FNUDlHYzZMRG9PU3YwY3RKTmh1UUV6VTZUNXh2Z2F3bHI1dFdIbHUvSmxFb2V5bjU4VG54YWVYQjdJeFpaSTBPZVRzOWtHYjgxaEN1U3NxbWhiVi9ESUhuRUs1TUNLZCtpbnQ1T000ZVlqRXAxdGhRaWl0N281S2pKQTY5aXJoUkQvSjFWZXhjNDAvMHBsWDZyd2hoWk4xS0NOejdqVVBJam9DcGlnV1dBNkZRSWFVbDFha3BlVlpKa1ZzaG1ueFRnZUxqcm9EWEMweE02dGFLN1M3VUFMU25mcDNTdU9wSXVURkl6Z1YvV01yMmY0WjV5czFtZW9iUDBQOGZsdlpwQ1NrOXlZVjBROG9rUlU2MEM3bW5uK3FkZ0ZZR1hCNEhGSSs3eDluS1NkQktMd0VmZVFmcFhobXVseVdxck85WXpxZjRHNUx5U2NaWFdxVTQxRTJPcmNVVDF0bkR5N2dRZUpsL1dWY1ZpdlpycUF0QmNhUktpL3NNV1YrUFA3MldEUGx6cTl6dDc4MWpwVDdOQXB4ekhWMURRUGt3czg2U0hwOHR1WU1xQnBVS084aVhZT2g0V3EvSTZvR1RvYkVXbCtINExqVTFiUVpNbWFaMGMvZ2lGeGszYlVFdVI4aEdNQkhGeDFlendUSitqRGNNcC9WQmVnTTl3L05hSUdhdVlYa0RTTWdRTU5sZVlZMlo0c3hodDdIbmNPYkUrcEpERGJ1eUZDZzlyY1JydFhLeFdsQ3ZhNXdRems0WnN3MTROeGtIMzhycHlHa1A1aXRRT0RmZjIyY3JLL0tjczJuM2NWalpqbFZJN2s3ZE02YmpLU1BmSUQwa01wNEJZaCtpbHhqTXY1OWI1WTZZSERDVEV6NHFpcW5jY0U5VkJUSmxWL0JUd0xXS3lQNkUxV3BkT0Q0d25ydzZkVzFzWEx4MkR3OGZENVlqczNNVWJhQk1nckpqWFhzQUpwOFRoaUF3Y3djQStuNjIreGhnK1c2WjErT0FjaGp1WGhaZUMxS1Z3TlNYZkdaRk1WQkpaSDAxYmtVZTJ6U2FtalphRkIrWEVXN3JHZ1NyQTQyR3NHVzA1aHcwV3p3RmxpV3ZrRHQxbmVkREZmVUxLaVR0b0ttOUI1bFlaNTFLSXg3OUJ1dG05MXZ4c1k3bFhMZ2JsVFpyMGR2dGFsV09TTWFIT0JwOTNOSkhDV2RUS2ZqQm55QkV3N3d3ZUxTMnZzWnBJclFNVlBrdmhiVjcrR0QvTml3ckJBZTFHL2F2MjBOMmtCMEFWSXYxSjErVm5SNVE3M1VGN3Q5SFQyekdVaEQ2MnBwc1oxdTh3RjYvME4ra0NsV3VKS1gwREIrdTRHTDQ1SU4vOVFET3dLYngzOVcva21DUFFud0VLbVhXVkpCRmtpSm9nQzhnWFJWY2h1TldWOXVmcjNLWncxOHZ1Z080QW1PZjhKaHhydU5UY2ZvdFc3eUdMZEhSL01MZHFLL055djN6Mm5DWGZ2TVduMkh6UXUyZU1XTWMvbGEwbnErQVZwVGErYW5hL3hpOFlQTEJYcW9jTHRjTkxJUXdKZ3dadjlkeFlDU3kvRUJySmRMTy9aaW51R2ovNnkvdkhyNzhvajJLelM2c3JTcXFwQU9GQTVOOGlnRlBNVkpVRlFpQ0pDUWVKTmpVU3NNbUpTeG1TWVJ5b2pyWWp2SklPb0k4K29ycWRHMHpidTlhTWRqZGNqR25ES2QrUURjZ0xrbEFoOEhIelJuN3FyYWJkcTNrc3d0RHdRSk1uNTc2YWRMTE1OaHpqaWZvQWZ4ek14blJibVdMc0VSSVBhSVovaUsycERwWUdEV2YycllvcTdhK0lUdGRuVkZ4QmlkanRsdWt3Y1drdk9tWmYyV3E2TGRndkJRdjZiMjVLUFpZWWpQRGxuV1d5WG94eUdZZDhqaDJGTGZudm0zRHhOUjhlY09tUmUwY2tlZXZCMUhEWmIzOEhoRlR4OWFwMzV2dS9RejFJclkyUTNlZ2JMZmwxUWhtOFZoOUJKU0ZoMEx0R3FRNDduZ1p3dWNzZTdNZnhzeDJUZDllWEZvZThlUnFXT2VDMldjL05EWVhLNHVUV3JJNmRFVWJHbHF6aW1tTDJLRXFVSHpSclZFYyt1bzg4VGVwdTlHcGFpYWl0L1Y4WWIxRjNwVlRxK2VBc0lXY2JsZnZaRzRTVnpXUVV3Q1VrZkVreS80a0k0NnNYU25TdmgvaXFwdnNEMWZVdWc5ME9idlYzNk5aZUhNQWFVc0l5R1dRM1ViOVIyNnIweVVibzQ3ZkplcitQanQvTTU1MDE2Uy8vRzZyWkNhcEo3cGNaRld1L081N3RDUDNWVVNjWHRSNkNTSGVrSEV5djh2OEhBaE41SVNlS0U1bFhzVGtmOHMwWHZpT0p1Yk1aZW5oa1VubGRWTDE2dkhVOUM5WTZMZk16VHpEd3h1cmZrbkYvV0UyOFlaVWtQNWxWUkRHT1RPanhyVmpNS2pWR2wzTzR2UXNmdnIyK0NHdldNakIwSjEzN0cwUzlvN09mTUhtM3l1ck1wSnN0Rm44ZGZhVDZlUUR3aUVZazhINS8vakcwbGFaYVBsN2hNcS9jalByS2NWM1RkNkpUWnRqRWs2TXl6Z2lNNVN3ZU9uU1ZoRkh4NEVWamJuTDVTUWQ0WWpUWTBxZHlHNmN2aFVIY1pnZ21EQ1NOQUtTcmZDcnNpRnZPU2x4YnJqVG8yalgvRWlSOTBqMUxhM3NYK1NLcUVDZ0dmNnBDTG9LbzV6ZVRieWdlVlBBbU9NNFlCUmhoYWo3aXVXMWpIOHJIVjNicUxUNWQwajlUVDE1WFJFOXE4TkJTanVUWlc3ZGtKNjFpQzE5VXdLRVo1eS8wNWhKNXl6bklZSEZaZjNjMitJTDBPc25HZks3Y3UyTzM1aHN0blpFZFZvREgvMTl3MnNuWmFoZ0ZadlBhZU1RNGkzYm5ZTVNsanBIWDdCZlYvZlZVY1lzTzhXYmxzUXFaZXV1SDhFb1NRRDFyajYwczQvejdKdE9Vc0JaQkQxazJEaHMwZkhZWE5MS0gwbHFrUWZiTGRJLzg5VXJjbFRSS1FraURodW9IVG4yTnIwN3hNSmZZSWpKa1h4OTRsblB5VzFZQVhRcFRZdWthajg5TzB1NDY2K0tMcWNKc2h5Ty9EeFFKUWtLZUdTWmJOOVZZbC9pWjlOcG5zRHNYN0s3UVp0WmNsWENncWpWMmpWZ0lrSktpZEwwMm1yMVNEZGluNGJhaWtqcDloZXVjYWZ4RVVoVXFNNExaL01BTWZhR0lhekUrYzZROUE2eHJZbVBka3dHcW1QQ0V4Z3hYNm5RelpmVnJsallMUHhKZmtBeHFWUWcyWXNYK1lvTDE3OU5qalRLTStaYlpOeW95Qi9yMTZZcDZzd3JQaWhVVWVqUUZYU0QvOVVJTG1OeHF3RDlGS1JVOWIxNTBSby9hSjJkLzdkZHpVZmwyZmVYWThYZlBUY1hyQ1dhWFFNcmZzOU50bU0yS1BOZTNmRmNpelE1K3VicHJmUnF0SExzdDJDb1JmQkZiR2tvV0NJeG9PMlRhK2Z3YUMzZFBXOTVyWUIxa09lU2VVaXBlQzgyYTltQ2pIWHVsVHIzS2N3aFdiaXE4M0thK3NHYkxOUGpSano2bkRHcW9KTDlZNTcxSHNDVEZMSkcwSDRKL1pYcE4yM21aZllOTGZ3aUo4RnB1TkVyTlg1b2tOK1YwNzhuNitTNVJTMTZhcVZoUzBsa1dDb0I4RU1vOTJUR3RaajZkV1pDMHZNSVVvM1JpcWVqNDI0a2VlYVZlREUxOVNoRnJNVlY3M1Nxc2pPVzhRUTcyOU5NY3dzL3pDY1BKdzAvU1VWZjFlaU1sdzNrZFVYRzNNNGw3QmM0ay8rTzZBVFVoeGJvSFhVeStRc0VDMWxtM1ZDQWZsTDV3QkE2TVNGT3Y0bGF6bEJ5eExXQ255NmUrSitQbWFkdkpWN1V0Z1MzWGJKZG51SkppeFhxMXVuV1FER0NjRTR5ZStka2F0cFVpa2pJa1M4bFRwczZEN3l0SmEvR0Y3bTUwQkZHelBSek1oSCtkVlRGMldjVnVSWnliUCt6VElaSEtZM1RyME4yUmc3NEUwQWxsMVJkTzcwRHBsL091QytJdnFqSTFhMXVIbjFzMWVZK05leElOaHNlaGg4bi9mNjIzV3MxKzVjYmNiZXVUb3Z5MlhocGV5ZEt2SjMxOFlrUFk0eVhxWkRFcjNObkk2ODlFcnZmSW9nM1RWbEcrMVJNamQ0UkVNbkNlNk1MTTBlMHp1MVo2MzdJT0xKR2k3dTExU2JVNmFiM3RKMk1zMGxIVys2M21yWC9ZSkplTmthZVRwRjY3YWI5cEVDdVNQMlVxRWR0WnVrYkVqWXI0MnRDUlJnMlJhRy9ILzVoWHoxUlhjTlArdS9kMTNabTVRc1YxUFBxRVVXYlhvYUJLdzhwaU15UitOd0JqS1BvSEtCY2FWUFJkbEhOMmJZNUtRbmZKRVpmTk1ydnl0SGVUNWZNRFB3Nmx1eVJocWdKaENnalV3ZnZMTG9zLzFOVlFkZXN2SndlSmpueEc0dGllbjFTMlc3U2xZWVV0ci9NNWtoTDIyU0Q1b05PeXVkY2NibWU5TEsyYnI4N1hqbXMyOWlERis5UFVWM09DZjhIaHRVMmJkWlVOS1M1OU02V2svTXcvVG05L0o2TW1qTUVvem1KbnBMckRDa0lVZG9iSDF6YysxSU10dHVkWGsyemhtVWJ2Uk9FT2xVUE13d3p6NmZZTzBORjI4SU1kQm1YYy92U2FpeG8zU3puRElTYUM1dmx2LzNLem40MEtlUzN2RG5nNlNQVXFTaXY1dHpxMWJiVW9tMmxrSVd4aTJXREVaczdWWUF3K1QzZ2JnNXFmK0ExZnNKTzVyQmZPUFBvN3grQ1I1Z1RjS1pJZmZvQWkrM2dNUFdTUk41M3ZXSUpaenJoWTdIeTRsdkp3anA5dTRFTUt3MkQwUXJkdTZSdXlLQ3dLblNoYUlWVk52eFloMVBidy94WExUUy80b3RKOXB0cUVlbzR6R0RxZGVXMkh0WXVHSkh2WGY1UVUza1ZuUGdqSjl3UmtPRUVHUzNQV2ZBdDBUNmtxelBtSTRGYVQ2a1hySCt2ZnF5UFJPSFhVQmRDSnlYU005R2VYQktSakx0YUtGdVIwZ0VLSkFpWVRPK2RPcjhkT1lqdngyU0VZemtYR1RjR3RDcmxxMXhZZVFPS25wWW9xZ3hzWVNuTGFILzBkS2sxYXB3YUJ5RWlzMkgybmoxQ1N3bDZPZHVKREFrRkFzR1F4OUVySkI2d0xFclZnR29BZ0dpYlF3RHZ0dTFYZVZhZ1FSS24rNnZwL3NkOFpxeDduem9PVWcycWVzSDFubjUzLzB5VEJTUkFNS091YUpTM2NUQnEvRVByQjh2THZTUDV1SjArdkxxMng3K216S21HMy9YRGtKWlpPWkptbGo0Rmd6b3cyWWs2YkwwVWh6L283a29wZjZLdFNlaFMrd3JWdDQxZkJheE11Rkh2OCs3dUNEaHJiS2svNjd1aXdraFRSYzZtVlh0US8ySlZzRWd1NjFWa0RQWExXaFJkYVZRd3BtM3hNSnVSK2lVVk1USENWODVOZnAvRWxFMENqUFRNOE9TVHpCVVlOSXlQU29zaS9oQ01RQ3dXd1BSRllnOHJ1Ykx1cVp4UVhuVytSRnJpaFM3OTZ5Q2pGVmZuVi9rc3J4aXByQ2g4TzZiN1RZUUsyaWloazgxZlp2b1pxS29mSDNrWmFyaUwyS3lvdkZncE52LzIrVTZ4MEpXbmY4UENlUWtQYkkvOS95SFMxT2p6UVAvMzhrK3gyV1Z0bWZrTWw4M2w5YzJrN215YWdNTXNxZWFhUTJWUGRHbmZhOFFVTDB3c05yYUJWbDVaQmpmdVJsenA5WDk1QU1Ua0hIUTV4K3pOcVVvajBxdjY3M0NiTlQ4cVh4SDhnQWtxVjRRSTFlKzZiTE56MmJlSDBZSi9HRE4yM2R2dGlMTzNwanFaVTg1aHZYN2lrYk1CbkNYbzN2eElsK0FuTEtiN0dGQzd3TXFFNjJDY0JZT1ZYY0NhSVlZL0trTlUvdTljOUlETldzSDlDVXVJUjhvaEJBaDJBOEZWQjhiVXVwZWVVVlVRVHI2UHJlRkFJTHB3eThibUg0d01TTWFTSzc4L0dWdU9GWW1ONk1hTlQvbGhQeDNXclBOUnNtc0c1WDFxYkI3aXNGZ2d1eGVZY1FyR1B6YlZnOEp2UzA3ckw3WFF3UHZBODhBbHBvdWpYanE5YkRFbUswUTZjK3F5cXAvdjZHSWJvcXErMnJVSE9PK0l5Rmo5RFVxUjVhdFZYem81ZGpNN1M5cGcyQmdQQkRMdjNMWHRpNmNudWUrL2E3RmltMjdrR0U4bnNlNURGWUk4ajdkY3V4Sng1ZmU5ODBkS21rdHhLL0ZjM1BQKy9JOUsyVzA1T25ySW1kVmNHS3dweEhGUnBIZW1Tckl5TEFySE9Bc3p1bHRVdGtaTWVVcm5YeXBJUmhKUGErQ3k0dkoxUFBxL2hWTWtaZ0RkSjVGV1lTVjZDZnhXak9tbnhiaERWckpzbmZTdS9HK0ZWRklxMVRUbmhJVyswZjN1aHlucG9KbTcxdDUzaTZEZkRtSGQrLzNoemEvVm54clBZTUljTSsxY052N2FYTXRyR1lZNW5xL0R3cmp0eHFnUGRQSy9DVlUzNDM5eVYzUmtVNEl5TlFoOEo2aUQ3RmNkTkF6QlQzd04yUlEySll2bndhWVo3T0Fubnd5Y2pNeTBzaGgwWUtnQ001Q1E5aE11ZjFmQVBqc2JqYUtlUDYxbmlIdFI5cDdNOElGelE5RW5EaGdxZDd5eWhNV1FZaE9MMnhyTmNmM2tUYlgwaDJHajdUc0FoRUNZWWRDdTBsbFhWUUxsakRKN1h5ajVKV1hrQklreFNvaHcrZG13dEVIbmZWT0x5WFdyRWRFcUhnTmdSbTJMZHZ5NThObWNGYVljYVpSTlk3eVVSWEdwaUVZM2U2VlhRV3BTMjVpaXZlKzFGeDc3V1J3WDN0V29JOVF5dVc1b1RwMkZndGtmR0Z3Y3lERjRuSFZrWWtjaXBPbGYwekx3REZLODlPN2dMK2ZTR0dXdEFkOXdZNWg4QUl5TG5RTzIvN2NFelpyN0dwTjVpYTNTaGtQNGdRTWdld1paTWtKWmVhamh0eWZjQnpMelduY0M3dnFLQlR2ZmVSdDRsL1BSSGlkWldidlVjWDJQTHgrM3gyejU2cVU5bld0Q1JNa1dDeVlybEl6Nm13THpkODc0N1pVUHlnUjV4MHNzaWZPNTFHZ2pSOTE2YXRoYTZLREhOMDdLQzBpdklGQ1p5ZFhoTFBNMEF5U2diS3RMZ1FXVzh4aW0zWkphS1JjTHZGN0dQRmFSaFBmL1hBa0tkNk14WXJ3T05PaWFRWWtVM1dsd2ZHd0tWS3lkcHVmVjlFaDZ5NHlWUklEbFhLUVVUTG5NeDgzUk8ydHJ6eTE4ek5vK0VKeWdWY3FkT0pITGU0ejdHRVBQdXYwbGJ3MlRBMSs0L2diUVJ6Y2JqeXNINkhoNmtzKzBIc0tOL1NUSmFDazBLUExKUE5DRTlPTTFueDBWY1dQQWxhdmlMUlVPQ2ZtS2tHSGVvdzl5RWNtdlBJYU1QTlRJdmtKT2FBZGs4Sy9QbUdlc1VTL0RHR2t2WVBpa1JrUHY0VFAwczRTM1g4Z2xxNEt1WmpHUDVmajBBZ0RaL0pOUFFuRlFOR1hsakxGNUt4cTZtMzBLTWg2OEV5cGFFZnp1V2JSZ3FTZW4zbm5PRFA4SE1nWFdrRG5ZVnNiOG9IK05meldMaWVzZUc5MnhHTlQyTGdLTytka0h0cWtMSXc0N0lSQU80UkN2NnA0ZW40dzIwWHY0VEh6UTl2Q3Nabi9aMWozZ0FPb0tzRDc5SUhzNHlYTXNhNXBXakxXeUJXL1hCMjJmV0ZnOGRWa0xLbG9KYW9seFMrT2xuNXdwcnZ3MDdUUHV6Ykl4cTU0dkRGNVB5di92QmJLTmNSZ3IzS1JiejU2M09RUW1KZ3lBQkZieVZzalBLMHg2aHpVbmwyd0hlNmVLT1MzWmI5dEVpaHVmYlMrcTV4eGh2dklTbEpXWkoxaEwycnlVNmdGSmVSTHF0YzNMUHhZM2x5QXM0WlNFZWczcUpNekVUcGY3bUZQYndtTWZ1d1NEWFpKY1dNZlE0SzRlWXI4UDhsc2ZCOVdMMU1NQUJJRlVHU01IZTJXWlhQMGU2dnY4Zmg4VFJFQUdyRjcwN1JXUStZdmdnNHRkTWtuZGFSNTNwTCtLTVJqbnoxK2VqNXI4ZUFLMW9sc2lQNDhMSkt0ZHR4S3pxVkFHZCthVXFpQ2pEd0thVjBNRDlKQkpxdWF0YUhtVnFDVTdRZHAyZ2k2TmoxcVVXQ2F2L3JNOEs5TSt5Uk0wZXNwWEpYK3NYVnlaNWtoczRVL05nZExzVGJVUXJKNFBzbHhyQTExTnNWdGFjQ2dpcWIxQ1IreE5SVDFPRjUvRmZScm1MS2VsWkhENWJpWllHS29Eb2lIMnNjRGdTOXMzR2VjR2czMXpYK2hkWHlZdXlpY0trb3ZlTFNwajNEWXpRT3JjQUNOOXBIalNRUlBTajRTczZLQXhaSkJZR3YxaFlFTTA1dlN2TXhlMXFGeVBKaXE2V2UxeEVGN0NQeDcvdjJNaHhvSEgvYUtoTmJwajMvUlN2YUN6TUt0eTU5QUxMUFo3MFNiT0dWWG4xM3l5MVppYTJsdDRQeE5pUTRYNTFjSEJaKzIwdXdzb0tuVVJaWjNUbHkzTWdzSi8xOFNDcHo2V2IxQ3o5ZGpGUVNpM3l6SHpWMEdkdDBxZnkyWWZlSTloaEJ5TUkwZUNxMnMvMnJzdmdSVUlCYlVETVA4VVZHVmlGQm5JK3VLd0FCQ3ZRZVhyYlRENlo5b1lsRyt0c3NRci9LSlF4ckVRdzI2OUpDQlhuUUo1a3pRVTRNUkl0bGpmYVNPV2FCallHMmNqRDVKak8yNXZZaFRERXg3Tm40dUFzcHBEYkZCRXhmUlZXMFg2alJEQ2FhSjd1OVlqc0FYVzM5emZYcDBjN2U2c3JTek1KcVBvNHN6T3QzbW5LSk8zWW5nblZST1hkMWtqcTBvaWhER0Z3SlA0RktsSDVJUnBueG0xeVhXS3hxelpRTlBZcE5BYXU3TXl3UGptcTRPOXhmbkJ6bkNyQ2VWRHlRTGYwT1IrVFRpS1J5ZWw1OTI1UUxXclJhcC92a0p5L2RsZEVvMnRmb01icnF6OTRPOCtnVHFvdWFsM2NOVHNvdDc2aU5MRUZCa2w1VEx0RVdqWFlDWVRpTXduU0lrVGtBdldGNS8rYWQ1OUNYZ3V3eHQ5UXEzK2lOem5QaTdTVnBXemhqTUdEUVNtWWtObXBMM0RVQi8zdVlJYjhZOG5tOVlOOU4xZTcyek5UZnRkRUxwZFhSb2EvRGwvVmlJcFVnTURodUhFbFJjVEo1c0lrYXZQNDN4UldnTEIxd0MrM2h1UlNMTU5MWGFWd0V6eTYxMWI1cVNHS0x5OUx5RUdXYU5DVjlkK0xPKytCR2Fnc0Nnakt0QU9xajlBcEIvYlFYMWpYQ0FSWEJIZ2xYMDlCdG5LalVRWmVNb281bU9vTmFTb2FUQXN4K0Z2bE1yNk4vUkFpRStOSUc1L2xKcEd0cG5FemdBZVp5V0JGQWFDM0lLbXR2YVkwVzdHZngrWVlpNjJ4ZUFwVTdHb25kZ0FFZzU0NTVQWU1NcVVLVGEvNUlrUUdHQjljUTJNU281QlRwU0VQdnVLbU8xeXdCTGNJa0p5RVUzQlZ0eEtRRGt5UTc2RGxKcENCQk1qc01EWll3c2JTZ0IxcVU4eWV5a1VqSEJVU3RrS3Z4MWxMMU1wSUVFSFJJb1h2MjIzcG9tTHFrMWdOckQxUlFvWXpYRU1LZ0JINFBEbmFVcnphZXlJUWZ4dXVzMlV2SjJZRUJTSUZGZ2d2cldrUTRoTGVBY2lGK1lwU2lXc1lLb1U4VW1pKzBLWkFxSEN3SWpvK0h6T0VPbzZmaEpVVHlNOUVvYXYyWGdEN0loblQ1Y1hoY3UrMjdRMEMxekhGZ282ZGcxd0Q3aVZ4ejVXU3FCU3FlUU82dVpxSWlCQjRnR1Ivcy9xNnNnVDVEeVhwWHdoVTNGb1kxZlBsWWdNSnVvN0RJbUdPMzM1ZkcxbFlhN2ZGUzczMnp6MW5PM2tML21sWHd2OWVtZ3JmbHhZTUltbFVIME0xRFhmelYvRHpJdlJ3VnFBVHhPUk5wSFNRcG9WWjR3OGlzZEZCcjNBUURGT0w2aTlZNkZVSUlIWlowTzFaVGkweDR4SUJRTHo0VnkrbU4rdVdwWUhuanpvVmF4d3BXVEJYRElRSHE5aFNqVjJvQ2hsQmtmS2ptUTcxSElxeERaTGhvemtLSkk5WU9Xck4wdGZwWnRWR3Z1dXJrNGREM2kveGlpT0VwMFRNT0pad0tBWllGOUJBVk5NNFE0MW0wTk5jejMxU0RCSWZvWFluVkY3NXY1TkVSNzA2V1dZaERERXNMNHVzcE9IdEd5S2ZCcUxNVWlMcUMwRlp6RkVvN3FSRzBBaHBKd0VKV1p1b2lDVTNKNk1oV3A5a1FpRzA0eEJ0bnVkbldVYlJkRFdoQnVYaWxtZ1RsUjR5K2lCUUIvblh4SVQrTWRqQTNHQlMzYnVYelBYcHRWODJWR2pCT1l1LzVRUUV1ZU1oM29rZ2NoZldKZTBUWmd0RkNpYUFlb25XY0R1UEZ4S1BpektCQmozZnVWUzdYVGRKdEhDRUN1TzRiRW1NRnZNbzVuUW5HdkRqcDI0OTBiNmRoaDZWVUhzV0NxKzZnc0ZMZHZnZU5mMmNWU2tKbU1PVDN4THZyNUljTXpvOHhtREZyR1V1WTZoOFozWG9RWVlLZUJMaUtvOGdvaU9uU3A2cTJtQVpyNmZYMGlvcUVlcTZYTTkvaXJoTzBqTmk2Um1ObHQ5SXQ2dXVNb01Ba3RpVVV4NmtOeVlGRVlNNG1YaWVveHNZb2hOZGF3NmdvK3FHamY3SjQ4eDd0OGRybzVXUVZqVmVYTGlpeGszZWJPVzRJZXl5cHFQODlvZ2wxcHliU0JPRnpaSUtEbVZTQTFUWHZVcEFHUmd2cFhVVXFjNnpFZHE3T0t0ZkpmVHZQbjYyb3FlSmtWRjR6SVdldFk5cW84eHg0Q1VjMXVOR0poTWRVN0lLSm5sSlNMbTA5UlZwLzAySzlJR3JPSkQ1SGhOVjBoaWphclBUdTZzMTg3V1RaWjRDMVh1TlZ2eUJpK0RjWHVaNEFGcTdnaElwSkRVRlZTdzBNdm9xT3V2NVJJM0hlOHdPTCtMMUl1M2JHUndaU2JBbmJYaFFMZzRPOEF4T05KVWRwL3dYVSszZUFITTJWbmhBa3BZd1lTeE8vVDg1Z3pEU3pqUlJ3dERJaTBPdGt0Mi9Gb1hUMmxjUjVZZDV3UXJ5TVVzSSt1RWgyU1RoVFR6V2FGMnEvSldUZ000MU5ZRkxNSEhUL3JkMUk0OXlNTld3TVBFcDZ6WmMzRWUvSlVjREt4ZlNaRktXT29qcDhoaEF5WXNUVHRlYzZ6SFZBMXpycCtpMWtIRkMzTEJ3bUJ5cHhOVFhob0RHRlV5QnQwbkh4N3NyNi9rSDlMOXRqeFlkOFFnMjc0OEJ6d3Ywck43cE9oNnBpVWVTUWpyZUtNWW8waDFWK1pjOEl4U0J1WDg3TWNXYW9kQ2U0ZFJBTXBwUG5odlo3VkkreXg5ZXNqdkNKYzlLVDRhbDY4TFR1MDZJNTBiSlVBdG1hWkJLVE04eGJUekFCR2NUNHAwcmNHOStBOTVoTlQ2Um1SV21GNzE0bG40bzZ3b0QvUlp4QjkzZDJER3pNQWZmYm5Vb3puSkh3Z05SazFhWDRiSm5peDN3dkh0TWVnM3hWTEtreDV6SHVUMVJkb3phV0UwbHpIWUFQZ0ZmdmJtK3ZoZ2IzdHo3VDFILzRnUDA1Y1RlZG1tdU5BSWVPUk1UQ1ZZZmFTd3A4YUxqM2oyMDdkeEdrNzlzVDMyWmRJdDdGMFRvYlBJZEd0eFQyWXJ2RE5QMG9iMUV5anBtK2lCUFlsOExSK1dFOC9NaUhNWHUzV1JPQXVwajh1OG5Ockl2YjM3SWtIK1lWbFM0c1laSWdYTmNmRWxhVC9yN2pXU2MzOHQ3R09ES2E0WVRkT1dKQ0dZS3NEdlhQL3gwOVhGUnI3Ny9Dci8wS2Ezd1BQTDg3U2dkUjduS3JQT0E0YkpwaDJPTlNrYStoanVBL2VpV3J5RjBTL0JNYWdCbklYVEZ0M1NqNU5aN0l4QkxUVWkraTdLOWRORUs3WTV4b29iNFV6VUhBVzV2YVJHandVa1RWdGxBV2ZsMDVkcks5TlJWK2ozMnpYTjdJVTh5SUdPMy9UQTNadTZWWW5vMVVWQ1dSUEJLV09mbVBBMlhUYlB2SVRTUEZGQTBLUlYzdy92NXZVV1ZBK2JJZzV0VTJweTRCSHYxaFlpYXdLUkUzbFNhTU9kUlcyYnI5VSsvRXM1NS9NT2ZGV0o4am9qR2NmUVRmRm1iOGRzSnZSTnlhNlZTNGlaVHp6a1NlWnJsVGZRbDd0NkdYZzRDMmMrNzBxMVNIZzNiVjkrK21TdVQzUGFzMVE0VG5QNStUa3RNemU2SnhBa2t4aGszSUNvejRBc3pEbWFZS2RqdUFOOWJmV1F4bXY1RjgrMk5pYWoyaWtjZzJPN1RVSEx5WThDY251N2VOMWdrdTlERHRURE5UcFV1RmFmbHJEZFhwTWxJazJRMHFCUEl5M25FTWN0M3ZwR2JGZ2Z3RjdwTjE4ZTdxRm5lUHA0YnRCcE52cXVxYk9rU01UblVLYlVYRFIrU08wV2ZlOWF3WDZqSEFvTEU3ZlJnQVZtSlJuN0orYVQwakZFV2w5TEt3Z1l5Mjg4YWdwVkVaODR0cTdBY3o3UDF4TENNckdqVzZMaU9WUTA4bzZwUXRGWTBKcUE3YXFabU5uS2tJaWkyTFN5Y1Zpb2o4QmsramFNUW53RURycGtkblBubTNXZWVJNGlnMWUvei9XeUxmSGFUa2twQnhtWkttbWJ1cDVDd2ZwUk9tbU5Ta25oOGNWYUdKS2VLbGhiNlhDSFhjbmkwRGFFakFkOGNPazZ6cWl3N0FRRzY2TjZRWVZIb0ZXZG5wdDgrRVdJWlo4NmV4bmtXNmRyYytyZ2h6dFNkWCt1ZFI2dTR0WGJjekoxUUVTdEM3NUg4Y05WclRuMExHYjYvTFBMaTRNOTdZTWkvOE03OXVYbWNoajZxenVpOXBlcktBNzZteC8wSEtFT003TndOVXM4Y2IyaVRSa3hxbnpBVzBHUnBpMGY2WEdtMzM0YVhYaDdIZjZpZnY2aktQMHd1dXhGZlhKcGNhS0hGb1R5bERvd0J4d3pOOTFGQ3RtclNyUnlLMWJDMXlyWEE3ZFA4T25HM0xUZjYwTGxVQmZaaWFuTGZieUtWNkFOWExpaDdVTEpENENibUR1Z3lNakNkZ0hvdjhEZy9DMlc5TFhFUlhaSlQvNVpYc1VUdFpXN0VranNRTmJIVUJtSU9RMnpxamFxYkVvQWI4UGJTVzZOWTFzU0tINFJCSkFhQ0FDd0FMZ2FUcmFTM0xrRjRpYVVLblk1T0VQYkkzQm03U09KVElEc3puUXk2QjNOSTdpRDBHU0dMTERZU2Uwd3FNRnhFS3dQRFZOYkJIZFE0a2ozbEtyWTlDeU1UcU9GZWdZbDFDcWVwVldaS0hQdFNxM2ZyUElrREF6OVZnTzNTeTJUTU5pTlp5cmZZUVllQzVjRndTTkVEYzU1ZXJGZy8xcXBKV21KRzlQdy9HVlR4YUY3dlI2V2NmbmNJNlU1VW1EQU11eDJBT1BFN0F0cVd6RUFHeUVyVGdwZVhuWTd2dDVnNFZ6bWNlbUZpNkpEbEFFVE96M25QSmhQOFBPSWdTZExiaDBjdEZoS3hPTXo4bE1zQ3AwOXlpOVpTNHgrUmZmdTlOcjh4WFpWbDJtNE5NV0tpN3g0R1RTc2dBYWpqYXN6aG5uT0pQVkJSaW1iVVdUVnVhRUhzVmdHOXVnTS82cENLN0dJNE5tZVVRSk50clV1V1RaUnlzNVdjUWR6VlpkcVY1M09ZYjRQdGQ0U3RSU2dQZGo3ZHdkZDRXSy9hNm84RGYyRnhoYzg1dU9HMVZaT3NrMmtaVmNGaVU0Y0RNNENoTmNWeWhLdUIyU0F3UnFaMENiZm1jOEpCU3hSNnlrQjgyNUhiYU0wQ0JNbHFkV0g4em5zU2sxR0dNQ2JVcjU4elZPV25FZXoyRVV3ZC9zU1ZCck5RMUYrVWtVTWIrcEdYbFFCOXlpK0pDMTVKVkJvWGI5Zk5jeG12TVI5M3IvbTMrWFE1ZkE2OGhGd0FYcGVVZ2MxVVp1RlQwUElTZjBZckN0V3lMUjVxc3BwRHluWi9UazM1a0lsUkdOUVZPVnFEaENoRFBGQVlHZitSdzNyMGpTMmxjMEdKY1J6a3lpMDd4UWFWK2pScUF6bXhhMjRTdktTWmtnQjV0Z1pkVlFab1dTUDVYb2RWdVVsYWdtYnVyNzRmR2RyWVRiczU4dEVudGt3WVhMRzJmVUwyRWZ0QTlSVHNDS3M3YkhvS3BIZytMNkM4aWpTcEhXTi83K1V3SjZvNDJDeGtrY2Zma0w4K0NsSkxxc2dBblRtREI4enM1cWxIamFucHFqUzJhZEpVeVNsWWIzKzV1SDhkR3NqUXJzcUwwNTNzY0VlZlBVU0VnOFoxaFk0ZU1SdEZMUEVsNWh5VWJNeHlZUituV1ZHdTdJSmFkN3NJYmtFblZRemk1WGNDRmZ4S0xiSDhGd1hPL3BrbnJlTnBWWVJuajF5VjZqaVdTTG8yRG5IbklheEZOdkVydWZzSnFOT3U5SnQxM2x5WW11cVdIR1ROeHBPNnBNWTNNMlB4RnlCbUpSamdIeTFQUjJUakZ2WVVxd3ZwZlNscGRFTFZMbnpKVUxPWEFseEFzTjAzbUJnRDhrQ0ZqT25NKzdMOHJUWXNlRjIrSGVJbmdNWHkxTXY2aXBuT05SdDFjbVFUZkhueVVZS1hsN3Zkbnk5cVk4L1diYUsxampURVR6SmxyZ0VYOFBWMUszVG1EU25xS3lMaWZ1Z2E1SGVZbG5HWnJrZWY0cndFWkdWQzBYVWtKT015VGljQnB1QndpYW1aOEZtSmpCTnNkSWtZMDJxVEl3N3RWU3plbkJ2MElWRzMxVkY0S2xTU1hqQ0o1ZmU1Z3hkcFIwY1hNMVdFcjVVZVNWdmlYVzAxR2E5TldGeWxvUU5NVVN2Z3pkWFF5MDZ2SXZrc21OR3pIQlQyQUxXekVnS0JjdmFLUUh2Q0o3bEx3U240UkZvazZqci9vcmRqNHAydEZHSm1QVXJQcVFlSVFTUkg3TEFWaUYwcm4rNDRWZy9RNU5aUDBNR3hTeElYbTdjZHp3bFVEWXJTeG0yNjg5MzFsZDVqU0pqYWNLNjBybGVsejlKdU02Y09CQXlEbzFpeURaazY2dTExVlE3eVhCaEVWczFOaEd4czdKKzQ3SlZuMHlrM08yWE9uVFZDcmhqYURkenpDYjB2QUorMkcrM29jazFZOHh4M2owSkU1WnlEM25JU1ZOT0lVZ3A3V0x5YjR0SU5TUDVxWld3Z3dJeitQTDM3T1BjV0ZVUjRZUHJ6TFZoSUF6ZFQ1c2Ixa1dvaTc5Y2p2ZzREUU5UVjJVczRWTFYrVktEWTNZKzFYTDl3MDVVMXFTanhoUld2UTRVekQ2SHlMMW1ucHhvQVNSMGtMb2h6WU9WZDhiL0Rwc3MwbnVSeE11aTNCL1YxNk1EQmpkaGgrd241cjR1K1BrcE9hcXRIaVNlZnpLUXNDelZRVm1KWlphcFMrcko2ZFZTZTBhcUxHckJiSWlwWmUybE9wMExsV3dibTVKdERCclpVZ0RjN0Vkdkk5MVMwNVlHZWZNWVIvejkwYW9WNDh1V2ZENC9YVjNzYjIrdXowLzZ2Y2IxV2Q5VWFlUllobllyOEtmOFNVL1hnN1lxRk42LzJtSlVLcUc2ZWdoNWM0Z0FLQ1l5QWxVVzdrM01ncnpnVGQyOFhqc3RXWG9ROWorcDh4cFRDTUFPS2hCTUtiNUNkYUNpUDJkazBIYVNNdmJzS0JxS3RlMEdyaWs3cjUxaHBHNS9DbmxGdDFjMkJzUG5uejdlSCs0TmVzM0d4ZGxkYzJscmdsMHUvWUlHTSsreTVCTS9BdU9pYU8xdG9VQzk2R2ZJMlZTWnZNT2ZUZEhvanQydWtwMDJkOTd0MWkyaldaS0N6cFA3T0lrblFjVUQwU1pVdVVMVGQ5QjBkUnZ5QnVnQzlpUUVaM3VwdmxleFU0c3NheWxUdkxWeDA3ZExIWUxESGFSdHZHYU5kOGJaUmRmZDI5ejVxbEhsQVpGdCtsV0xpZzlsRWNwcWFscE9tR1RaSlJCQkpRNUwvdjR1RHExRnRIaDdRYTVSRUMvQ3JsK3RCc1kwbnBaYkN2QW1VcTdXcG00eXB6SjdDQjgwM2VFSC9NeHdBZEFrSWdRekhxRTVyMW5RaElieDBtYlhHdUVzMkRhaWpmcGFYK3NiRWFzOE8rTGJrZDc1N2EwTjlWV1Zmdk8yakU4dnVxMmtqVzlQRWw0UmVqVEtBZGFJVy9oMmVPQ3hhbE9jQ1Y4aXhwMzhpNGEyMHJUWGVXRG5uZFdNNVU1VTdWQmYwUGJNMDE4SWJPQTRuN2ZGYUhpdGNnTUlaMUp5a1p2M20xd0pKcjRmNWNjS2ZQbFh1MjNCOGpRNldacTZzcDJjbEtSOHE2MFFFR2w1VE0zaHVoZ0FjQzBvaVg2ZS8xM3J1bUM5Qm9Kd1lzNWxzVU5oMnB6NmVtdTI1OG8rQXEwQW5wYkZqOWYvNEtnRElKQzlFRW5MUDBRSkRUNkpJQUNwb3VYZlFYemljUnUza3doUmFIcjBCQlNLSHRGZEhJdVJqVzBEUWZ3VW8rQXZHQmVZTm9CcVNWZDVOclRSd3VnMU1oTFhyeG9zbkR1UmxBZjk4MkJieHBpdEhEVnV1Qlc5L1lDSGlhTjljU0dESFBhMW1qaGxrOEwvSTdFOThJZnQ3K3NWK2ZYeG1wT005YmZaZnhvazhPRG1vKzFJREZFSC9MZWpVSGdDcUpMK3AyMENtbTRpdTV6TmJDWHVTS0JTMndCOG8vdndUZno1cGFXaFJGM2JBdjQ2Y1RnNDF2QSs4NHdtQXRCaWVvdzMrQklDcC8rcWhTM00yRkJFTVhLMllqejkxQXB5RG13T0JMaFZjaElzaC9OcGo0TDdUemJXaFFpazdtWTB2SWx1VEgzR1JsU2lKbGlqczJjekFBSm0vam9rMTQvL0tudkwrWmY1K2o4QUFGeTdhdnJ1THI4VWpmeS9IbURnZ0FDREFxYmxobEE3OGtRQXZPT1B3dS93dUJ0WlNkREx5NmtCOEo3Sm11ZkZQbUowbXZxWkhyUWhyVDdMVmJ1M0hxcEt2Vi9Semx1dHloTnEwNnJmMEFQcVJrdXNpaVBLUUlOb09WcUQ1ck9sUS9Ed2RBOGFCZU9LT3RXZWJ1RnJtcTJ6bC9YZGJQVmJwRDQxMVMvemRJODF5aFVwRlpFcHUrQU1HeUdYWWFVTzJkWDRYYWpicjFxcnBsV2U3MTcvN3hhZmpLejI4V2JZcDhhWDZqV05hVlhkWUZDbHdSWm9PVkdpMDJwNzJETW5VOE9QeFVnMUdkNFU1REVlR25vVG83S1BSR3J0WkpZZUVCcVA1RUhZdzM0dG1IR2htdWZXSlZEVGdFRlBZT1c1Q3dVUEgxNVo3UkhwZk1KaFBlMzkwRW9adUx0MkRyOTF0Z0J3UHBIcGtZVk9tUHBhTWo1OHFXcE5OTVp2S2d6V0pONmdRcDAvOHhNRnNKWW5Kb2ZGN2lPd0R0N2RKcjRHZ0dxSTR0bWF6TWJuS25vR1hXVURwcnBHbmpDZVZoZGNtMWZ6L09qRjBMaitoQ2ZBTnc2SmxtYW9mWHA2dTIvRzdSWFN3Skd5UEhVM1lCRklZdmNpQ09Ia0lvWUM3eXlDeFlaWXhHU0liU3hyMHlKaGZaYldJeFZKNlRjNExkQmpXSjlPN1RvTW9KaEZ4S2h4Szh1UXlKVHdZZENTVDNGcDAwOXV6R2Vva0F0cXU4MklOdkVvL2tjdkVZLzU2YlFVbEdFVWhwbk5qSkU5M3F4VmV1WVpWcmhFbGRldDFxVFk2S3c5RGd0ME14WnBERnhHdCtQRUNFOWl2b0c3dlgzYXRRa3pFMVUzc2laUFpxR0VhOEV5UXNSR1ZhRFpCMHJWZ25MSjBtVEtrQ0pWdXZRVkl3bnpoUlIvSmF3U09ZWis5Q1NhcDduRU84eFYyeFpKQk9JdlNaNFloeFFUYW5VUmptdStGQXFQeCtNRm01MnFDVXJBTkFLYUdOVG1RczF0TjBrcDBXeWVwd05JVUJrOGg5RmhHTjgvMUtoWW9EbkcwSVg2TElxSXVJZ0FaL1dMTXhhQzJhcUFTYWxrdGpHSSt4VVZVYXFIdkpENStDSW1FRi9jNFlLVytJc3E1eTI2bDlKczBBeWpXNEVXc25vRFVNeG1vY0t1L0tGY1JSYWVnMlF4ZXFxUk5DVDZSUnhuOUhDaVg2SUNvVzZZWC9TVGxQSW9HbVpEdmgvc3cwbFd6UlAvcjBUa2VmRVBMUllPTGg0K0FTRVJNUVFLZzVPUUlzaklrUlNVVk5RMEtEcDZCa2JSVE14aXhJb1RMMEdpcEZCTFdDV25Da3BER1grelpjbVdJMWNlR3pzSEZ6Y1BMeCsvQW9XS0ZDdFJxa3k1Z0FxVmdxcUVWS3RScTA2OUJzMkJzTkdrRzZac3RzWXVUL25VRWJOaDRJcmJnZWtNZk9hYTY2NjZHVFlPaE1QQ3lvWTk5amxnaXljZGRjeEpweHgzWXY3c2hmTTdMUmE3NWU0cDF1VDBZNTJaeDlqZnpRNm10M25ldkdabVpVZmJRRE9ycUhsZVMyc3pWTjBKbFhVeUt6cmI1eld6Z3ozOW5kMEw1alBLT2pvWlpmMmRRT0ZwNEkxQTlTd0licXc1QXY2dlVaVG9tUVZZanFOeUVJaDk2enZHNTRDYzh3SXBLOE4ybkdFNHhlRGV3RmtJMkRiUEFGeXpCbkIxZWZVUjIrcHFnVllwdXQ4WnpoZ0F3bFlScWF5bU1tcjZ6bzRKT2FBZzlqVDRjQVh3aGxrb1ZUQmJYUT09IikgZm9ybWF0KCJ3b2ZmMiIpOwogIGZvbnQtd2VpZ2h0OiBib2xkOwogIGZvbnQtc3R5bGU6IGl0YWxpYzsKfQpAZm9udC1mYWNlIHsKICBmb250LWZhbWlseTogJ0thVGVYX01haW4nOwogIHNyYzogdXJsKCJkYXRhOmZvbnQvd29mZjI7YmFzZTY0LGQwOUdNZ0FCQUFBQUFGWUVBQTRBQUFBQXRFZ0FBRldvQUFFQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFCbUFBZ2tRSU9nbVhGeEVJQ29LM1pJS0lSUUUyQWlRRGhBd0xnZ2dBQkNBRmlGUUhnd2NNZ1FvYnJKb1Y3TmhMZUJ4QTJGU3ZJeEUyWWE4eWp3d0VHd2Z3TlA2TEFQOS9UNkF5aGkxb3RpR2cramNoYUtJc0dHbGhsMllxdlRKdHFVYlZYdmVEYWxLKzVqc1FyVWQ0T0k2RG9mcmkyRzk2WElyVGhUR0ltMStiUjBrTDBjZnh5OVB4cFJEL0hZM1MxdmE4a3Rib29EOTZ2L01kb2JGUGNzbmhhVHIvM3J1NzVPSjNsN3VvVlN4U2szaFRUeVd0VTZHRUZpaTBXQkZyb2F3VUxUNHZac01tNkZhWU03WUJNNE1KNDIrcyt4c1RyOUt2ZlNadmR6OEN4aDFRZ1ZXRkFwSTNsU3hraVpLRm05MXJhbW9xNUxmQzF2bEtOQmlMa0NoQmdrUTVoQUVodWYvVm1iSERScEFocGk5MENNb3E4SHhUNS9KOHI4dmNhYzZCaWY1K3lYZk1paGNKZlVON1NGbkZncXhpTnNIb3RnM1EvUUQ4LzVzZkhPQjcvd09NQzV3dnQ3Y0plQkxVQnp5L3VBeFdDTzZCNy9yYVUzZjljTnZMN1orMjF3YkEvNmtyYjF2cGNCVElJWld4aTFXSVJTZFdqcG5YTzZUNTdmOTJOek5mckRCZ2trUzRROCtrdFdIRHBjemJIWmdMUmVQMGY5OW0zNjMzV1RYelY5WFZwRWs2KzBKb2ttYUdtRER1MVZSMmVlYXNDL3pDcnJjUWV4a2ovc3dqNDFMUU04dGI4L092Zm1qN2RrQlJpcVY1d3lDV1JleGtzYXloa2tHVGIwVjlSWjJ1ZWxuY2YvcW0rdWRjbXBuZGxkY2dxVmxES1ZnZ05JWktpaW8zcnJzT2tKNmU4eEQraU9kYkcwMjNLbHNzd1JZV2JQVmw4eDlVdUlNckI1Rk0xK3ZjcnFtQ2ZRa0tpMUJ2bk9iSzFES2RCbmhISE84TjN2TTlYNWJGbDNtYm4zSDFDaEtsSmtnVTduYnZZakd6QUFIc2dueGl3Ym9IRnlTUEJNa3pBQzF3ZCtJQzREM0JNMjhkWlp6SExjNkNiMG1ldkEyVktUTEd4Z3BpS1Zlb0lGU1dLMUtRWlNvRUVwZzRscHJOelZFQ0RialpCRHRBbmhkUkZGRmNnVjNKVlhFKzVkZWREMHlMZmlDNzJSaU9ER0tobTFNNHZzS0dxRm5aR2Y5ZkoxdUE0Qmh5dTIySVpXWnpkdDVOZnlNWlN3aUd3VzJYZ2NBY3lCWTRCTDczRWdUWmNIM2I0SWNqRUxtNy9pcklqMWU2LzB0Zm0vREw1QUVCaHB1TkdqWlRZeWY4ZFlSc2ZxWVJCSHoxR0FReHFDMCtGR2VhRHBhenNOai9zUlBpaGlPWTVjalRwTlVTSzR3NTdKUnpYdkdoTDl6MVJ4eXBia1pmN1J2OXVKL045dGs5ajg0VE16Yjc1dUFjT1lJcXJZcmFudHdReE1BcVQ0RVc3ZEtnNnFXLzB0ZjdZVCtkYmRjOE1JY1B2MHFxL1ArSHVpL2RjZHNYUHZlSkM4NDY3b0RkSGpKZ3FSbWNTLytuMk9NNDE2LzJXN2ZjWUswWmh3NDY3YlRqUHcxWWVRMHpLeUs5SVB6TWhmMXQ4MlVoOEh1L2UrL3NYemZ5WFdwWmtnQ0N4VGdZOE93ZnltZWZNZVkvUjc3WlRNOXEycUNNVnlndS84OVVvUDJsTHB3SXdTcWgyczlnMGhROE1SRHViRHhad0FyOE5VTkxQcytYV05OaE9iejlFa1hGbFZrN2V6TlJpRllkbUh3MW5GdzFWTUtldk9JbHMvcU9oUEVFUlpDYS9kb0pHNVIvZDVjTTU4a3FZTC9BWDNuaUxGNDltWmtqRnN5VW9BZlg3VXZ1eVFZR2tPUHlDdVRxNjBpWHdKRGdSSXpjOGVSQVJRV3I2bnNCdllvQytFekdTSGNQMVl2dmsxQVUrUFhKb1FhelFFN3lKcWJYTHIrMXFJWklvUG00Q1dlWU1ycE11M05Cdmw2ZVFRb2V4Z3Z5UzBwQm9BT1pzcmRodnI5MzBmMXU4UUZGZHlWMCtjOW9EeVozNjlNZGEyYnExK01KTVBOTFNJZHdTUUJuUXFjRFFyaWtCQmJVbDZLNVpCa3BaM2V4UXNzQXpZanU0TlBOUkJoSjB1czA5OE1jQ0NFaXhuT1JnSklIUnFKUjFlZTZPRDBaeGFuWUphRkpJbFJXYXBJbnlCNVBNZllvd0lZK0ZTRXZNaEtNZzNkZ0RrUzVuaEtBVWxnbTRSK0g5c3ZaM2RnV1RTaTdvQmpFdEpvVXo5N0hhZ2U5blFuekgrcCtVR2J0ZlJPZnZmdU1PZG05aXRaeVZGQ2hIOXphQ2lWM0E5dGdSbVhFaGRvOGdjRWswaWpJZGsxVUFITnBMcFQvdEErZDhLS2hnZGNoUjhORWZ0SzJzN0hSaVlNc2U0cTRxeHduTGxoQmg3VW5sUmllTWVidkoxWmdxMUpxOG4xa0t3RUQ2OWxWV1FteG5ZRDBKZG45dlZ4NVlGQk80Nkg4L0xlYUUzd0o3clI1S2xGQnpXVlBGU3AyWm1vUGkyazhiS2IxY0pqT3cyVjZENDhaUEh4bTlBaVl5U1BjbVFJYVlwdnNQV0lYM2kvczhqM2FqQzJXQlQzakxXM1l0clp0Ylh2YjBYYTJYVzEzMjlQeWF1M3dXWUdzeE9VZGRHcUl6aVdZUXVMRm1KdmtCMzJCUGk5WEJBQk1wYjFoYTk3V1VsWStWWmNUT3lOQkY5V21ZY0JYTHRjTjhab1pHUW1oVTVva0hRWnU2OHdqMkR2Z3lZRXNNaWhRdXM4RzVoZTQrOVhwNyt0MkRYTjR5VE1LdEpoMDdpSlVKZHlIS1libWVPc1pKNCtTWGpsa2NnS1ZSZVlzR3JFSWlXN1hKK1UvMHpKcFJseTQwcmhFQ2ZQYnJOUWR1ZVNaQklFaHg1SGp5S3F6Nk9WMWVWbUF0aVFoYTZUaWFPVFk1RWlndE1CZHp0ZEZzL0dudEIrcTNFOVdjZ2hacUlOREFrV3hjMFIxUEc3WE5KMUVxa1ZLd2EvQzByUlNzald2VkRiRXNpT1I2bEJSdnFlVUZTQW4wVEZ2TzZWQVI0cWIwa2gzbUhBSWFjVUxjaTdFUDhGMStWbVUwS0lsVnVsT0VaOUxTcVdiTmw3UUxwYjJBU054QnpmQm9VdmJiWmMzMDBPQkJIOXd6RGdnZWt4ays4Z0lGZFhqWVNFMmFGVWNXUjBjYjQ4R2V5eWNwZUxKREt3NHEyUVVxOTRSRXNvaWR5UEUxVTJrS2Jxb21RRjNadkpRTk1oVkhkOUd0TlpVUHdlWFhZMkZ0VjdQZ1RoSzA3M3hJcGdNZ3ZOTFlDb1UyTjRHekIyVmQ1ZGR5TkdHZm5pa0hHVXMwY0l3cFdpaDRXMlJxUXFxNERqVUlSei9MYVlJUkJPd3hyeHVxOXRPZk40aHBxZW9BaExBd1BPMVEvU28ycUtaRGJWUVhZSWFNM0l4ZU5qbmkvb05xUS9RcUlBTk1jYW9wd0VtcHF4NUxHQUdSWU94bk4zRnltZ2hKR1BjWCs3U3hneW9GK2g0bUhXYk1JS2VoMFdmRGdiUEFCc2JObUVFSXcrTGNZTkhuZ2syTm0zQ0NHWWVGdk1HbldlQmpTMmJNSUtWaDhXNndlalpZR1BiSm94ZzUySEJOQWllUFRaMmFNSUlqajI4N1FSTVF5MU8zNGd4djNuUG9NRE9EV0FhSUhZUnlYQ3d5NFlNdTJySTZOY0xJSFlUeVY3WmJVT1czVFZrMlgxRGxqMDA1dGhqUTQ0OU5lVFljME51M0F0K0NqT3BIYWhnYU9NRmg5U3UxL3Z6aW9zSFFRRFlUZUp1MVdwK2NFYU9jQkZpZmlVazdqNjR1MDRxSk8yRElCMGVYendHQW5QQjEvZEZ3UlpHKzBvMGhVSTdsYjVvejdSWDJsb09kd1E1QVJwR2RkZ2ZlcW9FdEFEV0J1cjJ2MUVWTVl3VjZuU3ppUnc1d3l5VDZIVG02bmdqSldMa3l0VFpiVVhsR1RKL0RNTmswMEtSVUM4U0Znczc1TVdDTEptME9GbmRQYk4xVTM3T3hvSVowK1RxQWxXR1FkQ21iZzNYaHhadm5KcXIxZXFaTEtWRmx1NVJKRWtpdVlwS1lhNHdhMlp5ZnhNbEwyRXpGYmxCZjFWK2ExdDFRNnhBeEFoak5wT1pyTHczU0ZHcElqcTNSK0dUVWpsNmxVeE82WVNNTUZZdTk4ZzVjaWE5TWk0NXVTdlVQRWNzWE52S1VDcWRTcW5UelJMZ0FxeFdnRHJLT1NXYWc1cUdTb3lGT284dERnTENqK0U4aWxjSnpZR2wzQnBnNm10UXFCRnBBdjJqUGxiZ1ZzWklVa2tvUGxzSjNBMHRjbERUQ0RnVE53eVprRWVGaU5RaXZlZXRHTkFxUWRIMTh0UUpESENzd3BZcGVSbzcwT2hpZVRCY3FpRktlZWRBeHVFYi9QNitvZERKNkdjbFdNdjJlMkszdTVBLy81bW1Ldi90YjhnN2ozRnZBZUM0OENTMGFVMi9KRGJjbHhDcVJGYUV1SlhNK1NuQVNGdkpWQmdvVEhFS2d1TlRlNTZxUU8xUE1XYTJFSVNiZUliUEhmaG43K0xXVWpScXJnTkNmQ2J0MThRU25xNmcycWIrb2ZuOWxGL0FDSW5HMDFLOEt0Q053bGhiZnh5QmFiZjVyNnFCT0xDcXZzaXdxQVdMaWJ1eHRWSmMwZ2RDbHFuM0FVOFEvdnNubldJY1FsRUhWVUw3ckd3aWFOTHpHTk9MYUVSaVFuUFAwRHNJTUNaU3FQYmc2N3AxRFB6T2JpMG03U2loZko3SEVsSWVqRUMxMHpDQWkrajdFR1lqUnl1b2RXbGZ5K1YraWJnN1ZSQ2xuSGdmRnFLWFRkU0c1NmZNNWpoblRnRnBSNFNLUG1BdjNPYWVkWTlKaGFVM2VJRXB5R3djNUcvY2hWa3ZVNGs5STBKWHVDUG1QTE12N0xSZjFQVFQ3akprU0t6RVM5aktHbEpVRUFvTGlYSTk4WUJLU1hiRm9pVUtvcng3WXU5MU0wSXFJZSt4S0hpMmlJa29vdUJMR2pFUDFialJqK3JKM3lJUFZnSDVEd29QWXpuL24zT09qSlBqdmtUeGRRWmFyTFNIcnUxNlJ5d1RzbWFwb2JRSVhvOXJqRGYwZ1RETlVSWkRTL0lLeTRXeTJtRGtldFRFZHJoZjBpaTdwNFpSNGNTNEhXNFc4N2VCYm1meGRlVDg5TEU5YSs3MmZMMStlQ0cyWTR2TlJvb2RnVGc1T1c5eVFGemx0ZUIrc3AzcHdiL0dIYUF5S2lISEhSMHpsdDEyeWpmOG5OTjlyRG9xd0NQbkpDTXdaeEV6aENZQnJhdWV6MkVjY1VCbU1ZekJzVnZNYkk3eWhCWm82V0Rmd2ZpNldJa1dJbFJ0TElOb1JRMTROMEdhK0d0Tkd1UFhubzNsSjZId0pUWnJmbHA5dWc5UFkzUXNoL2hGakxHV3BzY1F6Q3h3bTYyQnZKQUxIMkhFbzRUMnRBbElDNE9vYkFZVmVCZ2VnNmw3M09QRWd3K3pCdFRwK3RpRjByNmFZS1dxeHhtdUdudHlRZyt1RWRxd294bU9MV3BmNkVvTE8wY3UzZnpjSmc5Sjh1a0k2ZUlTN0NQZjF6VEc5RkprTVBiNkUwTE9zZlE3MHY0bHhvMjdDQXpiSGY2N3Nza3lTS1NIRURJSVVrT0IzNnZhY3pscXFlVmtjYnFoeDVicWtOcHlsSzJ3K3lrblZZV2QrWHdYRTVwci9PMjBDVW0rTTBza0JPcVlrUXBOTVRhQlliTnh6dHQxUkVxOVpiWUREelZxOXhoZnNhWEt0MFQ2MnNxallEVERYZHlJQ2tUVUJqWEg4aDYvdEcySVlPVTVjWmRRMk1JSUd2VDgwQXZGaEJHUnRML05iTXpMTTdYd3RsT1NBKzY3ZExGZFlveFB1OHJMbUdOVzBnekoxOGZFdUY2akRWaDlFVENtMHplRmtlbERHV2R6UmtHMFlpUkxuSjM0QWlIc0ZYTEpUVHBKaG9aSXU3UU4vejlQai81UzRuUFg1djR2OE55djJXcXVPU29ubXpmRDRiNlZsQXNLK2lsWldvckxOQVd5YkRiQWxCMW1kQ01XLzRhYmVPRVQ2OW9DSHlMUUdoamZUU3lFeHlvUnNmVElqR1I4U1pwdXppVXlKNnhsTk1iZFJvSHFualloNjFqUUorUXhSU1UxWW93eDgwZUVGKzdaVmxKQXlFazdVeVZSeVNsZ2NPbmxrQXFHbkJuc1J2S3RwdG9pZUtzM0RkUzhrelJnVUJHeTV0c2lyQzhzWHNBNTdUdVhXZ0QreFpsemlhblAzbUN1c2lWVnBrNkhUWVdyQTkzVkk0VGcwVTJuWXg4Q2VrazV1VlI5VWY5YWUzSlJCSUs0NzFPT1c4eFhIWlcyNmUwZXV3aVVpeEt0ZDRWVDFBQmgrNVJqN3BkS1pEYkQrWGNJdEEyTkhlMC9EVHQxeXhTeHBESzFza1AvY3RqQ29sNElnVW5Vb1JEZnBURHYzTmVnaGxFc0p6NExiQWtwSVhWTE02V2JlaGZWMWl5cTRVaEl5YzdScXkySzlPMjl1cFlXQmxKN0xFYXh2b3lVd0hvd2w0VlVjalVxWkQ4dWlFdVhoMU90UmcxQmJTY1dwNHVuVlRZL0pRR0RQS25RMWdzayt1b3dZbXU3S3EvcVBNbk5RN0RTMThGSWpSUWcwTEQ2a3pTcUw5Z3VpNkRsbXh2UW9FK0djOTNxVVpiZE5wVHhsUVBkWjAySUNxb0R5b2ZVdHVDNW5pQkF0ZEsxRXkreFVUeXRaWUVJM01zOGVjUE1hbFF0REVja0xveHI3QVFtenliRktxeFgrK0RUczVrREdVRXVZUlZlcFlJVW16VUVXa2NkMUY5MkNHc09RQ04xdXJmUzNXc3dzRGxoSmxOaTd1cU9jSmJqeWJLSWpsTXZUMXdiTFJxbEdxWlR4cWVwL0JSNGcxRXF3ZFZCb3luclZqRm5KRlpIVTRuMmhrb2VML2tEZmhPVHVVRm40djBTaituY3dkUVd3V052WVp6N05zZ3ZZUXYwUlFoVHNneHBtUWMwVTl2NWxBckVJcG1ZNTdMdTVNanVWOVNkV2pZY1pPaXhSRURTM3VhTUlENFhpK29uemRIVml2VDBma2xSQlBucWtJQzNONWJwbE1Oa25wSy9PMlUyOGZDUzVvNEpIWTB3R1NNNFBjcnppMllZb3JMaU5CWW8zTWtNeE5OTkxSd1dsQ0lpS01qdGQwVmQxbmJGYnpnNXFkZ0dsRjA1MVJDSnYvKzNneUdhOGIvVmd1dGd4MFhSTDVub1pQcVRTNEFaQkxOck9rVVJCT05UWVl0RnNLQVUvZGtIVzJjQ1lqRTh4cGtSbzZJUFdIRW1zKzU2QzgrZ3VMTmdmbTkzWjNuVzEvNkIzSzg0Nmx0dGFSMFFhRCtOTVBVZGNVc2M4d3lWUElaT2kvMlJaQ3VuQzVLWjJ0NFNodGlwTXo0UTNTbENBZGUrckJtZ1ZiK0FHVENScnFZS1ByQmpNM0lNTzBvNnlXT1RyN1VxUW1IQ2NCa1U0RVFtRm14djZ6Ym11ZU5DTjJzSUE3dWpHOG9zSkVvUVM5TjVvUHZVRHlrM0xwR2hHSmFLZ1dqYmlFMUdyZDJWT3pIN2Z0ZTFOQkhzMVR1dkwvVTFyUFJCRko5Wi81NnlVc2ZoVCtpeTFUOXYxdVR2VUZHZmhVQzNJYjEzSGUvVkNxbFJpRWFTVzNnQmlnZUp4bEFQWTlIM3BRS2ppK3pYV29zWFBsTUc5UWlFZEczMTNDRkRVK29WWTFaSVR2b2dqYkphTTJUekhMRWptekJYWEM5Y2MrUVlUUVR6Y2Flc211UGFEaG0wek5ZTWY5dC9DQTAzdWtWRWZlT2Q1cnJqU2sySXVtS2pBb0RIT25Zb0d3Uk1rcDJaeGEyUmZJVDc1Z0c5dkwyVlV3OUJHSzd3Y0o5OFJkc2JKWEh6Rnl2QytOek5DVkpUVUhMcG9tVG5tZ2JHazNpMHBET3JBUjdDT1pKUE5MM2FpN1pFWXBZYmpXY1pyUmRYVElTaElyZWVqdTloVGdPQ3lhcTh1bVhXNko2QnlOUVppUCtMd20vM0VFRUtOYmc5eGpJcytMVmFlSnErRGxkOU1HUG84blRCOFJhdmxyYVE1cUJFNFhJVW45YVplSW9mN2ZTQXZDTThaOFZVcjBYTmlyK2lkZFRnQ1lLYTl0SytvcERFQU1NdzhTeDI4TitseTA2cklDMUZFRVRIeTU1Y0hTcSt4czJpRVNJTHhrOFo0YWJXNUpPVjhWTnpINzMxUkkzSit0U2hUeEw2VnFZL2RlU05JdlhiVFVNWlNLRWFNenBFa0doQmJ2VTY5N0doSnIwUTJtRDdsT1ZBMkM2Si8xQVVvRXdidHVqK1AvZTdTRkNuYU1DWUZQQ2ZIQUpVeVY3dWZpMXl6NVlYOXN0Wk9xRHBNUTI2RVFaN3NMb0xKelNSTnJhQVJrT1NhZUhQcWFJWU1RQWRmU2xDd0o0blpNcjZMZVJJbHk3LzNFWVNBVXcycjltbUlqeHp1SW94bVNyMlFMU0JSN3VmaXNmZmF6NUpNcVM4TUhtK1IvS0I1UDcrODY0Z2k3OE5tbzdQdkJrL1RpTG8zY21Nb1ZXZHZ1dG1UNWdYZTNHa1JtVDR6YmRIdGJJdWJlUFNuRW1YbTNHMjlpWnhkRVlFWWFTK2xBT2IvYVZ6enZuWVpSeUkvdWFUdk9Pak5nNjVRck5IcnNaazVRQWNNL2d3WlYwVm9rdm9RYW1pdGZFQVpUSDJ2c1NpazVrM0k1U0NqVU9Zenp2Ujlic1NPenpQd1hBSG95Nzl0V0paQzc3bUNOTnNPZ1k5RXVNS2dqY3cvZ0NOWXFMQ3h6TjRLdVNXY3hnV1FtTWU4bm1jMjlUSTNZV1dMMWc0emJvU2t0UVVXY2xxTjArZHVrQzJBQzFzOVZwd3NvNFJ6eEJWeVN0RnBUN3dXT2FxSEhWckcvT3JNeHNhZkU0Nk1SQm14aml3SDhZMWRuZTNhRjBNZEFsUXFES2VHRWtVMVlYcGpXbmFrSlFJbGs4eTM1dm10ZmRIdFAzSlpBb3pUVTcvQmlKcUYxR2Y2N05SSlFTMmNoTnA0cWttL3ZNUEpWYXBMekhDYk5Ec1pYV1dvV2JGaDVwcWV5K3FwSkZRNTdCYXVPNDYwQ0hyMFE0VmV5d1lVWGF5NmRDZWswc2E5eVFWMTFncmxtNGVKQ1EwVzgySEFUVlF3N3d3Tlc1WWtxVEVHS1U2eklDODdYc0lwbUpaMU5kNnlXc3YweFNwL1E0ZWg2RnNrY2xxeEdZb2M1MjBxVWllM3k5MXFzaXBpd3c3czZDTGZmcmI2djJwQ0N5ajJPODJqWW9OdElMc1lGc3ZFdHNXY2tMR21xbnc0d0x6RHlselpQZjVQYnhUc1NQamNOYXBjMHRhdEk4NFVvK2FkOTJ1ZUdwS2k3WExFV2ZKc0s4dGM2TzlaM01UeG5lU21aQnFXTzNseUp2bUpXeXQwMTVBQ2VGZTZLbmhhVG43QTJ0RzUzVmNVYlM0T3U1elB4VTdrdVNFY2pSUW5OWElPai9nYzhKSzZ6dndyWUhmZHcxbkdYcExXVStnelpaTm5mNEN3ZEorMC92WUZpTjk1ZzdoRFl5bTVJcVcvVGtNQXoxakppTkFhMDBxcmRST05IR1ordnVtRGRvUlpIcThJdXVHdDlnYktFTHlGbWlkaGkwRXFWT25ZNHM0YW9RNHdpN0JGSXBzRklaMmM0Sm9ZcUpjQnR0clRzNm5tRUppdDU0TmZLZWw1VEQ1Zm00MzRhcE1KazEwazB3QzhlRHhVaVFLWExlVk9KdERCTnl2bTRIY2FidldrQzQ2UzVuTjdoR0hjYkZhbjhQcWtoS0NiMlVwZ1U5VlJGdkx1R3dEWncxVmlHT2tXWWVxN2ZyWi9XYmxiOTdJU3JyOUliRGprTWRYbjFHcVdRS3JrK1VtOXA2czlkZU90R251TUx0YnhsQ0hYeDNjdW8veGxqZmdhbldQdUpvVnVnZzhxY1hqODU1aGZpZnp2Yms5dTkvWHJGWlNvZ2ZDMjVKb1EvUEdGMmsyMEJyUzlLb2QyazJNTU45YmJ6VnBGZER1dWs3VGJqYzI3WkdaRCtUUzdURU52VDRFS1l1ZW5UYnhZVE15MHE3bWNKL3pXYnV0eXhUbkZQK3F6SDI5Vk44WHk3VEhLcjBUUXJlTk55SzNSVFlNa3BoK083VlRPeUdwOHpINmhjQSt0TGlyQktGRDhLdU5RaEpwTE1VeUVZT29TNmVyTjI1akhXeUNOeXd1aW1BSVN1dlpka211N1FwMnY1K0M2cTdyRjU2YzRVVzFkdmFvZHhFa1hvTEUwc0EwMjBjK3AzeTJOVU9BbmV4VGEvR1NWcGF2QVM1aEFTWXgxdHRPUEs5TDVoUkxGUW5IdXhnMVZOVTY3Mk1ISVRneUZDK3daTW5CdUdhVTNCMWhkakZPUkltbUNpVWRDME5rbURKVFRKYzBrUU4vd0pPTWZRRExkR2hsOWlUSGpCeFFuRU1pMTZZR3JnaWg1RkxCODdYbllDR2RydWxwclZDSVlFYnV3TDIxRzlxcG1aVWpkbklSbXJTdkZYaUJjd0k1eC80c2tVUGZXK3lJQVZIaGlWR1R0MUN3UnhkanBpeXN3VFBWV0ZESnl1dTZvNU5nU1JhQ0JkUFhZMnNjbHdSYTBkOW1UTlBhY1FSdjJCeU1HeGJCQ3NaRUsxcjVWSmgwR3NNWXhyTjFlQlptTUVZbnV6OGFNUGxiQzNRQzR6U0MxRkZjcnZSa0dvQTNVMWw3S0w1MEJBVHNaalM5blIzb0pvc3VYMTQxUGxrVzVpV0pXQ1NmekxiY1JRRE5ocWt0SDVJNUdHdnpQampMTHN0RllXelg5TkhaeEtQRGh4L3hiWjkzekV3V0l4a0NIcTVtd2Rya3ZlcWFITFZ3RmoxWTN5dm0xSEFGcVh4VFhoQUloL3JQcGNDNFppSzJlYmRuWEFEUkp0OXdtYnJkcTFkVlBKSXZ1bVlaMDdRTWJNRDg5S1dzMG5wVFZQTWplWEtPekxyTk4rSGppMUZKUi8yRXZ1bTRialJEa1c3aU1seWVtRzZQaWFpQTkwUFlhSXZ6d2trRUs4NU9rQ1VUeXl2TXU3VmUwalJ4T1p1RkcrbkJPQ0VlUmozRnMvTmgwL0dkcEVlMWJXaXlNOXFqZTNpN244bGtHUjJkbGFtZUc5dDd1eWZmWU00YWM3a3pTUmd5MzZKUk1Bc29KdzVNY2FkODNwdGp6emY5eWV4SXdUakRBSjB2SnhQb1BzTm85ZkM1Nkd6UituQ1d5dHlab241TkZXTk9xbW91SGRBNUNTTjJQaG1hL0UvZXB1dkNUK1g0NzRQNWx2TTVKQ1huWmt4QXBXNkRaYTVreTFNTDNKbVdjV2pHbEsxaDFDcGt5VTRHQ1VZaVNMVktXREtXTzhwSEtLeTVUdllmWVNxd3pMeUZmODcvaER1U04wK2R0WU5TN0tZQW51MWhJa2kwaFJqM2dvSm5nN2doQmlBWGtnRkNscjJhb0xsZ3AwOElRT01UTzh0VTFUQi9BNjZnRTFCWkRod0VjYUd4dWRZMlVCamRUNW9lK2NTTU9ZbFBBa1dDcmxlSHh1cXEyTlBSMkZEWkhMbWNReVo4V25rTW5KQ1VQcVYrWEV6OEUwc0xyYjVDVFRTV3d1Mzd4L2tuelFJcVJqMGRiQW1TTDdJYWhBNUV6S1JaVjlHd3JJa3gxdVoraENSQkgySmdIbEM5b2JwcXIxd3BjVjNlNHlLV203TE9yQXZqSE1ocmNUZ0RoTE5ZaU1sQVAyUWlQbDJsU0tFMXMvY2txV3dlMlc2dXViMUgxY3ZUVmVsWm9NcFhXSnhmZ25zV3RRTFN5TGVTclRvcVBOckNIS2E4Z1g3WG5PUndkTzVZMnNOcWE2Uk5NMi9Od2ZRUWxPQmZQYXF3ZGVXVWdWNUpteGtlY1BReE45YlBOUGtQVmdINUFNbU1uNjNZTVM3cllzMHBBVVdYbms2bmVnWnU5bjRNbVVMeEFhcy9tYU1uWVpGb0p4RTJjRlQ5RTVtdk82VjNhbWkvNFplQ3JwUERQRU5FNERBaVRmUmpsSWlheldZanZPaUVzbGsxcEhFbjdsMHpOUVErS243MlNzWHVJWEQyMklCYXJhR0tvRXdkcmdiVjRXaXpTUTVRUW94VDRvQVJxSkdac3BXS0hubzZiUVNyVTNRL2htS2pkMFJDM2tUNTU4RmZORndaT09YaTdVVnE3TmsrYUs4OFhZb0JJdzZneXI4MUlmeGlXeXJrT1I5aGN0VFI2YzE3aTRwNEdsS0VqUnE1REtIY2hZTHlGTmtNamZ0N1R1TmxscFZZejVsVHBTWVRjYnFxVG1TdmdqdDBQOGlhamNnSjZ1VlR1SlBTdXNpZ1BVTDNXemdnL0FZY1lLY1ovMW1YZThuSjdtU25PcHlublVUUkcreXJNa0hKbmFJN0pwWmg2ZEJsMis0VGhybVZzTy9RRyt3RlJlS3dSbjVOcHZwc0NqZFhmbGltdTZpWFgxWHhaQ3Ftd1YyNG9iY2FZdi9udis3UDBCUmY0dGlOenp4RDlXV3p6dDNOLy9Pa3RkUmFBcVpGalUrTGhNOFkrM3JMbmdqdmlmQ0k1d25VczNyU0dPZ0JBd2N2Zm9MNzhiRUFXcnlqYUhzWlV2NFJ3M3hLbGF0OEVUcmlHMnVjVS83RUFQUEQvUU1yTFF5REpxWGlRTGNtNjFvN3lrdVJ1NStlUDBEUERvSHNDMTFuSnJqWGVBQUN0bngxOFRMS3hTR3VFNUMxcUlLc0IyeEQ3SzZqMzlJVk1XWEpVL3hzdTRTc1ZNYjRVa21jVTF5K3BxRkhxVmp4UzFwZ2lxS3RRK1FKdldXdlhMdWtOcEZqZUgxaGdUUk0rWFdFNWJQaGNmNDNDNGM5K2RSU3ZZZ1RIeGRNTDNhdmwvMWh6RllIMXh2ZHJ2bFJ2d1E1WHJpc1BnRzZRV3JUK1BNZlRuYmttZ1dPS1daZlRGbG5YNExDb1FaZDV0eTA0YUhMMGZSR0FJVzRXRTdLelVaQnZLYWp6TzlNRGdiVUZMdiszTDVkeGIzcEU1OW02ajVpdmMrczc1QXJWS0xhT2NNKy9palBrZWwyMmliMVZDZkZuR0FxVWNpTnA5UC8vUmVTL3ZoR1FxRXpuRmVvZjFYSHMvZE5NZGtZV3pWdUNEVUZ5NzFWaXhNVE50OU9TbnhRcklIY0IxKzJzL2hyUnFyS0pGR002UmxHOTFrRUIwQUtmNXJja2hBUUlWTGxtY0hmYnpsUjdBYi8vRGY0ODd4Tlp2bi9uNTg1ZHplV1VmV09uNGNPaDJwOVNVS2l6WXhqMWZPN2c0ZktMMUxqY2xPNWVzWlBxVGpCeUUxL3JuZ3FMcEpYNU1ZSUVCK0JISzhidzRYQzNsa1BoZzc2YWtKMjNjUjB5VkVTMklGNzk1RncxMk9CYVArTG9CZFdTbkZGNmRtbjMzbm5ucUdLdzU4c2tUVjJMTXVHeUxuK0pKZGZKRDRvSm95Ty90Mm9zT2lSUVBRaWhmUGU1RGwyTDZ1czRxbzBLbW1vZVYxQXYySzh3QlYwTE1na2poRWN3NEJoY0dqSStMTGlaZVBJaUhIWStKQWxVMFFjNDhMOC8yTGhtaUhITTRwbkhDUHJVT2pqbzBYOVp4ZWwrbWkxdENVL2VLZUtnMzJGWW9jMVQyeE95QUhRK0xWUWZraW15UmdDVy9MSy9vcWkyQXMwTTliUzUzU2hlRjE5VlA4WWUwRk8zMzNjeUY2VGFhODVxbi9nZmtJQUFLRHB4UmZTNm5qOFlLZjY1WDlUR2ZMSGQyOC82VmJQak4zbnEzRzVRL1FDcFRhdFhnOVhaRnhRRGNYVXhqVXN5LzcyL1hUcjQzb3gvdFRFRzY1YnNmc1Q5Ujh0ZEROYTEyOGZPdC9VTDU5dlcvdjdkNHhBdkJJendCRy9KaTIwR0Rnd01TRUk2WXRpTUc3YlBzUlVEOGtldjRCdUFDWmpCSmdFc0F1d3FpeW1yV3ZCRFRELy9Kd1BLcFEvcVFqNW1LcHFHUG1oS2laUU82VlkwWGdrZldKbS91RzgvM3NXQTQ2eEtTalNTOTNQM1J6TDhRdmtmcGdvcjBrNTUwQTdIQ2M2ei9CRS9mZjM1RUNXSlNNb2xzMFVkaTRyaks5SytmdVFvSFNLSW5iUUlqaHpyaUl5NVMrVlpJYWdTanNkNVVsNlFsS293d1hQMEJMNTNienlkeWEzcFFYb1RJQ2UxcmJabTJYeXpYSzJxRWg5d1N1WGZKSXFwUitsUmN0bVhEdU04NzRSbTRKbTFRTWR5UXJjM2xSM1NqQXRtUEpSdjB5KzVXTjMwWUgzN2xVMWJWaFJSN1NKMDczcFQrRk1iWjdacS9weEx1cjl5RWJ0L0h2aC82WGxWMVdPalpVSWg0VGl5WmwvOWhzZjhjTkZCMk96eWxNNmpMK2loSDdNZi9OY3c4eHJab2xSRERBd1RWVVNlWERreUhSeHdvY3lGR2ZLU2QraS9zK2tvaFFod1RheTlRamthWWNIdllzZnhibS9yQ0FwaWUvOWRkTS9ZN1F2dnBqU2lhTTExZENLb2R1ejF2ejVOQVlqVVBydzllYWtaeDFDOWY1MkxDSCtVUjhiNmNwTzlWNHVqMjdZMDBLaUhqd3JVSmpmNXBOR0tLVkZqbktkYjFnNU4vbnE5OWtHeW0vUkgxSVhwdm1uZTRRLzN0REZrOERzcCs0cjdlL2ZzY3lHZUQvNlZBMVpaQTJVeEN1YlJON01SdTY0Tjl0Nm9sMGo1WVgwU1UvMlRRbG1oYmdDalZ1T1dXSk5sSUlPeEY3VjNkUnlUMXR6Zzltbm1XNmxnYXVMZlUwdEs5R2RuL2RnUXZDU1ljcFhLbW5XOUxwWFp5SVVSUU9jd0RMbVAzZVlwTWdJK3JYUnVUa2pMRkNycTMwSGpmeGtUd3VkYnFUQ0hDNFpRYkhIRHp4b2JPMjdUaWxpcEh5UDgvV0RuTEZKOXNtSDgwb0tNcWRNOTIwOWk3UXRxdlB4WkdKM1UrYzNmRXRtcWVQalRvWGxrclc1SitPVmEzK29WSVYvVDIvOW4vczFWMDBvUmRSZSt2NURsVFYrUTNVU21xV05XV3I0S3lFU2w5MVljZ1luNWIyTlZPc2YyR2lSUTFrODhzamhkWlNmWkRaaER2emJQOVRpTTJXZnVyVlJQQjZGdnRRaVZUdGx6SnJTbHBhb09jWkVqb1dDUlFRa09jczVoeVA2R1lsN0YzU1VaQWRLVnM0Vzg0eEV4ZVBGT1k0R1ppa1ZZSnUwZWtScm0vaFFmOG80aWQxTFl5OHkyamUwN0RWVyswbDNXdDVxNytrdnpVYzQrcU82NUZ4Wms3UTZwM0F4UFE5aU56SDh2OGtacXdnaERXMXRtT3h3ajMwZ0tydG5Oam5tUG9EWnZPYTJwZFROanYycS9RNnoyZlRkVTFRbmlSWDhFNXgrK1VxZ25vV3JZRnFlUDFOUnAzdDFPT0wrSWlyVHlDV3l5UDNIOG1sVDhrZi9lODZzL2xDZit5alNQa3Y3eGFzYkQxcEo5WU1zVFVyN2UxcFBpV2RIanNwNWN2NytPTjNSZFU2VVNRaGZ4WlMxaXFvZ1I5cGNzN0JROERQbHFKMmFzT3ZUd1VyL25QMnZ4MmkvYXVZMzI3WXBaYU5vN0g0RE9jUFpMNW1XdkRGMytZdVZSaDYxcVcyQjFkTmVQVU1BYitNTHZUV3B3VllsNjQwZDhNUjdjMTNwUlJnQVB1ZW1ySUxpUnYvc1I1NXFTTmFuRFJmWkNTa1BONjQyRE5rei9GcWU0ZjNyVFphaTRvcUZPeW41MjlMRHNtWlpFMDdhUG5nR0pMZnpSWHkwRzJHcHJ6NXVRR0M0Z2FROVJsT0Vvbngwa1FNc1NLYWlLTEhiSkxwVHhaUWhORW1nYXFpQUZuc2I3cGJqU1Zqby9UODBNSjFRalFxdGRFcFNMVjhFVUNqV1UzWkhRa1gxRHREU011ZmpYYm1SWjU5QlRuZlIwb2ljMGJUZWJpMDd1WGRQWnFBd2phMVJkMDN4U3FiMTdrV2lyVGdDRUpRamQzdmRtSmhybUJhU2ppUmdBTUJJQmwwamUzeHE4ZkZ3TWUyV2c3RU1hYzR2RkN3dFJVYUZvYkt0aW1VdjJCdnJFR2phYUZvUHNCckVkL29YZDJoYVFkejA5ZVAzUzdJYkM2WTMxME5EVVZ3SEw4cVZOWTVUeGVFVmF4UlZpZFVTbHNXbXE2b0cxbFF4UXlaMXVJemYxUzR0ZmNqR3lzbDFyZk5pbjRsRzVncElVMjlRbEdHRlNHb1FJcmlZaStuWG1FY0FVeVVoSTF4V21QYklSN3R5SXlmUHRsVnhvMnVtemN2cnlabmVtUGxjUUxEcUxOTGFhQTBsTzQ0SVFZNWlJbmkrSVkxTGRqcFNoblN0L0ZLWEtPQ3YvQURJYm10dXN3WSs4aHN6cCtaRk1iSW5WV2VmRmpLdE13MEg1RUw3WU5qQTBpbHVCelU1ZVVSb1BnQXlWcklBdzVNclRQYlkzNmlwNmU0Yk5qcjNKeFRvbVlMajR4SjNndFZNVStrQU1heldENWdSRTBtYXFuMHJhZXF0MzNNUy9Ud1JWb0kwZmxVaHFIckgxcnJiNkJDTEhCR3M0OStJa2ZORUVLSDRpck9NcjZwTkU4RlNFcUlQVHZlME9ya1E0K0dGaXJTRFZYQUxJekdHd2JqTm9sK3BYMlhmOG10MHgxR29oQXlubHl2cHhOSDhTYW1NbFVUeE1hTjYycStaZHYwSy9WTHhWV2lTN3VFSU9obFMyb2dZRFdIa2lYWHN6T0Z0QytIblVqczNCNWNNemFvU1hzaXJxRzd1RENlVko2RE54eTl4aVZQVmlUSVFDK1VYc0syQXJmTHdJbGhLYmtTVTBKSjJ3NEppRTlSTHhjVS9vcXpSUFd6NVVLays0SEZrOWZINXBpV2QvbXpWUm5sYjkyUStIdzRPNW41TEFnQXZsUkxGaUlkTVdudG5obVVrQVVmTm52QVlJY2IxTEozaWNSaDRUQVFvV3diNEF3WU52QVFFZzRPR3RjWTFCTHhwYXVPdkVrbStrdEFyakpmTEhSNlNrNVFNTklWdWp4a3J1ZnNESldQYWNyYXluaDVKd0xoN2JzWEswdEZFaHhmWVVhWVpxRnBQQzZKRU4rWFVob2RNdWlSblg1ekxyMllHTURyWjdpV3BLNjBLUHlLVzhOQWFLQnVrZTV1c1MrNHgxcmFLLzdVeDJjRk1wNjNGeU95QTZ0aTVidE5reENieG5NdDh6N2I0NnlNQmhJd0dyWEZDeis3akNlVDV6WS9SdXRPNkcreGR0N1NyUEYwT3hGWGtEa3lyczRqSVFpQjNPVzRQRXBJZUt4UCtvQ0hpQUszaEtSV3hwcmE4dzZwQzhQS1lOQWdOdVJIaGxSQmRna2hGTEoxWUVCUWtxMURIT3Zzd3V1OGRGTlMrNEdQck9hajM5Q29XMTNqRjl5U09Sbms1Y3N5RmgxdHNiRk0ySVN6RnhSb1JObWJzd0x4TXVONE9NYmUvMDBmNzZ2VU5lVjNHZW9qVy9ZUDRxdkVMYXdvUk1SL3JRaGpwenpCOU5ZWEx5bEZibTlFRW5CWjBEMTA1RWw2SGJFY3VSREZpbDRsV3JvSU56czRjeHo3WXF3bENRMEpVYkt5WGxTSzBrS1dUaTFmcVJ1S210eUsxdEpTVnVrb1p6eVFpZ0dFZnlkRUtSSGVLcEc1a1oxNlVTTWpybFB3TU05WnFEWGVNRWRTUUc2a1BoUm1xL3JlR1A0QTByL3NGMG5NcTlhWkE3K0U1VTUyNUVjRmdPdDlPOEUvZGhrVE1QWE5JZTA1V2EwYklTQVlzMjRhMnkyUlUxTFZaRzJucU5vRmx2NlA2YlZLV1RnRXlWQXFNcUIwbEN3RlQ2aGxXdnlEVTNxd1RkbTNvQ2ltTExMTWp0V1dsRFpvZlUxb21DZ3VYTFYvemFVdHV1ZXZlallwUWg0K3NXZDQ1eFJoSTZaQ3NaTFVsV2EzWllWclc5MjkvTUJnelg5Zk1MZU11eVhjQUNvQkpQSDBLMlJNUkd6MFNpelZyc0tQc3JhRXBTSzEyNFdnckFVM2hIdVVYUzhKdVJJSXdZRXNoTUcyZ0FubHhvU2ZROXF2L09obTh4S1h4WTV1VlYwcGdyYmJNakJzODYyTmJsWDBkcStFS3RUZGJSYWxoZFRUSnozS0lwY1U5SmZONm5QdnhySFMvZ1o5QTFTN05KbmdLN3VOSm83VUMrNzBmWTNOOWlmcXExRm1xbmJXZWxDRjFPUytjUnBjVWRDd0tDNnRMeTV0YXJjK2NUVy8vL0RZR0h1T2FSL1hLa01wb01xWk5ja3c5d2s4ZVFyNGtCMzRvS25jYUorUHJCdUtkMWZPOFF1RjJxSHFmbGIzN1lJMHFrVTlrZm9DOTdCWVdsS1JuTU4vL0o4b2gyMi9uNndjTnE4Qklsc05qNTBocmdhSnBWSlZrNmUwUDl3ckhhTzJqczJaS1MraXk5OEFXVFc3NnhBMzY3Ym1UNnNOYkU0SjVCWFQ3dW1CNHEwcWV0OFo3SThRbWpPcmNwY0pOM1EzUC9zU3YyZkQ1aGpjcEVJWWljZWJYSmhOSmNDdWxHQmZMMUE3MlpEYTA1NmtmQzY2Tm5mV0RteEtiYWtsdlhkY3VvV1hheERwcTNXV1hoVFJnVUVpZ1dkLys2S0IyVWdxUG5uN2FMOFc5MXlxaUo1ZGlBSUdBWS9rQVNUc29nNG1VOGx3aldsMU1QNXVXNVBhWXFmVkpHRFJGQTBoc0hpdWlSeGZxdWhBRm5YdTdjUFMwb1czL0piOHNRSE5SdERLcEk2RkIzL0t3cTJreFd4bWRGVmE0T0ozYmltNUczNDQvZkVVMTZ4UG9abEliSGNGM3RxcXEwZGJGWWwwMk9CMTFWWTBtUW82THBpeVNWUno0K2UraW5JWEhWa1poM0IwTUROOGQvczRTdzVIUEU1aGR6aElqNUhjWlNLcVFsbzJWNXpaMk5FUlpKZzJxYzlUNVFCc3JabGdwZ0FsUUFSam93ZkJiYVhOK1ZpY1lvWHY5LzVZeGd3Ukhmc29RVVNYNU43WVU0MXFXM3ppQkVBcnFFRG4vbmYrbFRaVUFLYW94U1NTbWFsOXVSTkNST1J2eWhpdVNYVVpoeGhqeEhmRWR3bzZHd0VCZzBOa0NVWTVrL1dxaFhwSkZQeFdta3JmbkZzZmE1bWRmejNCNXFpTXNGYUc0OTNrZnllOHViVHg4ejd0RUM2SU1PWitMS1JhQXpLdHVzUUJMeUt1K1hRSk5kTmZpam5rNXRxU0hFbGVhaG0rb3lzZkFGMnpORm16aEN3MEd5Qi8ySkVUR1JybXhNbGh3QVE0OVlaN0JGZVNOQzJ4MElsU2dER25FVUVrSUt0Tk9rMzBNZ21KZGlJWGEydjFYN01xZHFSbDcwc2RjNjI1bzAyR2l6MkU0aFpTcXl4Q0ppS0ZUOG10T29UZkpEem1nL0d2a2YwOWRGNTNPVlZCaVBwNlFHeEhlekxERlAvclRvY1NxSGNqRXozUk9mbjlzMFU1bHRPdmxIc0FiT0JQblovQUNUbDVXNWNvOXJSZ3NmYjlXRVhXZFNEMGhYWHpEeEljSjlsVHhyY3creUorN2EwWTZ1bTJVQXlNNitOcmZxV0FQaHpHSStOdzRPVXdQYzBTN0xoejFvOEllK0h2ODczRnVwSTNya2FqZTE3VkdDd1VjTHpjWExNeHhHSittVENnblNvR3Y2TWxVWkNSVWlyUVY4aFpINUdXZFJvZFl6a2RCM2djdURLUGVmYmo1Q2hUN05pTXdSaThNSmVaRXNyS01Mc2lYdU43Mm1aYXNmcFRiTHRhWXF0NDhndDRrOGZoQkhITWhyWWx2ZmZ6SDBNTHFhdkpyd0RGUmo2eWtVTnhFUmZvTHBaTDJ2RTVoRzBWK3ZyQUhBMENLaUNQU1h2SjEzZlVEd2dwWlVKRXNsMm12dkhqSlRCZFgydzA4bktKTUdOREtJeE5SclgzVkRSNUFhcWlPZEpyZDZLakkwajFDZXF0bU44elZXUHZXL2hLMytmamZNZnJidFJvUDZhTXIxRVJLKzJGS21qTys3MG9IVzYrWXloRXRlZndvQ0dJQlhFZ2JWcHRIc01pdGhKeEFvNi9HWGxtMlJwU2M2cS94cS9UZEo0WFYrOGJaQ0dyd3F2T3FNa1h0bTA0N1pZMVVLRVltT3hiRCszcHpkejNwVmMyYy8vaUt1WVBzcjE3V1dMVlkzTUNWRmhuYWFjdjlxYXNlSlZ4U3krZFQ2dWcxZXNYQUVuZkVxeTRVRjNjczhpbTBDbjRUNVdZa1dseEZkVkJXZmUxalIwRUFDK0pDY2xzRkx0VmZ0ZGNzUHNjcE1XbmxuS1ZlVVo5VWl6S004enF1ckhIU1kzblEydGx2NmtVdlovaTJXUnl2NFlzNHJuOCsyVm5oc1V2WWdVcFJxa2ZMN3BzYm5DWmVwY3lQVW5YV2xWODBUUHZqbjV2Z0ZJckdUTjFrRStuelV3TG5YaDZVL2FPZ1dhOU1XM2FCRDVSTjBIdlhOYVZWVjJ2RHlWY3lSVzdWQ0VoMTFjank4M3poTk5XSStmd0g4dmIvVDh6MGtPYjNET3BmTlZRTnpVVVh2d0FaWEh4M3prNVJVYkswa1J1djNxaFlEUk1UQXdkdVFqUXBHV0pzbWtLMmRQcUxNMGU1NWw4TldreE4xZnpWWFU3T0l4VklMWnpyMGVIRGw0K2o5OTREQURHU3ErejAzdTlkVlc1WTA3eHA4cjhuZUFQLytpN0NHWmVGWThsTTF2ZFpYOEQ4OTNlMU9yS0h2RXJ2dEN0bGFjTEJEZjdIRVdoL0VlVjEvcHdBZStHcnovK21ta0g1ZncrQXpzWVRtTmFhTjhNRmZIZnFLdzdONWI4MFlSR3B4bWlLVG5tVWRpZjRCUnlLY2hHeHhyNWllUjlwdTJoai8weGN2a0NocExFeERlSDJFdmFhZlUrcXVsVTlUeTc4VENveUNMZld1R0tsamZ5dmxqWExxV21VM0NPakl0SU5saE0yR29QMXY4QnFraUdsaGdFQWw4MmlKRkFzb0lZVWRna00xSHBCZ1JPaGYwS3FROHByVXN1Y0pidzZCcFZMcDlIeU5vWU9uRXVKb056Sk9MVlVORXJrMTdTSlZmYVhqNmhudndnbTU0UEtQdFk4cGxjNTVWUUhhYnhhaWFzN3YvdC9Uc2NJTDFGc2tOdEVXWGRiU0pMSFhZM0JkWjdzM0c0YUVpN0Z0dGlNbW0vMHByKzBaQWNwUDVtNnFyYXdxbnJLcXZIeGMxTlJUM2lPc2tLU1AxdE9hQmpDc1plKzJWVlQ0ellRT0lSYzNJT2lsRjNqN3Z1R2pJdEo0SFVSeWU5NlhuUmpRTjdCVkkvZ3dyVmxZUHBZcDAwdDB0K0tOTTRZTDhGOTJrVTVQeGtHZWV1Z0k4eEdjT1d6bW9aY3Q5Zzc5S003NjFqL2x0UEk4SVdRK0NmK0RLS2RvN244ajdaQ0lvVWNJQ1hUTkQwSmZpRzRVNUpYNHZ1TTFzL01mRFU3bkoyNGFxN3Y4dXVJN0gxU09VTy9JdWJqWEEwUlVPSXFWbFh5cmt5OTdHV3orOGxWeXE3T2ZlTHlUSzVXWUdSRkd6R21DZ0JHaE1IS01CdEJCZi9BYUpldW9KTmxzT3ZSa0N0ME5Lb1FGMjU3UEdZUG1pUTB5dEF2UHpuMDMxZ2xIdnh3VldGbW1iK3F1Ymg1NEVpTDlJR3NNTnhVckM1a1oyWmVqZmRhZEYvUFU5V3JtNTNKL1M0ZHVnWGFQazZvUGR4Ni9kUXNNdDBaNytxZm1sN1NjdGdjUS9kOTFDQmxxcGhxakNDNW1ES0lwaVRVOHNsQ1FQdU54TDUyb3RWMnNqZ2o4OXVjZS94a3ZwRVZuREI2T0VzT0NtclNORldjaTlLUzBsZjRQdk53NVZmOHJFQ1RWNWtxTEtwYmNYeXVKbU55K09FaG00QlA4UHpTb0hxc3FBb2NkcTdZRDFSS0x5TGlXMmpiMksyaFVnNVdzeEZweGRwVDgrYVZLM2FmNDdlTStobE5mYTZkRjRpN2VKd25rOGNkdGFwbEtyS0RqUDIzQTA3b2VmWS9ZaXRXR0VlTUQyVmlTWUFoZkxNVkhjQnFWbkZBYjhKZi8wZnJuREZJeVQ3cGx4V3VrMUJKTm12aC9NWlFYTExrMCtWeS9WME4xVWxaSk52dzRTVk80OUtaUHlwbG4wL3NEcTI1OU5rcXRYM2l6YXFrVCtQVXQrVGEzcFpWVVJUclFwanhtQlM2UXVJWjZmOU1hdEFRZEltMEhJRkp5VkZNZHIzZHZDVnljbldQRytCU3c1V3p2eTM4aUY5Y0xhR29tam1IUDd3OWZGc1hhNDRWV2JhYU5pQXdxZE5iT2NHeWFmL2FDWFA4eDNHNDBOeStyRWtSY0NRSk9CeTB5R2FMdjdJdWp1TWswMXBTRVAzaEZRRUpJaFJGT1hEVVRZZDE2YmFPNlcvRmdhT0hVck4veUxSYy82Vm9WcUtFTlZXTVFqNDBPTUJZa3Bkb2JYU0ZkbjY4Yi9XMmIySUt2TzRjSSszWFZVK0pKMlVvTEx0cDNaeFNSVHRKbURaWmNhRTJ2SHRJVTVwRG5INDBkUTViTTFPSlAzODlmbm04UDhjYnlNOTMyWWZZUzlMU3VjSklWbmw0bGQ5Tkt5SlJHV2JJdW11OXB0ZHBPSFNwWVdCTUt4TnFNU1Y2YXhaZnYyeW5hQTZ4am5teU1yWHFXZHROSVRjcnM4dVNtaXdMU3ZrNWhkTlRyZkZtTFdmV0tLNmxTN2R0VXNmS3ZMRnBINmdWbENNK1J2SDRkWVdEb3ZyTE5qL2V1eWt1K0Z6M01VZXNSUld6bTFHVEhXUjhYdFhDblpTZzQ0UTJOdTFXU3JMRWtjanZTOWE2eDNFUW9SaUk0SVJtMCswRFB4NkducFIrYTRGMWwyVmJkbHl3KzVDcWJGblVIN3V3RitQcHVQck1oSXJqem14SFZKZkFkREh1SXlMZ2RKck44UC9PLy9oQ0JNVm54QlVOckpqWHg3dlBseW04QlN4cGtraGQwcndIUCs3VkUzMDFqNjMvZVdEQjdvVXRLTWtSa0JHSXpVcStkYmhuNXBIMHhXTmtVNjdNdVA2eDJDb0p2ODJIU1lWdnpucGJLeHpjZC8rWWl4TGR5eXU0YUJjVTg5N0dKdmkxWHZMYlBtRWYycitRbENmMlk5d2JHQnFaYlBVeW1MRHlMdEsveHNGVEgvbzBIYTFYUHFLTkw5R3UwekxuN3lqalMxVVBGb3hpekdGVy8zTGc1QnVXZHczU2dYMFRZVEkzU1NheFR0emZCY2RpYSt6eEtjKytZMUFscUVjMkY3M0hTZWVwRERXeWdyd1R5YWFoYzRNNkRnY25BYVNCTXNmcE1HNFpQbTdTZlVYeDRWVWJLcm9qenh0SGRpMEFnQVJWVWFYZ0hhRjIvUys3K05wVC9SV09xdDlNYVJ6dFJ5Q1Y1cmNGT0tpMHljajdWMzh2MGNKeFk4b3NxM0hJT0d6Tnk3UkZEaWVpd2hhemhsTXdtNVhkL2NCdDB5L1RMWWNja3pIenB3amYvNnlsK1FnVngvL3BwOVJLaW12RU11OXFtMzZwZHRHNm5LNmNiaWFEcXhybG0vMGVKRnkzOFhNZzdYRlhRaTZ3UGthQnZGUEd0Y2ExLzl4SXl3MzFMQi82dENXdk1MbFUwS3N1YnhoQ1hKNndDMEU1K1RpUUZqY0JrR3BPRGM3TW1Lczh2R1JKNnJDNy9OSHEzR1lTb3ZFbUFBQXFOZmtqYjdqd0FIWUtob21BL3d2emRIMjdvSjVuMEhPbnRCZlZ2LzJmdHlvYWx6Z0dNQUFRWUNtbDZTdTdWaUJyRjJTMC9SNTFwaTVQN0pZc2xRNkdaMXBITWJlajVPUjdxdFh1VWhRa0puS0hmd3VibkFTb1dWUTlsakoxYlQ4ekJSVXlDYVNuc0pZM3BUblhkeFcvKzZ3Q0Q4M296SThwc1NoT3l0UTUybVZlZ0Q3OEJzdmI5Z05iNVpFd1F5K1lRRytTTmVHeGlTKys3MVFFVFlZWWZkSUR3VXV2dGVPVVc5MTI5TmhyN1JLOVU2LzdUVVBWVEFxdmZINVJsM1g4MnQwK0NSVU9pQjIrYmk0TUFKVGV4bmVubGVTZXNlUTliQktKaGFnRE9QMUVVcHN0MklkTEZGditLWnZ6ZnBnTVBLY1U2WVV3WVVkMWJ0SVBjVW1VdUpHdWhoZFhLZTVyU0NZa00xWmQ0QUJSM1BhM3pka1BoUFo4OFZZeXJqaE52MVF6RDRLc2NPejg1VlJGWGQwMjFjcmlLYjE3cTQxOGRjelBnNVd6Z1FOaVU1L2g0a2Qyb2FKa0xWZW00QXBhOGtvM1ZxZGx4bjQ1RHlrcEtTbDJRSTRRQTFMcmdyUTkvUlpGYVVXOGNSM3B0aW5LWU4rand4VTJET1NHMDRybVBvcC95SmVaNDRMS3N0WU4zS3IzenNHdEN2Sms3Zm5STCtXNkVZcXh5ZWprdWFUeU42TGNZUnd4YmNUY1NLUGlpWHNmR211Zm4yZVNwZXA0Ymo5QzdqNnZWOCt2SU52WGRTeGIvVkZMUWJHam5PY21VQU8yQ2xXdGMwdjNvbGVlUWs2WmJQaTY2QUZTbHFaYjFCSzVxRlZ1c0lqR0dYQXFBK3FXL1BNOVRMOENGcjcvZGxMMHp4NlRKb1pLOVpFRndlNUZ6Rmx0WEZzdG1wLzEyWHBGZldWRmdldmc1TUJYMHV4QWkwLy9HRlVZbWJkMHd3TURDbnBod21UNkk0Z2hjcjNKRmhNTDM5TVQ5T3V6RjBLRDZySWxxZEVpUHVNVE92M3pYYk5OcGtpeHFGZG1IeWx5QmRqS1piT2JZcDVYeDFSOHA1ckIxRnBLblhST3F5YWlDQWpLOVBKWTVkOGF2cnYyM2JoYkhLQXRBUTNhSTh3SlB4cWUrS3Y1Y1NFMzFmZGpQQTA5dzBESy9IOE5TSk9qaUdKUG5mNXB0MFRoSC80dmR1cW1SSnlpVjRsVTF6NzM3L3J0eVFnL2s3eGwrR3M0a0lLdHJRNk1ySnF3YzdERkhIdE5kWGpYbHNFNUJlejMraWlLSlNSN3lTZHFLbkFFSUp3NlJEcU92T3BtUE1CVHVRQUFhblNYOFVNQmFkS3RTeS9KMG03dW85WWFuaGFoeitKdzN0U1ZqSldnUDZHbFcwS2V6YUxWTURVdm1CRy95eVNnK2JpVmE3dHN0WmY2dnMrdTRVdjVBS0R3Nys2cHNSWnpkY3FjZVJVdTFDSjRTYTU2YVJxR3pxRmFYbnBHaGlmTEIwd29kdUEzTGg1ZmxFT3NtUWZJL1VjcDR6djBTbFErVU83ZnZ6UHQ1T0RHY2xuc210K1pFaVN2enlaZG40UnhyMEtUbHErSVdrU3lEcURxR09VOFRHaTBkNHdRdlgybVZscnBwZGlBMWR2Z2I2WXhsT3FOZFA0SHB2NEhyeEdVNkduMit3OHFDMmY0Vyt1N1M5djFqNTV6RkxxYUpWYTJxcVNrcVNubXpDZXJ0MEtsQ25SM0orVkZKSEllS3ZZa1JCTGpHUlJRSm9sNGtIdThEZklMUlFKT1AyWGlXR2FVZ3oySlQ4WWpVbytYcEZQTmJMZEMxUHVPUnM0SENVQ09Nb2dKdGFPaUFGQ3JpaEdKbUtVVDdSRlJ4aGozTzg1M05JQUxQdW01ZmlBT2wrRndDK1N3VHlWNE16NmY4L09qTUxxMkZ3TUl5bFVQY2lrTGJDTklZM1Y2aDVDTC9YenRQTkl4LzRHblZvaXhKVGtFc2NOOXQ1RkpoRXNIcVNLR2xUSXl1ZytCYTRaUWRCUmhwUFVqWExwVVdrclhoaUdLUWt5T2VvWldJejV2OUtkbm9oaXEyd2tSWkM0aGpVNnVvNXNRQUJKdFE3ZWNlK1dXb2I3a281bzZQdWNjZnBxNEN3S1Q4VC8reEpxVWxYVmNYNXdTaW5nbXFBN0lFYWlqalFLcTBMSlY0Z21xT2MxNE9YV2NUaFFCTzdiczJBVDBwYmJtNEZHc0MyQVloZVB4bThGWGxVaUFTUnZlWFltZW1qTU5rZ3VNSkFwc205ZHVHWm1GekNsRVg3bC9LYXFyamErV1hGQXFhQ3AyYnBtVk5PSWNsYndMNkhwdWlsakNOaWozdkRjYTR3K2FtYkRYeUprMnpVYjN0Wmx5NTlxVnBBc2dRQ212OUNRcVlvTlpEVFVVeXdNZ2hpYnNvY1dkZkxjZ1NkVHhyRk1zbWFkUTR1cXcxdGsvRVhjZ1pFYUo4ZXBrWldGSG1adHphdzZ4YlZWTUhZZzB1U2VxYWdZc0pXQ3NhdVJRU3BWZ0FCWDgzcW8wZWovV3lUcGZOYmw5UGVyWENFaEJwZ1oyaHFpeXliYWo4aWpRemxRWnp2dnk3Wi9wWWFoZ3VTZ0xJUUFBRlgyUVdKVDljcVdNd2lVVmRSbUZyZzcrbm1PUDZKWUR0TDN0ZXFmMHRlZXlFdVArNU44c3JhWDNnRklId29sRUdFUGdPZjViWE1uUkdQdlFra1R0eVhjU1d6ektOb0NsdlpQNkxnNEFpSXhwdm9XN0Q1R2drdXlOU3ppcG1CSjZ6bXZSQzIwQUFCU2lGWVF5OGZlYmRNQlMyTE5TZXZQNE1mUXFxSlZOLzBsODVBZ2dVZERWSGJNbzV1UkRETXFGU1krTWpYTkdqem5OWEtVQjJQTE1QRFZicld2ekhyTXRHai9kTHdnVEJtSExvaXF5SkZsRjRrUkZxTzNYZTNtaCtmTndRSnNTbG00cTNXR2dTRWtuamdVZUlZMDExVFhjZkZsSjhXcDdiVmxjWTNHZU41UXJPVDN5ZEcvVFMxcnVvREZWNkYzQnVIM1ZuVndoWnowaVl6bnR2M043bkV1UDdLSFhHbHdRUU80YzB2aWk3bzhscVlGSVVVdmNOMTZJM1NJVzNqWVZxU25nZUlXU24yTzFuNWx2NCtLeDlVSUg1eTBHMjgxMThMZ3pUdUltOGREMkJoSnVHS3pGeDNreE9pdjVhUElpb1M5Wm51T2crN3dpWitVczQxcjlRTWsxdlhDT2tNaTY5NksyZithcytEKzVGRTFLK3lQTFZqOGwzRWtKTUpUbWFBNEUyTVFIUWg1SE00ZFQrV1lCeHIxSUNQY2JGMWptbWlTZS9FRzVBa1lQRkdaNDBsTGx5WU5KeTBtWmFMRkFJN3dWWHkweVlTRHZMNnVLZ3lCQ1lnL3dOb0pCeElBa2xKamt3RzQ0QVRBb2dTUjg5bmxuUW9JSVlpYmc5YTMwOXptYmtGZVZ1SkU0UTFIWjIvK1VKek9rQjZHZ0xvekE2QWFveXN3cHNpUUhHOUxZQkhFS0trOTd3WWExTmtXS1ZLaGdNeGRXVWFWbEREa3NZNmkwT1dJS2NLWWtXeHc4VlZKd3ZsNCtXenliT3JhNFJRZ3NLendUV1I2VEVzS1ROZ3NHdFpRSzVkaXAydTQzcktyVjl1Qlh0MDhQdHk5M0w5ZVhCMlk2YWRLRnd4dEZ1MFgrY2VlNWt5MGx3MlpnZytLeVo0MEE0WWdlRGtrdVBSSTBKKzBIektqcVhQV295WHFDdXVhc2tEV3ZmanZBOE52SDUyZkwrZUJrZU5KczVBNkgzWHJaMW1sczFuaWY3NC9xcGs5MW9RMnArU1poMzdERVVVaXFETHlZTXpBUHQxZ2dHcFlxeXNEeXF6UVoxRklCZ29ZZWhXMzM0YTR6cUk0QzVDd3ppSXpUZzQyYlR6eVpvWlFZMzFlNHFmZmlXRGJJRHJ5WG5PS1NNaVRMMFZXTUJ4dktNWHRhb29uU0xMeG9pQXdONjNRb2VRUWZ2Lzk0dUJtNUhibGE5SGgvZHJKWlRjY05Pbis0bktDTnc4YUtrZEthVTBEdXJSTGJ3c2pnd0VHUUNBaUl6SkFZU2dRWlZ1RDhld0Myc0xrZHNxODJ4QmdNbmg0TDd3bTY5T0JaOHl6anArQy9xNVYwWGhkOFJoWFpHeEU3SkFTbkZ0d1lyVitDZTE5WENvZnRwaXE5Y1l6OCtsbjg1QVl5UXlpblFZYy9WaW9sSmxjVGg0RWV2YmFCQkJOUkU4bXVQQ2RPSUxLV0R6TVJzWnpwSElrQlBJZm5GcWZnZ0tmUkdTdW16Rkl2SHlKTCt5QlhYVER0L1JYRE9sTWtJcnEwbHZFMG44NE5OWTFtb0tidkpUWUQ1VjBGc0l6bEZVcUtmSU5FUDRJMGZ3a0hHMit4NFpHbWlDQnlvR0dEdG1HSXFBV2c5YTVoclNjTFdwclZVQVViR3V4RHhGdkdtRWdERWNyT042QUM3VC9YSDZHN3RYUmJIeW1UNzVid2F2elVrUkNYbWxtcUY0dzVMaFNNNGErU2ZzR2ppbGFHRGh3N1FSWWdlOVRJVEM5VFhWSlREWXNRU2lkRjZVNDIvTFpONXovZGQwL1JHVWZuL1FhVWludjdudzFQWmlnS2c4QmtnVklUVnRCREVDN1JJMmZPZExCUlZXdmw4U28rUzNRZkY0cW1WMG1neWlWWFpXWnlpb29BV1hmc0hTZHl4ZHpmaW0yYkFkRGRSYVY1MzZDRzYrUzdvZTQvaVpscDhiZFBsdk4rRitVM3A4MmFrWHBSSUxQR0l6NXEzSGw4MGJ1SkZPenZsTmc1Q2M2dFVza2w1U3lnSkpTYU51UXp1RU16dEJiQXJMcUo1VHIzdHVJUUVlTVM1MVpLb1VwWmNrcUwwSmJpclpXRDZsUkZsYlZ0QmtCMzF4VG1uWkFST1g1ZXpmd0FYM2Z4MGN6ZGZFVTZBaGkwaWE2VitBektWOGFvMEUzekkrSWNZVWdBa3htQ2dHamxoM0FmM3hTeGlrSFlOb0Z2RlQxbEtVVzlFWDVsZDRlQ2hLUGdZWDV5UWxQcTQrZGZDZU5MbU1hRzkxYm1sRzlnWW42dndiYVV6Wkg5RkZCQWY0K1VOak1pYjlsWWJmdnhjcjROUWNxU0pEaTNUUWZvN3FwQ0xIMnFNZmx1aHRheVJjc0FYTWJsNXhOQlMxbVB0TmowbmtXRHlWQWNjMkdkQTdzTFlEZ3gyQ05nTkVsVHB6ZG9DYms1N2lJWFI1QVZUcHkrR1JqSzJUWUJvTHQ5MldnbkJDVGthT1hGSnhtUFYzNStKNEl4MG04ZzFXeGh3NDRCb3o0SjhmZ2MwbG1ZbUdTbk5LNnVZQ0lPN2FVZFVpYXdybWV0eVIrYWxwNUJWa0lVR0FlNnNtK255VzRjb091VVMrd1ZJS1ZmZjJWOEF2ZkF6RnlmRHNvcEU4ODlvdk01YXQxWmJDcngzYy9DeGFtcmNOa0tEWGpXb3V3TnFZSktScjBDaGZRQzY0OUtuSzNRSDVOT3BvcEVTQi8xRlIyaTRKTWplVXFmQnQ3Z0FzOEQ4TzMzS29iUFY3N1RUUWlKdXhNZEJFTWZNTDU4MGc1aE5zNDFnUWJkMWVEVkVuQ3hnelE2RUZ4Q2FDMmQ4aHNQNk82c0tORStNMDYrazdScmszS0tLbUw3cTl4K3Z4VWtSMGFGOS9oZUhVUHMxS214TVdubGN0YWphbFZhQklaWGtRbzBRaXY2WXpqaXZtQlRjeGd6emd4bktrZk4raVZ1b3htQVZLZzdka0thQTRFRURFMHB6d295L1pDQ28zcWY2empXQ2VuaHMwQVZ0bFZsMG9STlQxbjA4cHYxWXpPS00wcHkrMHUySWk0elpkSE1GL0M2aHdWc0FkTXduUSt3TitjR1owSXllY2xPczRvcExTMk5PWDJ0OWhZU0FVYmZYSHV5NGFjNFJhYWJCb2JyVnh2ajVyaHdYQXJvRm9Wak5UVjJ1VnVydnBmdytLWVFoakpXZFJJb0ZhcUNCVFFrSWgzUHBtUG5xcStKeEZERVNmUm5JNytJS1ZZaXZZaE5HTmR1YjAreDFoUzRJWmZxUld1TDAxRmYvdW4yNUxCZU9EcnNLOVdnb2pHaTk3SElNVm0rK29LcjRIajdUY2lueEJ6SEVCa0YzMVZKekt3SWhCZW1OS3Y5Vk5BcFE1aCswVG9iMzEwdm9mVDJ1T2RkbVllK1hyQnIyNVcxWFNaaGVGMExIQ0VldlZFVlUvZ2RPMUZrWDgybC9YdlJOVm8yUktVNC9QME83Y1VkTW5qMUgvTzd5YUJMVjFGbWRSQ2tTSnRaakx5dmc0QTZZZWM2dFJZUVEvNVo1d0JzNWpRYkJTRGo5Z0hIQm8wdGRMY25qMnlQMDFBeE12U3BjaUp2VWE4R3E3WVhqeFhwZUtPdWYrRmNzcGlOekEzc1d5VkVXT3NjRWFaaURqS0hqY1JSaEEvT2Q3OWthcE5ZUDgwaTh3Z3FReGRRcC9vY1ZVNjVDaVlMSXlmTTRjaVY1OTVJaHQ4L0k0UDlRcUNlTnF6cnBmcGdROWxPdUlUM2g3RHZiVm9YYkxqNEtWSUR1SXZieDl2cHlKT05LUWRNd1l3dGdISXJjSzIxU21LU2pObk9GblBrUWlJWG4xdHlsTjdwUlBDYlZmM0VyRTQxRHlqV21tdGNmMUxwdTVuV2xnMHBlMWp5MFUrelNiZGRxMmEyKzc2cE1oVDZlc0o5M25wNURQRUYxSDZpeDVSd1ZUVkZRVTJlVU5NaU14V0dwK2hoNXA3T2NrY2FHR01TWjlKb2pkYmJRVnlmNjIxNVJUaU9HcU9UYVBjS3A0UkV2USt0M3pWazBGRngvMVNRZ2ZqKzIwNjdWU3VoeExMblRaVW1abzJidkZrdjI4dkNkTUtYL0NYNFVsL0MxRExzbkZRL0I1ZkRnOHIybk5sYXpEWHNrNVp3ZWMzVzNjTFFGQ1ZCNHNLTlIyMWZRaDZOK2t2aEFQQi8vUDU3Zjd4Yi91M2w0ZWJxOU9SNE94ODNHN0tYcmcyNE4xUXpzS2pMbkRTTmFGbWVVWVA5b09yaU5aZE1ZNmZNbWkwYlV0SWVrcmpxUEVpNXhKV1dIMVQ3UjJpMjNINkZHWmt6ekY2Y0dTRUpISnJqN0ZsYXNySDFkaEJYR2trVk1NZnpqbVFWNlZQMXliZnRlbTNxQnRvOGR6M2pmOVlhVXJaVlJIODlIL2hkUlNkb3pSbm9EZGM0YnVka3VyVFNnNmJTZDNOSmRsUzloT3c2V2hpYU1oK214eXVpU2krRXcvZDFzQlAvK3RGemRicGR6QnAwcFZ6S2JjNk1OSFdWUllHZThELytyQUJFelJET2VZNkYraVN3OFRGTmpGaktFVXZKcnRuQ3NGTm1zVFJFYVd5T0owRVhkd0FmNDhOVUFyZFVjelprdmNTMUxUTllXcXcwSEQ0TjQ5czVWWTcra21yZDU0RGcycTM2bXpCRUw1MURWRndpUFBWSEp4d1AyM1E1dXo3MkF0bzA5aHlzalhzOGVjeDdhQ3VOeWRrbWw4cGxXMjR5UUpCejBxeXVpc0lpOWNjcHVIWUxRME9HZy9qMUNpRHNKVkNwUlNnQm5PQ2tYMU85Nkw5cWROd0RsRkJUQklFRTRoVUJHRzBWRUdoc0k5MURsL1A3Ti9KSURvSHNxNlFGa0Rway9oL29pUW93QmhpRTkxR2dLTnBuKzBpNENXK2JWVW4yMHRKcnQvU1RnQkNJS0U1SWJWRk1UQm1Nei9uS2ZxV0FqL0h4cTFCTEFBTTlOcFNpNDAvV3c1ek8xa0l6bHB3d0xETm5ySDZDaU5wWVlrbk5hd20vS21NMkhBQll0YUFadDI1czErZ2dibGp3YldaUU0zdFJuN2NyZnpPTlhpdmFuQnFoVC9uODVPODN5M21Mem13M0s4NG9kZWJ4aEJJL1N6UnZnakZnMFpaQUdIZ3puZGRFWDgvVXFweDJLN05CSmFVc0VaRHQyQTdjdDErN2JSeENwNDZnOXVBU0tab3BWVHNxcy9XZFU2RGgyNGZDMVk5KzlHWW82cG1CMzV5ZUg4QVJqbTdPaHozMHdMSzV1MVVQREt4aGFkbEFiYWNoYVFHZDRCS1lBeThndjFBUlBHcVpMUnBiNVFMbW9vUTFGQ0JmV3gyVEJoYjg2Z1EreXR2WnNGK3JSbWVjdERWS2tQRUZCTVcwZ05WTDZpS1lBWUVOb1NjekpXNTJ5aVFWQTdTZmtQNUo0VkdpdHBmdXQxU2Y0K0ZyQ2NqT1c0ZkJnd2NrWWhJYUlCNG8zdkhjWG15TjF0c0JoTzV3Sk1NRmg5RHR5WHhjUXo2Tmhla1d0N2RiekcxTzYyVzlRR25pV3cwbW5GUnZsd0ZXcEZWdWs0SmhNbHlDdUJhL2l3dDRXbThIRUlvREtFVElRVDNjVmtZbzlMRWM2NTJZbUVrbWlhczh6YlhPTDkvaWRmTGRjQnp5TjNlUE05VUNUV3V0OGd5K25GUUc4UmtzYkpqdGlLelptTDVMaTNsYjZOUWhWSTRGaFB0cm9oZUt5ZEdUcUI5L2s2cjBkL2lqTmduOC91bGFpZFBGZ1ZrdHh0MEduZDdzZDl2MWFra2hEcTJNUC9BUEZjcWZSYjUvb29yZWFJVHk3MjJvYU1NMmUxeTIwMUpZZFpIdHpLYWFHNUpydC9UVWpVZVQyZVZMRTlHK1RxVDk5OWZUdzNublpqWHMwL1ZhU1g0bi8xVTljTXpvbE9TUDd6dEt2RUdKN0hGZkNjWGVMRlV0aUt0VXlINFN3VFZiR0pveUhNTUhFYlVPQ1lyc2FSdlRDZWFwbjkrZWV3NWRXNmF4WHZDTEhUL00rd2hxMVRqVXM1VktBTFIzRHRWRlQwd2trd0FyZE1DK1FXTUx3OXVUWFJiOXd1dXF1ZlpQMVJ6VndtdDhnSGM3SWo2VHVMVDh2OXhhOUJpZ2Z3S0kyWmE1T2xwMUVKSVZoc2VqaUEvdDc2SDZlaXNWS1IrWmMzY0EzSHVXWmRScDNZbnA3Sm04WFBwcldubCtESW0vRGYxcGdtV2FPZzdRd3htTFJHRFpOWkRIVVRuaUR1WE8zUkMvdWJVdjJwUWhUU1JjODg3WDJaMGJnU0x6bmNHN2ZMZWVoeGZTVTR6V3QyMkcyYWtqQTBLYWxOQ3kvcUt5NjIveHZJYkJPVjBYUmVnYmxTbk9tck1QLzRNUEdqTy9MaU5iVnFPZnVaSzZYNDJLTHlXVE4vM3BkajMzeGhOM0RPSk5KRDFWU2hZN2ovcGg5T1VVV3Vqc2k0QkNQRFZGUTZvVmtkVnFMcTI4bGh2VWRzcmJxTFB3M29jN1JUNFZyN3lvYTFjcXBleHV0OEZkamdKM3BQaVVuNzY0eUNBaTBSRG03SEVLYVdEVm5Ha1JLeHdBNzYyMjY4R0xlR0dXOW9lWnNSVVN3b0xubXFxREVTaDFDNktBS0hmaFp2R3dZSGtvNTFIdzZCS1drTnRjYlFicnlqTVVhbnUzY2JGdk1EYXN2VDM1MmlFN3lCQkRqeGRobUxlV3ZnMlY1Rnkvdlg3VWZFcTdGQTZ0bHZWZTVHWWtLTy8wZWhoMHNuOHpwZC9VbFJDbytGTUZyWE85SU5MUDhIbjJSY09Rb000Sm10VFNvb01adFoxNXN6V0hkTVcxTmNUK1h1RjRPZldpYTI1SHNEL2dCODJ5WlRYSXFway9sNFdHNlprNWE4dFNxem5EU2xtTk5xQkZxbk9KQmdkN2xWSmlKVml6UUlsWm0rS2FYTFBZWVAzMHBaaUFGQmFKaGRicWR0anJqWm03anNlbk9HZysrWjJQbk5abEdodmJnKzF6dFhtTWxYaEhpcGN4WkcxU3RhNjN1ZHZ0emVsdklvY201V2p5cW9mNWZFOU5rVFF3NUgzWmZQRmNxbHN5TmRqRzkwQWNVL0lQSWE1TzVKMXdOWFViNFROVXp6SUxwZHBjVnFNTmFlbjFKTGRQTnFzbWpUTFRUZWpiZzNUMmZibC82M3VIZTM1ZlpvaXBpUzdjTDNqN0FJMlZKZVJ0OGFKSFpBYlB0VDNPUlMrWmw4ckNqaTV1YWgyM3J2MCtzV0VkRnZWY1dsVVdCNGhXRzhZMEprdVM5MFVCK0IwZVA3bzZQbEM1a3V4N0hHT1RhdmVWeVFIZnBvbndzckxVTTNHS1o3RG1pZGlVTVk1bHNqOGFaTFpTRDhDUG1KekpaZVFvTU5Id29Ha0U3NnRLQi9MbzlKa09ycU5yWGlmcmx4UWIzTlJ3bUZkbzJ4VlQ1SkMrd29qVGFIZG96VnFCODJ4MVlXQTY2YlRBSHJZdnRXL3E3Z1pKYUcwVUM0Z3RhNFVXOGdjV1pobHFPL3N3Q01LMDNuYk5tenp4SEwzZ0hiN1R2T3lOVnhSOURncy83R2FGYUtqY1RZd1JGZk9lTlAzVU1rZklKbmNuWTdFck5NMTFQOEM3bVlSMGtGeFRJSTZMVklHU0Fqc2NBci9MSmRBaVZIR0FzMG9aY2tqZWFWbnN1Vk1rL3VxTHdwRWl0V0tZNUVzWHlRZ0NRdkNXSGhkVFYvTkZDbEhhSzRIc3N0SXNaZ254Mld2UWs2ZFNxN2Urd0tkajJSdThjN1lEMmRXMnlPbUR0OHJpdHcwVUdwTnhEcEc1VWEyRHMrdjB0TEE5VUNJdTJyUmtLMDJWSTF6SHRVdnRoSkNZV1I5emp4Y1g5UVZxeEhSdGdRaWMwNjB4WVI2MTZDUGZNZmdTRDkwdVd1L3NoTkFvcEFSK0h3bkpXQTVyVnl6S1hFSFJwSTByMVVsdHloSjNueUx5dVJQMTdJeHhQVjlpZWFLUFAycTIxYUdxR0RDMnFqRmtjOHQzNGJVOVd1YW94WXc1eExiZUxuUTQxbkxnOXRWM3lFYlN4MUNpN2UzN1luOFZUWFMrMTVzaUMvSERkKzFtQ2FYVysyMFpVU21BWmJ2aWlFZURIME95T2FPakNMNTRWMitXZ1dJUm5SNUc0QnNLZDdzN3F3eTh6WmdoWXNWYmE3Zi9PTnUvK21HKzJEdVZOSTBTdjgrZ3VYcFB5WjdmNFBxbExzY1FHVWxQVFVDZUlkUlpyUVVDcjZYbU1PUzQ1ZFNpUXhsd2h0S3NLbklIUTEwQU4vREQrVTdYNUtzbzR2cDhGa1lzVW5nS1g5Rmk4UmR4bVpqZVlrTWd1RXFjYnhUSWQrMUJ5YmpJaXhWOHZsamtDRUdTWXBORVVHZzB3RldlVkZyTU5IUmwyVkhpVk1DclV5UnE2clZGZjZKLzR0S01ZOHduVmlqNFNMcmpHTDhmak03bVRuWHVHbHY1T05RUzFyajJrckUzWnFKRDBnM0ZuVWpvcytHUXJjNGI5VDZINk11Y01zanFhbkxOU1QwdHQ2bEsxWlFGaWxSRjVpdE1MYXNtVlpVSGx5bk1CdnZMb1ZvMXZOV0EyZksrNzI0ZExRenZnb285eFdBdllWNDI3YnNzMmcreVl0WC8vL2YrZXIvaTQrM3g3dko4dHdlblZPT284ZGVWNkpBcjVhdld2ckZMV1pudThHdDlWYlovaGZST1VvMXpiSFJyczN1RTVvNVNqUzEwY1prRURNQ3hqdTFoSWpZUk1reElMeGZpcDhQc2xnQ2hZM0Ixb2FLazBEWjh0RTBUdGpGbFN4Vk13MUtjUnRHdUJxYmhwVkIzeTBFUXhOYTFWRUd0dyt5enh1dkZ4clQ0cXJXekJVR205cDBuWU1SeEd5SnNVeCt1ejArMXNJU1NxOTBHUS9INHRFWkpadi9oZlZOeXQ2bmcwd2hzRDB4SXhLMml1eVdXZElLY2d3UTZQbHpQUGxNYytSTURQOTRZOUJ0MThCVyt2RENnSFJtRmdSNXl4QlVGMjlINEZTMFJPeldDdFpkTEhxT2dtNDhmN0NXV2JZMUhiRUR0dkVVMzdzRk56T3ZxWUlUcElDTEw5WGUyNGtxOE1SV3N6RVBQckRNRXFJNGxpU29MZEEwQmtwM2NFN011dWRTWlJiNTdJakludjZscUw1bDRSYUlpZ2ZYR0Y3TFJ5NVNTcGtocGNGdkc2eTd0WXpEZ3JRODErbTVzRzl2R29iZUR1RkdkVmdtbFRxWEVPVW8vNzRyb1R2Rk9leWpGTzU4d2dBLzg2SmVlMjlqUzljQmYvL3h3Qng3aTRlM0RhdXBxclp2MzdnU043SHROc245WU0xWDU2RHVCNmlPU3pndXlLcWhYZWxlYll2YzRKbzdNZ09JUUpRN2htaWkrZVRvSWQxWCtpL3BmRmpNcVh3aktnZWM0R2o3bjU1VzFwM21ENGdibFd4MHUyaDNoQi9yU0RCWU94Ym9oUjV5aUtsb28vYTN3YWxkN0grLzFEdENmaTNOaXJlVnBCZHVrdUtmeG04SXVqU0ZSZHZFSWEydlQ1SUJSREd4eFBQN1hIMnlpcWU1YzkyYm9MNU00ZU9FY2c4NzBYQ1RDRDdFUndaSHkwMjJjY0t5Y3NNV3RJdEc2akUxd0ZsbkR4dkUzWmVDRjFUdjVrUTZtTCtVK0w2Yi9Dc0dVT29PT1dWbzhRREJBVVp5QyswRkJ3UENBVTZUNHJINFAwM1BJcTE0MTRLdUhXODQyWkl3cGo2WFVSbXBDZlV3bTl6YmJ1bERYRnRDb20zazk2bS96MjhOTmI4SFh0Tlg3a3FrWi9KU204K0tkd2RXTGx6RjQ0dzFERDdtRHIwMk00c0VBMmN6L3ZFcUY1NDkrWkdZdXBxTDRSdlNvbHdkb2wrMUU3cHMvREpWMVRTYVVWMVVOSHorbTFVZjI3NDRrSC80TEpacTA5eUFJOHVtYjd2TkgvVzBjckxNVElBS0VjRTJCZjh0Qkd1Yy9CT3ZEZDUxM3JWYjd5NHlDMXNuK0F2QXdzeEE1TlU3ejdzT1BkdExLdUx5WGgyMUpyYWhNZXJGOHZUMFRTRlFOajV4OVRNS3pWeWlGTmtpR0diQUlobUdxUG5OcGRnazJQRi9lSjdlNzM4cnV1S3hldDdNUE5qMG92UnVsRHdmWjZUOWNlZ1k1S2tMS0IvaGczdGtEc3VWNHZnT3ZaenlIajNaMUt1NTF4L3o4Nm5QVm04TUt6cXUrMG1uQ2NTbHF0VDluTXRncVRWZE15M1d6d3ROZjg1a1kvcjg1NnM4RE1WRENiOWZEQklJZ2RlTlUwcWVaeVlWeDVEaCswRGIxWWFVcFZWZGw0ZGR4c0h2b2h1emM3ZWZwUmY4ZFh4bzdsa3pjWVBQTWsxMkNhK0w4T3pFZUtTSnZ6SmI1eU42T2RLWjBiL053YVdiZnhGTDFCQ09uc1lPL1RFRSs5TUh2T2hlWHBGTVFpNWFuWlhjM1laUzRMbXpuNzFYKzVCemVGcDIyNTgxLzMrUHlzbS90UXVTQnozT3FZUWVpNHV3bllZK0dRTEV5OVVPUWRaZGQ3Z1J2cDdvSE9QSFc1KzJGUEJOZzdIMG1KUFRzTTFGZVY1K0pNUVY3SnM0UzIyeE8zek5GN1V6eGwxTXNkWjdpTStmNjl4K1BGMmZuejlCem9HQTRHRXo1Tmg0cXViQnk4a1RQYjRVTjVRSW4xejZjZUNIOTBQeTQ5QXRDVVVqeUprNW5KRU14d1J6cjRNMUhjdzJqSzJiYk5yK29DZnBaK0hVWlBIeTlzK3JQejRjZXowN0cwUDBhUDNBYXlxanduV2RLeFNkRmY3VGRlMWN2RWs4bG83RkU0cDE5aWVZaDhwNUpWakw3ZGovb3YySEtudWVYekx2Zjh2K1FQSVY3c1ZIRFY1UVZ6TWs1ZXNLTG8vVUwzU3c0c0kxUTQ2OHpmdHdDZm1vTE84eE5TZVFwZG5peGg1K1h5aWRlUGx6QktYOXI4QmlHVk9RaldlQldHWSsvaUg2c2dRNTEwazhrNU5NdDJMMm5kMHF2YkVsaDN1SWV0MEwrUjBXNzBtTVBRZWJWRG40OVkzdDZmeG9xT2pPcVZleS9scXVWeGowa0s3K3ZQREFtbnc2ZTQvZVNmUEpyRUxuRytUMC9nQWNWZ2tyNVRPanVKVTQrNTlTKytqOHdSei9xK3M3aUlQRHdDUWlKaUVtUUtEUXBCa3RHVGtGSlJVMURTOGZBeE16Q3lpWkdyRGp4RWlSS2tpd2wxcEpPcmx5QmRNajhiNWN0UjY0OCtkdzh2UHdDZ2dxRkZDbFdvbFNaY2hYQ0tsV3BWaU9pVnAxNkRSbzFhZGFpUFJEWlpMWFBiVEZxblRGN2ZPQ1UwMEdSRDkwSkJuREFRVDd4cWM5ODVJdHdrY01oQUEvd2dRQUlnUWlJZ1FSNTBobG5QZXM1VHl2Uk00RGtMcGpkYmJkNzdNL1BkYm95VHZkbHJmQjh5YjE0WVh0UFR6dGUwelZ0Zmp1bnRMMm5vN01kMW5mRGNEZGUzVDI5cDUwYjZaM1hQV3ZPYkRUYzFZMkc1M1d6THNQcHNBZVJrblBJYTFYMXB3SFkxSEFLL0krRlhIcFBJeHp2R1FWQTRsOE1ueFVRZ0RpdVpqazIxM3NCOVEvbmRVYWNocmlybzRqL3RBVU1WOWFmY2cvWEsrdjAyTnd1RUtzUWdMbUhwOWJVay9HRzRXUm5SUVFReHA4RGs2K25zSTJuWWF5RmQvb0JBQUFBIikgZm9ybWF0KCJ3b2ZmMiIpOwogIGZvbnQtd2VpZ2h0OiBub3JtYWw7CiAgZm9udC1zdHlsZTogaXRhbGljOwp9CkBmb250LWZhY2UgewogIGZvbnQtZmFtaWx5OiAnS2FUZVhfTWFpbic7CiAgc3JjOiB1cmwoImRhdGE6Zm9udC93b2ZmMjtiYXNlNjQsZDA5R01nQUJBQUFBQUg1b0FBNEFBQUFCRGl3QUFINFBBQUVBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUJtQUFobFFJT2dtWEZ4RUlDb1BVWUlPRkF3RTJBaVFEaUhRTGhENEFCQ0FGaUZvSGpqY01nUW9iR09NWHhOdDNSZHdPcEhiZXpIdlBSQWdiQndqeGh1ZE51TU93Y1lBYTJLNlUvZjkvVnRJaFl4dTNBZWk3UGxPTEpmTElNQ3JncTl4YXQwNkJFS1dvWlBwN1lHQS95bGdQTnlaa2Z0UzhxM2xBWm9uci9BYXVTUmM2QXJycEhMblJJM0lxbk9oQlVZbks2YmlURXJFaUNwckp0NFJoUEtheldMYXpsc3lXUTAyYklib3NMVjNab3B1RlcySWhHNjVSaTE0ZHY0ZGxSMHVJSHJqbFVNTVBxdGF0eXRvbmRzNVg5dDdvTC9nUFdWbVd1NnpQNG1kZzI5Zyt5V1R5RHBCdWRpTzVYUGJkSlJzU01nZ0VBaUdESkF5QlpFTUlrRUFpVTdZc1FWa093SUU0d0EzaXJITzExclhhdXQ5WngxYzZuVzJsUTYydDJ0cnAxSndtdmJrNUlMV3ZHNUFMRUpYb0M4Uit4c3lKMnZlZkxyKzdUN2gzWmlRTkF5eUxjRW9CUExER1dTVnJIZVI1d1pGU3dlZXJSMVNzY0QyemVYLzF1bFJrZklRRklaRjRoRU5TYUJRU1laOVBTQlJDeHZGTExPZnpiNXZmdmM5bkJqendJY1RSWkJJSE1rUmZSUGxtczZKZTdmN3FWL1hhNmJVckYyVzYxUWhMa05qbkZ1cEtLc3FFMWN4bUg4Z08wSU5Ec3U4dTRYL1dCcVdqb3Y2aVZsZU5ITFpxdW45S2JjZDlFbTZOMjRsUXFGYkhnNUUwUDNqOCthRTI3UjhpVDJEWEIzaGZ4VWw0SW5iN1lqTlh1STRRU09zUTBkWWpyYTlVZk9Tcm5NV2duTTY1aEc2Q1F4RGtDN0h3bmhBZGpvV2NZWWhkNDMvS3phY1BHMUVDdTFqQWR4M3hQSEZPeEpyNjVxcmlWM1VtUDBQZm5QME0vWnU1ODVFSWVFVGhvQVNWbXM3dXdPVjZLYVh5d1hQWnZPZkplNzZiVU5PRW11Z0s4a1JsL3Qyc1p2YXhHZUJqRnNUalB2S0JoQWpKcUs5cnQrV1ZWM1RYRkQ3SDg2ekova0xmR012aGtPUENoV2lvcjRRZFRyQVlqTWtUZVVoMnZDVG9pbzNadTUzYkNFSmhFMXJVMFBJdm56RGRsSjJ4Rmo2N0ZnSWhkRC8vd2srL2ZsclpyYm40Vzdxb1VIVThPak1qTXNGYmNncHdBZDEzeTE0ZmxQNi9sR2JNRGprSFFHYjFuVUp4Ri9leE5YK0NXT3BMYXgzOHdGLy8zbFNyOVA4R0pZTFM3QmExenM1NGp1YU1adTR1SnpWbVhaQ2REOElMZjcvZmpXNzhiZ0RFYjRBRUdxUkVOTVFScHlGRE5pZ1JCS1Vad3BBSFV0UXQ1ZFpZamRZNXJ2TlV5NFBETWNCb1ZrT04xYXp6Tmp0amZMVGhoUmVGRjRTYm5UUGhaZGtsOFdXUjhkR0ZCMDQ0YmNNQThSako5OXRQYmZNN3pJaS9LcWhWVDdvWjAwdEhhWWEvbzJ1RzF5R3B1aUZJTm5hYTNKNG5vZDM4dmpXMWdmbnFzaTRubE4vbDBGeHdBcWlxRUlTc1ppTXJaRjlVNjh2RzFpalpiYW45cjdDRWJNYzFyRVBpTGtBdzcxNlB2d3l5clE0T3JQaElmU1dXYkN4WUVzWkhiU3RUaWx0ZjM4ZzM3SHFQSTFoRFpFV2pFRkw2NHdVdVB6Sm5mN1pTMjByUC9yRTZGUklRQ2NvS0pQdTlCUDVkQ1FES3J4bEVCa0F2ZXdZQUxQNDROZmhBSHdpSDc5a0wrT1hBczc5ZU92Nm5TaDNoZ1FlTWJFbFNNbk9EWFFQQXNpZFpBQUMvZEI4QWtJa3BYcGlmMjJsZ3VvZzAvaTh2Ti9SQytKUWQvZHRtdC9jZGRNd0psMzNob2FmZVJwK0NXdnEvWHU5WHZUOHJaczJzbTQyemViWU5laWU4UjRiSWFES3VUQ1NUeU9TeUtKbEI5cm1jTGNmbEhMbFFIaTQzeWRmS2Q4cjN5aitTbjVQZmtOK1RCV0EwSjFJMm1NeEMyaHd1RGk2WmN2aFZHVytDSG5QMFcycXRIWGE3N0lwZi9CcUxabng1T0UvbVIza2hmMWxxbGtuMVkvMVNmMVhyMS9jdmUxY0tBSmtxUWZDdWVRYzhuK00vY1A2UGZMbFgrMFcvbWVGUDFZN1pmUTNKTm1YSE1xRXNYQ1o3d1lNbjdJbWZpWi9GMzNBeGtISUtvZUFwSkV1UFBuT3Rva25ScmVoVHpGODBwTmlLTCtJWFB4eFZLcFFIdm10bFZJdXU5VU85cURmQzg2MzAzYmRQem4xeTV1U3hRd2UzcjFnNk1BM2EvNTBlKzIyMXBXZCtuOWZqZG1rRG5kK1ozWFhUVldkdDUvSndTUlpuVWZvek44MjVlZGlHNUpYYmcrOGVuSExTQ2NjZDlhSDkzcmZQSHJ2dHN0b0t5dzFaWnBFRnVxek1OSlFWcFNVNUxMOFA5Z2NkNm42MXEzODJKakQrV0tPUE5FOWYzZWVtSS9yTCtITDZjaHo1alRTdmZ4dmFpNythVUlISDNRNWExTm5OT3hicTBVQWI3MzQ2bnJqKzdvMk4xSG9uSlpaQlBBb0RBOTc5USs3ZU81cjk1OEQrbEwxRG5DTS9DaFNhL2p1SG9tYWlZeWRDcE1TSmRqMElSL1FWQ2M3ZDV4VUZNQVgvTzZRS24vdWVyM0F3R1J4L0NhTGk4bFB3N21FUWVuTmc4bmxWY2pDVUdzazBaZThzbjVHWXNNN1dnZEIvdlRCSGZwNVdGUHJqaGMwVnMvQ3pNRVRENmFPWk9kTE1sT2pWV1hmTUswcGdBQ3BOVTFEek1GQ0xweEh2Ukl5OFYxUlFVY0cwNktkSG9VUVozb3N4TWlZcGR5SmREU0xQL2Y5Y3RMS2VWNnB3Rk9LaFM4Y1cxQ0p4ZnhKcTVsb2tOU3JpaGhxMDNPNGg1SUtNYWsyWGd1Q2VSUEdjay8vVnhLaDNqV2tCQnZ0R25QeFRyam9zOXFhUGxoaXhlejJRUTIrcnpqSHdXeUpwRk9STFhnOEp4R2txZFZGcFBKaTUrZEFDVlJKSFE1RmtJNitRVncyeHFrYjFEdjlFSXRnd1d3VVlSVE9VZ2RaTTMwZVZiQnJFcWRpV1lGWVdSV1JTdnFWSVZzeWhVUUZtaG05aHhiY3QzcGc5dy9keGxpU2s4d0FMOHNqQ1ArVnFjdk5SY2p3ajhtdHFMb2gwdWdMZW5ZYWkzcDJ0cERkKzlGSXRYbFljaFhjbnk3Q1R4bE51V2tKQi91Uk16Uzg1NU1NemI5WWFXNmd2bVJYT1pwRURCYVYyckVpdWxlcWN5TTlCTTc0VHpmRjkxVjBWRXFLam05N0dERWZNZ2lnVTBGTXAycHV4QUZlMEkwa2M5UFIrZmZBeUZnckUyV2xnWGp3R2pKWWxHUXA1OTRoSC92aGRhQzA2RnV3K3YxWktCZVBYMis2T1ZaeEFRWG03WW9VRm9xb2cwQTFCb3BxZ1VGM1FhRkl3cUNGWU5DVTROQzBFdlJlQm1yeVFad2w5UFRINlBVUFBZSUZaNHcvUDRoazhCeW1hRndSYUVDUnFDZ3ExQkkzYWdrRWR3YUt1NEZCUENOUmxRQTI2b3hWSFpPR09sajVyZFFHdXc1WnRyWFVWRWRVYWRmZDVtcXdCR0tFVExFMjdHc0pLcjR3NXNVZG9YVGQ3NW9CcnU2SjlmeER1V2V0Q0cwb3BYcGJhQkI3ejZ0L1NiSU1wQlpNMHlzV0xMRlIvNGZEcDB6M1gzYkxwKzFzViswQ0RFYTh4NG9SSjNUTkFmYU41YmRhd0hqRkVGRk96dDFHRTQ2K2l0aWJSdE9xZU4vbjVTR2M3SVl4ZFNDUTNoKzZIM3ZldmJsVk1RRlNwOUVuTHAyZkE3VmxlRmFESlNhalpLSUlhbmF4Y1BBRHAyR3UrbnUyUHdrK3hXbDY1d1E3TEUvUHhMKzJJM0FTTTZURHl1SFFqRm1PUUZPaW5FWXhUcFdTYXNyZ0xKNGNzeHArQzRMR2s0RU9wb01PMDY1U09Rd3A5MFFhMkdaSHBHdWxVTllEYTQxcVRLMWlEUkZnUVJsTUd2QnFsQ0Rlc0dkVUd0T05GbFZCVUIvRmoydXplZVhhVGRPSmxkUWhBQ1RxSzdSVlI1bVZsWnVOaHFOR291SXV5SkNWbFd0NWhvNGN4YndaZVNVdEZyVmZ1RkNZbXpQYjNWTkh5aHNXK3VXRDdmSHEwRDZDb0ZSWWJIb2RMYjZ2THF1OXQzOVVmdGpnY3h3TVdyMzdjUnp0TURLTjZMRHBHbHVkV2phRnNLeWV2ZmFoWWN6VXRkR0pkS0NqbTE2Vm93SUZkWE0vWXB6dkY1ZFNiNFIrSmtlVUI2eEJiSHIzUlhTZGRIUlhqajUxSndpZTZjQjRWeldVUmxQYmlDNzNpbUx5SEQ2cFhKRzkvZDA0dWU2NTJDcmdMYzREcWRkeS8vVUxqL2FFNlVOUVk1dVpEUmJ6Qk5reVJCTVMxajRNZUFadzJqaXNZcnlpQUw3aWdHcGZoSlltS0FpUUVsMHl3bWJvazZhSUFHY0ZsRXh6bUxrbStLRUJCY01VRXQ2VkxVaTRLVUJFY1RMQmJ2U1Mxb2dCMXdXK0FwSXpYUEhHYWhnK3d0YWVGT2RUbUNEL0JQT3FFS0VEVXJhTlFyNDdTK2lubTBTQVVkRytIUHFuYlE1L1c3YUhQNnZiUTUvWDc2SXU2ZmZSbDNUNzZxbTUvdmE5M2ZnYTV6TWhKOWQzNjdrTzVQYndjVGlwVmtTMkF3RVVQQUFEdzhuMFl1ditnTi8yVDNpS0E0NFhEZDVzT3NYMEYwYUE4RGlQeCtLQ3VMWWNBUXd6T1ZCWWVhQjVjeE5PRWNsUktVdG1FNEszaVM4WXB2QXN3OU1BQnVGQVJPZFFyY1FGV0lQK21NTCt4TUtmaXgwV0pDaUtLcVlFaHBYaXhSQUJXa1VJUmZaUlZnZ3lleG9mTElqZ3ZWaFE3ZkRNeFNBU04vNFBFanVvSVErbjZPYjVQNDBPT0ZSS08wOU42SktVUndJaUcyei94a1hsTmJGbTgyQllCQUdrSmtPcUl1aEhhV0ViaG9LWW40V1ZjakovV2s2NHE1NlN1UXgwSUo5dkQ5dEY5VUVWcFZFUWdVeE1iVlNZRGhnbVR2cUl5VlBsbDg3ZEx5ay9vQ3A4a21haTJkeGMyWTN4a3ZrWVBTWUttT05iT2FaZWdYaWZwcXdPYW8xQ3FTVEtaYTduSlUyV1JRMWhHb3pqV3BPa3FrMUpWb2ljUWNtMDRYd0R3OUFtQ0dMcW02M29ZandBQk9PKyt2eG9LKzl6dndxYlF5ZldqbDRQQjVrNi8yMVlVV1pTVmZyL1pYYkpUVGt2MUpibkQ4eFJhYTZNMlF5OW9oalliUXBIbVZaemxOSzFZR0xTeUpid0Z5SG85cXdPU3dGRmN4eFZjMDA1UHM4VmF0WVpXMFlMSjRTek84d3FHWVpoQ2tnb0JQU3locU15ME81cW1mYmkyNmw2eGo5dnVpODBPMVFIZW1DeENxd2UwV3RwOWMyRHZiM3hTNjBYb1R5VjdFazIyVnEzNlhINjl1VDNKeVQ4Rjk0d2JvdWpjdmMvU25JVlgwQlhzbDRCSmdoRVpuVjZWVEFDK2tFb2hMaVNxQ0xlQXQ1b2huTmtQd1pWRWFNamIzc3Z5WXRZazR1WktjWWJkRFFVSDZ5eGlnRVBSaXluT01IY2hkdXBWb3dOTUxBZGh0d2E3Qlkrd1VIOFBvUkJIa0JQM3h3b1dVUkNEemloV3R2VnpPU2JWTm9UVFhtazIxWVNLM2xZVkpHRVJPUmRUVi9zQTd2Ukp0VFc2RU81WmdEbHZHZ1ZCR3FhK2RqWGljWkJleVllU0NORTNmeFY0bGlBR05SY1VFV1RKU2RzZnY0bW8waHl6Q1IwQmV0WVIyeVNFTTVkaER3dUZaTG1TN0FCdGk1TC84M3lsT1VzcVowMGRNVlRkb1ZuaEdqYVBya0VNbzdUMWJtRFJrK1hyQU82SkJKbS9weVVseE1vMTdIVEJBbUpkQVlSaU9WdnliVHJPbzZDMnhzNU5RZHNZMXNjSjc4R1VYS0lyMkY5VTZDWTdURFNwTnVDSU5rUWRrL2RReEt1QVlqbXFCdVVoUEpORDBNSytsTUhJNm5idFMxLzY1dXBSL2RKc1BKUlVIb0VveElpY0Jha2tLM0RUdStrZy9Fc0tTVGtCdGpFc1N2YlFFbmdQbVE5TlhIMVpwUXdsTWN4dFQ2WnRrbEdJSUtQRW9zaUc2UUQyQ2VwS1JoUGRFMnVLK2NUTWppR2pqeU52TFNpNGlMaVBlWHNlRmdsMjB2eThyaGgybXFsR2pad3JoZ3pMbHBsTGRxMXhmWm10V1dkU1o5WWo5VXBrQUV4WXZXajVaVVg1NnYrcGVqd2FCUDVueFNtRmZYa3RMZ0RSbmJlTXFIYmRLMyswbmdsVlRRRk5vRXpzRE5ld1J4dTc1UmlFRFNmNzVRMml6MUR6SU9ONWt1VUJReTB2clhDV3Baa2RnczBrNlZ2YUhMQmJTZ25vYVlybFpkOHVCSng1V1kxdUlyUlMvMWRLcWpvRllTUDJZSUkxN0llQWl5aHNic2RhQ1ZGUERER05LYUU3ZW9ZdWk5Wkd6dGgxMEFjT1RPN3NEbFFzb0UwZmxLT2FQellHbDV4cDJsTEVGcXdUUUFBd3p2eHlSQ1NHMm9lY2NRYTFWRStyeHBxTUNSTE5vR1RRRkRYN1FzSGJyenFMUWxPYVo2SzJqU2l3MDBPcmM0QVRQaSt3SzMwNkVJNFlzTFVuMk0vZ01JTVF1MUpQdXhxcDVuQmdyTjdrOTNDd09WdllXNU5yN2hrUTZCcGZOaHpBbzBuVittV1dhRmxSUzhkY2FTT0dqb0d5MGlYc2RmRnVxSXBkU1NZOWhFMjB2UlkyRWVIVkl1R2lMT0E0aU11aXc5TldLTm1RbnpOSXNDdWNYWWlHK2wwYnd0dVJ1VlpsaFNzRU9LQjhnZVAzaktMblRkUzRkMVNCVnJMNEc2ekFqOWFhZXRWT3RBSFJSVlY2bnRsZy82MVF3cUlQUUlONlVjWlMveGJVR2hvNGR1MCtrMDZRTmJCVG9kZ0tXOWVIbXE0N0o1TFVTV05sSTQvd1dmem9RSlJIbDVQMkZpcjVrbGVBdnVERnpsekdXdFUrUnZsV0RkdWNSNCtJLzE2b1ozNm1GcU9FL2krUDZ2bURlSTRrRCt6aXpIcndwUWd5TXNHcmVTSCt1Tk83ZnFvVVNHdmoxUThhRzhOeWpucFMvUlA3dld4emxnNmFnd0kyTjhQK2k3Z0lBVDJRdThPSHUrczYwY1ptcUlqVXpRVU8xR1RVQlJ3N0hPUjdQZ3hOOCtMR0dvSmNBcE9oRGxHazloTmlYTEZDTk15emFRazdIMENva2ZRdTRqenVPWTJXa3FTY2hZNFoyc0czbUMyVUJDZEdlR21OYmZybmRlSGVqZHdOYnZRN2VydUpOL1pxNXpvMk0yT3BxOWFVL0ZDWkJaQXR1enVFV2pTZTZ3S1VuSE80aEd4NGIrUlB2a0wzTmNYMmZrQ3Z0UjdVbHdCVEFHRU82amh4TE56QXRjQ2ZTTnROZnVaMDVtUW1rdTBPeEZ3NWpEdEx6ckRFaTRnY1lRMTcwMTQrYkdtbUthWVJBMXkzY3NXb1FrMGdZN24xcmc0UHdsREYyb2hNMmd0U1JNRUJaY2FzdkRJT2NsYjRLYitnOTZPNU9BTmx6eXg0eVpYR1JEMXJ0NXdPcWZiM0xpZEZFU0NHUmc5bHNIVEs2R1ZrWGpsU29Wc1I5TTdoQUYyQzZtWnc2c1pxWXE2YjZKTkVlNFZHQVR3REg3REo2M05TbE4vNXd6TVh0dDl1Mi9uVFRiZFhpT0dQQ29weG9LaWN2ZGpQWUMva3J3R1NBV0lYVDh5SVhaMDNyeFRJdWlvWTFCdFNSNEx5ZGJ5dHMwSHpLWHR2RDNGQnBXc3JOKzVOY09MTGRwVXRSVW03ejhkdmtHaThnczNhWjNoTkc3ODVESks0VDdhcEJKdEVPTFY0TmlueGUvZmd5bzBLbEU3dlRzTkYzV1pUVUdNQURrUWwzRmJBcUovb0Y4aTRpYmdQL1dDWFU0Z3JIQlBKY21GUUl1cHFqZ0orRGpaWWtKVmoyNEZOYllvMnNFdGlwR2VuVlVxTnQ1VlJ0ckZlSkIrZmp4a1V4a2Rjdy9rNkpOTTZ1ZE04bnRQZ25pUFI1czFwM0wxQUxrMHRzdXJYZkd6SnNPeHVIVE5FeGZSeHdYTy9JVGpEN2dmWE5BM1VFY1dqZHNwZTUvd1hJeC9hOGl3YllpbUY3R04xZEdHWDNsa3lrQkNTK0IzTFFIZDhHSnBHZ0tJTzJRbjZkcmdXSisvYmZhUGxadGdKS3dmOVFMVlNRWGdrdDZxSVM0N3BIWmd1TElWMW45bC9Hd1h1Y1ZHQUZkSGFOZUxidHBaUEJmd3FTNnd2d3VCSlp6citwTUx3anVJT2FEWml4RWk5ZWhMVzh5cDZnZDNmcTdkWVZJVW9STTFGMHlHTHpLcmhGc0hZRFAxK1BGaUI4TFFSaTE3YjR5ZDBvWlhwVUN4RlIzR1h0dWpJSWIrN2RUUXEwTVIxcHFzK2pDYVYycDdvY3NBdUlndFZsRXBkYU5VdUcvR2tFSFRRU1c2SGtGbk1HbVpnSW1BM1hROUlOb1ExT3UyRC9vdEorRzBzejEwSWFVMlk4MGgxVWNUWU5UOXA2S2pBTkFVZWY2WUlMaGI5RVVML2hYSFBIS2dueFlnYlpxVGU1TzBpcGt1SzBiL2cyTm8zN0U5dEJuQVdua01CMkF4VnhTQmEyQi9UbWx0MHJnUVdZZzJvTmhBWXMxbGNrMERDVGNRRlVxVXFVbVRFK1hOQ01EQ0ZzMVFUVTRzeCtJUEpQWmhPNTl1LytuUkI3TDBReHNEa1hZYWxGcnZtR0YvZHJPcVBHbzFaWU9COVJDQVlWZ3A0dnhPakRSMllvUDRhZ2tWekRLMWkzMkU1RU5PUllEaUljWklNK2EwdzZmVEFOWjI3YUZ4d3dJckdxR3JjejBEWWZaOEZXQjgvNk9LTnJwN3dtL0lxYWZiamFJb3ZKV3Q1QWt1cld5Y0kxb3dieFlKdWlNNlFKSEpnZFkrenA5L29XWnk1Y21waUdhL25DR0lzYlFvMXpjeEFFdDdUQmREaVZoTjB4Q1NGakNCQi9rcytsKzkvblIwSmFrZGYrWjRsd0VrQVhmTE5ydy9FdTJCbU8ydGdxV1JtNk9mZWVESVVLeU5iaVcyTkhNQ3ViYktGaE9PUHNFZFBQc3A5UkxzOGd1aHVOQ3RwS3Rqb1kwVHdBUlNFU0h4V3UrUkxScW9TRGRNYVR2d28xWUhlRXl2c095WU5qdGNiczMwV2RmNWNnRHQyWmpoRjdBRWFiU1VJOW43bUhZTW9uLzAxUHlUUWtHTVZRa01ndm9FOU9LaGhlVHc2d0RGL0d4cHBQajRFZDN2MFpMQmNCMEV0eUlCVlhLR2xMckl6SmpHVWUyUEo2WVNDSzV2anV5cE1vTE9GL1VtZDg0aEd6b2tXRjZOMzdtV1RWaFBwenJ1djJ3MmpFNUNZSldTWm4rV1lsUnFlMXM4amNRYlY3a3RYenBRek55amc5eStOVnZjWk5TS1p2dWdLaFdZeTZBNUttTEpoRGZ0RU5CdlJRem5pK3NEOWZCZ011aUxRYmhlRVFKanNIUlQrYTYzbkpCbWZkNU1Qc1Z0TklhSXM2UFFoNmxaZDlRMEhlNnVyWGNOK3hjdlNua25FaTY1d09MMk53ajg3T0w3aWp4TERnYlgweE12aVJodTJzQTVSU05PSE1jajlOaFJXbDU2VktRK0NDc3hCWVlJVGkvWWpkZ3dyb2JZRHVJdHYxRUFmL1FWV3VRS1ZDZWhpSEpUdGFqVDhabjN0RTZQblNaSlp3Nm9nWHlodzBCWGJMYVI4RDhvT1NFenY1bnZoOXZTOVNPK3lSUG5lV2FPeXM0WUt3NnJZbjJ6UjBpbU1DWHUzVDMxc05ockltOXM1d2dhVUNZS09US0N3YWdFUjdvT0s4UGFScXVGTzRRUEZhdkFqQlh1M2FDSTlZaUFFZDhMb01qQTYzYXZmYko4dkYvZG1FWE5nZk11U2VtWTNIVDRjbnltQlFYb3pGZDJVdTZNQlBEMTVRRnhpTTBCSDIycXV5aFhhQWZidXM5SGpXMW5uTE5Uc0xCekFQWHF2RlZTOFN2bmwwUWZqdlZSTm4rOUVGN0Qra3NBVEZVbVQydWJaWkhodUpqQlREbHpwNVdWblhZSXpHSDJZRFBWYnJXYlpLaDNNZTkrbjdIT0phLzRkdVRHWEQraklwZ0hBQkdoU2JUTXNaZEdSOElheGxoT0pBbW5nZWtETzJRMGw4d2QreTJGY000dVgxUE9VYmdLaDFkcWFUS3dteGV6dE5GZjVxZi81eU5RQzZJaXVKZHZMdG1pSkpwYWNpTkNwMGh5SnU4UFRod3UyMkZlTjAzUWh1NXFxY2hPalU2NWpBOEdLcHVPN0dOYVc2cStZVElvcWt6WFFCVXJxMksvbjcvZ3pCRlJ3K2Y3c1Z1cE50UFJPVmN0NTZiL2ozRjBlZzRGczh3bXY1NG5VTE9KQnJGcXRidGIvT2RXWENQZ0IzVXdUZHJJZS9xN09UQVdiWW5oZHNSQXVQdE1IeVIwRlN3RXEraFVWREJVclZsRmxnZ2h4RUI4bk9BR004NHZQZmxkUlZhNXBnb0xrVFVHcWRJRmxtcTUxZUNIK0hNTG9QSEYvYzlXTXVZa2d1QlJ0U01kYTltb212QUJIWE1LdnViSDFUWk9xME1KcmtIUXdReFpGRE42d0s1YlR0Rm1PbDIyV2lzZTU1dHdsN2tOTHdHVDZ1NVpwRjhZWEVqdlB5cnhlRnlzUlVHSmxXTGJYeit3V05VMUJDT2NVSGJHd0crcGFOZE9IakF0T3d0QXhnZ0NsY3lNNVZDNEdEcXZ0b3MwdTZVMVczRnA3MktPT1pjSFFVSmpXM05aSERBSGJSdEZPRW5uTjV6Mkh6MFczRmk0QktObUk1ZWhJME5IQkdXWXBOZ0VHTDNtY0FHTWVidEhIZFlKMVBOS2VxelA1U29VQ2RON2VGM25qVUdCdGhlQkJ4eHpzZm1KZ1M5SXpvbGcwNmFLR1AweGhVU1N6NHhvQWxacEJaMU1QSWJPUDFicnpLRDR5bWkxQStOaWdVbmw2SFV5RjNqTjlZT01CTEZwSG02eXRlMGEzRGtjMlJNT0IzYzFzYjc5dE1RR1g5M0VSWGFoTGRDUVk1RCthUEQ4RGNHalVublVwd3ZLVitBbjJldEpvZ3V3eHN6bEtjQUFpUUpGQVlVVkJ3Mll3UVE2ZXJDQmJRWjdnci9DRktHYlB5aDQ1TXNnV0J0OEFab1FqUTRiZGV1L1VnWURZNUxvd1pmNVdYTk1lRUJvS285WXQ0UncxNU5wVFpCbVlkWEhLbVJhMEFYZDJIZDUvL3ZaRHdpL1VQQ3g3Y0I2NTVEdTU4OFI1TEh2SnNuRE13Nk1FaE1CRlJvcWlGcHM5eDNiNUpIWmVMYUlDRlhQcHpUQWRtbjVnV0dvWGZJMWFkT0VXaFBGeHp5SjdjWTNIeWR0SkdHM1VYVmhGcm1wejlBU3FlNWR0VEVsK0VyRXI1cFBTUXlmOGk5T0tleEpOZlZHMS9WOEFFN1NIM2FuekRKOUk1cU9DSkVMdkJscThqZVYrQSsxamVZb091ZUtOSVZhc05vMXE2c2g4UEFzbzRzVlNQSlFKSWVZeSt3eFJ5eTk2RytjendZRU5NME5KREV1QVR0UEVWWDdhMkdpMVNaeDl5QlU3UWhQQTJtQ1grN21IK0RvQlNKRW1OcXJKekdZbDdxNlpGTFpBMnhoaWVyMXRsbUpqbFRzYitNcW9KNXdVcjZVL0llQ0RzWXNUemcwZXdudlU4cjg3MzAwbW9FSm5HN3A2RmJZVTZlcjd1UnN3c3I1dDJaeFY1aVFHZmdxZFppcHNqMDJnMDVPVS9ZSkFBeWtFb0dlenNZM0VQczBCRHJRbHVIRWJDbE42UnBYVUw3RkFpV0x5RkZmRUxMUTIxMWZtcSs2TVB6eXkyK2x3azYvdWVIUXR4R0JEdGJmckloMUdFY1hjekdoRFd0d0orWDdZeVlpSUdraXAvUnVYS21nRFMzQ2JYaXRWZG5HTkVrKzhaM0xXckNLR2xrdkxnUWYxbWNYZG1TTnlBV1Urc1FzZVV0Z1d0bDF4NzZUYWxNUHlQSmkvazNYd3VKRzZvdDcyQUt4a2pCYXcvOEUyeFhBYzNrajlsSUZZRlFVR1pvQ1pHbm5HSzBZTW94bVFXb0ZGdTFySGptTVluNXM1SmkwbW5oS3plb3FOSGhOOUFtRzNFdU9QK1lDYU5FeTYvYmlOa2gzd3JKY1pJWDdQTFZ3SC91M1RzUGcyRjI0Qkx6SFcweUhVQlh3cXpHZXpSYVk1YXcrSkp0SFpTSU9NblUwMXlKeWFNbDdWTE9XcDFEVXdUcE5mSUtOQmhLWldXOEYrblBUUXQwR0JHWWhkakg2RDRCMUFSdUpwTDUvVEttV29xQjUwOTVpMmdpelBuNkkydjluaWVOejNsaStMbmNOL1FUZUo5aEp0cGtmK3N0aXA4MnpWUnB5OVhJMXROdUVZTllaRnkzSy9oSkE3czhFYkQ2SnJFdVc1YnErSVBDb2xiTVZpWE1XNkd2dzk2RmNJTDZpUGZiL1lzUWhCV2xrQk1zSzhrbXZBZytuNEg2YU9lZlVVSUpBK1BzK1JSNUp4VStQOGxzQzYwRGNoTmx1andxcVhvS2diQkpXN3I0ZVdZczRDZmR1bW92U1p5eHpLaktMM2MybXVURDhzNkVKWnFVb3NyL210QUZZSWdVeG5VTWtwNzFpem13czZ6ZkhJRlZhT2wxNWdrMlczUUd3Tms1NW9GQkpEbzFyVXJZRjBoS0xmcmhwcm96NVpQUXJoVmVORkpIWWk4bk5LeDhXNnEwazBTSUt3V3NGV1o0b3dkVGozSlYwMVkxSTVCNFRhUjdTaXU4b21GQUczZzFwSjRyU1dsSVVzYXBsSWYyZWNjeTY4ZkZER0pVY0ExWlF1YjV0M2pvbGExZjJveEkwUG1ZcXhHd2E5QTRweGwxWUlMbzFnbEdRM0xhSWdXOTBUUUpxMXJHZHk1UktpSnE3UUF4dm5BN3pWZWlLaGRsUXQ0U3NvQzFTT0E5anpCVjc0QWtEWFNiTXgzWmREQTFBMTRUUHdtaW1ES3FidkllYTRzTmtEMld2Z0JGRTlXYkpyTEx1MHNjME10RzhzSzdxVk1lL0ZwMTdBcGpmS0p3T1JiUFFtbFhmbEdFczlkaEk4S2NaU016eCtWKytEQ3dJNlAwbVdZM2lpVThUb05zWnpkZWNmcHhBM2IwTHNDeGd4NTQyeE85ajVRVElGaVFYTGduMDFMdG1haklzdUdFYjRSVTNsWGRQWWVNZVEyR05lSXFoRHdJY0JLNVhVVUxNcFBlUWZzT1JzVENvSFNxa284UUJDN0VDbHJDa3dUZlUwSU9pMGVjWEx5MXJtTGdOSnJxMUc3L1Z6OE1ibEdRRG94OURPbnB1QXk4WEZiYVh2bjMvTUR1MEZqYVBsRzU3QzYzc1RyU3ZuL05pR0c0dTZWajBSVk1nQmZwVUxkZlJ2ZHFQbTQyQ254VkR4TEVzNTZYejN4M1RrWVFTYXEyYmJVU3Q3WVVMSmdPcHFHRG1VWXUwUHBNVXBlZ1E3dC93QnJtUThDbjF3UXJGbHl5bUl5NGlOUEpEVEJCdVpvQnZVMWdGWGdtQ25mRms3bkU4dFJ2NG9oaTFaT01zOTRXUUJZdWpyOXJsY2pyQ3Yva0hncmtqb2cxMTNLN0VNVXlieVdZUFRFdHMzZTY4SDlGRHUwa2EydFJmL2xKTFhURHB0U3EyWVBndEt1RDBEY2V0ZGE5ajNVa0tnQlhnVWtmUTR4R1lJdS9xMWVoQ3psQzhiSWo0dnB3UUgwRFZkUDBJNE8wYWhQbHJRdlpMVitTblBLSlBoL1NPSjF3NjVDWmpKR1VXejFWTDdtNExqRGRGeFpvUHRsdVU5SU4xN3A3VnRDTytVakJudGEzc0NCTjNCdTVPc3MyTEhxN2QxWnJpeHNVYnlYdkpxMHNRUWo3bDRuUGc1Rm5taW1UbklkSVVBUFhIZmdvaHdnUSs2cUcxSGJORDFqWGk4RllkbWpmd3FPVjdwL3dNWEdsTVZ2b0xtaE9HY0ZGU0xKUmpBOGwzQnhkNkhTT2Z6Nlh6V3BqU2lXMUgvSkRRTThIOEdvTy9FTmFFQjhjZ3E3TnlRWWZtYWk1dE9tbCtjMUhhNXBnRDF5RXR1U2dBbVRCRzZaZzBkNmE0WDNnZ0FUZGMxUGFNZkYxZnFhMlJQMm0ycmMyZ3VrOHZsdWZST3BaMXBaZmNYREV4TmM1MlNndzFOQTgxZGF2VWxYV0hoYTlydW1PK2d4Z25naGo1UTZobmQwUzQ0YzlMZm4rWUtvc0hNMHgzOUUxbktqSFlwbnFuNkt2WlhTNXFlcGZKNmFYZDlrb0JBRVg0Y0hjalNBMnRSVzhnU0RDQnBLYlFaazFCcDdjZUhwdmlMYzVMYW9Cd0VIVVhWRktDT3JUdXhKdU1TN09tRWpMR0NTQUgxcExGc1Y3eG9pOFM2Rmd3WDl2Z0RXLzJiWVE0UE16Y1VSYm9Zdk9KTTBaVi9YeFRrSUhndE56R1ZKWlFFc05DYkhhb3A0cnlXZFBDUW5vTlE3Nm4xd2Q0STB1OFVYdmdva0dGMEc4VC9jY1NIc1Irak1abVBtOUpLVTlLMzAvSzF5N2NkQ3UwT1BrclVDTDRDUi9uMVFMSVpDR2YwWDFhSUovamJ2RVFtV0ZoQXNGZ2J1UTlYUTJvem9TUnJNRTN6eTRkMkEzVzhMT0Rva0szbm5YT1dYdm8rWFVzOGtRK2NsMzE2N3JFVHNFdjYxdHB2WW1nV0pJYTVYa01UQUdzTmh0VExndENkeGRaNmcxd0ZaOWpkcytyamViVUdkR3E2am91T1dSMzByclhDdnJNRVZEaEF2dDFzd0tUYkFlNWtiWlUvMFh5ODBkVGw0TTZGb2V1NTJBMGtMWWtzc1QvWjdhbEt4OWJTR000Wjd5VTBUVzlIc1FQR09kUWpIVHZMTGFobDc1a2RhNk9lOGJsK0o1L0daTFRGZXdBMGYwMjJxUTRHSDQxNnd6MC84T21XdmFHVGhLZ3pFU0lZZEtNVmpKaEwxQUFzNHBYMkdrYnNDNTh0QXRaT3EzV3hHeXgwN04weFRwQ3VrMUdPZEFuc3FtNW1jYmF3UW04Nmk5eGNWSTBkdnhYYXpOa1Y5Y2poQWhxSkcrb1hpNjE4dlMrbVl6cVpmRytwVXVwcWkrd3YxOVFVeHVlNm5meGh2OTU1ZmhXZjJBeFQ1MnUvNGMzTTM2MUQzd0QxMTJ1bUlBcEJVQVpaUVlrQmZwcW9sMU8rSWl4TkRaanFNQzJHUEF3dEFCY1ZGT0JNVmwxTndSUTJ2WVRPN0xrZnAvbjVrZVlHNGlKY1JQcUxyb3R5Vkx6S3J0ZEtYYnB4OXhIMnNNVzI4d25ZVWZweWE1R1UyUXdkbTBvdTRDUmxnbE9valhyaFZackFUdkNyditlRVUzSHZCUGIyaGlQUmJpaDJiQWJnMEFPdXQzSTcrTDMybU1LL1NqZ21VSGd3WGRQWHZ6SnF3MFhyWUNOUmpzc2VFcGczNWI2RVFrY3IvK0k1V0psaHBZV21WMEhOLzQzVHdHSWdWWXZVOGZuMVgvK0puaXNxRG14WkFSUFEyeTY0SnF4UW9NM2ZXa0NtNHVOZTBGcVBKOUczaVZEd1Vqbm84UTByVXFSVkFld3htTklPSEI2R0RHV0NGcnZnQkVlVURyMVlQZ3JEWmJtbkVDdlBzQzgvTWNXOUxkeFNldGpYZlJUS1lyS2lCR2VPWWpEdXlxNkVTYnRiRzdvVHF0V2tQWllzeFIzbDlVVDRZd011bXJTa2JMU0dWQUZKU2ZSVnV1U3ZYOU91TGFFMS9LdFlwTW81cTI1WU1sb1I3M1crNU01MUNidC94d0laTjF2MkM3VjJ2UlJieFR5alFEK29ENTBQQnN2VmN1ZnRpM2UwcUZqdll3dzRzWmVsUi9LZGJTYm5VS3dLZ24rWDJBUXQ4ZWhENTkrRzQ2SzBWUmQ4aFJCSzJMUDJ0WGsvaTFoZk1rbVBzSzlPWUhCcExYQWhUa2p6L25mZ1NkdURabVpTa3crbWNzZDlLYlI4Rk9MRklwclQwNVl3eW9HWk0zaTNiZzNBNk94dGxXMHFvbWw0cDdadEk0cGhLUWI1dmpSaEFHNDJYN0hoREdyb2E5TW1XSnBpaVJWdWJHMTlpcEVVOEVKZUR1WWtuSG5IcFBvTnlLTHhrcEh2ZGlEb29CeWpaTHEwUENFek9xcU4vblVwV3FqRjZLVEp2WW53Z1g5by9RMVNOM3NhYTBFdGdGdVk2bUZnOEkvZkYyRUtnZDdiYzFudzBoRG9yUGtUQUVDQkdnZFh4QkJkOVVENjk4S29PZStCblliMElaN0RRa0VLNkpGNkttTWZpMDhTQ1RaM2FsNWNMZllRdmhDVERabUlXR3BHNjBZRFM3SXBWZnVzMzBRWHROd0VGbU12bWlzanl5T01YT2tGYVI2dTlrSE5pMGNObEYzd1VmQXNXbkxPUllRUzRxRm1NMTJUQnBLT0ltWDBsUTN6UEVtQURMQ05wSHV5VXNlK3BFVnk1UnNjTGNpWFRnUTAyUkNhZnd0OGZMbGRVaWgvcEdEN2pOZi94QmF2cm1WLzM5bEhLTFRVa3J0NzhsTzYvSG02S2xmL285ancvNnlRcGJjUmgyRE9EK2NIL2tIL2VGeVdPZVJOdi9DZkNDZEpUT2dUT001U3g1emhhY1E3NWY1UFBNZjdHNk9qRmZMM1FsTlFWV2ZOdndXR0o0VzI1bER1SGN3ZUxrUWlhUlZLNmNOaUFDSEN3Q1B1dVY5SjJaUTJTWFVVNGk3bW51aE1JYndzS0VoMFhsRUdWT1B2RHdOMHdwcS80RTlPcGZYczdLaVhjWFBQcWZFZVB4cE8yZ3NXQStscHRVaVRadzVNOGNRb1hpRHd3eEtqSTF5Q3ErMVZ2NW5EakM2aUNnbUhWYVlpeGM0VktNYlRjbmIzME5oaVAvNFA5TGRrTFMyZm5YanhlRUppSW1scEpyeDM1MHVMb3RmT1ZZZFQ5b1R4WUhMK1VCTXljU3ptUlQxZFVOVnJKQnZlYStFM2hyYTczakFqRk5XYjJkSWhTZU4zOUxKWWZsVlNPZTI5NUp5K1ErcWt3Qk5GU3FmbW8vTFJ1YXlnSk0ydDFvSDIya3F2K0pqOThwRXhYT1lXM0VBNlV1VkNGSlZXYjRGdHpYUHY2Q2RSL1NDNnBQVkpvSDVCZHYyTXBTU2dIRjF4YWErUVEraUtodGZmRnhGNmJTZXl2TThuMlF4czk3TnNLc0VKOXNnZHprdUdSZUMvUUJPbGJzOVYraTZRRVVZRG1VYVNUZU1udlhCWFZsbUdwcHRxVDdObjBkU09VblNGb2xBaUNHY2FDSXRGRS95eHdHNHliNXE0Q3F3QnRhdGRSVEhzQUQ0aTdEcXl2eUJPdEJkcy84UC9pZlpUK29rcVcrM0l5UGgvUTFCazJjUUR2dmEySTl4d3pJcXBIMXpqZndHbm45ZTI2Vmx2SXdXdWhoOXJ0cTc1T2kwVkw5ZEovQTA2anozMForVFdJRUlBR3JVS09YWnI5d1Z2b1ltL1FpanJMb3dKK1NURXBMd0ZjUk0xRDlRQVE0NWJ6K1lTaWE0KzRGWC92OVJwZlJwQ3RHM1BrV3I5ZVZGSnVTQ0JaT1FaeTRjeWhTYXFTTzFacGNDc0Jxc2xEMWdYQ3JXQmtHVGU2ais2YU1QU0RRMWN6aGxnVEdxN0dxa0dDakxabS9LNU5BU1ltWFkrTU1WQnZDektzUGlQLzdDOTZQQmRYMFMveS93R3RBR1FKTExTTi9vanc3RG0wd1RrVHJ1R1hyUHp1VmYyZnB2OS9XaHZJcCsybFBiejZ2UmY5RE43SnJnSFlpb09DSkpYNWRJZGtzWUgwYnpFamNreVVHV3hnTll6NC9kRCtTUmFTNE5vUmFGRzZsTFFiMFpvYUJqRUk1c3FBREcxditBVnVsRHRIWGREVW9FVlo3empOTi9PMENxQWNUQkZWUXl2eGtKdmsxQnUrM3FYakVnVmJRb0I4MFo4c25adEtDdlhNaUN1ZnFGdTFhOGcrbGJObUs0Y20remFuUjlxZjh3UWdPR1J0RHpjc0poc3liamVOZ2dpYjM1TGZNQ1diM3VySEkwM0dWMkhpOERBMWtoMkVvUEsxc3RXMmFiZkpQMUNqblFtYXhGNHlVdGlUNzZvZ2hGclhuM1JoeWxmQ1dpbUcveENBUmpkTTMrUnMyR0VlTmFGdDRBb09aZWE0WGJISGdTVllCWEQxZW8xdnV2akdlWTVQSUJBajN4dTR5TlpxcTBwb2N2dmlYWFpCSFhicU9sN0VOOVZHRWpOVjZMOEl2U0NIK0Z2cWlLNlZMTytXdTBDYmdLTXZnRmlLbGRJUDJ3d0wvdW84Y3NsZVFRVE1LZlRNeVgxNEg5WnJkSVVrV0pHZFVTYkJBR21RbEtsVlRuQ3dRcWZsNWx6SXAvYnl6cFBQeWJjb1lFeFlRQlBIVTlPZ2xqL1l4TGUzQjhoSzQrZ1N2TWZveWM3K3FON0NFWjhxZWdqVndYd1psMUJRT3F1dHBPaElCUDdNWTRoWEtuL3NkMzY2TlZ6WDlsVk1LUEVJaU9zSDhxTy9jUlpsWVlva05lSU8yWDRobjFjV2tZQ0Y4WXpYQjVHZmR0TFY2V3I3dXZveGJBa01MWDM0d250QmlnU0xhbzhNd2MxT2M2dHlCMU8zNUZaaFlFL1dFbys2eEQvdGYxWVR3Si9JV2VMVG1PeHIzNExOOENSTkZpVVBqTGYrZlNHZ3dZMFp3VlBMaEphK1pUb001QUFKd1JRbHYvcGZ6cDM5bjloa2Z1dmNMaFg5a2VHSi9qbFhTSklMTFRmU1U4N21uVGpCV0tBU2FldE5yaUlBY3IxbHpHUjRycW9yRmF4cFlRNThpcG12Zm85VXhLRHdFblJLOVhEWlBDamIzS0thTWdWQ2kxcUtMOXFoNTR5R1N2eXl5czQyVndmMk5tZXY1TE9mUG9Ld0hNMU9vU2c1S1VaaDBhWTdOZG5OdjA2Mmw5L1JRYmg2WWMvcTlpM25PSS9GQ0J3YnVQUFBvcU9wd050czJXRm5KaUNaRTE2V3VudjNucElxOVdlRFRDdW5WTGpKTU1sanVDRmdQOUFvSVBDSFlVMDNWOVBGZzR0U3Fnd2ViSWE1KzZod1FsYUxUSU9MMzZlUUpCbWpsWlRlWGVzMlZNZ1VsVGNWK3BvN1RwWkFJTlN1dnBnY083Y1UyTXlLdzNPTE9ma2hqU0hUU0xMeHpDWU8yUW9hdkNZSTFSNTV1bDVXWk1hRHBLalg3alRRd3gvU21VWkt5a0w5K0JLbVJXbWZrU1ZHY21kUmdTOWpwSWlvYTg5alJ0YWQvblRGV0l1cFBwa0NzTm14TGtranVKRlZmV3RWejJhdUE1aXJHc3NLaWJKenpCQWNzMXJyQTdQenJ3QVBmOVhpK3FxelBNbFViVmxCY1lCRTdzUGpmRWFwZEFPVithTnFNcTZXYmU2ck03SkNTaGVMU0pycVVSVC9ubEV5ODRoRFFQVGZ5ajI5SHpaQU9EWStIdXhVWjFLVkUzMTVucXQrWkJ2ekluZHVSbEZ2c1pTVzl6S0puL2V4allaUlZtbXplYTcwNmZsajEwRm9UQkx0dmoySHhrV1g1ejFmaWVrVUY3TGFkandmQWk0MFhrdnAwdEVpSVgyc3pPM0pkMTRBWk1VcGpHTmdldDdTU1dZNDIySFEwZlhIVjRidjc2aFlyeFV3V1NFWU5MZTk3YTF2cmR0TDU3NlRFY21NNVluOVlSWHgyL0laakJxVUdEZGU1czduNi9ZZFFDSzE2eGh1OW1zM1RFRnF2dDZtOFM0R3VPWDNOdWhiODBIS013K0JrM09aT3ZvekpXVFF6dnVsZkN4MVVhYnhCWlJvSXJaemVKVUtIcjJiVHVNZU5zTGFESVdPd0grMzUySzV5T3lmSjNsbVhoRXN3cFl1WjdLbkJnYk95b2NqWTF0RWJmRWFrdzVDZ01zamtyUWN5S1lSUi9lUjNFU2VlMERjZ2tEdEY5aWlYVzBRMW0wTEdsT3FoaFBWQWpGQnFGWXFmYitTVGxOb2ZFdWZLVE1ZcmFlUnNpOHc2ZXpHZ1VWbVJaL1ZWYlZucGpJL0tGMXNZR1R4U3MwSFlrTnNZRjE0NFRUZ1BSYndZZGoyVGJRandBMmZpVVBkdjBUK2lBMjVvUFFQN0NMVjhrSGJBam9aOXU2TmtOTEYwTU1yVGdGYXYyTERPWGY2ZVZvT1FWUXUrZ0d3UjJ2QVRRdkRIS3h0cXpsL2ZCWDRzem1PdC9ZMWpLR0YyZW5LRTRHTTlQLzJaVU5iRmloSzBoWkE2dmhzR053QjRkTEFQbmptcWM5dFpmNDdNNmk5QlFqeHh3SmtVWGZDa1hRZ1ptZmhqME9TeTcyN1dRcmUwa1JOaGtqQmsvTkxLdXhXYkcyc3dWbHFUR3E5bWE3d3o3ZUlsMlJaQmVIRzhNRVE4R3FNcmZsQ09Ea1ZyejcvbmhRVGpBWklTajhzNlB4WTlOc3FoeXhvVVMvUnFtVTZmUmIrRUxnSFIvZjdaaDJsNnFOMkR6Y1pnRVREYkIyMWRNMW94ak9JVUkzeXJSbHhoaW1jd29nMFI2OG0vdVZyeG1ZM0hSUENJZ0VFVHhLNUM5ZnhucCtqaXl0M3pZWWxSdnRNRmZUY1I1bm54UUkwSk1RSE9KWlJVNlBOYXlEakJKTWYrWDBpSGxEN3k4Wnd3bmFzZURrdk5LWmM4WmxITko4OXY5T21BR0h2M3ZYNWtXaGR2ZlBXdXNDZVBvaXQxWXg0L1Z6YXdvaGNKeml2Tzk5SmVROHlXSnFiQ2xsbTVxenR6amlRaGtwN2pTejZLYnV1aWdlR2owY3A2b0RXb29QYnZoVGJ0enhYWmNGdzVLNzNxbk5WMEYyRTdzSjRuSkp0UHRlZUU1WHRLUHN1L3hVd3VpbjlpY0JiaGFEU3RXTkVzS3pFbll5YTM1T2YwWkcwWU54KytmcGF3cFNNQkJXdCt6TFpTZTZralgvbEFYTkZtZU4remlaNEF0WXR1eUU0NU54YjJweFlFcDdjVTl5OFJ4amkrZXRwbWRxcGRoa25DL1RKRG0zcndSbWhrb29xdFNRTHpuR0xxZFJKUnJaVksvdUM5Szh6cU0vcEhUTmZiK0NsdzNwd0M5MGpRcE8xZDlnL1VLS1IrM3craTNPQzBKSFlvbzBLMlQwSURLWTFBdnoydng0ZkRCMjdLTEZxWlhSMWVubURGOUtxMGx3dmZiTkVCRzExM1hENi9TNFdGR2tvSzdPbTl2bWE3bWEwMmJuWmRJdlkwcUtYb2xKaVpEWTFoWnVaUG9yQnBBSUR2eisvemFTbFJHZ1FoZGVlcFJBbHJKVUM1WTZsamxyYWZUT01UaUxzaFJCeDRmMy96VkY4Ti80aEg3anlHWThaZEkvVHdQU1h4OW12NTRPMUNxaVNNeW5NMFlVQlFCck1VTEphMXptV0RvOXR4YVhEMFQwSXpoQlZpWDZKbjlNbnBUWWIvMk5NSkdZQTJxZkNUSVBOVjZFTURKcFhuVHV6eEVaamVQc25GSlBWSGlZWGtiSElzRTQraTlKeGJlSWpaRk1EVDVIbDVRTktBeEtBd0VBYzdxRk1ZSzBuWVBnZStCN2Q4cDBxSnZuZUtWMERRRXdBSnlPdjIvbitIc2w4dDRKOGx4SGZPWll6TThwTXVlcmhDSk1oekk3bC9uYUNmYXE0dm1WaXF5RThtTE5tYlF4WjN5cThsWDF4cHY4L09uVFFteWpQRzFaQkZLZEdpSlI4eU9tYVdibU9PVFpxdkZEOUVxcWZQbkJ5elBwcmJqdjRxK3RGYnpFVFZ2Wk5ObFhwa1pIZ1p5VFFEK1VSaW0rVVV5NTl3NVZGUitpYU5rZXlBelZKOVlaZ0dpTXFqTUJaNVA1YnBWeGowNnFwVDVwbmYyRXBtVzZDVWpnRWJoL1RFbEJ5TXF5U2o1WnU3b2tmR3hGaW42bFlSVTR2cTZ6cjJXdHNsZXV2WG9GT0ZmRUdhdytGVzRNQkczczVTNUtiYzUxTWlFUVhLc3FjRkxQM1FOR3RQSmU1ZHFlQ3dtSmp0V2dxREt5VWd5ZGpWTE9tRkZRQUF6SnFnSXRlcWVJbTV5YTkxTXRVTHRnRWNqRmcrWDZXTjRIaHF5VncwQ0lUU3Flc0NESXV4eXBXam1KdDJZWDNvTXE1RlNzYSszdVNmdzNIT2dBTEJ6S1pBU3ZqMnczZ242WUVoS0dGQ0VhR3NpWjJoNFYwZUJ5NWVidTZsSzVmbXAzZUxJbU5ORGRtUnhLNk1Gc2htdmRDN1FHcTdoemR6QmJPRm9PYytLU3pSUHdjMWVBcTJ6bW9PbVFBQXZIK0N6V0xCUkR3cUcyUXB1WE15Ykhlc2hadzU3Z001NXBudFZFNEUxbkRJblp3V3dIU1dvU2hOUEgyWjBCMG1CS2JWdHZETmpkWmJZQzN4MVBFbkhWUWpWWGxIVDhPeXRnbnQ4UGt1UkJyYkhrOWlwckhQT3pSVWtncklhaEtCSUJaOUVNekNCWUgzOEZLd1VEUDR0V1ZPZzBRVGtKbkQwcmNiQUEzbzd6NmxwNStIYTRZREN4RjVnMTlmV0J6YW93TFdQNTRsa0tPbzZmQmU4N2k3VVAwS3cwK3N1WEVLcGwwU0luY09yclFqRHB5M0xnZXNMZjc1cXF5MHE4a3Z2WGUzTTh2a2tKN3BpaEdFUGlYNjFRaWxoN2Z0YlNva1AvUWMwK1pZNUdvS3RiOHZweXViN1JjUTY4UFRhMnBCL29Uei9mbjJYK2h2NUcxL0tqdHg3eUVBSSs0VG0wTlpxdnBlYzM0d3QrSTUxVGRDRE1JQlhlY3Nod1Y4djlDRmJmbGNaTU1NSkE5eWxFSmkxZElVWXB4UW9pTFZ6THFGNUdhK1VER0lScXNUQjd1d1g4aWJKeFVzQ0R6SWFMUEdrOUpGcUU4UkxydndIeGhXWU1YQSs4TjdSa2FDbUhFUENQUVpVZ1NVdUQydEVERk5SL0FFUzBMQzk4Mkhsd0gvUlZadEhHejBXRFBDMEtQWHJFMVZHNzdYUXRxaFVqcWc5enBvN3RjOXhmYy8yYmluSmw5K2JXQmZjMmM1TVJ2U3NJaGpaTXRmbkE2Nm1BUUFpUjVFRzNaK1ZtU3c0dGs2eFdRbHd1bVJBQUJCbEhPRzdWbkM5RVU2UEJDZDlTVk1PNWR5UjVHS2d3TkRiSjlWYS92ZjN0SjRvMHgxdkpnWDVvTkp0a3BoOGJDTERFblFrd0QyZkN1Slo2dnE3aFBLckZIZkJ6MWZxVDZTTkp5OVBEdExjVTJONi9ndFBucnUxS082UnNnaEkxZGVlcE5XVENoNEJvdGR6SVlpdEN5N0k5SkFEQzlKYngzcDNnNmd2L1h5aXVhY1RXYkNaYU0yUDdBNDNmVm5qVEt0QmV2S1hZNDc5ckM3QTY4UDlFczZQVXRpeG5FOVdibmZ6L3VnQ2F3ZmM3bUJtMndsM0oyZHcxRlRlZDhJdnQ5TmxLSDNsS01jTXU0UE1FSnlyeW53VnJnZkpwVTJYM1UwU2hkR1hUR0RHamx3QnZSeW1YTFBIaytPMWtzeTByNGZ6d0hON0NjLzlmTFVlWmsreWg1dCtTZFRoenNxdGdjUEFTZU5WekxqNktMMmRReWJXZ0o3RnlUNEFUTTNEeXE1L1pRK3RtRjZ0c0tyekJ2cnYwZUpjcnVIVlpZb0VwV015b1pOckttRHhiNFlhTFRhRTJFZ1JCVVRtOGRUejVudDB3Yld1R2hiZi9HdTl2S1IrZUJCU3lvU3VLOGdSbVBvbWVGQ3hZamxreFROSHVpY1pzTUtjSHJyR1I5VXd5YzY4eG1sN0haSFRTSW1NWHZoR3ZJUE1ieFVCUGNPSDgxVU0yd0Q0SDZOTmlkT2FzL0UyZy92NjZzYXBpdGg4OEc2V2NPemNsMjhQWUMreFJ5NzRzZWhLaUZMZlhWakIwSjh3ZXZTYk9yWm5COWVMWWNIYXRZMHphdUpFT2RSNzRveUxYL1dHaTBvVmY1YzlEQk4wa3cyWVVxUE9pRXUxMmswTDR5NG9DanB1d3pjdHM4T1FYdDJheWhzUk50bVFubGxndUJER1VFcG84RmVRU1o1U25RUHdoaE5RZ0gzNkpVQ013QjZsY1RDcHhKb1VKdHFwQkhyY2JNcGlsV3FiZGJXZHFwV1lEMU0zbGdlcXRnckNFd1oxei82STBjV0JFWFI4WFpyNytLQW0xUmw2ejRyK2R5dkYvYUw1eHhzNldVYy9memw0NWw0RWI4YUFBeSt0cERkaytucFRNbTQ0Q2Q1ZTJUZGpMdFVxMDFMc1RwK1hUdGJ6YmV3d3dkUGhuTUtwQk96UDRQWld6NGxia2RsRDEySzAzUE1WYkJENnFBS0xaVkNvckdtOVA2VXllcHUvYlJ6VGtIRUswek45Q1UxVlJWZm9LRGU3RWlTZzg1MVh1bTRydjk5Q2t3eHd6RnpBQlY2Y3FpemYvUHdyWFVvZENVeDdUdFV6dEg4c1JVdlpQM21mcDU2MDNJRjVtNkwzYjRWcjBibE9YbmFabGVvaFZYeXc1eUlVNGYvZlJ0WnRZNXp1ckQ2RmF2QWo2M2JUeEZRZ2Q1Q21rWmhaWFMzMHlhWG8rWFV2RzMrUENVMDVCWVAxbXNPVEdveWVvbGxvN0t3QVJDbGVEclk0WHY1R2htSGEwTXNMYlU0MlF1VkJnVDF0bXBnTGY4RFQvcWEwR0FpaXNOd0ZVdXhmNXJYZW1kVlN4dVZ2WjJGVDNmdjBIaXZzNkhsZEwvYTJWempzbDhWSUxCWmVGbG1iUHl1a0RDYXJrLzFRdGRVdTFTMC9Sc3ZQaDk0SHlUYVZyR3hvNTBYZWZKUEMxNjV5bnFMVHJZZmRDVERaQWRBVHc4T1Fpb05lS09peFhTcEJNcWhvRjJTUVkvZlVsZ0NPWkxNc1p6ZkZJY1BEc09QSzBWejZJdTNQeW56OHNVRmV0cVZLckxWL2RwWVFielJ4am5VYmZXSmtXOEM2cEY1VCtkN0Q2byttN3A4N3VnY2JXMzlSWDVFeTh0cXR1TW43cjVsb2prcTU2cFpJdTFHa2Y4bGx0MTNJa1NmYksxRjl5b0twcUlITWNpem11VkJPcGpxYndGNzJXaTZvYlNBcXVFYlVsQmpUL2IrY2sxTlZKSGRKR01vdXZxQmprcVJ4b3VZUS92Q1VGaExFQlZPWFpKbEw4cTFRdjYrTy80MWQ5Q0xUclFYajFHbEN1YU1rbm5yaVo1ZE9LT0JFTWYvdkVVRTVUM1JEUm5BbG02cml4OGJDaUdTUUVBbWh3TVBCWGVEdkZ2YmJ3Vi9mRFp4Y0lxNnQ3OGlHcjkwanUxazZkOWdGZjhNSUo5ZzRMakxkZVRNeC9zZGdsaWxueFZsREFaSVZnVHIvZVUycTRYRzNnVENFbkl2UnhycFJQc21iekhHcVdZUERWU2U5Z1hRTjJEZU1ucVROQXl2UC82dFZDSzZnNmltWEtzaXgzK1FpemlCSlcxVE43cE9aVHA3NWdBZVpLbjdUbVVZTHlqY3owanIzdE5hVk96UGFhbEZsRm1sdkdEMk5EVEM2TnBGYm0yOGR3NWk0c0RzUWNWcFZObUpsTDFkRkt5NXNDTnJUWnlxKytuY2pPS0tKODJvd1RMZFYzVVMwV2JtKzNnRnUzZUxRZU8yQVgvUjV3WEFZQnN2eFBoZUMwOXJTQTRKRm1uK3RvMkZBQWVPMlR0TTJidDRLV3R1SE5FZHJibVd0T0p3bmZpVEFqSnN1UnVFRVExRms3TWpvaG5FQjFNVHUxemtxZFpodWhISndIUENvQU9xOGsxS1VTdk52Mzl4ZmtmM3IvTm84ZDgrd0ZaVWU2U1h2NXRDVjJaMjYyS3NGNDhzTWVzMEFyYS9VbU9HVm43SkxCYk5rUFplWDFSb25qVXRKNEhsMDlaWXFiSTVkeFBQUHJ1RXpGWjZkVFowU3pNQzE2L3ZKNVZJdXhvbWVnMVBBb3dKZ2FwaW9BYkJZWktNL0w4cVI1ODN2b3JjamNYdERTYmc5SCtrYWlKZ3djQU9aVEpqMlR4ZU9LSUtna0M5bHlLbyt5bzIvOStIeTlwZXUzamlzbGxKU3hiRlZ5aDVGT0d1bHgvbk13UGV1ekF3THRxazBJaDBhR3JaOGhqTDZObE15dzlXVGpleGkzdnNZNjBrdXM5d0RXWndOSkVQQTFLL2Q1WUV4U3ZFTWJJN2l2ZlNFUS9UOU5qSVZrL1Ric1NQWmFIUVcrUDNyVVZJUVNkcktXZXI2SlVzbVFEWUprTGROSmVwcVZUZ0pubm1pNEd4OUIxeUtIc3F2UG8xcEtHMDR4UjQ1YkFKNzhabFZTU3NzYnQxN2N1bU5uTnpUVGxwb0d6SWt5UXBzc1VmbEZJQTAzQW5BTEJKRElodk0vYzlWUklHRHkxVlJDdkV6dURFV0NjNGJxR3lDekpTWlJFQm9qMHRCOStoMmRsVWRPTzB4dk5vRlgwbzJ3ZTZVZGZYN3FyOVRDZHk2bGh1djl1S2pzbC9FUzZYZ3ZNVzdhTDdpVkx0M1hRL1BqMm9yb2Y3SzZ0OGxzYml4M2pIS1lodzg2UlFUR3hadUVDbThLSUpIcE4xQk1uWEk5aHova1lvN3JLdVJFVUsyK2NaZS9sRGxUcDRRS1dmb0ZPTnppR3RONEhPcUZQVWJyTDJxUEo1WmJlSzFUZHpvbnFUaDEwUjh1QkhiWTY1S0hvNlNWZHNiOFF2RDMzMVZTR2F4a3A5ZW9GQXBLaWh4MCtBQjBVRzAvNjdaYm12WUZjTGJmeVpabHRSaGFEbDJwNFVaNEJBaUhzajBMTDYrNUsrSC9VZUVwTlNyK043SFgrWGczQjhSZGYweVpUQzZNUnNLWkVqS0JrRU9BVG8wZmYycTlpLzFvUTVtTnllay84c1dDN09lNGsweEUzNC9LYWg1emVNcEFLTGdIcFlUeWJMQWVZekVkbXNsU1JYcFZyZ1QvYlRYOHREdlpKdjBBcG5jc1p1cjE2Z2VqODYvTlF5bWh1YUh3RUtMdFdMTzRrSzArMGEvdEh3U21BK1VqeWppcW14S2xWQ3UxamwwMmpma3ZPN0prUVo2S2FsLy9jSExEWWFEZEJzSkhnZVBreEQxcDZuRDFPcW9mWk92ajlCaGJFVVNqaDQ1RVZEeFVoV3Q1WTVUQzZ0SU9JOEJzb0pucTJXUnc1WGZXellPQkY0SDlOazFrY2h0WlMyL25jdm9DM0V3dUozTlJBeWw1OUdDclVJc3NMNng0Z21wWmxmRWt3eHpESEdsdTZpWVVHOFpJMnB3THZUb01MWDdIVlUrSjZVTHc4M3BVU3ptZjc4d25heEZ3TS96MHI4bCtDOEV4QXduOThmT0NvNnZjQk1jeXU3M3A5V2ErRnRrYUtDSDJoaGxnZzVwWm1obndmbnlSWXJ4L2dYSUE0MnFSNWQ1eDV5bmE1cXRQZ3Q2dGlKYmxKZ0hHNTE4bjU2VDQ4UUVDdVJPdXBlZ2RjYVlBcWsya3crbUZVMmR2aGN4VTdCaFdEWksxbFBOamZYMklNSGpJbDA5RE14c3ZFYS9UZ3RaQy9FVXFQdjRsZEd3WlFkWEM5QklFOUU4RXhnU1VacFQrQ3cwU0NEWW0yR0xQOEZoL2J0UHJhNWpzZTE5YkZEVFBNZStwaWZGVXRwYXlOVlFPb1ZwcUFsNzdEb3kzYWRxb1dEc0JhNUhaUHNzUVdVdnZWT0FGWDR6NzZmc0pwVllzeGhvdFdCdURRTlQzSURRWW9qRUFDbUl2L2F2djBNdjA5bDNKMlVoZUJNamxJaUQ3TklCOHVCK1FSMUtaQUU2cTVhVFlBZEs4QjFPUVBLcjZvNE5QbVNIQzEyeTR4QWNXOEdSQVVRb0FmdXlJaTZHSHhDRjZqQ1B1NDFRcU1Ca1k0TS9xWEZ2aGVRbFZJdHdRZzZrZ1dPMis5SHhmZngrWm10T2lrTC95a3Z2NmZlc2N5dGtzUW1GWi9WYmM4aFQ0ZmoyVk9mR2EyUG9WMDBTaitodE5kOVVBaXpMc01zWlZ1cHRvcCtZREh3M29CQlo4dlhZTDJ1aW9tMGszb1hwM3pDWVNNQUQwY3dwcXhEUzA4cXdJNkFjR1NKdmNhajFxR3JNa1laeUFkMjZMVUxtKzd1dXZRQXVpSHNFRld3NSsvSGRhZHhML3lTWU9LekZ1Z01qTmtYMDQ3eDZObjVTYVBhYlJHa1VJSHdmOGxQRlpzV21PaDFIeFJ5QXdMaTRFd1VzQnhUaE92blYxZW5CU3FjVUxkcTQ4OWZNSXp0Yk4vMlF3eGNFV29wQkFPSzRnVGNUZWY2NzV2cFV4bTVQd0xCdUZSWE84QmdNUnhIUVFpSWtidVVGcmlQS3ZFT1A5SmVNOU9lSlVwT1lVVHhVb2JqM3ZVbkVLT2ZoVVFucGNMemZUKzliS3JiSGFCRTk3MUg3bmJXbUMxbE56MUIxWDZpQ3NZOHRzcWw4SU0rM1NSL0l2YjB0MjcveVo4YW1PZFN2bEdxZStLQ3QyR05UWURZR2ZmaHdwcitGRElpSEVGMENpZVMvUHlibWJsV0FySkpBTGhISUJQeUhSWWhlUHNLcTdmcjFCb1MwR21pYThpUm1lUDkwYzZWdFZSbkRLVm5taVF6TmdGcnN1UTdWY0dqTnFGWkxQZ3FLeFlaSTlwL0lzR2VJUnpjcVZuQ1lhWmJLVE5HVTQ5cW40YVN3T200ZG5qSWh6eERVZDVrL0w1czB1b1lENEFXVThmOUVvZzc1VHIxRFlXc1F0TnZkZngvN21EUWlraFArZFI3UVVzMXk2aEUvZCtpQ1BaTHA4THk0Y05QWER1ck1xc2t3NUZ1aGZnMGRlSmJFMUFGR05lbFdOWnMwYlBzYk4wdXBKWU5qM0lFbWR2U05oOVpaUVRCWm9rekhRci9hK1M0Qlhxa2w0NTMzdDZnRmpldHV5TXpKWmJJRnpVekpZZzRoT0NINk9KN3pDdXJ0V1J6WU9ST1hteUg4VWtiV3JKeXdSV0VtNlJRbjlJT1J3WHA4Y2pwNm1raU44dnZyNmhvYkdlbmtBUWsrakVFOVZWMWMva2NWUThiN0VBL0xHR0FHaWlVV1FxVzJZWkFtUVZ1VFhoQ1U1WWJHbHFMT1RDcHBVYjR0dkFnVzVBVzU3dVRvMmsyaGRzN2tSYzRRWCtiUTFsa0x3TlhqbXpFQnRLVGFJMmwzZ0gzZkZubWJycVk0L2xZN3FESi9Odnlja212QnN3ejhiLzRXREI1TDAyVTFnNmxzUS92M3lGeVpqbWU1SVZ4OVVlc2hDZk12TjFlWm1BVGtwSVBmVDBwU1U1TEdHcEQyU1JLdnZ6dHhsbWRQeWZGVU5idGdlTEdkeEYzQ0lmazZZVncvT0l5aXkydXlKeFRFejl3RHQxQkNkNlRyd205bVJWREQrTzVicVdqVUNLSlV3TE51TUFPOSt0dlQvUlhvaVU1b1lvZmN5dGsvM1JjZmxsVXdJZ1hKakdHTkJtZnJuY0ZuYU1aRlJJVllFL2NjV3BLNFdsbWE0NjRjcllsTkQ1M3BVS2J1dk5xRWRVcENucE9UTnZGd2xDUE9UZi9QN0FXQUIvRS9BWDJZcWtFYmh2MDBJQTZINmVrUXVjTHg3aWM0bDB5U3N6ZDlXYVQ5UFlVVjhva2owak5tZmRpZWlVSm9JUkxOa0laaytJcG84SWg2WlBpb2NuZjdIaFZPL3M1MWJ1anVqQllLZzMzL2dMS08zZktlb05hYTV5aXBtOVk2enpJWEN3aUNkZk80OHVaeUdnZzduUkUyWk1hdnhaWVhKck8yVDVZTW1lTkpXdnloWDRiYUVha1hGNjJkTXNSZ1BtSEJ3M2lXck9ieFJXRFBKVWxyNGg0MzVkV3hPRzJKQjBTUUl2bjRENk4zQzk0cnJReG5kWmNRMjFJNXZiSXpGL3NqZjBuc0p1RFIrajRJRTh1a3dyWW8xOWVzTW9WYzNHQVAyZEkyeGcyRHRGeGo0K0o5Z1lsYzZRVHM0cG44QWpCbHNjd2U4ekovdG5Ga1F5Z05oOVY5VEhHYlNXU2hJYXovYzAxbDVPa3UwV3FTdjZhTmt5WTRla1ZNeTMzR2Z2VUhyd2hsYmVrUnRSSmF3SUNVa1ZnU3RVSjQxTld4OEhxR3hGMWtMYXRQdCtXYXNrU0tVaWhqWlNsdmErc1UxbjdoQm95a2p0elMvYU5LMmFIdEM1TGFvbnpjN2swcDdQOWJyakxkYUprWWFrZWJQZEkrSno5NWd1U0VXMUZEZkhiYlZqSjdRVksveUR3OXQxVVdlRFNBMGFTS3BKUjAwZlI5cDF6bDE5MG9RWjRTMlVTc3RPNFdkeExIN2dEZk5uZno4UGdnYTdDYTNpYXR1WElydnhiRzlpdllWTUhqanVUdlptd2JjbDgzZU91TmZyV0w0NVgwQldVVlBTaTE4YUE0TE56OU1MVXlpcThnZ1ZiYllFZGh4TXlicnRuL0pFblh5UlpsLzZ3eHhFcWRhT0NVelFReUV0UDNKdXovK0VrZ0FpT3Z4VHcwb29NT011dHkwMWtEYmtCQVNBRHc0K0NDSTBVYUc4bzhyeld5cXdLMThmOFpEWk51cWtqZHZMYTJPaGQxL3Z3RTJXTjVObXFBNXhDY2RnRXFZcHJHSnVTdHNVeGkrbktiNWY4dU1wVWwzZFBuenFzVFozdUxnMjRGTmFzWGZDdlhQT2RFeUxoUXlxYXdxRlY2ZFhtTXpKUnJhOUhRL1JnclRvaythY0p5RE1qTTVwS0Y3d01JakJNd3BxeEtTYXlZZEQxVUh0aUtIc0pOR2dVL3FFTzAvbEoxZ0hQaGRteHJpVEt2KzdOQm4xZE00a0xyMGo1TnFZNkpSL1hxNWUySEtZUzRYaXArZ0NRMmV2M2pwMEdCSU15RWU0bktGQUUxSzJ1a0hUK05wSHpZL3RqU0pGSkJlWG5kazdlK0U4QmxFaXZnbUU0QkJWZVJ2djJaNHpuWXZtckczK3dNbXh5R1hSVGphNVdSV3BQdURhTTJjeHZxbHJhYnM4M2ZNeXRlQ2Y2Myt0VG0xa0ZZS1UwQllqZVFJeVBSYW1pMnZLSldiMkNaUkhkRUNFbUZiM1F0YnhIb2dJVWRMcjhFNGtTQUpsTHFVaVk4NTlKbmd0aTNkMWFHSVZUd1p2bXFmN1ZxeFB3TS9PaDczRTBxWEZCMUdXRVFVY2Yxb2l5SGdzWWR5SjVTQVpaRTVCQm5MQ2lYYzhWQWVBMUJ4MEg3NDBTTWlTdDJEdko1YWZNZEg3WGIxL2l4VC9OVHJ5UmE5VTB6cmwrcEpJYmNqeTBrbEcyR2FhVWFpNi9VWFRwcmQ0M0drdXEvZkFjaEdtdk1mcW9zUEtjOHBOVlhxazZ1cDBNelFuaC82d004L3AvRFNBRVkrQjhrbEpYR2tyZFRWSjlWVmNYRkd1V2d5VXIzckNoVDd2N2padzRKejhZd1dEaGZpbmJ6azh6RGtDWFJsRjhMa01PdE9RUUFyaElVZ05ZbWMxand6VVg3Ukh3Zm5rZjVyenFoN3MwSW03ZVo1bHpTWFUveFU5cEppMzJ6YzFpMlZyWGlUVWRmOEh5a1Bqdk5mMUVkT25KNUdKcWtoTE1RS0FWQ1p0NUxMb1VsbVQ1V1dSMVN1YWd4YlVtY2NqcTB6QlIvbEtncFdSVlJLeTZmMkltOGZRWkZ2TlBySnVYUmN4QkZGdWYrTDUwam8yVmNlSS9EUEd6eFlZcHovOHdpUDhlQW9pWVBVZDFFWWxLSW85N01FTVVLcy9lMTNwOFd4M3p1amYxcm1Cank2OVdZWTQ4K0dFamlTNXRuRUlFUDk2L2pFL01ncEhPRjBJUm1tMkxOdldrN2pOMk5DNGFHWTE2RFRscHZaZGdwTWxrbjhOVXJnVXpuOGRidC9KVEUyMFR5UnI0NE1KZVFuaE84OGc0TWd0TnpaZG9QZ1RPYlFaODFmaVl1OGJjN2xFQUF4UHQ0cDRSZEZGZkVsTzAvbkYzaUYrRWZpSmtMYzJDRG1oZDVQeUUrUW5LcFBia0Rob2lPNS9oU3NYS0UwcjZ6SFhQODVpQ2srRm9qbEl2R0pjdlVmODVvdUpZZ0xCR1FSOHY2SFJwanllZzJKTXZGYzFrTmU5MUYwSDVYaWRPU3UrSFlublJyYXNlTXQzaUFQa0NqWHFKelJmWHVCS3pUS2pmckpTTEdCK21kZ0JwWEdQY0FMd1RJN0NpNmxnUXdhc3U4Y1ZGWTJ5cUQ5ZVE3bTVCSFo0QTJmeFdaS0ZHajc0bWRnZkdZWFBaeHhON2FBcVNDQmFYK3B4SW5SVWhXMlRqdU40ZEFLVXZWRW01MXBDRFRMNTBmTXpyNFd3WmpFb0dlUUk2Ykxwa09LU093N3hQeFgvclNPWUs3WEdFengvdllsUFFMa29uMHNLbUpWSVl6bVpaVnhIY01XWityN1djSnc4L3BrZXcvQ2s5MjFoRXhHcXB2T2NOTjgwNHJZVGwzTmRtWU5YZmhJeHRUL1p4VkZOZmhwZXlwNHRHdVI3akoyYjlxWW9XQXN1bmgrMTIrMEVCMjNvWlJLR3NzUFhwaTVpN2w3L2RXR3BSOVdjTmlXenNDaFhhMHMxT1dXWWRIR0xHTUt2WmFHRnRXanY5SFl3ZHkwSXU0OENRQit3NExKNndyUnRmUFpDRWlEVS9oRnFnMis5b1NzSDZGS0tPR01RQ1NXaE8vY2QycGFJaWhONWJCNFRpc3ZhbVMrVkJoR2JHSkJmbkNhMUNaMWFmYmpMaEw0UzFKRFU1OTR3SVUyTnZOS2VjLzRzUTFqK1VYL3lWUVNPUit4c3FiVnh1ZlVRNFZqczlmdHpOb3RUc21jUU5HaDJkVzVrZlgraTBrbDQvYWZiaU14VUdZcGgxcTl4aWVwalk2aVZPd2VNQ2NhSllWRml0YW1VUEUxMllCRS9ZeWZHaVVFM2szUkJEU2JseTVqWmtQMlFpaFpzMUNFdHYwVy9rSkc3UlIyMEdJSitncy9CVkVLbXJMNGl3VzRSOUhteTBUcjhjemtIaFNXa1pnNHRwZ0d3Ymh6WWFhWlRLZGRvaU1SWlBXcktTOFZNREpUd2VXQUlaOGNZSm5mbEYrT0lHTUlSS3FWeXlndEtVQndIbFlHNDJyTjB4OUFmZHVlYVNTVVRDSVpTV2phZVVqK0FpUTcyMnZJbDFtNUZMLzdmd2NpaWpWOFQ1elVJQmU4L09ETU9Jb1lIcHFYemdDek5BRS9vRG14R1B6bjZCZi96Smh4OEhuMGFqUTdFdithUTFSTlV2UW91ZU0rU2lqWUFyNzc0L2VKSkpZa2ZUdWJ5dFNsZGxNampOSFNZd0J0L3J6aXFFaXVjVHViSW9ycjhSUVBLTDV4Skt4OXdDVk1jVTl0RkJjTjNyd0ZRdEN3c04wWUIwVGZMZm41U25nQ01nMUJtYnBVeGFUdStjdlhPZUdyRXpGNDdMZFBCSmprRU1scWlYM0VBWmwwQllHa2dvaHc5QjlGWFc1RWR3aW04eGkxSXI0SXo2eHRjNGQ5SDhua3VNTnEyL0JNS0F5T05yUWhQRG9ja2t4cHlGZjhnNGczMERna01reEFOcWdLZXk5SFArdEhtcHZOUUVVZ21ROWhPUlBhdlFSV0Jka2dBaWFUdURTa3J5UENXdG40b0VYYzhxQXgzcFdqNGlNL0kzVnMvblVuUm5tWHpZUmdrTTVsMUFxVm5UejcrQVlIdncvcmMvREhOL0RzdFFaUkxhTzVaeW1kUGlIdlgrU0xVUFlsNFNVbXRHTTVFSjhNaWxBR3U5VTB6dnVtSmN1WU5JbFR3anl3dkdpdTZuZUU5akttZ05FQ2UyTlRYc1QwOW82czJjM1lxSmhka1JIeHo0clk2QmpkS2pjcVNOVDU5OUhja1lYci83RTBBVUdibmxzcVNXRlVMRWtzVXM4RC84bnc3Y2lZRFJ1UkpRbTU1YTdkeVJCNWl1NWRGb0FjdFBZckpOZ0lVMmpPMkh5YUhvdXNoUll2K2ZmZlJZdGdrSVNlUWFHQWZ1RkNuKy9iSjkvYzhFcXhla3ZSWnNSMUFqWWlYM1E0cVUva2FWL3VJS05DT0dXM3F6d2hkd2xpaENtYnpXTWJ4VUt6a3FoSndkRXZYS2dQUU9nWmxBUnlGaTc2OTcvRmk4RmFGYWJQcHpsamFZaDVLN3FQU3ZoRndJY0djM01MQ2h3Z0lLVktHODFqTjFNNC9IaFNZQ0ZLM3ZGbG12eUprOXJ4QmZKeUMxVXQyRy9oSWc2SWhGTTlIRXRSUFpjakJVQkhRVUZ1N3RBZzNyK2dtdkpDZU5HdG9kckkyaWl3RVNaZGFYZlFJSmx0Z1Q1ZzAwMGhROG44ZjFSRGtuYVhNdENRRTA3TC9zN1d2aXI5TGpzTmRvYXdrSk84Y0I5aWY3eWt0c0NlN0NIWDRzaW14M3pGcnVWREJGZWNqRno0UHJuQVhodTBKNHU0eEZEaDZGbi9qWUtrZlIraW9LWlYvZjdVNVhrNkdVTEdlRXAvUWw5OW5vSTVLaHhGek84b1NzMzdwbWM2ZjZyTHpsRlcrcFU4akZHUWt6NDhOMitWQ1RuMGlzNWxCSDJwaXlkbnB3S2RrVmhkelloNGhLVUt0TFg0L05DMXBvN0Mycll3bURxVVU4QWcyVE14MEtwaUZkUVAzNU14NFBiMkROSTYxbWdxc0piNXRHYXRTdUhNSnFGY2VuVlMxdEJTMTRxdmx3ZHB3ZVhqUmV2NzRrdS8xaUVZRmM1V09GVnJhMzVLeTkwRFpLNmU0WXN5Qk9vdHF3ZC9VZXZOSHZuMjBINWlQYURYbndxOCtCOEMxVEVacGgyRnNYMmJ4ZFd4U2oyTVdzWFZmWnNMWTNlWUdNdzZDUG5mdSs0VW9ETXNackNzaXhldFhNL2hyc2xiQkhnUUIwakhXUG5GZVJ1M08vODlSc0pJNDBYcjU4U1hKT1V6dUNpSkx2ejZiMmpKSnIza05aczV3dkorczVEQm80ekZCa0IzU3ZldTRld2lUZS82c1BGV3pQcE4zbVRubzN2SUY0RlRla0MvbmtyYmtBbTRGODlieDY4Ni82SWxFeEVqL1NDTU15aVdNUHZuOWVDYjE2RFMrSG1ZUFp0RTQxY0dURW1PbFhOZGtHa2J0czBFTGN4T1c1WmtDbFR5YWFSc3NIdjUrazNqTURaOHNBWFpHVU5sVDYvMVd4UVhWcTdNay9wNHZ6U1BYbDJvL0RjaW1yRERhS1VrTUhkZVFIWU1aeEpWVnVSTHF3bFlHUWVzL1BadnhGcEZNUEZqVjFybVBnNXZyc0txbXNQWFgxMTZ2RTRXM3ltSnF5MnFqZXQwL3VveWp5OHRDMDJ4VG92ektEbmZROU4yVHR0L3EwTzNvR2RRbWx1aDZkUjA2R0xiMzJ1NkJXYXljSUlFNzFHNHVXNGx0SE05Q0dKZ2YzVzNGYjM2TjJjdVFSdnpmV1ptM0Z3S1ErN1BaMVV2ckUvbEJZeEhpZ04rYjdYSkZibTdvbG9ZSml2azEza2RqalFiUXltUW1tMTh6QkhMeVRkMzVDcjJoa2U5MHN5WGh6bXZYVzMwczYzMGRLWnBTTThiaTN2a0ZiVU12NCtoOWNuaWhLaGxoWGhreStxb05kemR0NEE3SXh5Zkh6RnRlWEZNaTVpUHpNbWhmRVZCdCt5Y2VpcFZGZG80S01rWHVRZUZWczJ2dHRTaG1KaGlXc2tiMStrRlNQVFlIUnRWb2RqN2ZsZWs5REE0Sm05SGdlSHcxL0R5UTRRaHI0M1oyOHBqVXVENWZmbVNCTTJhclRNejFtclUwak84RXFpYXd1VHBCaUkxVFJCVUVFNkwzMGF5ZjJVbjNWWnp0M2IyNXJVd0Izd0xjdC8rQi83NURsMzU1OVRORzlycDRNdEVZYzFUSnUxS1BUQVJXT0VsR2l0eFp5RVRPak1xSEQwRE1RdHhSMU5OSm52SFJLRGhDcDM1dEtxbG81UzFjQVkrMW8za1UwNEY1cHc4YmpkUnRuWjJEYVd5cnVvcGszNmxZU0t3NG85L3ZaT2NKbDRBMHkxR3hDT2s2K00wVGZKcWovWUhyUWV1SUFYUHhDT3VVZUdvQzdIY0hmQU1JeFovdkxyWDRTa2tad2hsbEhCV3J0UThaTjAwOHV2R20wUit0SC9qSXQ5SkdQTXd6alN3RlRxYndNQVpNeEhIcWEzYjhoR21SN3dXbnpVbWNLOVlNOEJpYzltb1h0NVJvR3BsYWc4UkxYTnozTGF0Rndhc1R2bE5tL1JlVDNvbzVQVGJRRWRyYm5CUm9UOHZ0S2pBVDlPa2F6VEZGOXNTOHZ5dXRVdUhwQ0hpK0NqQXV4NVNmcENVZ01BQXA2OEd3R0ZLMXZ0ZDRNMDluMzRZT1lPQnlaRWZudnowdlpsTlZaemJnVmcrTDY0WnlCOWZVUE5rOG9UWXpBbWVWRFF5bXRTR0VVUDV6TFRxKzhZNWVzL0RvNHZEZURoc1hFbDRiTVlZclR1U21CQWFuS2xnVWRMYkl0MVpqdVpVWGx1SW5Wb0o5bE41YXZENkhSWmQ5MlZLSmRxL0pOVStObmRNZWZyZFJtZDFRUFpodFB5clZkbVRmUlNWcGt5c2NZMzFwVGZGcWd4clRxM05tOWRvdDdCM21JdEczM2JsVkZ6THRHWFgyTWNrVjk4czVDZnFWaFljQTFmQnlZTTB6MXM2eE52SVQ1TmtIb2JSdlZRa0VuRGxEV3l3WmpFVHFRRGRZTVlnUC95b0VwbjdNSWROTCtGYzQ1UXcwQkNnQjNvbVBiNUllYmdPWDlNUGdHNEQwT251MTlDMDNEcVViZ2I2RkgzcW40RHQ1WGNUVWM4NGN6WUl6R0lRT05LbnZuMHFFYk1EMll2bUt3QTMxNDMwODZTUUsyNjVGYUVINXFXQjRQeHZpNmZwTS9QbnBFSzdrOGczcjA1azBoYTdRSTVyNDZQSGpWRXBzOXhaM0hHazYxZVEzQkI5S2ZCMThVN1h1WFBuamNGWDVpQTNnQ3VMYWZURnQ0Q2JJcVo5dmkvMDhoTjNJbUp1NVo5U24rWVN4VkFBV2lDN09ucHY5V05zUDJNZTFpcll3aTBQbTV2SnNsTGptbTlJYXJ3WnZCWHVuYzFlSmZoSDl5dGV6TjlyaUQ2Tk9PZXk1SFNFa3BWVHRYdUl6am0wY3R5Zlg1WFBXQk1kSjl5cWNFWXllc2tzT1dGc1M4eTFQUWhId3dzZlp0dExib2hteENkRnpFbXNjSHRtZFpaUXR6SFdwL1lOSkRNRGE0MytMUXlXUE1OZDV4Yk9zQ0UyUisxZGNQV3NCNUdqdDhKbFZsdVoxY3loS1o2Vk94TmZTbjZXa2l5clhaZ2t1U2V6b05xQjluZGNmS3ZVOGpXSU9jZGptakdOL2JyN2R5WmpkTVlJNnF6bThDa1RUbUdPekJobC9OSXpZTHpSYVVlZmhFY2twL2tUZG9mSmMzNXl4VWQrZVdWYUpjZlF3RktHTTFoU2htblJzdmN0T0hTQXBCRmcwMzBuQnZ6MzM3OXZRcTFMbjFOSnVBekxPRDQyeTByb2xZaWFiMUhlUjZsN3VxcjdzWU1pd1ozNVdGZjFTV3dmQ3BPVWVCMVc3QmtuOWZEVE16ejhjZEppRCs2UUt5SDRzc2JKUzhsMDhTNTZQYU9pKzdDVGFsS1FjMGNnT3JnUW4xS0Yxa3A5MWs4US9ZWHV5ZGk5eHdNSm11VzArS3VZdjNlL1BodnVsczlnZmlEWmZyUXRQZWhIbEY4VGwzL1d1MGRGSm5ERVY1WVA0TFVNMnRFT0RXTFRaLzVLLzhGUisxUGdHa0h6NjVFTkNCbzVJQmtBZmxuODBRVC8xUUgzd0g0bU1qTWFPNFdTNTh6bHJnS1hjSlFzWmFReTFxYVR3Wit4MEpBQ0VFSml3RXd1ZlhHaVpnWGxmUlNaTzRMSkVsVmtKSkdEcmVyUG5KKzVBcUhzdzBLZ0xYSzIrdzRBQWlBQUVQdFFEeFMvb215Zk0reFNDOENIeHVzb3VOV1Y5bzlIZjFieWNrYVBZa0l1NW1IUFhaWGFyWHNNUit0WDhWZnBvKzJBRC9sRkVLeWJPRjI2aUVId0tMbWpPV1duVlZhV2VHemwxTlNJNW5wa0N4TTlSYVB2VkwxZks2QUt6bHNqYjY4SDR3b1gxMldOZEdxS3JLV056U2RJK1N1STZST0RkUzhGWHNRQjBQeEkvK3JnUElvMkNOZk41N1FSMkFWQVFLV2YwOUxNeUtPRWlTa2JzeXNuc0M0bDZoWUFtelpuWGJ1aHNwYTZaaFpSNWp0Rk01WGtGcTNyMTJxYmNKQittZ25lcWcwUkJDZGlneGNXYUkreWpiM3F2YW00eVk4cG5KUGlFZjJvY0ZTUDV0ZFRid01lb1k5MklEM014aVk2TlpSSXNnVzJ2dVcvM1JvSWNGTERVem1CTS9kOGROeUUxTU01bzhKUjArdEVtM2JKdFMwRUNPcE45dXV4U1lYaUVUU2pSRHlTVzkwVjIxV2RPM25leVBqYXhMeEsvNkd3b2xJdHEyVXpZNU1FQ0d1b0lhMk9TanJwSTcyTjVCeE9QY1BROUpZVmNUdGlvbmhpRW5jM0FKaFYzOVZWOVMycHJhcW9tOHVYc0RweTgwL21DUDh6QkR6S3p5ZVhqNThuUVZnZDZqa1llZmJCYkVuMVhJb2Z6YVRRR3BjdHJXYmNBV0VNczFnd0hOMTFRU2tVbEdNd2VHY2paalBaQkNPVlBWQkg4S3MwdkxhVTZGdElxMFd6ZEdnV1Ntc2FXanFlVVNrT3Exeklaek5wV0k3dzUzeHZVbk40YlJBRHpwNnRxS3lza0s4Wml2Ymt0Ylo1Y2s4NXl5dW1hRW5FV1VOaXhUUXpMa3pNY3hobXo4cXdQcDQ0czJhNTNXenNSMUxGR1hodlVyRXVLNTQwcnF5dDdkT2I4alZINkQxN2QvZE04dmhQdVBVUkJWS2NzYnp5NmdYdG5iMmVhOC9QeUczSTZQOWtVckdMZVNSOWUzb1VrWE9JckNKSDVqY2NhZWt3ZUlBRUw1QXI5aWtNdmhaYWVYZnVlZFVteHVhV3BJK2VpcDkraE1TdW5aWXpiL2x2ejFpS0RHUk4wbEFiQ3dHM1M0UGNqVmo4TXc0OHRyWnFpL0dhRVVxd05Zb25RUkJjNzVUOVFWcVJYNytYa3hGY2YvKytCYlU4cHRmakNGbHUrL3ByRlVFanVCeS85c09aS1ZaV1VjRTdsYUU1Zno0SnZwR1FPeUpJY2c0a2tmUVFoUys1QUg0U1FRYXBEUElMNGRHaXVOS0REQVlJWVlEYlpYNWRjd01ZQkRJWW5Dem5uRTVLY1J3SEFCbFVFRW5FRElOUTRXVm83Q1dzZnlJRERtRGJ5cTkwbkFWT3Q5eWVpa0lBRGdhOEQ3d0JFQWNnOVBiVURhVlBIbDlzMjFHQkJTRG14QnUvRlAwUEhBU3hzWmRvT1JKV3lnNTlhYmFiMGJnZDJVUHQrNlBOL2FPd25GTy9PYWNTN1lQN3FZZWJtL1ZIaFVmMWxaVUhKVzhpako4K3FBZjJsZVNYaEpma0UxdFdSeUVmL1hrNDlZcjAxWU1DQmV0cVJ2NTZmbkJlNCs1dUJvbzJULzlKRWhIL0duQmdFK0x0U21TaDYyTnRIdlBjNnUxa1hUZXBJNk5oVEViRmVwU3lqM3FvR3dRZS9qUTlzSnM1N3d4WWlEdDBTTEs4bXppTmtzR1V4VFhqUTB2Q1k2dzIwZEx3OGFHYXhTa2dHVjFIWld4TTh6ZkZOOGJPN20yTWJZcFA4eU5Wb3dvcXM2TXdsZWlRQUV2bkVzMXpTT2xJZkxaUFJTVWpCQVVDVVhKMWNScldKZ0dXUlRPa0lMVnpEb25rSytoNFZZMjgrQWZRSVNCSExkU0l5WFNVQkxGN3k5STdtRlFGdHpBdXJtaVduRHcrR21pREtOT0ZzV1dPV1dVWjFXVDBlTU5RYzFSeFhwWGIyWWtZejNLWkk5KytwWk5VVVEzdkVVa01pbEYvSW5RODZJT3lxbWx0RFlpS2Z1aldBMXlmVW9kVFVNUHF5eDFhYjV2OW9uT0pkQ3dldng1aElzZERKME55QTh1Zy84QjJMRGc5RzZLL1BSMjlDc3lxZ2JKcTBDK29sdU1PWHB0OHRjTXdsei9YdnV5cDNVTGFvbzliMXY4VlJDY2NzZkhTZ2tkN1luYmN4cGJ5d3JJRldhcjJuWnNuaGYyY3BEa3lscldtMnVDMVZiS20wNkp5K1c1RjQ0cTF2V3QyM1hxK2h1aU5TVmpZS2RseG5OdGpqMGsrSUtpYXRiWkU1WmJrOG4vZGpqZk45Vld3K2pqbzNldjExaTVub2t0ZEpTQ1JER2cyUDl0R3B1N1UyL1U3YVdRclB6c2JOWkNrODIxMkdsdkZEQ3VHSmlpekdJNVFpOWNXeU5iUEh0ZEFJOW4wSmpBWjBNOE9aQmY2aXVKc2lJT00zTCtlRkVwZlJrUkc1MXErMXpHU3NyMldwUWdFVVZ0YnVyczhMbU5SMnVTQWpreVpiNGxKS3pKNlhGMHQzZFJXQ0VKeWRSTXVQeExWRWR0OU1jNjgxcFlhMjBZbDkwVUxaVldlYjZXcnJMUWNINU5zM2oxMXdNS21MNHhmWGQrZlJ0V1IwTGFycEJNSXZiSDc5OE03dmlHK09iSzc2MVdTT0dueXMrM3JUOTJZejUxL2xUTjNjZVdwLy9Wa0tya3ROMWNOWTlpcTRaYWJKNy9WUnRQVHplNzRCUE05TmJzZ0FuZW5qbi9BRitoeDNJZ0IzdEtWUUE5aEV3cy95aDE3eFkvRlBncEVFK1BGT2pFamE2Sno1VTRBTStKNG9uVEdIRjJGTmJFMDIyY3g2dXlhVElyUy9nV0paRCtuVHpiUFcyZ3hPbTFieUVJeHlsdDdZK2xmVEdZSTZPb0J6UWYyVlpoRmIzbzkxRG5idkY1MXlUclVldnNHUm5hSEhiazR2NmhCRGMzNytoOFcyWDM5czB2T2FYVXhTQ2lxN3FBYkFIZnRCTHV1Rnl2VFVFYzVWQnVVRTYrL2R6cE4yeTVWNndHanRTQmk0MEpGRnd4aU5pd3hBNFNqU0xDVG9naXpWL2RkM0xWemE5VFYxMStFNWxaWGFBRHFjTWhDeGtEUUpzaXJCcEd4VW9pdXA3blh3aDFKbVJiMWpTSjdVVnBqMUxIN3RQWWdVWkUxTWhna2NITHg4NUpucEMveXVqL0UvSFhWb3pmUzBMMEk2R0dMMnBlenJyeDlsbzdsenUxeDJ1YnEvU09XcjZiakZDcEtPQnpST3Y4L3VCUU9sZEZSTkFacmp3UUc1c3prd2NoMlpoQURSb1dqeStqaHJ4VDVlSWdGWlFER0txWSs4WkR0eklyQkFnUkdrNmxPL1AxZmxkMlB4VVdJTkhoUlYwN0NZbkRTUndUeEtTQjhVM2g4UC80RHdNNEJnT0FZSUIzbnJIdThROFZQenJDNWx4aFVhSjZDck14emdjRHRpNVU1bTF1R3d2RVBzYUFmMkltcGVTK1gvL1l3d3VqazhqUUxJWkVZV3FqaENYa0VpeWpBQ3ZLZjRwdnNSZkhPUEFHZnArSTVHOXNxUklVQWwybGhKUTZlVkthVVNUUG53TzFrSUNnMXlNQ0NnRDBJWUl3Z0ZUbWFKK0RTdXlMOGpHZUlYd1EreEE2NE1kR0tIOEcyVG5UTFJIL21JMXRFN3ZaV0pMUHFJN3JvWEFHNXZDMzdWL1M1YkJ6aEVoVDMxYkRENVhnU0F6Mk5raHMvUGZQdURBZ0F5QndHV1YwQm9GZlBGNW9Fc0krQzlmSW5MWWkvK2tKck9ablRZK1RMRzlibkxKVnYwTkhCNGN2cWZGNFJ5UkU5VjBBdTYwYit6SUtlWmdGQXI3dVB4TUVzQzhQYi9lSDlYYTZzclJCNk1hNVFOdVE0L2xNL244UDAvK1p2c04zMXFZd1JtbWtEYWFHTGhwY0E0aG8wWGw5RlExY0tpY0treHZTdVhBaXl6TnZ4SkV1UzJBZ0NlVjRNKzFrU25NZ2liOFhUanBLd21Dc1M4VnMrbyt3YWxacGxLMG4yNXlGcjNYY1hyOTFjblo4ZUhld3ZreWpjM3JlZGdkL2JaNGJNMVgzeEZEZW0vYm54bFVpcGZ3WEM4VEcxKzhqUXNVTjQzTUNnTkUxSVFDVW11Qy9WeGUxbEFMZjM2MHFKS0VSck5ob25DTDFoL2ttTklPQjJMTWszaDRLbUxjMkV0dFoyaGJhdEc5UzJYbjcwOXY3Mjh2eHdmenJlMnMycHJQQStsL0VrUGxpbi92TERrV2VaNjZER0JZcGZTSElkRDdWaXMvQ1NsMHRTMmh0ampNaU5aZWFSdVdpa1lUOHZIQUJPcU5QTXM4UTkzeExENHJpTEVLYzYyL2FaeXBXWnNkcDJSVjZtVTVWUWV2MXVkelVPdC9mdVJzRndtc1Jod0hlUzRTeTBweUd6Sm1zRVhDSnFFWGdkZTM5cXZzWkhzaW1vQk05R0RaWDlzMHBpYjEzQWQ0ZEEwRmcvS1FscHJwMzBCL0YyRXZxemozbXN6R0U4TllNUWVGQUFia3NWbjBhL0VWOVhNM3d2Q01pMXBzSHVsRW9Xb21pVkovR1ZOOVQzRHdEZ0J1U0NGNDB6Tkt4SS83dGNscmtHTWZEM0p0Y0NiZkFkQUJJMnlNakljQ1QrWGFoZG1pQW1Fb1JBSVlUMTI3QTRVeUlrU2FXejV6Q0RRMGQ2Z3l6bUg0c3dhTHArdHVEbEpLOFVkSk8yV25PanRxN0J3K2NWZUpISDhVUm5hSDV2SkpuR1Nmb1pwUzhmTWh3bXQwbFR3dG9hR3BERFJhTVFGU3Vta0FZd2o0ZEdXR0tJTjhxOXBhWUo1eDF0dUZjN1B6Y0U5QVlSQitNMXlkWXdXNWdZK0g3ZW1TQTg2Qk1BMW53OWpPQ0xLVEJ0U2kxREE5aHFGVHp4eWtaVnBMR04xb0UzK0lZMW9WbW9XMFRaWitTemRET213MVYzYzloWmtEdFllSmp2a0FqTG1GQ0tNQW00YzVMeUN4M0p6RzJpWEtJSlVjRGlLNW1mbm5mZzVZNzZCbW0zNUt1OTRacWwwOG5FMmZuTy91TjlXMy9scHZFbThSMGZCamVldzdROUVONlJDNVlWR0dLbkJpdHFGRWxUMkJidTgzUGI2KzJoR3cvSXV0L3dheEpmcmpJdVhxSnlpODNRNmpJeGV3U1B0Q0Z5WFF6eEllK05KMjVGaTN5WkpWNmgyeWNrdk0yUHJnVTNtR09IUUtsUnBwQUFCVTdJbkJ1b21yWHVqWXp2Mkd6UU5FZkIwNnlGakx3dVcxS3g4OXllSmVic0cxT2h3cnphSVMrOWovZ3VmWGZUYUxYbmFvYVFFTktxZjMvdi90dzFaUzY1NzlubzBHYmdqSmVST2RUQ0N6THdpR2ZoUEpQL0RrQnVZRFkvUndUWG9yMjd6OFNwNXloM2tMc0VxejdERFZjeG9TazlEK3haWXBidU1VN2xMV0pLdDlQbkticWxYTzFaam56WTFZUjMyM2JlQ0xUT2UrSUpaR2w4ZVZMdXlRSVdEQllrYlJMQStUTlQ0NFcya2lkZldsZFB1TXQwQTVweEJiTERLdEF1MHd1SUxYYVRhSFFXUXYrZldOanhTbW52Sm82Qy9vNy9ZeXRkOER3empIQXhJUFhHOFFRZGl5WG9TZHF0YmZQenZnOGRDZHlRUitoWEQ3YU9yMEErMmJrR0w3ZlQ1eW02cFdHMVQ4QXVCMjN4V3BQa3RWSlNVNEtpWGFFVnRFUDlaZ0xPd0FOdE8zbENLU2VjNmNYRHNobHFuMit1U3EzL2hWbkNiaHNiQzBCUG11UkI5MGFxMkpqRXpWZXdFdXF1Z0ZVVjJuYWI2WHZ0MVMyN2M2T1pWMzJpS3dXQVV3QmxYQ3d3YjhYWGpnYmpPQ3VCc2hVQ0pzczBlTVVydlcrTlU0eHdjd2YzaFUrMENadjFlTmlxOTJna0EzL01sUGFrRjVEZW5kU3Y2VEtqb0xieUoyUHpPTDU5L0lCTGI5WVNQVmpycmJtaklGRnk4R3RvVGUwVTBVa1V4NzhRSVNUWkxiRkFKYTZ4ZEp0UkxOcXkzN3J2bEhycUdvU2FtQTVLQ2pkVG1Ydi8xZjN0MlVtbVhDaEZuaFE3K0ErWFJHMnowMk5rMWFpSTA5dXEwa2FIeXE1dUlkMGFZOWFNeEZveVJ6MjA1WG8yMU9tYXBtQTJWWmU5SldCdk5IdGxQbjZqc3pHMDVUM0ZlbjJ6TWhzekJmYVo4V2dGV0Zwbjd0dENJQzF5cXdpWUpDSHJjTkg5cGpkSnN4NFBTVytBOHZsQlRHVEQ4NkFUc3RqeExJVGRmbGRuWkdtTXljYWlKZDNzenNlbmdmbDdWS1NOVGRLTTNGMFc1dW1XTzh1QkdPM0tDRVNDODVaU1FaQW1BRkdlYytwN1R4MzNVdjFuSjBpNDBhZUg3ZlVGa201QXIvYUdocEFCRy9hc2gvaWdLeElTQ2ZQbUI3TEl4N2ZsY3VlZWpTNlhPN1dsOW9qWThrNkJpbGl5OTR0VDltU1ZKNXl1OVMzNllHVVo2YmFnNFZJbHZRd0hycVlVOGtFUXRMbWpTL21jTGV3Sk13dG1RTzhWTEtBUW9hRnlDcG5MZUJKMjI5QVJPSVJrejFkZW9yNis0Nk0xVVVJYjVoUmh5TEE4MDhBb0hsdkJpKzRqNDFSS3M0NUNQYUc4SmNLM2hVcmtrZEdhM1lURHQ5ZVg1OGVIZTJ4ZlhuMURRVC84OUs2Vzl1NFdpUHkybnJmQ2ZhcjRPS0p4MGlVWEVJa003TUpBcUdReEJ2dllSdEZzM0ZvcEJuWHB4SjVlK1JrczAwVVZXYzhyc0xwSVpVVkxBRlI3TGdjZ3hiclhHU2VwcHY4Zm1KdXpMVmdHUFBEWmNKTXV1ajN4QTFkeTk2TFlleDAzYnAzbkNGd3hSUnZXVUNrYTFuV3o5VVFaSGVjeG15MW1qSmRjWU9rOUhxSytsQWVrMHVETFpZdFFBczlsWkU4Q3VITXdCSmZ6UEhaTlhaVkZyaVRQa2ppQ2tOb1lUZ2NnN3VxKytQL1dUcExiVEpjRnRxK05saWRRNStZRndIU2gvSlk5TG5LdkhqVUtmY2hDR1hLYldvdDRhcWx1K0pQZTdaY2N6QUxvbzZJeDRqRnMyMjFEQ3R4cFN2Mko1bzFrK3VDUWlVQXpyODk3RHBrYk1TcU9GNE5wb3RpMjl6SzdNN25hNjRSQ3FoS0o5M2FHcnBXQ002SjMrSXhuenIwNkVQbjYzU1NXU3dacUZTVUoxL28wdG8wMitWaklMbVE2cTdWWjU2M1IrcVNDZlJHcXBmWjRpSGc3Qi9hN3JzU1VEYTA3Qi92Vm0rdXJ5NHZ6czlQalNSUUdzTDk5VnhVaVM1TitzQmtFb3ZZSFNsREJDOHlpSnZxMHBhNGJZRXFoaHU0dllHWmRvOVpGdlM5TzFheEhnd2crQXNnQm9wMExNV1ZERTUzVVN1ejRDcW8rVG5hUGg4TmRqd1p0SHZoN05BNk9wK3Y2ZEtkSmdjc0YyVG1NYkZqTlBUYVpxQ2taRVl0b1ZCN0owbnM4aEwwaDZ5YStpdVVCeW1WY3hqUEdmQjBIM25Sdy9JekpNVG1Zc3RGeXo5MjNoWHFDWUxOVGczWm1FU1FONndLWURyRFhBbER6ZnFLd1RwbUNCQzE3dXNjYXMyZHhERTA1U29xRlhnTzgxaCs0YVVvbDVCUWZGSE8yVVNMVnVQZk1UTW1vNzFFZFBBcE1nUXNCaEVaSUVMempWalhaRDlpQUdqeE8wemlKTzJoeVVEUEcyU0dLTURBV1htZHhvaFk5SHBMZUFpUG5OOFJFbUo5TGVjUWM5di9yaVE1VzRjMEJpRWNIb3RSWHVnQmVYV1lkVmxETXc3WnFpYW9BZkYzaExTVWNrTUNZcHhac1lKKzlVc3MwNjF2VzIzMlp2QW1Sd2ZRZC9FVzVrd3FtQkZpdk9VMU1yMTlkek9OaDB0U2M0ZlE0dzJ0OExZU2pEcFRtWUROODVmTXlid08xV0tnbUFzOWlockJaYktNdWRvYk1MT3RBMVlZV0xLRWpzRXhLTDlhaXJ4L3dzdDAzU3ZpcFpiTmhrSXVXUkEvZ2hPc3IxNmRIdTh0a01aL0dFMXc0OXp4VUJhZVk2TlppQTk4Vm51eTZwQWFHYnMzalhrWExOTUFXMkZtSjdXWnZNaVlZUGRJeXV5aDFzNzYreDh2SlBrK1lOUHFLeEFOMTk3dDFZRVhYaWJpY1RKVWU0dHZUc0xsZTVvSUZKN1pCZ2Q4UFVHUGxzdzJneXUwYlY0cFZPMHZIRUNCT21NWmVyU1JBTW1qbGh3SGV1WUpUaWdZN2hVUXRHY09XZWgrMjJ3UVd6MWdSQWY0YzN6QWJ3QnhHM1IyNjdpamFKQVQxUFY1dXc5dm1RWGZYeGVyWmlNNm0yVU4vS0pRVUVDcWwvL0doL2VKYTh4MnlpL05IclZGWENuNjJLYktFSGkvN2V4c05tcHRqWWovdmV3TWJhazdVT3hXMUZsT01JbmsvRzMzemV0NnRMa1dPaDBxbzM2bG51bG84Q2pwejJiYkp5UFpHY3JqWkYxUlB5blIrbWFpa25lZlBtV1hVVFhZM2lkVUI1b0o4NENBN3Rhc0E3dU9iZTN1emVES09ndkk4L0NTM3FDZzJXSHUyTDl4OVJIRmNsL1MzZElPMG5RMzNYVHdYaWdhdUMvbjVySXRkRWFxbDlyZ1k4WFlPN0VIcUY4NkdBell6WDhVdjE3TnByTkR0N2Y2QWt6YTBIaW1HdXliNGRnY2ZHc1dJcysyb0Z0dkJOVE4vOFdUdVBwY3RSdE95cDh6cDVuRWdvZ1pkb1V5NUJmZm84ZUhoL3RpM1ZWbUVqU0lvM2UyZmlwbCtTT29oR3YyZGNOYmVtUExFR2ZaR0Y2ODAzYkZHeTFhUzlpTWlNM0VvNi9ONDJLcjNpY0lBNzV6d0taVmR3TE91NExtaTVKNnl0alZsd0JpdW1Bd0ZZRkdHSW94WUdnS2lTa3BBYTBGN0FZb0NaUmoyV0VpQ0Q3VVY4ZjRKdVRBYVg0dzBkVnhkclE5eHRBOFFBamkrcXBvR0pLcGxqVTNveDBJWldvTXQ1cUg4cGptWU5acUZteTA5Uzh6U2Uwb1FRanBMU1VWMzNIZEtCYkQxQ1hDV3lnajc1bldjV1VSeXVUQTM5cTF6OXVkNHR3Skpaem8ybG9DQWxLOEJ1RVc1YjdaVUNKZVNiem0wZXc5WFZnNkZLU1RMMy92TzZIUks0TlJ6YnZFOG4xdHo5VFZEanlFdlRTTitSNUxUdWdKNVpXT1BkYTdVTU4wbjIzN2JKVnYxTGdtTGhrN21sQUxNc05oaW13dGxmdEYrOTJmOWZBNlovUWFZNWNNc3BuR2VmdHI0MmhtbmFUeHJHVWhnMk1yRXhtQ2piYVR3MVQyVU1xYm96NzV2Tm5nRzdITllER0E3WEpWRGp0aFB5cWxFY1RJNThHVzluZlJ0MEdzUFVRQ1BUY0Z6Zkc3VEZBZVJBd242K0lISjhHV2NDT3RBSkxZQU4wV1lST0xYSVkvRzc0QmNvUjFHc1dFSzhXWTE1SnlGSEQ1T3JxV0ZIOUNpSTJXb1lkQ3ZuZ3pHQm4wQWxYakx2VEVLTUlRc2tNZVk3aHoxbmlKdXZ5dnpRbzRJYlVzVXBPTGVaaDdic0VPQjdFRHFscXVqZVdnSHh2YVhiSlhpemVIS0daYjRpSVlQVVBrZXExMG5vcWt4QzNNYVNNaFNrc1RIczdKOUZLcUY4Q1NaQkV5RlRwNUhYcWpsUU1FU1hoZy9Hd2d3d2FsOFVWcVlwOVY4OVBiKzV1eGtieldmdGpZa1N5S0dkNEo2ZHZaZWM5VkFvT0hvTjROdzlrL1E0eXRxOE1nc1dhaVJXUXhnK0xVdDJIU0Y3SnowZU9EZXVjQktSRE1kQ3VhRERUck43ZVVrM0RsNGVlcGJkT0IwdFBxZGt0ay9zVklINWh3M1MwQ25MMUlwT1NKTmEzd0FlK01VUkludHQrSWFqTDlpQndrRWxuS2xEZkxKUHFXeXExbHBZMDN4TEhGdGFHQ08rZU9Xc1NUYm11WlJUQ3JBaXNjcEU1N0dUSnJtWFdTZUdCblRBOXBIMklwMkNmYjhISE1OeEtxVWRPcytReVVtVEFrcmJ0TVRjRGFTOWpFYzlDMW5OQXY5NjhIQjdCUHplTmVUanVENDV0MThqTmlXZmxaNWhNRUNPNkdvcVJtWWhibnNITEJObXFVc1NuT0wwcDV0ajRld0w5N2FERnI0bkNzWnVDSXZTQWlEa0dzSS9QYjY3ZlVxQVNZcTliZkhwcTdpd0xGbS94WnZaeVNiQVdUWXFYdHRDVGpiWlRLN2E2VVdFQW9hZHJKNWo0YWhLV1FGbmwvT1phVjNlRW04WkNxb1M1cTEvM3Y5ckwvaEc2aXU5dm9mdkl4RFRPZGtTYmR6YkZ2MGpCK1AzK1QreWV2WkRESnFpc0xrYm5lYVBhTmZ6ak5nYnBkSHVuK1Zrekp2VjRPTlVZelc5cjVoMER4ODBVN1lBbS9VcUh6Mzg5UDdONjlmUEQwK1RHWjhqK2QzOVdqMTFxdERYTktzK2FvRmhKaDh1Q3N2V0ViY0ZHRzdtTVFZTVdIZUZVNWhvakZ2YnF5d1pCRXN3WXJuU2xiVXFsdmpWbG5kQm15a0ppaWg2Zm9hWGhZUFBXZWtEKzg1WjBEUURPa0ZyZWdSc254NXF5TXJqTmJmeCtOdGthZTJ3UGtWbExJZ1RjeFUzcUsxbW5qc3NhYWh3eDNhbitlb3BCVTlxaC9UVHhoYkY1K0RvZERLcEpWUm5jaEVCYWpMREFKZjMxa3VBcmkxTy9hU2MzWGlVM0c2bVpZN3JOSE1TejVOQXhyTjJXbURyVG5vRHhJUkhpY01Hd2puREx2eld5RVRXWENmNGdmWEZJTGFwVmNXWG80M1MyQnltUmxkenZla3d2TDhjbDI0NE1sS291cXBDdjhkRVMxbXBnS0pBT3pRd3Jmb1NJbW5qd0w5ZVZlZUFidDVNWDlZaXFYdFBrdklEUUt0Y1F2dHdzV0pLSkplREVkTnAyZmpLSVQxWlNWSjVydlF3Q2l1ZjF3ei9XUkxJRDZseGF1MDB0SHRIdEpqTE5QNVJaT01xbHN6QnJGbzJqZStOd1VLUXN1bys0WnhySGdkNDRGVnl0TUV4YUpzbjZqbFdNM25aeUVjSmJra1dYQkM1bXprZFdEVkl3MjdkSEhKNStNdUo3aGhad3l1VjdmQUd5cEdLWk42bVBVNk5rZmNDSmI0OXlqMVRsNjBwUUorT1V1eHdtME9JWlJtL3hjejI2ODljK0hpT0VlY0xUTk9HOStEYWJ5YnZ1V0Vka2dub0hCbFVzdWdSWlI4MDJKb1M2UnJObmdHREhNcFJDMXprVkFLMVZhOVRhQmpxNFJXWk9POGhuZDNocTVRa2lkUjZPc2RQdWJSRDR2c2RRRCtZODNVbDVaZzI0SUpzd2REa1VIWlJmTnlDazVTc0ljZUFldjdqUFVtRlp6MlR0NlBFZ0xDRmR2bzVyUzFpdlRrK1RJVVYwS3QxRzhJREJtakNLMThKbjJSc3lDQUt4K2xxN0JjenEvaWNXOHdkSFhKTlBOc0lldEwzbzQyMTgyYzFtVGZIaTZraDZCK0ltamoxbW8wdDdOWFlrUmEvV1Y2VUNxOU44YUR2S0ZKemxyN3BlZEw4VFFwSElWWk1BT2VYVjY4M0ZzdGs4VnNFaldSL3pLdzI5RnhOVUtiWUhNQkpRbU42M0hrTGNLc09Fd2JrR0QyNzFhN1hWUXdjRDkxdll0YVQ5eGlZeWtlVmlKUm03ZnlSUmtPVXRSTXlZWEd1UkJKbVowb2ZWUW04Sm02MXJTWVRuaGN5RU80UEg0SGEyUlF4MU1ZZTNkUnFPNlQ3WWgyeWJrM0hyYnFYWkp1UUFWVU5POHVlTldMVmV1ZmRSZnRuS21kYU5RN1hNRE5ZYk5hYzBXRW92QnFnOTRuUWVZbGI1bmtvS1ZERHFyTHJUUmxkZlRDVm1IbXdhYkdlSTdpTXozZ3NoRkd1UnEwSkNHbkpqaGQ1WWxzVCtaMm1Xenpnc3Fod294R1JPSmtTZWVlQStLc1JwMkNKcTZ0UXZJNXpUSHE4aEVoZUpIWVdlcVVYZExNc0U0ZnJjbGdxbENpamRMdUZORXhTdjBEV3N5eW5Sa0Q4bUtkY0VBaTY4M21jeGdENHdZVitKYlNGYW1vbFBKWExvWFNweC9ldlhuNTR1YnErRERUYWMrZjU1b3ErTVk0MTMzb2w3UjhqSjV2TGVUM2hVcWFVRVVzc1pKNk5ybHlUTmZ4Vi9CUThNcklvVXRSNkdDaUUvQ2FSNmxZeS95TytFWUptQWozQzAwcGVGS0g5ZUR3NHZ6dzl1aDJhL2R1ckVzaDZEWnZCKzdLeUYrQjBMSUdtUGdSVDhLS0dDUWJ3Y3M3TG1VVGxzVzVGN1VwNXdpS04yQ1NmVjhuV2p1aE8rVVdtcTlHbTBRRkNiWjRSUkVHZjRuK05WUmtVQjBoWWdTeFk2VVlKVHdjOFRLRlpMa1VlNGhZUkpCbEhpMWdOelZCMDZ2YllkdVYzbXZjWXcyOWdxVnBZM3NZVFg1MmlUbG9ZS2tDWEx4c1FjdTFrZXRVYnB3Y3RGQndoT0JMaEhidEpHRyt3R2pQdnJpb2xpYmovTytrek1oTEducUlaMldPUE9WKzZZcUc4SVFYMVZZTzk1OEEvNElsVWgrdzFIT3lRRFRyVlBLTUdRT0IvVkdZWlp1TDJZb1ZtN3Nqek83YVF3TG9JeXJRc3FlaVBGNkN4Y0pLS0ZJcW4xS0MxblBldXIrK09qdmQyMTNRMDdzM29OQVNsVXRRYkFhUm1pN1AyMnVIbExHMFd0QTFtVk0reXhXdGVxaEdvZHNmNUVlakxOaWw1ZGtrejZZMzMzeWhaQTRCK0dMWDVjbGRLRXhYK2Q2NmJCOWd0WVpjR3Q2WDN3a1JpQnJQSllhb0pxdmFPTVdTdjZOdElUUC9HaTdjQkd4dHk2THJveVpnNEs2cEpiRStabFQ2a2Fqa0xqbkhQdnQrYVVYTVVnZWlucUcvUTl0cVNpcThkZCs3TkhlNHVhR3BmUEZTa24xOU5BWnhlV3MyRFlQdW9La2tQN25HWWo1THg0OThpaFc4Nk1vVklkQWMwT3AvbUxGL3YvWkl6NTZqUjR6Qmd1T0Rya0hYSkdZK0M3OHFIV3pXMlJZVnM2aXpLV1RUdUVjN2szVC9kYUp4ZStWRWhaaTZTN0NaS3NnckJVSy8xMmNmWDcrNlByZHU0MUJwU1ljNVhKZ2JPVERhQ2hVMnVHUjhNRlRHdnRoUlY0WFV2QUdPOFBpRkJLejZmbGk3YVRsODhWMk9zTCtYNlJaZCtBeEQ4V3gyeExmd09NYzhYYzhySjhCU0gzSjkyUi9VcFkyNE9VSE1SakhDWlMybFIwM2lFWVl0UW9BVnhkUXJuaU5MQlNSb01QR0dINE5GeHJHNlU0MjV6T1VLNEp1bkJ3WUxEOWhXYnFBR3hoVVNMRXVWVFFYTllFd0V5dU5iNmJxMTVpVVdydS9yKy9xaVo0bWJ0YzBmbklYUE1xQURPLzVTRmYycFhDTnJyc2JGUHFLaVVnRUZuYTc0d2V2N200TzlkdEVSSDZ4aW54SS9BTm0vKy9aMmZRRzNxSjBVMEUza0tBK0t6Ui9NN2NXSW1NSmFaRmt1a1U4MWN3cVorM3VMZWFHcEFzNHRodlo4NlFrV0R1bVkzOEdPaXdQcUx0VE5UQUtOd0dEeGZadlhNNk9TZHFTU1NtR2R4U2FXNEozQ1V2end3YnZRSzFIRTlBVU5UaC9ZZytoSEZhM3hMOG92U2lIV3E4QTlrWWVhK1NEeTM3R0ZmK0JaKzJmVmVXd3lIQ0ZaSVhWV2l3biswcDhEZEZHSnlkeUdhU2V3UWd2NmwzR3NkSU1yTVlvaCtwK0NPQUM2SXZsTlpaeHNkZUxNWUlETThhTnhneHZTZVlYc2hCUVR1UzE5MkVtTjhtSlN0dzZSM3VXdXEwQVg4a1IvWHlHZUxiandEblIxbDN3eHZyTG1Dbm94WE5rckR5R3dGNlc0b1BGNGtwTTU5cXFCWFpvWGFmRVhyNjlPTDNaenNyZktZOEdldVVNdUdOWVJ2ZVR3RUNEd3E0NU84U0x1RFZIeC82Mlo4alc1VkNMMXF6SzZyWU1Uci9kem1jYkkxRHVjOE1SU3liK29Pd3RsSU9MZDFYRHZvank2REZHVFRwbEpMVy9GNk1haHlWK2xudFMwRGxHeUpqekxuWXZ6SEd0ODB2bjVpRGNBR21UbWpXYWxhQVc4WUNnbTRnU3o3NlRXU2t5cjVFUXRXOFdBLzM5OHU0ZTQ5Wk5ObXpDbDdDbERGbVc1WWpMbllBeFlTdDJzMFc0NEdxTVRaUWxhZ1ZJRldCeWV6bCtLNmNCdWFYZEY3a0J5Zmp6Zi9mejYrY1Z6YnlsaWRTd3F1SGJRU3l1WXVGRXQ0ZmtDMjIrRmhpdEplbnlDTVlhMmNKSTRwU3ZGKzFyazhCMGtFTUNYYmVraHMrMzZhNjhwNVAzcDV2elN2ejZzekxmOE1lajBzcGN6SlMrd3RoZFlnV2p1c1dhaEJ5UU9XTEF4Qys0VXdRcDlpUFlPeXo3aWtDNlNTUU03ZnMzNkRCS05DQ1VOV1MzZ1o2bnFrMVhlZVA5d25sVytGYm5BVFVhVkxTYkErdlVaT1p0SC9icXBhbm1qdVhmSkxha3dWZUUydUtFWDR3bXIzRlV4ZDltSU4waWp0ZHNaQ2ZnZWtRQUhhNFdyQ1lxUngzelVRQnUraUFWRmtKRjFVWjdwcnE5bG82QnBzM2EvNTZ6TzRHaXc0ZWhHM1FvU3RDNERrbFoyNE9oVk8yOXAyendVcFhOZVlrQnVXYUJyS1RhaCt0VTVTM1NlbHRVUGxGcThXcmwyZXJuSzBYWm41T3F3MEV1VXAyMVZyQTEyREsyY3R5ZGkyQzhMeGYyVHpxQzhXMW5RU0tmdGJMTTc3R3ZGVXQ5V2MvSUJxOTE2RU4zYWhva0tyTjFLMWVuK29YZERvOVQzMDFpOVNUZmNkVE5PU0MrU0owM2Yyb1lzUE5hN0Y2b29JdTZrNkNwZ0UyYURxa1dqQXRxTFV6MkFUZTJMYUEwbldBYWhyRTR5bDBhRmRkSnZKRFFiNjFvRWlST1pkSHhUY1R6U3ZYNitrSHlHTEoxWDVEV1J3aDFuLzgyQituYk1NUnA0MXpXQkxQWE5rR0xvT1g2dERMVG1SaDNyblVKY0ZoUUFBcVp2UVRlUFc3RS81aDlLbC84REFPQ2JaMnF3OU4vKy83Skk4R3dOQlFHeWs4ZWtNSU9TZFVla1pQbVlWdnR1ZG9Ibi9tTFkya2NRZmg4QXorVG1OY1hZNEoxbnlRME5zVEU3VDdsakM3RDFHazZhekhHZFQ3a2NEVnpnd0FDeFo4TURXRHdOZVhKdWFDZ29RZUR5QlV0NHB5L0llbEY0NzI2Zi95T3NlNGk2aEdqdytIWlJHdDBYT2pKTmdyYmFsU1Fwbk80UkJxRkhlWTFqNlYyUmNlT1dPU3NrRUlZRmtFL3ZtN0R3VGVGckRQQzVGUUJnUllseWM4YUJZams4Qm1JVGtUU095R3lKaHVWcU1WdzNOczh4a2tnWVk4YmxHZkNsV1dqbklhNGdzcHplM1dveGRDcG50Q3UyOEUwRjBvbzBuNFdESWhsY1d0RjFPcWFIWlJCUEkrMmRQdjFrUTFzYnM4N3RYYW9nNFR6czc5ekh0SVJicHhSb3AxSjh3M0FUY0psbVBhOHZndFZIdXJWVG0yOEtadVl4c3E0VHF4VzdXd2ZxQTBuQTRyU1pCSDl2a1Npd2EwSUZOSTUvZEo2eHRnSHVCZTJZeFlkWFZsemRPS1NBLzI0YlRsZDZVTGFnOUNmU3h0VHlqUG1oTmZ5NHIwdFNsa3dTd1Bwc1pLVnVHaDRMSkZud29GZjVDd2lLSlJvSGdJN0ZTaTBLa3BOZEoraWcvVkFRWW9yRDFvQ2w5YkZPeXdKbnVXYit1U1ZqTEZNa1cvU0toQThMNWdmRnpVc1NDaFNlRXVXNEJSNUt5UUZiV2RxazRCVXJ0UVZZaFVLcjRSVmc2Ry9EODR3NGdQYURWaUhNeXhuN1haQUhQdTE0MWpzUjRsMm9rcUFZS0VlQnBrd3MvRnRmaElBTjhFVEFzUSt3cld0WjI1blc2aVRXSFRDTUovQjF1N2V4dmhBdXh4TDUrd0YwQnZEUys4aW5oOFFaWStxS09kdXIzVERxSDBkSjRFcXczWG9yNEJRUTE5WlRJQ3pIVG9FNVhVNGhpUXlLeWZQaXlwSFlLY3krRlQvcnJ5eUpjNFREeU5qR1ZFZEwyeHlnVnVQTld3UGRyTW5pTDFuUHUyV2NHbVo1UExTaEFtT2d3YldGUHkrUzVWNzJqVmEwU1M4UmtqZzdMckFnMkN4VTU2V0JEV3NybFlobWxEbmNxVGc4UmxzVytzdFRxdEZWNXNCVFNSckZSY3VWVTl5aDZBdStqbytmNWhUSVZIaDQ5SldSV2JJa2lVd3NMSXVUMVBLbTRvNnBCRFVLM1M1L2F3RXIyOWIxazJ2Zm92OEpLMGIxZzhqNkptU2NnMGY3U1cwY21rbzZuQVRvSmpPMWVMbE0xNEVhSlFLNW9NSGhtbFBqMERjTHR3L25aeGd1K0VBenZhb0ZZVHVUM095bHBsamZqSVluZzlTSkJYQ1JCdElhYkpQTk04N2JYL1M4RHgzNlBMalNxcWVkVXF3T3Axd0ZxaXo1VTZ2VVZQWFBBaldraWt3RlFWaVpJOUVxWXlvZUcwNll0VU90WWp5MnlVcStpUWY2K3I3K0gzbFVkbjRtNXFhOWQyQlF4aGk4N0xvdkR0LzM1NkNvYU9nWW1GallNRGdDQnhjUG40Q1FpRmlZY0JKU0VXVGtGSlFpcVVTSnBoWWpsa2FjZUZvSmpiWXdNSDZqbE0zbHBQL0dGS25HU0pNdWc1VkFKRk9vTkRxRHlXSnp1RHkrUUNpU2tKU1NscEdWazFkUVZGSldBUU5obnZtVzJtNk9ZUU1lR1BUU2tBVTJPMkdMengxd01EQys4RGdrYWhwODdSdjNmZWxoS05nZGxBRkRSc3d3eXh6ejJCUXNlSWh3d2cwdmZJY2Nkc3h4Unh6VjU3ejNibmgwNGd1RUlyRkVLb01SRk1NSmtwSXJsQ3ExUnF2VEc0ek83dHg3YUEzY2syY3ZYdnZGbG5jZkpnS3hSMnJJRkNxTnptQjJMRGFIMitmeEJXMUVLTUlzSVNrbExTTXJKNitncUtTc0FxcXFxV3RvYW1ucjZPcnBHeGdhR1p1WW1wbGJXRnBaMjlqYTJUczRPamtqRElReUxxVFN4cklkMS9QVktWZUhYeGZVb1Q2MGVpb2FJSGd0UkpwdVRhTkU4cWZHOXZaYjQvUTdBMjhmVUFTeGFaS2NCOVRSUDM2Q2ozL0FVMnd2aGNHaGhUWTY2R0l3Q2xLOS9IbWJqWHZmN09zeGFvUmRsL1BhR1F3QS83N1pINERIZzkrUFU5bzVEaHJ1MjRwZzlUTXh1Ty9qR1VldlRiYUxhL0lqMTZlZTMyTjcxZlBPRHJySDdFTkdybjgwR1hRQlBmUnBvSUp1WEZJZ1lXUnByS0k2R3FOSUVnOU1aVmJ3bVI0TGRlR3VVNGE5VGpONzdVdHN4cDF0YjRGZDhXSmg5azdZMm5URXdOM0RuN1QxOG4yWUd5YjkvYlhvbUlCMnFFRDNaRTNjQW5SbVhVS1BkbUJDMjU5aDJPRUZHN3BCakVDNGgyQ1B0VHA2NGhaSUJ4OGhVZ2ZFc1d1ZFhNalhGeThNSWRoRXBCY21JeFY2akxUck1HNWhyMzVlNjNxK2Z0VVp1eWVkT25pOVEzaXExQkNLMldiSnJpeTJ0djFaVHNhM3Y3eCtCV3RtNzNYZlpieWJGand0SG55d0hqdEtjb0ZLMXNNQW95VFNYZ3REMnoxaGVGd3pzK0tMTnNKZHV6akpVYkt5R1dDOE5aRTVqbms0QzRobkhJbVUya21kbndWeG1IMUlpY3RseVoyNTlibFJsNFJrMk1pdURBWXlLcnV1cXM5TmRpT1N5Y21PMkZtWG05elJXbFZiZ1pYWnJIY0RBMm44eERCanA0V3I2czR0cmlRa3d4VlM0alpabUdGQkVwSmhxenJxTnFNYk52L3B2TU9sTStpQTdCUEFsZnlpZ3lDNHRQZ0FPQVRYbHRhREFHSS9KQVNCMkllOXczUVVSQTh6SG5LZFlqOEZBd0FCS3BmUUdtQjlHUWFjQnlQQmdVRFJBZXRBRVpkcUsxRkhUcUc5QUVnNnoydUNSZnZ0LzJ6Zk9zeEVRVWJzQ2ZEZHZBT2tKUWVoa2ptNTFnaz0iKSBmb3JtYXQoIndvZmYyIik7CiAgZm9udC13ZWlnaHQ6IG5vcm1hbDsKICBmb250LXN0eWxlOiBub3JtYWw7Cn0KQGZvbnQtZmFjZSB7CiAgZm9udC1mYW1pbHk6ICdLYVRlWF9NYXRoJzsKICBzcmM6IHVybCgiZGF0YTpmb250L3dvZmYyO2Jhc2U2NCxkMDlHTWdBQkFBQUFBRk1VQUE0QUFBQUFwYXdBQUZLNkFBRUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQm1BQWdVd0lhQW1YRnhFSUNvS2JDSUh4WlFFMkFpUURnemdMZ1Y0QUJDQUZpRzRIZzJVTWdSVWJNWThsYkZ4eGRqdUFSR1Z4bmtZRzhqZ2dtUTlOL1AvM0JBNWtETkRHcjArdEVnaThqS25xT0U5RHhDVWFxcmxtZUd1b3kwWVlIWVByYmlBL2p1UFVidlk3QWpyOFUydjRWcmRtd3hUMldOZTB1Y1NJd08zNEU5OXNLeEVpUk5pdzhCclcycVZmK21scE9GWmk4ZWJKYmxmanhTUjBBNjlUa2Y1UFV5T1ovYy9BdHBFL3ljazdRTlBaU1V6dWNybUlOZDQwdFZ6U2V0b20zdFNTSmsxYXAyMnFRbHQwMUNsZW9FaVpZV01EaGs0Y2VRYStEU2JPWU9Mb00yVy81eWZuK3Q2TUlBZ1kxaFJaMlMvb0lzWTBoclM2S2FYNkx0ZFhYMUdIV1A2cU9QaUtzZCsrdlcrS2lCM3FDZEpOSjBWQ3BCWnFKZk8vT2UxZjJrcm43NjRsdC9aTWNpYUFmUzJubkxhREpGdVBBSkp0c2szamxEUFVEQkhJMFdNNCszK2xhb3J6SFdobkVIQk1aVWpsVjNZVkVpVEtLVFNOTzF5THM2dmZ1d1Z4a0NDYkNFY0NETEtvSVJTcEVEL0VBODdPUlcxaTRWMjlJQTlsSlBVQWdQai8rdUR2ZjV4eHQvWjhQN1crNnRDOTd5bE94MjFNRkpwcHlYWWltOXN6c1IxQ2hWQ1JncS9CUStvMzZTRmF6ZnovZHdTd0E0UWQ0SlpJL2ZhcW5OR3hsdXZMc0FqVCs3eGpzcnUwQVFBRUJoZUR3di9qMmljSnVlV1ltNzRDelJSb1lVcjVWSVNrbVBrazlBb2RwMDZtUE9RbVJpNkdXOGdKbklESDY5UmViTFRWYk5icHQ5L0cxdTMvMTM3bjFYc3I0dkdIQm9SRWlNc2ltalJxZlZObjZnb3ErWHNvU0NnclBHK2U4V2dxWExPQlR2UUp0SGNmcGo1RU50aUgyTlQvYjZyNTdYMUR5b1FvQjhweE4xT0ZZMjRwcnR6Wm5VK2ZPbGN1Z2Z2ZVlHYnVRMWk4QVNoZ3NMU29BU1BBdFVDQTBuSkJpUVlJUUtZZ3lpbEdFVkFFSks0RlVITElYSWYwUTg1bGlLSG9mbEgrcmsyNWFIN1JWZjlYSWZyNy9mb0NXYlI2TFJwYzV2eVBtYzBjYzYyNUFLb3JZL0lMT21UbGJhdjdmc1ZDSVFUVUdEV3oxclgzdmNJS3d6eEpjRVVBZ3NLLzBRQkFxNjhiQTdEMlZZTTNSUVJTdnZ0UmdMdEl0MjdESnladzFqMEJGZVV2VUQ2b3dyNCtWUVpzMk5NaEFNOVlCb0RTME9LQkFZdC9TalJ0SmdIL0pZeVVibkY4d2t4VnNBUjI2b0JQV0VhZGRhVjZ6dU5XMVBFLy9QdlhxUlZPNkpTSkhScmdBeTl0U1VpckE2cGZUSGJLTmFkK1BUNlk3NVhUeWZoUy90SWZ2NmtFOHdBQzJacFF5SjVSVHdUUWViYi9senpHQTZDVHZ4dGlSMXJjSUdXSE45dVJKZnJycWNQU3ovclY5RGVxZ05VNFNkS3pmMkI2Zm9iKzlYTWtlNG55bmRjcXExL2c2UCt6eUtnV05uZmVBNXRBMjhZRElKekpvUUVSc2E0Yk1MVVVYeGQrbzhyblFiRU5UUmFUdDEraU4rL0t4aDBMVDlGRHZwMG96a3dYdjVNcTNjNStpUkxWRjNnaFdwS1N4UEExUzJ1Vm53Ky9nTE55a01UVnhNMW9lY2hmbnRNejRrQjVaUm13RUZlNjdnMDZhaUtvNkplQzF1c0k3UUc5QitkOUF0VUdpdWJOYTFucFdkQktvUjhiY1hBZE85Rk5mU291bGRlWi9ZYXlKb2NCVmQ3RS9OcjFiMU8wQkVpUTE5akorU1dVKzJSdm1qQzZQQUJHQi9RbXd5TW13Q2Q3OEd5dDFzK0lRMCtIdzFCMnp1UzErRWZoT2NyM2MwaE55YzdqMC9GWURzd216a3pDSlk4M0dFcnhEM0NDRWlndE5RSVZPWEJsa1pxV1lDbndBYm9MOFJJQ1VSeGRnQUpqTGN2aEpVU1lLbVJROWd3QytuVDRnZC9vZDlFNzgrbVNsemxHVnR6ck9BSGVTWU9Kb0NwSDBGTUp2blhjUXdMaEFyYkVqTjQ3WTZhd29ESkgveWhVUldxbC9jb3FCNllKQTNZNEM1KzlqNVg2SGFjelZYMXpPelFuZ3FOdjRyTjNqNGp6bzh0MnExWldNT3VEYVY0VWdGckFuTW1uTkk1UWhTY3p6Z0dkbU5ERlNSVy9mS2dJNVdkUmMyTHFyZEkvRzNBa2dIMFRmMmNuQk0wd2tpK3hTTFFxaVdITENtQ3pNVWhBZlVCbXJ5OEdObTRzNE81OXhPeEJFM2dQY0E2dVFSblVmbHYyTnJFVHAwTTVXRmhYa3RkMHAxT0RuUlZzbHhzY3JJSVdUUWcxQUpHMmhGaGJRYUt0SWRVMmtHbGJ5RFVaZHBxU0xWVFl2MWc4MHE0YVB0alZ3SXMzNkVLVmZtU2tZeWJiczRKQm5BQnBBTEMyQktLdG9OVFdVR2tib05vV21DWUQxeFNUVXFnRUhWU0l6OHhZcWtZV29WRkVjQXNpdGN2TjBVZzhpVTl0UzRMU3JId3dYT3QzYlNTcm5DanVmRHJDeVFnMVJpY2twcFcyNDBtOGxud01wOEFwVDVGS1U3ZE50akxReDVKT2p3TksvZVZYdWVEOEJmbDdUK2UrN2RhMEhWOXF5RktkWmliSGdiWi9HMnJJc1QzZU5lU3BkNEswcUpoYllNV1lQS3ZXSEVEcTNIV2ZsSi83UHJOZWNlNUd4alB4TU9UTmpvOWNhaWdTY0VMRm0xTWRIb011cjRybVVsZUtpQUdscUZveExwUUhRa2NjYlBKenlYUDhpZSsyNzl3aVhiWXZVaGdHTTZ4VUpJYTRUYlBYMU5aRWVTZ1M0LzFoc1BMUzBLbGZ1dTg3MGp1aFBBd3R5aGJ2VFg0eXF0aTAzM1VHcXlrNFZselJZODA2QjJ6QU02a0w0VS9RMFUrTDBqbFF4U2taZG5naWk0R3VTSjJaTGloN3dPblZHRjFnaUZ1M20rbXEzQW1zQjBsVDFJY0JvK29xN29NTzZ2RmdKYmJxekR2dzhpY1JOT29SZlFRT2VMSURGckVxMjRuMTRKRDM1R0ROaUpDaVEza1B2YWlYL21pbTZ6UzF3QjEyK2phcVMyM1Y3T2h5cHFleDFlc2FoS0U4MlV0dmttTEtMV3lSVWhieHJ1c3ZqNjBVcHhsUWF0SFE0WFluZzJMMHdoRlRKdy91U256RDdyQ3lnd2k5OFUvRXJBT2lCempFdkdobnU4NkhQT2FUbWRURnd3K0Ixb3Q1dStpdUdGNnpmc0N4ZmFvQk5rVnpKK1hKVHJkbE1EbGtCZlFkSVNmbURNd3FyR2dFM01LSnFkVzBTQTBxTldhUGpERDZtakhMQWFCamJFNURNTitMd0JZMG8wVXkxdFdXTU1GeUx3SmIwWXhXRTJ0cWE1aGd2UmVCYldoR200bTF0UzFNc04yTHdIWTBvOTNFZXRvZUp0anZSV0FIbXRGaFlpM3RDQk1jOXlLd0U4MG1Ua2taYVRxYmoxRHZYZWVzQVJjS25Ja1J1QXhYR09DcXV3TFgzUlY1TXpFQ3QrSHFGKzY2cTNEZlhZV0g3aW84OXRUZ3Fic0d6OTAxZU9tdTdmSEtuNFpCNys4bk01eDdzbk4wajY4UGR5dDFtZzBELzRFWnVmZHV3L00vN1B3VmlNTVR1Y0hUM1lnYlNIN2F3RWhpOEJLVXlWSUhFUXZLblFUQndXV1YwVEJtak5YbEJRNERrRHBOamFZbUJ1TkFYYW9xZE1NTkprUE5QNDRHZ3lSUmFabzdrRVV1aVJkbkd4Q01rUG5ZS1J5ME1GYlExbGkzMUpJOVp1MXR3SEVyTDFHQlJ2R21zdHFpcFN0cnZDbFlBcDZVaThWenE2ekNTazRXSzZQTHZEQWk0T2Z6TGJ3Y1YzNmxvNkdsb3M0bXBJbHdYWEdpMFZqanJscUE0WXZiMGtTR1JLWXRCZlBTUzFpRllnNWlxQ2E4UkMrTkxlRG54K0RaS201Q3NSVXJheXhxUmJLNDNGUk5McDZabGNFa00wa1JKdXpmalB4U3FHTm9tVkV2R3BXemp6emFKNHpEcE8xT0lnMUx4bEl4Z0FHQk1ZMmZwaDhqZVMvdkZUM2VwQktwRElhcXNTNnhSRmd2S01JeURubU8xSEdlWkIxZWFUT3hjdm03d21rTTYxQ0ZaMkNuNWM1NHZHUUNjRFg4bk9aTnRJU3NHWWFyQUtDYnJBU0Z0Q24xQ1Q3RGNGTkNkQTJHUEhhT25EemRDZTlxS3IrcGMvSjBzY1EwcXVFQk1sUW93cnBiM080WnFsTkkzQVNER1J0VTdRSWpHVVM1NkZ4ODc4V21mTHAwMTJsQ2gxdUw2MzN1d1B0cmlTeWE1UGR1T2dkQXpFOStYb2t3M0w2Z3hQeVJVRFlSVnlMZVJ0U1lFNzlDdGZUY1J4VGh1bDFUYnJWR0lBeWIvS0w0S1l4NWpVUENJWEJVNkhmS1NJWG0rL3hhU3I0SVZid1FKczl2VUsyOWhhZ2xKZUdxR2tOcWlaNk9RSDNSNy9JMnd6WXlwMk1NN0czNUhVTXVjRFA5bUx1UUhxSnlyaDhnZ0hBcUVRWmQ4SThTSG0va3VKVWpPU09tQUlJKzFIMk1hTzR2dldYNHlsMTFIYU41VFhLV1BqVDgyQ2xTVzRzQjYxNmo4aEZoNW94ei8xRlNKMVVha0FSYk9sL3kyY05VNDVEdjVTNC96aXd2dm8zNFRvZWlNMkU0Zys2RUtwdHIyZkNnNklnSjEvV1Q5UU9SN3R1aUdxV0tWTU9pbmJPbnVLNkFTd0RZdS9jMHQ5VTE5UnJQSldJMEJxY3BNU3dyelV6WW9kOTlaekJ1ZXozeTZUSmxhUHlRS0VVRWZlS3JibFRFcUxQR1hVTjd1VTVQdUNiQW03N3Q0S1dnUG8xRFFUaUo4SnZFNzc3THU2Q1BDNTZvRDBYdVFsTUpyd1NHWitBVWVpcXRvaHN5dVJPZGsyVUw1cWdjYmJtVzBjTUI0cHNKZXZqVXZxSkQ0ak9LZkExUUExL0E2Mk9NOUxzNG9kZ1doVnJiZVUyZHlMWDhZdDhWSFVMUHVINVFTa3ljK3NoQmpNWDJFb1l2bnVGb0lCNkorYzRwVHhvZkZEYkY2eTUzeitMdkxSWFRxN1BVSFJtRnVwTDh4QW5BTE9BaS9ON2FnSW8zV2NDM0ZoVjVGRXIveDdJOUFkZXFVY055VEZ6Q2EzNlVMekpDWmMvcXJwOUovQmJvMzBPMXZBeHdqLzdsdWlxRk9IRzl0TGZwQ3NlbmZDcFRuTHVJdndwZEM4bUs0eXN3eldhb3RIZDhFRjBKT3BWTVRnalk1T1oxOVQ3UkVtUjZMTStkcHRhMnRxVTBBR2lVWUE1U09WQ0krRlJaREQyckhKSGk0VzgvSm1rL1c3L1JUQkdwZ003ZGhST3B5N1lJOTRMbEgyRzZDY2RIRGJhcnhwdTJ4L25uQkhQdndhSmZleFJqaUxsRDVpaEM3bkFoL0xwYzVKcUdFZkdJdXBFVnRXQnlKNkoxVzk1eklVTWkxZTlrMkQxRTFaY0g4RHNNWXRQVFoyZkxDcmMyQ251YlNmWXp6RmNHU3lSbHdqZCtvaklFQ1RvenpUK1Q4WGZCWGRrYUsvZmVxRUl2UjhLSXg5OUl6cHBYbkYzT2wzWk13d1JaOTBtUEFxUkpxZldLOHgvN2ZHbEZINWJERy95QTlrakx2emh5WTh0RkI1ZFg4cHFaK0FuSHZ3Ry9EeU9QZnBaOS9UV1MzbVQ2aTJnc0hsQjBmWi9LaklrcEwrcTV5eGVPeFRKMVNLN0svZjVQZkh2TnZuVVhvbExJai9RTVBicGNTMTNEUE01U2psQkNQVVpvTE5WL1V5aTRXcXNSMjJXRUU0UWcwdFRPQ0RNcWg2RTlKOEp3L0NyRUtHd092emlkNVhrVDBkU3gwMVF1dXhpcy94WU96clA5dEhydDNHQnV3NXdRV3dwYll4RkdGY0JIVFZKQ2pmVEtGaGQzVU9ZYTY2MnRNd29sUHU4ZFJReFdkMTVERE1ieEU1K09kRllyZHhRZ1huYVkwSmFKQWFLUWJWUTgyMk1oWndPczNXWUhwUnVNVUxlQXZLeXppd3pUQlYzZGMzTDhNakVpL0wzd0VBZkhUL3UyM0g3UFMzYTBsNU5zWmlIempXTXE1MVU1ZnhsUHZnU2JJZUJlVmUwMjc5cmdPMWxscmhTWVV5VDU3S0thMVc2QVllbjFoSkkzd0RqVmVBTVZKVmFCZ01mNWt4Rm8zdWQyRko3RXBjZ2M4UkRYN2ErNXV2dG55MEZSYWZjUFljRzdHTHVqS2JHSFY1VDltTmxreVNZVkRDNjNVQjYzUEVrTVZ5alRoOCtrZkhSaWszN3NUVE41L1ZMTGhGRVBmVGpVMTdCMCt1Z1E0MlF3Qm1iOUxJQy9BVURicm5yZC9jeklCNFlxODVaOVBGZXdlTzgrbjdZYUYxMXFRcC94anZsekRqMzhlM3UyaTMxR2hQcC9uRC82V2VCV25MU1l6dkJ0Z1ZPaGY3MjlORmNNV2NHNHV6SGhMTXNGQ3dRTDJpQnFKY2ZMd2hoS20zRjN3ZEpCZWk5Q0dWOW52QURHbGFaUlp5OGFiN3h5Qi9HSG1WYm9BQUExUXZNWUsya2Qwc0tMdllnN1h1V2FEMk1ucFovaHNCTmVwQUlwSEZvOENMRExjOVN5ZG4yQnN2ZzZPL2NObmM4Zk5DK1RON3ltdjlONzA2TmozLzI3Zm1JTm5wL2dOdTdZY3lZMTA2bTJvV0RTb1FUTUx1TEljVzRGbmVkL1dGTk1rOFJ2V1d5NHJFQ28rWlJxZ0hFN285cUdLR1VDaEp3OUFhUTBuU3Q0YXF5MTNuajFzZDhJdzJjdG9WQzVVMVI5dUNNdjUzR3BaRHRoek1La0Z4RzdKaDJkRVU3a1c0MmdOQ25MSzJQOVVBbXFlWmhzSGhBaC9ZelhxalpkdjlYQUlEeEp1QjBqaGFUb2JPMDlBV2wzUFJvOWV2QWIvakpRSlZwVWIrQ0xOVHorRTlhZ2RyNkkrVVdQZ0w3T1dGcmF4VzRUbS9HcGxlNnJ4RllmbGlUSENidExBZURTTm5QRmNidGJ6U3dncE5BYTV3Y3Y1ZGlaMGVJMWpaMUN6ajdPeFdRZXhDYll6M2dJYzUxQm5SbktoTExWbXUwT0lMeU5jWUZxRzNWcldOdGg5a3hHaEx6d2tjZkdTNVRRNERVTDduWVNiM216a1lVek01aFF1dTZ4dHJlcEhlbUFOU1NNbHdLcWxhcHZ4K1B3Mk9keEdnbVYrZENBR3pLeVltK0loOGp6bmVLTnMyQmVVamFxNk9VQTFNZllUdGhmcGtHQi85cmN5VE14dE5DKytYaUJVS2g3dFhtRW0wSGFVQ3BzcWJ5MXBoMktLY1dEdXIzR05XUnk1bGk5dGlEaHNkREh2aGhCNHNDbjQxdnQrQjFIakRMNUdmTE5TYk53Mlh3b00wODVERlY3ODdLSjJQR09yeFByVXBMbnE2OGlNdWNRWVppZXlCQjJ0Qk9PaFR3SmpBNGo2NGtlVWtXbzRxQnN1YmVZYWtsTWVOMndsNlhkMUpCOERXczNTNG5BK2FENnk1YzhsK1B3bENIa0tIVzFiSmZlc1lYUCt0bkI4M0NiemZmV2lmVW01TE5NUmpYTURIdTJlTG8yNys2c2lMR2VKUStHTXNjQW9ING9WQ2lhVFhhLzJRWFJzRkZ6STBDSFZLaU4yemNXeXlPbVZwYVNhZE9aV0dSdEtxUFROSzVrRzVtVWtCdE10UFpzaWkyVG1QaFFJUktDWXMxM0tpTzlhWWJiUTFFN0s4QkpUM3VrOVlBZDF4cHU1QWt2UFlkZVVMbVVxeTYreHJ6cmQ4UFduSEM2VlB4S3V6Q3BySjhDOU1nQVpxYjZtRVp6SUM1RUR1bmg4aEVvSHh0eVVoUUNZTDJxR3luVUdJN2h2TElEWWpHMnpjMTZjY0p2c0hYSnZVQU94bnlHMHFab0xoT3ExQmJHOXNOUlBsc21vL3M5RVJDTU5URWtreVZzRWxXVkJ2U1RzN0JLWEVQZ1ZHd2s4SEg2SnZLUUlOdWtpS2NRcjlsOEZBQzdCRXlDQ2UxWEJXSWxwUUNRVVRyTzVzM3htWmYrdlNYeGV4bkNGbzViUGlXZWgvSmYrbnFqWlpaQkhIY0RyR2tya3NLYTljVEhXUnRXWTJ5a003aWlGRHhSem56TWY4ZEhPZExST3JTeVR0Q2J0cVVIUVlUTGJxOG95MjhsNTlQdmpoMisxMGxGemc4U2ZONlNsVU1qbXdONU5zYkRwZVdaUkNLZHRNZTJUNnIyWlM4eWpHSXJTQ0Juazhxc2tjUThDNUlwNHk4UlFwVUdGeHlneXEyRmVEWDJqS3ExczM3bXJJekJ4SWJ6T0gxYkFaTVgrN3Rsb1ZWeWN3WjZBc29Kc3kxVVo0WWdSYTBxUDkrSkdhNmw4YUp1YVlqRWYvSVRaWDBOWTdWa2dvWTJQYkZ0VExQQ3FXNFdPSVA0cFpLK2VscjJid3ZWYnpacHJjWlBNNmRIUUs4RG9SSkJNaXp0M1l3cUhxYnAra3lQT3NyT3BNN2F4cEU0QW9ic3BHYUxPNnFrUnpsNm5qR0ZIdU9XRWJOWHhQakhRZDNJSVI2OFUrVTh4LzhYQjZtK2Rack9Ed2g4OC9sNkpaekVDVFhSa1REL2JpbnRQWHBwYk1uWmtEb2k4b2FPQ2NJTk1tL2pGQzd2S3FHV3RsYkFINmIvalNFS01jYjFVbndjUTVIL05aT2o3TTBZN0dJTTFNc3UzaUk3bjNMNjByeVRWdGpHbjVGTk9KQlpVb0VIQUtmTndzdkFSd1lFRjhPeW51NHV1TlRuN2hSSERienE2ODQ0SjFZcTFiNkNZbldPaTZERkhCcERXa3BuQ2loMFJqeEwxcHBZQVhrL3d2WWQzTzZWaWdIWXM2cDI5RmxKYXRDNFhIYzVNYWN5dEx0MG9vek9LakI4akV0Z09JMDRQYVA3MnJ4ZXo4OTRRcmNSS1Q4RnBzalMxYXRwUFY5VERYY1FrNnpXc2F3R3NvQXdyTVRZQmxLcVN5NW9lajl2Yk95ZTFqekNkNDAxNGNGTVZkRGhlYjZtcFNiRFZCZEVoV3k5OEFwTlo5MEsrVXVCc3QzSXNlL1BlYUszVmJGQ0NUTzRnc1lDdEoySzQwZHhYMGFRWTVIaFRsTk5UNTVyT0dHMVFrdDVqVmNqeEoxOHVBakpzTlJOSU9jNmN5NmZCUitLTVVISGNCeGw0Y3FRc0VhWlZmVktaak5Jb1FaVmJJckIzalEzV1lHVnlZalZJOFBDVzVqUVZkSVl0TlZ6czI1Nzh3OVhaMzJheXFtUUZqOWVpSnR4bVl2VHNIYWRRRHFMQldkTDQ3UStvQU5JVkNOSTRmYWczMkIwWWdhbk1seHlqbEVLQlNXZjE5cEJWYmFMQUhiaEtUZkhiS2ptTkxNQmNsNWhYcFVDZHRKbW1WZlZZemdoNVp0K2NHMnhjSnZUVDRwWjlXczR6VlZyV0FNRnQwVk1pVldvL0VXc25mMFRwdzF6RTBLNDVxYUFyMGxvWllYNWdWRFhJc3p5WklMc3c4Y3hkc1F5eWUxc1VVdG9nUWNUaEhCejFFN1lSVVRPbUZwMUdGb0JQa3FPb2lMaW9TbStZdnJuclk1amhMUjgxRzBMMkFFRVNKcEFTdWJNVFRwN1EzSkdkallyVzhwSVZPNm1TWDZjcFlRbUdKMjE3SlN4b3hPM1BWYmRwZXZlb2tybEdzUGxoRWZqc2tVOTkzUmtDaHZvb0FuYkN2WklsQlZuWmRKUXZSMld6aXJ1YWNTZ1JSSkR4d3dZNllBVVJZV01CaDJmNnNnTkMxMUNXTE9vbnFFZXZxZjU3TEthU1NSNWQxWTN1cFpRcDl4SmM2NTlCT21yUjRxbVNmT3FhaGtqRHZPTkpNRWFKazRoLzJDc29NWnBKSG1LRjNJV0VSOHBCU25QYWFjTkFKOE04Q2dBL0RoVVZSQVFEaTY1cy9oZVJPdXNVRDJzTmQ3NktiS2Y4RzY2YUlHOXFQWHo3ajZMa2NUeDBtZ1hyODB5S21uWVQ5Q3lwYnFBamhlcVk0STM3ZnNUWHpCYU1sNk5aMW5ZSGRvR2lnb0o0M0UvYk4wTTdFYldnTnNubDVGck15blhBbmZ3Unp5ZVQrRnZ0SFJyTm93Q2E0eUI2U2IyajlFSjBqVVNZeW5qN2sxMTh1Um1nT2R1RW5aWm5yTEdxSVUvOFlFWkdHSWltM09zMFpXUlhuckwrREZPcEkyTVZ5dldlTTZ5aFNOckJjVTVDTW9lTHhIcUdTcHZVVjFteFNWSzJtelhlc2dlZ3VVdmJqZGtnQkJLNmhqdTBYQk1EcExoYUg1N2swUGJFTUl1WGlaV01xVktsVEJZeE5FUGsrSGRqaStVWC9QMCtsUE1vRjZmZnBoUjFjejdBaUlUSGlCMDl4bnY5TVl3MDZ0Y25hb1pxa2tBa0lSTHBUbGFXdExLMUZIY2ZNN251amhkaXJmMHpqQUlJL3J6bkE1eTh5WGdpNXRZdEFMQ3gza0JodDhQM2U4cy9ReDNkb1praTVyaU1YcVh5eEhiK0ZBSWROMmFiVzRqVWw5R3FKTklrSzJFMFRUSy9xMjB4Um82VTU5VWhJZFo1eDQzUkJPS2JGSzJpamNkR2hSMXExRThubnovYThEbmpONjBzOXV3MEkrbGYrcmVWaVEwT3RzL2VxQVhZQkNIRDA0U0ZZa25tMGZLV085UjR2S2dacGpzeDdOa1g3MmF6R281bTFBVUg2dkl4cEZPSzFFNmVjcXV6d3ZYMEhwTXA3YUZKZUlEcHhXbWRwdW1heC9GM0tXUkhYSkFGQ1ZVbkpmSWJRbGs4M1l3Uk8zR2JSYU9OOGkxNENrK3p1cVJnMlNmY0N0QUtXendYZEx6aGdmT2FwckdIUTlTR1RXWWIrZWxhcWRLS0xUYzliTEJQeGtHd3I4dzMzMUVxTGdzU1ZLVmgrN2FtWVdUZFFIQ3VWUXo3U1IwK2o4MVc3WVF5WXVNKzdTY3pUbmFseDFLak5jL1pnSmc4clRsTTZlci9pdElVaTJtRzFIWFVxbmNFbXpDNmh2ZWtzcFVJSGR5eE5vYXZpcXFQcTlnVWlDOEFQSG01UnprVnJIUFRxbk9lM1RMdlBBWFFybDlveHdJS05XL3pWV1h3WE9sNUl3MzZzOFJDM2hFeWM0Y1YxZmpndFdEMXpFQzRrRVM5UXpjU2dlaGhCNnR5cExRK0MvT1Jyc2kxSDB0MCswN1VEV2Racld5Q25yVDhCMlRsUXIxY0dUYkpDUmR2V2toeGN6RWhQZndZcVJqblpwSng2bitJb05nS2xJN29YblhxSC9HVUJxbUpTYkk2ODhqQUZCMVgyMXlGMFBhcmtsU3k2UDFQSHFTQS9jYzFlcThlWVRCR3ZmVTJJQUVMZHJhSTVRVSt5YWxXNTFubXRFNmtpUnR2eWlZOU1wbERJdzVoMk96dU5TQkZyc2hJOWdTNHF6ajhvd2I1K2RSak5CMXFwVkpKbGpYbytQbG1jR21BSTZHOXdJNG5ad2xEN1drUjdqb1dQajFJNmdQR0pWdk5HRW9vRGlxMElLWFNKa00rbVU2QUNmdXMxcEtQajdCZVd4L2lJdEZtTkxRNUwrUVpTTnV4cmZpVWNKOHpFeDdISGs0Mkx1c3JXU2dqRHNrNWhPajdleGtSMzdMc2wycVo3Tk5JNitQTWtrY05GK01GdDk0NmtZeEtzNUhPU1RtYUpOL2pFSWx3eFl3aW9oVzVPdGVvaUtaMk9sVFZpdXhCYU9nblJ4Qkh4anBCWVdjVHBTQzcxRlpzN2R0QlRjNTRIczFUYmZaTURjWG13SktaTyszelZpUHRqTC9yZEpqSEtNclFvbEtRdGpSOFRSd2xibThUYmtWUTRsSktsV3hnNDhIaWN0WFZJMlpxTUJwNlhORnJYZnZBRnFySC8vTG1FQUYwYlY4eWZtaml5Q1RwdFhWcWtaelAyeDFGQlIzbUdwZWxSTi9mN3IvaFcvTCtFdjdrMTNISDNRYjdPSFJBb2Fkek5RK3FkZStxVGErL1VxVVVFMDBwcnJqaDlNTGUwNnQvaTFaVHpUeHdhbjc2SXNicU1PR1pLb3g4WTFQVDI5T3graWNLYnVKbUp5ZUlvSTZubENRSGxoRHFHRVM2U3BGb2dwcXNiTnY2Mk9KT2RNY0VhdWYxTWVxOW9wNFhXeGVKYWh0R0hYOUg5cDQwRmZXRGZ4NjhRcEh3a1g1aTMyK0x2YjlNUjBHajRCTmNrZDJtOWk2d040OE45QmZmZW1HVmRXcXNWTnpVZUhSVCswODlVcHpoVWtRc042N0ZhNS9oWjhjYmxzeXlSVVc4SjZ2Q2RzQjBLSHpTTXI4eGRJUEhqbC9DV1NTTE13RVp4eTNHbXJsb04wSnh2ckQ4eTNGenFqdkkwLytZRi9CVit0dDVkN21HU2Fxcis4VE55c3MxaFJwTFBXYXd5NnF1UEpHaXlORVBENmNBaCtGem5EZnlXa2JCSHl0bTNkZHBLcHBLQm1jQTZlUTB6SWVpZ1ZxQXJYcEJmU3hkSlpaNkxRS1JWWTNSVWphVkw3Nm9OWE5KM1d1QXh5RE0xNnppNDhTazFTUzVrY3NkaE9DdXgzVDdEY2JWbloxeFYzMElFVVZkZ080eDZNM1c0cjZ2alJuM0RnMmVYajRHZ2xDS0hYZElXL1FuendFRFZpemEwZW9oTU9TVVEzS2lwank3eDZlOU1UK0V3NE9QSm1UbjVCTTFjOXM4K3IvbXA3YlFwMS9JOGRhYVBYZnZWWlFrV3RYcEZoVDVEcThVNThWV29UNW9CSUU5R3pxMGtucjNualU3S1hyRDU2ek9mb2ZXT0NqSk9WVUViWTJQYkwxOXM3UzFYQ0xUYVIzL0J2dlU5RUxDdW40a3p1ay9GVUhIaFZ0ZWtKZVZxeitocDNQUThmUytybFNESFdENTFJdVpLaGJQOERwL05WUWdqWFJ1alArRFhLZVpSSEtxd1YxMmdZUUU2TFkyQXdWckdmS21DQ1owMDZDZ0h3OTZSdWtmNExCSDNNMUFxWUNpUjI4TXBoRGQ5aEJFQXlObFkyM0FHM2c0b1pkT1dTMzBJVkNGZmJHT0ZKZXVKSDQvaXh3YjNKUzNLRzluUkJab29EN2xSQ2xIdzc0MmY0YW93NktzVStVVkpKSGIvMndKMHYrMzVDSjRoOHpqallBamZJUnhRaFVGZ0oyVlNHYWlwMFdxWHVQclJQb1VFNHFKd0dvcVFrNFpuVVViRlAycC9CT0N0aWtQR0JHTzRiYit2a1lEZVg0Zjl1L2ZWUXlYMTAzTDdHVHBYMlpGSjVyMTRjMVRUWDBoRjNEVmRVL2xDdzBxcUkwa2tFUVdMKzhYdlJHYXROMlY0bWhyRHkrS24zd3kvVnpOSDVDZlRWbno3VlRTdVRrVXM3NXM2OUcyNm5MMjFqMU5DVk80VDU1NFpQTm91OXVuZ0N1Ry8vWHh2b25YQVZPRjZNdkJBVGNRbzhRU21rcDlkRWJwV1U2Y2NQMWZac3JkdHZCR1EzZ2hFVFZYaVo1R1U4OUdQaVFTUlo4cWEwUUNMMnBmQU9lY2FLOHZoYW96UHlNQytxVWxMYXRXL0syWXVoY0pzbE9WM3NGdkxNTWlyRUtrUS92UWVJT0dBOGVnSHdWMzViU2dCRmpnN2FJMXpUTEVKVHJRR3JHNUlrVDJFU0RTWStaT0tSMUs5dG5WcTVHQzd6c3Bsb2dDTU9LRWZtSTV1cnZieUZlMDFZK1NreFJLVFlTUDYwZkVYclRrenQ0MHJRWHpRSEZvZzZPTFM4WHlzcFM3V1ZnSU5qbjZrSmpLOGhIU3pUK3N0bGlKdmhDSGpxbU1STWFEV3k1RExxb2xJTXJpVExVYm5ha2o2OCtNTUxxbzFwcHdzUC8vK0lsbGlCcDFNOVVPVStkVWlLVUlIN2dPSW1zSnkwTzZoNlA2OXBSRzFzYTYwZDZYSUpJYjJsRHVPZndqOGR5YyszNGU4MlJlNzJHS25CRGxEckZWUDN5QmpDSlIxN0dzeUIyZ3BBdGxBMkJjM04vQ2JuajU4Uk9NdkYyTUZZZkJUSGhDN2xDTnBxUVNNeDU2a1ZuWnp0am80dkxnVzBnTkRmM1IyVmlzY2w1Y3pxdUk3YlFHRStYRW56VlFrVTdLT0lqL0RFWFFxU2thQW5XTGVHTHg5R2RuNC9hdm1jd253alpMUVY3RDFIMlU4bnhybjJ5eE93SHVYOEQ5eXZkYjExT00vNVZ3bU9RZUlaTnNhV2NHSTFVZHJjUy9tTWtMbkhtQjhIaTd5RFFtdk14L2FzT1hyb29waGFzejlsOWVKcGZ5NGFLZ1RJZThUR0pDZ0hVMTRISXNjZ1JaRHlSeVJyeE9LQUV6TjFBNFpXRDJnV3p0a2FYeDJGUEtlUHBnNk8wMGJkZGdqcGhIZUNBYXR6OEkweTlYck1PV2k4TktyaVZxY0U4NzBEOWJKSDBkRjkwY2tZVS9hQkV0SmtRMlhsNVBqbEhyUWRkSThnNXRIQ2Fqd3g0K0dTNDdsOGVOcUVFY2RPdUVrVUpLYys1TDRiZ3V6QVhDdHVwejdqbUZZOXRnZFQxSkZKL1EzQlJrdk00ckhaOXNtZ0o4aCtPZ2hybW9ONnpIc0did0lJNmtQY0pDMDlpTVBoVWxmb05IUDFZQ3FiclU1V1FnMmFqa0NrdVFUaVZCRnBNcld6RUQyWU4yN0JKcFMxYVVmYXlDdVZaWU96cjF3Y0F3cldQMFNEd0d1UCs5SEZ0SkJRcFZkR1FBS2dvUVhibTNZZmN3aERZUVJlNHJZYm41VEpZckpiMForVCsxWUUxa08zL09ROUpGQmZQOGI5RkFDYS92THNjV1RVL05ZcVJXYXRDUFRtaEU5R1I0VTFOV0ZXVDNDbjBwQjZZRFhjNGp2OWRHQWpsbStyaXNCUEtBZVVpZTNGeFJiN244MUJtL0tpa2lGblk3dHMrdnBzWmNFNWVScW5xaTI4QkNLbDMwY3NLSGZiWDdaRkpJYWtacUpmMFZQeVNrd01sbUZ3aE9qcVdPaEIxVjRDU3NrLzd1US94WjNKOUFDZzJQaXZ0S3c0REM2V2RYc1J4VGlqNnY3cmpKRXFicDFHUFBXSEtxZ3FVY01yU1dPcU4yNUpuZHBqOU8yWEgrMWRLSy8vTU9JNEZqOWVKMy9QN0l3Vk5SUVUrbjJFZnJ5aTFJcTQ1cnJjbVBZQUtTcjk4dWJ2Slo0NXRNalF5KzZpU2lPZ2ZuSDErVUgzejc4MHdDRVB3bTl2ZzlEdUFTKzhDMm1wbWpNc2tpRWQ4QVJQK3pndWhaNGliQmdvNlJsVVVTZGdFK1lQbXVKQ0tKM0I3eUVqVHY1ZCsxNTJBTzlGZmZtVVRnWmVSV1FjTTVmd0IrVnUwNlJ0QjdCOURPYmRpanVnU05ibUs1ZDNjUEJQa0tRRHE5V1FCNnRxL3AvMGljNXBKRVpJZGhVNVBXa242RVp5YjBtdGZxOXpJelkxa1NicjRaVXY2eTltbmxaR0RaM2YvM1N4ek9oRW1OUXFtU25ybC9Xa21kMHB1T3NQZWxCblErbTl3dlNTTzRlTFdWMjk3dUhzS2hXTGZOb1c0bGVlQjNLNVdPczg0NjJaRVU1U2d6UXdpVkFHYmxQb0o1cVZ6Wi9JbEtEYm1DbnE5d1BpRWVqWFg3a0tUUjdkY2V1cVF2SXhmZ2pYdFpBaktNWmlpSDlTbmVVcWRMbUtPQ2NWOElLSy9yU1ltNktCOFFLWU9iRy8xR2wzSUxjMXMvemcwSFhSUlFxZGpWT2tVYUNhV2cvTmU3dkNtK2dWY2xHNitRcHpOTXlQYjk1QkZRWUdRbDFnL0cxaXVwbDNYS0xuMUswNm5QMk1uUHVKNVVCL3EvV3dhaGtBSXFZZVoxOVNSOGlTSVJGVFNHYWwxTzZhb21PMXJZbzNXcnpxRStFQ0o1Nm90cEIrY1l5WFA3Q0ZiMUU5RzFaYVBKUkxsUWlISUxsSWNZTnBUMHd5R05OcU5mN3FsTEFCUTF3ZHAxSU93L2liV2czVVg5QlQyYlAwS0JONFhsWHNjSVhOVFRVSGpZVHBkUnhHeFQzM0svQllSQnFZYU1iNDlxTWs4QmEvK2F2VjFGem8wanVES1BSU0JQa1dmUXRMcUNGSStRcmdVMlJnVkx3Q0ZvaEoyZHJFaUZDS0pya3JlaDVNTE02dmJucXV1aGJUYXlyZkxGS2pUTEdpUXNnUE1PSWdxdWdMdW5ETHhnYjNEMFRHWEtVRkpPWG5lQVdYQUo5MVZsL285azVqWFJWQ2RVSHJ2ajUrS3NMeTEyUk9KaWNSbUJpVWVpb08xWnFVS3ZkOHFhUUZGYUM3WG5EWUxJZW1DMmlBSStnT0xGZzAwY1B1dHZFQmZPQVRNMW85VlI1ak1qUER1a09ycUJ5QlV1TGR3SDVTejA3allBbEN2MC9iNUloNUltUGZldkFmVVhRT2hwbGFNQjMxRHB0VWY1K1I2dy9xK1NCQVlTTjE5bVBvY3JlcTB0QVAvcE16TWJYOUczQ1p1QTZERUJPcG83QWs2T0hnVFdOUUlZcnhjTUxReUU0aEhzNjdrRmVmZXhFVHpJTjFwSDlqcERtazdLTUlSbXNHOVZVcFRRdDFrOEI2Vy9YZGs3QU81Q0dIUmY3WVhnMm4zbXc4YTBZK1pjQm1EaDAyd01ReFUweDREVzFzZi8rOGg5NU1YUG8xTXJQTVVBZlZuRndSaXRUM1dMVHkrSzBTSEhGOWhxVFlWbVFJTTBxQXVFS1BKYUxLYndUTmhlbFc5b2dyT2h0T1VqZlNoNjlSd1kxbXBhak9TMzlqakV0MHcyVTBwYUtoRUxMd2tvQ3VXYU5ha2tMbHlIcjU4TXhseURhTmtYQldsa2RacU9sMnNZMVNOVGZMYjNhNlcwaXdXQ2VuN0dpdkVRbFpnUFRsK2F6TkZzZml4TDZpbHRlRmlIRTFPL2NSMGhnVDJuV0t3K3NEakhQSXlYa3JRQnVYREhGNFNMRitpV0FwT25Fa3NheWozQ3kzSUFwdXJyTXhZa2VkTVB2OTYvU1B4TlViRjNMWUU0T092SXg5alJmMFF0V2s0VWxHVVhCQVoyY2xDOUlGaFYzdU93blJRTGJvbGlQdCtDTkxINmUzcTdDcHVVNWZVWHpmMjc1MkJzeXpxRy9uZWpBVis2OEFYekkrMnAvOFpBb1dOb2thVHZoMmJMWVM1Y2hUWitGVGxZaGRzUHJBOFZQdVlJMGZwRjZPOXRmM0Y5RUl5aXVHMWkxM1B2Y2RWa21Eb0ZHd3RNWnRZOE10M3pqMnRoNDJiSC8ybWVFY01zWG9aN3NnQk1GUUFkVCtZem9kMHNqNG05SzBkYmFsVHZpcDlITFJhazFZVStZOWRMa2psMWJGdC9zRExUSGxnK0F1bCttdVYrRmVSY3FZRkpBY1k5b0lsSlhTNE5BVEFhNEtaL1RFdjlFSUF2a3h4OUlUaXNXbjE0Mk5ZOGo4RzlkZHEwYTF6MzRZYkkzNitHbWxhdDdLTnBvUmhhQ2VjVWV1Q0F2NTdDMlJoVkxWU05YWGlMYVhNSFRlOENQNUtvcjZoRVYrWHhNYU5XaTBXeDBMb3ZlZEI4TVhUZFYyeVJMbkkvOVRka2VjTWU3ZW50bHUwUDlDRStmaWpGVStQd0E5cXd0ODR5eVB6eXAyOExsVFZSQi9pc3B2Z2xVdUtkSnBHZmlVdlpuL2kwRU1WaWNwa0I4elg3L2liRVlycnZ3eWdxTjZWNlQ1RzEzM2xNMlBmWmorUkxra1hwKzFOSXJRNWYrQlR5MEZDOUs1QTVwdmVTQkhmRWlsenc1bG1aM0lDVFF6Vy93K3dzemdzK3Nud3J6Y21tNGZzTjVkNFVzMGVrUWVwNFIvUXllRE9kWlI2S215dGlydkszKyt0aXVVM1ZjSi9PWnNsOFN0MU1lK3JSZjhJWkFJM1JQdnpPNzM2K3JIa0pEYXlsQTJOUDNpbHQ0bEw0Uk5SaG16NmFidDRoMlNZWDRBalhOUGJ4SG4rOTUwZUI0TzNxcEdLTS9pTytLeVNrMCthVHNLUThsaVNQWnMzRmFzeHhUWUZxTERvVjVINlZUSWxxR0pJMzMzSG1XMGVBd0swYWY2LzFhbHE5amhkanNMMEFTVldsSGZaOWI3UlQ2aEN6MUlRV25weGFqTEZUelhPcG5OQXJGUVFXZFo3a1BjU2xPK0p6Skh4c0ppOVU5OVRCaXpmelkwek8rTTI0aWgzRkRhdHplNzFWUHh2bkthZDUrV2orVUpVam1xRmRmWWhoUjlaQ01KaFFXRHZKOXFFbXpIUWtPdWxYOE9hSHBhUE5MT2R5MUJQQ01va0FtbjdPVjJqaGNlaFMvbWZWWWtnL3RRaHJTYktDaXpaeExTSzk1Mm05dGFFMEJ5ZnZLN3FMU3o3UWFiZmpQK04wMk1tVkN0aE1VOFcyeWVBdjhLS2ZZZXRWV1dxeFNTK1gvbDltSUorOGZqalVydGltV0lwN3ZmL1M0ME9WcFozbDVmWjhxSzhTYXlQVHFsZlZCNVdqUEpERTROVktIbGtLbGpWR2RmWHhTd2lJNWowczUvcUpZV0hKbmtCeEVGQXB5LzZWNmxnb3FLc081TzhvcDhQSitaQWpISDlGbDUyaDROOWpLNTlLR2ZwQ3plR1M5azUyRkNGcDhRYXpjd0c5dVlncWF5UGJzRkZDa0NKK05vRnZoeFlUeXhra2xQS2w5aXp1TjhoL1hleElpeTQ0QXNxTGY2UW9DMHVSOGcva0hiL3l1TFhxWlQ5REpmUUk1NXBYaEVvazNwQkRyZTdYZ2VKQWMxSGZlREVtWW5CdVdTUDBndWlSVmlST2k5UDhTNmNZbjFBSi9uWHVYZzFobXFsYmtzeW1hVkVLWTFQOG56bS82WEFKV3NxTDl1WVdwZmt3MmxSVHZKckNiRXY2MFcvaWpUYkxrZElINzNJRm1TdFBxdWQwMjNoOWJpM3B6K1JtWGJ6ckJFOUozcTdMN1FiM0NIUXgxOVA4UFlhNmd1WmNTdlFtTlZ5L0ZWYzZocUJwZ0xpWW5ZR2d6My9xOGpCL0U4d0srbUx0TElOU3l0M0M3VFh0T0xya296ZWs1U3NBaUtacTFxbFhnVW1KUk9iYS96YTBpeG9QcHJ2cTBvU2ZpNmdvNTdjQ1pFSXJMbmdPeXVFc0ZqNHFqV3FhVWpFNVMzbkJCMkxlVDBKMlVaKzRxZWpyUVdDTi9DL2NhcG1DK3BGUFNCNlR1dzNzWi81L0VPa3EzNStvQjR1Zm5XamVFcGNGbEF3c1JXVGhzNERHaEtGeWQ3NCt3VVMrdEpDZWpMUGE1cytSaU84eVVia0kySjVOZE9WYXQ3WlN2R0loZjYxdC91OUxyM2RvdzZldHRJb3VsRndXWGMxVzIxUmFZYmo1Q3Y1ZGY1Tm85cWtvNnR3YWdjeFB2Vk4vL1BwaTU5LzFPZnpRbDZ6eFptaTQzMnZxWnljcjFyYW8waVBJZkdhdmZLVnBtL2JVdC9STmFzUGZLTHR1c2ZOWUtBKzgrUUJUWXdsM1BZQjdrYjQrOG5CakpGaTVCZ3J6OUlTZzN5MGZsZXN2cURZK0swU1pHU0xVOTRYcHRjNk1hUjlrcllZQytaMUtlZGo3blR5eTNJNXVvMzNsblZYSGxaMGFpMEp6Z3hhanBQZ3o5Y3BsQzBLUk9KMnFYV2pSWnpHK1huQy85YVlKUFo5YmVoUDlGdyt2QlJobFhJKzAxdmVsWm01dFpmRUN0ZjhlemJzei9xakRsdm80YUxkMlhGOS84dVFiVjZiZkY0ZXZwOGgwQTZQYi9aYUlhV29JdGNoU21vempmbml6eVRLajUySm9RTG1NYnJaYlRad2Z5UWV5NGRva0NOTi9VUGVTZENuQi8xbS9DOCtPenFiV2RrVS91Ky9hdk1IRy9qcVV4clJMYUg4bStNU1FlbTh2WUM3RkxRN0hyMy96eG5lR1FidGVDYjBaYlFrbUJjY1lkRWMvejBhR1EyY0hBSXRabFNTMHBUN1ZVSGhvK2Q4MkZoWld2bWdpU2JhL2ZNSEYwMGNrVnNMVVE0UDNyZ2RMUzdOZFF6VHN1ckF2bERKanhCTGkvWHB4NGZiVFN6c2VIalJheUxHeU1waVg0aUdvQUFOVkRiQWN1aHAxMS9vYzFyaGRzR0ZBZnJpUE5FUUdLL3MvRld4MWg2bVlCclpidlhJR1pCMmxLSVlzZURCc3hzaDZwYW5nSzNoM0t1RnRkVUQrOTc3SHZpUjVHbEcxdTJlWEtHZ01TS2hwLzRhYWp2SUZGcU9uc0tUWnI4MHRkUzg1c3FvTGhvV2RrcC9LYXVvRGRrT3ZmbDZpa3M1aWJoU1E5VlZ3VEp2OG5PdUVWRWpyL0NacDZHa3E4YThhUjZpUE1NMEhza1VLVkhZQ3JhMUFuSC8zWnBpZUFiekRIQ1U1M2lJRHhRN1Q1bSt2TnZPZldJVlpyT29sM2NqTTdzRjNKMWJoWkJuQUFMejBmRkVOYWh0MUQ3V0ZDOUNvS1RkNEh4MmZaeHp4WHNnQURvMmlqbEVkcDJKK0pyV0IzTno5Y01id1B0dzJ2cW53UVF3SFc2a1U3RnBBWnh5R1BPVW1zMGM2Nk9pZWg1TzM0eWd4T1Jub2hpVUdnZnUzR1Vrb2lwOXFCb0w4WU9hc05GdGdWTGl2d09HcVJPRUN1T3BGZ3JKc1U5amFmMkh2aWRRb1RkTnRwNERRb0Q1RitEaGdJbTZCM1g0b3dRUEpTWVo1TFRYTk5xQjlIUmlMNVBPSytjVWh4K3RXOGV3U09JTHd2ZnkybzNSNVBHYW0ydTYvdU1XOURTRlFVNTgvWnBkNG9QdXZiOHhPQk5ZaVRQS1d2WU9QN3VrRDNqbWgzekdGNWZFanQ0cEhrcThKSVdyN29xSlgzYnR4RjRtTFF5ZXFuTjNIUjNWdHU2UDdtWFVvNjdneE50MGt1Z3JxRUNFN2R0eDdhMEFXd2hYWXBMMmZmSHNMcFg5bTBoZGU0RDdTb3o2Z0hzalpqTnJHSFNzSVUraExSYWtkZzlzeDlVYjFDdEJLRE5YbFNnRXJ2NXhkUFlqSnRsNC8vek95eVZyMW1rM2cydVRPZVY4WDBIUzUrR3N6WVRBMXNUSndodEx0dDdMRVMyZ2c3UnJJVnBYNXNNRi9PU3U0dklYTVpRRVFRaXcxQVNCV1puU3dUNTR3d3BWSHlnUnlWY2ZZbTRlMXowR291NXlxVHVqbWNTVVhDOE1oak5XTXE2ZVBnTkNXVmtHdTV1YlYwY1Mra0VCNzR0TjN1N05BTVFlVHRqeGJYVjNrb0x6ODBlUUVyUStlQVpTOWlFVXhkSURuM25BZ08xUHArT0VVSURDbjR4bm5kVmd0QjFVMlRlcWJLdDlKa3ZlL3BJODBkRmxPa2VjSDVoZnBabUg5NDhxZTBFeFovS1RudExIVEdkQm41VDlMcm1tK05VL05yeTh3dGR2emJaUE1GaDFhUnRmRC9QczB0b25Cbm9acDE2NFNnTE5kVDgzcjk0NU92cTFVdkxoZGhCV1FycE5aWHp0eTFZZXl6Ty9hOWlaRXFBZW1wL1RGUSswQ0xtZURXdU9Td00xK3NYYmdqa09jM25xN04wVkx6cWRqeTllQnNWa2ZqY1FHM0lRcUxCQ0I1TGphbXVLTElpOUtURHFkRkVoQWkvd0xJMTFmMkxvVGlRenMvMlpqaFdqKzllTGFJK0xzMThnb2o4L2hseVlJY1hnUVA0RWlremt4OHJKc3crcEN4YVdSMnhxVzV4M3RxdzUwT1ZlZGhnbnpUZ21kOFBDLzM5TzNTa0xwL3NPWTZTQzQ1VGpKVktNSmVPUW9jVlBHN0hBN1k5VFh3azJxT3JHdzlxODYxK2Z2M3NMY1VoRFZRdWlubTBRR2N2TVpMUHBuc0NWTjB2clhGNlJuMXY0d3BZemwzTDd0NjhiWktHQzNLUm5aMDdHTEVPY1lBdUR4VmNGQmFTbmx2aEYrZTBMYmNJY1NhQXIyK1g4MlNFcGpMVlR4Q0kwMVo2VFh2aWF1dkgwejF6NUFnTVVmTktxSE5UaFdDZjU4MXhEK1NiUzl2MzgwbzRqc0lhaWhOQVg5bWdTYXpTUFdFeTltMExMU2h5dWNWSWwrRVlmTjZua2E2NDlEOEl5bzRHbm9nNGVsZk1pbmJ5aGJNNjZaWjVOOGtyYUhuMzNIejduajFIK3A0SUtvU3ZLQ1ZYYjM1dG9xVnh3b0NFT1hmUVh0VzZ3S2t5ejVnMWU5ZHE1VmRYeDVabnAzOHFweDBCNG05SytYU0daU3ZoVXJiQVVDV1NmNkhvdXRZdzBoK2YwQmRrZWtRZTBscm9YcjExbHRDYUowMk5sOWNtK3BBQUVrUWJSUjNwZlk5TE9wYlF2bVl3ejJaSDFlNFM5SkpiajBhcmhtQmdWYllHSzh5ZmN6ZzJRQU1VNEQ0Yy94MHlnN1NqUEJjTDJiM0xpVHN1UnRSOWZxUGYwZmY5NmlZR1p0dFhKemtuNGhqaTZkS1FmUXBFRU1oeW4zZTNleXJ1TXNOOFlXYlRSeTQ4K1p0b1lReFBzbTU1R1g1UVlwallMR1A4VTVQNFNDZ3pzT2k2cFNjSnZ0am9oNVJmQU5uTFppSlZIMUJURkJOZkg5MS9objJ3Vzh6REtvVHg3c2pLQ0wwbWhNcEpucVdtckQ1ei9SbkVNVXBwQXNrajRJSWozVWxLNkhDUHhpMkxWY0NLbzU0VHNoNGN3R2JvdFgzZ205WHBsRmlHOExDQm4vcHI1blFTbWY5MXlmRWdOVmczVXRobVg2anNjK1NXbDFRejRLaW5uSTN2YVVoeGpOQmU4M3AwZnF1UGUvKzNNMk5OUzlRNWRPaXViQloyT1djN3ROTzZUSys5OEdWOG5yR2lId1BvR2ltd29aU2ZWNGsxT1pNZW82MkllbTFnMkVDdjhXY0NvMEc4M3ROMFFjT2hwNVZtZlplT05RdFlybm9LTUx6dmlHeWIxK1U1UGVBbVA0SkhCYXBIZ0x3SDdrdzlnY0IrOXFlaVlJeVg2VXF6a3Nqam1ybGIwcHBBR1F2K3BuWDExRDhzQ1FmWHdXVm9iZTR3ZWczSDNIK2U1U05SWEgzVndob0xTTXdyVFROUWdTUU5lZE1Pak1HallBL2lmcjJQaGNwUEhvdjRlUkhObTBrZXIwNjdCL2VCTFhQcUpCOEtsRFNtTW1yUm4xRjRTTGFwS3RSemZWcERlZWt2VDFQcnVEa1gwM09MbkU3L1BUOGcvRGlKZVBGS1RSWWplb1NSTzRNV0RkZjRXMFBkT2ZvZ3BGc3pyU1poelZLRWVNZFNUalArTmN3dmtvdVV4a09wWFI1cldMbjI3dXc2T2V6N3hGVml0eGIyUmtNbWlIZ2VSZTZlOE1KZlBKY2Uva1BqaTkxWGFmRTlWdVhMQTFObG9GSHdob01kTFcwS2hMVnYrU0xDeFJkR3B0dnl0ZklRK3FicnNFcmxWekJLRERhL3c1MzYreUpObVNFK3NHakdnWkJ5Q0ZDUWpLZDNpQ3BuNllmZnptRnJqMEhHRC9PRWNBYmM1cWxLMkFZdFdhNEtHV09yK05JWHJRNzV0SlBVR1lnTEJ1ZnR2L0dSazRZK3VMMFp1RkNPL1pUSklRNGFNTVlGdng0UW9oa25WTDgrL0dMMXFJdk80b2dqdk81ejJHMkUxbVZaUENZendyMy92dTBDVnhUeXZ6M2lENVU2RlRBUU1kbjVTdmw5UXZoMHZmbmRJSm1EaXpBekJwL25USXR1M0pOOU96WUVEQVlHeWlVWitNdGdicEFHZkg3SU4xa2dFZXlJbUwrQ1IwbFFkcG9LZGlDSXJCZHptTkJZYlJsMzAwcEEzRko1NVlOY0dzakpDSUlCaVlXRjFlcFpKK0xtQVZqOVZxbXMxY2lRbE12ZHFLZFdnZndLaVhKOVBLbEE3ekx3ZzZsYkhpMU9lNWJDMnA5UW4yV0pRVFpMSG1KWVgvY0pucnFQeC9COUpFRTRGbHhLZjYyMUFyVjNwNG42TU4zWmNvajRTc1Q5dUlZcENLZGcvMDFIQkl4YXo2U1Boa2ErcGtiWkFpV3laY01ITVdETU54TzdGcFlqSFJ3ZHJKTWJTajNOVG9WUURXVERzOTVlV3hzbzFtNGpVUlk4a1k3T3Z0RXp2bjVOMUUrZGVCMTUvclpTaVNnenNQVllhaFFEK2FueFdGMTR2RVY3QzZUQ3BtcFVCV1FURVE1ZFJXbmk5MU9yMjFQY2dEZ21KOFJESXZBTDFaVVQ3LzB3ei9kZU51TTVGTVR6L0FsckNkWWREc0VCTVlsOWUxYldzdTVLckVHb3dhM3FXK0NtdTE1ZldYNTdRbFBmS2p0U243Q1lIdDY3NE5aRkEzaUVmN0wxaFFuQlJhc2R6OHhLZnlCYkQzL0J1Ri92MFNWbnB4c2krQVJhQ09XRlhwSkRWanBYT2p6Znl0dFNMWVlEbVp2cEEzNnhjK0pxQWRuYVB2aUJlWDVEWVUvejZDaXJJMXZSN2FEMnAxUmRiTTA5TEZNenJmejlBUWxHbFFGZ24wcmxRSnJ1cHBDNm9GRzFrY240b0wxNjg2NHpZbjgyRVFQQjI2UHJDUG1VelovWkxKaU10ZmNXUzBlWFNzVmhuVXNIdUtFVit0dENzd3NWTHM5WSsvSEZqek5qaU5GbW5JMUFTRG45KzUrS3J3eHQxbmpkUXRxaWc4akRPdFM0Smc5a3hWTHNkMzBwQm5sNENlV2xja1V0SGZuSUVnT2lUalVXNjVHSzJqdk9HbGY1VlhjMDJyaTBSWXRMeWJKdllsVm5aY3RTcno2TUphb1IxZ0V2RVYwVnBJRDhzcUlBaFdQWndVcDBTU1RKK0paRGY2SUl6cjZ1bHR6dWRrYTFmOEtoUTlZNVRDcFhXRTRtTGxRMitLN1VGS3g2TitBTXJ5OVVTUjBhYXkzcTBYTWRWWGhGV1pSVGZueHVrcTVUb0hhdmZNTUkzMWxXTURNcDA4MXRUT3ZzNWxUUnlQQ2lBTUZBRlF5QjZHZ0tFV0w1LzFTZGpQZ1hSL3NyMU5MR1VKS2dUMW9NVndsQ016bjlrVExtKzdpQmNRYTBvU3BId05iZnVBTG83Qi9WK3ZsZFlEUTdzY1FsMlYrZkhIRDB4bGZPWjBIT3h2U2Y1TlpnQ041ME9weWN2NklsbmttYThtczFNNkt1VGhlKzhvTUw1UkpTV2xQdExsYXlQWGtlNTVFd3U1NUlZQzBMdXVwSnRWSkt3Sm1IU0hNcFVrdEkvVHZGS1FRWDlCRWMrTFJKeHVFNVI5TFdSNVdTVGZKQTRVRkZlUkZkWDFrUFVEL3l4eTFkd3k1UEV4cG5yVzBwQUdOcGNqOFU1RWowVnhwazcwM0t3TXEyaFdDeERoQzFZb0czMHQ0dHlWeGx0SnhRQlFIZFZCYjlaa3ZqWHk0VUxuQTFlUFBHV09sVjg4ZWhvSzZXcTVWdFUvb0dVRTRrSndGL1oyblJoUEV4RnpXNmVQWjZqSEZOcFROLzV3dS9INzhWUngwSTNrYjh0aFR6RWtQejVZK2lZcXhrNEgwNFhzWHZ6aHViNkdXZkJrQW9obTJpdzVNdzNCbWRROUVHdE9rdllKZWo2VmRjYUpGUkZOTlF3Nm1ZRXl2S0hJNUd5c2hWVjZJRmdybVpJa3hXTTFVOE9VbkMzUGR2ODRVbmhxZXEyeVVOckdvYmhXWWhjRjcvUStVcUs1aG04MWxLNzJJMDRuUWduU05nRGk3UHpTV0FmMXpJMHdrbXZ1S2ZjV01LU2VpY29ZRTBkb3VkSnQ5bE9RVFBKRjZqaTZLdW5HQllpUzltTlNzdDBxaVBBWWVtZXpZa0xwck1aQW9kZjcvdjBZN1YvWFJjcW5KN0xMYUcwaThTZTJ5MXc0QTFUbTJISWl5MmN4SGdXTTFmZitJOWo1MjhMMjBmOCsvSkxUMitFTE9lN01nQlUyTDM4bTM3akFYR0FDUHNnWnd0VTBRZXdvQ3ZOL0ZiOTFrdGxWOEhjcXIzTmYzTGxZcS9TZEw2MUNHcVo3d0hBQmY2TVp3WkZFUjFVcW5ab3FtWFNERHA3Q0wyUWlsWkR4K3c5cXhQRDNmVzNxZkIxay9iazJ4S0ptUVhTYWNJQzJ6QVJld01STU5UVFVjUUhjRzgzTDh3TFRGRS9CcXN2Z3k2eGZ1Q1g1eVZNaHBlcVNhR0RQbnhRNXVPdnJVRDdMRTNpeXQ2bXlaeTVTVHhld1ZUY2dmaTVwMGlJR3VybWFqVUNydUVWVWdwVW41bWtSRllNZTlWOFc4L3VUejJkOURiSnhQeHNyOU9vblkrQ29UMzFaZXZ2WVF1VFVXZ2tldnlCTEJXWHhZeDlKOWt1UFlIVUw1alNvYmJCT3VtOU1QdUlLRVJ1Nk85Ymt4ZmxxQWJwVUM5Q0gwOW4rYTZFemZlN0Q2UU5yVzJmS0xxTExscHE1bXdWeUx5VElTZElNWXViZVoyYmhoUVdLZnJnNEIrd0lKSkVWUkJZS2lCekh6blBGcmhySDZvclcwbzF6TnMyKzhpSTFENUdrdVI3OVJtVXpJcS9SSmgzZE1uNzdFRzRmS2xldlhNZE9nU3p4SE8wQWxkNXRVemorVlpsZUFyS05GZHV4VUFZajZHQUtTcTRJMjlUQkN6b0Fhc3R5Yjg2d29OZVBWWXBLT2VnV2NodDdBZU1jYklOMUR4dUJaVzlNSjJZbWhVQWc3M28vVUpSWGltQ1BoeXNWakNKQmVHQXlITTJXeXE1S3VaQk13NHZITG52VzByUytGczZUZmdzdzlsWEpkMW9tZlhmV0RzUW9wVEtPTW9kZ2JrV1BXTGFlTHg5VXBwUlkwV0NaendHZjJ3R2hKY1VMcXlsREZRUmJzUGQrakR6TTQrY2JLOGdiNUFJckRBLzAwazU0L2xWU3IxZFpyRk5TOXRKUlBxcUFXVm85Vlg2am1nQ3g1cE9uOHltT0VraW94R09aWlk3YVR5OTlQNEZJQ09ZbzRMSWZER3VTcUp2aFFCdFJhRlBzdWh5cFNMUTRWQnQ2L2JOYXVWNm1ocTJUMlBma1FNWGVlYVdPM09aNEFJQjZDRnBQQW1CWkpHZU9RR2dwbHZGRnNKNFNEblVCdUI1SWFDUEIxSEpGd3RCNTlydkVSZmc2cVYrcDFhK1hhUTFyYVRnN0VPSlM1UDU2VzlzWVlPWWhJTHpwT3hGVGJ2TmZRTWkwSk5VWDhBMlltS2VwMktLQUVOcW92bHFETDdEZmFFaTFZektjOGsra1hqeWlKbHp5b3BmMWd6TVlSN2QrcEhqOEVGRGJkMWxjUEE2K2lodXlsWDlkSWhxRUFPY1lFeU5ZOEpCWVBsNHJGNTBqVFNqMXV1VmJOTlV6cllKUmNBVnhEZUk0aW5MYkRyV3JRWU9nVXNOTVlnTlVhUklSZFNJbkNJUFlVaUZwVW90NmVxdHhEUG44aUhLc3QyVFE2bE1zWUJLT0ZITlpieERwK01lc1o2ZjdPMnVMR1dpbEI1dU5INGlwSDA1OC9hT1AyU1JIVkV6cjBkRlVrRDF2RFhDUU1iZGV0RkhVYlM3eThtZVcyb1pDK01kT2w1YXlYSzR0N21VVTNoVkN0UDRoZHdpeVdhSk5OcUlReVNrUUYwU0JabkJFcDVuZmFjWk9ETHRncHlUbHhiS1FNV25hRE05SFd2dXVoWGlRN2g1ZlU2emtUdDJMVUh0enEyUkl2Q0lEeHJYTkZ2eTNKY04vTkNieXowcTIxVS9Bb295YWZrc1RCUTd5cm1kekRUU0tDeWwzTi9Cbk51ZFlVM2xVSXhEeUg5ZXFTUjBQc2dlOFFMd0NYenM3WnVUdy8yZHJmV1B0UHcvVHJOdkpIUGFseGdkZ0RmcS9YYXRCN04ybzF1S2prSEhTeEVUZElhV3Y4S1JQL3VyMkozdnNBT3c5bFFJQk9wVW96L1grMEgzVnRwd1BYS2hMdmlGZkduZlBTNk9TZ1RtaElxNzJOVDZwK3hsN1BTejVXd0xJNjlDWEQ2T2hhcnN2Q0JYcEhOQjd1OW9TTTZ0MHBzcUtYMG5JL0lmNDNJVHpLZ2svRXdFempid09mejc0ZWU3bTAwcDkxZGYvcWdvQmw1YzgwUkhxRzhQdFhWY05aNG5ndUZKVFd1dmJXUUNYTzluUTBFcDdqMW1SZS9ROFJJek45L1N4bkoxWUw5SWJOcVQ4OFMwMjZvL0RDa1h0ckJpZlJTd1ZHbS94bVZheGVVZzdyRWlOWlRaTFYvU2gzb09WS2tFT0s5RG9QUHEyZnJxM0tqWEtKNlBFNmRKYU8zTWdTSGZ1d09DMzZpaUEwU3ZrbkpmUksvcjA1dHNYYTVZUG9aVE1iRThHWlRCQi8xd1NTRTV5WjFiZzA2amV1NVptUVJtWTd2aFV3N0U3Q0hudFZMM2ZNeFl6bzFqUFRQaXd6RVRxWE5WcmNBNDQ4MktSSysyL0VaVTNtU0NhSklxN2psZVEzMGdReENDSmIrNElTTUFEZFltWjA0MXF0S1dOcjl6MDRJb29EdU9jSFlENTlRYUNhdGZic3BBeTh5TlZXNlVURGxTMGViTVRmUFpMeUJ1WEFqQ29XVTNUK3VaR1RpSHp2ejRqMGVBMnpkSDIrTnRVa3NOMHJ6bVR6bmxXcHJPMmNrU2JPTGxRZFN2VTJMQUpHN2pEMElhMU1NMWFwUndMYjBxL0hJcmZKYUlaS2ZVaVhBSHdBSjJ0Lzd6TEZaUTZTbDlncUt6ZWpvMDV6UHd0ZnZCNGVVWlBNZlQvc3A0MEcyVmkrUEFxaXZScjIzTzJyV0w2RHNKSHNWMGZodUlXWGNPeVFXT3N4blM5eEcxaGhyVmhpQnBBdWEzbTNXeGxjcjJ6TUFjYjQ5YUQvcUw4NlhDT0JTNW1sSHhXWWJMWFJ4R2JCMFZKb1luYVhhaXNlQ0trcTFpa0J6TkJMbk9ZWVpzQk9iUFFVMnE5eXFFTUNPOENxNnNzNmRybFhHdzgvUFVOcysvemF2RlZkNS80VE1wQ2ZNS0wrUEV4SUphcStNVGlleXczSEV1TW1IQzZpak1jUlh0M0xrMTZ0UXJqU0FvOWJXTUFRZkYydlA1bWE0SWptOTY5cmxNbVBaSEM1eFpnVmFWTlduZnh3WEdGVkpWcjcxZEZNNmRMUlhHdW5MZFpqZ3UyK1ZpSVZjVE5nVXgrNG9lUXc1WEI2emYrS0ZYQVF1bWw2NmR1c3h1TkZSQ2ptZjJGcC9BaDkrNlhYa3ZkeS9jMDV6MUR0ZHNsVU5BK2NpSWdPOFpZTTNnK2hMMjl1Nmx6TThySXZpbGlEMDdoSlVFZEFUWHQvdzgvdnZFeC8zdER5TC9VOHp4aHlMZitpeExqVmZ0OHhDdkxOUVc4bmtGNjVkVFpObWVSVWV5ZHlQUEFTNDBRVWQxbUZjVk50RkJIL2NPNVg2ZXl2M1NrN20wTjJkdXptK3dmdGp0MXVnTHFFQVVkVkN5MlQ2RVMvcnBIN1Z0V0ZOcTZtMFNOMzlENnRpekF4dkMxdFBZVnlSUDFZenpQSmNXNnk2bVVWc0ZHaWlZWm45b21LS0NVQ1MwRUVPaVVnUFA4bkc4SmxGQUdKU291NVJyYmJoaEI2ZE9GTTVqVDhrdWMyMnR3QnByRjhmME1oejVSWlVIc0JJcWpDVThuWU1senBzNWJoZlBiN1RhbGhrKzR4YWJqME5YNGl3eWQ2QkQ1Nkp5c1RDaHJvWEdWYXhHRklIWm1HSU9jNHF3VTZIbktBa0xMTFErd0hrT29qdEphVER4RmN5UEFzdXBkeElEVDViY3NDd1Nia1VxajJKNThsZ1VwaXZUZnR0bWVNRkVkTmk4ZkJzN1R0SE9kNVdFWXh6clhWckd6Vm1nN01LaGNLMFNubGkzSVFBbjVaNk55Sjhya3hzTU1jTjBCTEZSY1lVbzdreGRMUUhtNHJhTmdrQ3hyVTdRZk00VCtVYWNjczg4UU00cm5ld1hTUFZVVWgvUEp3dlVSNFhkdG1VR00yUjkwNE43TWZrV1V5cXVuaHFNeHhFdVRPTklpNmZBUmJHMENnYWZCaitEYms3dXZYbzI1cUpMOTBDbjRCVHY5Z0tCZVgydy9rNlNJRXlVcFBldjdtZjF2Ym83aGlqVVMxd3RTVDAxMUp5ZzBMZEdzTVZXeUd5MFdsZ3NPRWpTWmN3WlhraHhMQ044OEkyL0NmYzIzc3EwM3BhWnVqM0NIem8rdC9YSFVZdWNvdkpXTldRNnlvNnVCdE9rbG1wN3FLWVlaZ3hSeEhzRFM3cVVYYkFDdC8vU1R1TFU4ZUtsclV1VUplYUZseTRVQ1NNY0tXdmRiZHl2VE10d3Bpc05Tb0dNa3A0d2ZVTTQ2cUVaTzNHZ0lZVFc0VUhtc1NUUkI2K0hNdnlLYUlDTWdsSmZROHR2ck9mb1owYk90TUpLbWpUd1BheGkvb1NSN3FRQTVvVmI4WTB2a2hkcGlCU2VZbWh1M1p1RFN3c2JMQU8rcXM3Yk5NT3A0UnJmZUgxM2EybHhQQ3prdTRaVlNhQ1hlQi9mZDdtRzJNSndFTEVERzB1bDFFQUh4SEpjc001b3ZJNFBrWVR6WU0wMkEyZXdOaWtFMDh5empSeFlZU24vT2FzaGU5MmJKa0s2THMwWG45RnBGSEtDSFc0eWkxU1pkK2pSbkhqL2V3LzIxbGRIdzFhamNtZm9LTWl4ck9PQ3QvbjJTR2g4YW5Dam9CU0VNM3lZK1dXN01iVkhVM0VkSWE1ZWx0Mks1Mm1veUJrdnVNdTdKV25DajhNdTlHY1FzVU5Ua3RjYjdTU1JVUllaRTB4Y1BEdktDZXdscXR4aW9ldEVjV3dERmtDbEVDMXFseEhVSFpic29oQjJnbk5na0pWd3FIUGk3dTN4VExsME9qU0Nsbm5pdTdMakFrODFacmhHbnNFelRMRUUwdXdLTUxVdUlzWUhTNTZVdER4Y05Ma0dIT3FJT1A1Nk9YVU5MYUxRTnN1TVZWYkQzOU1TODNTNUxVS0RnWVBmc1lPT1lUeGt0cGdabjNMOXB1WWxMakxmMFVvYzVTamdoWmJTMDBiWGE4QXNDVVJkei9hazhEZHFZZ3F3QVpwSEphRzQ0WE5WeC9Ed2NYM1R5ZU9scTZEN1hlaWpqYlE2cnkrUWN0b2tiZVNxNkFza3VvNWRlOUpuWnVSQ2ZlUTVKMTR2NTY0QmxYZmxPVXJnQWkrc2FsNExDYjlHeWtHVTJsVlMrOFBGQTIzeUpNS2h0OWl6NVVVdzBKMi9sdHFhN3dJWVdhTEdVZzllZkR1NmRXUFFxNVhQSjFZVnVlL29KUjd3Z1RBL3pwQnB4dG9yQ0taUUE1b0FGMmJ4VTQrRE5kdHN0VFN2Und5emR5a2Z5dlNzTnhhZGw4NmQ5SVBhTzFtSHpXZ1dRM2NPMExHeHpaTVBHaDdxS2dxUHdESk5hNmVuRzJ5cHlqTmhYYnBWeS9MaW9GZXI1TENTQ0ZjV0hOV1dFZzI4SmpTZVF1Z3k1QklJZ3loNlhna3JVMlZDbWZnWWxIUHFZbXZxL2VJWmtRY0ZjNTdyWEhteXU3bzhPNUpkZ2xjVkR4SE83K01hakY2bTdCVlVDekFZV0g1Q3pta2J1VWZ5NWpIRE5ZVzVoVG5IQUJFcnJXcC9RS3JyODRtVTYrTXhrMXkzWWZmWlhGbmh1OWpQV2RPMUsvMHVxZWVPYlEyTHdEVjI0RDd2NitRTGxDVk9rRXEwN1JhaGhJbWExZEFrU2FVR3hZV0JsbXZpTnN6TUVxcG9MSnRnMTdGZ1RJYnJ2VzhsSjAxWlNVOHkySEE1dUhIdHgydk5GdWY2dlhZemsyeWJFbWVwUDk3a0l3NnVVdEJuMDBjU2hCOHNJdkcrTzhSclU1Rmk1UzVYNGNtUlJtMHU2cEMvcVFJL25sMkk2bHZsYmVTVUpjK0JYLzg1VGtZMGZzTEczbndiSXdVVnpyRWlBaThuZHlXOURFS2pZYmJvLzlOVWFKMjFFZGkzS2sxcVlwdXZZekpub1hzTXB6aHlCaDYxYWxVRDNkUjhHd25teVo2clRMTjNGY3VJc1JyQ1N0aXRUekFZNy9iNGEzM3N0eTR3SHV4cHdsbjlicXRSdmV1b21kVEpVNzNBRkZQOUlPNFBaZ3ZSVmxmd0VrRDE1V0x2TlBya1pSK0RLVTdkNGZ6Tjh3Tkh4WllyZHFVSWoxYzZTOW5kZ0JKYkY4RHRKbE4rOFFRM1NzOTR5WktPUVI3WkxwS2YwREtuN1Iwc0c0M3lEUWhBUVFWSFRwNmZ4MDR4R3l0b05FWUdHQmtaSFdhdGc1YlA4Znpuc2pFOFdMV2g4b1MzK21EOW9OVW5WRmtmRDY4endCVGZCcjgyUWxyMzkvdjkzZW54MXViY3VOOGhqZEwxTkhWZmFtdEs0UjYxWWpwblV3anVlZWRDSXJ3eS9YZ1NuMTZDenpVa1YrU0F0UEZSUmhzdi9TQzJRMEYrMFRZVlBReFRTTGF5K2c4KzJ1M2NOSlYzUCtHQ3NrM0hnSzBpU3pzMWQ4blVYZ3NRYUFPU2YzaFhmZ3lQTXFhYTRlYXZ6Z0EzVlBmcTJzRVVwb1RDTTZjcVpjNzRoM3J6MURNM1Y0Qm9RWlBOOE5LM3lKTWlTMkl2dHhXSTNENEo3S3FVeDRWMUdOVkhGcHhzK3JoWmZWVFZES1p3VjdyTzVESC9haENSNjFFeFdXdHJOQWZneGcxUXhZUEwyTlJzcGJzbkczYXh2ckZxaTI2aEdBM0tQeWJXcXgwYkFJOHh1TDh3NktRU0hDZkJObURTc2ZjaXVqWUhBcytZNGpJY3BpZzJ3QzcvZHBHY1REK3ZJdldRMHVpclBLNEF0YzJDaXBwVzA0cW0zdDlkWW43UG0vdGJ5MHRxNU85cXpsRmlUNzc3Zmc4RE5DME1NVlFIZEZ1Z2NxY0xFMGdlR0w0c3haYlBreXZxbzVwV3dsanZpdlhvWUcxcDJDOW1HMUdpd0VOeVRFamZiTkI0ZkY4OUFxSFNQWWlDbHpDMUVMcU5jd2FhQURWdVNWUWJiMzdtVktuUXQ2d01mQ1ZobmZXTzdWdHRMakNTWXNDY1R0THpPRFdmaHd5MzdGV0Nad1h0QXYyOHpORVloT0RQQU9Qdm04aUVyeEJQSG0ydnpZMjc3VUt1RVhuaVdKaGxwaGY3SWd3MzV2ZGpYTjBEQ3VrMi8yT0RCSHB0anVPbENWT0QxZ3hJYThwOXZLL284U0dSNXpmYWlQZmN6Y2RHNUttT1VkVHgxclRTMHdZcXhiSUZsQmVnOHNXK2l6QkZ6cEY3RExOVXk2Tm1oKytrTmJySXRwTkYzc1NMZ2oySDhtcWJXekk1L1VVeHBGRXVMRmNvNEttd1VZbTFneWlkKzcwdmpIUGRlRjJ2WHhzTjRUek9iMFFFcVhxcVJUUG5lUzhTSHBnQkx2Tm9kRldnMXQySFpQbjc0RkZRSWFBSXpUN3laU2JGWXc0bUNvZ0g5eGZtMjAxS3BlTHc5VGw4NGVjM3dndmhCZzVLQm5nVTFGeXNxMG5qSE1QZTEzU2RGb1djWVB0ZDRPb2x1dXhlS0FYTndjSVZLakEwR0dIb0ZxOWVOcUlpdTh3b01NMTBuQ2NWMEJnTTFKT2ZFTUZnWFp6OTY1Rk9rSnBsdHVrOVduR1hNM3RBZVpVZS9EODA0VytzNk0rQWFhYThtKzk0ampBSXF4UEZjdjRYSitKZEF6bSthVld1SS8vV00xYjZUUWlhTTNEbjltaEEvSWpIQ2NNODFRbzg1RU5QQjJiL0xWNEZhVFdYb1A1WGNJU3Nlc21MN1NxZENScGI0bnh3TnFpT0Y4VUc1YjhJZkFYbUJxM3RLREIyWUpsbDUzVk1vQUJGRUhoRFNKdUZJb1NvaE5icFV6WXBlZ3lETlUyUisyREZtVEMxQ0N1dGVaQ3hsajR1NU9mdUtST01JVENMcjBYc0EzY0QvUFByQjlqWDUwU09WV0ZBUCtJdEhsVjRLLzhZVDNlZjRYOXY2c1NyZGV6TytTWHFBc0pKQ25tTHVkaEM4MGRma3JKeGpWNUdGOG5oQ1RVRVJaYWlRb1phMXBCYm9GRmE0eGxLZUNyOXRWQmdBc1Q1RVJPdEgxV2YrOHpueDl2TDArUDExZG5KektEVEtoVU9JNnVrSVljakljSU1hQTQrTG1MZ1RvMmdvUTZWMUE1M2pCWE90Kzd4bC9NVWQyNFNhVmtNNEZkU1lubUkzZTNRR3MxOEN2dk1RNC9peUJJNmwvYS9VR2RodUJqVTlNa3J6SWFhdm9iQ3pTSXdkWDBZWk15TE5qNjA5SVJWbnl2WUsrR05ON1Z4Zng3Ny9TNk9YUHRpYTc3OWlaZ3lJWFNQWGNjdm56K3hPNDlEVzhaQkRyYjhvQStMOHBVbXRWTDhwREZid081bE5SK0c4ZnpldjZRdnk2UERPRlVUbGJoTkptQkxqQ2JYYWlhbEZVNmZUR2tsVGtMSENzZ3JiOHEwN3hVYVFOcmN3dEo5Vy9wNk12cDJmVGM3UEY2VXJCQ1gxS0pGR3pYdkVmT2xpNlJldkF4ZGhSMTdFMXVpRitha1piQ2lscXdaaG5EYjBHUlpUbVFMaG10Tzk4Q1E1ZDBPZ3R2alFSSWRJaXloaG5vVW0yaHY1WjZrZTdGRnF1V3VMZklvY0t3dGVydHdjMG1sTGxTRE1DRjk4ZWVOamxkd25lVHU4dHhzcHhXVDJhbmN4bFJaWjMyclhvazVsTklGaEFMVy9oSFdTMVV3bnIxYnJjU2Uyd3pORmdQU3dKTFlSYUl3dlJxcWl6SkN5dy9TalZXVnVEVjkrK25xWW05blpsQXBUVDJuYVJ4bFc5NTlWTU1oWEV6NUFiN0VhTlphTVFFYVhTdVlXRWF6UmZjVXBzQ1AxNzI3SHFkSVlyU05QVGwzVFF4bEY0V29GRnBEUXpTR3FYVGQzSlJqZUJKOWh3K1F2OGFoMWJCRlNnWHBWYTA5RUZvTExiWVpMdVRPTTlJVWxOcm0vM2RZVVpFdFFiSE1nMVJJNmZ4RHpiS0hOazRUbVR3UWZka3BiUy9Oc3JVNVRtZmU3RDVyOFkrS2tzNm5WR21oUW1KdloybFZBamNCNzhJNFY5cXU2aHR0WWhONzhuVU9YTjV5c1JIWlhJbkE5WngrcUxrMXVXOCsrc2Z5K2c4QStLdkh2YzhwL1R3RDlZdXQ2WkFoQUpNMzVFM3NoSWYyRVFDWWNHRnBUZy94WDVCT0cza1YvbXVubk5EaXpXNndOYmJNRGdZM1p0bEpiZmtrOFRvVzRqazZzYUozeC9jdHR4eFd4ei9hLzFFY2lUK1o1UDhoYUVvY2p0OU13YjlFR2g5Sld6ODFUWjNEdXYrd21hOHZQYkc5Z24vU0kzOGFrM0Z3TzdQdjhyQnhEc2ZpeDFGL094Nk9zL0p4dXZNQ0N5Ym0vUHpNRCt1d2oraWt2RU9RYjlEeGxXTCtkTC9obmMzYmJyM1p5SmoycjUvNUpjeTY5LzgvZDBrekh0VzB4N1RCTnd2OHZUVDFiazNIaWJibHl6VytsdXpvbmU2Mzg2V2ErQm4zb3NLN1lrYnpjUW5IM2ZtTXhZZHhUcjRaOVA5RHdML0JHVy9oa2Z3bjJWOGg5elc0bEpqMXFGcjhqNlFmWU5xcm11TWJVQk45cFZmbi9pVmhuOElNUkxwTXM3NXZ1ZkpyZUNmZkEvbUhFQmNoNjZPODBqL0wrWGVJL2pkdTN1ejJUMlIrVFNaK2w2WFZaZjQxbFM4Ujg5UDdEVkFBYi9nRkxFOEZ3WDR0NnFNQXZyangxWEtpd0F2QXZyUUN3TGRCbXIwNU1hNXZ1N2xnVWo5MWMwVVh2M1Z6dzJFbWIrNW9jWEpwckxyNWlQeXNFM1J5Ylpwazd3emE3ajM0OU9UU3VRc3ZSSXBqVWFLMHBLU1MrTnRLY2I2bzQ5UXp1WFZuc094amRtb2RMeXcxS0lmVDA2ZmhoTjhzSmN1SzFnZ21Ed1d6WnVMUWlVL2QralQ2WkN0Yk51eWJ0VjZndVMzM2JweW85RFhyc0ZtWFA2dVhTTjU1YVdhZW5EdFZKalhUcmZKVVZsZzRmaXRKKzRuWVdnUEtKMUM2N2x0bDVKVVVaZVVVRkJic1I1Z2ZVZW9OUlNXcjNvdGh6YTJ4MXRWeStJTUd4SitTdW1oQUZsTWF2Q1k4NFBCYXF0eFNQZm51MGpSSHRPUWFCODE1Y3hwQ0kyeTNMcHB6NkxaOHFIaW5TbkJXUHA3ODBqT3J0VXpzekhvLzJWTlFKT0loOGhuYzZlcXNWNU1uQmtoUnJEd1ZrQmdhc3pQdmdieFUrSUZqSWFBdUhuQW5SYXVFY2VWVlQ0a092VkhpMHMwQ2puQnhNU2p5VUtuVHREanJhMDRaRnlDZld3L05TcnlTejQ0RFp6MVk4cGxtUVBnRzVqZjhoSGs5WXpZYnl3L1VONUFXM24vQXZ6WnQ3QnljWE53OHZIejhwbndSSVZORXhVeFR4V2tTblNpWmx0a0lXSjRWL3lGUlZsRlYwOUhWMHpjd05HTmtiTmFjZVFzV0xWbTJZdFdhZFJzMmJaRzlEVHV3aTdBU2lWSTEzVEF0T3hIQmNUMmZ3K1h4QmNJZ2wxZ1N3U3VUMnluc1FRaEdsQ29VdzRsZXlsQlB1OGxrTjVHOURkM2REZVN5dHVhRkRaU2lodTdHcGdhb29oMEt0Sk5EN2EzZERkUnczMkI3VjI4UEhHaHJod09EN2VURDVJbVFHZ3RzY2lzelA0U2hLTFdaTWtKYWtpTjRQUzJVbVNDMTkzN043bXpvNjJ1Z2RJblA3eG1DRnJWVGV3OURLTGNISG1qcnBReXFzZ2x5eTRRcmhvZW9RMXBabnhXZGZCR1RYSk5xYm0yZ3lJMFFwSnhOVUJaS3dwTWhpNmVwcFY3b0ppekVxYzJXZEtCZUJTNEVLNDZCNFByS28rQkJWOVRyT3daUUhDOGtTeUhBK0puOWtwckdvVUcwMjRVSkZCRmxKWnJqRGRvcCtCUUFBWFFIeGtXQURiQWRIZ1pjeDdUZ1RHbkZVZHRNeFFaTm1qN3JkZG9vQUpLaVRyU3Nnc3FxZkU3K2twYUcwQ0NXOFZYdzBkUnZwSFhIb0dLSDNPUUNBQT09IikgZm9ybWF0KCJ3b2ZmMiIpOwogIGZvbnQtd2VpZ2h0OiBib2xkOwogIGZvbnQtc3R5bGU6IGl0YWxpYzsKfQpAZm9udC1mYWNlIHsKICBmb250LWZhbWlseTogJ0thVGVYX01hdGgnOwogIHNyYzogdXJsKCJkYXRhOmZvbnQvd29mZjI7YmFzZTY0LGQwOUdNZ0FCQUFBQUFGVnNBQTRBQUFBQXJqZ0FBRlVSQUFFQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFCbUFBZ1V3SWFBbVhGeEVJQ29Lc01JS0NHQUUyQWlRRGd6Z0xnVjRBQkNBRmlGUUhnMlVNZ1JVYlNwZEZCb0tOQTdZWTJKTlJsQXZhMHBHSVlPTUFwb2UvTHVELzg0R01NYlk5M1hoVUlpdXdURTFGcUdrUmRUUTdXR3NxV2xlYSs1bkZIVTlmbC9hWFpXc09YT2RvNmRpMlZEOTdDNHd1V3RGK3FtbUlNMW01N1hkZThkanRzUlVQdGlaS0VXUG9LYzZ6alZINEdEdFZSQkcwVWJENDM5SGJlQ3dJWWVDTUhtUDRDSTE5a3VUNjhOK2MvNy9XOXBrRXFRUnZDUllDZzBtZ21KN0JMRUFsNEJXZ290ZnR6OHovelA1RS9iMGY1K1I3YllkWngrVmhrelFwcE5zNm9wOFBCQWZrQU1HZGtDY2NrTERuejUyOUFack96aUorbW90TG15YXBhTnFZVktKVnFDblNRaFZiaFlJUEtENFhCaE5qQXJQZm1ESlRaZ0lULy8rdC92L2JyTHozM2ZmZS95WDE3cW9CTlVDcTFqUnBpVXlreGF6UG5PckJXbEFrbDN0TVFZeFpUa0hxSUhmb2szdGo4L1J3U2I2WmYzZGNTazJoZFBDbFlDZUorSW1IWWg2UzdmL0NjZkVXcy8zN3BDZ1RwNW5OUm95TDJCZGluRVVGb3ZEa0YzV0ttcTRhTHY2LzVhejAzOTZrNlp0VUtsUFl1RzJnU0RZNDRkZ3BzeE5WcUtlWjk1aVFOeWU0d3U2dEtEb2xjRWdiM1NFbFVlcVFicHZldmY5WE5YV3BwTk1wTWFYVWFjaGszakcxRFdzMllBVkdmK0JEZ25TVVQ2UklsWFpWemVHNWRsQndlWHZ5OWxRMnZXL3RiY21WVm9GTTlrcWVjWm1Qcm0zM2dQR0hrVGlIQlBpbjVuTFdibHBYTnBLRnJEQmtpV1NGZXZiL3htMlNJZ2h6Tko3MHNkRlhIL3cxRU03ei85NmVZUWlETXJzQ2Y3VkJ1Qk5SZ1B0N1ZjdjJmM0FEOTZMMjRsNXd5UEtlZ3h4ckhaMWJqenVYTGhxWHdQdjRJUEVCSmtEaWtzREtTNEc3T291OFFJRmFCZkFDUVZLMnFIRFd5Q2tsQ2RUdFdkUjY1MHl1OXh5anpqbUdvbkZSdW10VEt0clMwNGNJZmZ2RjZyeDNDM2RzemRhMElCNnJjUHdQMGsxYUxSZENNR2hyaVZVRG9JbEpyTUp1bnpkekp1SzBPUVhjNWtKazZZVE9QOXNRczZxZGlTZjlKTnQ3ajBHV01JS2lsSjJBNEwreVh4NFFmK3BqQU0vL3V0UDhqeXdDM1lVK0dmd0I5ZHVMbmpKemU2RmVnSWovTDUzNC8rOWdYWHpGZnhwNDBXOEo4Tys5RXlDdXR2Z2w1TE5ENGJKVzRhT3BXbkdYL2ZyRTZyS09SM2p0UUlDaXJXbG9hbW03cW05aTVydlpWbkR0ZC9KSitNNm56cFRJakR1TnY1enNqSnN1V3ovYTM5MmViVTNHNitWVC96NXAxMThxeTlrY25Sd1UwUWU1T00vaWN4bXUzd25MWnJQWW12VzdWZk1nTFo1ZCsrSDgrWTdvYzBMQi85enNaWlRJQTVFTGZRU2Uvb053L2luTjlhZkF0am1UaitrQVpmbUJRdk4vUjJ6MHJIVmxSSWdXSXFEZEJCQ0I1QzBKeHVPQnBRQTI0R2RmVVlWUFU1S3g5K3YremVjZ0txYXNuWER1SWdnSEhnMlllblZrNmpHaUV2Ym1EWk5oK2c2UDRacFlFelg2YkI0T1VKNmJ0OGlYVTVURjF6UC9GZGVCT21JcFRtbEQycE9uUkRKczlRbHVLWUVlVkpvM29QWlhnVm84alhnakVzbG9xYUNpZ2szVkI0KzFJZ0I0bHFTSXZaZHlMN21IdzFIaVArK2RXMjBxUnFqQzY1QmVtZndtQm8ySTFNQjFhTXdvSmtvWDVOZ2F0SDVwQW1RZVpFSzJwaFdFWHpGVEhBNVUvdWdPNnIybEJZQU9qUGo2bjZKdW1IQWJBekUyUFhXN2szT1liSjBDdmI4b0JrZWUyd0dxenlsRjA1eVRTaFA2VXBHY1FheWxRVzdSWXhpZmM4QkdneWhWY3lkbUtoRVdjVll5TVNydVVSU1VtbnVLMVBKWkVLTVNMNHFNeUVJVnVZdzd5RjdMYlBRWW9Kby9ZU0Zzc3VJanM5N0JhWnhWYUprRE1OY3NJL0JQVVUrcFNOd3pla0s1RXNvR0laMHJGMC9maGFvK3UzaWExdmFsUDBLbm9wdGZoNmR2TDNFajNYZW0vV2xVaUliZmgzSldEako1NWxoVUtpdlUxK2dWWjQwZ1J3cEtIYXdhc0pCS2ZYa3VSS2Yyb2xUcHRXcG9rQkMreVQwL011b2pzNHFRS2VPdWFweTRaQVZ3U0R2U2llcUp6TGZuZ1RPWVZVQ2o3d0p6eEtOblJEeDlvWWdlaVV5L2N6dHpKSVRmbHl1L3AwdEozalZ6S2xxZVJRVnh5VEtHeXVjb2p4TW9uNU9vZ0ZPb2tOT29pRE9vbUxPb2hIUG9IT2ZsMXdBTithcStXblQwQmVqN0h2bzhac3d5bkxnVXEzQWNGbFRHQ1ZUT1NWVEJLVlRKYVZURkdaVGdMS3JtSEVweVhxaXVJSWNpcWgrWW1CdlJhb2kxTG9FVVluVXhGMnNBSW0yczM0OHpOZ0lZcS9DR3BYbEhTMW4xVk1PTVJNc21GOVhuaUkzN3FublloNnRSU2FYWk90V1JaRnFBVzVqbFZ1eFZZU2JJSUExQTVYVVd4TC9RdmVOMDkzU250akM3YU5rR09JeVlrMFRBMzRnczI5RWNjNVlkNURFeUsxQkdkcUlLRXA5Qll3YWhqMjdQeCtXNW80eWFFVmFtTkxZb2Z1cGdHQjYrYUprQ0lhWlNLaVdibTU1ZjJwWUdBVnhKUVRwMWRhSGhZMUdKcHpUQUt6bmZjT0F5L0JRbnkwN3pVNFJsSVBvNm1IT09VakVpcHNNNXEvSjBZcWtXaVFWL0R1RnBvNVNlTnk0RTR0a0lTM1dvN0xoa1Z2aGlLdWlRZDR6UytVQ2hTeUxwcWlic1ZhMTRJZldBL1crejFrek4yYUFLV2lrS3hLOUpySFFsOGtMeFFkMStpSG50MXNFT1NYVTdLZCtGRG5UaUFRd2dFRHpHZXRZUkphaFZSeU1iR2pnVkU1VXVESGdrczhlTlJtb2V6OENpc3lwR01mVU9rMUJtdmQ5Z2Nlbk8waFJkVmszSm5jaytLQllzTitmd0pzREZwall3dlpSYTNYKzRWeE1NaktoTWQ5c0h3WlF4ZGc2QlhWYmlKeTVncmxwSkxRT292R0VEUERvY1pTZ0V3ekN0Y1BEd0RzdjBDS283VDJ3ZmpyOFFZd1NDQ2xCamJ0WHBqaEdmZDB2RXlVWU5JdG1nOHRoVDlIRFY1YWEyeGg5VWdzNEp1YmdmeW0xdCswc21QcE9vQUFPRDdmSjZET3hjRXY2RHVGYzBHRXBGc3RXY1FrZ09odHV2YVV3QU90T01RV2FUcERseno5T3A5MEkxRnBPa0pYTXZOejMwU2pWV2s2UTFjNmViV3E5WFl5TkoyalQzeGFiQmw2cHhPVW02WXU1TWsvZFdOYmFUcEIzemV4ZnNOblh2dlRIWTE1Wjk5WE13SVc0aWVSazZLa2N3SEpXTVJPVjkxU0l2cTQrcUw5WkVGV3VqaXRHb1ltejFzQzQ4ckE4UDQrRTVUZVRkeDBHZ0RhamRQODdzdnBQYk5NUGMrL1BLanhRSS9oOFJ1anpDOWwzQU1WbmdaYURzRnBCNGdZOEx5b1hPZjZEQ0ZPQWhMRTZ5ZEN3d0orRkNLU1lYNjRLNzRLdmcrc29jZXdES1d5bHI1TjM5ZnJDVlBnYnBLQzU2ZWxxN2oyNDFsaEhNVlRCcWk4bGlPMDZXVUVGdWhaaEl1R1dqSXhNM2hVcjNaamRtSk5LUXRGaUJyYWFiT2xmR0YrWTdhc3JvZkZsRm5Nakg0c1NPY1g0VjN6TlFPcDM2RlpPVlZ4TFY5Y0VtZjMvL1FITkV6Sk1vODhNMm03MHZ2TEpEeEwxc1ZibENiNm9zeGZ3NXYxd2t5R3UweEZ6RFlzS3Z3VHhLekJHcklHcTdVeTJpY2lGUnJpdkhDNnVLZUF3ZVl2VmJkYi9qZk10YkNCYWdIYnIvOE14SkxBL3J1cW1DZ0VKYW93bnhxZGtqQnlCOGk5eHQycGZIa1pqdEkweXc3T2dhNG1MNUZWWGlRRm5jUm80UVl1RVkzMUpOM0ZxT2VzaW9tcG82ZmpRTVIxMEtxa1FPM2JxbDYvejVtS0dFT2xveUtPampnNk84elc4RFBsWU1XSUpNVldZY2ZnYVpDM3RrbWsySmJZU3RZNUNWT0E3SjU1cHpubXJueGN3bDA0dVFhU3JTbTFMaTc5NUVVaVBoTVZGamhRT2ptWVlTZ0pVZkFLY2NuN2hhcjlhN1I0ZjcreU5yR2hMQ1M2SFZIRWxXT3BBSnRnK29veWE4OHdoN1RoY3I3Q3lGQjVVSW5OSlUyRmtzaU8yNUJnNG5PbGpzL1lhMGZJQllDQWppdThPN0ZJcDUwZ3lsUytESEFPY3NrdWxGODZLWC9DNjZLRTFNRjRiM2lFZHY5bzFSaU5sNGQ3R2VXWHlabDdpVzJZeEQ1ckJtcHdGeW4wWmJXT0VybVpFSVBSeTNGTkpldVVPYzdCNEJPcGFTZVFiVjNIY3JrZ2xrYUJ2N294OFlha1FOb1RXc0RnbFRPWlRub2FyZ0VZVk9SYUY0MzBuRnFWNU9zT21abmo2VWtHckw3bmRKWUZXK3pUYzZSaWN3QUNSVFNGdzRYL0o3YVEwWWEvNUhYQU85TWk5UEdtVVd1OUdwa0piRHB4RUtXT0dSbGVjaGVDWndYRnA3RDBqclIyWUExOGZJR055dWpxamNqU2VTRDh3V1dsaXhUQ0hzZEZ1Zk9Ka0R2VlZTbHZkdzM1Mm04ZjhjRmV1OUhqTHhDNndtQmVJT3ZLS1ZubmxlYzdmSFY1M1ZleFNRSVU0eS81LzBxZkd4RW9GVC9SNzFwbGlsNGlHc25vYmttaVBKV2JIaWpQN0hWK3U5VmdzUnhwV0VoQytOaHBIanEwL28yYWpNczMvaUdNQnpVOWQ4VjZlSDIrbkZyR3plbENnUmxCb081MWIvYks5dE5wem1RcHd6eE9QWHRVUUhVTDFHaVMzUHkxY0JhVE9oNjVKbUYxZUlLVXVjeExxcXpCQmlxQlMwcXRHSTZkNFkveXVDa3dWbHhEM3hpbGtUclY5RHZzVXFTd2RYNlgyT05OeXdNb2RtT3dJc1pIZjgzbzZDUW92a2JvWFF3bTR0Y1MzT0FvQ3RZa2hMUTZSWlVTbWsySHpYN010YTZucVZDWVM1VG0vSkY1d0RjSmtGYmVkUEZZaWlLbWlTdVgyTXRBc0I5VG84QTJPdHEvQkdpb0JjNDd1ODJ5MjR6S283UWcraktiait3SlNZU01vMWt1RTBQb21nTWR0VUJmZndnbFN5cDNDS1hhZFJBMDFnL1JaeXR4NVZTZkJBSjF3amdIcHcwSVNkaFhOQnE1MWNNS2hMSitwM3ZGYWlpanFxdUJYY3BoYTNrMkxJTFdvbU1JWnRjSzFBUFk2TVp3QjhIaHFRdWFOdEVvaFRpbTRXSnFUV0Zlb2c5UWpKZkNydFNIS01Belh1MWhnM3pOM1BlZkVCR1ZtUG9ZVFNJZ3dqQ3RZYWJsRmltTUlhWEYybnh5S0EyUDA5T2tRQ1FwT1AxMElLd3RDUlN1QVJsNGpieWVrd2lOeEdYcmNRUG92Zm9JbER5b2tTSi9rVi96ZEhJTXFBUGJpek50WVFHbTJVUzdVNy9VS2wxVjR3Z1VyS2VCZ3pETWl2Q21GVndBWkpYeUg2S3FiK2NUckUxNGlQVFlZU0JPRElnOEtvNE11a3NTRnJqYy8xQUNWZmVTR2hPZUdmaTRLeXJMeXdlQlhzOVdrNnlHVTV5VkVNVEc1RkRTWmVJSXU2MDZ5WFpoclJodjZkaU1aTlJmakJvKzNaTXhpSnI5SXk5cklMSG43aGJvSmhKcmVuS0RkcVJXUmFLMFdMeFFQYnlJa21rYmp3bmRwV1MzMFh5dU4rWUtrNWY1NkdHVXNzZ01qQTVCUGlhdThtN1NmVTMxamszdllYY2xscVR5R3RMUW54TzFaYXMvS1NHN09McmVYenJQeXVLaUpUcS9jTkZGWXU2NnZ3K2xZUk11VjJWVmRHbjlBakVMd3ROR0dKa2hmUHR4R3AxVmg5bXZpZU5kUHVIREdOaUxFdTd1QVgybjJySlFMNVdiSWpDTDJIaGEwYWk0K1gxOHMxWEc1eVBJZm93UGpCS3RhY1FTeTlKbml4UURwcnlLNXF6N3BSd3pSaEh3U3NmaG13N2YxM0QyOENZN0s1TWEwTFB1bjRXRWMxalQ5R2d1eXMxc0N2YXBMd0ErbjVpR2hwWWVRbVlpcHpCc3ErWDQ1bExpZGRvcWN5Q3UzV3RPUHNSbUNyOUkyZHpRTnQvRGFKd2RlY25GTDFraHBrY25vZ09zT0RpVnZQZHNxUlZBZWtEZFdnWWhiZWltVkN1WGhqVGNnNE5IL0NLQ1ZTYUltelF0NTAwZ2ExbXNCdWI2a1paVWNqMWR6QWF6WG9qY0Jqa2p3ZzF2SnhxTmxBR05Pb0dROUp3MElkbVdPcGFUbWxUQ2ptRkJDRjdSQU4vY3daU3A2d1RUTVR5Tlk0V2pDd0MvVWtGUU8zVy85UjcrMGdzNWM3SGhrU1IyNWtyeDVMZGNweHR6Q2tIOHRYOXdQRDJmSzBLa0wzNnhoNWdoYXE3MWxjdkpTMmRlQmtaWVdPU3FmMGhab0ErdFVET2prVVBZV1ZuaGlwOCsvRTZNdSswWXU2US9PNS9RQzE4ZWlxM1ZoTlJRV1dyRkd2Y0U1ZVNuYnJob3d5ZzgxR3ltOXRCam1iblZFT3BrYWoxR3A1NGpvZ1pybnc0S21kZ3ZLckZiWHhjNk1SWGlNWDlxYlFpWGpSU0N5WUNlS2pHSzJ3anpVbmJXc1orbm5aek1hdnl3MXhJU3Z3Y2Mvc3VlVG5xcWg5RStYMERUZTJKaUhqaEVsQjdjc0VnMFA5anoxUWNTQmFZUDVQcVR1ZG5ZRDVvVS9VYk9wblVDNWpZaENlWERaMFU2Q0xpZGJFMmdIQW0zclZiZUkxbXJpQlRCNnI0bDNadmVRUm1YZHJITS94RkI0TERGNjdDaHBCM09MV29DUnF5bnllOFBrd1Z0bkZvT0dORlYwQ0ZwVmh2WWxURXBpNUlYNUlzVnpjWGczVUk0b3FaT3NnSHU5cWFUOURzRnJzVGx5QldvR2x2d3VKSTFlUjAwaWN6Y1piWm14bUhNdXd0UjV2UTYwc2ZnK1BUUTV4NW5PM3cvQi9WalhNbExTUzVORWpvTktjR2pJS1JrSzY5RTBlMjc0Y0dFUGo3WkgrZ2E2MU4xaGpyY1E1WDRoWC9VNmJHZWNURndTamhEYVFDeFFuNXRleFJxNWsxQlhma1gzNVZoS2RoRGdkRlN6ZmJ3U1NSQ0htWm5KU3Z6Tyt1c0M0MzhJS0toR1U3TTdNWE0rM2RrSHo3cVJ5bHAzN280K3M5TVVBVXNRWCt6bExoeUpvalNudEF3T0M1ajBya0Y4MmdoRGl2Wkp0ZlZYZFBXUHpqYlZVencyTHBhOC9ZWGNCSzJienhpaW1QWUdoQ3F0WkxwcURUazFRWTd4UWdRUmppQkZDZWV4UllJNy9zcUF4c1NoMlQ1MitSdXlqM3VZNDFWQ0Z2R1ppdlU3aXVtNmxncU1zYy90T2x6aldtaUFDKytoOGpzK0pLNW9qOWIyMmZHMU1kQ3daMzAveHRaSzc4OFZyMVh5WTE4bHZKSmxIVmRXUk5UVVlZOU1RcTBDL3JOQnJrWTUyMFdMZk05OXNQVm4xUk02NFVDdm5iRjlpVGNRVDJjUjJaTnBlUVE3MkJuVWhMS1pOTTVMcDRnK0JBNEJSemU3ZzJGQjdLY1JSYzRKbmZzd2NXWXYvaUY1Q2cxM0NXNHIyUkVZOUtNYkU2MHZCWW82RDFBeUN3NXFvVWJUOWhrellxQjhJYlNZMTIxTkJpTTJIT0NXZWIwdi9CT2lSeEtzbE90NDRVZmRsMUFMbC9ITGpXaCtBZzNCdS9SbS9HeWtWQkVMd3p4a3N1d2xMenNkZ3liRTh1NzV5cDdtWk1TclhrTENoMmUzVFlMTnQ1YVozeFZZdkxGb1kzekY5Zmo2TW1wNmJZNnhvSzFFR2hWUkJvZXFzVnJORnNMb2tKYWpSNU5oNXQ4M0dud2FSRVRzUG1nWk4xdGUyZStsaCtKTUNnSUYvOGU5ZEtDMm5kQkxha2wwKzk3WElkbVhveE16RVk1TVVKejh4WFZYWEt4bHZ0N0ZoWEtWZXhqYkZKMnNyQlh2dHhLU0REcHFpZ0pOTDErUU5hQ0FyWjlaMG01TXlac0NxODhTT1FEd0Rzd01jNmlpM2djMm9iVFpkTHRwSXlsai9YQVpqS251SzhicWg1Wm9VRFVTb3ZaVUlUQ1BtVkpIRW85SGxzRlg1cmxNcU5zbmVEVWdJMXJEaFM5NUFwMlQ2UDZTN0c4MERiUFBmeGtCdllJM051bVNaUjNJVDhHYnhwV1NaM1ZmTk42Wk1wNVcwVlYwajl4N2dxZk5YQ0ZUMTEvYllDMTJwTVBjZGxBUk5KMm1yODBpeXhPb3I1UlJtODlOVnk1ak9wU2pUbFYza0JkdjBPSFZNektpUitrTkpRVFRMRDJUSGcxNTFQVlcxWXl0blhyeWdNNHV0OCtHWlU2MjVMa2g0VHYvOWhudEs4M3dwWFR0akd6VWdxcm1WQVV1U2Fvbys0Nk1jVEZtTFFSTzkxV2dNVlNsYnFJYnRUbXVwait5cGN0bU1DMm5nR25wdzZiSFpVQ05Yb2pENGk2VG1GeEdDS1AwRGtlYlM0VXl2dytwczJJKys2cDJhdGVnQkhiSmNEMVZHalZmbDBpWVJLN3VEZFp3Q1pkWmhqL3pTYU5SWWZ1eEowOGhLcFdtVzJtc0ZNYS9FaHk2Q2VFRUpOWWZYRzgxRXh3cDlxd0h2SXBGeS9NSHBMZVJQQ0JFVzhScW9GYXluYUkwbHZlVGZKVzBuN2ozZ0RVZWtjSWszM1A1aXg2cFFqMHQvZ2JXcVgvdHNuV2RleGNVak5zZG9iUkNlelYrOEgwc0x5bjJLWStVYzk0eFQ1U09CcVNDV3A1a2FJNW1PYW94MHhxQ3hGeGJoYURhTi80ZnF4cDV3V2JSR2dqb3NpTWZ1dGEzVmRUL3dJbnRNWmcwc1V2NkUyVDU4UnNCZWFvRXNWRWVnVkRZb0JOR1hHQmwwL1NHSUo5MkM1dVBtOHhlQlhraXNVS09zbDA2c0tBcmJoeVI2YWx5WDFwQmFvMG95bHgvais2dFFwTWI3MWZyV3ppWFJjMGJ2a2dGUGY1QUxZYzhzT0d2dCtRLzdXZHByTmxBOW1XM2xuZG9URkVCSHBpWUNxQkxiOVE0TVpBVmZvSWJuWkpuL0N6ek5lWnVYelFzMndxeFhaSjRIM0V6aStLUTNPUElwaDNidGJQdENpVUs0UndUV0h6Tmh4QU5FSmtuOWxZcFVaQ2RmMG5waXZHUFdCdFp0UUJRa1JDaDlFcVFQQ3QwZzdLUmVpZGkwRUhNRWlHL25vNVJSYzQ2alVaYVhvcmtycVRlOTU0OFlmUWFSaGMzcVBXUzBQZlN0RWI1ZWR4KzRLNWRBTndFRzhqQWFKd29KMXREZWtuVDBkdW0yWldPbmM4bTlJbmczNzY2Nkw4TVNsTXk4WnltZ2N1ZTYyc2xLWHUydXNrVWRTMHBqUDdKWjUwUmd4NGs2NDdVR3lid0RhL2xsaHd4UUs5TGo5ems0elpsT0tOU3pVaGxhNVpmOFR3d1BLRWNhbFZjWURReWxIYkNjSENWTFdUR29ISVdqK0c1TEM2K3RZUFdSYXBZbkV0NTQ3Y3VYYjlPeUVtb0t4aXN5eTZtMDcyTlpsYzlUdjNiNUhhcGw4VExkRDIvN0QrOWZXTUlTdll4eFNLRlhtbHMzdFBOTlg4ZEMxRWpwTHJJaUlKZFgyVnMrNU45R0RacXh6bmtJU1RJSG9iQTlVdTR2cjl5cTYvWFBhSXlWTVJQUUM2OXdRY3lXY3hyejkxZzVuRkxGS1NNVU5jS0VaWjI4S1YreThIdGthZ0lXSm1aUmlXVVN4R0F2d0NHUTl6Uy9Ddy9uNXR2a2JvTjVkMTRhUnZMYllzblhROU9TM1dVR2JYZHZ1d1dTRUsvRDBLSDBLZ0pCUE5JNjZaYzdRSExZSzMwME9WMTJocFdiMURZRGF6d2xialJZeHdySGxheDM0M3ROczlxUUducElHZG1GdGxCKzBXdzlheHJ1cGNRSWNZUXpYUzVjR25KUEtpcFAvczhKNFl0SEc2U2F6WTdHMW1YVU9XOTNxNlcvRzZtNEVaNnFnT0tOenhpMGdPZ0t2OGZycE5OejFnWDRsNDJZUmt2Y08wVktXZ2U2M1lrN1lvUitjb2pTaXBmd2dxbmE0Qk9NYm93MlQzUlFCMWtzS2tMUXRCS2tleUhoR1I5MExhS0NkSlJpRjBjbUVKTzgrMDRPMEtYN1l0eUxjNUR6aUNPRkkwcm1ENmk3bmtHb01tN2kxeHprSlhPMW9DcFMyeDMzaUJlc3pnSjZLRkEyN3hrUk9FQUgxRElzUHpVR1FmQVRMQS9MMWNET01yMkROVmJyTkJFVmpCUisxVitLclBqVFdPZExHRUZMREJaY1FKZ01TalJrM1RSb21YbTJwOFhDN2xFU210eEJ6S1pEQ1U5RFNjWTNsdjJ4SjIvZGNOd3ZNNlJtL0xQZUpQdHh5VCtHczMxdVN0em1HTlpFSVhoVHZld3VoV1BxbjFqUnNwWGYxTEQ2cUdpeEtUS3hnT1hzTXo4MVBXT1FkVDAwUkkrNE8ra0RzNEtBbFRpbmxTOWhIUTNIc1dCRjVJajNOWVdFQWNmbkRlSlVIQmFnejNoelhTeWdad1liOEdxY1RBUld0UDI3ZkJaTExmNHNzTzhNOGFwY3JsUm54eCs1RlVSWU9tRmcrWktWc0pkQUhCZ05ySWRZYmVTKzY1dEx3Q2VCbUxhQlRiMHR0Y3Nwc1RPc2hCNnRPeUl1K0NoYjBBSEtoYlBWalFvTUFzS3pUczd0RmdnTTF5cklaYUNjaTBHQVdkcDZpVWFidnRYZ2ZnVkV1eHFwVVVBZnE4TmtnR2ZWTXh2QVMxaURoRCs1aERUa1Y1TU1HZkFGQjJvcURMaWNZUkp6aGpTVy92d2dsemk5SjVUcDF0aVpFcmlhRzdEdjVJN0JMRkVUTDA4c1d6ZVpkMk1oeklVMjdYZ3JSTnZZcVJBakhTZUlvSzNsRk1UemloQjU3TFE5c3F4M2tIOFZ2ZEw4c1IwWkJ2ejhSbFJXYUJ5RUFtN1BhNi9IMjVGRS9ZWEhZZ0VFbmgyNVBvWWdJamVDeGdSbFFLOFN1TE1HUGZXSlhZZ2RDWUVMMzRJWWRHOTloY2RtSzI1YXlNNDVpd2tLSEN0Y0tVaU1nL3o5NzZISUhwaFZTM21raGdPc0FlSE5NR1JoQ1F6Qzc5THBUUmthRmMrNGxyZ0ZyTXMzWWR6UElwM2dwazM0RjhHZE1DTk1sWnRraCt0SlN4c1ByL0JBMHJYT29zSXBrZW4za09RTnNxSVRFdkdlMXZLS00wUE9KRVF3YUEzeWpFdCtWOVVOdmE4VDg5QitUUjVzY0dVWVEyMVpzUFlaZmJncUR2U05jNW1UcG0ybC9vSGJkbmwwVFJPQ3RTQzNnaFdlcE9iVm5zdnlJNFU2cS9EZkF3YWhRQU1ucUlRbGlZL1BvU1N6c1ZCOGhLL1FwWjBScytudUNjakE5a204QUlYVlFFWG1YOFJ4dWxHRXJIRHB1dFd6aVpuSW1Qa2loM2h0TkNrVTBEaTRsQU5HSmxqWmE3N0JkYjNwOU9ETUpGZUFzVzVZWVdlZjUyRUZoT2k5T1o3YXZFN3FEMUE5cG92ZDQrNVNPVWRYZVhvcFZQSW53MWVFL045K3ZGNlM3TmN2K3NmOXgyYVhqbi85NmxmL2Yvam8vei8rOVkrdkgwUStHbjlOUXYzUWtmMlFpcmN3V3MrSk9mMWkvNnhKcmIxditCR0ZuWDkzYnFrUWwrd2ZMNWs2akRBbXpwN0JTbEtJVnpLQm9TVThvaE9NZDRBRXJ3VEZKaVJlWkwwUnZQcXFvUmdvNno5YlVsY3hYQlhQamxaOXlNQXB5bG1sN1crOU1tMTVBRjAvNUlFRzE0R2M0YzNPcklEdk04bm1NREQzd080TE1BQmlxQWlEYmgzOTcvZTFCYzc2RWwveFI1aHZXRVhzc0J6L1k0QXBoU1RVZnRJTDMzRG9ZSUZ5aTYrMy9PZHIzMGxaVFRrZWxrSzhUQTN6OE1lR21MdW5wVUkrZVp1VVhuYitJeFhaZHdUTGJaQW80MzdIYXdRdmhsV0hVTFFUblRCUm5UUWxwbDl1endaNVA1TVRsWFZKbzFMMDg1bmpPU1VpNk9SNk81MCtETlJ2MjlKL2hzUUk0bVM0Szl5MTc3bENMVUhrcm1vcTFPcTBoVTIrQUNpNWpqRGlicjJtbjNjZzNBM3ljbjNmT2wyQnlzenFveTllWTZmUCtWaFJTeUFwMzdWVldSK3V2RlIwVEo4MCtRZEZHRVhjcWdRYWVDVk1GRVkwQVhuTVBENmhXa0E0dUtDMmRiN2dUekRYdUpWMm1scEt3di9OVnYxMlowOUZsYmozc2xpMlNubEdKdmw0dWVieW5yN3Q5c0pwOEFWdkJVNEhIaVh1aXY4cUlUNkxDZXkraWhYWHJrcGNIOGpMbGwzR0hwR2RsTkF2aSttUGpuL3VoVGtWQ216YnZaZHVob2QzNEVtYSs1eTdOOUo0MytWeHJmV1I0RWV4WUlRZitpditPOTluUXJUUGdJVU1MNFBmandnRDNjYzJGR3ExNFpYUkxvTTd6ZG1qanhxSUkyYmJFVVFDSDg0c3ZWcVYrZ0VDTE9tamdILzlpU0kzOXNUTUFKWUlkWVhlWmVCNTR3djNQL1R1akJaam12bW9kM2wxcHFGK1Zlb1RXdXpkekxSdi9mYTZsNllPNDZib2ZlV09oTnV1bVprTzVxMC80dVNQeW5INnNOZHBYMUdWRGp2Tm41Nk9PdDQxam84enVuYjhMeVFxejlWZk1lbWtYNkhvajE3aU1oZnZzUVhzM3VGWlZyTXRVSXVlLy80OWVWUFZpaDhhNys0MVFLOGhqTmVnQktkcGxydXlxWEF3ZXQxL0Y4cXI2aXVLcFIrOS9IaTEzdnJJRE04Yk1mTnNmOTllWkw3elVuOFBkdHN5aWZ2WlE1aGJHSUwremJVMzR0RjVIR3NHNVg2NUg0OVg4V1lERE1qdUNQUHdOQ2h0UDh2UElRVlZ1SnRNWGNNN2ZFbGFpWlN5cEc4UVhJWWx2NG1MeFVBaTRqb1A4eVJhNEk3L09ocGFqdllEQSszcTlTRHVkSTB5dVoxOFJIS0F4QjArRnpBSG53OXkraVlLOVpxbVRtOTh5Y2RPeGtxanZ1cmEyQkM3ODZYbnRXNEdTK3ozT1hsTU9mUjZWTDlUUHhqVUZmeW5Ld3VRSndXQzE0cjFWYjh5VTZsVDI2THV3bEN4T08vYm9tVGRKQURwOXFJaExJU01Cb0dXVGkrenQrL2dyZ3VaYUh1dytOMlIvS3BWSloyQ2VSYTFMdUg2T1d3NmMydkhhc3hiRlJCUWJJaW1tL2Z0YlNFdCtvSGRFM2pyS3NOMGZEUXZ1RERVSUwzZVpVZytzaDE3Y2pjSXovUXQvNFJkWWdwWmlIcXJmaGRJcGtWb0VGSVR6NnJHOXVUVG1hQjh2d3Q5OWhaZ3RLZ2VXem5YNVg1U1B0cmMxanN3UzU0L0M2eHRRVUFDaE93MTQ4U2dmcHBkdXk5ZWx3c3hJWWxHcFpHQVp5TEUzbElZNmZTbDlyY0x2ZDdDL0E3ejFXc2ZPY3R0WStlOUhHVk9ad0ZudHdtcStoSHFsSEo4czcrMjdvUFlFT0h0WkRLU0xzQUFhQlkwVzdVNm1TNGRIQ0tVcFhTeUtGdjlMNFIwQm9EMloxNFFlUnVNclljenREYkRHdnducUpXL0d3SGczbndCUnU0eUl5eGszVmwxRHNsM3BXMzlDRVA2TmlqRytYM0hIZ1lWelFESk55aDBrbzlBSUlURHFGSkdZWStIOVkwTmdITWRCV2ptMUxPbEo1T01TNzYzSUJvbU5UNG1nOS9MZ0t1WGliZVpyTmd6UXFxMlVmNVM5UmIxUm43cWdZaVVJcXplUk9Xd3VhTSthdkw0b2MyT1VDVnVmdlh1c1U0K2hlM0FxUEp5Uit0R0pyT2tKUjgyMCswTTlvMnZHWE93cGlxSGpmWEtJb0JJeUlibGtBUXlPRWVacEgrZHVTcnZtNXk5eGVtejJjUUdESUoxV3orTGdXNjNjTVQ0WmNtNk8zZ1pQdTMxUXhCemxJdDRKMXdHdzY0OG1JV3NPNjhPUTVxcW5NalhKYzBDSk1ZR1pWQ3d2azBNUGdZTUhqaWcyYVpkWkRBMTNUK1RYeHNIc29mMVJLTEVtTlBwUjBWekhaSXNRQWlvaUV2NmsweXZtQnhhcnE2R0RoeUFZTldVZkIwYjBxYzYrYk1paG9GTE5SdUVlQmJVYXRMZzZldWtQSjQwZktibkt4RHdQV0k3OFZoTGlJWUk4TENIeklxckhJVmFZUVNNbDNybnVQMXNhMTJhVkVOUE5lb2pVdTF1N1M1MjU5SVpEZUpHcUs5WDJncHNlbUtVdjFLYUZWZDdwbkJJWGdlZ1BDUWFQbmYxaWpoNUs2dkt2NjMrRTZqZDJOVGdxYTF5NmVkMStRa2w2OUFZM0grVnl4VDFMMWRPZm5DN0lqOFNQcmVHcHpuR1psZ0NUcGNpT2VFeUpEM2dHblhoUnh6UkhNcURCa0dERUkyRGtoTFhoWGsyTWdnWTBEank4a1BvQzFjRGk0dkdsai9qSjBPWTlqMHZoZlFVOHRhVlR1SGNIRkpnTDlSeDNWSkd4ZTd5bllHaWViTjRoYmpOL0k1bUNGMGQ1R0dOOXJrSkFDb3dwT0NzVnl2cWtyZmZxZUduWTBJblgzUmRKMXptUG9kN2dxWWc4RkRQYTVXWFJGNzkwc0lDZ3FLSmIzT1R6dHJMZWtyOWZFRXpZaG5MMzJJMW90cC9LOWJUWm9FbkRJTHlLRENxVGJHZ1FaQWlJekJjUE0vbkl6QUlMdTJYTHBHd05KUHBhWmpWd3hFYkJJNWJ2RVhwOGhWemEwVVppL2UrZTRFQWtPcnMxTG9aY0ROVXJFMUdIN0tYNnRVK0EyclVTVURValJjY2w0T0lLRUkwMkxScjZQWm55bmFvVElhd0s3Vk56RTM2dVZuYVUrTnlMcjBtNHNCREFFVUZvTUxvb2h6MXFhNUhLbkUrMmxIWFNSWnRtcGRqNUIraytNMmkxNFd5M3lneG5nZlJDSUZoQ0N5TVFuS1pIeFNJdU1nZ1FKSmhpRHhVSnlhRTJ1V1dPVFlpQUVMa3U0cmcwem95Q0J3NDl5Um0ydDhsbU5pM1pudmpkRmtxNlI0TURJK0VBNG5ZSmowSDI5RzE3c24vcldtenZWc2I3SHJ4S1p6NjlmMVZYYis5WC9Od2E3U3NSM09VOXR3OFdWZHlTdEcrb2FoU1MzMHlmUEp4NE94MGZmVzlERkpLUCt3T0JsWmhuVjFBaDNKU3VWN282YzlsZlJXbWU3ZDNSZ0hNS1FiUFJDamRYdTBpQUtVeXdLaWZVM3dIRmpxMmVIa0RUOGRDUG4vZk5BdzNNYzFWNmZ1WGJUZC9NMXJJWW9Ba0U5cjJ1TnIwOGI2VkxJNkUyVytaRHVsMlNSMFZoVnJENXczUDF4YTFueFpPY2JRVEFkZnFJcWVreFQ3MW1uQ09xeHJNampQWStlRWE1ZzdIL1p3Uzdpa1duQVNSK2VMWHhicGxkbjlpL0k2QTNuVW5jSDRILzBMRXhTMFVOMjhzT2NuczlSdWovRUNEV3YraFJPaHEvN1V1eHlmNStPUUhHOGFvd1V2N1dEeSttcDZSZm9PNmIvM0tkU2FrbnRNc1FLektjMWxjSFpMY2I1Z3dOeFRnMS9EM0UrQ1pDT1JESURCVUJ4ejJjZ3Yrb09JcTlUSWNzb0xCbUsrMHQ2VmEvZjEyN1ZZZCtQbmFSM095emZIdlBSc1c1S2ZFUGYxWFBQRkVoaVZndytWZlU3Z2dDS21pTXRqYldKUGdudjh1WHJkRTA5NlM1SUpPKzFoUnZNaGROcnpKSHVFNldiekJhKzhCazVCQVdQWkZUa0J5bURCRDgrNjE4azJnZms0ckt2MC9ReWlxL01BNEFQcVhkTHZRd0IwaG5NbFFxZ09oMjFydVZyZjFOZkZnUVE1aUtsdW80RGZwL2plM01VQUFBcG02ZHdEWWhRbVluV2dSd05JdlN1bkhmeHc0OVpOalBnVHN6a05ZWjk4NG96dExRdmt1UHdxUEhOMWQrbEpaZXRPUUpONjFqUnZpYTBTeGxZRTFCdUtUQ2xsdzJuTGJYbVh6MVR1N1VxdmJ1REVHUm1NUzkwdk9aOW1zdVRyZEdsajhTc2tGUW5sTnhhWitoSDFVeDF1cW1mUnpLOGxjVklyOWpCVXEvd1dJanM5REQxd2tZZ3o2bVUybVoxSWN4RU1ZcXgvQjkrcUU2ZjlqT3dGajRkcHMyby9pV2hkczFKY2JQVkJyUHUreThrMzlpUGdsL2xJRjl5UlA0SFZDdkgvam5ERjhTYkkwYnpHdkpPcDNrdGJ1akZNaHh2TUNMczFKektPc0IxR01oYlFDbE1SMWxRN21xNS9oNC9qbGJ3ZXV6Zy9NZmxZSm81MWlJUmVXUWl5WWNDVVplQnpBL0ZvaGp6bUY2VmcxcW1IZ052T3RacUFSRnhYcE5GK3hUbVdISHZrcXZsQU50SkNFR0xaUEE1TGZMbHhyUTNtSTNaUDVLUWNCeDkvNEJsaEZCWGNsRlE1OUhoUmlUZUlNUUhMVFRFay9LY3A4Z09hdFZtZmluNTFvdkpibENESEdPRlB1cWh5L3U2dnBYblkvd3Q2dVEyc2gxZzRVMXhibTJoczd3T3ByZkRjV0h0UG8yYnhlZzBDVUFOU3FGSUNXdXNkUmRmWWtXc0tDd1B4eHh3VDQ2Q3ZnRkpkNEZ0d1A4cmxNVzE2V1R6UUFaUDFWMDdmcVBqd0J0eFVpZ3J5MFkvNzQ3QkhwZ0d5WXdRdGk4NnpKOHFnTTRFMEJvY1o1NVBTZGl6R0R0Ri9TYnhsUGZiMURyekZ3SWJ2TGovSi84V3pRY1lmejk1VDhNZ0dqUndIb0NuKzVxbTNUOEthaFBSejBGNHdMeVIxaG52RGdVVkViaUdXeEE3eWFGbUEwd2NtbDZCaEVrVEFJR2xETXFoNlpZZTBGamFqMjY2K0RNTlFrU29PNmxuWmdOTXJPQnVUUnRYdW5oemdJeFVOU0RMLzIwa1ZNZCtDclk1eGtJUUxicWN6eVMzd01yWm10QlFFdGh2My8yeVRoditXRElNcFlHeHovOVNxdmUvUWZsYUNHTDRpYmNKdGk5dFlBa2NnNFVmWXFoK0Y3bFpMMTBPNVN3djB5bTM5enE5VlNGZmlmb3A2WGhVVmJtdThBTCtlQ2ZGeTdPSVpIZkc0NEp6OStTbmFxMHBQeGpsNTE3d3JsVlhXNk1XVWI2SHJSL2FLU1NiSVFTcGYxZTdTNnYvdVNIbWdXclBnUTArdEIzUjVwdTlRNXRTelJxcmVwL0dnWWl4UWZua1owQ09mN2Q5b0ZLV3pFMm1USWR4UW80dk9ETVZaU29IOS9xa2pkcUNTeUJGYnp4emlNM0dMVHFlNk81bzZySXlML0NZWGp5SUJaOVBOS1FXVndwK1dCZXk2a3RTQ2Z6VnFxbDNMSGRETWhCQnV4eG1mQ3lrQ3JKNUhBUTJUcEN5RUlMbm9kNFdkdVBFUGxZSTJmRFdxMXdVR3F2dGdBSjRrWXZsY1ZsMGMyTE9qM0wvWDZOMDhxc3JhMHRVMEcxcGpHeWJyNTFXMGNRQVRiYnRvSEMzRXhHVzZpK0ZuQUNJTHk2OVFGUjJ5ajBleE81ZGVaYW4yQXYvbHVHQ1MxcDI0NmFsL2puTGorWm9qbW5Lenl5NDhBY0VHQnRzZFhEWkJuTEU0UXdqcXNaUHVoVWJ1THBWV1d5dk5zUnMxV2lxOWMzSXoxOG4xUWFUN0c1UFdTRnh4bDRobkRFOXUxUnhZYm9uOXFWV1d5UDdIL2lvWHhDVnp1N29lVzNLYTI0VTFUNlN0ZVpYc2VQU1h3eFZ0RE5TL2tkK1JzYjFrZCt6RDhiWVdnaGk4YWU5WmduQXpIMXllaUlsbHA2dktaNy8rZ29WbncrTGYzUzdyUWdLYWh1cndvTktQZi9lZlZITFBLaUY3cG1PWUhISFNsQ3gvekM5eXBWZHB0NnJuRU9UVi9qV1FwWVVCd2xMOHFPRHo5YnVOQkdPcS9PVkxxS3k2aUhmUDVtMUd4WUlhbjRMOXV5d2gwQ09qNXIxSDJ6RU5OZEU2S2I4ZkZVeEo4U3R4Zy9zZU1VVkRiTDFBREtjRWdTZU5QUDRBa2dERVFwakRIdEVsejJFMXFpOGNzOElKNTAzblRJYTRVRTk1OHZ5UmpEd2c1bllVTXhHSUpaeTZ6WkdTTk1xd1hsWmZJOFhIT0Mwb1o3NzdickJ5b0ZFd3hXTUtwRmNySTd6SUQrQnBqdE96YWVTM3FmTTc5SC9OL2EvMC9MRXJNUDJzVkUxS2lqekQ4NHVYb2RyNXgreDJxRGVyTlZDZWI5Y2dPMS84dXgyYTVYbkE5eTdLUGZuajc2UzBiRENxejFxQlFFNHU0Rk8vdU9zM1huWGYwdjlUNUhlT1huOHFVNEU2MlJZNWYya0hVMDdnUXkzaGxyZGZPU1J6OURrNk9MV2g3M3NNVlFHZkViWGRYMWk2SitHdjQzSmhkdjkrd2Y5WTBlM3dITnorZHowY3VBeUVLY1d1VHpsa0dGNWFVbElXdGFTN253bncyNzRCUENmRmg1VHV1bjF4UFVqbG5mK3hJbVBYSFdybjNrUU9GSTF4aHRjeDR4SFNjNWVBYnBKeGIxdEZ0bGp6MXNPRE44c2twWmMyZUd6Z1FlTWVyeVM0aUJjMGs0TzgwRitwUzFmVDJwQ3NDUm9uVWRPdEdOZkdtZmZ3eEIzamZEd21kbVJITm9tWStkazRsV1E4UkVFKzdjQmthdkFNU1Vabzc5TWdrN0FvRU4wdG93MDNITXlDRkNYTXc0bFZPZkRUR3FieXFMMTJmMUhXaDdWamV4UDFaaHVtY2tWZG8vUDU4azFBOUxOU01maGRwaVZSK1o0anNhTjZUYStCNEdhaXNZTDZ5Y3l4UXRLTE0vcGtKcDZLMFBwY1RKQm5Tcm5UTjN5d3lRVVJOQ2lvSHM1K3pIcDlTSmhxT3ZmRGZVUFpFVFhzTFRCSmZWL2VCbkV6TXVKTVpCS3gzZ2hDazFYZ1pvMHd1SWczREJmWkdMaG9GOGJDV1RYRzhaaVFhTjQ2ZlZ5SWNxNVEvYWVtWmF4MUI3M1BMOFBjNTZSQXkwTnBzMXF3Z01Md09pNlFZVDVKQkFCV3hJU3RjMDRjNmoyU29SaUlKZ0QzMm1TamRTcmwrYXB6bWM5bWNJQkdXSTJ5VWhVangyTTBrK0pUUGlFOUt3T2F4Qm8vTmxQRzZGUkdPK0dYWlBKL3NvdENCNk45SjB2aWVRV05URW91NGE4c0tJV1VWZlhMdEFjVjY3ZTdvdWRVVDF4SjlPRmJ3NXpNZ3FnRU1YNzlTVUh2aEcwb2pXdHp1RFhZKzNiMDFzNmU0Yk04YjZTNGlRQW9WcjB0UkY2cjllRDBJRjAza205djArTVZqTzk1Nys0dWZLRmlIT1B1Mmp6eStobFRISnFTei9URHliL2FDdlJodklKUEcwN296QlFJK0huWGVacFZDN2tKUThvd2QwZTgzSEdJaFhKdWM1WlZZd3RlT1Uyayt5WWJLNzNGNjV4TTJnMkV2VGVDNjNibzlBRlM0NXBVVFFvY3c0VUY0TmtYM0w3ZDhTK05aTDZtY1dHY2ZRZi8yYkU1WmxJZHRYMkk2RmNPbWdWWmFHT2N3Nk9DeUtKeDhSUkRCb3dWR204cElxaXlyeDFraTA1VEdSaFFtQ1AxQjVkVERVOEw5UDNHOWNXOE5ydDVuN0Y1T29WS0NOYStWZGpieDg3V2RyaGZmcERmTERaaUY5VVgrSm9QY2U4NHRiM2pYY25Bam50Zjlvb2dPMHBLcjFIcWlBYTk5dm5xMitNd0pZYlZRVVBFbEtwTW83L3BPVGtlc015TmpDTmZ0K1Q5QnAyblFDVjFSbzhIVFJFTGwzSmdXSlFYNUFkVUhLMTQyQkpJMU5YSkprMmx3REw4bnI1NHp4SzlKWWRaL0RhVEtQdUVVV0ZZWTdQd29yVUJBK1lSNlU4bTVrK1ZVamxGeHV5TWM1cDVuNlc4OGQwWWZjMW9kT3JWVlNVemdEaFM4K1RBaFNIVmc1c0pYRkE2UjVEUnRDNFFmdGFZRkJCdVVDdXErS0FwWHYvVEtIRll4dlo5V2xTdnNOcU1HVGJ0QURmemwvVUxEVmRmTDF6YStzeHFLOHlWWDB2bSs0RXNTdi9DYWwwOElxMlZkTzd0L0NxZS9iaW1vdmVkcW1VN25wL09VUDhBYXhiK2piR3l1TmRCQVg4Y0UzZTU0ZnFHdjRLcEx0STRNSFZFWEs2MDR5UHpDc21iOTQ5V00zaDcrRlR5QzE4MGJENWVjKzNSWERrYWEvNm00dHJldE5uVG5rd3orcmtjSjVicjZkN3dVbTJGNTdxSGlVd3pSbGVjK3Vpam0rRmgzRGNNYW5pQUhJZnRxVGo3VDJkZ1FmdlgwQmJFbGo3Qjh0YmNzd3llNUlQMTVmVEtlNUdHUGJ3c2VHaGZtMlNOWUk0RFhPbTZ5Q3ZpZjZQSldMOHNBNHJUejYxdHZiQW16YXNXNzh5Qm02b0d3RkN2VnYwa21NYklETU9nYmdkTXVMeHNzUWxldktvUmc4SkdBRng5bE1UR1hEd1p2ZnVpeCtxcmxqc3ZhWng4SG9MK2d0VnUwdXhhTDMyWEsveXdYSFFweW9EUHNNMUNFdFRCb3l4WjBDc1k1bW5TdzUvc3hXakwzM21pVDJWODFLTnFCemQwR3lhemd3a0ordFZhM0VORFBRcjFvWURzYnlUMFdFc2tUTklBaGJITForSk13QU5LZWswQVhDY0NpMVVhdWR4bHY3Z2I3S1N3SEVENGlCQnZBY1kxMHpoWUpQczFRR1lCRUlYQVRXUG1ud3A4cm1ETXFOYmg4eFNEWXNzL3JrdFBBRVg3c295dWgvdHRuRUJDQUdmeUFZMW9xRWVQOGZxYTRINEZaVHozelhXNzd4MUErbUdPeitMME15THlOdmFjT0cwWGt5SlJXSnhERVBmeHU4UGdWNFdBakR4VjZ3bDhMVVpoQUYyZ0tGR2ExUnVvaFJqOENLNC9BSlNpL253blNQTGdUZzNWYW5lN3dRTzg2QTdqNjFBMFpRZFBwdkNNVW1Mb2R5ZVZWUDJFWE5wSE9NQzh1aWVvWnVueGpSQkVVQTVEZnB0bEZHY3k2TDNyWFo4OXhLOWltN2VjT2IzMTZOekFmZy8ya2hrRFE1YW9QTzQvdkF1ek9CMU50ek1veVkydXFrUXRUUEREM0pVVElnUyt0Zm5hd25Jc2hWOFZKV081emF4VUtYVEZMOWk1WUpPS1BIOG9UczVNNkJubFZSY2ZLd2xzZm56RVNNQUVTaTV0cTFtWTBQRDF4Z0gwc1BSeEdLUTdFUXhSUHFNdGVDZ2VNUzNFZGwwbUpQbEhYS3djQW1wZG1VczN3SEJXa3hnYTJiQkczMHUxNGczTFFXbnpiWUxYcDYydXNXM2xUUFdzRkxSSkM1azZMNnNBbVQrUUFoZlZScXAyWEJoa1VDaVdoQnZaTTFxdTZ1SFVobTBHOVozdG1FWmhzNEYrdnc1aWk5NFF2L0NTWi92bUIvdzFiQlBWeURsZSswTjYwMGZVblNEWU8zd01tbkNDSVQ5WVR4bVovWWRtYno1NHNWTXdVUEhxZ2pmY0F0OEg1OHY1M2p0d3FPVzYrcmhFdmVPTThSM1NIeGViYzd1RkpEOTlqZ2JJYnI5YU5qTWhXUFYzMEJ2K3pibjIvejdzQnpGWkhEL0RTdmEvWTdnUjlRUTNlZDdGdmw2eC9lTDJpVnVhMWFmZ21FQVJoQ0xhR21KTFk4RGQwMi9UbVpzck1oVEVxQnpOSDhydFduUHVteWEzbklXQkNxdlJwS0tmZnppRGhzK0NGTTF3WHhDL05xcEtLeEF4YnFZbnRLVzlhK1d0TWZFUFpRTWkyeTVGeVJrdXpQYlZOM3RoRGRrUkQ4aldnOXNib2ljVnlqWmJXWnBpR0JuMjlSdHZTa21WVjM3ckVLS2cxK3crNE8rcjlEYldCUk1qZEhzS1hlQllITThoS0dhamlTSXhNUnE4STdhRFljSlZpVGJUemJoYVV2S2hsbmw5WEswN3pTUTZ6N2VaclpLeWdPR0x5cDJQV1JLUVJFZjBITThLV2ZaZTNjdUFMaUw4eEt2S3VZQ2lkTVdtYkovZ2thbi9kS3Z0emM2VDNYR0VobXdOaERNNnFrejZoT0ZRWTNTb2lvaW5GMlJ3RVh6UGprV1grZDZEOTJTbm9BbEpUSDY5eWxhNVlmZ2tuQi9idTdSUWVrTGNZZzhVMS82ZndrbEZUWlRZczdKUkljT1QxYXVwelQ5TWFhYnJCeDJBamFGbUYxOTNhZHpreXdLelFYVzF2VUxOTUg4eTE5U3dmbXlTTC9wWkFtSkRrd0Y3SzI5VnlsQzlQL3RlVG81bytrQ0pnZksyaDNkMFhCbENoSUFlRFZQS2FvbU5KQlN1SVJTUURaVzRHcHVBeTl0cjNwa0ZPeVZPSGlSTVV6L1dtajFma1pLdSsyTURMNjU2MnF5T2wzYmFNS1g3TnkzS1dEVXM3RXFBaWl2ZTJ4cmM4UTR5YURIVlpoR2Y5S2pZbUF2cW8rK2kwdWVEUFBIbnd1SEd3V0hmeDhmS0RiV3JkSmlKd1IyQnRiSEVkejhnN0wyS1Z0blc0dlNXQmY2Yk05ditkMlhwa2VsQ3RpeTExRkl5TFFoK3ZVVXdJT005WHVITUFyQ2FBSGFuNWkyNWMwM2NWVHh4anR5dU8vUGQxODd4Q2hxTk1LRW94bERBQW9XZ25taHBsRWFsRHIvQ1UyOUZSZGQvcHhZbmUvVlJDVXJReXB0MjZ5ZnJMVDBsVTl1UCtmOGR3ZWRQbmVrT2tkWkRIU0cvNVRuWitENm4zK1cvMFV1ejIvRTN1dDhVcmRwNk8yYmNtK3lBZnhmb1N4bjRibDVaOVdQNWR5VG1ORU9XenByOUUxQy9iMWRWUGdxOHhzaDhNOTh2Vi9LMmw2N1hoMXFFSEx0S3N2RmNpRzUySERQOFoyMUxsM3p6Ylg2eTFxWFRqMVR6QTJRa0UrVkZCdE9CdDc5Tk0xVVpQY09aTzNHTDNPb0xXc21PQ0ZKc2MvM200MjhpMDNEcWpRNi9VTnRsdVd5TFBxVkN0a0RsdnZjSFFmVUtNeHJuTVNtajMxV1k0eXloT0Q4WmNPVm1HRHBlRkp4TDVnVStXM21MZCtPcnpobnlMdEVST2JNTVIrdEFydFgzRHp6TWcxN3lBREZobGdaV2srQVlSK3M4T1BHTFBDR0RnTW9GU3NqNTRoOEZSN09BMHNyRW43dTFsL1lRQzRuWlpuK2xjdzgzWDgxbEVDN1FGZXJrU1hGN01FRGdYM0R1NzhsK29Ndmp2cHF0Q3AzR0RScmd4dkdkSDBCZndnVDFVUlpVYXEvd092d0hZZE5FSzVTaVc1bUxJOFp5M2FOdWR6NUNwRFZQLzRFSW5uN3NVUWJuSXNYT2VYMVV5U1NxQVVhOVJZZjNsK3IxbDUxS3hFeFhsazQ2Wi8yakI0ekNxNmRiMG1LNTlUTG5xVlpQQUsyUUh6QXVmbnoraTM4YUZmd0V6VE1oMnduNVBkYmlNT1ZpVDc5SkYwWmZVT1RsT2tLeTB2c2U5TlNZakpkakRFY0NuRXRocEFiSjQ3TG03RjNWZ1lSK1dLL2EwOUljazk5c0ZVWVVMZW9ZcEhvd3diOEJETnhsWXZ2ZGxGbFhqLzlObEs1M3FReVV6d0pBVTYxcDhuQWVjN1ZZNWpqNFZOQ25pMjNFaGZYWkI4MlN0WnF2VERnR2JDNjEvYlh6Q2dMMU1pVzV0c3djTVBHYVE3Z3BBM1RCWXZjcnVBNTQ3cGFablNSN3Z4ZmpWMm5WVEdzdVhIVnVra1RTZjRDTFhCZ2Q5bks3M3IzT1dVT3IvcG1xOEdsNmtvRVVWNlNvc2YwZXI1UFllTlA3UkU4bzFDNkNUaVBqZVJFM08xekp6VzVPNFNNalRON1hLMWtvNlo2YithZnN0cnRUcVF4VjFIOVBkYUdEZHd4cFRZWnFNbEh3U3hocHhzUkxkV1lkZXp3dkNRbW1lS0w1NUlDa0lTSnhBcTVEM01iL3psaiswMFhXTlIyQnUwZDNZMGF3TENBYkFRYTJsd05LRlVSY0Y3QmRaekhWTmdZZTRvVXhMc0hqSDBWeW1jT1oxWVBldVNsejliYXF5S2tZVmpHYXpVMUZSN3pXYVpNNnphNURzR0pmWXJqT1h2TEtMYWo1aE44a3BMZ2lBODI2djAvWkc2b2JyZkVWaWdmV3AxUDBRb0xwZWZTdFpmNzFJcStXNnJyOVIxdFNUQlIwdTcwQXVraTY3NmpJNkk1WmNxVEhpOGUvaW5pM05TZ1l1UENnYWd3VzBmWk8rQXp5Zm55UlRlS1BXK28vbDE3OWppeVB2UnpzVnVTNHhRa1kzNFBZUFBpZ3RLZnI5a2JMS3VtcENqZlltNm1LUlp1SlpqUXhTM3hLSjNpdzhEd2dGSmJjREdvM1RUelIxUk1tRVlvdlhKNkRZeU15ZmtoVzdHZEo2cGwvdXhORjBueXpYN3hodWUydkxqZlpsaXl2WElBK2ZGOFQzbkdjMkp3cm1zRXNQaStPQmRwS0xqRDNxclpXZWVHVlJ0cklGaEQvK1BaMkNjWXlGa3pOYnB4ZjJhVGFhNnJWK3NmQm95anBHYWRiYkFwVDVUeEV2ZmVsbHk0N1o0bzkrOHJ1WnJEdVlEQU9zc0h5cmpEdjE0Vmx1cjdDNzVSVHo3SFVQVzBSNlVwVy9pV21kOHVWb0FaU01aK0tBVmtDeU9FbGZLZ0l4S2ZLWE5aT0Vmd2kxamZnTE9vNGtUVW12MHRCVEFtK1hyTjdyYlc3czRJcFgwQ3RCQ2hma0lFYitmWEFuNnhweWk1RFpZK2cxcWZiZ3BVdnhOekN1Qmg3R1NjZ2JTbyszS01SMFVsa0xOcjZkS0VLcHBpOUZ2bEpWM2dteXU3RHVyVU1QM3YrT2tQVjdxSk5ieHhBVlhtbmhKLytzbkNLWXg2KzhxNXVqTm5EdFZ5WTZHUGNzdXN3T09WWGZUeFVJbG8rcEZkODBpZDFQcXFNQjVXUDltKzZYWk1UTktKUkNuMy8zbmxEa204MVNKdys3NFYvNzZycUs1K1V5RUVZUGpCMXd1aUdZMWUza0JtOGdWcXk1YjN5RGJlODR2eElWWnlXOWFXQjUxWG05WDUwWUxKUSt1S2dLYUZseVhxamYrZ1lvWHg4K2RrOHhteTNJUVN6bCtwR0wxVFhKVVQrbk5mS09UOUp5Nk5nUTlRQ0cwTzEwQjJiZG55SU1xa1hjQnJLaDE3M24wdUdudlRMWDdJMjlxVGU5YWdhc25GRk5xdWlTVDRJc3grbjh4NkN4ZmxGeFN2N1Z4WmxXOTk2dno0TncvcU1GcHlFUUFNRkRkNjkxU2ZmRTE3RTRKU1hoU3JhbUpMQ21DNStqTVcrSTdIYThTVVpjZ1diS01xeVpEd0pyaG5qZkswbFgzQ3pCUVFhelVLN2FvdDNlRk9FY0g3dkxwVm45ZkxpcUZxOGRrYVpyVEtFUG9pTWlyMGtyRk9ZZ0NJU1o4RG56cW5wL2FiUzhQdjNTM2VHQXRTemYvdmJQVWVQSjJyNTRUNGN3U2FWTXNUSlFLazB3bXluNXpzZmVLWi9XMzBIeDVmYU1VSkJqc01lNVhEeUNQSGN1bXN4emxDdnlHaXhkR21QbTgyM2Q0WjN2N3JkR2YxaXJGVDMxKzlBUm1GMWRyMnZwWExLYlBEcFBjMDN6OTd6QmlqZFJpK2tyRVVhaWVwUHp4OUl6S0g1NTkrcy9EdEg1MS9xMWlPUDhtc2E4R3J1cjVYNzgvK3p5ZGZ6NjVxbjNkZld0WnhuL3ZGWmZ5ZC8wdTJlN3JYZGErRitmNnMxN2N6K2J0MzVJZFc3U016eXZmMm5uMXcxR3VzZlZoN1lRUXJ3ZVhQNEZKZGU4dEVZemJHZ0MxUXNvWndmeW5GUEZrQUFrVU9ORkpYeHpuTjFMRWhUb2ZFb1o2dE5CZW5sQzBvcEVEMFg2NVdpZ2puK1JucDQzdkppQ3NpdWoxYWdiaDhjbXkwNGdHeFdDRjdVZ0E5VVJvV1Y0VWsrdWVhVUFhSVB1UVM3Q0hNM1VRV29tWVV4VUl3cDRQQzNDWllxaTRWTWgzMGZ3dXJrYVVlYWg1VlJYdnMwcEx0aWpRRll1R0JGK2p6VEQzNklCdEp1SERSQjA0eHJLc1FGcnprS3FVaDE5b09vVGkvQ2V2bWhFMXdreHFRMVFBV1VXdDlLblZEZzBXTEdPMkxEdldiaGdkd09KcXFuMGc4NzhnTkg2ZWFCazh0WWdEVWVXb01BMXBLS0REcVhXenl1WWZNTk1WNHdMdE1EWlJXVWRSaGptcytHZzRaVEc5UGthMGtQOWdUSjhLY2RnZnFsdFU1U0MyZW5TUlNvWXRZMXFCRXRjY3JINnFIVjNmVlhhL1hLVmVvWGdCODBUVUFtMWpDbVN2ekxESTZrR1ljZGgyWm5FVHBHY2hYa0NwclBNZktoRjJDSzlHQllyeUdVcXU2NUFGZUNVcEYxb080V09PbGZ6REROYjhmb21zamZxRHVyTjlhclcyeFFNd3UwZmFSY0lWbG9kNE1DS0l4alFrajJVTDBMUUVST2g0MTBiWVB0c2VjT0djYldETDdyanEwaFhLaFlCQzlwSjZGUzRtTGp1ZHk4NUJ5Vy9IaW93dXJGNjY0S3RXN3ZNVDFSelFsTWNzcks5R1VoVVN1bGdSdXozbWtCNlQ2NmdGSFdhUFVBdnBmRk1rN1VWZDFYemlLc1RKd2RNVmh1WkNPZXJ5ZnY3eFVIZytld1MwdzR6WVBMcFR0M00rMGVJVzkwUmt5akZSVHNOWk1NMFZHVWkxTDBpVlNKd3NHT284clN1Q1RrVVVra3pkTHhKNm8yQ05Fd0ZORjZUVjZMRVdTRGxKcW5mWEpuQ1hvbEdGb292dC9ZT3l2eUxpU2JXOE5aNUthaDRhRXZic2hDYWFNVVZIVEl1ZmNIb3NrNDB2b3NxbHp3bDI3ZWpBSW8ybStnSkxvcU0yTGFtczY2UDFDV0pMb0pyaUI4UElqbmNPTjVJOW1MdjJuNHR0ZjExSWtlVS9NTEkyMUlpUUFON21GUUxrbWgzVzlCTUYxYmU5RGMwMU1vK1J1ZWlXMXdzSXRBcHpqb1o3eW9CYmZGVUZXSXRJbmN4VFBIc2IwRkowYTFhcEJXTURKV3ZzakVkR0MzZEw0bjJ0SHRYNlh3YkNQMEJKUVFtTzYwQUJ3MFh6MEdlaFFWbEFtUm5tZWhpc2x0UmxlUmNJbkMrZWw1K3piTGRZdTh4RnN6Y2M2cUJnVkxXNFVwNkZsbHR4VWFSdlNKd0tiVUg0VWE4VHBzSlEwek9SRHUxK0JTY2Q2MFRrVzF4bHd5NDVhNC84MVJqSmg0TFZNK3ppSjE5aVczM2dqOHYwb0RMcVo0ekVEQ1plQ1dHZFpydVp4a2JuY090YW9YVnJXK09zNEM2TzBGSTZxbGVDTTlzTWhRVWtuYkFPTGJoeE9ITGxqZFNYMFNHc3piRW5NVjNFa1VKNHNxTnArMFpJSS9qOGJRZEQ1LzNtVmk2amdIY1dpOTVHTzZXTDRtbmFkSURBaEdJOFlBSUVlRWF4dVVaRTZ4d3hTaGJVa252UFFMK0kvaWpCMkdKRWVtQW0wMW4xNGJzcy92RUNOdU5CVnRBcHc5ZDJvdS8rbkovWDJlNzVhd2RoRzY4NHJxOTJiY09wV0ROcmZnWlZacG5POWV3cVhRWWVEV2JYZ05KL01qN1FZaElZaU5Db09vYVRHbWhMVXRlWDVIMXpaVG9vdUNSVEh1Vk9XeG5WclNtay9GSTFYWStaYlRmYzZ4cUJGZlZRWFJlWFZnMXBmK2FPbHV3Z1BUaUtnTnNaVWJKSHFScmd0T01wQUN3WEtLZXUxV3IwNm5GU1JvektteXlSbDJMeG1WZGlPYVJSTGFESUtGRUwxSHFBL1JsKzdxRzM3VXNWWkJlclkvNHZlWExOS0c0NDFZODNPRWRmNkFYdHM0b1A2VzBCbWFJUXR0RzlObFo0OGFOWnZnNUZ1dFRKZVBJWUJ5QktWNmxTdWNFalhRc09DaEs5bTJBdlc2WkY3anRJMkJVOExLWHd3ZDM3N2pzNGZwVmZSM1pWQ0pZYzFqTlJnbTlSdWRrOFlaY3M2MzhXVkRUbml0U0Z6RTZ4ZDFYOEZrNmZWeENuVnNjd05RQ2Q4RjBIeFVoYmJMN3k4ZzhBenB3YldReHduNTMvdE5IMUg0eUtsRFFVREdJQ1V3Z1dQUXE5a3dlUzY1dWZYTXNWaWtnWWZ1aGFyYWZWMDM2SWx1a3VQd3hRMStXV0lkSlZ2TFZPLzdKMXVxakF2eDJ4d0NxaEpxc3BpQk5EME1yZXl4Wmk0NVZTdTJ5L0VhMXpseS95OHNHYXZHcEp2dUh5ZmROdE1TMkVKanhsRXUrKzJiTHZiRzFQVjF0clMxTjFJZzE3M2JZdXkrSjdNMStlbEFjV0kzazRkUmRZRW1pejdUUFZSYnQxT0xUaE94SkJlM2tla0lZWGovVjRjYkIzeS8xLy8zR01hRmg2VDgxU2xleWtFczlqdkRzV0k4eWdvRmE0bEtkT21VbG9BdVBqTEtBTzdkR2hqdFdOaWNUTVcvMWFrR0dubFBQd2lQektQUjROWmFNYWtJL0ljM0xWOHFTRjVUdFByMmlOTk96M1MzV3FhVXZ2NXVISi95VU9TNkdpYVlteG53MSsrMGlKZGd4YTVHYVdPRDBod1RyS09pNzlyb0Z0aG9od0NYS0VSNXRPRWlrb0J3OE13NEFlRS9CdlFiM3QvQkIwUmdOYy9ZZytNdUdLRTV5ZVN5RWlqMDNPRSsrOVpreVJHWFJUaTArQmN5QkVsdEp2RlFHYmg2OHNiVDVtNkNIdlZRcStlcFhGMmZLTkZ0dktnRnBMM2cya2dWV09QRVljQ1VxS2tTRVpCQkR2QkNFQWh0cEdqMEVhMVd2M0trTmxNUlo0QUtCd21DamtsYmNxZ21aYXQwWElFd3crOS9TSjhTT3owakpyWnNsaTRmM1UxM3Byb0N2K01ieUJpUGpoRlhzWVNBUk9NSmxXck5DL2NoUUR2Q2hOUXJRUURiWTlzZ01icjd6cWNmS25kclZwelh6alc0eWFjeS9oTFNWYWxTdk5pbTA3aFFFbFBuVGwyS040RTEvL1A3NnRRTzk3VTNwWkR5S3RkTnh5Rjg2MGQ2MjlJVnh5S0xtVEZWNmREQ0JoWVFWMUZUTXdXUjczUmxHbk5HVUxVMlRxalZXdDZYelprTDlaaWdqYVZpaUNiTjZva2hTU040YVlBTU1JdWpTR21yT205ZHJFK0ZnUWNxQ1lmK3pCcGZpZnh3UTZxL0dkRUl0S0ZIT0lXUnU0MEZZRkVZaTdlQldaQ0FYaDNiZ2dVcWxVTEI0MmtzWHNMcGNsM0NOT2UxNTlleHg4R1Z6eWR1bWZqNDdLdHFDb0dYdU9rVXRrZFlabHZNekFZZVprQW10YnZHY2txMm9RSDV2RElLOXlDeXllM1NMY3F6K3FDaDJpM2w3OSswOCtORXFuK25iclpkVElCWmp0cXdyQ0V3OGJPS1NEWG53MGhXQjBCRmc4cHlEWStRQlVjcElpTG1vejAyTXNZemx5K01DMmJmYUpUclJGTVFYUnlNVWdFbU1GemwzQjJhVGw2dUIvdWJUMTh6QlBmenhmYnRCUCs5UHRZUGwxV2dEM1lnNTY1d1hWRGdhbmZ0N0ZXSytCSjdISmQyWk5ucDVrUFJKdkFrM1ZhVXZMRy95cWI3NDgzRDFTRy8zeWhWdGpYVUo0N3NpK01uSVJoSmVyWmV2V0lJYzdZOUNYTUdlSGtVYXFVdFZURHVHMFphYXJJcGY5UWdRZFVzR2FNa3Y2eDRObHgrM0NIMDl0Yjc0K1A1RWYxOUhjeVlWREZSdTFxdlo5QjBlTktyMklKRFZ0YmE4Sk1iclZjRUsyQ3k3YU9jdXRXWWtvSkxEbm9USlRaVDRWYnhEY0NOVnVVc0FUTlo4RHFzcjRXRE44YkNaWlNIek8yYTc0dUdTMm93Z3hBT1hsQ2tEMEJrckZFQW1NUGtsOXFvTXdLbFdmbldMVDJlVldGUVIxTUg0N1NjZ3djTlVwZmo1WkZtRXFnajdLVFVLREh2MHhKMjFVYUFIcG9UZlFZQUpSaVNvYjRjckZYa1pkS29RaDlrUE1JSzNVZU5SeUNoMnJicHZaenIxS1VMTU9tUUo3UjlBQTU5SWZPMnV3MnN1cGdacm5FdXNnd3RaeHdwQWlHS0xES2lDSGVzaGtLcHBRQkFINnpDUGJmdnpKaVY3ZDNDQ2JLeVJrWXdJS2JYTFJlYXVsOWRrQjVPTEVERk5kR3Z1VmFsZE5BbW9IUjhKQldzcmRZbEl5RmV6bkl0bzJMY01QUkxDSkNjK01tbnFxR011MEJBS0svem9JUGFTR3BSUGVYU0pYYUR6cTZZS0VCN1duUVdnd3NKWFNJOEpiMFhuOXAyRlJjWkQ3bmg5ZHdWWDMzQ1lxQzVWelliOTVtYURrLzJpemk2NmRuYVo2bWtTSkFEclE2OUpObitET21RZHhITnNSTXptQWVYYzlsRUZjTEJQbTFqWDhrcXhlaU54NEpLN215UmNtY2lEQUVHeklhNG9OdXpoZkU1TWYxQ1UzR1M0VXNwemFHQk11bU5YTzl3cHBlc2FqQTZYcjhjcG84TytiU0pIcUlyYnVrSWE3V1VyY2hTeXNKWUJpSURDVWZhZ1ZlcUV4RFg5d3FPRXdkU0U1a1hYSnFSUWdob0pKVlNYVmFuUXNtQ25oZ1k5dTJ6TUE0cjdYY3ZRZlV4eThnTmJrWUw0TUJBMlZYSmRLQzFjdWdobE0ybWQ0d3dIeHltcmRiY0FHR0QvdDhtMGw2cEdmOWkzV2FWSmdEMWJIODhhcjhSbFJMYnRtaUZSRTJrZjArWWlUSWQ4a0phbXZGVlRTbjdUb3huQ0NPRlNsOERXZUlmaXpXVloxdnRzVWlqQk1MQlhmZ0xTT05lOFNuRnVUYWdmbmU5V0M2VXg2bEFHSGdpUDlZV0tiQVViS3JHZVRqTjlYVlhSSllJdDJUbENqUWsyNnRlWU11d04wbDdobFUyengraVlVNEhuWTcrY3BJTUNmaHRjZ2lhamhzd0Q2SWQ0aFRlUnVPKzRyUm82MHFGOXVZVzdWcXdLdms5VGJBbkJRcnUybUxhSVo2U1lBMTI2cmx0aHczY2JhcXVaNHh1ZDdZM1plTlRucmRwT1JpRWI5S3dFYXQzbi9heTIxMTVRNVJuYlJZa1NDaGdyMW83ZnV1TGRyeU5zM1ZWTVBkWnNPazdwcmFKV0F6bk1jZTVoa1VGVlpBZ1lDMTNOMlV1WW1VeWRTdk1Sa3RmQ1hGSHlOcjFpeTVTaWZ1R3paSk9sR1J4N3IzSWh2OHRyTWxWSlFZa2VOZ0VjN2pmbWdFWE1WL083MTJ3bXJ3QTZVL29OS3hNYlliTFh4REdDVFlDYkFXWkdOYzBwVEk1WDcyZmppQlBmTVhVZjNSc3BEMFVjbkZFVG83ZFlDNXJHV0VHQWhpTmpqTzRpSjdpSzVhNUdHM1pDYUMzZDlpNU5PTzE2N1ViSm9EZTlMaXFkWDVMU2YwSklqTUw2aGJtNFlVMWdieWtwdWdoMGFENnVlTkVNUkcrNmcrdmRTSHVKcTdUTHBRc1JCNlJSd3pTbnJkMEovanFXcXlxNEpPYkpld1kwTGZlMUJtZnRvdW1DT3ppdkJLOXJpYXdBZ3Q4YkRjaXZKRVdNWFdjWUE5V2haV3lNNVVjZFhUTGpYbEZPY3ZyaGJGQWVtTENnN25HbzNpa3BZNTFZaEU1SDZiZkV1SXlDeXVsV28wV0NhR3ZUb2E5bXN4d2xnNTVyMTdOcXdRcFhmRmpVQm9LQWNFQU5hL3FqRys1QVBYcW55YkR2bUhvRWd4eTBpc1BMRjFkc3pSd1RKek5OanlNaU9YVGZzTTZtNjNJL1BPT2l6Y3FQRmtkclJaaWtWWTA4elJRdGJhU1REZUZBNVhva3dzQnphMUhzY25lZzI5S0xJc0wwZWJlcXFuYzFLQWFoc1d3VjVUZzBEbGlvb25wODUxNi84U25OdWFGbHV1ZVZEWGpxU25mMjhiRHpGZFZxZnVEWm1oaUFYZXhlYWMra2pKVFJtTk9BVnhlMk9zN1J4SUl1cDV4aDlEWXRBaE0xc2c0UUx0bktNVGFDcTJVbUNhLzRLdnBHNFY1VHJIOExjTk93YjVCcjd5cmJ5QmpVNlNVNGVITzl2clp1bXZqOWRyUGlZWTk3SDluYklkRFVXR3JDWWVWK1hjTGxlZ29Ram5WK2t5UEpDUWtLU0RkZE5PS3RObk52Q3NBcVZwYlNkWEVEVHM1UVRuQml1Z3hLVUpHVDBtVmxLb0lDbWJqbVV1dEVPanBGazIxRUl4bGN5NWwxRXI2bVMxcXNYZ2psMC9laDdIclg1NUVnZnNjejI4aGlqQVBPN0FSLzJoU2NQMWtVeUsyVWdDRDFJVnc3Rm1neUZhSXpzZGh3UHZ6ZkJ4cXZTYjkycmVXQk9EWFZ6SDRnOVhRSTJmeEp1Nm9kQk83WVhtYTRQRmF3VUZIOHhhK1N5NHBGdDFPNWN1aHZKMXFnNXR2QjhjcThhNENHSFF1NUlNa002MHh4YU1DdGVqVktvVldPQzBhNTR2dWpIcFBNRmNPYzA1MjRGZzJSdVJsSVBkVnFhMEkvSWtSVnRJQVlwTGJDb25HOWFXNUQxWFdqRWhkNU1uV2lQeFd4NFh3NGVHbE4xM1F0M3BIRFFQOTBrdEQ2ejFuUHlzQ29HdlhjN1BRdjBtLzhaZHFqWkc1Y3dkYW1sbFdFeldqVWZBTkFFS0lISTdzTEx3emRweUg0bzQyckxHNXNiRWFITmxsNUc2SGFwNy8raTUyVkZkZHdIM1pNQWxWZW81cmhkbVZxQWUwVFc3L25KSGZ1Wkxka04wK3NYeld3b3BWa1pERmFoUW11bVpzTGdCaHRrWUdzL05HK25nMi84VXNqVUplaVJFSVJaNmtrTngzU1VuVUNOT29ndWNmNldybytHcXBlVFNjWWk4U2U3QTR2Ujh4MmZpMFVNU05OZGQ1aHVCOEVNQ1R5TWpyWjBDYnQya1E2ZVlqaFdMdWVZeE5FKzhBS25WK2kvdXpxY2xRd3FIeDNkVUhQTlZEekRDcjhTaml5MHFtdHNQZ1FkSVUycDFjc2lQWkpYbXB4ZFRrU3FsblBzb2o3QTljdVcyeHl3emRFY0VkYW5OeG9nRldoMlFGaUJNS1JneVRRS3FRSDNiMHMzTENwdnlWWnFDL0VOUnkzWlBWQ3luNUIxcXhvSnhPUCtpdVg4NkI1MXkrWmcwQ2lrQWFWcE5RalpDL01tcVVPaXYzQVVwMVVNVHZXQlBJV3hGcWJEbDZ6NlhVdVlsVmpNamNSenJsS3BZdFRnUGV2aXVUeUlIcXJwc0QwWDhRVFQ2SGt3L2ZYamZUM3JseVJ5OFREcFl2SmVCUlJMQnJlVlhMMEU4R29tdFBrQ0VPNjZDYUJ1elVKTmU0aFYxMldWN1N5TjF1Q3d6NFdPV3IvSndRRndEVmMzV2lwalFmODVjdUkzM2ZMWmkrUTEwSkZ0S2lJYy90cUQ0ZEZHZnlWYlJNZ2FDTFc3NTFTcDhnVnl5WjN4QlF4TitQWmpSTTg3RGp0SnFTU2NtNjI3ZHJvb05ISUdiMWJxdXA4UkV6OG9ackxGV1VkcGUwV3hlekZRb0RMZG1rS21lMnVEOXBMRlVKNURxQUVNRExrOVZKeWdWNkFjdVZ2TDQ5cW5sbUZSTmNYUjFNV3BHdWhFWW5UUmRPZFU3NnJwbG95Z3BubDU4MSs4UGZwZ0drcjRqNjlpSUlrVHNEekt1dE1KZFZZaVIyUkppcUpCcHBNVzdRSU1XNGpkVjFoM1NnVWJnbkF6Y0c5MERYTERqYnU3YjJVbGY1ODFXVmR5MkhGU2Q0SUw4U0lWZ3VJdFZqaHpza3M4WlNvUGFtb1RNMkFyOVRERGw0WkdpRGRudFpVRnpRMlFUeHJva3RQdlpOd2lkVjhsaVVSRC93QmEvbGxYTGExY3lQaUo2c0k0Uml6b29CM1c4UWdCQms0aE5RZTJ3MjhFL2ZFYkJveTlGUWFTbTR2L3RDZ1o3K1k4Uk4rNFVROXU0RkNiVUNWM3VuRU4yVm52eGZhMVZORTErZkpLQ0F2cVArSVo1K1FnWVFLK1hNSTVYTEpnb1M1aUEvRDRPNmZ1Z2NKVVV6OUw5MUE2SEJUU2NXYnFMeXh6WXBEa0ZwOWh0UVZUUi9kMzJhSm9pT25pNWR3VytJSGJMczRJT3JoYVowYWM2YmMyUlptWjBLQnlzMTgydlZzczJpd3pHVWtkTEJQQ3NkYk1KRHFkTGRLb0dqVUFaZnNsdjMvNjBnL21OUjRPR2NJbWNZd0RZcTBscEt2VTh1TEdFUVNZRTQ0RFVzRXppeEZBNWFZTG0rZ2J6RWZDVy9yZDF2cldVeHd3anNCaWxIQzBkWnVaZElhQ1VnYlNLQjRBV0diTGxYaEVOby9NdjNncFlLMGN3KytPVktrL2FSbm9TazlPcWVhRUgyQkRFYkN1SjU0a2NnYkdURWhGSktycThld0YxUVUvdDlKYjhvVDBoUFNwMVZmNjZzaFB1K3J2TUloRDUwaHMxWVhyOXpCWm9tSlVCUnZscWhRdG9RaVpncE9qRFF5RFdhMlE1V1hidnZ0aklWaGhJdG1Hc2haNUttcWNYcVZnN08vSzh6M1h5UGVPT1owekdvRXBTazFYZUxWZ1VUWXUxU2cvODZva0FWTUFDQ2ZoZ0VLNm1taml1UXp2T2ZUUWM5empSYjBwTWMydzhKQVBvaVBRRzQ3VnJmbXZXN04wN0J2TktzK1NsSnlScmJBTmNvZHIxNmxtV3BoUldkSTdaeG9RUEtoM3pYYjNIM3JVOTh4bXluU05xbGxoN1F3eFdRUVNjMmE0aGwwbUZxMjNtMnc4M3dBTkw1UHBoVnprZUVadlNucktDcDNtMHdjQ3R4YklNWkdxdmZaaE9KV1UvZlJsYTdMcGpWTXFuZGJoUDFocTZjcHlDWHBuSVAxZ3ZVN1hNck9zVTBtZHluMERidjZqUk5sTmU4U28xREcwSUw3bDV5dHN0NWRJS0VWVDlVVnkxY3ZmUzY1TXVJdWJaVE1qZURQSXNqQzBWRmtoQWhKR1RueTRNWmtQQ0NlUTJjai9KazBVN3ZwMnVPbHJ2aTJ4cHN1SUs1djVUWEVGWllXTnRhU2RWWGI1VHlKakhZMWlnd3pVLzVVckFJUWpzSFdTSlArR2lrdDN4SUczMXFlRkFvTlBjVmp3Y3JOT0dVaGp0RmVwenRCblY5SElWME9jRVpCT3VnRmowN3NLMmFpRlYyRHpkYW1YSDBpNEN0ZmRSeGFCRnM3YXVnVVp2djNHbldmZ3BFaVFsWExNNmVoK3dUZkZVOWdoMXNLYnNnMEpXNEpsQjZEbE94b2F4MzBIRHZMbmJXSjZ6Q0x1RHluT1A0S3ptVDFUU0hyaXFsMGExdWFNTGZEOGp1TUV2VER0K2ZhU20waTZLL2FpWmppbWtXcmVDZ0tSMFJuemVwQkZwb0NjbkNKaDRxanB4am1mbDBzS1NoQWZTdFBZOXVTNDZPZTNXVEVBOGVzUlpGSzI0ZTFJSXE2MC9oSVEyTG1PWlRUUkRYYjlyYWZOZVliUm9QUWRjWGZCK0RySWxacjJyNEZCSTY3ZFBjTjcvMmlLT3I1bTR2b3Z3RDQ3dGovMjUvSFlnMWVMTDd5MDJHd3BoMElpSC8zWWwxK0VZSE80VjEzbHozQ3YwM2JHbjZHL3QzTC8rRXZ2b2NGam5FM1BIMk5WMXd5b0w5SCtvK3krSmRNcHdkbC9iRkJuMll3cDJQejk2TTBIK1UxUU5mTTV6cSt6WEMraUV6RlBQMlBhWi9Lbi93MStzZkxZSGZSMWduRStnNzdqcU50M21zMlBTanFLTTczSGJZOWhiSzhTNWozd1hVb3l6ZngvTm9xMzRQc2gyaEtHckc4Y2wwUFRhbEZZbzRRNnhwaStSbWNmNnp5QkQ5ZllPWTdGam5FbWZhc0YzRG95OEsrclNLL3R2SU5SUDhpOFMxS1p4Qk5PTzBmUnZwekMzMmV5Qk5GZDhkUjBsZHd1b1BZS015Sko4UHRRSDROSXZ3WG1tL3E1TU5FL2lSdk5Na1dtanFBWnU3b3htNGdQNytYWWdpMjA2RGRPRk9OL0Z4RGFTK2dOQi9TYWdsaWZhRnBiNkd3ejJQMkNEbjJCV0pkUWpBOVk1WWE1b3VGR1VJc1QxNVRQTGpQRzJuYmcwQmNrL3NBYmtkZW9udHdIbTVCZ3Q0QWVEMTRmVGJpck5lZlRTQ0tENTlOWWdpZlA1dENlZFRaTkNxVEdHL1duUzNMdnVKalQ4eHN0TTI3bmNZeFR2QVF0M0NJZlJ6Z0R1S1N0bEVqcmtXVEpsM3E3M0p6TFl3YndTNXUwOHRIaGhhRmluZmw3Vld1VHR4a3UvNGErTndQYk5hR1UzS0lVektjOFdBcWd4M3Z2NEdIQ1BkNDZVeHJ1SXhwK1E0Mk5ZazdJM1VkaDNmaEdJNXhoRHYzMUMzc0Y1Q2hOQ0MrN3QzaVBHcnFUMk9sa2xlcWNrdDMzL0ZTclRwMFNXblRybFBuTzFkQ2E1NVlkU2ZnWmZhNDMvYzB1SUZTdnJydWVZQUd6WDlKK3RUWXZaelU4RFhLbXN6UkxUMVZ4MGNyaDlqTXVBWGR3R3FwZTlqbFJPTitXajl1QmhuY3FHeDVpQjNaejhnSHRmYjJXSGx4aC9mayt3UGZVdU5Vb284RVEwZFVubnpYbEIwRVNweXZzc3NoTlltcGFzemloTjdOdlhhS0M2b3I3M0d6V2NNalFWZFpkbjljQnZkSzZoRFhWMmdMM2krMWlwS1JtaHZFUE9MYUd0MThEd21IZkxLWEcyZnpOclk5SjUrWWVSc05DS0xYN1h5UDMyZ1dZNWdhcHd0MGwvUC9GMlpUMGFQL0c4Y1Q4NHl6WXZMa0sxQ29TTEVTbGFva1ZFdXFVU3RRcDE2RHhpZ3lXN1JtQWU3QXFiOGt1bDNRbzljSVJqR0djVXhnRW84aHhCU21NWU5aekdFZUMxakVFcGF4Z2xXc0lZMTFiR0FURnlrb3ZEVUlnOG5DeW9Zamp2R2E4UGtLWCtWcm5QQVVUL01NenpMR1BMd2tOQXRZaUZkRXhtS1c4QnpQczVSeGxyR2NGYXhrRlJPc1pwSTE5dTdSa0hOeWNwSWZ6NnhlM2NWWU91Z2I3MkpPWlZaMzkzWkJhNGZRM0NGajhYQmdkUmRyK1dUOTBLcTFhK0M1Z3lGNDd2WVE0ei9RRlVlMkdEcGpmelQ1ejQxMXNYWko0OGlqSklNSjJCMm1LZVR3ZUR2NFdtWjB0SXQ1blgvaDBWM293U0hyK0Q4b1psOEQzenBZeTd3dFMzZmxuNFpnU3M4RTY2NVlmQ0p2Znd1aEVJT1U2UnZvWWpLT3UzSkozVlY0VjNHcGtwaWRlODR0RGdtNVp0bjIzYk1WaEIrRmY5TE5weFR3YjkvZXEveDNiTHhPVGtIaHh4OFlGNnVvL1J6OElleXlxOVhUSWNJVW1QbEtQLzVFNmN5Y29jSnpoQTRGaVlHNU5XRGtmWlRQL3JwMlVqNzd3UWYxY2l6djBkSXN4Y1BHQzB2TlpPUlhrUmFsTHRlSmpaT1BLc3NSMmZzWFlodjUzaEVBQUE9PSIpIGZvcm1hdCgid29mZjIiKTsKICBmb250LXdlaWdodDogbm9ybWFsOwogIGZvbnQtc3R5bGU6IGl0YWxpYzsKfQpAZm9udC1mYWNlIHsKICBmb250LWZhbWlseTogJ0thVGVYX1NhbnNTZXJpZic7CiAgc3JjOiB1cmwoImRhdGE6Zm9udC93b2ZmMjtiYXNlNjQsZDA5R01nQUJBQUFBQUR1NEFBNEFBQUFBZjlBQUFEdGRBQUVBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUJtQUFnZ3dJT2dtWEZ4RUlDb0hQWUlHc0lRRTJBaVFEZzJvTGdYZ0FCQ0FGaUhBSGdsOE1nUW9iSW14RlIyQzNBd2hKYW0xR0ZLV1ROREx4Ly8xQXliai81YlpLQVNGWlZOQkxPWHA1SmpXdDZ4eFRhdys3dEZmdnV2VllVYmMvK1Q0SS8rSjBIQWNybGZKWWZqNjJsZVhhbDBvWHZKYXptcFl6NG16RjVuUVNDTUZCNEJ6Znp3RFIvSEhjSk1MNGd5TTA5a251OExUTmYzY0hSeG5VZ1dKd3dJRkJoVEZ4SENVcUttRUdPcDB4bzdhNWJGY3V5bVdxaS9yYno5eCs1NnAvemVkbUgwd1VHMkRRUVlJUEdqWWhHMTJiTCtMVlAvSHEzblhYTmhwU3RtZFBjL1ZWdmU1T01wQjBocElaWHVEZUlWck1CeUFIUjZSSWFGVDYrME1uVDVnamlMODNaM2QvV2dNNzRBQWVwMXZIMWdTNjJkbUJKRjJnY1FDVHArSm1QcDEyR0xudDU2M3VDQmxDcDA0Y3NSeXMwZnpnQUZnSDV4Mk1kUTdwQXJLZi83Ly9CV0FCOGp4NSt4NkhrTUF1NkhZamZUZE5Hd3c0MDNiOVgwN3ovNlE0UDc3TmFPd0FPdXNRcmJOQU1KSUNCM2Zyc3N0Z3QyeTFiTmxMRkY1Z3JxRGxjMSt2UVBlZlU1TmExbTl2c3JUQWZNcnJtZTNzZXBFdWh6L05iYWE1ZlpBbEp5eGpDRUN3Q0g1byszWUhSU21XNWcyRFdCYXhrOFd5aGtvR1RiNFZOVVdkcm5wWjNEZjNxcHd4bDA3TzBDUHMzZWZwV014T1cwYktXUDMvejZsZGVPOUxRY1VPamVLVWlKSU1vRWRWaXU0QSs2U0w3WDcrejFTei9iT2dDSWlYcUFzcDh1N1pQWlVxeDlKbENxV3IzWmxaek80ZzdpeHBBUXNGY0trSUtBSUtGRWpyZHJHUURKQzZrSG1PUEllUUtncFFYRHBDbHlMdkhHS3FZdTVDN1B6Y3hzNUY2YXAwNjF5NXVxSnAzUFIyNmFLenplSDNSVVRvVDFlTE1QUmRrck0xMHY4eHRWMFVBWmVWNVAxcVk1ajlON3ZKTzN2OG80S0tDZ1BZVFFJNC9zakxxQUQ4TVBZR1lPV0swQ2Q5d0ppNzB0M3hYT0R1dkZ1L2VOMzQ5Z2NQSE42TFEvQ1dxanp2ZU5FRDF1d2hBRUJ2ZVJZQXVOUVNRdVQ1RUZDWlJQSHNsK3QwT0FxUlNhd3ExSmhodGxHSG5ETHVmT2d6SmQvSXQvTEwvS1kyMU9iYVZqdHJ0UGJXZ1RyY3ErTFJlTnovL3dQQVdVMVdwYTQ3ZXYyZVA4K3ZhMzNIL1hXb1Y4R2o4TmgvcDJrL2Z2U0Q3MzNuYTVlTU8rNklnN1QvZFg5ckJacmRLaFU1Y21EZnJoM2IyVnAxa0llK3FVRTVCa3p1MjQwdmdmL2ZTUXlBUDlxZlhYY2QySjVZZllqcUNSMkRlQkVIQTU3K1EvVDBsTjdMcDhqWm1nZGZhWVB5NHBOaStYOGdnWHkwMUlVVElkNFExN1NmUWJtV2dpZkJqYlR4RkdBRi92dUtPbjZhRDBLTHczSjQ4em1LaWl1dkQzeDhqOExzeTRHck40dFhYNFlxMTVOWFBQQ3FGVEpFR3ZHUk1QeTdIRFlvUCtjZDVQWGs1QnZOUFA5R1dzRGlYU0NabVNQZFRJbkhjVzNtcVlBQjFKWlhvT0dyU0tPQm5nWW5ZbVR5MUZCUndhcWFSOENGMnNjTENrVCs5VkRkSkhRTmlMTHg5MkhiNjZocTFQRjFUSzljZm1OUkRaRE9YdUxCM2Jpb2NZVUhHdERucFJtQ0RuaWFUMWtGb2Mxa3ltTkRmcE9KNXVicEEzNnVXT0R5bjViM0tLVmJXYkppcG42OXJRQ3ozNVpyUTdnb2VFUE9qWDgwcnloTjE5Q1dxQzFoS05zWks3QUVXb0F1NDhhYUdVUXBLZ05GZXlzYlU5QmdjQ2NSd1hNRFJUeW0wZWRDcTF4RWNTcDJVYmhQaUNxbGdoOERlendqbkVtZ0NUN1JvTmc1RGNhd0FqMUZZYkduQlJpajBnZi90SHhVdG5QcTRVd29iN1VJQXpwYWpLZnZZdFUvdlozT2F2L1ZIMXJKT2ZkMWZQcjJhRGc1QTVYamwxRmgrVjZ6UmdtWkE2M2JtSEppb1JaUEtPeER6aFJVOXZDS1g1TGFteWsvdzNMNkpwb1pmaHQxSWlES2kxeVdOeFk1TVN4S29FeTBLb25obUJVNHBEMUo0bUptQ09mdFY3QlZTZkV1MG1yQXdJTFJrWGtsVTBBYWwvdzdIbXhMSkZkRGVmdGZyam0vZDNmS1BLdW9xRnp5cktFQ3NYc3dKUUNXTkFCYkdnS1dSdUJJWTNDbENYalNGSHlKVzc1c3d2WXZ0cW9GS3VnSzFORDlJajFybUErUzBrMFVvQzRLOFFvaUNVQXNEU0NSaHBCS0k4aWtNUkJwQXJrMGhVTGl0Q1Z3WEtDRkJQMkN4RGloOHdLSGxPY1p4SThwMTJjQTlwMzlteHd4QkRCNUI4UFd2S09ackhLaGxCTzdFNk1JTmVhTXcxczFZVHJFcXpiR3hCZ2NiNG5lQ1hETFpYMEt2WUFvN0ZpazIvZHVzcEg4d3VQenA3K25PM1VTcGhjOVowQ0x5ZVVVY1UwL2l6em5hSTYxbmd2cEpCbFVNdm91VVVXU00yakVJcmJKWGZkeCtYblNTVFBpd21WRzJiL2plUkhDOVBCRnp4V0loTnBXSWpVNmM3eTBMZ01GMnJKQ2RGcDNvVkZqVTJzUXRNQzNVOTQzZTRvLzVYRHc0SDVLUExpeWtBYnpBVkcyT3lSMXZNNVhjemJ4bElwb3ZoOUZtRlpLeGJ4U1pXOWt4enlsb2JMZFVsbWhPOUpSeDd6amxBRkhpbG5FMEFVbWJLS2E4RFE5TVB4cFkvbFZWczZpam9PS21COGhTelRST2N4cE51QWtoRVJ1M1NiQ2tOSjJKeEk0N2NCQUE0QVIyOE1CNDJkTlpBd3FxTnZDODlpZ1ZYR1UrbUdnUWQzd3pGSHorQU9ZeHNxVHhHcHd1REtabjk4SjRPcUZwMC9vc3FNTVJ6TjNBVVVEWEdwOEU1M0xXMU1IWVhJcEVyeWM3TlVnV1FkdnNhZC9CVnk1M3RLcjROckx4cUhseXdXVjFXb0Zhalljb0JQWFNBYVJzVENyYUtHeEhZMHYydGRSZzRvRk52NUJUQVRFSThBcDVpbGIzWEVTOG00eE4va05QTUlRU0RlN1JiZnFVTmlvMGExb2JYNktHVk5ST3hRS3pXN1RjbnZoSFZSd2IzRE9QWGZCcFFYM3RvWmtVRFFZeTNhR1N0d0hJdzNlMjYzd0FIT01ISEFLTWlRSXpEWWVJRXVDdUMyR1hKS0RDN2h0UEVDZUJIRy9JRklLY0lHd2pRY29raUFlRitTUUVsd2diZU1CeWlTSTV3VzVwUUlYS050NGdDb0o0blZCZHFuQkJkbzJIcUJPZ2hiMHFoc2d6WWYzeGlseUJFWkVZUktjb0NBR2N3SjJnS1VGd3RvQytiWWlOOWlEQ0k0V0JHY0xncXNGd1IzRThMUmdlRnN3ZkMxNHRWLzE2UlQ1L3FSZGZWYlhHbTlGclAzMHNWS25Ed2pVR3V2Uk5sUG9pYzJjQUFRTEFFU2Y0SEVIVVFEeDZMb2hJdHNIMzBnWXJOWWkwcVVObGFpYnZNb0VUUXRCYklFcVl2dEtPdytOUnVhaTZ0UUVUOUgvb3lsaVdNWDRaK0hXdkprYlZsWDRjaGhqTDNpV05wQ3FPd0c1UGl5bnQ1eldSQXBxTlRpSTJuRHhWTFV6TXdYRDVuSUM3L2U3TXkzZWVQYzJWY3VHUnc5YThkazVSNTQ4WGkxbXZvYzZ1QmxlMkpZUDUyK2RDOE9acStuSmhmWndXMXpaa2s1RXg2NWVUdXViVzl2RHpEUTFvYTFveEl1UjUyckNzZFFLWXFsZVN1ZWhQWXYyZHkraXVSKytRM2E5WVZrWUN6a1gxZ0tENWJPWUZEOXdrVWxuTldVa0NGTFQrOW9TWVVnbXVSY1c3SlZTNFQwZ2c1Z21UY3dXeHBSVFhERFVGRUx6czhBY0o5ZVd1b0hEdTQ1bXNGZTZ3TEpqMllqTGNqT0I1ODlGS2dPaUlhQThoOXVjNnBJaUxBODhmTjFTRVRmTzdUSFZaRFV1Q1pPbHVYQ1NsTXYxZWg3QzhucG5tUmh1SUlLSWVHRW9lUEY5em1Ybm9waVZwTkZYMTY2TmdiSWhHWTdjc3NPRnB1VVpTZ05mYWlLeHEwdGkrU21HdkFKcUpWSDNOTUs0N3poREJKTkFtaXFoZFVNaE9aNk1LcllIMTBMMUtTLzEzMUtQSU1YRkgwT1A0MkhnUTNOc0RCcTg4QVREM3RaMHNRZENMVlpaZ0pBZ0Z3UUgxRVBKaWJNVE02a1pvQ1lhUFdvTFhod3BRYkk2SkJxdmhaSGhLd3kxeEhCNHBOQnI4eEdXNi9FNmw1Tk9ndUNiV3NkVG9GWENrUFBOZFlRZG11cHlNczRiMjBWN3dtYTdkVW5vdHZRSjU1YVRHSjJwcWpTS0NxMFlxWGJvdXN5cnowU3NkeVRMSTJWM1lIYjdMbDJVbW1uS1NBd2ZJUVpIaVE1RHl0dmhyWjhma0hRY2hOSGhOMEp3aktuK1NlOXdGNUxzRUF6cmxBUWZLelV6V3ptZVFJaVFMN01saEpNayt5YSs3YWpWV3FjSm5SY1J4SnNsSVFrSlJEc0VTVHRkcUpBekcyZnlMd1FSay9wNkdXTHJiWlJNNENKdVg4ZCtKajJqTndLMHovOHlXT3NmN2lYSXZlMGViODJOOEttSW9aRVlzQm5Cbjk3djZ6V0dHVFpTN0NrUHVMRGMyaXBSZmQwM3dVY29UV0k0WEFnUkhVbVh1NXpOTzBIMlZPN21McWdzbW1zaXpFeXg3a2ZCWThnajVVRmdhSDFqSjJVNmY1cG85S2FyeDBzcWFvWkhzSkZQRXkwS1c4aHU0ZzVUbTJuemNBS0dQN0QyeWdTUmJ1cERMWkdZc0hybWpiMk5sN2phSjlicExNS2U2SHc1anFsSlg2VHkyV2Y0SUxOVVlqUUlmblJ4VXlCL2xsb3l5bGxXOXBkUENya09RalJrbUFaMzFHMTZ2ci93WkJEUm43bUVWUW5TK0c1eTZTRmdhTXdodFBlbld5MHVtYTVUNkROczhmZ242RTI2RG1XcDZWcEU5dmpYR2htT1dvMmhvd3gzbVdqaXJBdUtpelV6VG85aXlMc2RVTkpCWlZsSEtHM1ZyWFEvUFBJSGpMcE1vdTdLekFQTGNSZWcwZ3lQdXZnZyt3Z2d3MkNVZG5NaHBKaXNuWFV0dENpNXRxYk9hV05lSElhanlMdlhndTFFUlJvYmFXY1ZGUHBIMk5UVDVzemFmYkhqbU4zZEFhSytlSndhcFNTVzEyT1NxTGx1M3B2bUN3eWpVMTVwSzhLYUhLWXJhbXpTYnI4d211UTJndGE1dmxkd3k4bENJWHdjdEpGczVTUXFvcTJSSDJ0SEJDN2E5cW9Wdmg5REdKRWV3bUlTZlRIRjY1RDBFRWhYcFFzaUpZcDVKc09tWWxvenU5b0YxYnVTdEZpQmNHQ2djcXRqZC9XT0NZM3pGcDllS1RLMWRMWGpQSzdIUlBOZGtoQWVxZTdlZm5scUt1VWlLZUt4a2g3VnNoOWhua3Fvbi8wellGS3U5SUVMM0xHbVpnUHQxOTZVcnRDeGk4cFR0eEdoWDVpWXBjTFRvUlB4NktrbG5CbkJTazVZVUxrMnJRd0xpQ0xqM3lMVTFicVlkZkRpeU00aXVET3JTZlU2YVk3ckFIZHVLSGo1ZlhhdXJsTmk2Q1JkSW45eXZ0dWNxMjBoWFhqQ0c0NHBpOUVBbC90Vi81bkhWYVRFclY0ZStaSFViRVB3NHJOTHN5SHVhKzFwaW10WUNobGU4TkxzRWYvY3pELy9oRkZMRjRlNnBHZUwvbnMra1M0Rno2d051aC96Tm4wamhDYUxPdmZBeVFlb29KMFFISk5Jak9Ea2tFbkE1RHVwNE8rYlVlZHRWWUZSUjQvNCs3V3g3M1oyM2swZUpRVlF5YXF1NWhNYUpEcGFwUE9iWlBBTUQ3Tm9EZTJuOUlTM0RKckVUbXNiS0NRVTg2Ui9VbklWWjhXWDdpeTUvTEFkSjF0QUZqWTE3RVFrOVNUWEhRTXJicnhNUXpudisrZWY1RkZYeEFXdkdlQWJTYVdEMEgxd0RjZitYdHhPdnJxR3o5bGNrdlU1ZVo3bTQzWGVzYmw4VTV3R2lGbXVYZ0RGVVFGNlJtY2M1eXRTWGdRVjhkaEpJcjkrMllRTkNERVM0anhSclNJMzk3c1NzM2dJZTZNN2RHNmhPWStyU28xOVF3SmU2MEoydGZhTWpmdVlEUjgxQUVwaWQ2WWs1cUg2MnBJaWkwUlk5aU1uSHBmTncwWVpIYTFZYnpIZEc3Wi9GTEJxV3p2K1VsV2RkYVgzLzlTSjZJell5VlNPeHdsQmF4Mm5acTBkcUVzTWhiRDhlQjR3TmdXc2xjRmM2WW80LzR0b3NzL21OaFE5ZU1xOHhxRStHdE1CUnE3TDhBRERVZGFKZHczbTNqdkp0cklodlFUTktxM0dPNE1ZQlJvdTBYR2R5QXhNa0hvZGtzS2VFSWVrOHd3ZlA5bytqQjZYWklMZVZIWmVDM1Q5d0wydzFSdjEvSVR3UmlqNGQ5OEZ3NGMzdHZGVHBmcE1kWHdFemY4RklhSnQyWStncUd0clNPSE1seGgrYjljbTkyWWVQQys3cUI5VHlvazNLdzI1VTlQcG1xdUJQdE5KSW15RnJld3U2KzhZWG0zbUhGdWNDaEpQV09ycmlZTDNXeFZocmRRWFlkZW9tL3Y5Y2FMWldJbW01bHp3d2cxOFM2RnBhTEZLL01Fd204Wko5UzlxaTR1SC92WWxqREVkcWRMdzFVWVVYYjQvVFFRblRldWVkd3d5bDN3a2d4aW9OM3hJS1dRMkJMdjNIWTk1Y2R3VnVES1A5UkpxT09rZG5ERERCSHRGd2FSRno3Zkl6MTJsajhJT2hrc0VFMkdmeUtVd0VlTU9FK3NIVVgvL2FqUUhIWWFUNWhDKzdVeE5wdWhiTkpGSExRS01sU3R3RVFoY0xubVRDRHhZSldvT0VBcjdHSTdKaGEzWGQrNmt5UERpWW9ZTHJYYUY0YjdCdFd1Qld4MFNzMHd0TlJqN1J3empCd3luSFNHMHAwUUxNL09pampyNGZpckxUMVBqeVdhTllZMEllbkNtVERkT3BnMDNXMTA5RWUvMTRwMG52Skp0VUQ2SXAzRGdLU2dYVzBSRzQ1TXNYTVVYSTRLaTNGZ0FCOHFOblRWWnVoZUk1cnBYU1FwWjBoS1ErSlptOVlJRk93em5DVEJha2trWkpRVnRVblV6Tk1iT0lKeU5PRDVDc3pQbldqdHh4Z3UzZ1M1Ky9IejBsMFFJRjg1U0J2THVYcGRjVmFFTzJQZHJTWnhyanZTUlRCUUxzSVdRMmV4L3JLU2R2SXV3VmUxTTl3YVMwUUExTWpwQTB1blp3L0dRZUhVeW9obTJCY0Y0NGlGcnRlWXAxS2JTT09jMnBwbVZocHhISVRQSVlmK2c1Ym1UQTlRWXN0ZTU2OXBPRjhNd0Z5dFlQRE5jTnJ2S0k4dWtoa3F3NHVKWkozVms5SW5yamNpSXdqM2E1MnVyOHljK3dlU0dxb3FQQWV2WER4OWdXTzZCVFNJWVQrb3RrT3BGUTFTN01kMDBBQlZURjFFNDE0TmpES0hiME04OXZZN21kRFFSOFoxT01WSW0zNUg5eHlXOWtXanMxZGVSZnJhZlNXL0wrczNPTmtnNlZ1UElHc3JSb3FqeXRMVkxUSjZQdk00elY5b08zNnB2c3VwOWk4SjRSRVRsV0xLNXlxemJpOE1JY09mQ0tyVnhhN2ZzYW1qVGVMMFdzcVNRekdRTXBlQk1wWTltSUhkOWFmWUJXL05LZmN5aHV5WjVnVnl2Y244eUhadVJCNjg5czd2TkIrT0p6TUxXRUwrTzI2bDdXcjNKWDBEM0tuN2VTMUpwMnNYZ0QrTGR0dFNkTmp3QnpNKzRQaTVjUFMzTE0zT2RsWHlCQzk2QTRCblg0TjdVaExkelR0Zm1RUDd3Q0k3VHJ2N29WcTRXSDNEU1NBeDlHbUtvQ3ZTVzNabE51a2VqalFwNkRPQ1dYV1piMTVHdjBnaWFVSFovSm9BODh4Mzh5UitSbnRCVFJlbWpIT0hzZkZ5Z0h4ZHByaUhiMEtJNlRQdTBzc28vRXF0RWNCdXM1QTA4c2lyNU0zVllNNUcwQSt6cjg2K28xYkZwSFp4WVBkbEFNQncxaGVDbSt2VkE2eHZSUktDRlk4U3dDekVsNWFkaFoyQUFRRHUzTGczUml3eEQ1azEvQzJWQlRwS29FaHNBaUlvZGl4d213Y3VaTFdJVDM2Yko4eFQ3MGV3RSswR2EwQ2dPNVpidVZkS3diS1ZEaEVRUkxhRjlHNmlSVUlRZ0cvM0pUaldQUHd2ZlRjWjZ6ejJlTVUyeEVzZm4zS3BnZzdTVENpaFZIOFhsZUQxd0pJU2lyb2c1MVNka0U3K0pZRStlYlU2YWU1aDVtL2FqL2gwRDNiVythK0JFcUl5a0xkOFVhTTY3V1hBclRqNkNrVEJjVUdrWUFHNFBPcnNUYk9XWFRyaldGSDJVTXhFUjZOOHRHRDhaUnJ1S2dpS3o3T1NWZndXOXRCTnU0QWIwZSsvcVFFSWh0MHMvb0trZi9jTC9Pc3pLdXhMaWNtbU5XZkJwNHFkMFBBWHhMNkNOeGVKVkJaTncvcC85WFp2YTRwNTRaRVR2YklPSlltMWxud1lZL1FmVlI5a3N3dVJqUFBiUmV5dTBUQ2ZYanhLTm5YUXpjOXNxRmZyT2ZkaU1FbEV1dGs4bFVTU3pFdjdrU0RCUS9SMncyYlpOOFk3Q1U2c0RoUm5mRGZmSWlUWkxsaDJGd21PWWxKUE1MQXUrZTQ5aGVBU2J4eDR0UmI0VVBURitza3ludE01WG1BV1I4Z2VFYXQ4eXErMW9UTG55Zm1WU3FGWkpKb0xtZlNjckNub2Npc2VHNHNMTFZKRlhIU1ZtV3BSMjZnVHVUbWd0UVBuVkRiWmtWSys3dy8zRWRoaHpoUWxDVVJvZ2ZmTjZtVnA2Vk5UQTZPQXVEbDdxWkh2UW4rY3FqZUI1bWtHQ09Wd1lYZjhqR2JHYWwxWUlJTWNyQ0dDcFZITVRSUTF3Wlp0MmxWVFpDQlpsQSsvWi9ncktnSTFnRFhyRGFRUDkyWVFiWTczckpqN00vNUNRb2I5VDMrVHk3UkpTSzljWDZ3a1NLOFFDUEZJMW1oVmdLakc3VDM1Q1RnRWtLVE1zYUNyQk9iRmlPUWVCNDFSdzVQZEZ0eGE4cm5mSDk5ZDR6UnlhcEc4d0xqOFRMc2dIelBpNHc0R0tDc0J6OGJPUXptbEYxKzRGTVplWVI0eUllSU5jbXFSM0N0ZG16WWV6czhWZFNldmMxd1ZOa0hJOTcrN3JzeCtPaS8ybkFsUlNucGNGa0I5SzVnK0JXVGc3N1U3UHJGRlA4WjlYTFVBODJRQldySUN6c3dEUHRlWlhjVTVFZFhkK0EvSHR5aThpRWFaTHBrMmcyeHowY1JHRnVxWnJBRUV2M0hnRysyRDJsdWMzNy9ZNTdhRXJXMklmTU9JMzJTTDVFaDZUVmg1cFY3NUFLSndtU0U2bmJxNTJSSjZHNEIyWkVPMWRnTWRMdjZGQXhXWEJGRzJsZGIvc3JiR3piNS9mSGduZm8wNzg4QllQMTNzeFJvTzRwbHJjd01EeTlMaW1uVHZJWVpYeTRqOG92c1RsTE9aR2JqZkh2ZXM4V1BGbTFuKzNRV3lLNDVoTE5hZjB2OWZXWExhZC9ZSDFrcjNXWmlOdHRmYVhTTDNPVmY1T2RrdDlUdlIzZHdmelk2a3E2N3RMR2t0VUcvMEFvMFAvdE55ZzBrOXBWelUwSFlYQWw0UTRmV3pmK2RhaVBTZE5VZmV6R0c0RitiUTRxUkl1YUhXSEJVUlo1QjF6NlBraWZXVjhRVjVSZlplSGZwOVdITmVUWUJIVVFIRGo5ejROSHB2M1cyQndxTVVnYXpMRzV6L3FZc2pVWjV4VzJ3bWZFdHg4bGUyM0tVM1ppN2N0a056dVhOY3B1YnR0M21JSUVMSm1oSHE4dWhkN2VhaDIwdENXNHh6N1h4S0tMK0hUYnR0dnRNVjQzRzRaRDZaZUN6UkY3dzNQdnhlZUg3MG4wSHdOb1hFK3ZjS0xGVVMxOHFsdnZ3VVorc2l3MFZoWHJIZ0k2cEtCSG1KSXZQY2ZEU1BQUTlFOUVDcXU3QXBQQ0srTXVsWEd2Tk9vc0UzMWxqN2p1U2lTR0Y2THFUZHVZY2NmeDlZT0V5cVVYZ3ZXK2RNck0xY2UycnI5RThiUytaWVBkVnhZdjBoSDlMMVZIemF3UkRHc2htdFZ2ZVJJMUpkclpZYUZCcWtObUFEQkpZR3QvdjhYa2RiMGpOZk1RV0VNNjFuUEd4RnhsM0dvY0FFV1hHTWNGdFBleml3aHZMRU5qRVlQRzFDUlFhbkQwckpzcWFWRjZrQUdxWURkS2x3TU5aUW9lN29teWVCT1NQam9BT0lXTndtQmd3NFZoZHVNMHVnb3FUSGNCaFhSZ1VQWXlLRVVMdW5UQ2ppWVpDcnBxMGZZdCtmcWFLcFNUS2tzM0w5a1dyamtzT2ZKS2dHM05xSGl3UVA0NHRMQTE2bG1oNUxwc3pHamlteE1hc1dnNlVrS1RDMlR1aTk5YnROOTl6eTNFODRHV2VHcmJTbnJqUjBObElXeEpFREZmb0xFNHNWWWdFZStkQm5JZThQd2xHMTdIVmxYVHc5WlJZbHJDTXgzTlBwWTJQNE5LT1d3MTl2WjFGWlczbENYTWQ2VE1MTnJWbTl2V3pDbEdrRWlpbUFMbGZLSHkxZ0NXMEtaR1ROS2ZoZ3V5alpUd0VibzlZTHZxTmRsZGtKa1NjRklRcCtRcUNkSUxNWFMxMGNTSkNaWDhML1lmNlI0TFFVNXhlWXhIYjJCWmt2a2dROUVoUDB6N2tHd1M1UzUyTFA4aXh4UTJCWmRZZ3oxb0xwMGtpRDVNYkY4a2lCRERTNG5IQXo0b211Y1cvb2UvVnR2dDRVaWtNZCtsRkg5aUZScjBtUHpDeHRQU015L3ozMXlXWUE1cXkzNktieUJBbmVwK01WUGJCeEI0RkhFZXFTeVVVZGQrSU1zelpHTk5yT2FoVGw1WDV0RnBRVzVmNjBkb3VkTEZYdHRXTmkrN2RINWVudlBkK24zdnJNMDJ5WlgvbnFUR1paNFVzSmhva3NhbjVVMjhNRHllQWVnNFg2Q3hESXhrdERybUhVZytYV1NGK0RIUkt1WFhiMUQweVNROHB3TWFtMVRxRUU5ZW9oL3F0U3VpSDZGVGJMeFU2dXNPVC8rWE9VTVdCYlBhVW53UzhDM3BpeFJUdExPdXBnYjVodWE5R1VhODY0cW8rdUlmbGE4SjJSd3hhUnREbm1Zb2ptM0RwL3AzbkMyOFUwQllzNzRzYVBZWWFoeS92TkhYSEdXbER3aXBWQ2JKcXVzdDZ4VVZ3SmtXWmFsMXJLTm5tMDl1UXcwZ3NJb2hwbEFtSGExTGREKytwdFNiQ0o5VU1EQ0xoLy9JVk5pWEZnRWJPWkl1NFFrWlFaVXArR0RkdkZkcU9FVXE5bjUzVlVPVnNiblpGN2UvazJqck5oL3R2M0Npb3ZFWGlienZPYjNDMG1yMXhBV2lmL242M1Nxa1NNdVFlakhQWXJnbEhaMzBKdmlzM0k4SW9xMDdNTUV4WWNtQUFxb2hSL3crTGx0M2Y3cFg5TzIvY3pUNThaUFgrK3FLbmNORzBaL1B6b0RLL3E5STUwVmRlN3E1Ny9WbGhqeTUrRyt0Y3FZTHNFUEVZdFMzYW4ydnV5M2JwUTFVNmdEYnJLTyt6cnlhQzBhMVRPbGdaSFBvTHZxM3VBK3JULzBvTHhic0xGUU9xWmZyUSt0bDkzb1p2RDVFcE9aTE54aitmL2VneVgxbkxXWS81cmloOElsWWN4UHVveDdxM1paVVdITVFGeFZZOExNay9YMVVHalo1RmZjL2NEWU5NZWZnbS9hQWZnRnpKb1NyelUzNS9MSG04VUx2YnRlcXdsL2VHSmFYbTBXY0Y0eE91ZS83bERHZUVLenZxY21yMTE2SmlpeWxFNnZ6UU81bi9jZllJNVBqSitlSXZabUx0a2VmempEbnNQRU42TzBZeS9KN0FoMXlmTXYrNVBhcTZmZnE1dlJrV0huWDVNRURXeU9maDhDMHhzcGxPalp1ZHhFRWQ1Y0tLMDJWczY5b284VE9ZNlhsRGpKb2JCN08yZm5kZVlBOTYwc1A1Z1lFSEFXcEdoNmNyb0srZE9wRDJFdWZaUUtZWDRNTi95SjMzMlRHL0tuNE1lTUxCVVh6NC81UWJZd0JqcnFCVG5SdzdTOUN4ZWRJanJLK0Jvc1dqZ3g3WDhJcHA0MFk1VWdiOFg4K1kxMW1mQnpzdTF2MlBxTVE2SHpZK05kTmVyUEtKQ1d4RitTR2FpVS94eHVjVnA3M2N3NzlUUGJkUWRoazFGdUt3clhjb201SlcweUExdkNKYWZxK3NmSWlxMi9OTnRrYUZsc2FFaThZbFBqVGFxUkdIeXpkUGt5OGJMbDNaL0xWMWduUGxoZW9KZVA2ZlVHOW5HbXMxQzZxOEhyci9jbTNLVFFOMjZWSGpuOXhCRS9ZcVRRcUJqcHZ5eGdDOUpTL1YySFBwSERkLzZ0ckdnaXBCcCtpOHp4cDdDVnlTUy9ScmdZTEIxZXpjbjE5SU9EUCtveTByOWhRQmd4Zis2Ly96RjdpSXcvK3ZsL3JJa1hZM2FDdkg0VlZLU3lGSEE1Y3hkQXVhTzVNWGlXOXpXMGRrMEQwM05kZ01BSXozMnQrdDk3YzU2K2x1Y21oeVhsajRLaUlrN1dvdUxZWEJEM3U1bC9FNjJkS1A3VGRXRWRCUDIvdjZIRTJraDdrMXUybGZvRG42ejgvanR2RHd5akxkQzJyZ3FabTltMTROZVp6ZjMvVEhzQWRjR1FKMkg5ODgyWWJpMnFVL0xnVGhSZHYyOGtZbm1tT1d1Vi9nbC80V2hYZE50dnowK3lyMnh6QkhPdWJxMnMvNG52dnJUY25PU1N4RFlqYlZSS21LbXMzMWM2c2gzd3o0Z3ZuanNmdTZBS0ZFNmJOdFd2dEpLNG02Yk1lREtwZUZQYURiMU82Ky9vZWdVR2orYVdwNTRKNVRRU1ZMYkE2U0JyRDBIVHgwb2FkTFE2K3RkNC8vVTMwVG03VGxuODFmd3NKeXZMVnZEMmo1RndhaWxFVUNQUnVZeTRjQVdOTVI4ZmtGMGxLdjJUcy9wYXJ3b3lmaWpOSnBKeWp2djNSaXFtM2tqb21hSXB3WFFqekhZQ0Rlc3JCSnJPZFUyeWd1RzhOVlZMbHdrWU1vTFg3L0pkdFpOKzllVUN6OXRadWFEMjB4cHRZM014QjNsejdZYjNSRmV2RjZZeHF5a1JUUXN1emtuMzh4SlJWRk52MkRjOFo1eTVPZGQ0UTljVFE3cXhzdW1lYVRPNyt4TGx4V0ZlWmJKQ2I5bE8zM3craDV0NDJuNENUMHpRL2EwTVNwRlJ6UHRSYkVUa0JGMEJFTW1PL2NpTGpTSTBHeXJvbFlUN0hmcU5kZXYybWovQVBVOC9tZG5OS2JXQXU3OExhL2lreXRobmR4WHpLSGt6d1c4TS9KUC9uaWd1VmJZVktzZjBab3MrTkVIa2ZWbDRmL3NrUWEwdy8wdG9QRVJxMUJJK245YVoycEZ1M0xOazQ2WTl4bzcwemxRYTM1Z3BQWnNVU2N1SktiYTE3ekg1clk3OUU3TlM5TmZkWWZVSUZKWkYvcTI5VWo3bXhDZWwrMDE3c0FPOUZINEticFRpenJFVTdUbHZrcFFwblowalRoakoyR1B5VDVxOGYzeDFpdUUxTjZzR0ZYU1grdjd6cmcvcEZQWnBmL21nMXRYdDY3Z1lIUGtyaGZvaEIyTkdTbG1qcnpTNW82M0JZODZHRlR1bGgycjl0c2tpRnMwa2J4cXVYdzQveitkQmxBaTYyd1ZlZFJqemVuK3BFNTJheWVHc1hqWDBQTHU3eWJGMzhaOHNKRk80ay8wTFB2bkR1a2I0YU1laFhDb0VZTXBXZjh1T29xdUg2bXdXL0NFUE5hK2NQamI3SXBnMWF3RVZWME1EVFluOG1VTnpyOHk5ek9PbWJjMkNZWXh4RFlYUWhmRk1md3ArK3hiWmVVY0k4M0FNS3Z1RGkwVFJ3eEdkS1lERnhmTkpnbnp5bE83SGwyRzg0NCtQM2k5cC9rdGZWcjB3Nml2c1ZZalR4WXJlZDliQzZsTHpoQ09ySmdxdnkwYjBaMTRqQ0xLc2l2MWRVNklmU1d5Vm82MkxjWTdGekFhaDJTVXllaE5PQUdqZlhtanZQZ2dRZUpQQkxoM1JQM3FsczRFVkkySTExTDd5aUJ5SnNwazBCeElLYng5QjFpYlFBMHlFQWt2WkpEdGZYbitWdTRpK2FjU2tiM3RWdEg1S2tNdVpVNmhVcHJXODYxSmV2R0xIbXFrUlpaOGtpcEtGa3VzZDlHUWhVNmNQYkxVeEpwZTNFS1QyR3ZZSXRWS3hQajRHd1BSRmJ3UVAzYjZmMlhzbndlcjVMK0QvUUpHQlg3aEEvUGI5K3h5NGxwWXQyK09VbzlNYmt3UGtQMzlLbGRFYXhhR0UrRXcyK1EyTng4eTFhYzZCMDlFd3JZUkNFZVpJeW04bE5TUzg3Qzc3Tlgyb2VrUmIyZWxhTURMWUo2emg2MHNCK1VOUnJkR1B4Qnc0UUtvTnFxTEhueVpxQis2Nmk1eEg0bjc5K2RNSWpyTHpnT0NxWWlPNWZEbENSTm5peHBRQnQ2bVFQQjBOb3dHRThxU0FLSjgxZjlBK1RsWnE0cm8zYnFPYzBjYU84dS9yV2FyMDB0ajJGWFNvRS8yZWtweDR4cWszY0YxMHlhWjNrOVVuc0hCcnZHekRFdVEyU09LT0hMR3ppZXBUSDZKSGpqRVNUMHVWaHo4c3JabEltMVFyTlFaOXFHQWVzcTdsOHdsYjJRdERqZHNkUTBtcTlGYzBnczRjV3E3bUE3K1FiTFVIVUk2KzF4NG5IaUFTa3I4cWZ5dE9wcHV5WW45VThWUkwybUt0b3RKb1NUYkduMnBPTFAzY0p3cXJ1REJRV3d3Q1pIL2dqVVdaK2ZWdXR5V2Z2bVByRERTblVldjVOY0p0WUNjbDhQcTRVSHlHNGFYTW15MmFZMW13OXR6a1pjSHhzK3dSZGlPVVFCV0YwZU1TSjZXMXAwM2l5NkI5UDlMcG8rRTVOTjc0eW1GbCttdkduVjJLbjhrUHNUZU5hQTJVRFRFSmU3TVlienhWbFhIVzk5bGRib2hTT0I4OE91NmlSZG5mZlVwRE9EYUltSEZYd0gzWXlZaG1TVVJmc2ljWFc0TkZqWVVaazVydkl0aWlDVGtoKzAwOENJNGN2aWlKWko5N3VTNHRKYkFobkNiYnNSWEtuMW95TUlOVGI5NTZieDI4VXZwWDU2WDdPOXFqY2hMU2c4MGVsNlVHM2JsMXdQWEFEckNUc2xxcE81MnRub2JLQ2JrdEJlWEpxcmg1UVhTSmFmenRaN0Z3NkV1R1hWdGh2NVU2d2EvYlJnN3p3YTJCbHVrZE5JNTY3bXZDNzIrTlBDUUUyNXg1TVpPLzM1RTc2ZXgvVHBYUytkL1pSRHZURlZOZlgrL05WVlJRcmxLVHJxcEZ6dVFjZUJqSm5ySmx3ZXViNHNjejVzdVhQQzY4UU9nM2hmSmNjdThueEYrZm5XSERveFJIeE9hd0dGYXZmaUs2MUNxZHgySmQ2OUNVL2dxblpuNmo5cy9NSUd0SmxpVlZxeW4vOFByZGR6R3JlYnlnUEVrNXBDc1hGOXVpK2FjUU9vV2dJMDJMaGdPNXFSNUtlK3RrWHY0MVgvaEpWV3dGSG1TVEVmWWNrNk5vYVlseHRFVFJMVERPNi8vbEh5RmwvQWxCRENQQXQ0VTZrZm9iQzg4RzNXci9uQzZzTUxreVAvRXBaYStpZWRRTTIyUVpaTGZJRkIzQmI1Mm9aM05OaUQrRm5ad29jdDVnMEtmMjR0V2JJbFhmUEhlbzRGSms0UE1SWGRmQzFTZ2hXU0RKSmNxTVQwT1pjN3NhTm90SWV1UmdSY01QcnFTZHh1TmdoeitGclZHRGljOStjQ0oxWmpKV1ozMGVEVllzTTMyaDFselN5dHNoVkp1aXo5QXUrL1R5QkwxekRKVEc3a2xhSko2U3V5RkVKckhacjVaaE1DUGo3ZmphK0xwOUlYM21aSDFvM3h2blpkMUVYMVN4NStKYlNaRm95MUw1ZXFmZmFwOTR0YU5DV0J2VGtzb01VOGNOQ3F2TXZTRi9VckxmdVY3ZXNvVEtTOEZ4cFg1aXRZdlJaZjB6OVloQnBBOHRTTW5oUWZLVmltVzhEYjA2ZWRXWEFtRzhRdHM5L3llUDBzSGxydHoxNnRTcUdVM3JnQUgva1V1RkVOK1V2QVlBWlpmbFZQRzRhT1JOWENlT2VadDlZdEV1VHdQY3NERkZ5d3lqV29iUFE1MWQva1l1RVhqdHJTUXV4WHNyK0M5c3lITy9lQ2p5NURNcXFUQU0ycjY0b1VqSVc3Rm5SZVVubFovU3crbDQ4akRwSG4yYTNFblVKcUFReERLWmo3d3VYOStyVTJTekJ2WkE0Y0lpUmh3amNkWmozRXZXTmwySXhvcWhuK1RRYVNnOHhpQWlSc2lUSjhoTmNzekQrVEc3dHZSNlNRblZrcGxuaVBqNHQ5eXhDR0VhbzJqUHBCNE5pYXRLSjlWUkpjMFRoUDhnMEV0VTNlT2tRVitaQ1VWU2hYdWVnUEorYUZFcVQ4ZXJma2NwMlArTFRxZjFVMnFFUElFOHBUOEVKZDE3TkdaNWR3c3JGYmxHTVJ6dlZtWDZJanFscHR2ZGF0RTV2ZTQ5WFB5dE1PcXFSSlo2ZmFLWmdnQm8reEljU0dOSENSNU1OWlg2QmppUUZBSk8zZkl4dEhLMVV5S1ZTWUkweExwV0NpUW0zQ0RqbVUrZzFLUGdhbjhEaE9xZldsN3Vvd2F2aTZrUE9yT1lZSFdXakk1eTg2MEx3ekFobFRtWEJzTmord2kvSnBWTmhSQ2lKRFFic00wV05tZktQRGt2NXFQMU1rT3hRWG9GdEJmekFUZVpJYWVBbHhJTFZpY2tDTjVjU2tYdXk3eTVzdnNJZGVsNXZsS2U1Uk5NemMzTDhRbDA3NTlsaHpoQmpnbkJhSFFxTjdFR2ExTlVZd0tNWC8yWU5LZWc4N1VCVnVyN2twczVVVUpCRldTaWs4M0JDWWtkRFN0aEcraDBKQUNjTDJpclpsWnM4cWZncytQSHJlV0orTWlIVWNHMUNqYjdlcGRHaDhaSVQ3cC8zNitqRkt3bEZ1T0hzWklzRHVBSjVnYzlEbUJKWW9YeE1aR2dBLzZ0ZkNFTTdXcjFRdU5qRUVoYlVIaDVKOTBhSEd6dEhHaHQ3Mmd2NzZsZXVHQ2owbVgwOXBSWS8ySzF0QzJ2V0laVTYyK3RwcFdzeERJNTN2ODJwRXgrRjJPaTQ2TmgwbmlwRDFxNFFYRytqUld4dUJCU212SGVEUmJWVnArQVVjd29NMTV3L2JPZi93MjVYMGQ4VkxnNGduVytUYkVCN1U3RVBvQnM3ME1DeGlrcWxJVmpDcW8vQlkrTmFiZytJY2Y4R0VROTllczgvTk9iWlVKRzlMcmRtMGRCeU81cVZsUDhmS0hnTlVnV1YvTGhtb0g1ay9BRU5KUkpRRmFBb3NEc1JCZHpvcEJpRGpZcFBRZGN4dWVKZWZ4SFAzS21FNDVtM1VNVFhFbnliVERxd0lGWWhwemZ2N0puS1p5YTZlb2NMR3NtV1A0VXZENVBmWnVqSUd3YWZPbjl3VWhXdWl1QWx0QitQYzR6TnlHbHpJam8rODRJcVlkemhxUGg1RXgzdlV4TWV0SmdkaVh6anY5YXQ0TUNmNEpGQXluU0cvN0tCUW11QlhnSFRLdURxSFNFUGpDRU15TnNZVkdVSFdqWk9GdXZsYnQ1aFFLQmRMWHdZUGdITyt0cTBtd2ZESWJEdFRvSVVTaGFDT252NWRCRUNudVl4VjVUVzcvcWZGc09zeW1KTkQvNm13cWlDTlpJU2V0a2FlRmZYZUFtSjJLcTZ2SzNWWFEzWXpIemxTNmg3R3FjWkhPbnVoaGtSL2hqRG0xTldsK01KaVEwZTR2U2tuNTRWUnJ6Y2lZZGVJclZuVms0UHpLQzN4cWxUNStRYkpvR2dpQW5OOXg3Tlp3Ry9RWis5YWY4VXpIZ3Y1LytCSDROWElxbzhvQWNFSnpHY3Z5cFExZjBsZ1c5aGNUOExjbVY4dUI3M3lrbEc4a1pBMERlRzRGamRzTFBvM1J5Zkdham8rQkdhckdQTHhYZlVBMzd0ZGEwRm9mMVZuYUt4K0lCUGs0bmhiZlJpR3QvTFZCbllWeFQ5Y2w0bDdHOU1WcFNGTWhCWXlOa1hLcEZtQ3RJWlNNa0RSM1B1ZmhpMGNMaUp2YmUwd3MyVW1aWUtERTZkNjAzWmsyMWpXaWhuUDJmaHI1OEJISlpCaHIvL0hRT0FQNFBkbzZqRUxybytzZ0NGcmUyLzBwZWZxUW1BUFpCY21FYmtqdllpOTRaMFlJTkRObzdDdCt5dmJhT21CTGlaRlJXSFVYaktNVDJ0RUl0bktzQ1NsdmJmd20rbjZtdVNZbS9KU1gwaWtDb25WSFhYSHVkRHorZkhnU3d5M2VmS0ZldjMxbXZ3SzJ4dCtHb3VOd0RrYjBGMURCK1pDQkJVU3pPeUFwL2JQTFN3cHdJOFVQRnpJWHQrVmlBTXU2M2VJUE5QL1UzTkNmMnZ2dnQ1KysrZnZydzV0WFRSdy91MGF2NTZxeEo0NWx2RzVvcXp1QjNmajd3Vmh3OVdJTGpGd2wwNlB0cnMyYm84QmFFZ0dLS0dPRi9RWmgrQnlERi9leDZjbFluOXFpc08zcDdSTUtpSnhEZ0NSTU05VXlwUCtGRnpJenhacUt2OG9xWitIcC81OG5qdTNkdTNTeFhCZkVYc3dYSjdrU0Y4b1dqeW1kZlNFV3FDK1BDUHRDODdEVDJHWUhlSTFDN055S3AzcFRXRm1HUWtxZHpHbWhScTV0KzVSaE92V1BXTk1ESS9JM2l6Uk14eVVWT2pGdklVbDkzMDVkYWR4NnkrT1dqaDU4ZWZUcWZOQlZ5V3J4VlR4bVhkQVhpWENzOFRSVW54MW1EVXlnNFhTUjNxYW9oMnBIRnJjR00ra0VRM0FQd3hSMGtaYTJzSGNSUjVCK25SdTJ0Ui9QRjVremxBQnJDVHdwcTdBa0ExY1B3MTV1dDJuTmpoVXZPaXlsSGNCL0QxNVlJamtVa0hmQUhrTkVqaksrUFZqT2RvRnZsSTN5SUFPa2dMYnBJNVVwckIxL0ZCa0JpYkpwNHNOYlgxNitPYS9XVGlNaWdUYWRGSVJEMVBOSjZSd2t2NlhZcER5QkdlRTNacnUyRlZsMHljNGJZdGtyTXpXQ1p5NnFzT0lpWUpFSTdVQk4yNXhtdVVZM2NETUUwMEtLdVI0UENVL1dPRDQrUzR6ZGJFa01UbVBTNHFta3FqNDlNbkQvMlhEaVBscTFSR2d0YXJ0R3E0SDZHUGREOUhiQVVtcGhBVXZGMTdYaU54R0lvc0p1cDB6b2FLUEVGUXUwZHpnTWp6bzNTaUVNYkR0WVFYS2dySE9NeHY2eTQwMjVURDhJem5mSEJBUlpOQVVndmg4NXdwODZUTDdoT1huR1hCU01BSXJ0alVWN2xhZjlyVTQxSHBYV25ibWtNeWlycmRTWVVLTVppUGVCZkpLdi9GVVkwdFRuak5iVlc2MWtydUI1b1VUc3R4UnFGMCs4cVl6M2JpSmFiRFd4SjdSeDNZcWFYK2pYZ2Q3ZnlrdWp4cEl3YzVWTWkrS0dRWTJyT1M0VUlCajdCWHRnR3VjaGJHWnJVaUFFUGJUekgzQzIzK1o5a0hrOUs2NmljTjd5dlZwV2p6RkFkWXg3NHJDZ1ZQSGRsRVhQL1VxbW9oakpvT0gvclhhL1dQaTVjVmxXaFQxYVRVK0g4RkFBcnJIWVRrcTczR1RjbG55QXNVUnpTMnRUNjNoQWlHOHV2M21NU0hmS2xyaWsyaXdRQzEyU3NVMTBQTkZCNTVoa1VJaDJUS21YdnFBdTZxN2NRaVhkM1lzNmhDQlEvMmJjSnJMYUp2Z2IrUzJ6SzU4YThyY01adGhTSTB6eDlXNWNLbXhWQUZmcHlBcFlSS3Rhc2hXMEw0aWh1YmE3YUd5b0o5dGNWSG1ma3RDNHI3aWhBYVp0NFpBZG1PQUVZdWk0elpzVGx5UmhNblJnV2R3OGxQQXNNTUl2aG5CSGpaSXpBK0xpdGFJQ2FENW9zRDd2bDdlN3EzR1dKcWZTd3VrMTR3WXg5ZEs1UUwrNnJ5S3BLVDVDUS9sS01kSHJ5QlZOb3cvakhqalVFaXUvWThWY21CR3kwYXZJTmpJMGJpa3J3WWk3dDV3MkovRnA5R0FZTWJwU1VnWHdsZ3RnUjhGS1g3ZzFwcS9kdW9IT2dhKzBIc2hlRmVMa1VGYU5adEQ4YXJRZ0tqYUphbncyT0p3YU0wR0hhaEtQQ29takJWYjkxcjVBVGQxR2dPNmdMNWM4blNnSTJsZ2FKNis1QlpZaXNOTXUyNzhvSXJkVUF3YW9Cb25wdC9SZ2pqNXEwL09WQ0NJUWJ5LzQxeEJURkZlL3ZWb1FObWpjSElFUFpFeW40RzQ3VmhjVWRFOW1odVpqcnl3bURBUjNsVnE3TzQ2RGw5Szg4Vmo3SExFMU9xWkpUR0ZYdGFvS0RTaTNsY2RsOFM2NVo0c0hGZWxXVmVSWUZqcTJqYWNFbFhzcWIyS0VGRzZpclEwTU1mUVJncVJTYjNSUmsxWHNKTnhxc3pWMDRpbFZIUVlwQ3cvUjVQVVdLaklnRk5vU25YTVZoSEhRaTBnNDhtSUpXN2FzNTh5aG5NQnZuNjJvZCtCNW02a2puMCttdnpoQXVaTGZvRXVKZzE4VjhQVXhrQk9VRlhDRFNib2dKaE91RzJLbGZqQmEwNnRFSWFEZXh4ZFJRK1Q0NWVwVksyd0RXRldhczVFUVlwVUQzSVBMREYxVEVRMlVtTzNUaXdSTnNhR2RqYXdaMzE0dTJpYkdGWk53ejl6QzFucnpkQm9pYTRrZ2J3N21BQ3RQR2grdzRhT1V4SnBMbmVaRlJRMTljbmFWNjJGUmhHbWQ1eXZ0NS9PdWpDRVNPL09JTWk3MEZLc1djQ2p6SEdTdEVwdnZsbEI0cnJiZHpZcDRtU2VYc3MxSVNZSndLNXplQUkyMkFId0FLY2I2N2svSzVTY2lxc2hWeTg2VEl6RTdoeFFtMXlOUnpnUUFnWENBRkxFYXcvdHQxOGZLeTNodExWVnFVa1JKM1laYVNnaGExWFprRXB2ejlneU13MzZzcUY5c1FwLzNJTkFSVVNLc3RTWEZaZ1NRTXAwV29vWDhMK3F2MVpUdzBpbll4aVhUZE13Z3Z5QnhnTjErUGhSUjNhc1NBMzNXSTVWaW9NV0o3YTBURjZ6Y1BNbGRFNVFFaWN4WWk5VGUwdU8wQzZQNEdGQVJyeXNUb1pVaklNb2p1ZVBYQmVCR0h0dG5CcXMzTlV6SFJZV09tVGJVNktqVXh3VnM0clJhN2VUY3M2MDRDdDZhNFhadzNuYXhCRE9VVGpBUExsdS9MTFA4Nm16b2ZINFoxcHNBbE5qU29ZRmdkdG1RcW9RY1ZXU3BzUGpwZmt3N2xTK0JONnB5blFsTmlUb2diRU1FNEluOVAxTlFHQ0I3UDFpN3lhNVR3bVBjSmQ4YjBtdDRXdGVmTlFqNFZiUk9mK0UyMTQ3VlA4bm03ZTZoZmZlSXlHQmhTZVM0RlJBTXdJU1lKQ2R3WTdrRjJBbmxkdUp5Zkwrc2lpWDNuZDUvMkZFY0pnbUt1MWNXd01GMHh1VEJBTWRmMDRjbllhVDNRb3JiVkplUTVpSGNWdVBwbXphZ2JKeUlwYjN6Y2xvbUlYc1pVaVN0amtrYUJqcGpydkY3Q3paRHo3Z0diRktqQnFCdTAySzBVdWY2UDlVOExKUnQyWklnMXMxVGhkVjd2Wmd1RUZyU29KVHNUdmFiZlB6Z1U5OEI2VzNiRGUwc0FwT0VpS1E4dFlhSmtqdmdhY3A3aWRBdUo1LytwWlJQMmt4UzZIcU5RQ2NCZC9KZXJoWSt0WlUzdTl0RUdHKzdhaGtKZDhjQmVXZ2FXZ1c3eHpXZnQ0R3RwYk8xRll1Q1N5QnRKWG5US00yeDFHdzZUeEVwKzJmbE5ZSGxaVXpOU0MrOC9CbnRHQ3pia2FDd3JuTjczc1R5UGErcWE2N2F5QnJ1aEdVRzJxYkdCUUxTYXBtSWQzeVFycUVEQkR3Q1VSOWdyWCtZdVh1bk9Hb2dtc1NQRHc5bGxKV1FsbitEamcvenExb3dQaFFXQ041aHhsRW9yUkxvUmVMNm5WS1NtUWVwVEo5UWJwSWRNT0JVTVptZTVaa1dNblFjWXdCdkI2YVM3V2QzaytmWXNQYVllNG10RXo5TmFaZnBrRzBUT1NJallHbEVRbzEydEVSNUVPVEtSZlYzMDlBUTBjQTBhc3BtazRJN2NrbkJOb1psOFFMUEc3SW5kd0pkKzhud1ZHSEYwRkpFOGljYVF3M2swR1hSNHZQYVo5ZnoyUXRMcEhsTkxXV3FCeVZNSElQSXd4MWllRVBVZnBMZTZqclZCY1AwWk5PRHF0bzhZemtEZ1pnb082SjhsM1BiZUxzK3ZmSm90Tm9OYktLZ2tBSHVNck9qSVRwMnRselU2b2pKcjdpTzhhdUlxUzBaS0Z2SFJFc1FaUU9QUUhqUnVhZjdLWWhtQTdycVYrTWlDbk5SZ1hOUFNTZzBhbkRzTEdBaEpqVkZqNjB3WWdsUEN6bHl1WFgxYnIwekxwcU12TksyQ2hsbHV0NER5R212SGdEdklxUWxXVFBKcXQrdmlxOXQ3ekZkNUJ0andINzk5OXVUdTdldnp0b2tEeDlZaXpyUjZsWEVKblBod25PejROOTNNSWtoQ3A1QTVOMExyMUxCbjBXbTlyM2FSdjkrTVZDMEI4TnkyTzhBbnlUOGxBSGJCM1pidU9UODZtYVp1S2gyNXp1Z09RcDlwSXVtdTJzdFVFZXh4aEFNeTQ2Q1pLOW9OYm9DL0JrVnJ5dXptODJEcFpNbVdZcjdlbFVVNFpFTnBRcTFya3d4THV5cWs5U3oreEJJd2wwSjdaN1RkMFpQaVRrMms1TndPYU5lWGJ6WndiRzVUZE9NNmo5Z0c3azNLbWNFWE9VTVFodDFZUHJWK004ZGFVT2xKZ2tHampncTdhN0ludXlUU1FBdm1LeWxIWEhmYW9OVUVFcnc2RG82SDZvaDg4QVJOWVQ0K2Q1V2U3TkR1VlIxS1I1bzVwYVVHbTAxTm5TVG1PSGVybzFjbHBZN3RnTEw0dkhqTFdNVHJkV1lCTTlXSGtyWVIwMEErTkd4eVNBbVJiY1dhSEppZHdxZjNYUXFxY0Y2STRDSGpLbUxwbGRnZHhLYWJ4dHN4aTFQN2NadTBMclpNVlo2dGNJaUhjcU5xUDVTbXhiZzRCSkVIdDJvaUJYNGtNazlFR3MwT1plZkVISENlcjRwaVQ5SnQyayt3V3VFcnBIQzBFUmh3UFEzSlFQN3B0THloVzNKZ2dMcFdtNjZGazhXRDZyRE14Wi82bmQ1a1pGZGxYMlhGbGlGdElZSUMwN1VsQVdzWWtuMkkrMmZyVmVETEMzakVLekZtVDYrTHltZHdyT3p0dmZEbG1NSDQ2bW1XM1lwYU0zTlpsQmgralV4aXdyQ1RIVFdJNjdPY1JBRzJWRGhiNHc3dkJGbGVvZHVRYkVYOHF4NFJUU1h3c1JTN05MSmE1SE9MU2lYbkVZK2F6ZUo3YTNJbjBMdUlrNVhVNkxyaHlWM1BRWEpkY0prWEJ4L0hZV3hyMEJvN1lzUjloTDF3Q2Z1RWE2L0RrM1FndjNBYUFnbUJJZ3R2M1hERUtQdENlKzVDQWtVMDdOMy9BbWNITGR1bHk2SE5SZk1rUy9lNGszSUh5TEVpMW5BSk41OVc1R3c1aEt3MXVhT1dDb0tOeHBIMUtqMjNDNkRaQ0M3SHE4ME9ISklqc1hpQjJicXNSbG14NUxNWXpoMExVOUZpMnBaa3pZVHFRU0FhbmUvUEgrV2pzMUVKVGNTNEdEa0hJWHZHeHE2NEcrSWJBRHZZMlNtUzFCYTdldFVac1puQWYzVXBvWkN1SXJENXJxOHZpMDVJaGZLcE9ZUWUrVHF5VDlHbk9YbFZ0WTRJRUorRlpEL09FbWpvUjVVSDFWRzdPVjhCQUVuaTVOUmhPRDNNQkw4MVprNkhWbVBIYnV6Q0Y2MHVLV3k3QjIzZzByc1g3TW05emEwM2VJSmcyb1hVd3hCbE05aUQvY2Z6KzlHVnJLUXduTFVDWkZoS2dPT1BhMjNpYTZpZGlLNU5BZWt3bkVqQ08vcHdsWkZIdms4SmliSVlYV2Qya01HVG1VYWdKZVNZZ0FPYXFQY2VuaXVhTWFDMXErQTlTUGQ2eDcyMG5pU3l4b05CTFZUTld3aEZ4UHNkVkdaM0lYMzVvdFJpa1BhdE9neVlCTlg2bnNTby96b0RYblRIUEZGWk1jdytweFRET3VuYTg2L0NVazcwWk5lUFZDWU5FUnB0aVN6YVNmT0puajY1UEkzWXRsdFdpTUNLZnF6emF0cmwycFloUlp4YnFRREdqeWNzYUtjMlJiMEZxNk9GTVBOUGNJS2tiZzNMMnArT3FJTjJvWWd6UjlqMnMxTmRnalJaWmZHeHk5eVdyeWYreGd6b0Y4K0xQSmg1RHRQZnB6OEJaRE1FWHE3VWZ1bC8zdVN6LzQvY1dmb1BBT0RySzBPTEwvY0wwTnlqZDBEb1lLQ1Q3SklEUFNqdnZBRDRCRmU2SDdrWlh1aktSR2daWHR3SzBBVTZVa05lRzFsVFFkOVJZYTNKdEdIZ2kyTUs0bGhUdTZOS3lkV2FockJGcGVDMXJpVGtqV2xla0JqSko5UGdMcmhnTlViU1N1QWJsTHhpWmI3RVdpSlRuMTlRUS9TYnFwN0lSUkU4cmNsLzNJdGNxS3lQYTBDa1phYXhSVnlRbmllS0tuUkVUQmVKS0YzK09uMVVqckpHQ1FxT2lFcUZ0azBEZUFiaTFMUEVIK1JsZk9UeHYyNzRFSmNLbk5RekJ3Q2FpRzhYNHBrY0FGaXFBZVltQllMd2lLV2Ixa3RHSEVoTmI4Q252cVo0eE1uZlJabmtKZVBIMTFBY1Vla0Y1b2lLYk1HRjNPZnhoeFZxaVFuaUVua0JMNEVvNThsdlBPeXFVS1JqMkp2YUtLTktrWm1qa3NJeERRVGNXbzJMVWpGY1hEKzBmcFFsQUdpUlB6eDVqZVhrNFQ5cXBsN1VORXdxTUxBWXRFOXQ4dUNNWk4wRDZuTGJmZllBeDBEaTdUa0dSbmZ0R0lUSG04ZFFKQWI3R0NvaVVxdlJwbVBDODF0U2VXdEdNTlhiSEticE5GMjNGbE0wNjRWTDFDQUp6a0JISjQzNm91dXBHT2ZVcUVkYzd4QUlLb1FhOVFZanI0TExMbytlQmxQNWZub21sS3Jud2lKMWloR1pyaFBTUmUybXk5Mml3bnlwR2tFaHZjZU1OSzVYYkdJM1RSdGNkOU5lSFhyejNhWm9OQlRSdVAzb3BNTVZ4ZFg3RFNQS2k2c0pHSUZwZmtZV2FWS1ptS1ZJcWRpUThKNU5xajFSbGNrdzhMUklvMTBkcFp0Tk5XY04wb0R3UjVJcEtSRGxqTVphQld2cWRtN2phUlVtT05MaW1vVUxESTJBbHU3WG1KTEFRM1pTWEw0NjdaWERUMmlsTXlGNnM2TzJSNGtlM0tBbWZlQkIzVEZjeUdyVEFoNVJSUTNyZlJJSktlVzRjMlJkTHlpYkY2NUFwN2hiZWExWENhaktoejZqcDNraHpubWJYb1NyMDEvU0xkb01xTWZWVG5naWRVWXpKUC9iZks5MHpGQ2xlM1VXWGJ0a2VqVEFrTzdVU3hHTkE0azJ3Ri8wdFFxNGVidVpzTWQ5L0w5cEJ2R1ovN3RjK0wrWmU3ZEN4OERFRWlaY2hFaHNIRnc4ZkJnQm9TalJSR0xFaWhNUEp5VkRrRk5Ja0NoSk1pVVZOUTB0N1RJR3htbURMQ2oxcjB1NlNUSllaYkloMlRtNXVHWHh5SllqbDFlZWZBVjgvQUtDQ2hVcFZxSlVtWElWS2xXcEN4aXJMYkRLdHo0TEJKLzdJU2o0eXRlKzhZWHZnb3BEZ2RKbndCenpMTERJRUx1Y01lYXNjOFpOalBVdFJXdHl0TGgzWjB4NVN6dldJc3ZNc3hHRk9WeEdlczFzdmFHbGhmcUtDSlkwRzFXMG5XVmFGK2JMaGZuT2d0d2gzdHo0UStqM3pWUDg3eVhxWEQ1QSsyK2RJUG04K0N2UUdXeDNxWGxyMC9xL0FnRENVS0R2Q3R2b0luQU9kcmgwdmZUT2xyNVdDRWw3eS8zQlhGRG50VDRxSEtYMHpibWpMNlN6ZURUay83OStWZzJVYXBsUUR3QUEiKSBmb3JtYXQoIndvZmYyIik7CiAgZm9udC13ZWlnaHQ6IGJvbGQ7CiAgZm9udC1zdHlsZTogbm9ybWFsOwp9CkBmb250LWZhY2UgewogIGZvbnQtZmFtaWx5OiAnS2FUZVhfU2Fuc1NlcmlmJzsKICBzcmM6IHVybCgiZGF0YTpmb250L3dvZmYyO2Jhc2U2NCxkMDlHTWdBQkFBQUFBRGljQUE0QUFBQUFkU1FBQURoREFBRUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQm1BQWdnUUlPZ21YRnhFSUNvRzZGSUdlS3dFMkFpUURnMjRMZ1hvQUJDQUZpSHdIZ25FTWdRb2JSbVZWQjJMWU9BRHdSbitPS0lLTkExQU44RVFVTlhOUk9rVDgvelZCamhGajBKOVR6UHh5aHExenp2QmExbUZvY1hpdTJOOGxTOXFPOHp0RFNIMWY0dXFJRmFnVlJka3kxYys0NzJOUHZpRklvY2JuVDlaQitWSTNxY2xhb1R3SXg0Z21KVTdHSDIwS1hUMUNZNS9rRHM5dnMvZmVCMVNVU0JNRkZBT1FOcHFRU2dGRndJSDJkTTZhenMydFhaV0wyK1kyViszeXR0c3VNcnpzVlZ0VjIzK1pXVlU5Z3phTjJGOHh2WWtESzBxdzgvVFBXK0ZHd09GNFhlSS83c1hmZTI1YUIzcGtLZDZ0WXpnUnk0UWczaHBnQkJvSDhQOW44SE4relR3cTg2ZGsyNlN2UDBYS0FuYjVNRWVrZ0kxQmU4YVNNT3ZrQ1hQNkJ0dXNLaFloUVFNVUR5VllsWTVKMXV4RS9YZmY3eHIwNzdmNVBuaHIzQ0ltZGFLSXlWYTd0VFUvT0FEVzdPZi83LzhBV0lEL2wxUDcvenEvdVkwME1yUjJOM0c0UUU3WldZaGJUbDUzcEhHZkRpNTczOU15eUZkcmV6T1VVMEJjeExGbkdhNUEvNXZUa3E5U09rdUc3TVNGQmFZN3VYRjNzZ2lYeS85WGFmWW02YnR4V25hVElXREZHbmozSlQrMGZiT2l2RVBUdkdFUXl5SjJzbGhXUGlvWk5QbFcxQlIxdXVwbGNhdldjdmVCS0RBMSt6QnRWTG91R3Vtd2ZHSmtuSXZwNVgvcEY5dW5zVVBUaVhzZ050K2E5ZG1xWkNHemdEQnJQdnJCVmVZTDgyVjFkYVVyVlIyc0h1cGsvcjJaWkRHejJGbWE1RU02bFlYczVET3FPNTc3eHk2YkxHY1pEd0VzZ1RwLy9ndDVUcE1DRmthYzhrRHd6M2V2NlgzdlQ5cUlCVHhLQWNvWlN1bFVtNVl0SU4yWGxtU3ROTWUxS1NOVENxRDdUbjNwY1NTYjJkQnM4SlhHVlJ1RXlrb295VmZiRC8xNjJkSXVlMTJJRW9WaDhCOEJRUFZIYllBSDBIV3VBbUROYXZmd2k3d1FYbGRlQ1B5MzR1OC9lUG00enEwY0lPN0F3Ymhqc2ZyODYvNEFyRC9EQUlBZnVRZ0FydFRpd2pEbTB1RXlHT2Qvc3hnM2ZqWktVcVpDMERSenpEZmhpRW1YU3A2aXZKNjM4MVdtZW5NLzFUdDdkMC8wL2o3VVI0Y3lONW5MZWZJRUFGZUZLbUhSd1ZmL1d0N01GL21teHk1NXNJOE1KVzRTTiszdlI4WmZQL3JCOTc3empTdk9PK2tReWYrZWYxdnRzTTdhdnREMHNROTk0RDN2ZXFlc3VIRFFpNFVOMkE4cTV3RDBWSzhEVDFSaEFlaHovQk5DaWZkV2VUZkZNV0lBNG8wNEdQRDBIN0szVDJudVAwWE8xdlJlMHhybGpSY0t6Zi9qcnVHeWhjNmRDQkVSdDdXZlFNY1VQQWxleEd0UEFTekJWNit1d3FmSjg0ekRZbmp6T1lxS0syK2Y4L2xORklhdkRseHRMRm05R2lwZGUxN1NROTBOaU9JWThSRXhlTFVDMWlnLzV4MUlzZ1R5aSs2S1pNd1hHMkR4WnBETXpKRm1wc1R6dURMemxNQUFLczFMVVBOVnBKWkFJOEdKR0JsN0txaW9ZRmwxR2xDSmZMeWlVTkRkVG5tUzBra1E1ZUx2ejAycm8ycUdLcnlPNlpYTGJ5eXFFY3p3UGg3YzJVV0VLM3B0RGNaNGFZSkFCeGhQVE5vVVJDektGT25hV0hZaVp2RGtUUUI2SnFXNCtLZGNDaVpSaHJGUkdhbGZkaFRIRkdzN0R1R2k0QTBTZHZ5OVNVWW1UVXZib3RLRW9UZ0psbUFKdEFCZHpvczFRNGlRejFQTnZaUjhVaHhDZEJzaHBIdVF4allWbmxLdGNoVEZxZGhGMFhVeXFLSVU4VE5pdTJjV1p4QXd3U2NjbER3dXdaaHBnQzloSnRFekJ6Q1B5anI0cDF4V25LUjFFaFBLUjU0c0IyaWhQSjYraTFYbHZaM3VHbFRvRDA4Qm5QYzZQbjE3Q1RrNW5hVlppNmpJOGIwbmx5U1JTV0RPekthNDBGRFRWNWhaaDV3cEtIWHhpbCtRQ28yVW55SFpQSWwyYXB0ZVJ3S0U4bExyaXNZMFo4d01wWkRCVzluNHNHd0ZkclFuU1J3bWVuL2JlUWtiRlNKNkY1bVRnSUdwNUpKTUNoa0hwQmRMOGpzLzJJU2V1d3Jsby85aXJZOC9tVHRsbm1WVWxDNTVWbEI1aWxHMW9HWkozWXFHTlUwYldvN1JkcHlPRTZjKzBKQ2Zsdlg1eVN5L2Ivb01HOHlZbWpyWG1TN1N3cVVGVjVZNFZyalczTENoNnhnOXgvR2NxQThHTFRIWEY0bjVHSjBXeWFYTlBJT2FnNURyQnlDc2pOZW5WUjRDbU4zdGpJM21QYzFrOXljYU9iRlBiK1NoM3puajhGUzFudzd4cXRuWUc0TnpOOFYyQXR4U3VUR2x2c0JJenBRMCtYZDJiVkQ5aGRmLzZlL3BYcDE0NjZMbkRHZ3hVM2xFM0k3YThVNXpOTWRhendXMUZnMVVWRHJXWGFJS056dUR4c09FRTl4Rkg1ZWZaekxUakRoM21SSGhoelV0UEU1M0wzcXVRRlNwbEVxcENqUEJTNnNTQ05DV0ZhUEt1Z0VObXlpVkJNSXcrTkdVMzIxNEczK0s4KzIwK3luQ2R0eENHdXlJRThVWjhEaDFURlNjVFR5bElwTHZDd2ltcFZJeUwwUFpoN0pUbnRKUXhtRUhsUldnQ2pybVBhZU1jNlRRVEJWMHV3bDdyb1Zwa1NmNFRhMzVsWkVPRVhnd1NsYzlYSnBJb2tzVDU3RUxiRnlvSW12VUFZWVdiZmNxenUwbEdKY0FZTVR5c01QNHJrdTF3UTNxanZCNmJOQ3FPSXAyRG5VMDFFTkdnWnpIWDRDSnI2UkI3SFlPbDBqbSt5Y0dYS3M4ZllYT2FLWG16ZmdCaWdhNGd1T2JpTmFhR25oK3FkTDdXTzNWWnNJQmNyS25mUk5jR2Qva1crRGF5c1Y1R3krM1YxWUxxcld6WWNBTzNBNHlFR2dVdGlsYWhHK1A0L1AzSGRSNFJVTTBQaUZtQVlndHdDbm15VnZkYytMeVJqRTErVjA4TkFnYzl4cEZENmdyZzlYd3ZyY3lQOFdFcWVqWktibG1wN284V1BRUUZkeUhPTmYyUitBeUE2ZnRvVG9vR296RlNhQVNqeEdScUs3c1ZucUNDZWk5cWpUMmhod2lBam9hTms5azB2dWNDcGVJZ0s2R3pWdVJ4dmRVZUVRRVpCczJuU0tkM1ZUd0lnTHlEWnRCa2NGaEtrUVJBY1dHemFSSTZ6UVZzb2lBY3NOaklYN3pTRGZMN2NHa2ZHU1ZGT29oc0l0VW1wNHdnRFlJZEVGbzlZdFVocDc0WUF3aVV4Q1pnOGhTa0ZpRHhCWWtua0c2OVJML1dKejQvcW11WGJlWmxyZk0ycHY5WmVXSGZJRGcrRVQyaWlPS2QvMXhHcURmQUVpOGdsY0ZGOGhjQTBnTEhhckg0R0hnYzdRcTlCcURpb3ZreGJva2hSSUJtTldScmhCRzlKVHQ0THhBSjFXU3FOVVRxeEg3UitpNytBZ0R4cjRsNzJCa3FzeWhENWpPYlBkcHFVS2creFkvZWVwTmJ4dURsNHBhR0JoZ2xjNEVPQ1VNUkthNU5Wa1pYNlFrS294R3VNeFhGenpXejFnUkhpL2N3RFUxMHFnTHVrM3NGSngvU0d5QklmWVQ4cWpXOVZEakpEdmJGZFpOMXN4alNEU01vYWVwQ2tBMjFuQ01rRFpWYlFlSGVHYXh0TmxveWp1UENiR1laVEdHeG1qMENScmFWSGduRnhFNG1mRUsrb0lYR0VIR3VKOGo0d0dteGtVRW9kN0UwMEk3dktJNnlqOWpBRGlYam1zbWJZdGtLTk9heXFtY2V4WlJDRldlRUFsV3RDWVI1Ym0vWElNcXJqL2g0YWlTVkMwdXMwbGxzZDJ1M2l1NXpOcGE2NURHSDE1T3hxQzFUN2NRamVxVGlob04wZExJeWtPd1RzMkxOTnZDNGV3S2k3T3M2aG1lRnFkbDZHZ0tORThkYVJTUm5aNjl1NWZPaU9pUysyM0M5L1lYNGJHTVFYTHhlR2ZHOGNWNHdBUUhTR1V1QVVnbk5aTzloeEZUM2tVbXV3ZnBIc0JtVW1LRFdVdFVxYW4vWVdsbnJDTXFmL2ZmaFdiWGhFM3hwQnM1T0wzUHRkSXlFa3NNRXZpeUJxQTlDQTVSWmFwYlliK1REOWRPV2ROTnk2c1NYOWxGamNGUFRiVTRxUVZFaFpNNkpyMkhQNCtrRzhmRVVtN0Q4RnUyeFFBVDNpd2h0dDhGWEhWNkFCYlVJSUt1SzMwYlJndTF0N3A3NzlHa0JoZ3l0R2RnVDltZHV3OFNpa0x1UHBFT3dJMllYZHRRVXAwb29SZVRaV3JXRUlHWHdHRHV3ZTZRQysvN0lsNGhaYWw1aDVBTTBoRlNod0RTeitlMC9oTGdXcjd1R29DampNdDZyejFzeDBWeUlpeVlQSXlvOXI2eGwrdFRNVUhxTGMvK0gvTEdKNDBZdGpPYk00a1dNbTdMWWdSSmVVR2thZ0N5cDUzbWlBcklvNEhCOS9uVzFFdTg0Z2R0TEUxNXBXdzZvNHk3a3JIeHZ4aUtWdkkvWEFjSFNLaW1HMVoybkhUeTRGMWh6RDlzSHhwZ3ZaQkhqWjUxZTgrNjZQMUovdFdIR0p5VldHNDBHbUpjUkExU0EzNVlSS0MvQ0VsazNLTlJJZFN2K0JHVGIxdzk2c2lKWGNSSm5sVFROU0xXR1Rrb0IxenJFRlBPVXdUSFdXTnJZanozZlFmNnZLSUFyek9wKzdWdDQ2c1RqNVU1cTdMT04rOHFPRG5vRHFPcTV4QUFuOWNMenpMcHhaRkJZMDFuTHBFeTdZU1l0MW1KdVRKaWowbVI4cDhyRVlXZmNDWWdBOGF6akkrbThvYWdyZUxIVHFqbEFEcjBPU0lKQmg0dXkyV3hqWWNrRHBCcUFnd2VqZU9XRmZhd0tUMkgrMXcwSkNYT21QWTV4RjdMdU96WU9jaGRHUURJenZRQ3FZdzVCMmtLOW00bVp6cFpBSWtiU2pXaUdsWFh4dUlhMzZpbkwvdmhOK3FrVGhlZVdkVzFsTnFpNW9vaHhoL0gzaE02Y3RCZWFzc3dOVVFtTWFHdGtvcE0yakhjSHlEYjJqV2FJYlVubk9NUFhZWmhsQmtDNkxtdWlFenF0QWE0UnNySVI2ektUdXFXamp0M0JDS0t6azZPeWZrYmN0RTZWdWUrSTRvYW1HZ09oM3ZYdDJhc1Y1MEc3WjNzMms1bFZPMk1YdW56dnBZdGhaTktNNWJaVHRLZUhuUkZxTzB2eTRSY2oxbWJ0enc2MkYzQVlhd0s4REtUVXJPeDJmS0JBK0lDcVFtenl0N3VnenJkdEh6Wm5TZUdqOXdqWk5GYXRVVHdNZzdHeHNVMTJHQ1BPc253OVBiTnBqTnJwbXV2WjVJRGFDMEFtaVY4eVFoRlh6eTNvcHduOGp0aTViQTVkcFR4dUcxaDQvM0I0T1BUL3d4T3lxZGRFYTlMcDlMZG9mWDB6TE5TUzRuamMwWjFSRE16ZEtjaHJSUnBXNC9MWWljUFdaQWZhRzREb3IxOW5kWWN5SXN6WmxLSjJMcy9UK3NrQTAvS2pXcTF4THA5bWNrT3AwM0kzVGZGRWdMcVdnK2YxVXphOVlxTWtiakI3NGltTlR6alNGTmxKNWNzNDFOT0oxUCt6Qk9WajNWaXJlaHJnWk9yWlp2VDlBVmhLTE9JNmNna09ENk4rQ2Q3bGRhM2NXOW9uVFpQVmUva0RWbG9wd21NZDlBWW85UUJVbTRGam9meC9oWnVaSS93cElIWEVob1ZPYm9pS1RFdVFHdXV3eVcxNWMwK1FKOGpWYWFRQnRKUmdQNEVSZ1NxWEFCOHdSbjNPK3NxamFPRUFIM2V1bU5idkJ0YjVRbEJsckcya1ZKb2pOS2FHQ2hMQk0xQ0ozTVhtbnZJeGRCUDJoMWFQTjl1VzgxM0lYZ1NRN29KYnNwNURwcGN5YmkzWE9aTEx4MFFrbjlIRUFlK2NWM0dzNnV0ZHh2L2pIUjVRUXQ2d3VPalJBOU5hV1lUeGJnL29lRXpmQUlkNEJxOHd6RjNuQ3V4bFY5aC9LUWVkQUROSVVOcTJLc2xENnJ6dWsxeWdFWWZDSXF2eFkveUZpK2RlZkllYzZ0M01zeFZrZ3FGdGMzSFl1YVlGWEZQazJ3azhjd3o1a2JXOHlBaTh0WS9tbGkwM0hXLzQwM0luVHk0TUF3L3RYaSs5dHpTVU1JVjN1clVDVjFlbFQ2NjVheE9jUktrZloxcjFGazZkTXFxTWhaZzAwU3NvTzNGTXFlZjJJSzQyT2JQMi90YlJTK3diU1JNVTE2NHlZSHhnTDdHSU9zc05vS0thZ0MzSW1VbWJXbW45ZmZjbkpHNUc3czlSZUNXckJMMDEzZ2Nac2JlY2lPcm83elQrODhSSlRobEhadHRyUVZIQVEzdVVEZGFQUGx2YWxpWTRvaGU3Zld5Y29VWDBoVXlNUGtyT3hheFd5WDhiSDVRUjhIWU0wZE1Ub3k0YnUxdTJzZnZqN2pYcG1Cb21rWjl5eWtTM1lwdDhSTWFnaC9va1BoWWtxaXR0UFAvMFRLcG1Vemwrd1NuT1RVbERJRmtEVnhTblhDYTlESXYzSVNvWVBNUUF5L09BRDc1eE9IbDlvRDgxNHBuYVk4UVVmQ0ZjSWZBcDNhUDIyNHV6OUhIN2poRm9YWklqTk5oM3hOZ3RtWjJmWml0ckl6aitGbCtsczNBbDFZbzN4L3dTbVF4eE5rV0JxUFdGQUFoOUFTN1N0aXBPNWI5VHI1S2dvYndwS3hkRTBRQSt4RVdDdTVmNkVKc1QzY3RGQjNyUHh5RlJvbm8xVmxSMm9QWTF1MzkyWlVVNDVMYVhGU1I3WXZDWkJycUtqOHJaY3V6UWNpMllJa0YzbTJnTWdRaTR1eUcxREpUWGdVZmtoSjdja21TeDhBNnVNcE9kaW9wZ0dUWTJIYndHWm1MTXhUczRHZnZXbVpKRUNmcThwT3oxNW5zMkRzb1NxQ0JWMERUa1dUR3NWV2FsQmRzU3RHVWdZU00yR0VXVURsdVRlQWw3Sldzbkt6VEU0ZW1aaEVUYzJiT0hsSTlCcTBkNDRXdVBmazV1ZzJhOGEwRlE0dnpUZWxmekwyS0xVVm14NW9YQUpuSm9WTW56WFZNWm5lMzRqZFVaR21ZYkxTN2lHdlRZRVBIOGtzNVU3Qk03dVhMUndHczZhRnErdkY5OGxKYWZvcWRrd3VaNFZJRUN0NWxEUDNKakpYL3ZLZ01CWUpKelJqWDVTaVVSS2o0VUI1MFRERHBpb1I3ZXJwOThSWEVTYzhsMmcraHF5L282N2EyblBzMmhDSWRsZERIZlM5a0NHUzJ6WjM4K0owb0xVWVNUZjFZUWlDeHdVeFlNQWJ4YkMwVVJ4YjBUUXZnRFdSdTZhTFNVejd2WHF4WGFFWkR3cWRVbkJZTjdYc2JjdEVEdC9pZzV2VDd2RXRmbHlQb2dZOENDZmJxS3dqV3JmNjB6bWxCZk9rUi9VVjRGUFVpcVBDQlpsemF1LzJsL0E0enAwL3F6UjRTcmF4bWNxNGJvaUdYM0hldmxCSG1VbzBTb2Q2MkNhU2VySFVlZUxYNFM0YlVva2RDemFCdW4yZVM3d1hJbUJtdjdMb0ZNSEtWeVZadlRFWkVsMGhOSXNST205MXRueWVnM00xc201QXJXc2ZuRWkwUmhhN25CNTlOMWdDazVoaGd6ajNJbGRlVnl6QmxEQXM3QUtmUlU1elI3UUJseDVIdjFXcE5RR3gwQ0N6WkFYY1lmWXB3WlQvRFdJQVppN1lsZ1RNbDV0UU85UnhCa3hXVDBwd3g0Z0tISzRXTDN1eHRtRWcwRkJ6d2tGUzMxYktGTzNra2x3dXVEMzdRbllHYkZaTnFoZ2V2ZG1mY2VkVDVpa1ZtczBWV1NuS1dsTXFyNXlqOHhhRk1vbE9HRUtiSDI2cEk5Q2Q1Yk5jdEh3UEZUemx5UVhhb3JaWlhtY1J5clV4elhESlU5ZXpMR0VHcVZsMkYzczJPWlFRN25ONzF4VHdnbHZZNFZCUWFFVkJJbVMvNU1RL2tlNGg5UjVLczZocUFNM2lEUWZDTEUxbmdBTzF4YWV1SlJDN0lDNDg2MloyWXlmSGp4Tlc1RzlGRXkzemY5aFdCc1VkV0ZGT21qMWsrSjY3S1QzbTJYUmRwZEJSNXB5V1RRUzZxK0crV3hiaXp6UUxHT1hySmp6MUpqUkRzN1pwVUltQiswQlZWKzBQUm03M1ZOeWFZQ2g1VS9XSmoveUpzb3Y1bjUxNDRpbys1MUJGZlFtUE5uMTFFMjJLeDd2RTIyKzZPMzE1ZE1BQnZ0WWJ1Tm9BMDNRZ1c5MGU1Nlc1azREaUFDZHVhcEpaNDlyZnBLb3FkVFBTN0VHQytoSlRzNitaS2tNRGVDVHc5L1Z0c0cyRTZtVGJXWUdvcXg0MlF3WXNGTWlqb3FUZ1FwZEFJYk52RzJTcGpXWnNSdWxUeWEwd2FoU2tlZkhsUUE1VVVBdnF4NWw0azdacFZTbXljajYxWThnUWhIZ2NFTGljeWhuTytUMlk0Qm9OcHp1VTc4REtCdGZ6d2hyYzV1dUEvb2tKYmd1MFB4Wko2M2E2WFYvUXJxQXRLSWVFazdqQ2w4VTdURWJFR3lTalFJcllwZGVGQ3BPaEhlV25ieWllV1YxdldWczlxOFlNck1NdVd6bmhKQklzWUJkWkxWK0pXWXhueUtpZW1tUDk0TGxpS24wU2lGRVMxWDNTNWU1Q3VEd3pOWFQveUNMWkllc1hrWG5RSURCSlZQRWVhREhZa1lyTmVQYTYyQUtTRzE5WmtJUS8yYndSRDZrSzkrcUo1WTFUcytGeVZQZ1ZBUUorMmpYM0dySTBFR1FnK09zVDRGaVV5b2Q3R240L1Vub0pjK3laeXUraGFQYjlLWEhFVXJLMnNKcm5pakNMMmcwdlNoM0k1VE83bHJpNTVtSk1EV2IxR0lTTUVVQ05paGxGajlkTFBpK1pBQ2hsaEo5Y1JQM3JZZUZQSzdsY1VOZWVUazE0RXZ3cGx5RGNjdXhzZmQvKzJaZnpyaytjYmlLVFNUQUxldURYVHlEZkJjTGo5VllFemJEWnU5dnZCRzR6MDZ3dFo5aDZMMll4NmVua1hTUEZiM01OemdYV25aWnlHbmNjVlVOaXIrUUNGUThDdzZYalpSRWh4VjJOenFqY1l4M3pBNjk2U0VGZXRwSHBBSk5zcGNENjFCd3JzQWpzS2h6djhYOHZRb3AvMGFTbHoyRjJRSzB1cmNwVHFRL05OS1VkakljVGxwUlFLc21CS2lqQUZuam55MDJGNHd2NHhmKzVRdkRxeGl6MmpmMUlqRHFXbWtLeWRzNDNVallVNE1ZYWdJQXVCRlJEaEM1dWxRK0hDME5FTVBYRXVoelYyWS80UnhlRWNEYUtGUCtVODU1ZE13YzBYZVRpQlRzWCtLTURsclNlNG9ncWJ5cWtobmlId0xtKzFjZm9TWjU0cU0vQ1NLbE8rM1oyVS9qYVQvTTFzWG1qOGVTMkJSc2NMYVBVUGFQNDRpZ1BTWU15NW4relJETm5DNVFjSVdkSzN0eFduYjJkNGwvUG45bElUUDN1WVpaWWxoWStVTnkySUdua0U2bDN4U0hrUkQ0dWZHdUlHS3krOXBZN2pDTjlKOEdCODAvS1FHQmxtZkp2dmVHZHVuWE5pSXlvdGdmaFY2bG4xdmg3Qm5CTjh4WE45U0tHZmJTQ3JiT2FpNG9nazFKNVo1MHBKcmY1OVNySDBXVTVLTGpzMVJISjRCajIzVmNzcnhMSGMwWFVMamhRellyRTRET0VTVm80YmJzNTR1Y0taNG82OGJMclRoNk9wYlJINjlUVnJjVWVmOVpZS0FndWNCNi84a1pHOCthZkxBZ0g2dXkyd0lqbmxWSUxkZk5vOVBjdTJNOUIvcGoxOW9mS2pXZXQrcDRuZU8yN3ZHTGF0cEkvVGJaMUhoN0pXeVM4MExaekhvTHFPMkpSdm45UzVXUjIwRjZheEpIVDhHT3h4WVlBK0JNdTMwQ2hxVXV5RGhDNU5ZeXhaU1dIODY3TmxMUW1YdEpJWTF4aTRtUHR2TEZQcmRHWHExY1YvY3lsVVNrekdvSzhxVHM0bFVqRUs0M0dyZ2Z0cXFIaFVLZXNsSlAwbUtja0lXY214eDJJSjZXdWpaTE5jQVRlTjNXWGZsU0JhMmdiT21weDBhc3lic1FtYzVSR0tUU0VYNUFWZ2ZIeVJ1KzN5dDM5OGwwSlJhZ0Fpb29IbzZDZ2lxYms2QjlXWW0zS1B4VVFzeEI5N2tWYmp4dUt1eENidS91TEpMMHpQVzN0SGdva05lVmpJa2lwTUdPUTE1TXgxVmwzditKQ21PL1p2V3psekdtZERvVmVrZlQ3UGs2NFZhb1ZwbzRsZHBibEhnSy8wb0N5dk91dDhnbDVZdmMwcVhXeURFQ1VSeExoK0F0cUFzVm1PK0ZQeENRZzN2d1pWVkpCTng0K0IxTk9pZVlycmExLytTMWtyNDJZdjBEMkIxTWNZL3BCOStteFRyU1ZPZzJNazBZamFiSFpJZSt2UzB4c0hVRHJCQ1FUamtHSmxvL1h5Z21XYWdwNVFwUFJncTUvczRjeC9scFBld3hkNlJhKzBBL0RmVnl0c2p1YllDTGx5TmVlOUJUa29Cc1c0VFJVRDEzaWJUL2pxQzBpcDIyeE5JMWZMWmxTNTJtR3FFZkpWTDFlVzJWVU8xMjQ4MmZsKzcvZFNSREhqTlhqcVliMWhOZWVrL2M4aS9WUlNvYjZ3RU5YdVFpaUlLQ1lveWFjNnFNNkcvdFZVQjhMaGxFbVVMRWMvZytGNjgvRjg2RnhLQjg3WkNCNC9oa0doTXJIcENkMHQyQ1ZLa1d1UHFYU3VxRm1iR3QvdFVCT3YwZnhEeDBRNDJQNkRmNE1nb3liSzdFMnBjcDFvcGF0K3VVU05ZZWVzWjdsMzNtSTV6RE9Da2JSb2FyQi96SlExdHpDMStjZDQxTmNIOTArVDVPL1FpZklhbjhOME5UOVlZN0gvaWYvRHpiU1BpT0xrUW1wWG9mcGxkK25RMkhoVmc0c21pek0zaDR2VHJXOTlScS9LMS9XVDdTdS9PdEV5L01INzBPemZKbWJ0R0YzdW1haWFuenBIb3BERHVBODF2eHg5N2tQU3JIL0FJWEFJNGxuVmliZFkxbkRRTE01KzV6ZFJCZGNETkJhQlNYb2wzMXFiVmlhbzlGU2RsVkN5SDJmcTZ6S1hRRFJYbjI4dk43UmUwd2kzWlRsQ0trM3BHVHc5NTY2dzFHS3IrRC92c1JEYkFOdU1JWVA0WFpGaDNmVnZCdytReDEvVDBuMzgzZTJ0QVgrRDQxOThXZHREZWFaUkhCWWw2UkVMUWEzT1ZsR1g1L09rMkNXNXgraEJqZFpxM24yc3k1VDc2K21PWkpjeSs4ZUw4QU04UitQVm1ZNmNYTDFITG9kNnl6OXU0ZWgyN0h6cWhUUW1pazlmTzVOWFdTRS9sM1pPWGxFSjZUODBlZCtuZkRaeWwzMTNoSCs1SXFOV2FEVkpaa0c5RjhYdEhMdFVsTHR3aE53eHpWYTR4MWN0TUx3V3pzbUJwSThvMmxValYyNGtZTVArdHQ4L3B2YTk2ZmJ5dTVSREMxUFVPQVBIdHpRODF6eERZbHJlYkdQTUVmczl6Y0ZBY29nZFdESFhNaTBuTXBuWE9HdVBMZEdYeXZ0SDhWMkRrdEhUUW5YV0dsejBwZ1p1US90Z2lsVm81eVcvWkphMUZJdnNCcjVPODN4YTRldUJWN3p0eWxxaUplMlpqbnFzaThabWI0MUpxcFFTNDE4WXNiRUsxai9lMVF2NkdKbi8yRzlPZjZQZVhzZ1NuVEk1N3lTcjR4WnVCMXZoK0dyQkthckhIRmk1RHB6dlltZllsaVptajBxcU85T3pIa2Jzd01LZVlYdzdQeDlwQThWcXl2YnZ1S1NHaGNWcFVIa0xDSDhBOUF6NVZkaGlWczVmR1grKzZBRE9vZGExQWNVMHJ5WHRGNURaUUlNNnhYUCtqRkUzYThrT1JtTkQxdm9tS1ZkYVltRlAyd0xwQXZQcUVqMjFKT3VNTCtFcHUyYzRJZ25MRzF6cXA2TjdnK2JBbGkyZ3A0SGF0Ylpya2FVcjM3Uzl2VGFtcCsrVW1hOUdYN1BMNHlpaGs5U2NOWTE5ME9jRlo0YVo1bTZKbnR4Q1M5R081RHBDUGtoYmZ4eWduMzlDdEg1bDV1UXBnQkNyZzkyMmI2OVhIMlZpL3dUWUIyVy9RblF4MVEvSUxvcU5oVDRadW9wZDBnL2dUT1dSd3dCTDdHRjNJamkyV2JRL05UZGoyMHlhVnlGSEtsWHJDTkd2M3pPemQ3VlJqME5jZ3RlUThTK1dHUWJvYUhwMXFvcTFzWVc2NlR4NVJpS2xZVnFmTyt0d1cxSlNuNVJVRFV2TElHSEJ3RWNZWG5VWHJQMkphbXA1ODJnbnk3ZlZpWU5BVzQ1QTZmVDkvQW5lWHBWcUVRcTNZYUNjQjQwWUxuMTkrcnA5TXR4NEFxRjJ6VVJRcUI0SzNiVm01eHNnZC9iaEVsSXpoaC9nWVk4Q2JqWHhRNVorbXdQWDM1OUFoMWtBWHRQT1pOWmhGSFA2R3ZOZjN3THhselNrVUl6eXNwSXJvTDZBRVJ4Y1FhOHJNSVFjUFpGM1RwVkVLcWtnK0c3d1BVRlJ4SDdTY1Fwc21sODBiT2xGVWFnSDNoMms2bHowM251ajcxQlpHcW5XU0w1NUR5Q3BwTjg5THNkQVFvMTJLN0xRVTNYMGozNWVZZSszUEl4b0ZhSXFFY1RSSVNNZVhpNXFFRThveVBFYTlYcGpVMkVFTEFqMWNrU2JFRXh0SFR4M1VJeURGQ3QvRDBsWEpRcjE5R1kxSmhaeFovZnU3aXo0OHkyTWlrL0w4VENiTm5UeWlrbk5QZWRyaW9MbERSNjE0aFhsTE1QT2xtVUZwclYxTjJQc3VwWVROR1lDcTZGU2ErdGE3OUxOR0grSld2VmV1ek43NTh2RFB1WmF0LzZvcTRuMWRkWG1vdXFYZzRYM3ZVMGdqQ0hPaXRTRkVDM0h0K2FGTy9KZmZCR2JPWFU1WnlWeU9zQ0xKV3FPKzFycml6SHpwQjdOZ0xkaGliWHExVGR3MlM1dkt0aHMyalMvdU5nMDdMK015aFlDV0ZEUTJRVy9mdFlhcGl4T3pYSHFwMTl4TVppdVp0THViOVk1cTZjL2RMSFlydDQvYlh0cHJGNERGbXFKUWMyNVJDR0JZaklWdmNEOUJldFhGYXRrNW5leldzYTQyN2s3bEV2NXQ3STNNVnNiL1VVUDhDUU9HRW1hQ2dteTFqVTFVZmptdzMwVFU4RjltMllHVGNJdEs3WnhJWHlueGFjSk9lVEM4YzlQbDN1eXhCWjVpTWFMMTdmM0d4TkhSTE1DbGNzRTBSVkdjMy9oMmdXZmJzeFJiL2VVWC90c3Y4eXk1c0MwQkVNUmxNblFVUC9ES2ttVkJDQ1ZDcUVGZm9pYkJ3SlB2Nkg0dXcyRHpkK0NERnJVa1ZpWUpQTG5SMzU3blNuL1UvWkxDSnYyZkppbHJ2aG9VNjIremQ4ZElkb1F4Q2R5eE42dmc2V0Jva0pZMDZrUXhVY1RmT254bUR2ZHZ2NzlvRmNxZ2FHZFBjKzlHeWdzaEoxek9aT2lyS2ROcFJERjNCRjNIN1FYOTBYeE1Ra1hhWSs4cFpLU2pMb1hSMHRuMWNJVGdSUDFyVGhJK1Jodmo5bVhvU2hPeEVGbUI5OWFzV3dSUWRrV3haQjhuSUsxY3BKYzVkTzY0U2t0UU9VVnVXNVo1RTJUYWZQK3VsN1dqN2RYd01qNURrWDQ0dXR3MkxEUnVLbFVZM0F0MnRqbmF6MTZ5VlpqMzNvT1ZaUjNMK2JNdjNndmVWVUJGN1pzYm1XbUxlZXNwQ29SU0Z1WnRxTGhYWk8yNENjWjZrbUpDWVBjU1RUYWZPTUd3dlRHNjhuWGtpNDNBMjg4VllmdW9iT1ZZdy9hci9uOXdQUEd0VUVZT0JqWWgwZENJWHJTOWZzUnhvLzhqWm8vNjdoaFBrRjhJaDluelZBQlo0MXdJbTJ2eU8vbnE1YndMSEtQekFOdDMzLzhGSFNhVzJ5MHA3NWhsVnFhNzk5RFpBTkEyWHN5Wk9LVVNRZjIrb1I2TkltbFpZK1FaVGdTbHZOcVVpZXFSLzE1bXBveWR2UnpaWGF5YnRVcG9TbWxBOGMwbWFqeVNKUlJxODdYUWhCbko0bVNkbVRrYUhMZVpXN0RvZmpKS0hYNnlkODJCODBCY1Q0cVM0RndiTE0xdTRZVk5ZdDhKWlFZNGlLM1g2ZTNHdSszd2pBTFNTVVE4UFpZR0NkTGxITE1SLzhrR3psclRVRzVMVHJuSGN2K21WdU9OeWRRaTNjWnZiZC9oVlNncFRid1AyWlJ6cFNaUTBSZEJ0bHNUbUlTSGFic0M4aG8xR2cvNVQ0d0ltZURKOWFJeS9za2w3TW1BQmgvRTFoRjF0Q3VUNVd6MUZZNnZUVWpsWWtuZGxhNWREVjI5MG9mZ1h4VFNwRFMxRjhVdGJIYldXMkk4Tzh5QWJZQlhYUzZTZnI2aGNhM04yekxIRXFYY3FIazNlSkZiTHRiZ2dNeDFSQWJPUEVibmVXTXRuaUM1UjV0b0pNbGkvRk1zSi9MU1Y3N2krakwwcEZuTUxJR1U2b1FFRnhZd1l3WURLZ2VHUXpNeUFyQkJZRDRQRFFRNVlNOEF0MkxpVnA5aDF4VWp2SThEVklzR0Z6OE5qRHZ0Vno5dHFCZzU3eXJiN0E1dXo2dnFsNGs1ZXNlOHlFRU5QUkk5M3RtTEE3SG9JRXp2NGlva1JlQUhqSzRwTlQxVDB4UGlqREZxaG9hbkdDaytBM0FEZml6S01HQ3BYYnJranpqbnhyRlRJeE13cTR1bndWZ2lacFN5TDJZVHN0SXJOK1pxYzVObDZkTFN5MU9TbzlHemxzYXM1Z1ZLM2pUZGlCTlZzYzlXQnhQdjhMWXRFZGQ2aUp1L1B2Uk81dzFxWXRXaXFEcW1ZdnpJYjB5MFYxbTZZNG1tWENWdVRnVVJndEttSmFyR3Nob2dxZ2QvYXNUY3hUVm85QlpuRzRSTjJOcVlKOVNCWTNCVGFya0ZjdllCZXNoNm5yTWZOK2dmOVBGa284TjR4QUQxK2JQWkI3ZFBSTS9rN0Izb1BGRDFMK1lYRTNXQzRWZ2ZuWitMSGdhZnYzV3h2N09VS0VhTm5vYnZWZjVscXBsSURvV05BZlcyOGpQditMYlFhNEpUbnNPaWtWZFdES2VBQ0Zmc2N6QkZaUjB2NzhBSWdSQXpWZnJMbkhOenlIZXJhVHhmcVdTeloyMmxyK3Q2a2QrMVMvUndHMmlvNm5aeWJLeXdtdFdlT05vc1pUK2Ywd1JhL2V4NDA2ZXZGaWF0dEtka1JqZHlGUFdTNFZtYTZsRW5jdDdEb3dPb1hvNEM4cTFudndnYWFOUjVLb0laUmtrRFU0QlQyQ2pZYktmb1ZUaUxQeVFsbFM4NGg2ek03RmQ0RkJwT3dZZHd0QzU1TmRZeXhjaWlSUkt2a25DME1CTHRjblpDZlRYT0hudVltZW1aWkhWTW5HUEtSci9LNjBxeUJzbjJpc2xrKzdLeGdLbnFIRHFDbG5YYUw2NzMvd2lZVDlPVEVoQ2tNYWpIOUdvYkRGb0FzV0tVVThJcCs1MEpnb1MrOW5kTkpTZkgyNnpUTFhvSE9XWnh5SEhEcUZXNjlEejc5RDRUUEhGdzgrRVBzOVQ4NnNiOHJ5czNuNVdtZ2xidHQxZVpha25Keis3ZEhtdGhwOXprSldRTjBrczh5ZmFrcVUvSTVTVGpRYWl1VDA5dmJhd1VJamVtOUxwSUViV1lEb2RNdVY5TENVcitPSjNsVnJjSjRNTXhiYURjWG5jWERJK1ZYQTI4ekNTU2lERnhnaGxuWTZuUHBNcE0rUnZ4S3J5RUpSS3ZPMi9OZnFoSERjOTJUbUpyejZId3NpZGZWK1huSml6bDZuUHo0ZGd3VGNMdjVhcGpHTTRKRGlCWVVGRVM2YWM5bkNyY0RnRWt4Y3J1NmVxNXRGOU1heS9YQm1aR0dDZEVvWVBHZGJVRjRyRlVORlVPYnYzRzY0UzM0T2ZWeGxUTG93QjBKU3FsczMwN1EwMmVIKzRjYmVpU1dLQ0xTS0wyRUtiYmJZMHJhQy9mVVRaSElXcnY4dFVVaVgrL0ZvSWE0Y043dHFtcWFsSEsyYjEycnN1STZrVWgxTW04ZllUYWxrMFhnVXE3Y3cwNWJrOUs1K3JoeXA0VnROK1Y1YkFvdU92dTF3d3pnNWw4Z2M4YXNkbWhTT0czY1h1Rkg5dFdMaXQ2ZHc1aVBTR3FxcjVZQUVpNjNEb3loVlVWOWc1L2ROUGg4QVE1TmFYM3M5WWFjTzlyTE1yRmg2U2FNNk8vckV6T3hzQkRDN2VkZkpVVXF4TS8vVDkvQitOekRCcVJMdDJBOVQ0RWdicEFZQWFFUlNia3NlQjNES1RMTmpNU3g4S1ZLV3FYMjFNWS8rYzlDQW55TXZuQkpKNE1od056ZEZGbm9lZlhuNVo1YmJSUHg5NlZlS1FITDRjN2ZkT2YvbVpBUCtzeUh1d2g0QlZUMnlnODNJRnljdldsMmFqNWkvV3Ryd2pEem9jMUd5MTNDM3pwRjdPQlFPRDhNSjVLRmVObmdNWGtzWjZ0TldoVnIvYlk3ZE5YV1A4YzJRa0xIWUwzVFNZblMwMjVCc1pMUThxbmU1S01wZG93eVdVSFBpZG5XTXNYWnBqeFQyNm9kZnhQdERBalh2aDZjNW92ci9KTzFWYU40bWZtalZtZXE1RklvRURVd1VlbXdsOTVHeElUZlJEdVA0NEZZMFhEZStKTzdhREJkTm9xSjFBOWYvZDBIbmlIQnFjQmY0L1pHRUlRVEJkOXFMekpaSDRaWU9KSDd1QU5ieCtZamdzREdZNTVpK3djbldjS29Gck9iMXM5bU5YeWIyVXlKTGRkU0tIWU8wU25paFRwR1o1ajkrMmRDMG9UdlBXRmhjVjFOdHkybmVSV21uSkd3NTlzK2Y4T2FpMy9PUFZTMHpMNmo2ZTFZYkRiTWNRTEN6Q3RMVEx0RU14d3htY3pHUUNyUGk4Y3FTb2J6MXh6eVRoaFZNd1BSMkJWY1BwSEFCWDRjMDBMN3dLWDQ1NmVSR2d1aEVPUjJGOXdNK1hsMlZrd3BVckFUSVFDNXkyaEhoRXZUSG9ud3hZVUkreGpCcUZXdGxPVUxmdWoxZSsrWGtja3NiTkJJcXZ1MTY4Q2hsWS9kVFdBK0NxYjdWdkRjVGpWRW1EQ2ptVUZ3eitTL2hUbUJ6SGVTazFwVjdOeVZweVNIbE5jS0hIL3lkMHl0Sk1ydVhXSFBpTUZzTjY3ZUxMNGJXaHNTWk4yY2dwWGlRclJMcHN6QzB6R1lwWVlzSmtCN3NOeWVVSTdkdUhnNXFGTWdOSnFDbXVTZkw5VWQraTVaNHdFSEVJSE5na2wwUEVhc2JPbUJOYTNWK3BVd1hQcDlwMjlrVXBhZXZnQmlPbHNZNm94ZEV5bjU3WFd4c0grWHQ1RTFQelZQK2xJSzRNVlZlRHVXeDVoRW1zZ1lEWkFvRW1NczQ1Snp3MGxNcjVLN1AzdlR5a1VrV1BxV0x5M2lkYnNyc3M2Vnc1RnliMmNsWVhQSXhpb09NbWhJU2RKelM5dENBam1ETlBjT3ZWQ3lVcVJERkJ3cjZHaW05NUcydzJuYUdaRXFGQlpndEFqWVJ0MTNRTkZoUHJ2Q2ErMlpOM1NhNU02VXBxS3lXbkJkQkRSUE5BdHh0Z0VPR1JtZU5OMUE4TVo4eC81OWhpWWcrallZbWJYYjJHNDdQM3ZLVFFpTDk2RjdlUUV1TktjRU9LbFkwZW9pWFp4ZWZwZEZPQVVFb25lUXJXOStZTmxLbUhvU3FFZ3ErQ2JId2dlZmRUOHBya1NFN1I5K0VyMzFQeU55N3pXWEhWSFhnUmltN3FvdTN5YmdIZ2dsZ0E5ZnBwaGZBa29WcjBMeGg1Y2x2dm9SZTErWGJpaE42eHoxN2krV2ZTQVNGUUp3QjVDOGtPTUJ2YXJ0VUx5czhKb2c2elJUSktTeXQ5VFVFWFNZQ05JcG14ODkybVhPd21hVzFiNldNZGJrNHY1L1BQcEVqV3FrYlpucVdvMGtNbTE3ZHdDUXpjbzlQSlcwbHl6RjByRkpJRUx5UGVERHE3U01ZZmZ0dkQ3WmZmL3ZIekQ3MGIzTHozeFlmdlAzLzY5SGh1aXNYcHd0UW1UMXE1YS9YQlp5amN2UC9KaTZlUGFuZG1zZzFNZGpFUW1LTTlTT3oybXlMUFZxTTB0L0xTNnd2Y0FCU0lGNk9CWVd3UnFPcW9XdFR1OVI1MTdZMmgxLzdUb2xuVjY3d29FaUQwcFU4LytXRGYwVkJpdkVGUm05TkxmZjZaZEJHQkpxWUV6RTRUa21va3Bibll0VnloMjkxUE5ZaG9MM1VpWE4wNkRPcENpWHVhVTZaK2lqNy9YSnJBOFl0SEQzdGY0YXRSVUpjODFtZHBaaVdEOHpqMlB1eS9OTHRoV2xoSkFPSU04dWhmRHRLOHJlOTU2Y1c4TGZiQmI5ZEJiSTBOSDlQTk5pQjRGb1RockdsdVo3Z2ZmWHVWMTNvd1dLV0FtNThKMllYU0R0Z2ZuNGpocjFFSlB6YXF4cmx4SjRERVZWQlVEOGlVaHY5cXBsWlE1a1Z0cWVpOGxVQVFKUGFDOXV3Ri9FbWdDblRVc1RUbHlwR3g4TUNIWVVZYlFJbEZiekcyVFBkWVVXdTJnaVBSNjZLd1ZLaGxMRWhvQUF5Mk1BTlB6T21ubk54ZlU0WUlHQ0ZscjdVU3JtNFJPaDN6U2R0M0M2TmJMWjkzVDMrMW00UmFoSmtFYmtzTGFiQUZkMDAvd0gzbzJ0bG9hc3hPTEJCMUpyU1hyTHBCd1JGbUJhWExmZnFwNXI5b1o4UTFlOXh3YWVHVk8wZ3RaS0g5MWJKV3NEZ29XNitrQXhTTXhwanlkWDIxZmFsMHFzZ2NuMTRQek9mZUN3L2dNTGlkOUhrS016V1lmMzZQRGd0eHVMM1dsV1FlZGhFVHpKZHNUK2ExcVZnZWhZZmxjVjFlOEhHYTI5WW53WUlBYkMvRTJXalJLYU1lSDJSOXhQQ3prcVRWOGhJdk52U21TYWRwWlNVc012UWFQWWx0U29vVUhzbWZRSWR6RHlHU29vVzdOVENiUy9QV214cXU3bUJZRU00MzgvM3RXTjEraWo2WHNMU3VpNExkblpwSHR0Vm9Db3Z4TVNTTnY3RThwY2MxRElFNUlvU1MxakZEQk1KRW5sV3REK0dxT2ZSbzFvNUVCRzZmYXBnak8xT0QrT3d3MzZjUk0xRUg3L0xkV0pwYWFRSVFvWGJhUnRhREpDVnRCd1UwUFoyUVd5RHk4TGd3ekZzRUpIYkxDTHZNdFpkUEIwaXFoTXhldTBnb0ZnQ3hwM3RTNENlNzEvbnk1dm5xcUZsV0VldTl3bGZLQzRNWnlucWtaaG1Jc01JQTF0TGpBSWt1YXdNZThKYmVUbFZuaWhVWENjRjhVbnN5UTF5NkF3K3N6VVI4YlM0QmxrNnpOamRDbWp3bzk1WnY0dVQyZy9JZWQrZDg3dTY1bDA2YXZVSFZmS2tFUzBQb3lPbFk5RVREUEpTSzhYRmdieExwekRmVEh5YXRLZnVoVkR2YW1vTVhBZWdXRU02QmJBZjUvS2NlTWNydnFWQ2t4U2UxYXdvMmRjV1h0UVdyQ09rWHRnWWRUcHdJa29LZ0s5WWlwUmhhYTNZQ0JNQVNOZGFiTmVUbWNIbk5MSVlVbGpMOXNwMnZnT2h6NlJKZjNMeGNyK284QzMxbXd0cUVqL2hSTWsxdGE5T3hOOVdDUTNROTVPMVdMY1VRcENKUWVFU0d5ajdSN00wd2RUSmpraGIyMlV1WjNHNE40UllCcmtXRGZ4aTVFcjNxT1dzVlVMQjdWN0tuNDQyb0Y3WmpNdE5TUjNiTUNHUkx2RHczeSswSEVhakg4TzZYZVFqZmNRN3JBRGlVaWtSaDkwUFB2Tkp3QTZqQ05ZQzh2SHNjRURFTXFWNzFuTFVLS0NvOW1mSzljTE83Wk9TZVRyVjd5b1JrejcyeVZFcmpZOG9wM2NzbVdyUUJ2N1hjdE5GU05HQ2xETHdBMEtscW1zS0xpaVRvbFUrTDhnR09YZHNBR2g0VUNSeVpiYWxJRlBZeHg3U2NNdjRCcGIvOTg1WTJrS05QTEJCVnB5b0JaeUt5STdLdVZJZUN0Ni9ZUTZQdDhMVDh0K1loNjloRE1NYmlpaUJaM3JrZG5UWVJ4NDUzYlNBc3Y3Q2JzSkRVRmZPM3RaS2pZZDBlYk9xcTNET0h5YUdUQmFNajBqaDNFYmVCa3psU2tTaFkwQ2xqNnJKN0QxRnI4RzdHN1RhN2E2OFZudEN6Y3VESnV1KzZrbkdiNU1CQk9mdlFuQnhQSnpsa0tyMXlObEpRRkptMFZ2ZXJTMHcyd1dDS3JZbUFWMHNDMDlhdy8wckt2dTc2akwwMFhlRUZLdU5jakhMNW4zNUExQkEvSHNLTjNyN1ZpNFR1NUpGcFVjNFR1b0k2Znk2cTNBN1BGNVZJK1pQRXNQUXhsQXF2MVBEZFFBemQ4NXhtU1VpVTZ2VE9jckU3WGhXck5QZTR2a2srRTVJWUVpL1IrQ0psaHZHUzZrWEZjb2ZacGJWU1g2MHpGY2lxbEU2Zzc2Vkp5VWRPaGhlNlNuKzNpWU1yMllXOU1tMjN1NCtRMlB3UEZ1TmJ4bVNjRnJaTmlwOXJOTUVRQjBiTEVva01ybEpEQXNKb2QrOGNhV1djMTBvTjRsNll0SWUyRU9RWVVvTmtlYmNYa2duemlLSldoRnhuYjhQZnlSSk9Sc3ZhOEZ6cm4remdGYURWdUswSzZRcDBhclQyM3RuV0pJb3dlMHp3N2J2WGRMYXh4Uy9tOXhxczd5MGhHMkxkTFZIUHBxU1dpc2dtM3BCRitpRmxtVlh1SGMzU241eUtTSlJtTVlpbVVRUkkxamlSK01GeHVIa1l1dGkwSStuQXFGNTk0TUh4dmt3QUw5NWJHMG9wY0VpUENWV3N3ckFvZ3FOU28zb29RZkI5Z3VValVsSXp0ekIybVdvcUhLeWVUbnh1Z3RUcFhFRldpdFdLaWlBRlVjMFRWVlp5YlphZThERXJoc09nZXlHaFlaNWpaT2hxRTQvZTRPWWlMYnlFUk5XUGx4dVpPcXMzcEVyVmloNmh3WkplVDVRNU1sV0l1UDdXanFZR1J6bExTaG50cjFlcEx6eG1pOGo3TmlJVXZhTWhjd1ZzYmpiU1V1cFlsUUtXVktsYXdiYk8wZXhXK1Y2TG51YW9UVHdRclFvaTdtMFpHcng3NHZWK25zMGQwaytCNS9oY3JEQ1lzS0ZGekVkRVJZYXU2d2tDMWhnYVNGSjE5bXJPQWllYnd5RlJtdEZlb0NtcFQwOGU1dEgrSWxrQmI2RlVkdjNUMU0yYm81UldEVWw0dGpXbzlDNnRPdDRkSkdxM2R1czFLcGhYMWhwS0RxcE9kc202U1NWVjE4TXRPaDF5Q2NZM3FuVkJ0ZElXU2Z2angvTy83ZTYvZjM3NjRkdXZQLzFZZU5mRnZNQXpQUW5XQ0h2RUZmbHRQVkNSdDNTbHlDRVQ0U1BXMWU3Snk1STNCdlV0bm02cS9rUjM3eEVWVmNuUUE2U0l3U2NaN3I2b0MrN2d4Qlp2MzJrenpEMmpRcnJJcjAyOEVVT2FSbmVhOURHUTVGU3Qyak0wdjJ1SDVGUWZIVm1EVHZsSkwxMERyM05KbXUwL2QrRlkrT1hzeXk3bmNIVitFK1NJZmgyM2w0MU9JeWxPMjVVTFlVbHI3NDlHeTc2Vm5zSFRIaE1icENEWWxvUjFHcmpwMGRkWU56ZnNRY1BsTlUwVGxrMWRtOWhaSUIyaG9jWmNvbmtyOGtWbEpFUks4SUVlYWlSVVh0d1FxMlc5bS85bVRyUG5saG9jbU83cFNHVCtrZ01Eby9LRmlOUnlkOHdmMUxzbW1ja2R5MVkwYXFjc1JqemQyZkFiVTFqMDlXR1g0VXNxY2hmdy9ENGlSZERBeVVlZ3d5Z3dRbHU1SnB5QUQ2SXozOU5uK29rZndhSlBOdkFtTDFPdjlxWGdISHA5cENhdGhvUitvaG9qSmIycXZZQlZGQmpEYlo4MlFGelVjRzI1UHhWNG81K2x2V2Z4YlArc21UcVYzOXc5eXJRc1hacVkvUEk3RW5MZUVsaWNZYmU3ZVZIQ2tIaU9zdFNUVG1oalhsQnkzUmNPZlNhK1RxU3lwTFJrL3lBUlREanBSOFYwRmFkOHVzSzJJZ0pSNjN5K2RSaHFLWFhsamlYeFFVY050TE93aUZlNmNUQjNMSU1PdWgyOHczZmlhV3E3OURLa2dhc1FsUUFLSitucC90RUlkaEYxd04xR2toSlAxRTBkMVhpWkwrczBOV1pHZ2RMRFhpTThpSzBaRnRMU3JBTmxIRkw5cERzSjVpT2ZlRzg5bERSNmxNdDIwbDhQeGQyRUNZL1k3M2JZUGxwdE5rcFRlekFLVSt1MExqNU1PS3NyUDdScHBlTmhoa2JzY1dyMEZ4anJyZWFSaHJOSzE3ZlQ5NEs1WmVxak5HUEdXalVMUm9KVXByY3BsOE9pMWs2ampEQXF3Q2FPYmJleVM2TlNqTmNqNndER1hxWlRvSTlEK0lsWU56UmFKeE9lNXRQbWtVT3ZzcnhRK0FjRXlOWXVNa2UyUDVJcEpGOFZ1YWROOTl0eVVRUG05eDY0M1c1VS9taUpBR1RWaVZ6TFdxVlp3b2dqdDZlb2p5djV4U1B1QWRnaWp0RmJHZlRRbThrMHMyK214TGRYOFhhYVBLQzcvdWtobnZrbWs5RFlHZzRKUFVmRldkSXpVSUhTV3p1aXRpOWpINVJYRFBNZG1pNHFkaVFHdmJwQ1p2WXZTVEhSMTUzZVFrVlB0MTBxdFBhb0pJZVFFOEx0akEvSzNPMW9YQ0Q5S21IT3VWNGtINnBzNkVsYjNhZFE4Wkx6QjdrSXY5M09JQnQvYmFCNkRkY0thYXFqRVFQc0gyVG5jT2luQyszWHJhWHBXYTFsMXlSYmhhanpIZHVxYWFmU2RROG5vOHdCc0hkS3JpRVFWQ2x2NjZSaUJmTHdHeS96RDhHejRrcFByaTlId2FEUDgrREtpOWxWZUhLT0xvcFRWSUhnRFFBTXRhMko1eUFaM0x3a3l6K2VRQmhaVkUwWlQwQ1JnNjJWUElNU0dPTW4vbEs0UG45U0pkb2o4SEZNL3k5K0s3L3A3cithYkp6NkZ3RGdtMHZyVm9zK2dlQ3ovQXVKMVpzTVFoQXFCRkhmb0JCQU5XNjB2K0p6M2Fjb0ZYQWIzTjhpWE5kZE40MjJCbXBUV0x6bGc3bk5XR3pQMWFqMkYwT2x4ZU5OUkJkODZhcXlnN0xZNmhsaCtPcDJVQUdMZlpXYnNBR3U1L2FpRHBuTVZ0WEVsYjl3TWp2TkhpQU5sV2JKQWhqSlI1ZUtEL3dwNGU4OEw2L3dGMXN0M1pUa1lubkZkTFhkQ1NGUnR1YjNCdHZJS2x5MmM3SENISFV3azVaa3A3MDkrVklHSTQ5TGJrc1RGQlZQWHNMNFF6YzlwOTZXZmxBUlBLeDFsVldnT1dJc2hjbzhvYW1vLzFncS92K0ovbURiakhZS2M1ZENnWHJHRjlOVWZ0cmhMb0RNNm9CbzhLaXZkM1hsdUs1NkYvd3cvNUhXd2wrSFdFbHYrRk1BWEsycm91eUpPdXhMQWM5VTFBbjR0WWd2aXUzRC9GaUxoeXN2L1ZQVFV6VWdiQ0tHMGdRWkY4S2pERllFczVNcEhJSEh1ZGk4V1A5TzRUck9GaEVyM1dnSVRCMHhHOTVFK0E2R2xvRE8rVzFlUE82TFZqczRrZGdPM3dkTWg5THRtNDRrZUdFNnh1YWQ2VGppU3BxT2wxWFZteEs2cHBQeU5XcC9TWktwK2pNNk0vVVkxcWREbTNZRHVISTF5Y09sSUNOVElyOHl1U1J5Vld2UlQ4NjZIZFlnZ1MyWGI5SkNqTXY4NENTSXpFZktxV0F4czlMMEtHT29NYU9hdzV4aHVHUkdlYmNkZGFhcElXTDlxZDdDZW5BcnN4blZOVk9iR016VWJXRHA2Tk9tUlpndVVmM0JsT0l5eThrL2JUaHk3dTlTZ1hIT0xLZFVxRVF4bFFKRmlobzJJTlEzbGZpRVdVWEtEYjBod0p3aE9zMTIwMU0yR3lKZy9JSlV5bk5LSnFHK1RzS2M2TkV1bklrak1xUkR0UWlYeHpRTUdodE1XUFBTWERjbmwwUFVqT3hkb0lZWUkxSll1K1QyTTJFMUc5aktoaGJzODNHSmlMUDBGbFpFTUp2bGVqTlRuYXVjM3NMQU5MT2xFYWNlY2h2elhCc1RJTTdzK29pYzVJTW9KeFc5TWxmVW9Dak11aExRaUVzNlBIcFVhRVREdllsbXBCVFJWU2hZejJUU3IwaS9KaGpNSGdPaVN4UUlkQUgrL0MvbFpHQWJaaUN2cTN5eVpBcitrZjl2Ui91UGQ5Nml4SXBERkM4QkNSa0ZGUTBkQXhNTFc2SWt5VktrU3NPUmpvc3ZVeGFCYkRseTVSRVNFY3NuSVEyN2lJSnkyc0JDV1B5cnBsU1pjaFVxcVdsb1ZkTXpNREl4czdDeXNYTndjbkh6cU9IbDQxZXJUa0M5b0pDd2FDR3NzOFMzUGk4TVgvaWhjTlEwK05vM3BuenB1NHJCa1lwbGdrbW1tR2FHV2Vhd3gxbm5YUFMwOHk1TXp4b2JqdFBJVm9mdGxFWDlkU1U5TlR2VGtzWmtHQkxTV3hmUGhMSnRYN3Bwbm1CMURlWnF5SnFtR2hMYU4yOU5QNDI0cTI1dzE5YUE5UVZ2SFB4RCtQdmhKUDhwN2lJWEh4QzJ2blVDNFdQSkx4WEwrS0Z2eFhTTzJmcExBS0M0Q1dMN1FWMFlCOVczTGxmczE3M0RGVzhIYUhaMFYvakhGNElUYVBoVHIwM0ZEcDlkL1RLeFRQZGYwSlBSKzlwYnRZWjFzd2s9IikgZm9ybWF0KCJ3b2ZmMiIpOwogIGZvbnQtd2VpZ2h0OiBub3JtYWw7CiAgZm9udC1zdHlsZTogaXRhbGljOwp9CkBmb250LWZhY2UgewogIGZvbnQtZmFtaWx5OiAnS2FUZVhfU2Fuc1NlcmlmJzsKICBzcmM6IHVybCgiZGF0YTpmb250L3dvZmYyO2Jhc2U2NCxkMDlHTWdBQkFBQUFBRFA4QUE0QUFBQUFjSndBQURPakFBRUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQm1BQWdnUUlPZ21YRnhFSUNvR3hCSUdaT2dFMkFpUURnMjRMZ1hvQUJDQUZpUUlIZ25FTWdRb2IyMkpGQjJLUEEwamlFNEQrUHlZM2hnaCtSN1RWOGdkbGttd3lLcm10OVZKaG53L1pGYVp3ditDZ2tSK1pocWVUVFVvUzdqYTd5RkFYK25CaDdJV0RGaXlSRW42dkpEZE1qZ2FTS0pHMHlJa3B2bnZIaWgyOXREUXNGdWtQZSttWjBYTmJ3YUhPckVVakpKa2RudC9tLzdtWENrRkVzRkVjRmlvZ0VoWVZFcGRvVVFTc21qclg2dFl1TXY5YnZiSFEvZWRlaDY4Vzd0VisrUGF5aUgrNDd6Zm4zTFlOOEpHMWdKWHRHSmRvWnQvYUREWU8wL0svMXJMM2ZuZlBZdWZQTEEwc2NKTFo1SkQ3d04wSkM2VFJTTFJuQkpGeUp6UmgrZnhmTHN1cWJudVQvRzhIUzFYZHByYkhZK0FCQm9FOXZmdDZrVzY1cWhLVzNzdEpZUE9RY2V3QkprdXFnY0FSRHhmUy8xMWJ2WUxTUzA3OXl2UlAvNHlQT0lOdVh0czBnUUpQZncrR2h1Vi9MazNkR2NCTFY4cFRmcEE5MnBSMnBXclhkbXJSVDVYY0FENEFFekgwdE9rQWg0SnIvbjRzOFAwdk1CQndNSy92azhwSms0SlRlQjZkQmJ2NEFNLy9mR0U3MW44NXM2cTYrOCtNWUFhK0VrTFNocHhPdHBFMlJONDMyc0RpRlBTMHg2c3ZSNXRiKzRxL0JUWWhDRm1YWE1rNDlQdmZyODNzSXViRHZQRGV4UkpONS95SVdmdC9ZVU1HUDUyUTFKdHBodi9mcHIzdHZUT1dLV1IvOUg1Z24rTVE5U3RaSVNoSzZsS2xITjE1bzVsNUk1d3hDQllFUzVhODU5ampSUzM1YWJUL3kxNkZDQ3RaWHZSK2xBTkExYWI3cVpES3BNVFRJM1lweXBTcDZxUktDZHhXYWNwVVpleXc3Q1I5UzhmZ2Y0OWhWbU9idE50TjU5a25pYUFvSUtpZGUxNEZBT2RwdHNJQThGT25BTEJ4Ly95aEQ4TEErTGpsTWVDYkkzTS8zalBJL2NtaHhGbXlVSnovVGNFKzhmc0NiTGtiQUFDOU53RUFEcnZTalZKT2dRS0ZmclJ2djV1UzRVQmpxMVlycElrQWI1cGRjKzdlbzg4TXJBK3UvL2RhbjNUL08yZC9qdVowenVkcWJ1ZGhxY0Y1WERvQmdFTzFwS0N3U1ZMZSs3OWU2YVB1ZmNkTjNzejlVbzF6dVBpL0kvL2ZQdlBJcklmdW1YVGFsQkh0ZjAzMXRiL05iZktSMkxRWG5udnFpY2RuczVIWlJzRG15MnlzTUU1Y2FodituVi8wejRyV0dnRC85Ny91N25VL3F6aS9tbDZHRG9MNU9GYmc4VitVdlh6TWFQWkQ1TUZtRmk2bkZZcG5pYUw1WDVYd0pqYVhtWFdPWUprdzZYcXcwYVRnQ1lHY2VPVUpBd3R3R3JsczNZZCtrNHRzUEIrLytoaWRPRnY4SFVTbW9xTnkyWUt5bzBuWnNxSWk1Zk9DbTVCMnB0Z2xBUmRnd3ZRVXJGQW1zZ2hFTXlHY0ErUEVJMGtDa2hNZ3Fhb2xGRlNGWUNvdVZUMVJ3QmhFYTE2QUdIc1I2U1F3a21DZFV6TDJSRU9jT0N5cXl3UThHWEg2TzJSWHpqeU5kL2pyWVNpY3ZjdHJvUXB5bFdqM01xWVhOci9TS0dwTksyZmo0TFpTakhLbzdJNEJjVGJxUVlvMWdEVndLU2NnNkpzeUpiT3E1c3BNMGZtWndRR0ZyQ0RaL0srbEtISW9NTVNLNHFUdWNpUThlbHd4YWNaaDZKZ05CSkg1RVdJdWFTZ0lucVBWcEcwdTh3UUxUdG1nbVp1S3ZMYVpTdjZrYU5RajBwRm9oSlE4eVVVVXduTUJNbFdvZWQ4b1NrZlJXWEU2ZEdVSENpcWNpbmpMSys5SnhVd0ExYlpIeVVOQ1NZTFNuK0VNVW5LZVNWZ3RHa3Qyd0Y4MHhjbzhpVHZQRWtyRW9EcUFOS1BoOFp0WTdXcW4xMEw2c25KczBKRjFMK1BqMTQra2RWTm1ERTFHQmVYdFcwTy9GV1FTcUVFclZCSU50ZmtUL1E1a25ZQW8zWGhsWG9JV3lwWUhJSnYyVHNMQ3h4RUxCd205Nm5PcXNtbFRlZ3FoVlVSVml3Q1BXMEVNNlVnamhwNGg1dTBFWU1CY2tScS9pVlFKR0xNaGdYVWhGQWNrTXZrVlY2d1IxYWhEdWZiVXlnc2NEdmFxZXFhZ2d1cmd5VVFGZ252d0pBNThpWWRBRWlDVVJJZ2tDV0pKaGtSU0lKWFVxY04ycy84M3ZGemxFZTdKRTl4L3FjNjI0TFhmS2lQbU9NSU43YUdRT0NnbEhpcEpnRm9Tb1pFa2FDVVpPa21CWGxLTlhPY1Q3RUlNcGE3RU9FWmJCTjZLZzVvUDM5YTNIY3JBMHNCUzM4NHZCekQxbGtSQjhxYVlzZG94VXRicEdoZUlaM0hXc2R4WEROZU80dzJ0WUZ6SUdXOXdVUVRjTGE5WjRmY2dFaEhKa29HNFozd0IxTDJDcnk4YzdiNXNWaEhYRGozRndDNm01VTBSMWorSlBDdlJuTjMxbEtEbVNadTc3eWhGaFNsVjBiUU1JWnJkOG1GNU1KV3BaTVNaTllzS2VPeitNSFB0cWFHbkRBUjFqMmg5VkpUbXJObG9XWFlMc0Z0azVwWGtJMmp5S0JBdEFTc0RSdHI3anNycCtFTTZCaDMyVzNLREpnMDJzSXczRkM1MlNpYXB6MnROaVNjN3hJeDlFOWpTUW9peXZPZ2hKWmx0NGNrR1ZVMlR5b0orTExTVFNkNjBRcnhOS01wVUtnOHlZZUVTVExId3hQczBSdk5iRlNsRDYycGthU0c2TlRFck40dVdvUWRVZFlNNnBZNWlIQTlTc3JzSnk5VjU0aVhZakFrUk1aSVdyNGxYSzZCeDlFaVlpdzEyeFZuQ2tROEhZbGllakZTYjg5RDdNa0Y1YXJBbUdTNXF6R3MvaWR2aEowL3V1V29WdzZKTTFVSDhpR3VweWF1SVROV21sbkhOQ081UytMMllSNG50amZPc1ZhQXNaR3FvTFRsNUtCeUVsT1ZCbGFNNDA0aTJobVd1Y1kyR2dkTlZNQ2ZZUlk5dVp2ZlNyVVhGNTJoUXhkK0txZlpSOGNtK1BQMnViRm9uNzFHblplUTZab1RyTCtsSG5heFVENkhLYUlqVU5rNjdSenMwTnZCdXNRL0d1TW1FQ2xoSnNyTGt6YUNVUWYzaFFGMGthREFwODRSUHRnWHFtT1JGY1pvVlBTQnRRNXZFWU8rRkNCR0p1UU1IZFpLVEFyaDZJVUszeE53VFVDVjVLWUN2RnlMMFM4d0RBVFZTa0FLRWVpSENCb2w1T0tCZWFxUUFUYjBRWVVSaTNoeFFMVVVwUUt3WElveEx2TEVGckdJemJ6MUpzbTdJYmFpQWRodlVCVlJDUjd3YUd6bzdxcUdybzlycXZxQVNldUkxSDlEYlVRTjlIVFhRMzFFREExMjFNTCtqRmdZN2FtR29vM2FiWWZ3ZzVLUTZUMGJZdHNWbmFicGZIUHVVRnQ1QUJqcEJ6VmZ2VXRQOXE0TmNRdFR2S05tb2c2OWIyQXVVL3dWTDFudmRFREN3WG51VWFVMjRSQ3plSy9nVURFSDZsZVdpMzJ0TjJSQWdlZ2xabVh3VzBQNHY2ZDczZm4vZTdKRjdneFdNM0tjb052cjlaK3ROOXlHaHBHTlc4L1lRa0VZZlhQSUU5MzZ2UFMwOGRwRVQyOHNqUmxidm5PRWJvWExlVFFteVQ1ZXg5Vmc3R1I0VllhRFVxOElsZTlNNUNMeFdQL3BJNndTQyt5Z1VMaVRJSGxucjl2QXFqL0NZSFRxSjRHdnlVRmduUk9pNk5XRk4zbGFiYXFuZHBzSkljSzgzaHRUcUhqMFJiTnR2MjM2L0Z6MjMyazFNZ3lYZlVMbkVma1hDMFNtU0FrVXlxVVB0SVNFQmo1dm5wVUNYdmhVaHVyN21QYzdzOTZ0RzdFalhiVVRmWmg1QXBydTYybkExZElLQlExNk5WOUlKcUZTV0pVOU5FQzVGWUhsZmJTMllBUTgra0ZaaU0yOXVoeUpCeng5dThvbHU4NmNiZ01ReG1RbWlNVEhEVG9CY1RudGlhSlRmVFdiMEdwVGRKM0hENEE0aFl2UVcxY21DRzVQWWRSa2M4eVNWZTY4WkFUaVpibUtmUUZlTTF2WlJXWGJ3WFllMkUvNjcvQ25tL2VmaG42VkpSWXU4K3Vna2N3YlhxL0xOYlVoK2R3ZjN6ck1Jc3Y1czB0N3JXUXFYTGkxTFVuRE1RckVjUlo5NXdvamRhMEpab1FncGxzZkg5dnJYRGJ1QTdsTFgvbEtlQktlMW14eVlyWWxyTFVmS3c4N0VkTWJHMkJpeGI5VDU2RnI4YnhyLzFtcEJGdjNHMEtCWmJtNXliSXAvb3plQ0tJUkh0dnk0cGxzVnh0M00yN1JwdU80QnoyR3N6TTFzejRHamlRbHU2ZDZncXU2OGRldEkvVU1lVUJtWjBneUYyS1UwZFU1TGxmcjlCWldrUEllblM4UmRzR2dwT2VjR3A4ei9OL0RSbmw2WVJpKytNUHRNNk05THdreEtmMDVaM2JPTzUyYWZXT3pQZDJYREkwVTE1MlJCN1JWWWhLU0VTQ1Y0N1BRNlJqK1RDZVR6V2F5OVhTZ0wzZTEzdDdmc3ZQNllXYWhOckd0N3B6dW1MVmZSc3VPQ0JVQWxoZzNuN25mQUxhcXI0Q3dhYTIyZW9NRTE4Y3h5UTBRM250MUQ3RlZqeXk1b3NhR0Vpa2ZiSnMvbHhyWDRLNEVwSkFCZ0NiVlVWOW9qOGdIMkdMUFJjcFVselFDY21hZGJQV0lEcEVzSndFWVJodUlTdXRxZHVHYUEyTE1pL1RRNDVnUU5qd21oVGNWZnVadHhVeGZCTVpTNzBSZlRuTFBQckdaQzRuSlVrenV1Vm9YeTU3RFRPZFBzZEZSN3B1Ym54TG01eHlwVXZacTBsTENvY3o4aFFKVlRWaGdLUFM5VkZYYkxSRHJEQjNIbCt2b2xWR0pJYlEyNXd4OG1zVHROOVJMeU9RbGpBcHlJTGVHajIzS055c0kxZEJlUE54V0JGbGRBM1k2T0p4U1hwV3JEUGtIWmMwaCtOakY3cVZRZFI5S1dxajFiRzlBeGM5VHBrRlJWU1o0aTZhUkk5a0pMNXo5cHZvWDNTd0p5cUZ6MDJmTEhRVUVtWEM1OWhOWWFvN0d0SmozU0k0ZnFVemhDNVZPMnVocmZvMGcrcUlVelY4bTR5WEEyTnV4TUZLM3pWb2NuOGNTK2FYVStEZmpOQ0I3bFk5ZytyWHhVV3dXeXNjWm9uM3crV0k5SlpBZXVvTnZ3aktyc0pCWkN4SzdYbWcxZ2xqQTdZb1dUbk96WHZXN0diSmNHUE1HR2NWMXNUSEV6NGM2WG9PZU5hWjJNMWgwK3JSY0NFd2plQmpXM2owcHpwT3ZpWFJnUjc0SnpLaGRTdVdMaWx4Y1RTQmJwRVluVnNYRUJYYlZVWkhzRzdFWDNGUndzdHp1S3JqRk1xRGhhbkRuaEozV1BLZ0dLMEJYM1ZJZk9ma1piTG1KeEtZRk5USlFBRHptckxCKzVGZ1dLS2RNeHNpREoxbFJseG1ZN1lnRXZRYWdvcGZVd0ZtT3FOSTFGa0M1SkdXbnpJMzNpVjZ3L014bDRrMlo5ZVY5NXVkUnVtSlp0RXpFK1F4MWZxckZ4cWlzTDV0SGtLT2ZSTXBJb1krSEMvVkthM3pPVmZWMmpxWHpmc1Q0aE1zM3RzL2RIVjJ0OUZwaHZaZ1NVZE5zMklUT3RyY2Q0YlJRN0pOcWxvZlVjWGxSUTZMbzRYaDBUNGNsNjJyemFQamZWS3ZXSmxPTWpKU1pGVnpER0d4M2RoaFRmSVVWMlVLRUhiTDZhNFJSOXJORWp2bGJTNEtiSjJsbzRaM2l2a3M2MktSUmhuMkFvdGRYeHFKVU10WHZhc25jV1AvQUhCMlhabnJqTldPZHdhWm9VZ1dCRHBlVEpXdXVGTVl4TllxbHBxaDNCeUlhNzlROFRxbkxGZGJtT0JOOHlxdkNJclExczl0ZmZxY3VVTEh2cnhBd0s5Q2NFZGhFVTY4MmovVmc3c1B3S3VxcTJiOGlpU0d3U1pUeXlQOXAwM2wwa2VxdTgyNWFJczE4YlBzcWN5bHZvRXUvUGpBRVAvZ3JZM1kvajdHdXlRZ28wREwrRUpscWhiMGdJdjd1c3prN1lZTDR4T1lzNmRkajNCbyt5TEZFdUg0MVkweXhrbmg1RkdJWUJwaWtqWFM5c0JDaDVRWUZlOStUMXFGVlBRVHFBTnh1WmlzNVhaUHFlUWx1YXliK2x6SnNKM3crUVFIZnM3bmVBNGc4S1ZnYkg4cGtEa0xSN3ExYkJXUnFTV0R1dkRHQmgrVlNOMzZ3ZDl0aUFrREZxdVZWWlNDVWZ2QXJPTmpQYVRsbnZJTzJ5bjZBZmxZZVV3dTExcDhmU0p1c3VwRkxGL21qZTRjZGlBZWNFR3BLdThRdk5ReG11QlFFNHl4aW1mV2V4VzFjOFJpOCt0SmFwZVA1d2FXV0piRTgyeXp5UFJjckc5OUMyMVdjTmdSV0dKOTY4a0R1a2xyYkZzaEdNbkFWcmk3Y3Q0N1p0aC9nb0p1RmxFUHFqUU51aWU2ZlJxMjhZeElDZFZhS2NaUTdldERmMzRQYXNidktWTzUvd3M2QU9OSHJNYTU4Z29FQWFLRklwRHFtTm9UVWhPd1EvU20xMWFqbm5vTUpkczdMUzRXY3pLTTdrMnBTK0xMZ2tZQjZ4NjR5NExtNURiTGJmakM4OUk2Q3lJMVR5SnVSMSt3YTYwNmxsK01TQ2R0TjllTEp0cldmeG1mdzAydWd4aU1hRUh3U25jYXphSTF5alBrRHgzVFNtOWxCOGxhTDltZjdua0V2T1NpV2tyNDJqMjRId3NwU0ZrYTc1c2FhQjJYdHpnblB2TW12OXovb3o5MG1ieUZzOGNWVWswL2xwWitxSldxejRkNXVSMWhydlJtYWZHLzdidGV3eXNiZVB0M21tTVptWTRFbEN0K0RHc0xLTW5IQkgzRXBMaWpXaVVWRzZkS2YrYnZLUk50a3p6OHUraEY0cjhpcHJRTTVXZTNxeHBuV0VRME9vbWNoQ3UrdURCWGZySDdhN01tdGpVYWZZRlJVQmY5aDRodXRiK3V2UDFEMlkzZVAvUHdnakh4bjJ6UHN3SEg3UjdLVVVLRklrQ0VDc0RkVDVlQzVQa1JnOXcrak9EWWNveFJUUW50elVpek1DZk1QbGVoV0lQL3QxemRCYlM3cWtNNC9hYTRNY0ZUbWNIZGY0UlBSMEVuTXhWK2pyZ0w4eHVlZDZYOFVLQmd0TXkwVERPTlpoNFhwUGJ6WEdiMHdKUkNsdndPUXFIVlJlSWxiR2hrWlZ2aHhkb2tldldMU29uNVlJUmp5dW9rem9Ha1pYaE9XaG9tOXZraVJsZVZvNkk0MGRJcGV5ZE91UTZuSlI1OURNRlZLdTlKS0w5VGxUUVVCQnFJcVpRdCt6QmtoZGlCVHJmdHhkVDhzQ0tvMGVkOFoyRFh2MEZXZmVaRWFmNTFBUkhzTTBwdlJ4ZEMrVmgrbTBiajFJbXJNTUg2aENJRnpFMVRlMFl4YWVJWjZ1ZzVrVWFDbjV5a0ptajEzTzZMQkFoTmNLSEkyZUs1bzJEOHplcXhOL2M2ZExXckNqUmVmdkw1bUVFc2xBNGFQaFFqVnRSVzNxbW9qaEFYT3MyYUo4dmxld2pEL1I4S3NnNEd4L1o4WkFPZWROWHZZeCtIWlhUZDRtT2RtNGFqWmhtODhDY0JZOTlpaUxMVlNLeWt3K3BxVWtyNUlORzNoZ2prbTQwR2s3MGhFUTJQeWVIdy9iVDRxZ3VLUUl3N2RDbXFhTE9LN1dOUDZpeWRDampJV2s0QnBoejlZVjF4QmdyNHE3N3V2OWhFQjNhOEowMkZpRldCcFNMWmUra2h2L095d0p1N1Z3UUdnSkFVOFNxU3poRXRrdFVrcTNyZTA2WEpjUGdLZk9OWGpSOWxaYy9TaU5xVXNOMTJxSExOa1JvanVNU0V4UjRYQzFGNDB4c2dSWXllVW0vR0M5RXdCUWRMWGh5Z0Vqemg1b20xcGZjSDNVNlRiNmEwV1U2U2MrdXVkeTJJSEtHaXBYdVNFaWg0aDZLS2JVVXhrYjVEcnpEQXBaMVd0MkpwZW10SlJRL293VStsUTVWdTluOW9jd1pCRUJEUjJzZWp4ZXpod3QwSnptTWFIb2lnZy9jcHRIK0JjUGlrSzduSTF3SzNxbFdqaXU1YU5wSnVTNFJITnpBRTY2ZHRMdWtEUk1tVm04R0hKV2NtVmszQzBzMnM0SDNOajBlTlA5Uk96TE9IekJQb2FCWVlUOGdjZnNuYzV3azcvMThWZURZTkM1aTBSK25FeEVFeitOdlUwTjdjMVFrbjZjOUM5Ky82dWhRUkRWTWk1ZmhJc1AzY25vYlJJR281SS90ZjQrTFVsb0trZXhEdjM1eFc2WVlvRjJGN3lNK1ZSRERjeTMyQUw5UEdUN0dkNVAwQXBQdzlCSTY1NW56S1R4WDQ3MVdwRFRna04xeisrOG8rREVCN2FzaFByNlltK3c1M0YvTFhyMmF4a3k1R2xkcXVNYmk3MERxdWJxK2hyckFDcjEwd2RxN2ExVTFNQytzMnJlUFBYWnZEZndyK05oSm5Uek42Nk1USmpFc3pMZWc5VVYxN2JMN2NNdW83eno3Wi9vaGRvNjFBdkNTRTN5bitTcko3RmxpdFR2WHRHOEZOMDFYNU9oVDBlZDFVaHhiM0JONnZ2Rnl4R20wdGhqNlpJb2JERk5NaXZKc3FpWk1sNXRrV3JRZWYvV3VYTkhVRXdVQk5XblFCRE1oTXRQOS9hY2YwTmd2QUk3dldVRjA1bkYyYXZ6K1hqNXB5dlkwc1ZveW1YRVhSNkFZaXRwbndyemVEZFhwQ2pVMmY4T1NMSnAyS1FZT25VV1dQWWZPTFhtMldLL0JkRHZHUTlTcDA0dHBwRGhYVXAvNi9YU0tDMXBmaFptV1VnWkpkRDNNR2pueGZYVjNXL1dpOC9UR0h2cDJCMGhKU21qR01VWXB0T1E3dkI4L05leVdqcVhQbE95Ryt3NlNLRDBGaGMvWmo4dUx1NUo3eWt1RVFWTE5JMTR5MFEwWldQL1dSQVZtSC9EVGVLSXpPZnVjRTJVL2trc2hubHQwdFRCR3VwZVo2cCs4NDFKcm5mempNVStZbXVWK2l1OHg1VzU1TTBmRnpub3lqdjdOcTJ2TGRyeEN6TUZWNVM1YXNHbWZaRkY0ZGhTeks0V1ZuaUp5V3RhOXVNUEJNL2QyVHB0R20xOHFVYnZyQ3AvTFVteGRkekNYN0tBUXJVSndvcmxTVmxrbFZHN2JrZVRVVnQrL2dBNDZOMWkxMnFtVkNlUEVWdStxNnlvTmxSVkNqWG5ac3Q1UEdnV0dtQlJMaFlWL2w0c01yMTcwZDRJMDFHdm1MUTQrRzAwU2hGVm9sek1Lalh1dTVVaWc0QllqNlY2bDQvNCsrejI5WEgzZWJkVytoeDFFOVI5S21RSkhXazhBbHNXdmpYeWR1clJJVFFHNzluNHZXUjVDaGR5Ung1VXloUldTRG1xWmpWVDJleWNRZ01LS2lvYTg5ai9EZ1VoUUZQVHBFSm1aTXJodng5bEtYN2dxZlNaVm42THp1QkJBK0dSTFdLWFRmQSsvNlJFUFM4ZmdqNnZxUE5HeWU1Y0NMUTZqSGlFU252bnpEeWh1VDB1MFJLMCtVSkYyb3p3Y0h5MDlhWXZsOHdpWmNKSDBVbmIxb3Bkc3VCc1JXaUFRcnB5YnB4aE5udy84S1JJN2oxaVAzajhKUTY2bzVRVVk2R015d3VlSEw2cXUwZy9CY0tTSDB4OTdTNUZtNzV3RVl0Y21XeHljMk5Rc3RLNEtZMFpYRExvY2k0WkRETFROaW1OeVRRVlJFdVJXN2JXeG5jSnhRdkZGWHZGd3B2SG5vZ3FKTWFxRG1mcU9LTnNEcVZWbnRzMC9yMjA5bC9iMGhRTmwyKzF0U25WTHIxdkZRelJPR2grOHRYek5ZNDNVTkpNOU40NU4vM0s3L3BCbkpLUkpXZnBaa0ozaXRaa0NXWHFLRDZDMTBuWC84VHVuNkVXMk1TY0tGRnpOaENJUnFQTmYwVzdEd1YxNCtldDF4L1VaWS9KcEE5cndkQ1B0NUorWEFyaWVUdzArZkd5bVR5bkFrbzNBWFIwcXFid3FBNlAySFRHVWErWjJJVEp6YllWNFBheUkwWUZmUzhOazh1SEsvSS9LWDdzMEVlajBhYVZ4QTNUNys1UXNEaXhBMk5SUEEzUmp2NGlzOFBuWldLU3poczlIMGxoSWxwS2VMbFA2aTZOTnlZalRGNlhDbUxrRjlZeVBqQXpXcGVSL1lTY2dXVUhHNGhIMlVwWStBWHlRSHZNVkY3NDF6WTBzM1p0WFBEbWV6UHByS3lEUDE5dVFUTC9lQnFJeGN2OFMvMTBXQ0pSSmRnUXF2cFUvVU4yRWdHenl2MTkvUUZlS3Z3RlZHMnRPdFVWVEt6ZkJtSjlLVUxUR2g3N0RGZmZtNGZwN09KbHVEVTBMbVQyMTA2NGFlT2dzenRpQmVaWmt4T2NycWQ5OW5TS1NKWHpKcStheGhhVUdQNzBCa0g4TjAzRlFCcXVpRTZmS0t1NllFZ1gweUllZkpXMTJqcmdwdkpzT3M1S2tVekx2UDh5bkZ5SXYvN0VmYjU1UWh0WmZMaW55Mjd2SjZJRU9MdU5mUTdGOHlkMjdRUjNlNU1uemxDSEdheTBIT3E0MjZQVGhLbDNkNEZGM2NjY3RrS2JNK0wyVnowelR4MXMwSy81eHRIbXZmWVNjdWtTK0ZCajBjd2xyZ1B2cVBmYVRlVHNHZkJGZ2FWQTNMZFdJMUFsYlZoZ2VROXpzckVrWGpyWG1qMHh1V3lvcmxGZ3RCYXFPTHFnbW8xRko0V0JTR09xalNaRmZJVGhBLzJ4aG0xRzFzZHFiNks5d0ZEUW5talhHRFJ0dmtSN1FwYzlWdG5DeXNCeTVsaE92c3hrNWJEblNTU1ZVT3VLZWc5ZDArZFB4QytCUy9iR2dSWHJ2bFZwSVdqazYvZ0h2UjFIa0pxRys5ekRkdGY2UjM1Zm9yMnRBK3ovMXVGcFNLeXp3aGNwZzN2MzNjVFRMVHMzRDFJdXd0YjFpYkFRZ1BKT2ZrZXV2LzZGZElQZWwyRThlZXRDR3NSRXRkcWR3dlgreEE1NGZtWDNUbEhkaG1oNC9uUVAxWTdlR0ZrcWozaytPNkhpYWRPRVNrS2ZyVS9ldUNoWGxqUzgvWHdQN1R0R3luZE5WTDArYjRQUTN5ZjVNRm52R055VjFocmVoOURmSlJBRkhrTHJrYzZCeGpNdStrQXFjd0NoTlo4NTRFOXMzZ3p1ZXNpOXcvWEplb3FxcDhHTTN4YjBKWi9zamppQVBjWHVBSkV1T2lPQVNTMGZ6VnhJOHl0TzdWVWlCZi9kQXdHaFZtWmZ0SWhUb3JsRzRQV0hmTlQvaFB5dkZ2YVBRNTEvL0VtM1oxWmNheC90YXovaklTRVVvVnV5UjJ3Zjdvc2RRcEsyTVZPVnNBRDZoOGZFaHMxZ3JIMHdVT01XVkRzYmxhenJKNjZKM0tMd1lZRVlzRytmZCsvT3lpdGRkNTNBK2NRWkFxdGRETHFheVdNbW04YXYwSnZPTnlZL24wV20veFFpRnlxY0g5L2tQK2txVUFkck9QdVp5aUdyM1dmaHlTRmFCT1NKN0xxTVNyRzY5RFF6bVlwK0J2dkxtLys0VTkxU0d4aGRIczljNlNQWkJmRnozQ1lrZ2l4ZkJwcmJtNWVCZ1BmdWorMC9nWi9La1lxRDZtcW44WWFBQXQ3NktmeGpmaFU0RWNEUWZWZnpxcnRDK3hMSGc2OFg0OXNvMTJDSlpPblY5eWhtR01LOHpwMi9vVGZEWEY0TzAvOCtZaWpxTGpLd0R5RGxhVWhVd1dhYU0yT2VKbjhsR21BSnk5Z2pzUWFtdFRtemJjRVJ2STJZam5jc1BPMStXRlVDK3lFNDVvOEdLamhNMCtnMHJoN3lFVHB5TkNQZUhBM0UvSTFVWlVnWlZJV1VvVVZIWjJxZHYwR0xyVDNlaGREQjh6OE5RSGtEUnl0TWRUNElXcGRZZDMxUnk3YldiVkNsclZUVHZNMkxldnB5cmNCNDlIc3JwQm5lOE5LbUR2RFZhVkNpUXRhL3dXeEpuWGp6YTFNdndhUnR0MzVWY1oyZFlNTjhZcjhmWkc5eS9GWjV1cnhJT3E0QkZBK0VmL3ZQUllzSDhWZ0ttY2NzRjMvOFpiZHdXMHpscUZjNUZQWTgwSUhPNFNsTVgzcWVmNTZuaFBKRTh5MC9XTEtENTVTNWhqemsvNDN2ek1mdjN4c3NPUDJ4SjNWZ1FtaW9vS0ZlTmhseWg5R010RXkwOGtKVy9hKzVjUm9Ob2hlZ3kvSXlhSG41VGVjc1dTbWtSOVd5cTlkTzdiUUpRMm55NjdJUnErenFJNFZwa3k5Tk4wRVRnUXBlT0JIY0NsOUtXOFNoSTJhcGJVemsrTzBLdFVLNzloOVpUTlFxblZXaFBhZGhpZ1hLeldVS3k5TEg3NGtjTlpWRjFpWmpwa2sxNzhNVU5JcEJwNjNNMVhFc0dZaUZYQzFWTjJUZ1NBNXo0NTdzWUNMTTMvM0F4aHpYSnVmYlhyR3dBcWNraU9waXBrMWRpTEhjenlLbm9Qb2gxTHR5S2FKYUVVMUZHTWtEblNmOEgyM3Z4K2d3S0FaOUpVZVZXNThSTVpIajNXUkxUZ29qaCt6WmQ2YmU5ZWNycURaR0dUcjlZZzVTVHhhUTNVcSs3T3E5NzB0UlkvRGlWMDlnd3BqcG90dk9FNlFzZk1tQ2lSUVRRNnpvTkxVTkJnYzFKbzFBak9sRVBIdVZteHp6R213aWYyQmRtYW01eVNEZkJsVkFueXRUTmRqV1ZoU0hWNEtlUm5FMDBGeWxLZDZvNGJlcTZYNTYva2NHNEFLRFRzb2NrczlLL1g1cG5RMVpVdnNEaTVhdlNmN3p2dzNIaW5nN3ZUdDVSVVN0eUp1WG5mSVAvRFBkS0IxQVJWTEpLVHZITExlT1QweTYreGh4Q2JnOUdUNEVEcUUvcnVkV1BBbGlWNDJIRThIMWFrVjdlM3RFTVJXY212QXQzek1IZGlVSDhXQVdjcnowZVduaHQ1bjRKRlVLbE12OUtGOEVMYmNrZk5lanJWWlhqOE9GK1B6Y2s5OXpsOFRxUWwvU2Jja2JMN3cxdVdIVW5BZ21kT1V2Q3JmbnQxM3lFNnoyMmVKWlg5V1RtMGZXdG1xd25hanh2cFFVSXM0L2tKdG5aRlBMMWpSbERKZ28zeXVWOGNEOENUK0JuSU00S0pRWmc5cFZKMVNQSVBGYnp4YXdmdlRsMUp6YVlSREZtQ3NVaWpWMlBPeHlMbEZSRVlaeC9URDBuL0R5WU1yc0szVzZrS1RDdFhibUJ6bGQrVCtGNngvbGZCcHVXS25GazdLUS9UdjJ5SVBydy8vZjlZa1Z6c0FvdE9PMUI3OVlXNUMyUkYxaUtWY1pBMks1eXRkMGZwVks1Y0k1aVlNdXpVNDJXQnNKUHVzeHJiNEsycnpRTG0yaFFmRmxJVDdyOEN1L1Zza3gxUzlueWpYYUY1WDRKS2N4RzJwcmRjYnU2RVd2aE1oWmIrNGsvTnJWelIxbWFiMEdtOFdZeTdMSW01UDE4RjhucThqVVJBZE9Vem14MEdwQ1J0MmphMHhyRm9BRjZVdmFWWlRqSGlQcGN2Z3l1T1QrZjU3OWJEMXV5Yjd3VkxBZ3B1SDNjdGpPUnR6YXE4ZHZBcmlzSEtZem1xRE5JRVRJcm9lRDhJbGJDR1V5Vk9TaTR6d0VBbHBiLy9jOVQ3Ymh1L1kwRkNBUFVnM3k1OUw4V2JIOFJlcDduSW9pVHdiYVNJQXd2UHoxUlZzU3hlV3o2bnV1MjE4OFBkTlNiYTQySC90VmJoWUMzWEQyWjArTDhZTzcwSUVXcGU5ZHdhS0psemgxdnZqdFIxYktiYWhjTU4zMWo3TVRDRGh2SzNhd3RCUVNqb3ZHMjBuU2RIVlZmMzdhVmdSR0VBaHJoYTc5bjB0eFB2NS9LQnVCSnlmTEI0dGlndmtYNm9vR3pLM0NuczB0cStYUExBWkxyQXNxQnZlTm5Sb0VmYzdYdUZJOVA3Mkt6UlhnMHZoa0M0a3IwWmRrbUU4enZGWmQxdzZpM3dtMHpnWDdUOHpMdkRYK0xJdE1TVDM0N0x6Q0hRZFNLZVFUNUtJYVFWRmh0K3lwRHE2RHg0UE5WY1JvRHJLMUdEYWJTeGptcWVDVUxxL3hDWSt0VlRKeE9HMS9OTkhYcSt5Zlk5c1FvMmdKejVjb1hSUWZEZlYxUzlNVzJEdnMyZWhZclNOTWgxSWFaclVJVDdsNE1hblFmWTBleG9iY0pTMWVyT1M5Z2JNTktjSzNiTzVXSHNHQ0VZdGRyYXRMK0t1OWxlNEtqSVhBazNrYjMwTlFPVnVkQjk5WnhKZlBRQmVuU2srRDhqS0lwOW1UR2p6OHkydWQ3OXBCWlA4K0VNZStHam9jNkQ0Y09CSUEwQndFQm5STU95cnQ4Zk9uVHMrQ1I2ajdCejE0cVcvUDFiZEdTZjEwMHl4UzBMcVBGR1VLR05RQmxldGRSQ3Yxa2E5a1pWOFpvaCs2VEYxQzZOSi94eTQvKzVscFkrTkEvd0UvYlNDdHBFbWY1ZG9TNnhSWTVjRUtaYlF0U3R3WTZnKzA5UVg2QTdBVWthTlQ5UnVld2lQcGFIWjZjOUcrRGhXcmxBOWp5M3orR3Y4ZldiNHBINUQ3OVg2aTl4cnJXbnVib1EwckRUeXN5NE1ITnd4dHRNZVFKMStEQ1JTNmJsUEw0c0NZWStqeUorQlREL2FUMTladzN3QTV1ZGVYWVRHcVJxK2RHTVpuTGMxbUZVd1N4a1J6cjdhLytrZUl2czZMNHZ4ektoOGZKakxtS1k5ZHdrckhLcU5kcUhrdkx5d29pNWlESnRuaXA3SlFYVkZKSlJUcGo5QzJqTjNjTWhhaFBZSytQUUVuVC9seWFSNFduMFh6aExqb1U0T3FGUmlCL25hQktaOXFZYVVxRjQ4UVhiZi9pUDdSVDQzOGZSYllOczcrbFlJNlNoSmJVQkFDUDU3T3dDN0l6SHo1R3FlL085cjlqaWVwWXdtcEtSTTdsRDc5R0VZZ2xFVmN5VW1jdVE1aVFHVlp0ZXQ4RGxiTHpyUWNiR25mRVdyVTZBSVViRTJ1djhSYVl0djdMb1pHZ3FlWkR1K2ZWSlNTNi8vaFRGbnJoVnNsU0Y4MDRPNzI0M1FZYXNtdEMyV3RaMzdnK2xGSzZwOWVCM01hbnNhOHU3ZkVwaDRoS3MwcFBPeWNwMVkrOEhobVFEckVjYTBTRG42ZVdGSTBLcDRQTVA1bFBqM1hOZlQ0ZHlPck9LY04vL3Q0bUFmS0F4TmNQWUtlYS9oTWp1NGsvcy85WXBmeVArN2p2b2VmMzFaUGxtUUpYWjNtditPREw1Z0hFak1BZmdlU0ZhZFpaN3RiMW1DQWVSYkFYL1BsUzJaa1kxRGJXSzRyTVkybTFUWVNPTm41dTJZSDFpM3gxOHFsNkcyTEVmQWl4aW4wZ2ZMYjRobHZxMGdCelFITjBUMEVJNnRGYjVFVnBLVDFhUUVNa29odlcxb3JvV3NWQ2ZGMXFIMUx2VVpWbmFxRmtXeDNsbGpYeThydUZtTG5JRmRZZHYzNk1mWG5qMy84OHZPM1gzLysyVWZ2WDc1NHdzaXlyaS9JTXpKRDBxR0p0Y2N2RGcrR3ZrNGpnaTFyUlU0QUNQUVI1QWp1YnZjTHdxZjFhZjdiUC9pQ2RXNUpnQmkwNkRPaGJsTDlFNXFxSjIvOE0waWJ3V3dJZDdmQzN2YUNZR0F5cHRqZHZabWlhWHlXOUVQNnNyRHEwR2xWUXJ4NC92TDUzWW5UcDJjWDlyYktrK29rOExHakpIVHMyclE2TVh6cjg0L2ZQYnJiMXdXcWVVV1lKcHB4bmcvcnVseHBiUlpwaGhrSjlMQjZnK1hjajRGSG1DWUJ0eU5oK0l4bkNMWXJEakVFYnZKRjBBQXpUWm8zdkN4WGEyYmVuTDk4enU0bldWOUlvcjQvbnRybFlDeTI4VFlIaTQyd2lJQ3FjVXlqVnVtQUhxSHdlV1lXUUJ5QS9IMnR6VVZjYlM0amlIaDZzOHd6TEZmaVdXaXlDVURETkVpbmpGb0FNRE1ZN09XUjVaQTNwWlEySjNaUUM0RXJEdWVtQ1FqblZFVFFIRFlINlRNNC9lVnl5OFVWVHVGWjF4SUFLWFd3dXRBVklxQnEzSUh5QmVOWG0zZ1BnRVVrK0J5QWdXVStyWm1LbUFHMDJhbmpyRlBJRDErWWh1OHd3aWlRVW9tM3F6QVlaWU5qQTRYSXB1eHR4aG5JZFZqNk1SY2FMb0tHSlk3dkx3U3g2MG1iYnJaUlhaWllHZmFGa0RhbUJVTUY2M0MwaWlJQ1RvZ0ZESURwYVkzOUNLdUhBZEZhOEFCTnhLZ055S3pmWGk2Ulk2MnAvOGVxU2xqaGl0ekdXdWkxU3NKUm1ob1gza1VLb0JLYVlSWHBvMHQ2TldjQjhvaFZLWlpXMit2TE9sNDJjM0lvdnd5cGg5ZkhQR2lVckZ4dFhuT0ljN2pIREJIdFRrVWdtaUdsZGVsdDN4UU9ydkFGZWpXa1c0RTRmcENoZ205SUNkTjNHMGt1eTJPVXVIdTRMVHhpdGYybE91RUpueWg4T3BTUzRLUkg4Z2NqWkVmVXBOV1ZVMk9LZldXSE53UXlsSDNXT3FFYld5TTVyR2pWMXFYbEdFSnFnTmVRRmtKdzBYMFlQTXpuSU5rejdIRCtHdHNPNk5SOGkyZndLVEtyb1JHdE5wNHRjS0hNRXVKaTVOaS9rMjd4bHJOd0M1TTFyN2hmbE1lK2R4NU45TnRIMnBoQmhPM0JiRjNIWWdVcVlaRzBEclQ0dkJVcGoxSERaSzZBaFgzQkp5dms5QkRWeS9kOWVORGpIenl3NkRUSHliQlg1Y3VXdE1UZHczdFRuaEtrVmpqTE0yKzZEY1JJMjRESzJmYVpTVkxnZ0lsNUpIam1qK3dPdnJkbGxzdW9YdHJBTTl0STBtM1d6SlRMVEp5WFF1RGN6VTNZWE4yK0NJd0hqR0JoWkJFbGFPTDRRWVFOZEQwWjA4MEppcjYxZGZ6eTVHZzd0bzFQUzhtOExxSU5vY1VLbE9uSkdpYlkwOWtDN3YwMXI4SU8ySUNIa1VXVW9Na3VIbENGUUF3bm9wQlF5NUhwK3NKamFVMjR6NUtUQlVCczY4YUNhME1DWXlZYkdUbzJDcHpITXlON1U5ZzZZQ3RYWFE1S3F4YVpzcFByQ3pUdTltdmt2SmVYUlBmT1F5cUI4UW55Vy9EQzdJUUl2Qlp3eGRTZnhBcUxsSFB6d0FkTHpSM2x6U3dIZ0wvZVRPL051VnYyZk5TcHNTbmZabldBVUdwdVp4RDBHdHErMzVUUzFWNmlwTFVqVUVsU3pGeS9qS0dXNkRadUpVL3hCcjZ0V0JrblNBcWwySWVocXExYW9JWlNMUjBDOXBHanlRak1tbzVidThUayswY1B4eTdQVDhlK3pIMmkxWGpFUjFxalBOMkZucUd3emR6UlZ5WHNEcU0xelFZQXhDekdkeWxwZmpOZ1VJQVljVUtQZnR1UHBzV2dHMk0wdzBsS0tyUlFTRHJOL2NEQzdJSGdyNlJ2eHdQMjd6M1lGbmtXZ3dMWlZJcUZxdU55bGxmcjNNMncwUXpJbnF4cnpPMStONnljc2o3R3F0TGptZU1GOUlaaW5nWHdWOGFXSTJROTZvSUU4VlRzbngwaWc4QmNOb2U2Z3U5M2oySkVGK1QyQnd1ekI0Sy9NdkpUNGRFZVEraFpMYWR4R0Naa0tzb28xNGFBMlFQbVU2S0hacWFPcFVYVjM5SkRCc09aMzFmUnhpYm8zL1JGK1BudW8xa0NvM3FuWFlpakk0L29UVWRneFZtZWxmV2g3eFhCalQ0WEFiK1RuTjY4b1ZvUUFaMGE1c3RBQTcyOE1yS01tdFpKNW1JY1p2VzBVK2NyZmVtaGVnSkVhR01hbVIzSU1mRHpJMTRlKytpdmxjR0VRUEh6bmNOWWZTYkwxeUUralNhbXBsUjM5N0tPeGZxdmNhTlJ4bnlJeGN3aU9sV2RDTGhaQUJteXpFWGE0SVNWc0VqcXg4RHk4d0JnVnVQOFZmUFlaMVpRWFNzdEdnMEx0dFV1MllZMmRqdGhTOHNrelkxK09kcEhFYURCQUhNVlFKZEdpMHBlWDRreVIvbnZ3NHBrc2V4dVcyZ2xuajVjQ3RSdEFFQjU3TWRBS1c0RHI0Qnhmc1pwWEFaVHFtZWZzV3VxaW0zQitQcklEci9jRUJhVXM2ZFVGOEt0RVFHekJ3eFVGemJZaGJSTGtBT0lyTWI3RUhTR1VTemRIcEhmZW5kajdvellKMEo3a05iRnowdkVKOTh0OXRkYkZmSTRPQVpLY1FmWWJCOFFReitsSU9zM1Q0TlNCS3oxc3RaTE1jMHdHZnBiVE90QnJmUFByZzZQZlJKNXRPZ01ucmVBNFMwRUM5R0lSdDM4VXAvOGNLMFltRDJBcUxwSEJxZmxkSndsb0VEUk9TaGtpMUdwRDZGNEhnb0dTdGRaWUErcFdXSllaOEVUS0kvOUdHakVNekN6R1ArTXd5N0FIbkFBeHFEUHRhaGhYKzNZUlZ2SDRhbmdKUjhQVzVQbjA5WFVDNEpaaUFRVkd1b3oxRkRud3JhTkNsZWh3aDRBZWR4SmY5MDFXQ0NNOXZsdVkzY1c0OHpsdGFoZUd2dzhFb09zZ0JpSG1VNkZRaGVXTm1JTHY5MXU4RlN6VzlrV2hnVng1bGVNeGh5TzhTbXg0YjhPeU9EeXJxYktVNHpBWG5OVlNxdlRMckhjVE9IMVREREphZis4MnhFa1d1VlA2djdtVUV3ek5Bd282c0c5Nm95aitKODVKVmR2S09iWkFKOEtKYitlRk8za0wvU2dXTlBidDUwcjgyVGpHZUZSZEJZMkgxVko3NlVpWHFnSDNFT3dPbk01c2I1MXgraWluNElxUy8vcm8vMloyaXN1Sm1sUjBTSHJSb3FuWVdRazZ3cWpvTlBOcVFkV3lLNWVjQ2FQR3hPbWYyYWpTalQ5c3hvS1hWemFjdHNNVmRjYnRuYlZ3Y0M1Z25PVGg2MFV3MWliSmt4bHhMM1U4aGpQK1hJVUVqUERpMkc1RHoyblcwTTBnQkFvK3hUMG9FVklBVGhITEI3VjRTUGxTK2RjM3FpUk53Njc5MlRHSUZaTVBEemZLT3YrU0FLenpNMjRpMGtkUFhaR1dWYy9Gbk9LUzlpZzI2V0FDdzh5RnNZamJkU2dwdkRXa2JrNWl2ZEhBMEFMbEgwS1dzZENPb2lqeitwd3hxSHJPVTZuQ3FIMEF0ZzAxcml6aUFHWnc5TzRPcll4ZDBaNDNxeDQwdUdIaUM4UVo0TnpoVm5yVjhWaGUwRE95TlQzK20ybU5iSjZNekZXeW55YXoyTXZodXlya2lnTlp6RFZVRHNTMjh4WXBWY0hVRHFtQmxXbkJ3ZXhrWHIwZ3NBUkhsNllvWkhWTjJHTmxVN0hqbFpXcHNvUkZaaFh0cUN5SGpOUzJXYzBNelJOOTJxWE9odnFiSW9GM0s4VUFmSk5oSjVFb3NhMFNPYU5xczZaZnpVdnpaOStQSC8zNXJ0cU5YVHRaMTZwcCsySmFhQ1FSejFuZXJDdTZmRWNma1RsRFpXelQ4TU55b0VBbUN0VVAxazBxY3NydnpPdkpvV1l4cE5Za3IrdDRkd1F3SEpHQ1ROWjdGbjdERmROcTA5QVBMRFlVYVRXZ1h2eFRhb1Fkam9SZ1hSYW40dlZjWFN3MXMxN3ZEeTErdFJwWU5oSEZPdGtCRCs3U2NHOWUwSGs3Rk1nblJWNWJaVzZNbjcwcFpmNFdKcDFJd2VNa2xmbWVKZWJQNDFzbmZxcWFORTE5c0pqb1dKdFArdWtBZU1CRXlyV2NZc0lsV280b3lqRlgzWUlZcXp3OCtiQ0w2bGkwbXY4YXZ2RTJZMjJ5ZE13d0k3UnF3TG5lVjVOcTQxaDVUVUw2L0lEY1RhRWVrRkVOYmZjRzUvR1hvdEZDeDBmeGRYNTlqQ0E2S292WkFwMmIvMDZtMzZPQ3hyNTVLR093ME11WFBXNExHb2w4M2tuUkFTalhHRlhUbjBGMjdqcVUrVXZZQWJTTW5lRkllRTFndVUrOUp4dVZaRmpRU0JtZ2NnNE16aTdUaTVxUkM1ZGJ6TzdSc2ZKSGhmZXpkNHd2U1NkWkp0T3drcHVkVk1BSWVvVWxIRTk2MjJGYUtXcVgyUjVrWXh2N2NIaTVXWGZPQ3NyS0tzSUVQVFVvblZYb2ZWWTlDNUJiRjVQS1N2N21lWmh4TFh4bm4wVUxsWDFSaHkwc1lDWUhkdXFNTzN3YVZNbFVTdVl0QUZJSkgyeS9TVFpRVkhGdWZXbEJFMExyUWpNM1FzT01JbjZrekpOcHVoeXZOUCtZYVZtU1dRYUYvUXlGZ0oxSGJlOVlRNXI2Qm9raEJGNEM5K0QrOWduaTFEL3pBQzMxeFJ5SEJjOGlOYm8vaGR0dUNhWnlqSEdjNXVyU0xhaCtVaUVnOG11Q3hJa200LzJZZW80RTkwdHo0SWwxS3JYaUlJWWk1bEZuR0k0b0daL3dhcml3SGx1N3dMRHJaQ25RMHB4N1dHakY5dXNTa1VHWVk0aHNrNmREc0ZOTmU2dURRZEs5SXEvN3crMWdkbncvVUN4dndMRWFRTDBKMEhZL0htUm9zdk4zaFhveHFrNCtDRGRnSmhDZTdYY3JSNzFtelMwb1hEU21XdUdLUGVrWndKcUFwRGVPeXpnUncwKzk5cVVoOWQwRy9ycWtWd1pjdWJSRFdYTStSYUJsN3NNZDA5UHViVHVBMWVPZlFvUTN5RVdhbklnZHRKeVhQRmFBQWlKb3U0YVBIZ1pLTXVrQlQvMGRMeHFHMkI2L1pLRTlQdnFrb3hKR1lVSDBMSGN2NDVsMWY4VDhVTUdRVVpFUlAyVWJTNm1DNXJxR3dEUTdOWFY5bkFWV2xvQUJQcHNKTVVxUnNEQVZWWTByaU1MdmNxa0JWRXM4Nk1xQ0l5K2hBVFBYdEhORWxBYnJTOUJyUjE2ZzVIYVBscUZxWSt1bVJoRGRkeUI0Vm9EMVBJOVlDNmg1dGQwdHVsL0VMdzZMZGhNRk45aGQzelVwVVN3bnY5UEpQVWhhaldjWUhqS1Y1WnRIOEN1NTJCL3pHdzZPWXhGcm5aeDd3OTA0d0lva2ZXWjRHcnFrMVlJNTZyZTlSdDFmNDIvTGV6eFZoTXFrY3V4WlBBK1lsekZLUG9rSDk2WFVJTHViOTUzUkJZQWdUK3YwU2YveFZpd1YwVTF2d1hORndDQWU0L0c5NWZxamo2Zit3c0FlRENRUXRFVk5DODQvN0hDcFJqeGE4Z1N2eE5oSWp2Vzd3QjByRGMvNG93S01pN3hGK3pQaXBNcWtOWE1yeW5GK3hSRFZPT0dXa0ZLVDFERk53c3l1bnpGbUZtZmU4NUw5blNsdEdvZTB6OGk3UUd5RnpPUEZld29xdnN1Znozd1Z2NHpEK2pwNTNuOVRaOXZ1cVFKMi9Sc3Z1bENBejdoN3l4cEROdmptb1g0K0QwbnY1ZjVZaWVvOHhxVjZNOU5vM3NUMHVScVBEODFQN0l2REtCUHZHSlFueWw1aExtbHpJaGtsUDJZeGNEa0c1cVFkbDdZVnhhNVpKNTBwbmdOYnRXbm1mRENuTkc0YkFlU1lkUGZRTzB6Vk03QnNaTjNmTEJjL0ZYSWJQTXRPVEpsSWovS1d5MEwrNE02ZkFsZFdWTlM2eEI5ZUc2MTFEdWpRakhmbk1iNHhZdDVFeUpUaXRWTDk1eGZOOUtSTUs5RGV2WXBIUGxmc0ZRblRCN0E4aHFNVElMNDdoNWZuT0RmODZDMW1uenFjV0FRa3UzNElJeHFlaERGNXMxQnRMSmtEbUxreDF5UGpSK2s5UGJRKy9wU2llY2VqVDc5RnBtdlE1dDJRemdLeFJUeFhVVUVCT1JLNzZZd3orVFFhakdJYS9ZMmRDa0pXOHpZdDh6SFlRcVBXUmxpenFjTGlWRm0xdnRZemJtaUt0MHMzaWQ2TEZJdm83dnMxOFNGWVE1eXErV2FidVhjcDdjWjFxMFpTZXYxMmEyaHZUa2Z0MXBvV1dVbGYweXFjUEthcC9SK2NXSmJYZ2p4UnU0N1hnVUpPUm14U2xMU3M0dVIyL2N0L3AzeXlkWVkrVFZScGtkelNyZnI1T1pDblhIamQ2Uk9rVnlxVVl4MlljMXB2b21VMDN4RVQ2dWpYQmFIZDlZZ3g4d0YzN1Z4SE5sZEw0ZE5zNTc5a1FCcG1VbTBtKzNhM01FY1RKZUVyZGFJWkNnbmhtRjJjK0ljNWNPb21zUE5pdWRvY1VwbXRhU3VOTEhnc092SEZYT2VhOGtCK1ZYbG5oVXErOE5LNWtsM2dxUFpBckk2TktjL3FydWV3eHVybVdKV3hYRmNIbEtGS0tmMGtINHl5Ky9zb0JnUHpINHNPNmlNZXJ5YjJ5Lzk1ZXowTEdrYzJOYzkvV2VsRVlrdC9TK2doUDlUS0I5VzhBaUlTTWdvcUpMUTBDVmpTTUdVaW9VdFRib01tYkprNDhqRGxXOGVuZ0tGaWhRcndWZXFUTG5PWlVVcTJneEtVUGJIcGtxMUdyWHFLS21vYWVub0dSaVptTld6c0xLeFF6ZzR1Ymg1ZVBuNEJRU0ZOQWhyRG96TlZucmd3eGg4NUZFc0hTTjg2cDc3UHZZd0Zad05ualhXMldDVG5tdFVISEhWTlFrM1BPMzY1bkEwdTBBbDJKOGlxcEJlU3lQM3FUN0U2c1ZBUWlDTFc0dE9WZ3NKbEpQUmIwYkVMQzZqQXRrOHVSbWY0b1JvTXlITm9QNFp2SHl4QjBKUExnbi81Y2d0SGg2QVBYc0RndUp0OHRQeHJLN1hvSDFuM05uUEF3QWhkTUJmRXlpYkgyaFhOZGVkeDV0OSs2SkRYR3FuL01vWWFBM0svUHptUWpFdnIyTjlKcDdOMWZQK1dVM2Z0RkpjUm93ZiIpIGZvcm1hdCgid29mZjIiKTsKICBmb250LXdlaWdodDogbm9ybWFsOwogIGZvbnQtc3R5bGU6IG5vcm1hbDsKfQpAZm9udC1mYWNlIHsKICBmb250LWZhbWlseTogJ0thVGVYX1NjcmlwdCc7CiAgc3JjOiB1cmwoImRhdGE6Zm9udC93b2ZmMjtiYXNlNjQsZDA5R01nQUJBQUFBQUM0SUFBNEFBQUFBWEFRQUFDMnpBQUVBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUJtQUFSQWd1Q1pjWEVRZ0tnWkFNL2xvQk5nSWtBM3dMUUFBRUlBV0lhZ2RvRElFS0d4eFNzeEVSYkJ4QWJPQmZSUEovTzA3R0VIakExT3hrdzJsRmd6Wk5SWVRKbmJYUDJud3dRVGE5eWJ6R3dweHJMSTRPaDgwMkRZSThlOWVGaHlKakhhc0Z5ODgvbHI1UWFldC9ZT1d4aGRGd25oOGh5YXpmdjgzUGJzR3JldEN0ZnBKbFlKbUFwRy9DUDdiT254NUF1UWNvV2lDSTltekdGQUtuR3pGRmpwWUdhSnRkRW5rSENJaWlnSXFGaElXSTZGR1Nrb0tJbmJnNWErbldMaUkvWE9tMmo5Vkg3YiszOS9lMS85NW43blB5UFB6WXozUGZRMFJLZ2txRkpHSlRxZk8yWkNWUkoxNjlOUW5wLytqbTkxNUk4QUExQjI5THFzNC9FZG5hazJHOFlkVnQ4djBQekhNcVlTRTFHUmZhQ0d3M240NEQ0ZyswcTFzUmx2LytoZkQvYVB0bVJibUxwcm5oSVRhRDJNcGcrWVpLZ21hK0ZUVkZuYTQ2R2Z4L3AybktibEQ1bEVHN0JxQUxsYWNBbGNCcFVabiswL2VkcjlzWEs1R2FNWjFwOXpscit5ZS9nTlUwZFY4eWhUTFJwMGpXbVdJdkU2ZlhqcGhUbW5iNDMzU1dkSld1SDM5UHpIVEF6dGc3VG1tN2VBYmVJNjN6aWJhVlFxN21hdkRVVzZ0TldRVjYrbnY5aWQ2cktXQkJqVUUvdXF0SjE5SzBqcGNQOXY3UDIxVFg5LzRwd0U2QnBnQlBLczVsTFd0aFdhUjNrbjMvLzFOa25XeEhkN2ZZcDZEc05zckpLSHZBQURyRTdZUTRrU0lGcjh3cXdnNEEwOVJoeStnT1c4YzVIV0VZTzR4elgwdkZTZEtpbWJqUjYrUFhZei8xVFJvN1RldWI2U0tDRERsWmR3elQ4ZFdIQUpiajZkMEN3Rzh0M3dhOC9mRzY2ZGMwWVl3bmZ6WDhDL1A5RDNnQ2oxMjJ0Z0ozbWpsYVovOWZpNVpmcFhnR3dMdU9ZSVk2L2luQWwxTUx2Y3luVE5MTmFBUlg4bmhKWnF5UCtsakRDVFBlbXYvL25lOGVJNHo4Y2pOR1R5UzFUZmxaNHgrWEFIRDJ0MlJtR1lTQTEzR1NwS2Yvd1BvOFJmNStpc3diZE4vbnRjcnJkeEQwLzFWNEg4MExtenZ2Z2ZmQVRSc1BnSUU1TktEam50Y054TFVVMTYrZyttbm9ZczNKWXZMbWMvVG1YWG5uOHV4WjlHVHZuVmdmRzlidmsrbzc2L29sM1hMa2hhQ0VqY1dZR2E0enRGYjUyYWVnODF3Vjd0dENQMkZMV0ZibWROWTYwR0tHQWMvaXlwVzhRWEpOQkVXL0ZJeXZJa3dDc2dUbmZRS0hCdXJteld0WjJSVDBabGdCK2lja0NSNUpwQmM5TG1TcXVEOS9iVm9kTTBEMWRjeXZYUDhtUlV0Z1ppL2pKcHhoWWVraDI1RTVDaThOUU93RHNxYm9XaE9BYzN2SXRIWjgzcE93MTI0cExrVVhOTGo0UjhNMEZncFdkazY3UkI1ZjdVdjFRZGtFVHNKRmIzSFF1cTZBcmdzeTBheTNKeWl5SmlXNGFBbVdBUTFBNStNMnlDQktJa0draitkWkVZTkhuSEFSb2NjRG1HUlUvc0dvYzRuZW1VOFh2VzNGdlpKVStDU3NhN0I4MTZRc2swOTRoSkswaEFSL0FWOGd6MjZ3WFRoMmFhVi9OTXdsdUt5T1lWWTVhOVlRV0Q1SFBIMFhLMTNHNmZSYzJXby90RngxNXV2NDlPMWQzUGxkS1V5WkZ4VnN2dGRzbGdPT0FkdVNVV21DVU0xNzVLM0Frd2tKSktvRnM2RUk1YWZtTmwvZVpzdFBkY2NDSlBTb1EyYkM1QXl1UXAxdTFxczNxM2luRldESHhpQ0I0NEF4UHV5L0lEWXFVdDVGYkFtYVlEQi9kQUdIb0Z5UzhiZmpiSVo5ZUlkeS9wMlhqUytwTzVVYWlxSkM0VkpEU1ZTOGdUSWpVR0VTVkJtRkdtTlFaekkwbUFKTnBrS0xhWms5b1Jac25kS0huNUxDL3hiZEZodDBtNTdZd1RhNnRBZVBFZkNaQkFHakVESUdFWk1oWmdva1RJVXUwelNob0lNQkJxNk1jOVpJRFRGYUU0b2hJVUt1MVZBd1NCbjAvZGlSa0dZOU84T3hmcyt3clBkTUxaeFBEZU0xS280blJ2M0s0bkVTcnlZajVRU2MzcjBuTlJXNnBUT2ZTZGVSTXBNRm1nSTl2K1ZBK1JlTTM1L3hQZHVyVXp4ZWJKZ0xkWnJwbkFSdTRoZW9ZU0hhWTEzRDh2MTFJQmN6clZlaWtuQTZvOVpiUUJEY0JSK1huN05nWnIzaTNHRmphd25ERXVOeDk2SnVzeFpBR1lwYmZWOStORjVhbFdFZ2RXVTljcXBObFZveERsUUNveVhPS2UrWlBZOC9TYlBkdUo5RXQyOHAwR0M5U3BWZ3dnMjBhWVl5bmtTbW9qRGk1eUhNUzBOaXY4UlFJZDQ3S3pJTmRUc3QyWnZ3UUZSdDJ1ODVRK1VVZ3JjcWFaMVpCOTBJVDZPUjRMOWloaDRtMklMcW9pUVVDVEg5RmRGMWtxZWxDRnNUeW5MVlRHRklXcmZYVW5aK3FCSWtUV2xHY3BqWXY2VWFFUUYxWC9BcXR1ck1POGdybXdsbTk3aU15bEtQQzNCNHRYb000b2h6Uk9ETDR2Q2tBaDRaUk03UjNacFp2cDdwb3pDMXdPV2R2b25xVWx1SGVMalVHblE2emF0bFpCTjZacnZ5SzhFNjVaeDdRN0JKaSt0MkhyT3VzdDR2QUdYTGNEeDJNOGhBSy95dzF0UUpvM3M4YjltM1JGVXJCSC9zRjdNQUpCT0lZaTdiMlo3ekxxLzJTVTl4MnlMbEVMQTcxZDc2dGRpZTJYaFhMMFdvTVVBcWFqdTl3ZlArbWJuWDlyNm9nSWNkRm5uZEE4SHFEUS8zQnVXSnFkVzBCQmVvakVmQ0orTThUQmZBcWtKeEFjQzlIajJNd0RzVUJQb1lDVDhiM3NjQ2VCOEw0bjJzRXU5aklieUxoV2tIVlVOQllJU1JxTjdoUFN5Szk3QWF2SWZWNGoyc0R1OW45YlNEaHFFZ3NKR1JhTnJoM2F3WjcyWXRlRGRyeGJ2bnRDbG1pbTB0MnArSi95cmRsQTRzZzA0RE9HR0hCSFRGOUJPZzI2ZUhIcDllOXU2UmdMNVl5U1AwKzBwZ3dGY0NnNzRTaUEweXdKRFBBQXQ4Qmxqb016UWJWbHlrS2xkUW1vbWFVN2Era2M1OWJMU3NGTWdCUU5EL3djWTQ5RXlEYjdlYkd3R2pmelArb3p0SkE1eitId1h6RlFLK2kvRXBKMUw0bkFvR0hWb0tvUHI4TEZQSVA4RGhMb3pyaUdOdEpCUGRsT1dzODFobGZDZVhOMHNkWldPcjZpU2hUY3doL2w0T2wxeVBqeTNwMjN4cDB3V2NTVWZwU0pnTzI2T0YyaXBWbUZwbXd6bVBtbk0zcFhNQnNwS0lpTEkybXN6eXNpMU9Tb2lSTGNRbGpTWkpBZ0pFYWFXSWdyN2I4UTZRWUlQaGNlYmNUTHFmT1Nld1BtQThTU1F5ZjJ1Qk11L3ZMR2dBcjF3RFRIc3YzMFZFNTdDU3RjV05wbVU5cExMM1JIQlVHOHBPNkRCNVMvUGdkMU5paGJMWGtOWUMzSlJtdWlCRXNNM1lSR0wvWVFZR2dZOUljSVhIK0xWNjErT0RCSWhpUnFxWXA5QVJoNEpDSzlBUjZVRkk3L2N4VDhsSjk5ZlBpWDdDM0pmaWdTb1Vhdys1MkhjczZDRTlhN1VOZkl3WmQvTThJdTZWc2dBL3luNkFQb3A2cGJ2cEpEeUZTeUxXY3JYSmlCSW5zV2duK3NHMDkyTEdsMStGKzBCbzhSbXcyZzc1cGRPR0ZDV1ZMa3JsNFR4akR3S2kzVUZ0VXdjeGw2WHVab1ZYSTg4QkFTeEhKV1BRZzlzbHNBeFBPR2Y2VWZwS2w0VnlhWXp6UDk4NWF1OUtNRC9EWTR6MGcveEp3U0ZkVXN6U1JwcW15S3I2N1ZhUUk3STJrU3ExdUdaQ20wRDhaVWp0K1FBMXBZSVhQS3FhWWJVbXJmWGtDck8zZzJHTVlCYzdKaENacEdBOHdQTHpzK2ZNSGg2Y2srWUQ1OWVLVlQzeHJua0xyaEdWaVVROVA1UVdVWHl2RlAzV21yWGdiZ3ppcFJoKzBTZVdWbkxtbXB4VmZaNjlhQzNWUU5ZaTZNaTVMajJRQ2k0Y0hPZEc5VU5RRDdNQjZTK3VPa0ZhRnN0NEZGQTJ6cXphdG5lOXovMmFFUXprbXYza21RVnAwUmtIdGRSN0E4Q3V0MVJCTTNMWVlEeTcwSGw5VStPM2ZiUE9LSkdYVXRoMHovRnNRenlJbUpsMlBnVWErZVhRRXJjMXYxSTd5ZzkvQjRQVCtVNVVvYVE0MVVNZTdMU1VBemd6Z3Z6bTgwMi9MbUk2NWJtVjhiYktKUW01MTVXeUtzSmZjSU1kQllCREFLZE9TU2ppT0RIdnVVdVJqZFpGL0Irc0NUY3l5L3dwYzdBajJMZWlSVXpDcGVqSTQ5b1FZbytJUnJVS2pWVkZ3NTF2ZFFYU2ZkbUI3VHNvS0MwOXVNMVVIMFhmdnk5WU9rbnFUS2EzSk1YTWR6ZFZmd3FwSU8rQmUzcXlTb205WVBNT1Q4aUtBOEhKSFZJSnFQYVR3STlaNnhGcnQ3QlQydExNMEZDYVN2L08xajFFa0FCWWUyYVZJVjVGVEZFQ21NajVEOEFManZsRXNwakpTT21nTGlCWWNYeXV1a1R2VUNmS1JHNGI0VmFqenlMUzU5WUZPaUM1OGFkOG5BanF2amZCZTVlRElCb2U1M3Qzb2h3bjE0S0VxT1BXZzZtZ3k5ekpxNHRQL0tYU1lhK1VrYmdYV2RWVjlSSVV5eVRNZDlVNHplUDhPZDd3NEcrVFJ5QTFBTEdZSlUyT0JMMGRxNDhMOGVhSzJOR2UxNE1DU2sxUVA1SGZNbDVpb2xud0tBdk1xMEdFV0dGVEt2N0N4ZVZsY2tTZVlNc1dnTE5Xcm5ETlVPVHJiVUlqQjZCMm9DRjhhSHlKZkkxaTc1UlN3RGRDZ0RCQ2x0Sy9VQWZxZ2NOOGFhM2U0Rm5OMWNJb25wVXgwV1N1c3pIVFBzUTN4cGpmdzFYQ2x5NzFjeDJ6NFNlOTUxUlNIQ1lVZ2hRT0Q1VVdEM1BYcWlrOGVCQ1R2c1l5THB3Q2JEa1dNN29aNzg4WkYzVVBNdzMwMTczcTVWWEx1WWpZeEFocitJcC9QdG5QTkNOUlpkZTdtNHlsV3dJZHdDSFZlRlRBSUNxejhWMTFvcFpkYnozcUFXcTZEOTFKQmNXOG9nUkJPKzNIdjZMSDZGOG15bk9IZERjV1htUmxMMGxkcmtpUHllZjE2TExBMmwrNER5SkNVVnJSaHpZM2trbDd1N3QzUzY4aUh0eCtNRDJLTCtnenkvbHVpb1dabWNMM2lTZmlyZ2VzWnRxbmg3cXlwa2R2SWxmcjRtWk43U2xveUY4WC9sTmtQZjZ1OThTN3hCNk12OUE5V0ZTOVlpSktONVpyN0JhWlR5ZkxBWFg0VEZTcXRWYTlOR2ZBWk8yR2tNaW0xYmJac0NsVkxzVHp5VFprYlRMWFBCcE5CVzB6UElyVm05cFhCdm8zQU9MNTBsMGhmK2VvYklraWUrNVJEZ3B2OU9VdHBXUTRyeUhML3ltU1diZUpXV0p2SndSZ0t4RkpLL2NPVmtDYVUrUUJ4UUN4WlJ6bXJjV0ZQczk2c0dtV2hMcEpvSWJmMm02YVJ1ZUNZa1FINlVraUVJMkZpWE1XMTVSZ2dLdWptZWZGSmw2ODZCRlIyNmF3UWZBT3lzN3VET0ZZeXZxRjNyWXVrd2J5RkhJS2g1Zlk5Q3J4T0VkNFluYlR3Wi9DZldJdTloNEFaTVZzK1YrdGZYa2R2SWJWMkNIUHozYWRSWVc3RXlkYjYwSEIxNTVZeS9sZEtXMUIyeWh6TXhXa0Z4TmFzMGkxaEVGYmpFMktmVzJTdE1HY3JjN2FBM3k3TUtxdVBMblFaakJwZXFoT1RKUnAzeldWcGRYcXN5WkRWdWtMSnVWaXB0c0hWWHBaME5VbjBERFladTlOcldka2lSeG5KZzMyODNCU3RodFh0T251b0t0ZnVIOHZSNlFmVnlkeldTdGo5M0J6aGtzVlR4YnZRR3ZnZFNPd3FXcXY5dVBadlR5RjQrRXBSV0drc3VpZlZsc3IyZXFXd3hRT1VWZFd3Vkp4ZE5EUEZ0Z0c4aDdSVHNXOW9ES2l2YldXTENJVjNST2pWN1JGa0x2Ylk5Y0dLc0d1ZzVlZ01IWEJNb2lLajZLeUNwTm90UkZHM21OTXplYVpiRlAwSjJWMXhLV20wWHkwMnl1cnE5Y1RkSEJQZWkrb01EUGh4R1FxSzhRUXV1am1DeXRmS3RFc25LOHkxS1pVS1ZidFJlTEhDQjBMeENtMmFtUHlWUFdQVnF4TTlWU2xqbHc3NFZueTRDUFRsdDZJL1ptdEdOSkxoNEl1SDEyeXdxWFBLRFJTeW5HdlprVzJBclhIUWlmSVV0VGhPL3h6eFNCLzZuLzA5VkFjMWxYa1M4djAzUTZiMVRKMWVUQ2k5SnJPOGJSYXFOcUlERDFBdjZrcnFJRnNKbmtiYmx6K1pvWFhKNENRSHZZYW9wbjFybnFJd2tFdVZXQU5zQXlVYmVDU0pmUXh4V0c3TVFranc1NHZpcWxxNW1ES3h5RnVaTGRZZHdmMlFlVlMxbzBxTjF6NlNzVzVQSUt4S2hhMUpjT3dCV2g5Njl6TXgzSWs4N2d1aWJJK0tGTlVDdTFxUFVQbG9XUEIwOWt0b212UksrMEJpSllvWG92azZtSmZsb3Rmam85dmhkSUoyZHFGR3pwamk1UVZ3a050Um55WktERGtEQkQrSkNyMnRGbTVDNDVDT3dCMndhR20rY0JMMG1lcml2WlZkMkp3empoUHZEc3EyZ2lDSTF4am5NWWZzc2VhUGxHZXhHZWJoTVRxSmF2bXJmWHF0cEJzUmxBZXpuek1KY0VCSG16dFdtNllKSHJKdW1JSFJFNUpiS0lSdFFBMkVMY2k5R1lpMmlwTytOT2R2RHprc0tmUjVqN1BTYmQyQml1dDVwQUN3YXJVQzNYcG9iaXNlTlJobmljQ1hXNFJxQXFqQ0xSVnloRk03a3k4Qzd6dWdiZmkwNnJOQ1BvVzNjcnMyR0tEQm9BNHFZdGhHcUx4RTNRemJMbDZ5S05MQ2x3OGpEU1BCc0NRYUxGbXo4VnFRaVNpU2hXaVplamdvR0puaVNjb1pZbW5TZGVwcUNFeFdiVTEwL3hiMWlNclVnVnNWRE85cUVWNFQxN003TTVMTGwyTWVFY3hPMzQwZWpnSW9JTjV5bzduNHFUdG9vQ2VOb1BwRzZueGEyS2pKL21yQkp6R1dwMis3NjBVYlhmbkY5S2hlMWlyTjFQMFcwNW16SkhPelhCaWY4TkcxVUU2eDNMb3BnSllXYTBtb1lLSGRQYmVrdGd4WEJMOHBQcjBnQzE3MXM0NC9TY2hWa0xwSWk1YVUwajZIeGhkZ2NtODcreGE2R3BNRkwvdDZYL2J2YmMyc3VibnVCellUTnBLMzMxVGJ0dm5heThmMlBEdW1odWJEZ0VWbzFCTUpQaXM0Q250YzVISlY5ZHBtTUhpa1EvczdJdWNxalFjeEZUS1N2Nm1ReEMzd0VpQlRyelg1dktxMnU4TDhCWGc5WFEyeDRKenU0VFV1YnhuMGdENDMyM052YTE0TlFpRjBhbm5wNTVmOG50N0J6M3U2Ny9pOWoyWEhTZk52cmpzOFZ3Zy9QcTR0Y0Z0dTE5d3FId1pKd0t1dDlqV3Y1T3lmbkszQjhWaFlzaXROUWZtMFdVMWg3VEYyWDk4MDhJSDhGdm9WaHA1Z1UzeS9BMDVRdktvZ3Q4OE9OMld5MThrc0FSTXJ5eG9rOFdKOXI5ODlhMm9yU0xsYVlrbzFlci9vd0trU2Exbko0dzNxK0FNMXgwOHJkQlU2dlcyNmVWSGNyOWFuQ3RJUVJydHJSYWliSnYzZGlPVzl0QnZCWXE4RVFtblRUWWQ5NGlNbWZodkZ2WFdXYnZqOWJKSXpXUjVzTDYveG53Wm9rc3FNM2x1NWhhWC9IcU9iNGtBMTVwMWpGYWxOZGVkd2pMaUhRZ0Z3L25pMDFXK3VzclM2QWorNVZYelR5Zm5rek04TWxzTkVZVFZxQm9sNkNSa2hnTGlISUJYd0FKOWQwRDEvZTNhRXpCWjZpTWhzZ04zOCt6MUVBV3VQa09hb1lEUXVkSmd3UG5BcmhnTC9BVDlWQ1htNy90cXp0djBRbkVPQlpuTGQ3NmJhczlOWFpNbTN2K0hLZCs2SDZad2R3bXdTVDNwaEJqNzd0RnkvQUJNdmZxaklDbTIyMU1TbmVTWDFmZmg1SndpYU5oMTR4ZU95V28zczd1NURtM29QTWhkdGUvRHJJSm80MFIrSVg1VEtFMW4zcDJKaEY5eXR0MEZRUkFpaUJYcVQ5UytNYzU4NGVleXpJbkRQRTRUdTlKeFk2N1l3YlNpbU41SWw4UTVkY2NLVFJ3cmpQQm4xWkpKbEV5R1FuakZzaTBXOGZlUy9MdWFFcVVEeVF5Wk91TmYwVXdYRGUwQjFjWmhuRTFHeDgrUzRZZmdiSm1JU204cjd0UDFoaDB0Z1RwNTFjbUxUblhpSjVjcTZBaFgyNk44YmNlM1ZuZEZYOVdZeFh5MW45NUE0ckd1ZkJwZGFtNzR3eGswWjY2RzFoVUtaTE1ydGQ3VGViMndEODFBR3ltb2hMUXRoUUxRdlBtaWtVemhvN3FmTk1JbVRtRzVmQWltWTlseHRyUHRBVnV6NXI4V2tiZzlaL3ZEaGhNZzFDM21DZGp1WldNZTRXa1dZMThIRTVteWUyZHRlR0tPS0RITHVPTDI1eXpXK2s3VGlxZXVXTUFZVFNCbGJIdnV3ZmZwelUxcy92MlhuWEcrVHZvL3hVRXRKelZBTGN3ZG9mSytUTXhOYTRYbzZhaXJsWFU5bm1idzlCVC84SXkyL0NDZnhlQXBTbmorcXdyVG1BSUQ5ejNGaDFaZ0lKSStuMmJnVERISnFENCtENkFtclB2cXNqTjU5YlpVZzZZM3Jhemh2bFJSWnZEbFA1YWVBS1ZCZUV5aUh0Nld3VUkvK2lac1cxUlR3VUkzbE9sYzEvcEQ1RVpjYkg3UWxIajUzbDlMVHBHQnpMbnpaSFhtdmpLanMxOXA3aUsxeFZ1NDNWVmNaZ2dtL2JoM2hTTDhDeWhxS0twczNWRDlJWVpqSURKSlJ1RmxJKzNaVFR6NnR1QnNaUDlYdHhuSnlZekRucGEzQUpjaHc1WVpRRElGL1pNQ0UyMUFodFcrMk5kVDB5NTZRVVpVWitTb0xhdU9aYWZGalFxeDB6RDlMUUNSZGVSNWZzMlFqV1FzcmZ5a2ErSjJkNjJKUGt0ejZGbUM3czFWd3h1Q3hvemJmK0FUVkhtTyttL3AyRkpJQTVGYktHQmVmYzJ0cDJaVzFRaE0yN0Vwd3FJUEpyM2Z4K0VOdkpwK2YySHBmNEE0U2tib2FUdHNBalV2dHV1QnQ2OW1PVVBjY25EUDV2SmJWTlVaT1FVT1N1dm5nWEY2VFlyRnBnMGJUVXdLTW42SWdoM2NYMVovV00zcEhkS0ZlTWxKc3F0OSsxNWkrWFVIUExFSFBmT25XcHlNUDFGK2htSzNDTjRLK1ppTGpoVUhISTJzK0s1NHJUbS9FOGY1Skxnck1IQkZ5QUh6K2kwY0s0ZzNOUllsaHJKWkYwcHhGc1lPZFlyT1ZWTElmQko1ZjRIenRwQkpFVWRGY2Y4Y1IrbGl6ZHM2MkJOODVRcE1ObVJRTk1UUmQ0SDZ6N3FwOWpXbnA3ek1tTkJwd0xKUHZsKzNjQTZEQjMwZ3VvT29rZVJXcWxVNm5xV2VFMHJCUVJ6SHF6ZDhmWGtOYlFVK2dIVEVkNis1OTN5ZVRRaUtQdEdOYy9SQlc4dUdKU3VqUng1TWFYVm5NRmpHbWRjaGVuSm1tU3BOVGtOc0NVdU81Ukg0OHVYSEc3Y0xTbjJxKzFYVEdjNVVLLzRyd0Z6UUJKTDVoMTR2WUdDL2UrS0NvU3U2R1RRaXJtSW1QMXkxWkoxOUlHbDV5dkpMTURKSlJYYnZIaDZzZExVRTBtdFMzREdiUUVWc3plS0x0RExwZWhoTmwxYmc0anV4QWUraFo4ZXRvODNtcEtwTXI4YVJ0QXkrYTJhQUlPeUxIODg4ZmRxbExOL0N4MVFMZHNDSEN4eGxiVVVWNFZDMjFmVFNEcEJjbW9IQWlidDZmU3NXSlZ2U2RnMXkwOUs1b0w5cGZ2V2RqVENkVzhnQjhRTWt4TjdBNFRVQkl4Y2o2N1BGdHNheEhDam1UT2RibVF5cGRmVE5wbkNYSkZYR2tlMUxXYnhwVFYyZzZOb3hydGZLNDhjVTFrc2ZEQ1VydS9pdG55ODVUWktxM1M5bGlTK0lmTjhzN05IVWl4YkxwRGpvRmJaTE55cUlwc3VWM1BUUEUyVjIxNGdoR0F6bWZXNVg2dUJHNkpTMXFML0IzUVpBU0RSdVZnRjlmRkVENjlzenlHWVlLcTF5OS9jN21naFBRWG02a091eCtOL0tjWXcvRVVGU3I0TEhQNGUvWm1tbzZMV0hPNmVRbEIydWxJVHArV3NNK3Ixc0dpT09jcU54NzlNUWlFTVFUa1o3M3lJS0tpV1U1TjU0b1dneGlabW84N2QwaERuaGowTDdOQkJwaUFJWDJBNjlDUU1WeHRScytmL3dNTllhMVhWcy9uZkFoa0piWVl6elBaUHlGRFFEcDBSVnoxaHZkUHc3MW5GdVYxWVQrOUQ5Zy9GRkJYUi91YkhxRUk1THJRZlJxMTd2UDl2cGVVc0w4M2E4L0hFQmRPRUFuUlkveEZndVY5NWZvdWU4bGttVjV5ekpNbm8wLzB6dkxtZHp3WTNlUWZSOW0rb2wwTUdyU243SktYaXBFeGVRMEk1L0d1MUFqNTg0dURBa1hyZnpPd3ROSXhjZkRjTXlhbHJOLzIyRjJsRGppU0FJYmZ2eGxZNmIvUnRlTG45UHRHSEo0VFJxM3JBK2xMRzlRUzRYbCtjZmp1UkJFQUZMZlJuc0QxUEtrNUk3Zi9oN2VWcUs3T3RBcDcwbTlPTlg1bEsvZ04xR3pYM3BHVWJxNVBwVWQ4WEVENGo2R1NYaVkwakFSKy9zUStnMitkb3l4cXRlNWMxMGZ0TjFTem1IOEpWWGRWZ3R4OGY2QWliZHZ5UUhtSDlRT3JDeTRETmZZNzdyMkdZT0tvenVtakc2UVdQWlVPRGcrUFJlbURJbWgvWDJFdmRjbGxuVFZESmNWcWV2VXRKU3FTbEcvVXVQeUFwQkk0bUNoU0IwZWE3UkVJN3d4RDhIVk01OGM1Nm5xTlg3eXNzM2Y3SlYzbVc4M1V3MUtxSkZvWk5mR0NyRFlDK2p5dnJwcUtmTzVCbTVVLy8wbDhmNGh2b3VWaEpYaWpmanZBMzhlWUJFZnZjRVNRZXllTnZzZUhYdEJuZXY0b3dKeDdBUWNsVHQ4eWF5bHlWa1ZzQXdDcGJiOXQ3cjZyVmFYYktWTUlVS0c4RTg2TjdVUUVBYlhYcm93OTYzZDhsd0dWNndYTzcwQytTb0xWdFplUExjN09HUHMxY2U2VS9keTZlM0pQc2g2RHhXL3d4cjRNUHBWRkpDWmJGMTV5V1NwRERlMGp3MTBGa2RaUzdudWFDdzM0MlJhQjJWU3hlZFBSMG1rdC9zY3U4VkJ5Ym9UazFGWjNUY2Y2aEV0VWpEdnRCOWZuOUp1SEpFNEZwSGhiYmUvUHVwZFRnQ1QzOEEzNDd2c0ZFZ0JCWjhZbUJPMHNLdW5ZVldiUi9kbFkrQlloZ0tRYlErWG50cGJYQzh4eTcvYXdWYitmTHVUQVRTUlBlTXdTT1hyeU1VQ0NmTCtPZWRBb1ExZlJrZE8xWTgzano5NStaSUpMVW4yeFk0bStYb01USkVpVW4wMmZJbG4relJsZ3RiU0J3TW40YW9JbHdZditKSHBYVmIzNTFmS0RtS2JGSGJaMnVHd2xNOVI1bDg3U2Y5dEJzdEQvOEFveXZKY050ck9KZzNVV0pVMjVnanBuc3hJZmNwcXpmaW4ydXE3YXl3MEQ5K2lWNEQzc3FPV3g4dlRlV0tkR25GV3h3MlU4c2Jna1ZkaHJLZThySlUrcjJGY3FoeHNtTXJrSS82M0l5VkpPSDhWNDdKSW5zTkVWaDRwcFNkY0xPZEIxZDlxWDdoL2NlVEVvYmx3enMxUCtVRVZxOUl6SElVRGEyMStiSjg1RUZjc2E5U21HaDhUWjc4Z0xqOWpRL1h1MDJtWEJEOHk4bTgxZUhKdTlpSlF6RER5UGpNWjg1L3ZwekRkRVJTK0xrOEs1ZlFCbEtsT1JZcEtUZXphM3JpbzEycGNwL0oyZEt3bEJOUzhRcmMrWVIwOTZMRTRLRFdzVDMrNGZ4dEpGN01RZWJkZmxTU1hINHM4eUVRK21iSHI3SDVzU2RqY1dQeE5Ja2FwNkRyc3hKVVh2bkl5ZVdQTmpSMVdmSy81NDRXUmkzbC9GV0tRTU1iV2U1YWxGbGs0dGN0dTdZeG8zb1BUc1VCSlVURmtZU0l0ZWw5aWRsWVRpVm9PU1haUEFITDcvZC91RzFMN3lFR1RKQ0hyYWt2dkV6eXhvUG8xemRHZVFwKzlobWF5NXA2c3NoMlNVRDlmNW5LSE80U0M5a2pGcEFwRDFVMGxIaDQrZTZ5YUF1RkRGUkllSkg5bGJadTZXUmwxdW9yWDNJWlBoem44OXhYUjZSNDY0WHBsUG1VLzJMT0dwWDd0b1FpZzhGdGdZUzhDa0Fab3pMSmFQaUZTa3RDSEEyV0F0S0pDdmZrVXcvczVzQWdRTmhyc2ZRQlNEeVh3VjdReGUrUDJmNXJkWFNDN0ZIVXJBanNSWmlLdEdjV3lhdkZmb2hySVhWSDhqQVFoWW5scVMrcnZ5OW1xT1kvN21ScytjcThkMjRVbnlWbHEvRHdHYUkyVnVQb1NwMUQ0N1RPU2NzWDk2WUd0bk1IeFhXSGo4UXhpZXVYdkdESVJPdmczYlVlMEZqa1ZxMTlibFJ0Q1YzZmQzUitUVk5kMUkyaDNPamRpNHRaUGp5SlF5NVAzbk9qS0treCsrMnpuMTcxT1l3Z3pxbzUwM1JtMUdjS1B6THVwWDhtMEFYYWxiOUJPZXQrYlV3NjdaZTFyVW0wOVpxYVgyVGUraFlLTkdPbGxpVEIxcVRNMjNibXNnYjJMeHgyeDhVY3NjV01aVm5QcGtPSlpCRGNMbEUvSHBKazVWVUkvSVh6QjFJYVVuL0xZUGdDNUljSEN3T3JqdkF4TnU5R1p2cGQzdWFpNEc2TkJNQ3VrU2dVOGdMZW9FbG5MUGF4WnVNSW1RRERCWmovaVptKzFPTUtvN05NenhEVHpHcGI0VFZhbGRLV3dhSkF0d29pZWlHdVgzaHEvWGxqY0VaRkhZUXhLcEo5OExtUlV5V1YvM3pPQk5FOXJ5VFBWUm5aZUQ1cDdHNlRwOVJPUjlVcTdNRVRYMXV6MU9adzNpK3B6Y3Y5blNvVDV3Y1BsTTVVZjdSS3lTbkFaUklVSVRObFVoak4yOVZnM3J1QzUxZGcrUnVaS0xZajQ4cDdiYzVhVjg5L2VSZ21MZC92emYrVTMvQW10WElMK1BBckRyUEJXUmp3TDE2UCtWWWphRkJQUWhmTW5UOXNhVlZhVCs2MTJJMk40WTgvWG50YUVvS1FqeUxHL1lJQnB1azdKRDFPcXRtWDFwd2xPSEI5NHF0N2pPUnMvc21ac25xYi9TcEdZVktRVVVHR1YycFJqNGxkM3hYWGR2dnF0MFNheWwydE9FWmxrSkZuMVE3MWtLSTl5NWRqcVp6bzVBTHZ2bVZ2RDU0VUZ0Um4yaDdKczFyVGIxS2dFSVFZUWRtVUpzc2F6blk5NC90eDM0ZkR0UU1oN0VlTVQwTEhzY3NuZlpjYzNOekFuMlJvOGJZYlh6UzBYQWNCeVkra3cyQnB1eTl2ZVBMRW5iUklKck93aTRQejZzTkRhUk9NSERTRU5ERmNlbEFnT25QYVgxOHZYeWNUMzF6VzNra2t2WnF6WE5MWVpoa29rWmxjeHpaSEJ1TWJZMHVadkh1SmVJaE4wcUhtdVdkVmJydW5kU0dZUmxiRGJBYUU0SmhDY2ZDQ3FSbTEwd0pWQ085TFM2SzNmeXJQaHlJZ3hLTTc2cDd3Mi9WZkVWamdGYTNpRklhdmxYa3FQTW9qNy91RUR2eG5vZWhtdHA0dlQ2TngyVG0wSVFwN2VxUUk1NXNUZ3kwWEN5c21FM0VPRlNaSk1abE02MysrcUtWTU9hRE82elNzVmhQbkVZWXlseFVyZkh4QTYyRVFNQitDOENkQTZrcWNyVHpFM0JYVVBoamJzMStRNDg3bUNqaTFzZGE3VTZhMzJhM3JnRjltNDJJN1Q0ejNIMHJiWEhpSEwwN2lGRFozTm00OCtSdnA4NTkrMEJnelVHUXpCZG45Y2syOGZjcXdhSlhXZVh6Q0hZOHBUVUc1RDdpSWVGL1R1TDE1WEVvelRLYmFCVGtNb282K2FOTk5XQTNkb2tCYjRZSTNNK0pmMUJhNExRM2IzNVNVMUdLLytCaEhldkk5NXpORmRWTE9sdWZVRmszNU5UNVZDSUpQUXVnMzNrdytDNkdaaXV6T2J5cksvc3FyejN4YmZwbjB2bHRsN1hDM1VqQTJrck5LRmw5eWxJNmZYVFZlNHRpNmRPSVlUL2xUSDhiZGZtZTRlVVg2THhnc3Y1SEE0WExBUG9BTFFDQXZsWE1BcUlhU3pvMHJhdDY3YStYa1prZXlhL0Q4RkpBVG52L0R5RGxEanpWclZpQjgrcDJqem51Qk5GdlZpeUNOYStsdXpsbk12VHdYR1RTQ0NWdFh6WEtzdUF4QlJKSzVNN0duWGwwK0pzQUVMekt3RlBGd1BiL0NONTlsTFlMSkNqMXpaY0ZMOWFkVlRJQ2VocENQdExVOHp1R2E1KzdPMHl3enRrTW15dFhRQnZHRElJaS8vYUloRlhVWFhQRFRMYm5QdndCTlRodktQdVRPT3dGZ3pMK09QQW5kZW9TRWdBQ3Jzd252a3VaNkhrS2liRmx2bGpmeElHNDhwdzVYREFqUktQWU1ydVk5NW01QlpMSUFnTEF1aEJyV3JuVldoZnhlbTdYTXNGbWxBMCt6RXkvSjRrOE5pM0E2YXM2bTJqMllkcklWekdsN0xJZjUvSXoyMkE0UkJFWk4xNHk2WVFqc3hsUW5kdTJCQ1RJbXJiMzJPSUsyNkNsQzB0Qkx5Slp5cjJsb3ZSQlJ0MkFtNjBUcUFpZ2xyb3V4eHJWSjE0engyQTRSQlpnYTRtYVlad1VjbEdsL05UdWw5dS82TW5Cd3VwRWt3bnVuVWROTDYxbkdKaWRDL0ZzVXhCS1hSMHZXZHNlM2tOQWNQQURpbTE5L24vZTl1dFhYd0ZObGNyU25xN1dsc1Q0U3FuQ1pqWWFTNVh3b1NnVTc0NlA1NkxCTkwvbXZvNkFVZWorL0FhQ0RVR2dRVkRUSUlBRWxvUS8yWXpTWXpzNloxMzFlYytXNFY0ZXZEM1ZPeWpTQUJ0YkZxUWpCK1dDRmswdEdoaGZFZWpxYkdrSkJwOTJnbjQ3dlJQSVpNWHpmdXp0b1F2YjZ0T1haNVBiUnhLenorMkJMRHdGeVR3TGdZSUZ0RUdBVWJPWmpKSkZrcG9aS200b3JBM1NJYWxYYmhzZkZGMERPbHVoYlc1UlNBOXRVcGhnT0tyMHBGUWlLWG1UUHcwWWk5RnhhWkhqMjVaV01RZjRFQ2Y2NjFTTFBMaGtacm8zbVo0ZHhMeG4wUGN0T3YrUS9rOUFFTXRiOTNFQnc4MkJUNVRhMU9SV21ObTNySVRTbFFSa09MTG9qMmh6YlBnb1JJYTNHbE9paVZ2VU1vN0w1MkZrcHA5aWJUdUJCdkJmZmVWZzM0ZUs0MFduVnY3VzcxVVFLaUhlOWtBUjhVVjlQVjBkemZWWElhUy9SejZkcHQxUXdFdDZXdC9YYTlKS01KOFpJbjM0TXFSeE0wbmNzUXA0MDl0dTRtQVFtaG51aEtUTEZsU2djWlNXcGEvS0JGL1d5SXRBazJpdG9yRFE5NWt5bjRpbmtWTTQxWjRLRGhxOHk3V2RjMDM1Vjd6WUZZUmZZd3BBdEw0ODYrL3NUbG9MNHhQazhOVlVjTVJPaG9NMnFLeHlLV2lWbko4Y2R1V1BrMXJ0aHZUbkl6NGZqZlFrcURoV0ZoUUp4cDEwRVJFYnNNVnpaY3dtenRaVlNCaE5UUWQvRklVQ3BmY2ZsQjg0S1ZHMERpYnl6V3hqRVpIbU1scEw3QkpOQnZPNFpOaHdiNkdocnJQZDVMS1l5dzJMV2F2Q0NWK1ZWM1U0YVRPTW1yVklEZHREejBBcFlpTUhpZ0R2cFdBWXk3bVM4QXRveDhHNE1QQmdlNGg0VWRJcU1qbk4zLzhhRGlHYU1GaWwxQzVNQlkxMHBXVm5Uak1wR28wa1pVZVgxVkNQeDRKZW5GUDhvZkdsL2pIcTNRVVREWERkbndhUzFUUHFuTEVKTlhVcXV1TkpIc1E0NXBmdmltK3N4OWxsa0dhS0FqN1MxMUVkRHdRcVh4YWd2bWszU1hyWGkyTkh4Mlh4MnlLdThNNWhnSnBwZnpNMlU4Q0FBZFlpVXlrUGF0ZUxScHZpMFZpbE5rUWhCcjMrZ2daVjVqa0Q3Z2JFZksrK25Jdm9zMVhNOVZCNTl0Tmp0TENvTS9YS2VTWHBrL0tWZ3lhK09sMVZxT29tNFBZVWF2a0J4SW9ERjNOUnUwQkpUeTVBUGlDeE5sVkQ3dGVvYXdJN0duUlVjYm9palVNWE1UcllRMDlvTGtBS21jT2pnbEtIVS9zeVgwSit0cnYvV3VyVVRRZjYzcEl6aXVmUlBMUVFRTFBmUkVWNitIaklBK09ySDN3ZjR2TStmTDAyVkpiSklqZlcxcFFzL3grZEVRZHd0eUtkZmtySGY4QVRLNlRocTdHSnFLYXRoSmFjY1lEZG5VOGgyenJXUjNaRFZRc1d3M2tOQXQ1QlN0U2k2T0s2WDlXYmx6VmROMjU1eUNURUx0NXN1SXVXWWVxNjlIZFNXNjRtKzc3V2wwUlA5dmMxTmtiRGJXVncwbll5SGNkaHFWRW93a2lPOWwxYkhWWUZDcUo5QURjRGtKTDY3Y0lBbml2RFk1Nk15d0xMTkpndUF1UkpQVEJYRkYrMVVwYTc2cHN6MUlaVG4zU0NHQnRZbjNTNlM4Z0FBWHVlMWp4ZVBET016OVZaWVRmcml0T2ZwZHZ3bEdTY0xXZTZQTVJOTWlWQnZ1dVFBSkNicjBNNmNRSWErNnFZRmtOMGpINXlvRUN5S0NjcDAxWEh4WjFkOUhVVEt1MXNRQW9YbFVWYzhoemp2NTdkOUZjVzZYcEozVk1PbWJHcEQxT0xzUU1ZSEV1ekd3bEtyWVQwbkRVQ0NCSlV0aVJQVUFvMERFc0RGS0pCRE5DRVZLbGhiQW16Z2ZrcW40YzUzQ2F5NzArZHgybFU1Z1ZjdXVvb2J3TE4zVEpQNTlSRTZsdWdkRkk1QkJ1ZDRwdHR2MVVrVVNKd1MzWWpKOFlEVTgxUlRTMzJFS1JIMDRoQ2cxTDdqOGdObkJhNkdnN09ZMXlMQmtpMDVFYW91U3plUk5yL0lQUW9BaXo0ck81WHMwZDN4eVJBSk8zZllKcmRrZEdSQnJLZXJ0ZGxsTHkyWmpnT3ZVakpyekZPOGxLU1BKSXZJYXdwUVAxNHJhYUVrbW5veGZzcVJHZFpiS0lTYURzMjVQQ3BRenkwR0VFK3VhZUNEYnZERDJlQm9EQ0t1SXhWUFpjV3RFc1dlOWRLWGlUYUxXUkhWOXZkWnRHWTFNekc4b0tjckdnbjYzUTVUcVc2VlJJMmFaK0g0aHlQd2dtd2YzZkx3RU9Wckx1MUlqVWFjdzZtWEZWQnVXSnVobDlxQ1VpMW1Ob21pTk5HQkRDbmlLZ0FWeEQzWVlMTlVEN25Yb0oyMmxNczBpeUt5QUFidWxjanVCWlYybDBnNk0vMzdYczdiT2ZScDdKaVNLTEN4QWFkTzBVdGozNUZQdlNSajd4eUJnakloVXZVQThkTUFWQk5MSW9TQ2hINWFZM0lNMUM1bUFrWDBxbStPNlBPbVZXeWVyVXo3aEVzTlNCMmJaMUhwSWpqNVVvU3d6U0pGWitWK1MrdWI5dmZnOHRrWmFYcjAwVVQyQnJGeEVvMzR2U1hGODJrdjZyUUtXZUs0Ri9OaTU5NVpwNUh3emlTNWhTdzRZZG9DQUlvU2ViY3BTbHBKbVJEUzUyYk1hMks4b0YzZHZYcm9BNE9EVXZ1TVN3K1lGYURhaGhHNVUxczQxSFY1bkJIWFhQTWd0NXhWVjFuTnhVV0xXZHFyVi9PTzdqaWFvOTJUZC9sQTRyMDdCamc0ejF1SVFXQWF1QVR4TUNDZ2VPZlBUd2VIa0dpbmp5d0pzM2ZaM0JSVlpIYjdxYS9icWRzSnppREM1aGlobnBNSklNS2lMTXd5VUpVeFpZQnR3NWpPTlVtRTJKYWZ4YjZ1c09xOWRnbldqc3RWWWJOUmtkM2I1SlNMd1J4UGJ3MWt1SGlFUUR6bmhCSkpHa1NIUjN3NmZvekg1STBCVTFqL1BWS2ltZUI5SHozL3lpMFpYVFRZMzlib3JkQnAzZmJTMCsyRVM3S2Qvb3lNdXcvMmNaWFhUWEhBYy9OUTlnTXpVTWxoYnR3cjNWZ0dIbkx3UEtnRGlGQUp5cTB0QmRmUWVoOVRMSmNaWGZlOVZZM0kvek83eUF4S3FQUkoyQXRURmFGdjZDSTNOakkwMk52ZDBsanBkMXJMREt2WnVOOXVWa3BHd3BmeXBjRm9LdmhVSXM5allTMXVIZXF4RXFQYmhtZ2VkakRudVN1NUVEaWp6b1VJY2ZXVXdtdkFwakN0TDJVUWN3bkdZRU8vWG9SZWhDQi9YVWZxUlZVSW9FOTVRZnlnRDk3djdveEdmQjY3eldRc0tSYjlkdFBNZUh2ZVhnczhWUDZ1U1ZUYWJ1OGNORTdMMUNEenkyQ2xoeWpTencrWUpyVlUxVTYrVXZwbW5TU1oxRVMzU0JnM3AvRTNZeldqVVk1czNIWGpCVkQ3dm9BakQrdU12amlOZWJuZnZJeFJ4Nk4rbHQ0UDFVU0YwSXJ2eFJaV2VPOUVrdlVndTBRL2ZlVjczL24yLy8veTFMSFk4WVd4bnE2V3BwcHF2ODloTTVVV1ROMjJYZkNGZk1HSEhBQ0JQNC9vcHVVS1ZRUDcxbGVpSVg4R0FEK1AvWGY4VzlELzI5ZjN5c2NBeFBRTFR3cldncDA2Ukp0dUZNRDVWb0FZNjgvMGorbjNtdzlEL255U2M1QlMzKzdNOGxHblZNTzk1V29vNjZ0Y1VEWnI1SWZFOVNMb20wNURiWDB2K084eTBodVJWTjlZOHZKYmpmVVZlL1dBNitzVGplV1QwOHZmMU5ZWnRHWGx4bm9lbFBYenRwZnYwZFhyRUpTbmFqVCtURGNCWkdyR3hZcU56UHg2NTRlTVJrSkpNMGxUZnVUSFlTTGhKTWNuTWtiNDVrVEJnMzR3VVhGaDZzU0FjM1Bqa3ZiV3hKWjNaTlBqSGVIYStoMW1MeHNjczk5TzIrMXcwRXlHRmhkeE01ZjZzblZ5amx5RDJSSVdXeDFnNXlzUHdaREdyYnhuL05LcWRPeDM3SEkwNFVkcjVHTVJkOG5raG1BMG1odTI0THk5amxuaytKVDluS1ZNRXNrSHo1K29UNzNkSVh0c21QZDdlVjBlL0pmdjc4bXRrcVJTNyswckpoRzBqTXR1RmcwcmY2WWk5NE9odk95VnA1Qk9rWHdGdExTN1JRbnZxbExja0NpN3hNaXZlWmZyczZITkhYWnhmclFXQnVGUGNydE1SM1FuTmRyRHVOVEdkVmZwWElIMkVUdDFrelA0NUFIUStHR21iYUVLbDAwNTQ3RmhiMi9kUkFIRmM1ZnZxQzE5WURvUFpyaU5IeUhjSDUweDhWNGxlL0NpQjJUeVlTVzNhQ0ZuZXNtdEFzeUhyR25MYTJEdktueHB0eEM0dEh1bld4cktQMkh0ZXBZOWI2YkpZU1YzaHMzb3pmWXNPUU5HTmttMTcvTDRhZnFnbTlHY0ZESjgwRngxTGJrQUNINEFTWVBZSHFoditWZnd1dC9hdEFSalBOLy9TeHRYbTM3bXo2aythSndyalZ5NkRKbXlaRlBJb2FRS0dqdFhYdEJqSVJZOTFXSjZKUXhoUDNTN1M2MitVZ091TC9DRFFHUVc2WjI3TS9oalNjN2RXYkNmeXpGdzhYOE43N2xTN0ljVnVLM2YvVTE2TlVKQ2lNUGh6dnd3N24wYjBwdFhJelBadW5jSVcxZ2N2NkxLTlMxYkh5c2o3Nk42OGtBWGJhWG00cStEL3cva0hXK2hWWXR0bVFVQSIpIGZvcm1hdCgid29mZjIiKTsKICBmb250LXdlaWdodDogbm9ybWFsOwogIGZvbnQtc3R5bGU6IG5vcm1hbDsKfQpAZm9udC1mYWNlIHsKICBmb250LWZhbWlseTogJ0thVGVYX1NpemUxJzsKICBzcmM6IHVybCgiZGF0YTpmb250L3dvZmYyO2Jhc2U2NCxkMDlHTWdBQkFBQUFBQlRRQUE0QUFBQUFMdEFBQUJSNkFBRUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQm1BQWdnUUlEQW1YRnhFSUNyRlFwa0FCTmdJa0E0RStDMklBQkNBRmlHSUhnbjRNZnh2bEp5TlN0TzZCNEM4UGVCazZXZkh6WURqcGFiZVpWdFcwNmJHT1pHY1JOMU9yL3I1OGhQZ2VoMTV1VUxEZ3dQSGlIM2NYTUNNa21mMEJ0L1h2YldNd0lnV2pob2pId2RUWjJNREF5Qy9xV1JlZ2VCWldYYWFYWGxSNFA3cjkwV2x0ODlBaWlLaWxkSU1wMk1UZnd0K3RibUU4SkdPL2QvZE4xMVh3U0xYSXVuY2FPWW9tMWltWjB0Ui9YQ3RucnlBTVVNekwvT0xzWkFwMGdGbHErWTZUWXJJazlNbnF1Q3IzTW1WSHdsUzZSbi8vQTl5UC96SG5xK1FVOVpMZmRoT1VBRzRqTkpUaE5HSnBZM3RIeVNvUzJGYnkvd0FQOThML1A5ZW52ZmNtV1hpTEtUQjRRbUdyYXN6TnkveC84dkkrUVpKU1prb3pLV1VXczBBekpVYTNDRE8vQUNBQkZUdjJGUnFFck55elF0WFphbGNqYXphY0Y2U3BHSDFpc04zdjIxb1RiZTRoSlR2QTRBNWgvMTMwMHdDQ09CazdpUUNJTFhrdmdDbnhnZkVES1VFQXQrdFY0UHVHYjM2djVxYjVZRm80Q25aem1tVC9MMm8rNGUwc1JBRTdiazRBMThPNnZVaWRDZ0dOektiZ2g5Q081Yi9DTUt5NWtpMG9saGJMaXhQNWVIb2hzdWYxSitPMDhTVzl5MTVlTHFXOFdqYmxYOG1tZjllL0tsaFBXay8vTHdETElsa0x3RzZEVlRmc2xyOGtyZkpXK1BQYXVNdXd3M2JCNHRUeWZHdTFQcW9ubDhUODUva2JmNlNQVWU1dXA5bXNzcHRBNEw2aU03Z1JvZDROUFBEc0J5aHJucEd1ZmUyWUhrbExVNWdqMVRzUzV2aFRBaEpvbThoWWFVMW9JZ2pTYndGQkNOWVFPYksrdVNIQllRbyt1RDJwaE5ldEJhWENZRExZZmVPMGFKVWF3WnUybmFiREs5QStQc0hlNUpHTjgzRktpOVlnNVJqeFB1OEFENll3UjlvU2V5QlZ3ZkJaRzE4cW9VeklESUwzSXdIUkJvRVFMRytHOHpVa09ReEF5aHlub0VnNCtJNU5sblRwbzlMYUUzMkdGQ2RhTktaWjFHcGhrWXRDQ2dIQnQrVkpXclg5QWdRa2RzdE14NVo1a1psVXdvNEwyeXJ1ZWljK0ZEcjIzT3FyNUhJSW5ES3hpS05ZNkxaQURCSFFuampGQWdJWEk0bldlVlZ0MjNnVVdRM0ZIS0lkVHlaTWZsRFFDcUFDSGNHYmRvYitlRjRTcmhYTFFCallTZzg2TU5ObW9Va0tGcUpON0ppVU9ZZ25KcE1mVThJNklKTFVrOGxHMGtFVEwwZWJKZlo5blJOWlArMk1ueWFvTmdXNVRPeE5hbVZTMHVPMEV1MHJiYWpnTWtGNEdJaDlReVZIOUFHVUMwOG5UaWN4V1UrRkZMMm9HTmxReFlIcW9jbmdCd1Z0eWVTZjMwQUlTRDVQNlFTWnI1cDd0dTh5RmVqN0sxUzB5N0VCbXFOMzNMTzlaYnZTbTZIYm9wYkxvQklPUEJXTVIvUmJxbXcrc1lRdzJ5eGZvU0pETkFoSW1rOC9EK0xJcE13MmJZSC8wS3BsdkZIV3BPeUFjRnAzay9hVUN5TXF5REVyNU95VVpIS1JHVUEzN1JNRlFzRWpJcTQvQWpxdXVJSHc3RHVxVEJZRHlreVdVaFBCWnhHSS9xOXFkUWVVNExMSmw3VzBqYU5kVlhyREVDNkRic3RRejJVVXdVQUVoQklKWVVSQk9Ja2dnbWlJSkRGRWtRU2lpVW5QNWRBUUYxVERDb09HRFlZZTB6RmNNZE95WWNWWWpFRldqc0ZJQk1RUkNTYWlJSjVFWUNZYUVrZ01jMGdDRm1JOGptQVZmSkJZSHFqMm9TZDkzY3NMNXc1eWJmZWpTZUtZYUdPaTFUQ1pBMFlvU0VIaW9selBUcmxsb0xRL1d2azh6Um9OVExabUVlb0g3clpQdERXRm9PcG1SNlZ5ZUlCYnBjVXZKanplSXFJdGl1cjZBaGllZ2Z2eHVOZGxNYWVpdmpKTTU3Q0tFZTZBSVBBUHlEQ0RheTZzR21ZZXc4Z0JaeXlieFdVQ0RITTBTMFF3TlhqeFNkb3lraWFKY0dOMWZhekhaTnRNUlAzWnl0RE9FUXozbFBsd1RhZFBSdWpPVXBJSldFMTIzMjZ5dTlCQUZVaVpySFVFK2k3N3JHUEhmU2JZRTZ5Q0pPRkVoTGUzd3VNbEFwTEpLWkJoNExNVXppay8zSTV5Vi8wWkVFS1lDa2toVHZlQUVDSkhwQkIyRlYzcmlnS1FTcEJoWEZSQ2lUQWtZZjY2d1M0MllPckxTRWxBQmN4VFpJNXRSZU9JVk1LSmFYTTBEalJ0Myt5bUN1bjVHM0pDTUl4cXR5aUJpa0JXRjNVamR3ZVVtQ3lBb2ZRcWkrYm42ZDFFR29pZEY5UzVCcXVpRlFubEZVRFIwTGZUWnZGTVR4N0N1WXhWYmVTR0F2bW1lbTdPUjlJaTVNZ1A5K21LcVRibU5DbmdCQTE1d1hEWEdXbmY1Q1FNNmVyOUMzZTNmUllPcCtxMmQrK3NhYmYxbm5iVnpMWWwxcEJWYzNISlBpa2pKVFJNR21rVkdzMUtrRlVXQ0ZiRmJsbnNOM2JYV1JJOHIyMU5MaGVqSnBScHlOdk1IVmRsVWVteWQydVAxZmNNU2lNTlZOU3RaY2FqRlZMSDRpYjlXZHJSd3VYb25iRUhwRnYzU2tteHRNNkEzWFptbEh4Wnphd3ZsTThOREtlQ0JzTms4dFBWVVZIWEpidWNYbGxsM1FLNEY2d2loR3FCZ0RjUlFvME40RUV2SWRRS0JLd2poSG9id0luekNLRkJJR0FqSVRUWkFBSTJFMEtMUU1ENWhMREFCaWpDaFlTd1NDQ2dqeEQ4Tm9BTFd3bWhUU0JnZ1BDNDlwbzV3KzB2UHRqT2d0WG93RUxvN0FERENCM1ExWnJyQk4xbUx2U1l1VG80UmdmMHR1YnRvTS9NZzM0ekR3Yk1QQmpzbnc5RFpqNE1tL2t3WXVhUE1WcC9aaWppS2NxejU1aVNkcEx4MjhlUGxzQVUyR3pwZjFVQXpRdm9tMDBoRU84RnpBV3JYYWdCOGdVQlo0SGpxVFZBVkI1cGdpbVM5aHh0RWtRN0pBQXBDaW5LV3paYjQrMDNSUjhXczR6MTdkdWo1OWdPUFhiamhSMHRhdFd5VTg4aHc1czRyamVuUzF2b0FxUjUweTUxbFpaMXcvdTJoOGJRS0lRU2lWUkRCQVpKbW5uZkJ1SmJRWXI2ZWJMcWt6akZWcmpTZDJIS2xRWElhMTlZU0F6U2o2bGV6ZWpaRkFZUWtRQjZUeW9CVDUrK0hXZk56RHo1QXFFWndkQjhBU1h4eUdMaXlvNmh3a1dKOGQ0M3RnTFhqTExDOXJvM1NORUxpQlJXT0ZyVjdsSzBEU0N4Q1FCRGtoN09XbXVxZlhYdjB6NWwrWVFkYlF5U2JFVSt3cUF2UkFMSkNHM0NGUjBDaVhKbTBOSjE1S1Z1N1Vza2tiKzY5V24vY0p4aUpXeXZXLzEydGJQQ0RGYjFPeGNKWGt6YzJkYU8rY0V3ZlNtU0Nzdml2NGltZHVpMXpZMkx6TitzdGRZOC9XZVdnOFRBa1pkdmpBQXZtWjhmTTBodXB6UWpjYyt0WTIrTzVIenNVMzVlZzExaUtjdytzclJRYVF5TmlqR2ZaWUxsQVVPOENMbEhTc3l4TkJadVhnazhpQ0tCNU5KRVlUZFBLQU1qbk9qQlVIcFNOMXNmRVpMdkRUWGRQWjcyMGE2UGMrZTJjYXhHdGlld1l0OGtVUUlVQ0JmWlV3TFJ6YU9qN1RTOWYvV1RQVGsySjFnZWZMWWhBRlJHY0oxRTA3STNSVG9ydmpZa3VCVWl0eDV2Ylh6R2xtcWpjcVhKWHVYd1d6dkxWaXJCaWdNckdPbDVzSGpsWnJpMUhDUHpQek4wZmc1SjRzQzk2aUxiQ1ZabjdXZi95blBGOHRzYXFURGthbDZTNSt5WnNKQzBRWXV6YWtZMTRXRkJkUGZqcEc4MHl5WU1TYmxHY29YVkVxSEthOE9LeEVaMTZ6Y3BoWTMzZGhUa3lZNjB3KzVhVXlvTk15bXhyZE8reURWZU8zNUIyM29sRm80OFFISjd2Qk9yWHRZS29maFdFZzllbnZuMG42Qnoxbi8vUzV0SlBIUWxWbUM3KzNkdmJsbXhwWmlXeTM5SlR0WkZuWGphNEk3dmU5dk9SZFp1RThLNlRRTCtPZS8yZmRrakQxWWcyVHJ6dGQ1TXFjNVdyMS9kMnQrQ1ppa1NVMDBPWjBqbVk3RGR5MVRSa3lNOU51SnlsSWhqTGdEbVBLTm5ZaFVrYlV3dENFeVpPWW5icG1IOTFNZDArU3kxUzEvMm1XdHdibDFVb28rcG4xVUQ4c29aODVWVGl4ZGpSN0RjTVNoOUo5eFJaT0ZXNTV1bEZ6UUY0MzFMamRLcUh2UGluVUpJdjNROTgrbmtXYzRjVjJUZUZKYjc1ZTNHZThTUmFzRHh5S1Bsd0JVUHh5T0xzSkVlUEV1NXE3WjdqZmFhU3d3Q1VBZkJWNk1KdjBqYXlNbVRwOVJRZkgycXIxY05EY0orc1FvTU03V2FyRmFweGpMYXRHbFQ2NWU1S04xWkVVay95OTRia0Rnc2lqUldrWFB2S1NHZmtuaTRIV25Kck9NdGtJeWlZclJxcmpxWEg3VXpTSjZPejRMNUFmM3lWTWFsbFdrcDRWNkZCK0pQUzJVMjVqM1lIcjFGb3V3SjU4RDBuSWo0NUp4MkVTOFd1K2hvcHZGTjhBRlhwKytQOS9QYm82ZGNwZm5oZmQrM2d5MUpMK1U4RnpUSEN5eHM4U1FJZ1g1TGcvYVZFQ1NwM2s3aW5UekZtTVI4SzFhOUJGVE5lSjh0NzE2M3RXMzZrN1BiN3dsZWk5SjNJa0N0UTFuSzVnMFplbUJvTS92MFNvcVg4QU8rcUhxU09uUzlseG0vL3NRaGlxeVBNdGlzZ2ViL2VtSXllZENuVUF5TFF3NWRmV1Jpc28wUGhVaUdaTmRxU1RHNm9xVXk1bG1wRjFHM3l0NmlrTlJEUHpYLytvT1dBNHNPV0dqR0kzMldxYlRZcjRBUVAwL1VNeEtOYlZiTGcvdXZCdjNPdkhxRnNzd2ZsZi9oQUp3SW13TmRoNEM3TDM0N2ZVdUdYSkYrNi9TM0x5WWg1S2dySytlRXdjbmxIL3Q2by9WSUVEeE5LT3VWMnNGN3loTXZWNTQ0TEdyNHJyNDg3SlpQbTJPakk4cEZoMDlVWGk1UHZHZFFTOU1mMXpoRUlvVWYrTUE2bWYwb3picDNTS01kdWhmUGNQOG1Qakh3WVg3OFplUkUxU3NWYVBLODhtNWFvK2pRWVZGNVJIUno3S2UzdE1RVWV2aDhwU3lJNGYxZTlHdFV0T0VJVFkvaTZwVUU0dHp3a2prNFZXblU3NnRkUFQ4ZFhBYkxtY2NjcVFYa2c4UUkrcUxnWUhKZGFRZnV1MEtRWjUrYWRhYm1hZW8yREY1TWMwaTExM3lXcHIzdWc3K1VjTE90K2VhMk5rbHhWVWwzK2VqNlpDZDNpeisrb3ZTWlAvcjk4Y3pHdlNlOWhYT3VzZEZaaStRWHJsNTYxUHRCTHl0YW1XbXZQbldzZWVOWGo5cllWWHM4UmtmNmZyanRkdHZlZTM3a0hKN2l1N3J2MldzTDduZGx1Tms5cTFqYm8xOXRYRzVNdzlkQnV5bHVoOXYyTzlJOVJuWVIxYWZBZnpzMzY4cWdVOWpETjVlVFNrdUJSZWtoLzVyam43NEIxeFY1VzhKZkhoR2NSdzV1Nm5ZNnM5UElLTGdUek55WWJwMzg2OUI1WHhzdlhyRDBIdW85b2cvaG0zenpQbTV0cDE5Uy9ZeHh3cjNZbU5GenJST0MrMzJsUU4yU0gvYkgycHVPUDJqNXluNXg5anhjTU5hSVNSUGVNZHNTYkFuaVhmL2QrU2RWMC9RZjJQN3Q0bThoOXNzWTNpMVhHMmxEVWZiRVljRzFGUlozWjRML0JQL0d2Nk1aUEZpZVErOXFLMkNycEl4Um1wVWpGQWc1V1ZJakkwM1NhcEo0a0kwZVFOdE9DL09MZTVlWkhwQkcrODVLdkx1czk0YkVMSVdoZlNOZVRQazdWUGZrOHR0VXl1Y1ZaMVNydzU5T0Q5dFJuTFRqTmVJdjBUOXZKeHZ1MmYvL051WkZuOHM0bDNhdlBNQ3NWOXlqV3FPN1BDMHZDajRuWGgxVDh1NnRrUVlhdW5zc2NqcHdsSEx3WkFidG13TU8ySVA0dDFnQlN1SmwrbnV0emxaS3dPLzhQOHBITE03VVI4RTFOcFNNeHhuSHc3MWUxdmt4T1ppaG1MTGxWTGlSYmN4T2NUdlJDYkFmWTl6WTBkSjUzdnUvbWc3L2hmd2xIUkdCelh2eUt0S1RrZ1VYVFVaU3d1TVJOcTdvLytFeUdTRXFWazNGaStqZkZxU1hTbjEvNkRvMU5CRWFRdTdSbmdrM1BLUTcrV3oxWjlXZmhqQ2FjYTNoSWEycHZUTjVhcWVtelRUdklYcmtJNW9KamVGbGsyWTZJZTlxZDByV2libVRmMnNmTW1qSE5VeEk5YWZUbkw1NnVoZjF5dk9CN0h2MlhJN3Y5SDV2R2NGd2VzdWIzMGZHVHVuekhHTFptdHV2cnBBN0dKNGtpUmVwQVUvN3d6M0Y5WTNNaE5pUnA1K0tqYmtleEtzOVVZcWF5YnE2Y2VHOXJSVUJxU01LWE9WRjM0cmVxd2tZR3pTbElIRDJsSTBySnhmSWY5U1AxOVZOS3J4Um04N25vOExNaDJZbTZ4dkx5bHBESndOZGJhRWQ3ckl5SXUzSTB5ZnVQWGp2TytwM3l3c2I3M0dGMzYrdk5vNFp5bCt3MEora2I3UjQ1a3ZibTIzTmRHaXRWNmNScjRoWkdkdHdtcDVZbzFHMEdGYkhyWWlXckh4OFRVTVBUYjNpME9obXluUlhhRkZvam53ZDZlR25HK09xeUIrb1JNT0k3b0h0NlYzbUt0a0M2WnlBc2RiczI3WExaMjZJODdYV3hmbk11M2ZKbk9KQXE3RWVXZnBEL3ZaTEYxNy8rSkJtbGMwd3NzSHd3R09hbll5YlVXOXFhcDNmT1JYNjY5eXN1MGVqTDgyRUxGMzB0WVc5SEJQL3BDbnVvdnVpMUdQTjN6NStONUxxRHFiSnNTZW92NDBhQ2ZiZkxIR01qRkMwNWVsZ1RiYjhGdXQwRlJNbHpZVTdxcjNNTTczVTlwaDNUS3VmZkY3bHRjTXRUeXdUVUZmbHpxNVp1L1lOYTVMWkp0ZUgrdzVJUldiSzNTZ2ZZZHpNaDFONi95OWJJdTQ3VFdDYWFhd1lsaTBVbzd1TGkzdElDVVB5RkQ3bHc1M0xmVmJWRmg5U2RuUzlkczN1MG9Mc3lkcWxrV0hOZXgxWjJUa0VONnhTdGlZU09kbFpqcjNOWVpGTDJ5YXlDMHE3SmZ6TzJVdnVYZU1CL1FtQXREcm1xdjdPTzYzdjJlbW5ZbzI5K3Bkb3RBcTRlckwrVHFlVTlONUdVd2pFdnUwV05ZbGdhQVNEQWtGSUNVR2lrV3hZaElJNFM0T3MrellSckpDaGtLT2hFVkVvWWlpdEhqZFpyVWlzZFNCcjJSTjAyRkVZSTIxYkgwQWdvanVldWNTUUhISHRzdUZPSDA5aUpSRGhoRkNpU0FDQkFHUjFXUzhENkRKM0NQbHA1aEFrODNRY2NwY2pLNU5QdHNiMVNiUlpYYVc3SU8zbzIwb3pFazRNaVZjRkNBREJvbXJPN2tXcXZGOGxNdklMQUlCM0J2KzVLQzcyOTluL05wYkNTQzhBU0JEQWtXVTJZODNGN2FFQi80OUp6YlJja3V3dkRCME54RFA0SzQrZmNPQ1oxSlF1eUlxVDhMU1lHTElZdWtiU05pYTIvMzlNV3dJRS82NmRIcElnRW9kaFlPMkNGTEpYbW9Hc0dSWlpzQmUrZ1A2b2hGMno0UzU4MDhZMGIzdHlaV2QyanFST1BVMHRCbkhOUEl4MjVhZk5Lb3NOWUpjdXpPNXBiY3g3R1VSYkpnQ29qZHRzWXovQUVvRXhoSVJGQmdCT0FjZzJSUWFuTmlVbzNia3BxY2pqbTFJU1FyNnBTSHlrbnBIV2I2ckl4VGp2aFNxbEYrNEZRYjhCazRaMDZkQnBCTXVpelZ5Y01CV1BseTJScVpROE1zdXQzYkNMNmhQRFdtMEoyMVViS003UXBlTnJsMFFhWVBVVTZXaFg1Vkh0QjBkbjZCZmdVcTlKNVJMVzdialJJclc2TEFsVGVJRnpPQ29ZS2cwcDB2OEhQU0txSVZ1M2g5Skp5SzlKRGhicUxCTFphV0d0NW9sNmlBcWpmMVpwTW1XelM1ZFJhamsrRGNrOXJ6Z200UTdOTS82aWxLU1hQNFdkdWxGTnlEa1MvaUVGNXBhaFV1RG1IdXN6K2VmVElnZzUwZ25kaFUrekZPcGg0dFFZSEJuQWxtenJyQ3ArdmRVTDREYVRrdHFxMHpqek1JQ3FGWEN4R2o5eHFKbTFWRUhVYmZUaE1BWndWQWR3YUJhWmJnY0pTMVdrcU5xQXk3UUJ6bHdCQkZ6bHNrdDg4cVc3clI2K3hQSWJJOTNWT0hhcjRCbFphclEvOEtGT05iTGpSK1NRbG9tekdrZ3F1VGwwV0Z2NVdGWWpXU2VoYUEyUy9kaFBWcTFJaFdzbmdUdkUvNzhyVXJYdzd3ajkySk9SMHlwUXlHRWhYeER3SnN4aUNTa1VJWTFpbENBRGgxQUtCK0VzeWxBT1IxQ0JTbFNoR2pXb1JSMkdvQjROR0lwaEdJNFJHSWxSR0kweEdDc2U3ZXZpZVNjdlBua2FuOHFNZEFVRDdmOWNVMnJrdXpVbkZQdUhodnJIUndlWWtmeVIraHhyYXcycUxmSzEzTHVPaDZXbThPS1ExRFFyd3hJc3Qra1pJdkJXaXBWcXBLMmF4dHV0Yk10cHVnYTUrVld6UEhZamUxVW56MXNwVnFxVnZyaE1LSCtISjcyTk54QzNOODNnNjh3VDM5MEErdWdHZ0hXMGU4bWQ3Z0lBaGsyZkJQZERQRzZxYVp4eGJHb1VJVUFKcWJzbHF3Q0xXdFJXMTJpcXpsOHp2Z0h5elozNC8zcFMyMjRRMDVFbzRBWUEiKSBmb3JtYXQoIndvZmYyIik7CiAgZm9udC13ZWlnaHQ6IG5vcm1hbDsKICBmb250LXN0eWxlOiBub3JtYWw7Cn0KQGZvbnQtZmFjZSB7CiAgZm9udC1mYW1pbHk6ICdLYVRlWF9TaXplMic7CiAgc3JjOiB1cmwoImRhdGE6Zm9udC93b2ZmMjtiYXNlNjQsZDA5R01nQUJBQUFBQUJQOEFBNEFBQUFBSzNBQUFCT2tBQUVBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUJtQUFnVVFJREFtWEZ4RUlDcXc4bzBFQk5nSWtBNEVlQzFJQUJDQUZpR0lIZ2lRTWZ4dXNKYk1SRVd3Y0FCRzRXWUwvOG9BYk10QVByQVdNNElBWWNJWVFJUXdZaTUxNlJaKzFIcXllTzNXNENJOTlTbzZveG9oUkJmMElZMUYydjlobVlJdlhrZ2ttZUFEUFJrZ3krd051NjkvYnhxaU5LUFVNaHFKZ2dEQU1iRUt3R2l2USs4SlpjZEZlVlB3TXZhaWZYUVQwOSthK2VvM3M2SUNydFpxRzRZQUVwQk9YWmhpTXlKL3pIOWZLMlpJbGlubVpYNXlkVElFT01Fc3QzM0ZTVEphRXJ0Qng2bVhLanB4cCt4OWd2UENncmxSS1dMb0xDa0lNVTc2R3AwdUp0OURGaTlTeTNIUXlCQWxrS1FqRCt1QmIvdHJtbjRpQVo3SW5lVVdOdmFGb3pIY1BtQjZha2dhTVA4YnEvLzlhcGIzdlYvVkM3VzZTU3RneWVTQWZZV1BNNzEvZGN6NDBWUzlWMVZMMUFGWE9TYzBpZEFjQVBhQUNycGtBbzJLVmVFUU5MUEg0eURtcklpTnRJclNNclNGNnVDaE4wMldTQndIc2x0VzA2cFNDaDFDQ25Fejh1WHNCUkpyRVBRU0FlSWozQU5qQlhqaitzUWNDTG5hNkZueXY4TTIzSGsvdHJhOGZYTGcrVFhML083V1ZXcktMQTloOVE4TEZKTTc0SWtSS0F3YlhMbVUrTk9QNGx5akNTWmF1MHdxclhCMGZpKzhrMGx6R255NnZsRTM1VjdEcGYxVW41VmpLaWYrbFFIR3p1RjI4TlhDSitKTTc3cGEvcEpIcFh3Vit1bmJQV2J2dHRzdFFVOUx3RGZzZThlNWY3MzcxYnV1NzlQR2lFVk1hRFRwdzhTWWxBSVI3Ti9EQTh4L1FyWHRPK3ZhTlk4WWV6YzloanRUa1NGajhLUW9pQ0U1a3JMUW1QQk1vL1JZRXZNRWFJc2lsdVNFQlRNRUh0dWFwK0tZMWk4UTdtQXgyM2pvdFdxVVdzSGJMYWJxOEFwMnpmWjNQSGxtNkVhYzByNHBjbm1NMmJUUEFBeW5Na2JaRkRSSVZITTJwYUFtSFpTR3JGdndVSzZMZmdSQThPY09uM1pBRUJpQmxjUW9LdU9Vb1VFdGFyZExhRTVNaEJkR2lNYzJDbllWQ0lRakJCSVIwTmtodXRmOFFpTVQ5ejNjODJ5cUpwT0syQzFzcTduZ25QaFc2ZHQxTHEraUtDUGl4dUFBVXN0dENrU0xRVXRoaUFZRjdJNG5kdkt1dmN5RUt0b1pDSU5oQnBKejhvRUlmQ0J5NWdwY2RIL3F6R3hLaEZiYkFPN0NWVGpyRUsyYWhKSEVXb29zZms3SmdSaWtoWjhRanJBNTdwTzVIYm84dW12am9OSWw5WDRkSGFzZW5jWnVnNVFLTWlPeHBhcVZOMHVlMEV1MHJiY2dpRTRXUC9iRnZLQU14QVdUNGJKNVgrdFY2c2x5NklaYUhFemxBUldwdUZEK29FRXdKbVRJR3BCNGdEMEt5RkhpKzV6TDEzZmRYcTZ2OXlDZXBkRHEzM2ZQZHJhLzBhYWszclVLR25uZGZ5dUhtaWRsUzdwNUFFbVl6c3lCMktPSlVRTksyWEU1aWpkVFBsTFFOL3NPdGxub2JyNlRVUU9TbGQwaDdNaHlScGJ6YkpHZjdKNU8zbUNHY2xUNlJoTE1XQmZjM3JrYVJvZkR0T2NyVllrQXBQTjJVS0NGWkJHTCtxdWgzUUFZZW0zcXlxcHJLNmE0cXZhRUdHVVpkUXkweXJxRmpCQ0lZaVVoR0lZb0pzSWpSaUdaQ3hEQVJZcGs0blF3MHhQZHVwZURYU3NYZlJUb09CVm5IU25vNkpFNndNREFDOFl4RUFxTmdaQUlrTWhwSlRBZ1RFOEhNeERLTnNJSUV5OEJBUlVKUGROUExDOGM2MDlydVEyTUZMVTdMMWpBZHdBZ0ZhWmU0SU1QWi9sY01sUFpYMVphbmE4WXAwN2RaSU5xQnUrTWpyZG9KYWkzWHdRTVBjU3RIL2pqdzJ0c1VvZ2ZSVXR3TzB5OXc4ZUhwcjhsQzVrVmJHV1lBS3hqaERnamt0NUJoSnBxTEs0WlpPZzBpSjdXYnlVWW1ndTRhR293STJ1QzlUOU8ya1lqK0NEZFd3K05kVWR0bUNXclBWWVpPRUthazdQSmtzeWxtZDVhc0Nxd2taMnhuT2VQUnJQdEJTcTF5QlBiMCsvbGQyKzR6Y1h5VVU5QWZqeTd5ZGl4TVJFUktNQXBrR1BoUTJhVjBHSThLZzM0R3dUQVZrb3hUenF0K1ZJY09ZMUZUMDdzVUJTQVZaUmdYbEZERUlRbDdlbURIR2JEUU1sV1NVQUViRkZpVVRhVWpVdkhKakFXVGg5TFdhTGNjcFBRM2NrT1lvazhVUkNvQ1dWbFlXM25ib2tndGdLSHdLb3FtODhWZUpFVmlid2p1WElNVjBZckV3NU9CZ3RUN3RKbUhlUG9xWE1oWXkwWXVGa2hyUFpjTGtGU1lJeDFlcHB2RzJwYlRvQkNTRitRRnd4M25wSDJUcmFMcHJrY1c3V0xyVUZ4R3JWWFAyVjNUNmRiVG5wbzVuamhIVk0xeEplZDhBQ2sydEZhcTNrWmpzVmlMc1dBRlhGNndHeGZYV2ZRK3NtMU5Eb3RSRTRvNHhEaHo1aFZaVUxyc0U3WEhxbjFKYWFPQitTZHFXZkpxazlpcHBEVHBwUFVXK3FNODY0L0syTzZlMHQ2eU9vT29Qak9QUm5uTjdDY3FyZyttQTBHRFlVcklkTlVyNjdyMFdhRU5xS3BieEFERmc2b1pvc1pCb0ZxR3FITUJmRlRQRUEwT0FqVXlSSk1MNEtZQVF6UTdDTlRDRUswdWdKZmFHS0xkUWFBT2h1aDBBZnpVeFJEZERnTDFNRVN2QytDaFBvWUlPZ2pVejdCbklHMCtOOTE2OFYxL3Z0Q3dycUlpRE5aQk55VVhRdFU4STRUdFBDeXg4OHpRVEp3d1BESWZJM1krUnUxOGpObjVHQjlaZ0FtN0FKTjJBYWJzZ3JtbTA4NktLZUpObFBSODVoWU56SlBHMnFXdkxZRUZVR3ptakJ3WU00UGFHNEJNUUg4RDhmU0R5Um9BL3hBY0dzNzJIZ0JkQXJsYjZKSUVOMGJBVE1YcVpTSzVxTFlpd2lhU0pIUCtIV28zY2JHZEV0cXNYYnVCZ3hzRWJTTTdST01PVERQWjR6K0RxbGx6TW5qWVNGd2YxL053WGJSUUVHU01aNkQwbi9rNEJvTHM3cWRMTmVBK2NlTkFwaGhubVpzQnptTUhxUVNtM0ZaWUtMMm4zdDQrU1dFcmxMVTh4VnVBcUNKUkljQUFGVEZZS0pCaVNRMUxVaHpTdURvUVhFUVFCd3NKVXN4NEs5ZDZIbHZKakpja011WFdUOXR5WDBMTFUyajlVM05UakNtSXlPRmUyaUdPQUpwRVFEdlhDYUdaSzJqc0N1RFN6TjB4UEk0QnhqaWtOMFl6VnZDbE8rL003UHczYUhNY0FkRUtvdU96b3RCNW5rOFBoZCt5WGRObmZKTWRlMm9zWjhiYjJXYmxXcyt3aldaT012MXBNRCswZHVRYktsYWpIamVhR1o1eUx1dHUzZFRhbjNHWUhSbWVEWTl4Nk44YW02YWN4NVNPMGhxcEdWdm5tQkVKVXM3MVVWRkVTTUxZV0R2WExyVzNaeTRhV013d0JqbTRkV2JlT0ZldndGMXc1YzRlUnhHQ0xhSHFLUit5aFpqbFQxbFpMODkvd3dScDA2YnVpbXdOUmV6T0NXZkhHTy9xVnpuNkpZM0pBb0dYamJTKzlYY054SkhqL1BJL1M2RGtFODlaL3hPVHhuamZNUm1FclVYVm9BaWhwSTJadjd4WHY2UlAzMHNUUHlzOHEzMm1Ud3RpV2t4MzRKZy84cTMvNm5NNzNYamZVempNT292ZFNPMkVjaVpMRXVaYWcyeDJ0UENmVjhJNzVhVHpTSmtPY2g4T2Q5aFphZitLWXBoOEdVVlVZYWIwNE5vaDI0T09iL3R3K2k3TlRDMUdwZG5rL2x6YnI3dmY3bzNGY282ZjhXQ1Z0UnlwVWNkMXArV2lkZVoyRC9rcSs3WDBWanZMdFNZNDdHbXRueGJGZHFUYUpFdXJ5SEl2QVU5cjB6eXpVT3U0bXNhQjNNVXhSVUd5QXdnU3pYV2IwWUNDSUhPek9WNlJsZjNteXUzL3dhOGJydnM3TXdZcFIyc25xclBLZnNhdGI0TjBGdzUzdEk1SjBsMXVhRDhFWGd2NHFsMUwwUHBqUXN6RDFVajFNS2JFZUN0TitlK0ZJQTlRTVlENUlnb0tBUzVpd1BUbW9BOHJTS1d2N0ppQWFUMW9VRmtaaUtNaDIrd09YVkFKaHhVVjh1bFRscXdhclBLUjJVb2ZreWpZaHRrYjk0VEg1TEFhWWQ2T2tkS0s1TXNFcVc1aDFJSG4wbHNTSkhCK0pWZ29uM3JtQi9rSUgza0EzRWZxYkp2RTV0MHUxRWVIaCtuSHpuSWc4ajl2Ull6S0w0TXdTZUNBUE1TUytybi9BSWhqa05tWlpiSE01ZXB4ZjJmc2tMOHFVTjVCYnY0NHlGelg5K1VZODhOSFQwM2FQeWp3bTEvUmZ3KzJRWUswNDN4d3c2bUhobzdtbVh1WHhNWlE5UjY0UTNmMWx1YzZjZE43aFNoL3Exa0hnTkpyM3dGRXR3aXBxd2dCc2J3WjdzOUtDSkc2akJUYkN1VXVubUlNcGJla1hyM21najhoZ3RiQTVHUkNhczRDWUhtM0tQZlYxalpRa21Pa3RQUmt3eUhhaFBvR2duWitnUmZaMXBCTk1RT0J3MytaWThlVkQ5NTMzTlBBdjNRdXBvbWtubjNoOTl1LzVDYUVsZE16L2ZvVDcvajA5MmRwSW1OQ05NbXlSbU0vZkMvRnlvSWl1RGZJdzd4UW01bVd2TXkvY0dOL3ZIOHZYYldsVGljMXYvWjZmTEpTZ1FSdlBHVDYvUzBnYlBpNTR4elFHODQ1NkIvSElpTVV5UW5tMTk2aUpOOUptZGI0clhsb3BDb2NueXYvejR5T1g5L0VzbWZQRk1aTjJzVHdIR2pSekJGRDR5Qy9MeXN5ZTk4Z2IyZ2NLVjBFUHhoSHc3RUNBaldWN2M2YmJ4aVh5cHBrckhHaTZ6Ty9YZWFQZ2g5U0pzSk9DeUJyRHJ2Tjlmdk9IeXVJblVhd3dNNmlGUGkramN6dnQzL1dOV0ZrYWVGVVdGNHNaeDk1OWIzYUxGV3A3NGN2YWRycnY3Vmt6c2l3eHJuLzlnZGNrbGI2eXg5OHF0TGFyUGRlZllTVk5jbkVUdjZzUGF1bWRwOFUzMkFXTEs0N2QzNTAzZHk0TGMyZnV0c21obWRCOUw2YTJpejdXZDVKVTIzNGVzbWZEWlUvMytRaXJ5VysrSHkxWWRjdkh2SW56NjdWaHMrL0lLNmwwNzlSbW9zbFMrNXI1MzVkU0txdWlYbmtkNjVFb1RDVnJyRUhVamFsVmFVekZ6eWQzS2N5VjBhTjZheExReGUrMUE2eEYxMzhURHlHYkZYakprbldEMWxvVlZTRitlS3ZIZEQySUJXOGJlZTJydXlCblRQbXRBMTZoN0pUeWZyaHJORGU4ZXNsd2I4Tm1uejRuS0d0TFdmVGMvcUtENDNtNFZLalltYlUrelJEV01HNC9MbWxXaVRscmZqeUxjb3RkSmx5WDBKOWIzbStPVEh2dXB4akxkQm1xQk1TNnFTSWpVZGNwV0lyb3J0bHoyUE9LdkdRVEVzWEhsaXRWRktHejg3cEZpc1pqVGZqN3F0VFA5VDh0bG01Y3l3bjcva1hHMmo1eDBsWC83ZlFTdC80U1dxVmlrOVk5TUE3OFJsL1JzdVUzMXVWR3lPMURibzZ5d2ErOWxPaDNsSGc1bUplakk3NFB6SzFLaUgrc09GTGxVRHhCMWZPdFpxZWl0MGJlYXpidUFiMXE3VWU0K2psNTF5V1FxOCs1b1dkdjZEenY1dWNMMWFTb3BDSDNkL1RRMHBveUxBOFNMZFdKcFpiUEEzMHBlejdZYWpJSTVCc2IxcVR2RG50QU84T21qbVJyNXZibFc2b1NHeTErT29hTEo0UGhsMDIxcXNMcFIwN3VCZjJLVlBNM1Q0U3RVYlZwaEVZejcvcXFsa0l1QUxxUUJYZmdiRmM1SndsWmRQcWtybklYY05SNjVLdklvcVo5c2c3QnBnU3ZjVDk0MTcxSGRxNHR6S2ppbmRITmU1TmVDcE84NHhHU0pwak1uWHFTL3FZMTFVeFRkRTZnaVluTjZqdjFPcWZiSTh5Ym91YU9vQmZ4bXJ2VUFzSlhhcFg5WGhNN0pmeDFUSzF5YjhtM054NVhjYjhIdmxvWTFsV3owbTJwUkxxWWMzakpIY2gvZ1liZWMvOVl2R2VqMSs1dnN3WE5XemFkbFk0S3I3L0h0SjJRL3dGam53Y2ppSWUyajJ6WnBBbTUzYnJiMVFwejV1aGZKZERUanAxTHlXdFNSWGx1L2FWaHZuWnRFK2FwQ2ZxVDNXSE5KdUFDNjhIcStrZDN4Zzk4Sk5lTys0S0NlL1U5NDJEL3JzSmpVUGpRZENqUDhzVzdqdyt1YWk0Yll1RWVVOHNQRE52NnUzNnVLbnB6SlMrU3loNVR5cS9Mc0pqUE03ZjZ2OUQzdVcreExodzI1bXpPdkVMVXNVUVhMaDA3cHpzM1BrTGwyQ0lUbjU0L2hWcjN0WHIyWkQ5N2NWRGI1YnR2MkVxY2JUbzh2N2xKOVkvSHBVeFpqd1RvWHV1NHVEeXhUNncvTlNHZ085cVIzckR2RzdqK0lwdzduV3g3VDJucTNlVXJQajUybE9zTWpiR1BpenVTUnV3bnJ3aE9aUTZNbm5UOVlwVkZUUCt5SEt6eDJ3M21NenorV3o0YktWV0F2MnQrWDdJUkp4dFdURDlMWXk3cjRwQjlaNEVKVlZyVXF1UlFDSEpyRnp4UmwvNTJpN2xnYU9QNWcrV2J3amVwdGhweEQ2YnFUWXM3a2dON3dMWUpmVlh5Wi8rWnRUTTJJM01odTd0VktBNUhIN3c0cVllb3JhdmE0L3pJN2x1TGIzaVo4K2RQbFc5WTkvK0NjcGV3UDdRcXUzWTNCd3FOSkg3dXIrTmZ0blE5TmdUanovdXcvVnpTR1QrdWJUeUVHdDMyZyt5eXl2L1RHeGFXZTBQZHhkdGN2WDBFVU9oMEJEM1UxaXB2LzB4dlpEVGhzSUJhZW1YTWJGdmxrb0Q0WkNXRStvZnUxMnZEUC9VMkRuMVRoRTU5Nm8vRXpremJaeWVnYnp4RzJ3YVRVa1h6Tnp2M2JSM0ZjemNsT2lYY2VRejE5TlBlbXU1eEYyaFlTM29BUURaeld6OEthK0dNU1FwQWlyWWc1Nm5LUVJpa0hxV0lSQXMxcUhCUWtlTUZta2xPa2JDRVZISEdxSGtXWnhSVFk5bVJxZWpNelhCK2FrR0JCRmRQTHR2VGVSbVJrTjlnZEk0ZVRaUVJlWm9jZ0NjaklCa1JaNmpwVWN6MEh2UlYyVFYzNUxzR1M2bXNpd3YxMnJSeCtsTXNJT3V4aEozQVoraW4rdDhsY05JRUFBQURkZjM2K1RwMCtYZDh2eGZSVkx5Q3dEd3p2Zy90N0MzK28vNm4ydUY5NVJhQUJBaGdDRzN1UnREVzNXbVJ2MjNVZ2hXdkVoTS9nSmVKNXdCV0gxQTdPQ2NYREFocFlJenZPRURwT3grUlR4dWRpcFVPOG4vY3kwTy94OTFXeitTK28relVhNlg3YWRIWXhwazUzd2FWWjBlNzFDYXArdEZCWHhRVmJCaEVNWm80MEQ1bkZPb2MvWndiMkIyTDhGbUNRQUtzeTdYT3d5NFRSQ2JRTUlzQm9CckFXOGpwSFB0UmdTWlN4dVIvQjdiaUpJVW1JMEVqSUUvT0MzZWlJMkxNZkJRWlRMU1BieEdqVmxtUXNoVkJrM2htQVVsWThpemlTazVMQnhsNTFrNXhRWk1tcWlSUVEyODBZQUtRcEtHVXpZOVkxWVNnSFhzTXNpcEtyS20xem01dzE3OTNEZHNtUXFMR3JmYm9sdURrT1Y5dkhvRDdaYW1EZWsxd1cvVWlDbFdUZmhtQU1wWWtaK2VYSnlyUTdHc0R3eGxGVEROQkJYVzZEVTVaTW5obENHejFHSjJJSko3WktXdGhic3ArV2F2K3F5RzliWndVQmpWVXJ0QXdqK2tVSElabWdHWGx5Z2ZvbmUxSW9Jd2pXVERoUEFaamtJelNaeWFBYjVYNVVxMlJVNjFYc090QytBMWs1TDRhdEE1NUtRRDFXRFFZalg3b1lreXA2aUcwQVRwSzArQ3Fta3kvVGdNaDh3TU9BbkxWT0tVR21NbUt3QWNzdElSMHBxWGZZcWQ5ZHBEMkJxK2o5TnJoa3lvY2R3K1F3Zm5xREc5d0ZQYzZuakpsRnlTTW9WVHhraWxpeW1UZ3VWVFk0TmJ4b3FpT2tUMlJUOWREYjlLMC9HRWkzMzhmNjdnVmN4L0dYemRrV0tvRkNyaTBxVW5FUEFHdklra1VuQXNDT0NvRzV3SU5BcFJoR0tVb0JRWlpGR0djbFNnRWxXb1JnMXFVWWNSd3VtUmtNM210ckdYN3JEeDRxblFVUC9BZjVrVWdrNmV0OXVtNDNodnJ0ajJqZlBhY3ZZY2J6bldjdGljdVp5YzIvS3NsZTF6V2pscnVXMjJuRDNINXpKdUxRc3FmdUdKK3BZRnhGMm5lZnk1Y2ZHN0JhQ0hDd0FwMDEyS3puSVpBTVRyYkJLSzM0eTRaZFV5NzlyU1lxS2ZKYVd1aU5iQ3ZJcUNqUzJ5NmpRTnZnQk0veEwrUDFJNzM0anRTTkJmREFBQUFBPT0iKSBmb3JtYXQoIndvZmYyIik7CiAgZm9udC13ZWlnaHQ6IG5vcm1hbDsKICBmb250LXN0eWxlOiBub3JtYWw7Cn0KQGZvbnQtZmFjZSB7CiAgZm9udC1mYW1pbHk6ICdLYVRlWF9TaXplMyc7CiAgc3JjOiB1cmwoImRhdGE6Zm9udC93b2ZmMjtiYXNlNjQsZDA5R01nQUJBQUFBQUExb0FBNEFBQUFBRzV3QUFBMFJBQUVBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUJtQUFnUlFJREFtWEZ4RUlDbzhBaTFZQk5nSWtBMTRMTWdBRUlBV0lZZ2VCSEF4L0c4a1lJeEZtazFRQStJc0UzaEQxNlF1UUVOTVNZWEo0KzlGNWhmQTZUcTZ6eS9zakpKbjE0V2xidmY4bkdHWW9KYmQwck1LSTJEQUtCRjJqQ3Jmd3JuRzNqWXNJdmNoV0w3b2grSGI5dm40elRjQnh5SUhFc1dibmNQMFBNQTRiL2g5dC9hd293ZFBjOE1BM2c5aVh3YklHL3AwRXpleVhvcWFvcDZ0T0JyZk1uSGVFbUZaN2hKMm1ZdlRxRVJVNGZWUnZONlgvYjY2MC8vL2thSzVJaXNrVDJBcFpZeWF6dS9jeW1hWHNBVkcyaEtRQUZUa2lVTzNKK3JvVHVxKyt5bFRJcWc1WnhuOG1DZ3BkMXlYYWtLWlZpV0k1K0xTYlB3QW8vWmtjQndkUUQzMEF3T0h5OGNtSGlBK0t4NEJ1NEFjZHZ2ME9yMUoxaWp1UlIxNWZNYkozVFVPOWdibExCbzQxTTBpU1pIT0xhSmNHcE1peElRdjNuNzJkRGdNVlpCRmlOZG5uZ012b1UvUmRoZ25jR2lTRmZlWDFCc2pDeGRwTGdiU0JFUGJsNzRWNzE2YWJISFBVNEg4Ny8wdjRULzNlSCsrSmIxcEJrUHlrN1JGRUM5alRORXR2L2tIemVBUC8vSlJJV2hQK0tpZnE3UW1xM1gvQk5WeDJhQXZuUGFnSGhUWVpRQnhMREJBZ3h5Y0JDaDJKUjdaZ3c2Y2gzSXVjSGs1M1BpZHYzdlZPVWJXVlBIblppYlJhUmRvcnF4TG1ka2VFMDU0V3JxRTRLNDRhSG5Ib1JMMm5DMEdVaGJYa2ZrdXNVUXFrMUlDU2MzWVF5OWxBVlRyT09jQkFVOEZxZHlRNGJpVTRpZkFTbmZjWkVnZFltRGV2bzhxbFVUZTNBYUtVQWxwSExzek41OTRKMWN2L3E4dFc0Nm9oc0dFN2xTM1g3ZVJrMll1ODNRUXhOekNQZG43UHlBRUZidzJnOEFEL3poUVNUU0FvZHREMEJKSkJZcmhiUUFGcEtpUWUvbU5kS2lsNTVKVk1GbDhtaGw1T3dLQm9PMDdqVFE4NEtPZDJnRmVVZEhOaUJncGh0V2phQnlVNlFpdUViWWhXSTYvSnc0ajNBclZ1b21aRkpCRTJ3WldJK2U1QUpRYjFqZ2FwVlk2U2QrYnpUUjlRbzlKU1JHYzNDV2dBaVNWVWZMMDFnaktKR1hVTDFxQjYvSUFXUWdmTEZmV1BkVmtmbEtCaFVkOHF6RVZnZDZMRG03MVUyWHlTTDZNdTdFZkFoeTNZVG05Mno3YnpROU4zN29zS0xmZE5MYm8ySklsb0xZTkNxTWFxMnFGNlNzak1CS05iVWdIZytMQWFlNDlVVDNMekJrbnFYa2NEMURoZE4yZEd4VGxxTXpMemNOZUNPUEc2RlRobEV3aEJ6a0M4djlZQVBaWXFhTFNYMEVyVUZFa2lvb0RXcUFKSmZ1c0d5K2k1ZSt4YnEyOHRpcWxiN2xJT0dGQ2h2eFV3b3BJeW1JaUNtUml3RUF0YmlJT3R4TU0yVXNCMkVtQUhLWjBSVUFQNWVyUkkrTldpNE8rRTlzTVNhck1UL2RFUFpiR0JBS0lRU0F3RUVRdkJ4RUVJOFJCS0NnZ2pBY0pKYVVZYnRCQkRUSGhCRjJ0c2xCcTdoT01RMFRGWGE2d2d4b2k1QldJaHplV05QT3ZXYkNwYmNLckpuTStCT0JlZG44eUl2VlhPVDZmcFRoNWtJQjZkK1pQa0owQVBiWmFKMnZXUVNGRTZ4QXlZYjE4ZVRMK1ExSjdKVTF1ckNkNTRNNUFFdFpyYm5BSUsvUmNLSktNNTF3WlN5SE1oUEN5Wk1oV1Z1dGtWTlQwTkJFWDNlM3JWZStZMnQwNXA0WHlHU0J4U1BKbWV2aGxJQXpEQktxeVNuUmtsYngzM1ZwSGFQazB5VUhxQ0dqbDVzQklwMDBUck1qOWwzbmI2U1ErcnNrUFhDYXRiYzR3bUJLb1A0a2l4V2ZGdEVXcXhtSXZjRS80WktNdVJ3YkE3Y29Pd08zZGJaUjZhSjk5ZGRTYkJCcHQxYTg0UU9JTXFkR2xhYjlIT3NyVzByQ3c0RjA2N2M1NkVhYkJoVkVvTmlkYmludW5tMEplWEF4azQ5Q0FKTHFSWXU1YlNNbnREa0NocEpoUUtqMWxaZmE1QmVGQzlIR3lwVVd2ZVFUdTJFQnpzTnEvTVVxOWVnZDNPbW04VWU3eGpTU2o3c3EzdzB6MVdlWW1lNXpEajNGbVdBMU9EM0Jsbk8wbHRicXJWRzI2bG9uSUgyVnBHSHNmODZRNmRQeUxOQkRzTFJxUmJMN01pWU5iWHAyM0RZTmxnbFFjS280eUNKd3h6VEszY3R5WXlwMmhVaGZlcWp1SDRUOHdqVUF4QnpESDd0N2Jtdk04VGZiYlRLZ1lrRGdNcVNmUzJYNGV0RFJ0M2VvMExhR2pBc21pZXlrVW4zWVptVjhudVVRVWhiSkx2M0QwalVpR1VYZ3FtcWFuUnJBOUtzQXFWalVMU3BqUU1LeDhOZUtDb1l3VVJxR1FFV0VVRXFoc0N4VmhEQkdvWkFkcUlnTDBoa0k5MVJNREJDTkJKQk9vYkFvWFlRQVQyTWdKc0pBSk5EWUVTYkNZQ0xZd0FXNGxBVzBPZ0FOdUpRQWNqd0U0aXhWM1JxeGxRVzkwTjltck9TK3ZCWE9nTndiVEdQT2dMWkhKQXY1NEpBM3FtR3R4Z0hnd0ZzaDR3ckdmQmlKNEZvM29XakVWbHc3aWVEUk42Tmt6cTJaTk1SZCt1Wk9rYktMTmdUaXAwclRMQlZiT3ZLK2VJQVFIeGV0TWlZUlkzaUFNWWdmd00wTStRUkcvWFpnN0Zzc2grakFBNUk4djZiemNKRnFGcWx5RjB0LzM0OW4yWkxSSW5zWFVTY3pEUjVUYUlwaHRxUG0vZUlQT09EM3BlanpKTFMzQWIzbmlYbm1aL3k3N1VjWGIzL0lva0dhK3YvcXhwSFc3YnU1MnZoQnRFenpKNTJQR0Z5M01CbVRldi9PUGZXbHE0M25DZTg0MC9iWDhhRmwzNzFCQmRFbHp1UmRGaldwN2IrTHZ1YlRLL0lpMFpsMXpyZjluZjJrZlVMaFdSOXJrTW9tbGpvKzZDS3hyZUxwdkxjaTQ4MDRyNTVkcE54OHJlZDY3Y0JoMW15UWkzWVQvVFBlRnYyTTY3elBubVpaWTNscm00WGNsZ0NGbGZ0NTkvZWIwYjYxdVdzTUt2VDEvQ0V2NzVyOGJqN2picmNiajkzcENsSmJocmdzOWsxZm9NTWx6MkJ3blJtK1k5ZDlOeDRRZmpYRGRtM3A4WkZycFJkOEg3VzAzNW16VXZmdUp3dEgwa3VTMFd6L2VpcC9WRGNlc0ZMWDd6bTJHYmpxZWxCZU82ZmIzNFRtbkJjYTIwdzIxN3c3Ym9NVzNVYlpUY0pYcHNWODF4RC92YzlWM0xmNXZtNHhhdk14aVdUU2FId3pXUHEyUEo2UDdUc0tnUnQ1REtoVjFRM0FmekpKY2F1L0ZVeEVGYVBzRC82a0RqdSs2bUpyaXY4NWlXbTEwZWtuVE9ONzlQNXMwWEttemowb2V2Snpjc0poaVhqUjN4Q1dzSlF0VHJOekYyLzhUTnc4azJheVpmT1QzSlYxc3o0aGM5dkpub2IyZllHeDk3OW9IOGhleGM3OTZCVDc2OWtiL3g3eTNubml0TWFMUTgvV2J3MVpOVGZKVTE4NGZRYWp1RTd6UEsvQS81Sk0vcDc1UStsRmdTbnZKVmlHK3BUa1hFNnFNSC9hcjF2OWdhZnZSTmxCTDdlZTNFblZDMnFITmVodkRKeXpsODl1TVhTRjVSMjUrbzRESkR5d3RPZVdwQlVQMm5ldFRaUE52aVVSNTNEdWFvdWQ4c0gzcmxyWGEvOVpZdzEzNmZQVVVpZFBLN1ZON21MZ3EraTZFR2M4TEVoMkp5a2FwR2lYeTZzYjdpZVUvWWZ2ZXphM1UrOUZUOHFlWmQrcVpFQlhTMFVwR21xSE9tSWlMZ3pnN2RhWUl0WW1pL2RtZVJCRTFjS1Y2SW8xVGYxTHlyNW90UUh4MHZKSi9sVjZ6aU5KODFoYzNPK2V5dVUwR0wzS21iNnJHWHpuV0VxRVUrc2VDaVN3cDJhUnAzSUk5V1ZTZmpVaTUvUTU5cGRMV2lnbGNtdDhmcGxxb3RDeUFUV2xWRll0NWcrYkUvMDlMdmE5MTZCY2c0Y2p6L2hPdDZubWs0MldlN04vTGVxUDdhZmhzaENRbmM0MmV5VEdkQmV3RVR0K0RGdHVOdnlwODlLM0pNRTVhZXVWcW1PQmhjdjJYbHFkUXJmNnNzWVk1ems0SFhKeDJWVmh0V3kzaDN5RGYyN0kxOThRMm43UlU4TXZ2RUxxOC8yYjUvYzF0UHYwT252b09DK2FWZlNpNng5YnhRNmlxYlZldWZZZnlmNlRYVXEvaDdlU0h3Mll6RWNHTVkwblo0Zkx6aUhBV1VBVzdFWXBsRGlGN1BMVXlSdy9zRXpYU1ZRMzVIL0lHZmZMN2UvZFZ1L1MwNk5UVENGNFZmNVBQSEZGOFVLRFU2eS9mNlhMKzg0dlU3L2Z5VVh4Uk0vV1dobXBJekRUNjNPSy9rNDRuMmZGMktnNDF5QUZML1V1WmQ3Z2J2dnpTSnU1WmgvM2JpYURCbnVKTm5DV2hjMU8wK1preG1ZSmtTR21FYXloaWlCYkJReWtSMm5RclUyVGE5UmtiTEVPYkx4YWpBSndNZTZhTG5vNzl1MFdiOUprak1sd0R3N3RpLzE1ZHYrTGZ6M3ozY3Rkd05BQVJRZ0l0elBKT0RobnFWSXRyNzdUWkFrSlNiL1kwRTh0UnlJQzRGL1JDdm5sTUhGZ05Ta3c1L1lFd3JsZWozT00yczArbHl1S3U4LzFvWElNRGZ6ajZibUM4eGtuYmk5NEJVTHY4ZUJ6WHJiR3RSaGJwdURYVm1HbTRkS3lLNW1STGpHQkJXQ2VBS29ONkJ3T1NLRGhRYTNOT0JRWW1uT3JBSURWVUhEc0dSc0NUZjEwRk51MG5kKzlRZ0tUK0FRaU5HdVl6cjA2UFhKRm00RGhGa0NlTEVTUmR6ay9GdGZGbVJMaFAwY3JoakxXL1kxVytITHRGa3UzZk9TcFRlUEY0U3BzbFNuV3FUalBtcGJUcTlkSWhMYVl5MlBwMWExT296VjVxb1JsZExuVEtvS0IxWFlxUmFUWmJsZUU1MTZWRldxM2NrZ3l4bkdUSEhSVUVwNjVZYXg0RkdERHQvaW5ScGtpUkxsZHF5Q0pGZFZkRkhzVTNQTXBPV1dnMXBhNm05K3JHYzVkd0kvQ2M1SW53d0w2MitBY3FsMmc1emE1ZlJ6dFdxenpZbDQ2Y21VSnQ4V3BkT3NyKzZsRlZvTTdSQVBoS1JKTVdjN0dXWG51Q1d0UkYyeTVtSjR6NlpTZzVpMVlFZFRtQW5UcWxPRzVSc2xlcVNrTHBiV1V1dk5Fby9sL1NseTZSQTlMeDluaDdQbXJ1c203L3VVbG1iYVZOOVVkT3czZUNTTWk2cVRXdDZ2dXA2K2FRTW9vLzRKVWViak4zVEozVDRUbzUySGl1ckJZRkJwRS84V0pWS2xQSEZVeVJqOVo1ZUFyL2xhM3J2SFJKVThKVWpWNTVtclVIeEp0NGlER0Z4TmVFSVR4UkVJRW9pS3FhRysrTGk4dVBLcDArTVMxQk85ZzEyZHYwdllhSVZFbnhwNjRqMGlIeXRvR2xxY2RxTjBsSDY0WmthNXhvaFIrdFh5Y2Z0TTk0MThBVnJRT1I2VU9HQWV3RW9qMmRtVVBTQ3lUblZ6dFc4YzV3TWRGS280ejdCRGNMU2FSMDJKNW5jMjluWG9JcThoM2pQSEh0a2pWYW1jWjFGQUFBQSIpIGZvcm1hdCgid29mZjIiKTsKICBmb250LXdlaWdodDogbm9ybWFsOwogIGZvbnQtc3R5bGU6IG5vcm1hbDsKfQpAZm9udC1mYWNlIHsKICBmb250LWZhbWlseTogJ0thVGVYX1NpemU0JzsKICBzcmM6IHVybCgiZGF0YTpmb250L3dvZmYyO2Jhc2U2NCxkMDlHTWdBQkFBQUFBQktjQUE0QUFBQUFKMUFBQUJKRUFBRUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQm1BQWdUUUlEQW1YRnhFSUNxSmttalFCTmdJa0E0Rk1DMmdBQkNBRmlHSUhneW9NZnh2RElWR1VqVm9vaWpKQkdZajQ2b0EzakQ2ZWcrQ0RoVzJ4c0tBeC81aTZUcldLb2lCVjNQRm9oQ1N6LzROejluK1NwbW5TUWxxYUloYk1pNGgwWTBvTnZKaHZ6anBwQjVOWEp0eTdGcmF4VTUrY3FDQXpPVE5tQmc4OGwvdDN1NkpKMFBQQUFpbGRHL0I2WHVSWkJHdWJ0SmFUdmV3ellkR2hzRFhtbmFxUTdYOEEyTUx3LzJqN1prVUpIdVNHaDlnTVlpdUQ1UnY0T2drNjgyV0xtcUpPVjUwTTNqWDQxWDU2RjJKRnVMTVgyR2RVM3NRRHRmeHhmR3RrbmV0Zk92UGJ0NnZiZ0VMY01mYzRmY3IwYVpxbjFlbEdxNVdqMWFFc0Iyd243TXVNZFNHV1BSbjd3dkIveVZBNUFGQjk0S0xFOW5mWEZMK2lxdnRGR2FqR1JuVm1YUkN1WFdqVnFFSDhVdDdXa0Iyb2FMV1Y4M2tnTTlscXlwblZJTlpONEVXZkFBZ3lFc2RBQWtBNGlMY0JZRmZ6dmZFY3RBSUM4clEyZ1A5NCtQc2YrREpMUjZXdFVFRDg2MGx4dmdNMTZReDFRUVRZMjBCQ2JoUnRXdmhzb1FZU1R6MG9lRGdaU3B5QzByOXlsQVQxckRaNW1QaVVPRVdTd1Q0aFhNZ0xJYTlGL0hFT3ordm41d0VnaXBTZ2xwQVFQWmlReVlHL240UC8wcjhuNXk4NlpkYno5dHFqOTI3NTNhUzdpdFBYVDdPbnJLZFdIaHZFb0p2bi9pUGszeWdvQ0R6SFVaSmUvNFhwK2hyVjlXT2tNU0h6Mmg0cVA3OGd0UHVuY1RXa001czY3OEU3bU51d0I0RnRxQ0FRaVE0ckJEUVgvMXR3K05oblBEc2Z6VVk3bjZJMzcvTGJrc3NwZWlhdlRuUjJ6WjNYcEVLWDZlWmtXR3NHcHRDS1V5R0cvendkS2o4NkQzU2lKWjNJWWtmWG9TMW9xVGJ0dElNTUJBWm9ld0cvOVFvRkdnbldiaTRrdHlKU0Fpb0p6dnNFUlJVTTgrWTFMeklKV2xpdG56Y0M4UEhLb094OTZGdWhjdVR2YjlsS3BSd0doKzNZYnJsdUowVkxYa3l1RVRJM0xrczU5bTBsVVBOR0Q0RU9xR3kxZmpzbUVBOGRJam1FVWloQzdpdlVrRVpDejlsZmRvbUVaSmxFaWFxdUhkcDVQUTE2ZFJNY2hlc2ViOUR1anIrckUzUjNNZ056V0Z1TmNqeldIS3hhK0FGRkY1RTNnUWh0cVZvMzFMSXZlZ2sraVVLS1Fuc3c4REY5UzcyK29TeWpkK2JUZFY5aFJCR0ZKVC94c01JRUtKSXc4TlhPd0ZnUkpvd3p5STBZOGM2WklTeW8zRkIvMmFVNUhtTmlxL3l4aEpnRFZzbUMxM3V4Y0RaTUY2dTY1L2JkdHBKTHQrUHIzWXZ2L0V1emoyeERnWG0rYjV2QnJhRTRZTFlFNWdnTnhiQW54azJodmdsRnR6STVmZ3BtVS9raHpjbTl0OHFhcnV0SVFCaUhlYzlQR0RqQktFWWd4bHZGK1BDMkJUaGlRNGhnMUpPbjI4OXR4MUtCV081RnpCSTBRaThCbllDUG9CYUtmMWw2eTlEQkllU1BmMXNsaGw5ekYxS0ZBd1gyR3hWT1NBbGNWSUNiS3VDaERGNHF3VWRWNEtkcUNGQU5CS2syUHd2VjBPZlFjdkNqNWVIM0V4M0NFa1l6azRSSmlFVDRCRkVxSUVZVmlGT0dCSldRcENwSVVUV2txUVl5VkdzWEJCcUlvQmh0c1VRYUdNTmRLcnBXU3lIazJrd0pGSTNpdnFJTWFTSm5aSzFic3kxWjhVU0I4K25qVlR4MGVlcFQzaGZwbmFONE80M1JxNEpUbUdKSkRYcGdzcWluSG9XT2VrTEk5QmRlbElYckozTGJHVDZ4dFZMend2V0tCdFJvWW5JQTV2TlBWTkZFZmFLcGFGRXJLR2hVM0tLTklsei9rbXBOUUR5NHY2T1grVEdSaVhXS1UrY1RKS0Z2ZVhRZXZWN1JBWERCQ2l0VlpiUzhzY2dsa1pyY3laaXVXNjlhVEJhV1FCaUtqMVYrMDhrNS9oRExWZEdCMVdIVko0WE5ZRTVENVRoRFlPTjJub2tJZy9tbXFIcDdYMEd3blJzS3UvazBmTDl6cnROdWhtTFpIcW96Q1E0Mjd0YWNvZUVZb2NoVWROUldlOGVTc0tCb1lRYXAzUkZMRjhKaFVGTHRNSUM3dGFIYlJaMmR2dERyQUJlT0JobGdDS3haaTJqOWlkQklrRFRtK3JqRG5NRWljd3dXcU5mRFU2elZtSGVJK2FjSWlicXZVcUxzNVJ2Z2FsOFZER0xMT1k1RXNqTjhtWUJiZDd6T0dJa09EelhlWEQyQ3FRYXU0bmducWtsMUtYbjFqVWo3OFF4YnJVd1lDcE85K1JSMFRPNldNOUMxSEZrMjhYSzAzSmtpTUd0SzJkNDh5RmpnOFJXWUdxTEIxaVIrc2hjb21sSXlST1AvWWhLQWZBUjhFL1BheHRhY2QzbURUMlk2U3p4cUlLQ3JCbThIcklvWk5WbTd6dld6dHNlcWFCOEpkV2pzWjJlemNvc0M4bjJhYm1ZSDJnajIxNEpyWktvMXp2RVlxdG9ESXRKbjcwV082SUdoMVo1Q25JZWNsM2l1QzR5OHhVOUJxZ1pCZlJEREFoUnFER0lhQk0xQnFoZWcySm9ndFlOZ1haRDZCU2l6SVVqaklOZ1V4TElBUlRZSGFSa0VXNFAvdHRoRFdVamY3ZjN2VU40Nk9zeW44d2VhWkFGZGJ6WUQzYzJtcDlsUDczUTk2VHR6Nkc4T0E4MWhzRG1zT0hOWjJWeFdOWmZWelhWNmd6V3hSMlVVOGY2VFhYUnR5TFFkSWtPWHJuK3pjb2s0UU1CNVlJYXNKN3RET3dBdG9MNEQ5QTdJVXpFQTZBY0V0SWFIbHdRQWk1dFNsRitBd0dpWXBWV0I2WDQ1L3JRcVAxOTJsVzhBVGRIM2J6RTBOOEFuS0RmTTE5ZWJsWXQrWWhERlNUakt5SkhzVFp1YWRXakc4MzZWN3VoNExtYUM0MXcycnluek1XcWsrcmpCYXVlNFVjR1YrOHZOMXJmVnJKTmxIV2lQbWEwOUtkbFdjOEw0ckhWaTlCbXdPY0Y1USsxa0hjK2lmUUluYmc5WkZjQU9jV0RkVG5LM3ZDbllYR0I3V3NrRDNOTFl4NjJvR0pJalp4MVNzK0V6eGtkMG5LelJIUENad2NhK29SM3RFNTZ6czZaSlg5L2FZYy9IQWo4di9GWjJCQ2NBMk5TZlQya1A2WjNFVnZXVWZrci9UZVl6Y2RNR0Y3RlZBUFlMOThvd0Y0QzEyT1oyRVBaR0pxbjJld2N6eDZlOXA1T01EYmJBNlhCV1lnLzJUWm4vb2NNK3J4YmY2ME5aeDdnNjlWWCtVQk9rb1czeFhQNTMyVjh1ZmpmYXFMcGh6WEdUaG9uYm5PdW00Tm8vazZCMmdwTjFOTS9NbEpXVmZEeWxQOGM2RHFCMTlDbHVkRm8zblRyTmpaYlB0cVNQQ2k1d3FwMzZjL29wMXBFMlk1dzVJcXlqWktySktTYkc2K0tFNTRUcDRnYkJoYzh6MlNBbTlvbmdQRVdyb2RzQUMxTXV3ZVlPN0pjMm5taXdIL0x5aXphWlhQV24zVklySDRKZnNWYVU1WVlVc0wrbEhLMHVzS21QblRKUFFlRlVPK1ZGRCtDczZYYkhSRTliSEk4N2xuS3d0STJQRUljOU5lREFZeWtRdHFHZ3lkZjgyakJUWTUrTWNWY2JhNDNoK0dtRGE3ZXU4RXY5ZExWdG4xeG4zS0tQOEl6eFNQeDRYN3BUdmNNeFBtNzNmQ2w1OW16dExOb1BsOWtQVS9vYnJVNjFzMnNRT1lXRW83cU9ValljYXhhL0U4SDB0RzFiZUtEUFQ2WitvUmxyZTg2YXZqTjVXcVhpckFOYklBVnFsOUNOR1FYYnd5eXJkbXlYWlFnMEo0ekExOWpWUjg5cUhHb0NrUnBTSjVneGF4bk9SYldZRnFiOXB3MGRyMFA2NFFmVjRyamVVazVWNkdRZGkreVpCZ0VtMFM2RytjTHhueThVT3dSMU1acHlwM1hXSFdhOWNXTmFheG1WR2RyU2N6MUNaRTh1Z1JaT0REbjNpbVhMZ29ZR2Zma1dHcFBwZUhXOXBIR2ZNV0hqMlFwNzFMMkU2eG1BcVJ2NjAvWHE5ZlhHUHVNTlE0NW5FV2R2bTJadUdkSCtYUG90dkYzMTlWRDNVdS83eDZFaGEva1Z0VzdsSzF3MnRaeHlzaW05QnUzalVld0s0K0QrNTQvZ2FTK25rd2xHTzFlMzJqQnRFTURTVzJpT0xKNHhmc282TkJxN3ZjWVQwdlIrN3hjeXJmd1RDWUh4emZCRVg5TlIxWHpTUDU0eHhZSFRRU2ZrZTg2YTNrcVk5SFZ3ZnMwc0tpQ3BPUzYwSkNZZFRtSVNXcWRwQTYrQzVjc2pmNy96MXBGL2RnZnpTcVdCL3ZHMVg1b3lWcTZKaUR3UW9tNUZFNXB2WkI2TWdqS0FiUDE0Z0w3bGRKT0VWcklrOWRUM2RacmhIUXQwQjBKMG41S2lLMzc5Y3RKSGxDT0pkR25NSm85UlhxN2tEbzlPODFQbm9mSmFpdTR2cUlUOXBhUEo3YTl6bHhRZVBSNkxqYlJtVGlublI2ZU5DUnFNTzEwS3c5ZERSK2xQR29LOHJEbWxIVnFkdk5aM0Q0d0Z4bGxWbFRWYkNxS1VyUkNXU3QzQ0ppNWtrUDE2clhFOGpPMzJiYm9SMWRlOW1QbTlKc2lnbE12dVo1ZFhyem9qS1dkR2hobDJTM1gzZ2lXTHd4L2J0T3E4Z3dTZi9jdVJyZFBWak1sYUhyaGZKcWRsOUh0OHVJempNMStwc3pXS082dWlmSGRERlYxSFliUTR5Mk9WclNBa3ZFUkFmVkI1ck1XcE1GYUN6dks5YjFTVlY3ZFozN1lnTE5OZFpWQXF3cXRpZTJxYTJSRm1lSVJoTmhnN2RaWUZ2MHFqTFRwRjVUcS9QcnE1NjhIRjhTMEN6ZllHWnk4OVMybXorVE04cU9yZzF5WThHZXJIeG5iMW5TVEg1S1FEWU1QMkg2QVlLMHRISldlUVk0eXV3L3VNT1M2bVhHTk8zLzZSSnQzaUxSVnEvWVUvajFWNXVTOS9RVmdrUzB1WkxXRVhoRkZ0cEgxc2hZVW5TTi9EM0Vtdi9iQlBjNW41UFBlQjBkRURYU3N2eHNvODRRU3Yrd2Ywdi8randCT2p4QUtGOUJsYUduelNHUndweUFuQ3ZYcmhhMXV2RS9QKzM3OXdYK1U0ZFBSYXpRSFR2VmNYSXZzamd2UUtuYzhYV3ZkSWlJNVdqZXd6cWFQK1E5SXArcTBHa25yZ1MvSEQvOXh1RXBOQm9xK1dqeW5tQ3pHWitGYzlJeUc5dGgzclhBZXE5Q0NEdks5QzdpdXphVW52RFMvRUU4bDhDU2U4ajVLZE5UdnBsQ05XMHFEMGdIV1RuamZxdnd1OFFxWW5ZeUpmRXNOcmZjVXIvQThCRXgrdGVlN3lBelJlV2NjcE9UZktnZDhHWGlQVGVoWHl4VzM0TXYrVDc2b255OGYrdkhncmxDN1NWeUY5V0NhMWVORFZ0eTdtcXljcjZyNGcvLzNWNkNJK0k5M3NHOU41SXlDVjF1cWxRQitsODJ2ME5Sd1pGbnJETTB2SDYydnZmREdWK25UQ0pmVThXVHAyZUhoQTM2NmhYNW5TK3FSN0t4TFpzSmZpTXNNdUlUa1huY0tMaFBmNWZ4anVUZWZlZ09Ld1Qya3YvUTYrSUdyQmIrejBId3g3c0hGejN2THE5R05WcSsxTFNyYW10dEFEekIrSFdBb0haVDRhSDltZ1NIaWYrcE56cDEvYitUcEJrQXpEa0FUeHVybW80eHQ2TS9mblJZS092YmpKRVBMRVpOWEMvZEVHU1ozc2RYOU1GbE9pNTBpY2d5dWN0R0JlTzc3UjBHSUFqM1FrUmUrdHRESzNiV3VsV2h5VWVYdDV5d2JuUFA1K3BObkxXM21sOG9sdEZZdTM1QXpRSGN3L1J6a0tlVWx6WG9MTGphcjZBSW8zcmVTbFIzRURqY3BlOFVKNmhQazdDNkpmR3QyZjA3SEl0cjNpQ2Q3RDRLRUVRMzlSU3V0VGJpMklPYkNiNXJMQzVqem8rSllPTUZTWFcyaTViNVhYb3BtZFAyM1ZQNlB2K0xyeVFPVmtjL1M2cnVQT1N6QjNVQ2JmVEkvT3FhNmV2blpHdGZFbkhDUytUK0psU3krcFc3NVlPbmZwMk5WcGhnL20xWWRVQ1JtYWVmeWN5KytScWkrV1hWbzJoMUFqTWo3WlRmMUJ4c3FycDFWWHorZ2xWSWZvTXdxZnZpUklGRjY1bVg2bk12dW82VmVmWC9OVCt3S1BSU3VuRENHYkZvNXNxV2pMM2NpUVBLVVVwT0MrZ0ZUZ1ZiYlY5QnZMTThxRngxSXQ3dlJrZ0o4a2tkdTVxandiSzI2NUtjU2dkTGVrL3JaQlgyTDFQSlhGODZWb3o3TDVyLzUwenNhY1RIRnJ1WnNhUXE1c1psNUkvMVNwVkcyK3doOXFjcjgvMWRocjdPcDZ4amIzYVNxaFZOSHMrc2ZYMDVBditRcE5JUkRhbUtOdUJEWW9hcHlPb0NzSW5VUXRTbzhuTkRmTlIrUGN3RzRKeVk3djJFbFRTT1hrdWpkS2NLYk9Rd0pUMGFJMFFmZkRVa1hTM0V1RHVycW41QkNCTVVDWGdDYlRaYURMS2QwVHAyTUFIS2llU2Q5L0pLM1JQZWNxdzVHL0E0QlRLKzQ4MDd6ampmdHVCMGo3NlJrQURCQUFURWhoSUEwMW1TV2h3L3p2WndDRW5FdDJDNUtvazRzQTNGVkFlc0UveDdaQjhUM0lDTXRSMFd3QkVqSWN4aEluUzBzdDg1ZGtGQUNBRy9kTi9CRDVPNnpETGFob0JNbnVWV1FQaGlRZkFvVis4YXB3RXZPVER5QlVtaWFIMENhMThHeUs2YUpzUm9rd0VJWERyamE0SXhhSmR3ZmdoL08vZTllU1BTWjFnSU5LVkFIQUd6MTQ2NWI2VkNZZzMwMEdLNUdBbEF3QUhnWG9Ea1BRZUhRWUFXN3crakFTeW53NmpJTHdRRDVNQXFGQlVpbTlhSmlDYUVmamQra0dLZEhiVUd6QW9DRXJkZW5RYVRWUnBCWlJSRW0wdERMRjNVV2lyaTBxMFdZVnVlZzNVb1ZLMmtRTHlNVVM2ZEw1eFdPRjkwdVVRakxFd2pScEVVd0treGF0YzFXZklRdGRvcC9VckZHVkxodFdwVml1VFcrM1JpK0xsY29NNkxlNktWYUdaSnNhR1cvMHVXUVJ4YlFTZDlvVGhNSjR5NVV3YUFQUlpHa3laU1RuVXFWTG43RUhnVDFRc1NjdUtwVmpYYkVxWGgrTEpqdDF3NFhyeFFQd2t1U0pja0djMGtRUFlabmx1THR5RVRzeFdYYVpKa1g0NUNyUXhGcUFKWjdvcmpGRmkxbjBGUWJmRENUQmhrUW5XYjZLV1ZRNXBGMnN1OGpLaEVna2VxRnNnUzZ4U3FGWUk3TFZCQ2thSmRzRWJLbFRTYlRFSVBudXdzc3JoVUJzdk10VGllSXIwN2pDcWxlSkxOYUs3QXFhSHMxNlMwVllwRVZwcXRDeWJtNjFMS3lMc0lsQkZRbVAxQ290cmhPRGhxNk1OOERyQmZxVG4yQ0pNcFZzZFFUa2VjNFBsMlIrOHJmdi9FNkFBem1vNU1sWG9FRlRRTUF4dUNPSkZEeUJFcVJSaWd6S2tFVU81YWhBTjNSSEhwV29RZzlVbzRBYTlFUXY5RVlmOUVVLzlNY0FERVFSZ3pBWVF6QVV3NlJyK3J1MDJrSnQ4L09UdFVteTFWMjlyVzMvYkNDZkNVbUpqTVF5V1dGVlVidE1Wc3hLV0dsVkdWZGlKcGJJa2xneVMyR3BMSTJsc3d5V3lRcFpFU3V1S25HTFdQWDBVcDdNcEVxMTI1cTB4RDZKcVZxV3lKSlk4cDFTWU9FUlBsOXVQb3k0cC9vUWZrRmkyODFob0lzT0EwU25HWm5YdlFFQXNsTmxFa29Pb2Joem1mbFF3VTR6QWEwMFBPOU54Z1pJc1dHTDNrd2x1aHZyTGlhUGZoM25kMnIzWVdLRWt0WVNBQUFBIikgZm9ybWF0KCJ3b2ZmMiIpOwogIGZvbnQtd2VpZ2h0OiBub3JtYWw7CiAgZm9udC1zdHlsZTogbm9ybWFsOwp9CkBmb250LWZhY2UgewogIGZvbnQtZmFtaWx5OiAnS2FUZVhfVHlwZXdyaXRlcic7CiAgc3JjOiB1cmwoImRhdGE6Zm9udC93b2ZmMjtiYXNlNjQsZDA5R01nQUJBQUFBQUVISUFBNEFBQUFBaDVnQUFFRnRBQUVBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUJtQUFnVndJT2dtWEZ4RUlDb0hnV0lHNVRRRTJBaVFEZ2d3TGdnWUFCQ0FGaVFvSGd4UU1nUW9iRDNJbGJKdEc4YUE3Z1BkWUpHK2VrWUhjRGtoVnB0M0kvMzlOYmd3UjNBNmFWdjlEd1prb0xGUkRJMFBJbVUyMWxHUllXbGlyQ250enNtSGxhVUY4ZFdGT0Zvc2wrcUtROFgzSFkreGdaZnBMeHhsWnJlR0VJNzNYVVI5SFhYNjhGVXBSWWUya1kzUk9HOGI4TUdMem4wWFNaeDBlRmgwaFllUTRFc1FuYVQzTUJ4dVg3M1NMNS9NcVBnUGJSdjRrSisvd05KMy83aUpOb3ljV2JieGlTU3FlSmw2eHBLV2lxYVZZcWEvRml4Y2RvbU40RVM5cTIyQ3dZakt4LzhlWU0zRzJ3Zi9xckw1a21TRGdBSTRwd0pjTjhDaDJZSUM0UHNLaW1hNDZldjBpSlpQMWZ1K1dKUk1TZ2JNSVM0SkJrbUFzUXM0WW5FQVlFalJDdjFXbVdabFpWZDA5SUlIRGNhR3BsQjdPQ2JreEM1ellCNE1CT2hzVHBPTytzcXczRE8rbmNtWjF6K1NvTy9rMUdBWUNoaUJkTnNRTEpFamlyWFhnQ0I1ZnFmWW4zZStrYnRzYTJzU1NLY1FlQ0lOZ0dYNVk1dzh1NmVvSXZBUFEwa0w5QUV1M3BxZk1razBvRkVaaW5FRHkzUCsyV1UxcUNpVEJoMForRWlnMXBOTlpaY1hQZXIydW55NTkyL3pnQU56aC81bXROMUtVWWpTWHZJak5ST3pMeEZpTENrUmg1UmQxaXBxdU9rd2Mva3RaUys0Y3IweDNOaWY3bXU0anJWcHl3UFJRZDBCQWJtUXp3QXFjeXA0NUVYTmlScFYvYjVveVBKL2JmamM3YzZMMWw3WGViUmlvRGVCN0hFNitUU3h6QzRseXE2MVR6OTdaN3UycGNJQXdEWDl2YXRxK3Y0QjBPRVdjUXFwQ29weENyNUJxMjBYbnpwWEwzZmMzL3QwRmdROHdMVWdlYmtIcWFPQnVEQUpuNnJpa1RnQUl5c0R4RkRMbGtDTUVYRnp5Rk1DVFJrUGxURGxYSVlYZXVZMmxTeFdsdXpwVkhoVnQ0VHJiakdhQkltZXl3TkFYVzBzYnlOVWlDZ3ZCdEY5MGlKWXpYZnMrR0xBa2xOU0d1SENTenZ4Yll1aEJ0ZzBBcmo3L0N3ZUFYNDREd0pMWFc0YSttQVBqOCtTRHdJKzc3Lzl3NjVUY2wyOWdvejd2cUh0VFhTZjU4Z1M4ZmpVQW9MZEhBS0MyWkFteEdNUm1RcnlFN2ZxdmJodzFOckdadHRwanhPa3NNOFYzZmVnbmZoNG5sWGdaeVZpT0pVaHJoZXM2dXdWQWJmSE1yM25mai93cy96L1lUM01GdjJpZFgyOTJ2Q2Q2cU1tL2VYYk0wUkhqZmwvNTNLYys5cEZZSk95ajFWK0RmSXd5aFlKVU5ZTzh4UndHbWkwSmdCL3p1M2hPOTBVVk5aK2luakdvQ1RpUHN5UTkvUWZhOVZPNDIwK1J6aDZsNTd5bmV2NEU5dkpmU0VKNlhkblNlUTljQTRGTlIwQXB5S0VEd1ViZDY4Qm9MYjV1SU9YVFdNcXpZTGFhdmZrY3ZYbFhyNDQ1dW9vZSt1ekVvTHhpOEp6VTFKYVZOYVdrSW9zSW9jVmF1dkExUjN1cW55MEF5Y2c1dHRIS3BMTFFnOTRDeUNrbEI0cVVERGlLbTVRNkdEUVR4TXRhc0hvVllTM0FXWERlSjFEdElKZzNyM1ZqRjBFUDJUMytTd2p4WEFZejk1VnVxZDZYN2U4Y3JkVUhTSGtkOHl0WDNxUm9xZDVEYitQRTNHakp2aVVaendLdlN5UGc1QUNuODV3eUFUNVV3SXM5TmI3bWJ0ajU3UEFvVEkrczFUK1NYb1RoUW10U2l1Y3BiMHBnNUpjaG1JV0xQdDBnbHFiZmtuQjZVRmdsQmZFY3I2eE8wenBYSE5MNUZLT2xlOUFNaVRiZ1ZxWk8xa1VHaU9IMkVaandBQXFPVXYxUlhtS1cwVHZ6NmFLWExhZzAzRm55MXFZZEd0Q29wRnhQQUtTaDJPQWtWSm1jRUZYY3N6ckNTTXBxK2tmU2EzVmE1LzdLcWsrNkJnR3JaL0QwWFd6a2U1cE9ONFYvN0ljZXlnMjhqay9mWG1QT3o0RkkyekFOZEwzWDliY0gxQUo2cEZIS1lBOU51UkYxZGRDbUNjWmZBVEY5VVVneVdUK1BIZmZNdlRXcWt1VTBBRXRQK3NoTktGcWdrcFF2MDFtVmRPR01EZUNRVFVFQ1ppUEs2TDRBSXV4cjRQTjNFZDJDWnNnV2tBeFVnektvL1RZMjl2ZTA1SWY2OUI4ZTdQZ3AzS25VRWFWQjVGS0hTVU1saUFrWTRnSURDWUZBVW1BaEpYQ1FGbmpJQ0FKa0JURzNqSHJRdXdZcThCdW93dCs3ZEk1OVRFV1VKRTl5WkFNWkxBR0RMVERnQ0FSY2dRVlA0S0FnOEZBVUJDZ0pvbDRqR0tCQ1BTOWpWRTBFbTBCR1dSa2dWbFBjYWcyb0IvVzU3ZDlFV2xUQWhXdGx5NHlzOHBodTU5UGEzaUtvT2Rxa09XOXNObWZ4YXRxaStRMDJsUVdrMitoV3g4WVg1RGw0WHNZQlJmbUp6N2dRK3dXL3ZuNm05MnlydFRPOTJORkJneFlkZHdPQmZ3RjFkT21QRFIyOTRDaW9KaldzM3FmaEZEdWozaHpBMGZiUTQvcDVhWGVtS0M2ZE1WNFdDMk12SS9Qd3hZNEJRQXppS3cycWo2aExtOW93YWFnRHdCR0dDZlZaWElpRk9FYzhUYnd2dlk0LzhmSEJyZnVKeThFb0JSZk1DbEoxNHQ5R20yZUlob2xsSjVMdHZnNVdYaHVNeXRxbE1GYWNzT3hDWlZkMUY1TnZpUlNibHkxbkNKcURmVURybkdQV20yVitwNU1EMFgvZHJCZll2QTZrN01uME5rS2VaWjhycXFhbkEwWWhpSW5hd1JZVXVtM1lLZ1RHajROZ1FkSWN5eUZmYkhLR2JrSEJkRk53Rm5zTjVoMzQzZE1FQzNxTTg3am04ZlZYcG1wQ0RTdmNNQk95UGUwaEluSll2a0NYM2FSS1p2Y01ReUp1ZGVkdm9vYlV0MFltbHdxTE5vLzBhaTAwZnVLbzF6K0hRYVQxK2pDTXVuRTh5T1djNnVCbEdFUTlEWEI3b0RFb21naFRwa0Z1YnlWTUxXakNjbm9ITVQ0a0Z2ckREYUREdk9aZ1c4NHozdUZUbDJ3Smg0cUFzT3J3ZGlnNzBUWk4xcFk1bm55RVNkUVBwYTJFT3ZOanNqVzBUUU1VeHVpS3ZvTXkrZ1BZM1JqRVpxWmU4K3EwVFBFOUpzZ1l1OEd3ZlVZYlhHQmhaQ1ZBV08vZ2htd0UyVmFMTElVZEV1eDNjRU1PZ3V4SVpDNmNrT0M4Z3h0eUVXUlhJb0Z3UTRMN0RtN0lRNUE5aWF5RUZ4SzhkM0JEUG9JTWlQZ0NpZ1RZRG00SUxyaWM0S2VXYUVhS3NaL1dGS0JJRzJqdVlrUTZ3TFRYOVlFTjFvRUwxazJlU1FlRTlzWUx4R0FEcEdBRDVHQURsRVZOVUlOTjBJSk4wSVBORVFiLzRhYmFmTkw5YldUbzM1OUlMZmZZYTVVR1NSQm9TdmRaUEZzWWFxem5HQ0MvdVlEcThIa1NQYUQrZ0pudzhSbG40V0NCcXVFU2dXMldCWlUxYTBvVFArWHZNWVhhWWF3Z1ZIU2UzTkFMQStmS1l6ZFpKNW1QeFBwWHl2YmhDRUljN3hTWGl2aTRtTUJFK1loWW9WVjFxK1U4SVVGaXhreWZoVnVZU01URThpV3hsRkFvWkJTaFFubTBJQTJ6U3VKRWt0UXdvVDh2dnk0eW9TeWh2QkNYV2tTNktKK2tLajNIV1ZsZGFGTVR0SUduU2hmSGlrM2hXQ1EvMDRrUWJxRlNZQjZqcmJIVDRqaHhOSnJvejBoTmJtcXFjTnI0T1VZUktrTEZnaGlsMlNDVkVLU0tKeFlZYzh4U01TN1Z1NkxWY3BmTldpSVJOOVNxRkZTY2dHTitnYWRwdUJlVnpTNythQm1LNHRRdHdiUjR2OThDbWtodFNMZHNkdXdXb0lRVjRpQXhVZE5pdmRLUTF6WGlHTW9FZ2hZQnBDaHg5Z1lUN3RscGFvU0EzSXZkT2pZeDlZSGNTZ1NxMlhZL1VqYm1JUFVxSWFrZ3RabUpXSzRtYmJIZ0NLTXRPclgvR200cldYWnlKNnNNV3RRVHdGdmpDcW1JMFNlVGdTd29QSWdoa2tzY3ZKczZzNFBXa3F1R2ZseGtvd2hON3VFUDZvSzhXNHJVYW1paWQzSTU1UUI3Zkx3MGI2d08ra1lwUGl5YnlScGI0bzk1RTBDWWM1RG1UK29DWFpJS1FGM3VRZDZMSk1ubHIrT0tBZWcvVlEyOVRaUWplY2hrVnNxUm5DcDJWcFlBb20weDcwZkE1dWI2MUxBcVZ5UTh1M01tTEdnWm9hS28zSGw5NFZKQ0o3QTlDSFlWN04wT1dySHFNNlFuQXlPUUpkNm1CU3pXelFLemtQb2NZQmFmR3BUN2JpeTNtdTE1OGk3MnBpL05UcjVXendCdk5kYzY2TElOS1RhU1FXZzVTSWNNTHZuOEdCTG41cVlPVXRqL1JlZENwZGtYR21xa3VtV3ozWFhuUlJIM1VhVXV2bTFUOGtUM0lGcXNCU1haNFRZdlpSaU9BZERpM1lkWktBaGpRckxIOVYxOG9hb1VFdyt1VWhRRUNtNzZCOVhrdFczV05LVUp3OUhsZTZRaGpNMXQyZUJkdmxjRUFsM0M1blBkKzFld1lhQkJ4RzZQOUJabU5JZ3NGc2p0cEZEM3NPY0J4TnJNc0NZaFV6M1BnZjhXbHE3RGFsenlTd3RrOXRNV1l3YU1UOWNqWUNJNmp0QlRxZFNOS25leDZlME5rZHpDaW1PeE94dGpTS0xlQ2dFMXd1WjNFMGk4VTZBelN6Qm95cUxFQjExSzdQUTBmZHZUZThzejBUbTVYQ3FSU2w2OWhNV0psbFhwVGp4N1RjSjVac2Vtcmx2VkpkT0Y2T01pa3dVdmwzUWdNZEpDQW1iTjFkNm5tOFV1dnNKRkhUNzNDbjA4YTAxQkdDTEZzd0dpeXRZblZ1VEVpVmNMWkREWi9uTE4zZUxENlB5VG1qL1hJUTVveW9TbFYySzh2eFRTQkl1WGRPU25KN1dQczB0N1lFUkE2Wm9ONUxUZWErRm1WYmVUL01uTkRrRlZ6aFRGOXFVM3ZIU1NONlFnOFBFdTlxcjdVYThkV3UzRGVPSVlMcjN5VHpzU3hSd3VRaVFrUU1xZEQ1Y09FZkE4UmI3N3VWcDE3K201NDl2clNDeitPTzgzay8xVzJHY3NPclBEeGU0dW1JS2NuTisrcUZxUXdVUy95VkZ1dFMra2t0TVpROTJVUTVwelE4VXdIRERIYlRHTlV1RktoOXdyWEhvbDZzZFNGS0tsK0dqeTZoZEljNHBkdlhHRVBWWUlSSGF5YitkdVppUkc3L0dtbzc4K1Ezb3cvOFdLcHVjV0pOaHZSMEFxVkhydDNOam1JMFRjb1ZjUy9jQ2tzZHpDTDdNNmtmZXhrNnpNbXV4b1NYM1ZQWTlLempqNzc4Q1A3THFkNGNiR201SjNsMGg0ZVdTVWpHOXkwN2o4ZHRKRTdUdmtsdFRkRWFnMmpGSzB5Z09rZnoxTFFQT3JFVU9kcFJOK1dFQXkwK3NUd0hvbkZ5NTFVcUFjcE9ob05wbGNMazBtSFJTTjJHVHV2MURRQjdHU1dYZXg4MUZ6S0VJWVlkdU1yTHZHMnN6ZW1oRTJ3Vkppa3h5R05IbW1PRFI2dnpYamdrQWlkV1RINVYyYk40N1l2dTNMZ3AydHlLR1p5YUJVQlJRMi9wQ2ZMT2VXRzlqd0t2bVV5NkswcVUvZGp5TnFIRENaWlVudmdSZzJ2TlRpTmNxTzlFb0JIL3NVYXRJTGo5TW5nSEF0dWMxWDNsQzRrcnd2Tk43c0xyOTBFeks1ZitURzZ2ZXNKK0dTTjZZT1dUSGNwdWQxbDdyNGludXpUZWNVOXlLdG1jVm5rZGJ2VmI5dVp0SmZ4Z2M3WExNUnRRRjhRTC93YUl0eHBxOHA1TXd6dktrSDc3cWJsTTNpNUFpWFRKOGNXYitpREFZQ2wra1ZuMUhlTUtuMmZmbjBBTExoMHovSVNlZFZUTldpWE93SXJ5SHQ2R2RsVHBMc2t0Z2JxTTBsQmtzeUl4bEdETEZ0dE1mSFhuSEtRWE9oVTVSZHI1TkRhaXJIaWlKTXJDdDNncjBWc1ZWeXl1cHBtUFpYWWxsVjRJeWdyNzJZWEx1WG9HYUdvZGFYNEFnTjdYd0dHQ1BIVFhUUlFJaGN5RjhpUjJHZ25XU1dTNDVpcGl0WEdselhnK0phd2tKRnVhNnErUS8zcHkwM2tjV3Q0dXhKdGp4YytCRm5zVjI2NjFwNzJNeFRrSFZSTUFnMUNSRmhseXMxdGs5ZE5RS29EZEZsaXNsa3FKTTkyRzhDVzhXNkdReE5OblRUOUw1YUdhSEZBdzVJeUZTOXZPQmN2V1BTcnZweFArMWxSTDdsaXdZWm5mN0piTVlQTDA5Wk50N2k3TCsrR1ZxeEwrUlE3ZUpxNnp4cGdoajkxWmIxNVQ4Y0hJVU1qT0JhV3BoaFhqellrWTJMZXQyTE0rdWJ1ZnBLbkpVTDZTb2w4OFZxc1hOV2ZWOGVNZ2dTVEN4Zmp1TENuY1cxQzBOa1UvMTV4Vmc2dEhiWm1mY0RhNjRUamdIcEQ4L2tmWnlacUxHVDI4V1dVeWZTYVp2Zi91UVlKSFAwckRoYmMvNUMxRjhGRmpibkV0QXViYVdyc3o2OFdldndQTUo0MnJpY1pRVWNzeEhUS0lZaUpsWW54bC9pamFtc2hMeTQzcmkzLzBpOThsWjY5UEJOWkRkR2VuU0Q2Wk4yODY0YTBRdlArNnRnaHo2WmZrTUFVQ2N6QW9LRlgzekJ3VjdRTmRjSkptSjJKMDY0NGIyb3FIckplOEVtSHAvKzlwNHhQN1A0cC93elpDemNYZitKVVpyS2kzOFVna05kc0s0NzljcWJnM0pVRHhTWEhsNDJNSFhjRDZOMDFFK2hoTVVSWG5LTUkwczE4aUZSeDc2UGRyMFBFd2NPU0FPdFR2eWZWazJaWmhFYUM4YzBnUGhBZk5jMEFDaEQwcE9TNmFQYUFiVEF4RWRPSFNWdzFEc04zREg5dnhtNkcrNHpLdzJUZmgzdTBoak95WmsxYlcveU1jNHl6cVRUWG5hRjV5a1pjWDZKb2M0bkxtcktSYng0OGNMaklXajhBV0RKamRQMGsvRnlrN2R6OGp6UU5BSm0xTTJDUHZIQVE0SFVQdTBFTlRUZEMvWWp6eklaY2FHYy9oMUorUVUxK09ZakVGVmtlajhvbXo5UkFLcUVmU2Z0L3R3QjBmTEI0aGNtMU9yZitEU0hUcXY0cGV5RjZsN3h4NWJOZkJVclJ1Y2hjdlV5S3JVamJPSDgxYmc4Z0g1V1kzckg1UmlkVXcvN3dDZkZuQUM2ZkVGSDhmT2lXUzdJQlhXVW5ZOXhrVnhHSHFIWW1ybVpKR3N1b1Y4MkNTZG5JS2hDS2lwRTRXdDE0REZLR2p4a3ZWSGhTbHJyUllYMUt0eHJGTjVkMDhEZTBFby8xK0xzd3VPaGJocVVROENLeGpSNE0vMlN2UFAydkd6MnhHQldFTEVrczZVdTZhbCt0TlBwZTJqTWMySzZCL3ZJZzhXaTNkVVBZcjFqdmk0OURzL3UvVHIyZkZqaXhpVlM1WmxXRUIwZWVDY3VhV1RoMmdqcVQzYlZrWTFtMWpmN1NsNmVQTUxNamlaNHU3WkNCdEd0UGdjNHZESmhxM1o2ZktQK1JEUkUxZllWb1ViUitlM2tUZ0NQNUxlYU9VQUxYMUhiOW5ISnc1d2JkWDFvYVpOelJwZDRSYzdha0NYc3BHNU1ZQy94SUUrUUhQUSt2bDhnMVVrVG9LeERyL1UyWk5xRFpoSHpIRE02bHJSVjFLcHFnRENuMkJoU0pxdTFkZVg2U29vb2ozdURzcmxPdUp5NHVzOWRXWnIyMFRSYjIySTgrQ2NwYlZ0K3oyNkJVR3p6eGdJdks0L0JhS3FDZ1J2N29zQStLNitOby8yRGpHQm01NitXYWEreEVqWER1UkZWNzhpZ2NrM090NHFUaksxZnkyTGtwc1pYbVVqZTZsdWxZTHg4dHp5bjNXUGFGR2twR3pIMWFDcjdJMlFXRDloRkZVZC9lSVFYOGFrMjgxcXpYQlZFM0F1RGNteUVmZGcxV1ZmMnlDeU5OT1NER3ovMUYwMyt5aXlPMjFCc3JLUWtWQ210aGVFa1A5YUV5Wk5ESmlLcmZ2L1Q3S2w1Rm9jbm9ubDJsQW1SQ2lJNUx6dG83U0h6aXVybUEwMDkvanJGVEgwdW5jWlh0VG5RdEV4bTkxZ1Q5ZmpkRlI2TWdSaWxXeUw3cVNIREJVa3NzY2cxZ0JFWmRqNTNPZnlJM0ZZbVp6di90WmtqTm1SZG9NV0ZHTEpnaXR3RzN1WFJhSEZ6ZzB5dGtENDJyUVBVWHhYbG8yVXV3UGhocEhYbU9RN3FnM2hpVGtkSGJOZGsramRPem81ODFDeTc1a2dYVXk2UjVhanRpNUtXL3pWeUxwR1Rld2lnWXdRNkRVMS9XTlVGUkx3SDJuamMvK3ZHZUE1aHNzOXVqbGdXcWdjWm9XbW9FSE9mcmFybmREM0lPaVdyUnFIcHJSZjg2UWhFY204eHl2aFN6b1hERE1IT0tLU3pHbE56S3FocWhLR3FvRTBpSU1iTTVkYlJQR0JhTFBITEpWcjlWdlFFL2k3V0VpWXRWcWJCMEUxZEtCOW1QRW9JM2hPTDFGWHJVQTFDbEZQWThKaGhDeGU2S3Y2MjYyWWNENkFDN0c4OEJiZGJwVkUvZUViMXdaVlRZeWlkSkN2aWZFUnFUZllOZUw2aElPSzQrTEJDSnJ2czRVS2RaRUhhWHp1SW52S2xPNWQ0N3E2RTY2dHdzdzlsbnB2OEs4UWFXMEZXMDhTZGlrWkRlWmNuV1E3MVJPZnVSU2Z4U3lrR0dWcEw2SlYrRjR4cXVlREVwcWxYbmJMRTIvenR4LzNEeGlPcDJuNEdnTnYwMU92b3YyL0k2TnVHdFJMNzRCcDRsdGNab3E5d0czbDFRTUJnTlNBQnBhUVhWZE5tbkhVMHFMbmhTb3NGOHlISjlRNmlTMTR0ZUJuWU9iM3dTVlJkRHptM0l4MW01Z2JhSnZVY25tTGdnVWZSK2UxMTQyMmt1dUZqTjFRaFJMMElMdlZkTFloYk10UGNOS1gzRFpZS1pKM1hEdjEwVmIzNDVNRUg1azN1bmoydlBseU5XUzEyaUNqR3JEcGFFa3V4RWNWTEZ0SlA2UGFtV1VYMFZwa05ySHpMSEVmU1lqSDcvOU1HYzI3ekZrckl1ak8vRFdrc0xVTjl1eWczcldLZHloaDIyc3V5K3BQUURDaFBnT2sxdnpCVmtETjJZTnRpUldLZno4citONVQrcGFLZ1RWUXdNYkJObUxjVkowLzZsU043NmRUb29KY3I5U3F5SVQzTFhtNi9TMEVseExKTVVCUUZlV09kUnZpVXJnTy9PeCtEWHJBYWs5RlJSRG5xaVJpV2IxaGxTZ1VvV3Y4TUVZT0dBU1JMaVpnK2cycWRtbzJ6ellDY2lTdURmUkNzYURLamNLSmxnUlEvWHp1anZxb01NclhNL0NaWmU2anBmWEl1eTE2MVpjaFFxOWRiMWtzVXpKUFNnbzBvV0VXdEk4cDJORXQ2UE1FajJlRzVJbE9yMWNZR2xTRUZhTFNOY01jd0dNUGpPbEYxWmxhY0pLc3ZXZk5sNXF3NTYvRTVCQzVxdTNoWDBQUU1oZHN0aHlBMGxuYmgyM0VJQ1lIMjhVQ3gwOTFsUzN5N1lVR0Z5THBISElDMktuZXVMcEJ3bjgvRERvUzFTL2FWZnFNS1ZqNWQ1YkFYeVhNajdkaGk0OXlnNmNHbnJpaFJERWtuTllaQzFwNzJnN1hBRFdnblo5Y1lDd1M4T3dJaGtkZ3pvSnhPRFpYRmcxaE5BWEhmSzA0dFNRWkhxKzVNa0FFdGEvbUo0YWlmekJ5bys2SkgraUJBQm1yTDQ5a2lYYW40VEVSSm9KU1RycVJqaVlzZnF1Z1JhRkhUVlBHaHlqZlByaG9nZkppSjdtT0wwSW5hYndzczVGcXkyV3c1WXVIZFNwUktaSUhuRnZZcXp1RzZrTGcvNmptSDJTdlRkM2Y5SUpXOVNrbWZTK1dTbU1sVzRxT29iMGtSdDVGVEhmRmFQNEgzdnhiQnFlWUdSa1RUM2plZFduZVZLdzFxVHdFUEMwYUdJRGhtWkRFbEMvcmk0NjJKb1c1aWRzenNESWw0SWdkT1RLakpyaVJGRnZseHZGemg2NWNTbTlSYXcyMk1qZ0hOYUprbWllQ3dwZjIrK0RnWTVUcENuUlp2TzFmeUdoZEtTS3pNU2ZVRlZUZmhFc1ZRU3A5b3krNjlJNzhnbzg3VHhOMWt3UGZWZ1pxVlZVUlV6cGdZci8rMFRhaVdheGYyazZwV2dXSWJabXpLR3BPRzFTMzZqNkErcFpDblFhUDYzcmE1NS85SUtvd3RMVXRTWDBQK2U1cktEYk1VNnY2RkdqbXJlWFk5SlMrWi9QUEVQUkQ4dnZxOWlVaHNWUTEveDFWTjZzb0VBbEJJK3NFQ2JYeThzU0FWMnNyYTV2WjQ0L1hlRDdxRnFyVkJYVThLaEdMM3RiSGZYUHhVa3ExaWw1Z0RzY2kzaUpXRVJMaGgyT1FWdzZ5OWM4M0czRlExVCtIcXk0dlRlZjlwRkNvUytvSzVFYStZZzRsZ0ZuSFdmNmVSMFhETG5WR2RnbFhkMHVSYWtwQmk3VS9IVERrVGdzcGk0eVlJUUVrRWlrOTluSnJzcDFJcHYxVDJ3cnhnNjRTQlZLM2dIQkttSi81SHNBdXIveVRISm96dTAyZUdYL3BvdWRMa21XVTVGMDE1VmhhbkwxR3pZTVZOUmhaM2JPaXpIcGEwdUhJUUhxdTdHcG0xR1JHRUY5YmNSQ3l2VXNLbisvNkw5c1VXalBtYXYzeFVnQlc4eXUxUllpZ0EwMkhIRTVWR2lmbWRPZjZzMm9zVE4xWU5sUHk2WTlnc01TclpaQnJmRm0ydzhaNHB2THNVM3FpdTV1eHZWT1hyVk9FU3pZN2ovcXlhaXhPWDVYZWF2RjVUTkVLaTFLSjNaMVhMdHpkRU4wZFZScFlWcGRReG56eFQrdFYrcFZMSVE0VUNGZ3VDNE5NaTh5b3U4aHBHZm1zT0pJTFFFMHUvaHI0anNDQVMybk5DYUdCQkVJc2xFS0k4b1ZMVkpFRktUZDloMEpNbE8vL1duamMvSm1LOEZDVnZBSisvcE1RWFRBejZlSlFmK0c2UXFIUTR0YVRmMG4vL2lRM1Z0R2pDUEtySTlGaWJ1OG8rbXl2NDRVSWs4Z0Y0aDUvaStsaVRTaWRlRDN4ZytTREFYSGFyS3VUcU5lcXcxakMxQzYvekk0bGlOazNrSG1sZlRZbGRZaGJORGk3empPR3dmU1dMWTJVbGl5N1cxRzJmUE9tSVQxSVNWVEwrVS8vSHFORzliZ0Z2dkdOZVNZc3VKOUtYSG1wYmJGdGZWV2lkRTN0UFpCamFqWDFRS3lZL005OWpmMm9vbXJGUEpGc2ZtcHprcHhTeTZ0aXhxWlEvS2ZuS1pVaGpXWkkwZGkxelM1cnN5dE5raHJNS3V2T0wzdFJrSldVbFRKL1N3SUZhZ3dJTU5RMEZsV285VWZ1by81MmxHRUlrM1Q3UWVLSmFyRXVyY2psVFBpRzJZc3orcm1rWDZEc3k3ZEYvN3VjSTA4ZU96OHRXVi92UzE1V0ZuT0pnREMxMERsU1hWZlk0RnNpNTlDMksvSmZtejBrWVU2M3p6R2JpblJsUGJaRTZ1MUZKRExudlE4NjBUWmtVR0xHbVhBRUlBc0dzRkNINVE4MjN3Ty8vL1ZUTzlSc2phdWZNQnI4M2hEUjAvS2VCeFBlRWNBay80dG1rMXBnb1A1MU0rNk5pYWlPTS9ybVZSckdpY3UwdnlmRHNXNnhHNFA3TmxqckdrUlp2c3VVY1JRbzZ4eDNITVB1dU5OYm1UYXk2cHh3MlFvNm85T2hHMElJNk1RTTdoQzJybmc2bUg0QldENmVIcTV6dUZvZVlFNW8zdGJWMmZsemRlZE1uRlBrZGhVM0I0VmZPM0RKcmUwWitXakp6amhHYU1vMmNlaTc3YXFnazhjUDhRbklPbTczN3IwTHM1M0g5V29FZHBlSXVQTC9admlZTURUU0o2bmZOcnM5ZDhxMHRpZ3hQS0VxTmpacCtRamdMMS9xQytxUXc3aGhYY2VGb2FKUWxwbFo2cVdkalYvOHpBVGJVTHlYTXcvbEVUQ3plb3BNZExoU0VVd0pKN3N0WHhZMW5PVDlGSlFUcVNFb2J0ZlVGaXZTZFhuNURWVWFNMVhFUlVhdUJ4dmMvcTIwQkI2TnBvYk92YWs5ZmxkQkoweGhuZzhPdVJZQUo2VG81KzFPbld1OU9hUEthalJINTVzcDhjMzlEV29FOVRtZ2Jkc2ZIcHhVNjQ4ZzVXay9XQW5oNVUvVEFXVlRnNEVyTjAyN2F2QzMreVFXRkowa1NtVjZVLy8zTTYrcVNzZWVpNlpzMGM0SGkxKzh6V09RNWlXaTdHWUFJNDZZOFEzSFkzRGtES1RueUtJZnhoK2xzQ1hPcC9mTFk2ZnpNYnZZMFh4dlc5bUJPemhtZkh1WlJoaytJTDVvZmxBcysrVlMyNFRFQTdTeVQ0a0dWcEZRVTB1UXZlNXl3VDdYUDRuL3M2T0dKU3lYaWMraWlpclVOcFpFWHppVWFwc3V1ei8vQXNuN3Blc1cyNnpMcGdPbmExYzJFOUFOUGFpaWlGZk9NVStjVTV4SUdJLzFOSVNLbmF0L3RydHFYbVBWOVpudTJyN3QyMEd3S3JJc3Fpb3BteEtvWmY5TFU0NlVxblpPcW5XejlhZ21CRUdWQmQ3QmRSTjZPdXpWemcxUWlXeEUrUUR1ZlRZdzJUMTF3T01ndkVkSHUrZHBYUDNmN3pRbTVoUStYUnhTaXNibHVoZlJCeXl5RnJjWHQ3WmprOWk2STdDS1Q3V2ZrbE5sSWlFWVRjZ1QvcDhXMklIL1J3WFovdWRhOTE4NFg5N3lwY2taR1J4L2pac1o4MW5ZMVh5cWg1TFM1dUtoY0pjR3JkNmsvaCtDT1Njc25kY0FRQ3hhb2QxWGpFcVczdU5oTXl5bUpGSWRCOGorcHJQQWl5cGRxVDdhR3A3S1Mvd0hhZnpZRVFDRUsyVUo2Y2pyY0NhZlRrMlZES1lGNTZtRVRTWS9TcEg5MUxvNWdTOThCaTVlQW82Q1JtZllEMEVhYTNTdUdJM1BHcENnM2ZPdEl2NzZOd1J0ZGptUFd1NDNNVVkycUxyMkIvcHROYzkwM3ZRVmdCMksvRVc5bitCUG52alY1NnJSak5ZS2Y4OHlBYUN5UUoyZkZCMktxbTZpdUlUTCtJS1FEOHdWeXcvY3BTVGR6UE5PNk10MS9aY2VtMitPd1RJcFJVYXo4bnZFNUplVXE1a1VUVG93U1dQY0xSc3lxZXdwbVRnZXpIT0xuN3BwRkN6MWovckdKVmt5TXJzcWZ0N0Frd1RTYWFMdVFNemtheTFTVmM3bWtvaVRDUk44Ukdva0JJbi9ZVEVqN2c0b3c3V2ZIK3hBYVpURC9jRDZ4cEdYVUVJMzBqZjUrTVRyQi9mcjd0ZzBSdFk5T1ZTaVIycDErbWhsbGFIL29LejBKQzhxblpHWWVQTlV2TGsxZTMrVExualdsOVNKczZUdTM0UFpuWktNeTVzZTZZSUk4YVBmMGRsTzFSOVBUanBKbExSMS8vWnlIVDB1aEdWdHVKR2NoOUwrR3FqL1pZK2JaemhZc0dWT2NQc2hMbzZKWDFta2c1ZVYzbGhpblB0NVhLeVZlV2NMRDdVOUkrcjExZ1hlVzRDaWVNN2VKSUVkSm9pbEVHcFpzTHlzTklDNHgxUGNjZSt1WXgrYm00S1l2dnJiM2JvNmUzb0pCZ2kxOVp5L010SnR2M2J6L2VIUHJ3ZFZOcUp6MloyV1BabWY1YWJuS1k5cTlDNTJuY1hPa1hMN0xzY1hYSW0rb01zdlpMQ0ZpbytTaW5ubXQ5UUtHMzAweGQvYXlDMFIxYVZaUFpkeGVFcEdlcDhtUDZkQXNVWVQxZEk1azEyMExZaDRNcWhoK1R6ZkRtL2lNc2cwVTVPZkwrbjdXN2tJOXFPd3M3MkIvUXNocHY4ZUxldE0zRWFTcjBsMFpGMWxRdUJoUjIvbU1vbkx3blNVNGVWc3JyMTd1bmpDUzVON1dTK0JOTHhpbE55TTZZQi9YdGpaSTZReWpIMzJla013L2J0ZmJqK2VUbCtVTzZFamY2bk1HdzlYaHdmbHE2YWhVUFQrNllZa0p0V2JLOTVKVUp0dVRzekpxa2cwekloWVRiQUN4NVNtblNVOUI3cTZkMWlRL0xTZGYraVNqRXVRcWN0cnNjSnFyNnYvL1BuZWNlcEorc1BKVTVxbktnUmk4ekhIYysvOEhoVDIvMGJ5dG5NMDBZQzlkeDhqeXBqUWRpbnFieEx2NTJtdnE0emVqM1hpemIrZHF2MHhsNmgzeVU3YkpnWkdvZHdKbG5uNFZyYTM1dHRiU2tiWFhQLzdoK2NlOUhzRnNqNkQzNCtjL1dKUkhOODZLWHYvcTBBbDB5b0gyL1ZkN21FVDVnZ05UZW16NVlZTXExNlhUL0hrOCtVK1JtTklPb3FGOHJzWGViRU9WQXZ1TmhQR0hYQ1Z1eHhzTTNZVDByWFZ3UjlTRWhTV1hNNy9NdkZ5eWNFTFMxVDdCZFZ0akVEMXp3MklEdldkemJ3aHJJWS9yaDQrNytjM0liTTc5TVBEaGg3M3p6dllHUHhIbGgxbUxwRlpzL0VleGJFSy9PaldGSy9rQ1Vhc3N3THR5QjZ5L0pNaFRaaGJJczdBSmd2NGxtVEN3NWp0MzFvZk5pamkvYVB0ckVIQkJBNXY4d2xBbUhlR1llRVRlS3IvZS93Y2VaN0tGMXlGdGRCSTMvTWszdHRpb3l6citPNUNBZFkxRVg0b2U2VG8rTW5jU3J0ZmNUYmpxQ0xia0dUclR5RjEzcmpieDZHNWlGMEU2Qnl3RDgvbWlKcnE0TnJ4MU15MWdmbDhhOVUwUjZrRzZwKys5dXQ4QWUycnE3THdBMnJ3bUFXWjlndzRoVkRmaGp6REdqUm1TTWdNTE1HN3IwcWp1cFMzVFg3eDlIbFNhbHVhUHlSL0VqSXV2dVVjbmFjTVV3YXYzWnk5TDFQbUF5NldVSlpGbTVxR1pkRTRhZFkyVm43M1QrbmVUM0M4UHpldy96SzMzTkpaK3A4N3RhdWY2dVlmM2M3WVUxRlJGelhGRUZnVzZyQkRVZGZkZG45cjM3dDJ1YkFEV2pvVCt4UkVWdHE1T2VEK1RmazVMaUd0SXJpUDF0NmhRTnN5S1RQM1ZqT1pkSnlUVWN4cWYrdmlOWnVkWWVhZ0VyN21HWTFaZ2hZbjJoM2FyL1dFN1lZV3pBSTVmcThFbHFuSlFXTENtdE40OGpaS0phVG50bitHbjVicjdQQzBzZjloYi9qVkNrdmJqMDR3TERZY05QQWp3aklhREM0M1RqdHRKRXZtNnZGZitVQXZ6N3V0VUhwUE5MMjRRV2l3ajJVSVhka280bnAwbmhPS3dXbkZld1hIUDhRSnhIbFliQnduWmVjTHg1UUVYT3hWUjZrd1VuVFlsc1dNaW5OaVcxRWFUM2JLSUZ4WjIzWUx1TFI0OC9NRUh1eitlRi9yTFBycmtTV0ZCQ1IrVm5EYTNtZHJZMXNuWlV3T1VCMXcxYzFGUnVVcDNmeEFZaW94R2l0SVdRU1l1K2htQkxyMmFkZ1NvanM3WnlMME5WWmRMS09LYXc0c0tVVzVOKzJzb1NxdjJiaUZaTUl0eWJ6Z0RoYUozQ0dqcnZIbHlFOHJ3bTBpcXBPOU1EWHRIOHF1VTVnR1BsdUFJd1k2ODREN3lxUVMyeHVIZ1VrUVhUWjVHZDJFVmZGN2MwaDlqWW5URm9aamJvNXZlOXIzdjErWUpFL0dpdCswZEhaRDB5aWtvZjRtdnkxZmlwNU9yNHRKV2l2VUlPZHllZTRTZzJlSkVTUUF6UnJKcE5OczNOcFh5WHFRVXltUjdSd2VUL2NUT3Nmd2IwUEE2UGJvL1d5TEJHb1QvK3VEcUhpN0hEMDJ5UW1CU0Z1VG5jSHRXRDc3T1I5Wm9GbnJJMURFcHliYzNlNU1zanBFTTczc1pYZWQwbm52RHpCdDVmb3VmeWoxbzFicnBadXBUU3VsOFBpYXRjMis1dTRmZDBUNlhlN2R0SVM2bnpmcVkvcGZhTnJEbTdXSm5WWG1tYmpoSFZZQWVXeW5oY0tUdkdnZTFPaE0zeWZqaCtkbDYzaUE4dHhCVlZSaGo3ZHJGNUw5NmpqUER3RFdmRTRYZ2xlenpvRXJaQnF3WjdQbmowZEF3enhEc1V5YjhyMlFrcGtiQjJ2TE5TWCtzKytNTVYzbWFkdldIeVZQL2VWb2tGdm5mMUhLcTUwOFBsQ1RhVXV6bGZGTUlncVgrWTNFcHFVOHA4Z2VTc3h6YUdrcmZpOG91VE05dTlOa1M4UHF2U2R4c0pBU3JZbXU2VkUrcU91WlVXTVBtRGMyN1lGWjN2c3l1bFNvcXJkbG1tZzZkWDJVUGwxVGFKNVI2MDEzbENTNmQxOUFORDBCWGpxSUMyNSsyNG10Ly9sbk81NVpCZ1RadU9WZUlianE1QVd5SWZGNlZtWm1Sa2lmZ3U1ekcrY081MWFiVS9Na2xVc2U5U0t0OTRsVDEvajJMQ25OS0dITncvWFpyMUNLdzZOMUpxbkRmdDNVcDFyY3FZSm9jSmJHcllXeSs2d09XU3VtMHAra3I3RTQ2SXozdy9wTm9mN1FFazlQKzlBdzZKZGxQWDg1QVhjMTF1YkgrYS9NK2lOYjlxQ1FPRVBKdmpEQmdCMUthNGh5TkRmbVJ4WFBUZUtoTUVyQkRrczhKTDFqMVQ1RXowK1IyT1Y2NkhCVWd1dkZJdkRORmFHQWlxMXZMSXNOZmxaSzNkQk1SOFJHc1R2akxwWWZTT2p1MWE1OEhsZnpxeHhPU0cvU0xTUTc5czNkTGtueThkVlJHYzJoc2dNVjJ3NzFaaWhhcTBnRjhTbndLL3lJWFllYU1CTlhaeTQ0K0ZaWlNjS1ZOQ3VycW45OWM5NEk1TitKb0pVUEZrWlNLbG9wbi9Zb3V2YmFZZHRoQWVXWTVhR3dRR29tZE83TXl0ejFRWFo3QUpyK2hvc0lvcVdwckwzMmI0blpxWkkxQjRxRzlpRE44TXk4SFZDaFY1U3lXUUt3bUdHRmIvelk4KzNXS2V5ejNjdEUxTHlhSnBKZHlzU1pIRm04YjUwbTJvdlFDRFhBN2tJemlON1BMTW02eUF5aTB1Zm51eU11RHN5b3ExR0haR3JVemNZSnRabFRocURXanQ3NnRjZ2t4YlljcExkbS9JMTAzQWc2ZnUxRml5WnpjWmJKZnJuWWpHVVhsMUFxbDIrQ3JSdGQzMDRiUHpXLzhqMUo2bHdSVnRNQ09vRFNLMkFXMGZIeGtoUCtxTFU2VHVyUnNLTXRRR1d4a2dka3pVVnBNdDcvZWRFZERlbHFaMm1Xb1ptd0twNjdxMFhmaFhkYVFmTHcxMDArVGJPaE1kc3pOa3c5RjhUVGh1YXNOVmZNOVJvZDlIdHpkTStDd0ZwbDdXeXhZWHhpV3Z0UVVHSWY5TEI2Yml4T1ExakI5bUNEZjNwTG05YVVrZVV0VHN0eDFxY3FTeDZOaE02K0hJamp6bWx3UmVjWUNLLzEwTThxYTVSL1lQdzJGYW1xbkliK2YzZXBWeG5zNU4yZ2UxMHdjS09MQmQ5bnVuRDRCaGpMa04vVnh2TGMvYkF1em9SN3VlOE5WUEhHaXNEREVMN1VXKzVNa1hlYy9IbFRTRWxXZ1BMUTh0ekQ5OWlHa0ZYRmhoaEFlRjNNNkc3T24xQW5KQ3lTK3hnSVA1eldadlFVY3U5V1c3YWZsU205cVVDV2IwRVNid2IvbWVaVGxucFpCZ3dsZXZsdmdyUTNMalg3ZVZoNENqNFJ1M3pjYkNtNkkrdHhpUnVrTGtwYkQ3UEFvYzlMempZVG8rc3huRFpMM2ZVb3hqZkw0emgvcUxUbmViM3NodmVGNFREZVJsWVFHaDZSa1gya0g1U2hkZTBMNkwybHZxblNtdTdSMW5LNko1bmcvclFTTDNtMjB3QlAvSWhEcm1XNDJqNVFmNWxBQktzZTNzN20yOUF4K0VUUy9mM3FJd1ExdDhxcHRicWhqSXVVdElpY00yaDRxWUtPTllNTjBhLzVnbnFKcXdoTVVCZzZidjNObmR1MjBUdzZ4ZnZQTFpRbkRTVHZ4V1luTCtXTCs4c1JaZUNpbVRpNGJEQ3BWeWZiU1V0eStZWnlNV1JTenFKQXVpRmxFRWloaXZiRkpUZFJQcE1wakt2UDVwWEF6UVduRnFqTWtCL0RqeWVMSldFbzdqalVkbTVwY1JwZnFlUkFNYzFqUjhyMXRMRWgwM0w4TE5kKzNQMUdmUS9KaHhXNjgra1EzVHZrOEpVTmtJeFBDaGRsQ3VuVmF1MWZleVZTTHk4Umo1TU5Rd0hWVlZGKzZ4cEdVazVnRHRlOHcyN1hxdkRSUjZnLzlYOUhSRU1qaWpIZENJZmQxY3Rydks0MlNkNWxhRTI4ejJXRG95WXBrTDZMMno2ME1GeWtxOTFVb2lUc2FBUlJ0ZnpEdXh4OXRJWnBQU2FWMzN6dEw3ZDhCeURMUGNmaUZmRGszdThwS2dmZVdVUVFpeWZscHJ0TStUdFRTMUlNbklYdXRGNHlpYXM5MHR6NGpnNG9YN2lYQ1c3eU1sOUw5WXFSdlV1U0hKQXNqakdoSTR1SlVKdC9jK1hBNmlSSGFwT0MvMzMrK1N5cUtPVGVMU2RMNWdNdkJaVUFmUTU1SDUycmRKRCtFSHdxWFkwTUxaMDlvMnZlRlhMTndLTHRja0lsSlJPMmpIUWU1Y2hXdE5OclZuVytQbCtBdUtXZ0o2cC9uOFV4K1pHVXVnYUZFN2tyRWI4cmpQVGUwZ2xhOGVnTjNxNUNuSGhpS0dCcFFUSG9PV3BySmcvMjlmZjBINkNiUWZHZkdGeHNMbERmUW82VFpNYWMxcm1ObVRlRHQ5Ujh2Z0JaQXg0dDVyQ3NyRUZLSkdITlhJaXV1c0FwNUR3b1BEaW51R2k4NTFxbTRFaHdpaVFVRGlvbmY2bHVhNkFQOWZXWkwvMEd5dVZYMzdVVEZ3QUtDSEJwanV3dXY0T2hPdjhXV3ZQaXROditUVzJKKzBHYmJqbCtqcFo5MlYxcndzOWg3cHJaRTZoUk4yUEsweEEwcUpPVzlXSFRzTHpDRmdMRHpDUldYR2tLOVI1T01QaDZyUU5UaC9RQWIvQlk4WWl3WVFJVUJDYkpGdGtVc2VUM3N5SVBWTGZVaVlrSHdudW51TXkrS09GWXMzLzVhalVZSHgvMW14eVhkZjNPa0hWMTVNNlVvVGJvbUNZNmxHYjJHa1BNaURtK3VEak9uVnhia3l1MUszaEFhOHVUazlVY3JSTlJtdWNXbm5zNlpIWVBOajNVOHBVSXZTQnQrUGI0SmFKTHN1M2JPTCtzUUtLcFVYenlWNkJEODJieWhDR2lRaFZZZ2doT3p0Ym1rbDBKN1VpWGtBNmlUdFNiSzlReEhkQksrbDhvbHRjY0xrL25ybW94YUx0YjdkN1pZblAzTlhFeUt4UkxiNjNWWDhEZndLN3I4bFg0TjVxdVlzZWZEM1lCOEgwSkZhdlc2b2dQMmUzZHpwdlJIb3dhZGUzblp3Zkp0TFluOHBlZCsxYzdoZkI2aVJOYVd0ckowdXl3Uy9wK3oyNUUvK1hyd2ZVSzNMTEViVmpBUVMySSs3UG9wOVMrb3VEajdNUytOUGd2RHkxKzlqeExEUitkTWpaWVdaYXZmdnVPN0NpSUkwNEw1SVRBTWg4enZaOGhyMDF2YW41Qk0vNzJCcndyNXBpUm1YaWdHZ3hTUXl0YlBJRGljbjFLdU1CbFBZNmRBRUlRaDNkbjF2TGVPVmVBTDJ3WnhHRHFHK2t5Skw4SzVBT0lhRXY4em9iNWpFSXdQQXVvclhzVzkyNlRwMDdqWUJvR0FyMUFFK3kzOVF3aFB4a05jeWsrYlJtMW9mNHZMSWVVMll6enRLY1RIRzBNeWlGb2lJOFE0SHVjczR0d3A3TmtlRUpFQ2JVa2ZPU3JSNTNEVHd4cURWcytxL1pPZmlaKzN6ejZ0SDUxNTlLMG5YeVZ1dUZYbTVYQUpoVG84anJvbXY2My9DZFBnMTVRQ3FzMmYvdVJIclVvWmRIYkdGTGtiWHE4a2VRUzY1aDhPV2Y4SHlWMDNNU25PK1NZcmdhU2FjeFBjK1lrTXo0NUx2NmZDWGhLaC9wZThHd3M0dENNWll3VXYzUUpKTzZCRDdKQitreCt6NTZ2Skh5ei9VdVlNOTd2Y1Jhck1jZzZYNk5SSVdoRGVwYitycW9jUjJBYjMxMmUreTBnUStucGQ1Z0xZaHNCd1JxRzVsS1BsS25UcHRZeDZHRVpNOElMNmpCczBJbUd1ajBSZzJBUWpsMGwxT2YzTGRCZ2ZXcmhJeUI4WE9ua2xHMjd1T29hblo4RFllbHlVYklWZjhLRmU1SU5sZkFVNk01UU5MVnNtWCs0MWVrR2dmczNSclhMK3FZTlkwcG1Ja2krRGZTSjlvSCtJd01wVFNDc3JOaW5NV1lUUDVzQ0I3YVVDTUdkMlNTemZaRW14RE14bFh5S0k1OXp3VlZVekJtdWt3em5pbG00Zms5d1QxWGh3MDNJdVRPQUN2UjQ5cmtIVzMxeTV1UTlyY0RSTTRoSEo1YTYxYzFRcDJYbThJNklrcjNWTERLcWFzMWJ1VXJ5UUJBaEsvQVB5OXdhMmtaZ2oxZWtGQkE1emwyODZhT29zNEhLendja2MrMzNFM2MrZ2JsSmgvNmFJaEp3R1RuVzRKOWlsN2dwNndqblZuRkxwWjMyZGJ5OThWZ2YyVm1CU1VzRG40SkFHRElrRVF3bVFWcTEvWm51R01RU2ZwNGRRZElnWkduUFpZSjUrRXlFNjhlc0V4MWhBL3h2RFAvbW45TGZ6LzE2OUpTZDByZS9LYWhIQkdoSkxnUEZBTk5NU2FWUTFzOFltM3NRRkU4TTA3YnVzR3psamorZTFSNnhTRjV6TlNjSzZnelRyTzZPV1lobFlZZk1UZDVYVnc5L3B4eXRha1ZuMVVQMFdOT3QvbUpuS1JxcU5qYVRob0dVQnZlV2t2ZDZvYXdvdG5hRlJMd1h4NDlweXdRODNScHI1NGR2ZmYvNzJyeC8rK25oN3VMODRPOWdOdkRqNHRIbmxHYVBEZmJlOTU4dlpZZXZybXNsdlh1WlpPS1NzQnVZRzczQ1phdy9SeEQwTXRHM011SVRHSDM0OWJLM3ExYnRFRE5PNDd3YU5HQlB2RDdCY2RTMUFCOWU0NU51NGF4V2k1UHB4ejNydGowZGx3UnZSR0JpblNkOWQ3U0x6Nyt0WS9PT1ZQdVd1elJrMmYyaFkxTGhNYmgwNXcrK0ExTUtOd09FZ2FCYkxhTVFrTlA5L0hCOXRUaCt1OWZsUWFwV2xZdHBYT2hOSElxOUEvSXFVSkwrdnBrK0tQbUdaZEFTQkFZTUF0eG5PSTM3WXNuQm4zSVIzd2paRHlRSHdoLzdNUGtxQWZCL2Z4Q2JWdzkvNjR4V05FemJHZzBjU3pFcDkrbHQxVTJnVUFJUDlGZUpmTlNQVi92N3pZRzg4dEpvUmQ5VHI2SXJwWUpsRGJ0UndIaVNZcmRmUGVZblpGczAxV3phU2o5S2k3M0lnNkd2TENQYkZqbWprbTM0T0xGdzBhZFM4OEp2UTd2N2RRdGcydGxQQUFac0QreGpNcUNZWWZQbWxFdmdzK3lwc3VWWG5ZcmJjeTRIZmRHS3paRkpONFdDNThCTnRJd3NKa21UVVFRN0tMU0RaRG1ZRGErVG9aaGg0enJxUDA1Nk9hZFRmTGlJNEFuTnhPU0lzWUIwbTJTY2lhdEdxbjRudjREN3dwbEF2OE9XSGFlOW9hYVFSaE50WFFTVnhRR1laRDMyWU5uT3JYKzB3RmZMUVNDTXR6dW5tZFJZNVphUU9Ddy96am9Td0ZHdHl6aDEzSDNlWWJFNWVQNTJNbS9NdVZ5S0o1dXQrUC9xeHZmUDlMMWxzK29ZMDFmUEkrTDVOTWlkaC9JZzBIbGlCMUdaVEdxVG1TcnVIR3plclMrODYxM2RCZnJkS00xNGJheGRrZkt2NFZySTVrRmxyTlJBemxUc3lrRDhSb0dLMFhMZVdxV2M1ZDNTRndHUjZvZlVGcndRejlvNjVqalNPOFBMeGRGeWY5N3V0djlwZlpUUyszZFNvSFVscVprZ2VsSW13RnhaR3NsZ1BVenlwaW5CS2ZsWGNKMDF6dllQdzVGN3pycnV1bHBQSVdlZkFUVytteTdiRFNEVFJtTGR3MWllTUtsdkdoazBrM1BqZ2V1aXYrMVhCUlMvR1pSYzdjbEhZU0EvVkdNSkxNOTVPNUhTWG8rK3RmamZydnhrc3U1M1RNWTdFZUtLOFVtS0tNN29OS1ZrN0xoZ01ZemNrd0JMVzR0RW9EcXhRWkJqSUlJV0t2MTdzU0tYVFE0Q2k0T04wc084eVZ3ZDd1RFdxZXYzZ01vWHZueUlKZ3NuK2lodXdPU3ljemR4Q3dUa2dMQ0xQNTl1NFJWT29ZVXpKOGY5eWprVTRrZDRJeUdteVAwS3NOcTlUYURJSktIOFhtZW0zZnN3MmNPaTdrU3J0cFkvVXVlRzVZeHU3QloxYWJiVTUyTUxuTXRhblBUZnA1QU5OazFsTVlJT0xUWXN5ZHRCRjMvTlpmRzRiU3lKR29iTEFJNDZscFZCY1dxVDdnN0M1NXRNRXltSk5aT1pZR3NnamtFTjViYk5zbWdMU1RaL1NQRFpySldHVjBmVWtIZXE3RHAzaGtvOUxxS2JBVU93ZjVEd2V1RVlHMkRFNllhTlhxNktuZmFLYnpYU2haeFpHMUNtVDdsajRRVUhhYlNSTklOei9rTEt3Tmt0eEMrRFZrL21zVTUyUHZjdGN2c1h0MUE2WExVM2s0cERNU25pSFVxSENyMTFtRDNqRURDbml4d0VjQmpLTmlldjczZlZ1cmk0VkRvVHRZOE56UnlCNlJ6aHlrOE80Nk1hMUtRcW5OWDlpTHQ1eFZmcEF1YmloUk01bGd5R0ZwZDBKSmNXd2ZodDBlbWNiRHBJNVZ6dFUxYUJxQ3JYVmd3U3ZzaWk1TzBnVUhKN1AzaDIwK1lsWHZ3Y2NVVHpXV1VwZldodXZyMm40Y0g5dlp4SEVRaVNTRGcxL0xPNTFJaEpFcW5EdHh2NUtVcE44aEk2MG15b3RwUXhLVTJpenFWTmh6Um9qTmRiTGhSOXZoTXlkVDM4SDA3YkoxVG5zZkMyaHVqd21yVFdHZko1V0lSZW15R2xYUGNFV1c1dE1mRzlWalJZeDN2RHVvK0hybDZaWjU4em9ncy93Y1ZjVnhGbGJqMk1mZk45dUNuWUh5aFJmczhtdUdGQzEwMU5DdVdXQUZhdXZKOGRIaC92TGhWVU91Sk8zM0s0Lzh3SWoya0JMR2hMN1JBeEMwaFRuZGd1b3JnK1krelpLVyt6R0dxUFR2QnRHR2NZU0h3dTRCWWVlMXVhSE95Z2M3RzRIN2Z3UVp2QmFsc0ViTDU4Y0JlUzJhNVh6SU0vVThCZCt1V2lDS2FIbHdIWitZNTVIRWJ5VEE0TWxmQk5uREN5TmtNcnpGZTRqak1IeUpLOEFUZDJGcnYrUmQwL2VleUU0NWEzbldwVms3bDZaa25VVG5WeGJYQlVDV1prMVgyYUJ5VHdBZnVPM3I2ZjdPOHZGWkZ4WFphNms0SEhZTGlMV1YyUHNJdXI1Qmdadm1XRDdaQ2xxcDVadGxzT29JVUJEYytybVFRN2dBUTVWWnp4cXAxbGtUUGVsa0llZ2NCdHlCdGwybzRTT3VOdytweC9ldnJhSUpQUDBOeithU21scitwdHpPT3lGVFJ5VGJqemNpZ1ZoN3JFQnhGY3lJZXJIRTF6TjZ4UnFVNzFpRnQ1ZDZPd2Y2WGE1SzhCdHF1Mlllay8xZEZmTVJVamJNbmdDL0QzMmtMeVhkbHMvUExZV0VLdUpMUFJ2bFRCMldqcGRScGFkQ1VzWjlMZjR3R203NDh0Tmp1NHlwR0RiajltSGVZM1hOQVhZL25aMkFqL3k0NnZWMGg4UG03cmN0ZHd1UEhMekxTM0w4T1E2OXF3NlZuVW9oTVlYR25LS0JWWGtuWFAyYTVwM3UrdmxkTnljdFcvRVJIZ2JrWGNXTUcybzZMeENlWklocUkzWktMQ09SKzNBNUVOZm9SczA1d2JkMVd5azhzb2NEZi9CbkJjc0MxdFJGR0FkR2pIQThhUSsvVVRva2cvQVc5NmVWRXdMeGt4MWYrdU5FWUo2ZEtnT05RM3dHQ2ptMmVaTjgzQnlhUTZsZ1NSbUZKS0Z1aGY3NE5oVzBqaXBKa0ZJNm9LaG1EUmlLOXJtRFBPcUdYWm1rL3NtdDQvcEpycTl6YjM5N1lFN3QzMWxSMTF0L2pMVTBsN3pyblRXYW0wVmI2RTNyeFhtM3ZOSUxjODJ4WmdhUGZObmswdkw3cFdkTW4zajJvclFTT0srNXZFVW5DVndqSE16L3dUZzZLaGUyRHRmSWdVVWFjVFNQUFhlQjRvMUNZRVZVNkt5Z1RZV1QzbTZuUHlETmY5cFZsTnl1d1o3Z2ZVUnQ3MEFSem02Vzk1L3dnSEdFVjhnUDUva2ZKNW1CRkVqRFJsMUNJbG0zQXp5MFVFQ2VLUUprK3RjaGNPWU1YNU50KzJqT2FueGZvVlpabndqQVdwZjVyUjhoMkhkalpUQTJqQzcwVUNCQnpOU1ZqMHArTjZFNTFRVWlSUUpHd0paa2Rla3BJUHViQkJFMXJ6eFFrNlQxQzUxT0kvUlhXUi83Tm9HUkYrQVUwVitRUXR0ZFd1SDFrOHBEVkhJYjRva3dxc24wM0ZUbFR2Skk5L2RUQkx1ZTc5TG8vN1QzL3pqRlkwbWtPeWc0YmV2NFJROUNjbnI4b0RRR3Q5RnJQbUtnQk9iK1JFdmxpWUJjNVJxWXFwZWNrQWFUN2JXUm1vaTNMOTVQdktNdXF2OU9YREpTNkJUQzViRXlYZEJGcWFkajNTUFQ1aDllQXZ1V0VVbjFRaGZBUXZmdTJoU1BuVCtXSWxFOHQxRmhhdGVmU3VYVDFoWUk4Y21abHpUOHRIRWZPM2xkZ2tyTkFldDFvVkU1cHl4Tmhpc012STZWa2tOaEw2Z3lRZWJHK1NVWmZwMmFIMmsxTkFveDhvblIxcVJSZ29IdXYrWUk1TUtIa1luRTJpSnU3czZVcEZTNmFlWFhtbitFME9rT1UvQzN3YWtaY1daa0p4TnhLNjBZTHFlY0sveWVJY0RiMFJxcVNHb3ZrYWpQY2JUMDJNOTZxUUhSUDF1cE93ZU1RNDVnRGVGZlpjM3pyRmdxc3F6Yld6NW9aV3Y3YXFvamJFTytBQjMxNVFTL1JSK3FDOVFaazJxSlZVMUg2RFh5K1JnZ1dHNXBLTk5vOHVyNkVzckl3MC8zbCtjN2UrdGwvR1RXRlVwTVE5Vmo3czBFUFZRbEZOZ1h0eEtKMnZOY3MwWlhDc0EwazduVG4yZStyTXBXTUtrYnJnN2I2L1JTaVB6T3JBNTFRN1Nta09JcUxLb2NObXJPVlhpUitHcWhqenVUMWNLOWNSaE42L3Foc0RjbDdTMFpxanV3MmR5NHZiVmdxZXh1NWsvbkh5b1lqQnB1bitJY3ZYemhab2hROHRuemNLQTUreC9xRG9CQmRGdVBrakJaU3ZJa2VhMVQzZi9hM3MrYnJQQWQ5WWw0VmUvVGlTWDNGWHJzMURiRHJzMTVMNmhnWnJkR1BOK0ROUGNQcjJSWElSbDJ3KzhDRDJtZnRmQjVQdHkvNmlLTlhaeDgzS1JJL0lQamN5R2d6cFpKU2ZSZG80S0Jvby9aaFZjcGRHdE8vVGh0RWd1VzdqbWVDZWlSZU9jYXdLQUhYYStuVzZ0L1huVlpGSUJaM1NWZ0JIdGtSSURWMVhUWGtLbHJOOUppN1JiVTkxM2hlZ1dXR3RUSkRtVEJ6UXZFRlFWZVNkMUlKUTFJNXlLaGdHKzJjUDdoREN3Sk5BaUlkUGcvS0laekhjL3FCVHlRaHZJaVJsM3J1TXFid1JrcTd6V3Zub3JZSlVxVTRkMWl0ZExWWDl3UG5DcW4za0FCS3ZrL1pTeVp1eTNialJNSkU5KzU1YmloSGhldm5KdjM1Z3E0NDRKOXFyVFZzMThwMXdTSmlzNDREa3BBQ2dKcjF5b0lwb242Q2djMG9ndmhVemZrc3phcUVNNm15aC9lbk9TQTZGbVdPTDNJMWE5c2tMU005RXFsY1pzZkNFanc1b2hQY3BvQmdPQW05emNKZFFJMkFIZzVGRXFtcmlIQXJUTWU5Rk1acUY4UEdIT2k2N0txMUJvVXQ4Tmk0Q2E1a1pzUTk5ZDRTYy9mV2lrYks5aTg3dWRNbUlCQ1F5dk42TXM1RHFTc2ZWTG91NlF3SVlUcUxxQllYaTVtMzhocjRTUzFudWIzTGwyT2tpUlJJd2VHU3gvNElNMkZQZm9WelphczBkMTVVeXN2ZHdMWmxXOWVKS2RZOGRsTlBsZ0FjckdXbTA5MWZRSUJtZHlYOGs0ZWZrQVRBTVFRRU9WTXNBTytjZmlGR0dZSkVlcFI4OXRic1BEK3FKZ01XWitlbGdxa0dFdUgyQ1lXRE4zc1ZIbHdBRU1DdkVhelZNak96KzMxM1Eydk5aVmxxNzdKYkRybjEwY0ZmbFMyOThjdTI5ZnF0NjIzcjlGZDBaZGFYRzVmcVpmRDhkSHV6dkJmRFFvaTZzbTd3c2FlWlRScURvUDlibzUyeUhvamxWWFB2eWdXTkluZWZPcFBwUlViUGZvbUsyUGNuZ015WEV4UE1vaE1acTg0TVh0ZmwzOVUrdVoxdnFOUWFWRU1GUThtZDV2Y0lUc3dSa2RDS0ZydEZIV1loMnREM2RGcjc1Q1M3dFFxQzNhMFh5a1VORG4rc1hDYVBDY1NlQ3BUOThVZERreEhFZW5aYXZQOFVYcmc5OTArbWlXblhGSDhlcDJFa3d4QmRvbmlDZ2s1TlI2R1hqZWlBR2lrZHNQanplTFJaVHpVZFpVVU5kSm5LZ0Jqb1Z1M0s0RW42VCtXV2pMb05GK3VPTHFTaHl5VlVXUGNLRy8zOVZrS3lwNVVGcUJDWk1XTnR2cTNmZUFZS21UUjRkc3ZRQ3F4ZzlVK3AxemtRc2JzY1hCK0VCWEoyVlRiSU9NZ0NPaXlJSjd3NzE0UnhhU1hhbkY1SHVNRUFWMitaK041VkJRd3hZbzRPdmx3a25HM00wNGNNMXJNWTF5T1I1alJ6SHFXaFBoQzlMblZjOXAxZWtrajNZbDMyVUdHTnl1VjV6UVBDL0VENlNLL2U4bCtEbjAwTXdkRjJmNVY5Vm9yczlQbVM0N20xbkNZUStISThySnNiSktrcU9jQlE3R3dWMGcxRy9MQnlpa2hpZ1ZhOVZmTk13TFQ1cVVuN3RhZFdtcE9DYUhJRlAwQ2EwQ1ljR202Rk8ydURCQ0NMRk1sUkFHeE13ajFPWW54WXFBZ0R2SEVrUCtCcDFVZllTV1FLM1B4TjNCUTRiVThPUnk1OGtlYVYrWUlxRC9ySkNFQ21SWlB6K0dYZU1DWWZIM0NxMTh0WHlpUVpMNU8wOW1mWjJWQVFENCtLbFZtclcvVmE3R2t3REFBd00yWWtiTUw2TzZSSFg0SHV0S1AxbTJGOXJLeGtVZmZtOEE0TFlwOU5McEtCeTJTd1VYbzBQU1hKRXVTck5FTVpRU3hZb1JLODRWaDZVeFNtTVZRd0VWQVlHakdDRnlwTUZJQ1JGUzRWSWhFc2xtaUJCU0lKZzhsaTVTUUpvUkptd09ZVXgwbE5oeW1JZ2t6QXVSaUUyTGtVT0tqWUZRa3RHeWVNcXN3bGdBakhYQ1JqbE1BcHJrZWt1dlltbE9xR0FtcFpOS1JRTVJ3U2lTZ2RxSXd4NklBUUZndGhrNEFBQUFwbWJTRDRDdWRSMEFBQUJBcUc0c1VGY0FZRHNnSFFKUjIzNElUT3l0UTFoSzNEMkV6UlQxSVJ6NmxGUnpXdzRSVlpmZW0xSXNjWG1LVTRkT2szVWJwODFZdmRRaU5JdWtabUZpa2liMllaaTVVTTJsQklLbWJZZ0dlNGtKWVdCdkk1RXpwL3pDcFVTdzk4NHFrSW94UjVtQVM3bzlBUGZSQXNjY09FU1BmL0YyNEpCTml3MTlFODNZMlhDS2dRZHg0WWhFcExPaFZrekNuNW1LL29XclFmRFdranlmdWhibXFSNXpGSkQ4OGtxcUtncUt5c3J5cVpuMTB5dHhScVJrZy9WOVZvb0ErT1EwRnJDbGN2Sm4waEx0bjNJK2JVNnZCazZsdm8yRW0vMzVTOUtRazZubHpERGZTM0ZJRDdaRnJBQmhzcjJHMDV2QnJ4bDBybFpETWZaalFwcFc3eUNhckdQd0l4UExvYTE1Tmd3eWNUd1RpYzRrN0F1bkZoQnJuN1VpZHUwQ0F4TGwrOTdKU3YxcXNaWDNMRmtQTU5sa3BoNEh4WE54Zmt6d21lemFTaGxkVGFTdnBOdVEyVWhUVW9ObHdwRGQwSWhwYUZyRzMvWFQxa1lXVnZHM3lPZnB2cHFqRXBtOVUxQjE3NkFmTlFRaWlVeWgwdWdNRHBlSG9CaGZJQlNKSlZKM01sdWNJQ2xBTTZ4Y1liZlVONC9UWHJJQ096L3ZrQktwL0sycXFXdG9hbW5yNk9ycEd4Z2FHWnVZbXBsYldGcFoyOWphMlRzNE9qbTd1THE1ZTNoNmVmdUFlS3haNWdOZlNPSzg3K3VFMGNSSFB2TzVqMzJad3A2MGRrNDQ2WlRUempqTDEwV1hNaWVXK2tRbWt5bVBTanQyVzN2azh1V2QrOFB0NXB2ei9XTVFCUEFmYUVLSHdDSzZjaWdPbmd2ajJURCtQVU1KRUR6S0dzTkxvbmRETTVwWkpXSnhkNEdKOXR0Z1NiUWtnUHpUNExxLzhnZ0VMYXNhZ1hZOTFLRHpDT0E2ampJUWlDcXVPQ2JnUWJ6amppUjNkSWpqTEFzQUNJUTY4SFlDdHZvczREcWloNFo4bFNPMm9VcUhGc0dZYzVZM0NDQjIwbWt1cTFReXFxNHRIUlB4SUdIVWFlalZ2TlBzcFVmZ1RvZlQ0Z0lBQUE9PSIpIGZvcm1hdCgid29mZjIiKTsKICBmb250LXdlaWdodDogbm9ybWFsOwogIGZvbnQtc3R5bGU6IG5vcm1hbDsKfQoua2F0ZXggewogIGZvbnQ6IG5vcm1hbCAxLjIxZW0gS2FUZVhfTWFpbiwgVGltZXMgTmV3IFJvbWFuLCBzZXJpZjsKICBsaW5lLWhlaWdodDogMS4yOwogIHRleHQtaW5kZW50OiAwOwogIHRleHQtcmVuZGVyaW5nOiBhdXRvOwp9Ci5rYXRleCAqIHsKICAtbXMtaGlnaC1jb250cmFzdC1hZGp1c3Q6IG5vbmUgIWltcG9ydGFudDsKICBib3JkZXItY29sb3I6IGN1cnJlbnRDb2xvcjsKfQoua2F0ZXggLmthdGV4LXZlcnNpb246OmFmdGVyIHsKICBjb250ZW50OiAiMC4xMy4xMSI7Cn0KLmthdGV4IC5rYXRleC1tYXRobWwgewogIC8qIEFjY2Vzc2liaWxpdHkgaGFjayB0byBvbmx5IHNob3cgdG8gc2NyZWVuIHJlYWRlcnMKICAgICAgICAgRm91bmQgYXQ6IGh0dHA6Ly9hMTF5cHJvamVjdC5jb20vcG9zdHMvaG93LXRvLWhpZGUtY29udGVudC8gKi8KICBwb3NpdGlvbjogYWJzb2x1dGU7CiAgY2xpcDogcmVjdCgxcHgsIDFweCwgMXB4LCAxcHgpOwogIHBhZGRpbmc6IDA7CiAgYm9yZGVyOiAwOwogIGhlaWdodDogMXB4OwogIHdpZHRoOiAxcHg7CiAgb3ZlcmZsb3c6IGhpZGRlbjsKfQoua2F0ZXggLmthdGV4LWh0bWwgewogIC8qIFxuZXdsaW5lIGlzIGFuIGVtcHR5IGJsb2NrIGF0IHRvcCBsZXZlbCwgYmV0d2VlbiAuYmFzZSBlbGVtZW50cyAqLwp9Ci5rYXRleCAua2F0ZXgtaHRtbCA+IC5uZXdsaW5lIHsKICBkaXNwbGF5OiBibG9jazsKfQoua2F0ZXggLmJhc2UgewogIHBvc2l0aW9uOiByZWxhdGl2ZTsKICBkaXNwbGF5OiBpbmxpbmUtYmxvY2s7CiAgd2hpdGUtc3BhY2U6IG5vd3JhcDsKICB3aWR0aDogLXdlYmtpdC1taW4tY29udGVudDsKICB3aWR0aDogLW1vei1taW4tY29udGVudDsKICB3aWR0aDogbWluLWNvbnRlbnQ7Cn0KLmthdGV4IC5zdHJ1dCB7CiAgZGlzcGxheTogaW5saW5lLWJsb2NrOwp9Ci5rYXRleCAudGV4dGJmIHsKICBmb250LXdlaWdodDogYm9sZDsKfQoua2F0ZXggLnRleHRpdCB7CiAgZm9udC1zdHlsZTogaXRhbGljOwp9Ci5rYXRleCAudGV4dHJtIHsKICBmb250LWZhbWlseTogS2FUZVhfTWFpbjsKfQoua2F0ZXggLnRleHRzZiB7CiAgZm9udC1mYW1pbHk6IEthVGVYX1NhbnNTZXJpZjsKfQoua2F0ZXggLnRleHR0dCB7CiAgZm9udC1mYW1pbHk6IEthVGVYX1R5cGV3cml0ZXI7Cn0KLmthdGV4IC5tYXRobm9ybWFsIHsKICBmb250LWZhbWlseTogS2FUZVhfTWF0aDsKICBmb250LXN0eWxlOiBpdGFsaWM7Cn0KLmthdGV4IC5tYXRoaXQgewogIGZvbnQtZmFtaWx5OiBLYVRlWF9NYWluOwogIGZvbnQtc3R5bGU6IGl0YWxpYzsKfQoua2F0ZXggLm1hdGhybSB7CiAgZm9udC1zdHlsZTogbm9ybWFsOwp9Ci5rYXRleCAubWF0aGJmIHsKICBmb250LWZhbWlseTogS2FUZVhfTWFpbjsKICBmb250LXdlaWdodDogYm9sZDsKfQoua2F0ZXggLmJvbGRzeW1ib2wgewogIGZvbnQtZmFtaWx5OiBLYVRlWF9NYXRoOwogIGZvbnQtd2VpZ2h0OiBib2xkOwogIGZvbnQtc3R5bGU6IGl0YWxpYzsKfQoua2F0ZXggLmFtc3JtIHsKICBmb250LWZhbWlseTogS2FUZVhfQU1TOwp9Ci5rYXRleCAubWF0aGJiLAoua2F0ZXggLnRleHRiYiB7CiAgZm9udC1mYW1pbHk6IEthVGVYX0FNUzsKfQoua2F0ZXggLm1hdGhjYWwgewogIGZvbnQtZmFtaWx5OiBLYVRlWF9DYWxpZ3JhcGhpYzsKfQoua2F0ZXggLm1hdGhmcmFrLAoua2F0ZXggLnRleHRmcmFrIHsKICBmb250LWZhbWlseTogS2FUZVhfRnJha3R1cjsKfQoua2F0ZXggLm1hdGh0dCB7CiAgZm9udC1mYW1pbHk6IEthVGVYX1R5cGV3cml0ZXI7Cn0KLmthdGV4IC5tYXRoc2NyLAoua2F0ZXggLnRleHRzY3IgewogIGZvbnQtZmFtaWx5OiBLYVRlWF9TY3JpcHQ7Cn0KLmthdGV4IC5tYXRoc2YsCi5rYXRleCAudGV4dHNmIHsKICBmb250LWZhbWlseTogS2FUZVhfU2Fuc1NlcmlmOwp9Ci5rYXRleCAubWF0aGJvbGRzZiwKLmthdGV4IC50ZXh0Ym9sZHNmIHsKICBmb250LWZhbWlseTogS2FUZVhfU2Fuc1NlcmlmOwogIGZvbnQtd2VpZ2h0OiBib2xkOwp9Ci5rYXRleCAubWF0aGl0c2YsCi5rYXRleCAudGV4dGl0c2YgewogIGZvbnQtZmFtaWx5OiBLYVRlWF9TYW5zU2VyaWY7CiAgZm9udC1zdHlsZTogaXRhbGljOwp9Ci5rYXRleCAubWFpbnJtIHsKICBmb250LWZhbWlseTogS2FUZVhfTWFpbjsKICBmb250LXN0eWxlOiBub3JtYWw7Cn0KLmthdGV4IC52bGlzdC10IHsKICBkaXNwbGF5OiBpbmxpbmUtdGFibGU7CiAgdGFibGUtbGF5b3V0OiBmaXhlZDsKICBib3JkZXItY29sbGFwc2U6IGNvbGxhcHNlOwp9Ci5rYXRleCAudmxpc3QtciB7CiAgZGlzcGxheTogdGFibGUtcm93Owp9Ci5rYXRleCAudmxpc3QgewogIGRpc3BsYXk6IHRhYmxlLWNlbGw7CiAgdmVydGljYWwtYWxpZ246IGJvdHRvbTsKICBwb3NpdGlvbjogcmVsYXRpdmU7Cn0KLmthdGV4IC52bGlzdCA+IHNwYW4gewogIGRpc3BsYXk6IGJsb2NrOwogIGhlaWdodDogMDsKICBwb3NpdGlvbjogcmVsYXRpdmU7Cn0KLmthdGV4IC52bGlzdCA+IHNwYW4gPiBzcGFuIHsKICBkaXNwbGF5OiBpbmxpbmUtYmxvY2s7Cn0KLmthdGV4IC52bGlzdCA+IHNwYW4gPiAucHN0cnV0IHsKICBvdmVyZmxvdzogaGlkZGVuOwogIHdpZHRoOiAwOwp9Ci5rYXRleCAudmxpc3QtdDIgewogIG1hcmdpbi1yaWdodDogLTJweDsKfQoua2F0ZXggLnZsaXN0LXMgewogIGRpc3BsYXk6IHRhYmxlLWNlbGw7CiAgdmVydGljYWwtYWxpZ246IGJvdHRvbTsKICBmb250LXNpemU6IDFweDsKICB3aWR0aDogMnB4OwogIG1pbi13aWR0aDogMnB4Owp9Ci5rYXRleCAudmJveCB7CiAgZGlzcGxheTogLXdlYmtpdC1pbmxpbmUtYm94OwogIGRpc3BsYXk6IGlubGluZS1mbGV4OwogIC13ZWJraXQtYm94LW9yaWVudDogdmVydGljYWw7CiAgLXdlYmtpdC1ib3gtZGlyZWN0aW9uOiBub3JtYWw7CiAgICAgICAgICBmbGV4LWRpcmVjdGlvbjogY29sdW1uOwogIC13ZWJraXQtYm94LWFsaWduOiBiYXNlbGluZTsKICAgICAgICAgIGFsaWduLWl0ZW1zOiBiYXNlbGluZTsKfQoua2F0ZXggLmhib3ggewogIGRpc3BsYXk6IC13ZWJraXQtaW5saW5lLWJveDsKICBkaXNwbGF5OiBpbmxpbmUtZmxleDsKICAtd2Via2l0LWJveC1vcmllbnQ6IGhvcml6b250YWw7CiAgLXdlYmtpdC1ib3gtZGlyZWN0aW9uOiBub3JtYWw7CiAgICAgICAgICBmbGV4LWRpcmVjdGlvbjogcm93OwogIHdpZHRoOiAxMDAlOwp9Ci5rYXRleCAudGhpbmJveCB7CiAgZGlzcGxheTogLXdlYmtpdC1pbmxpbmUtYm94OwogIGRpc3BsYXk6IGlubGluZS1mbGV4OwogIC13ZWJraXQtYm94LW9yaWVudDogaG9yaXpvbnRhbDsKICAtd2Via2l0LWJveC1kaXJlY3Rpb246IG5vcm1hbDsKICAgICAgICAgIGZsZXgtZGlyZWN0aW9uOiByb3c7CiAgd2lkdGg6IDA7CiAgbWF4LXdpZHRoOiAwOwp9Ci5rYXRleCAubXN1cHN1YiB7CiAgdGV4dC1hbGlnbjogbGVmdDsKfQoua2F0ZXggLm1mcmFjID4gc3BhbiA+IHNwYW4gewogIHRleHQtYWxpZ246IGNlbnRlcjsKfQoua2F0ZXggLm1mcmFjIC5mcmFjLWxpbmUgewogIGRpc3BsYXk6IGlubGluZS1ibG9jazsKICB3aWR0aDogMTAwJTsKICBib3JkZXItYm90dG9tLXN0eWxlOiBzb2xpZDsKfQoua2F0ZXggLm1mcmFjIC5mcmFjLWxpbmUsCi5rYXRleCAub3ZlcmxpbmUgLm92ZXJsaW5lLWxpbmUsCi5rYXRleCAudW5kZXJsaW5lIC51bmRlcmxpbmUtbGluZSwKLmthdGV4IC5obGluZSwKLmthdGV4IC5oZGFzaGxpbmUsCi5rYXRleCAucnVsZSB7CiAgbWluLWhlaWdodDogMXB4Owp9Ci5rYXRleCAubXNwYWNlIHsKICBkaXNwbGF5OiBpbmxpbmUtYmxvY2s7Cn0KLmthdGV4IC5sbGFwLAoua2F0ZXggLnJsYXAsCi5rYXRleCAuY2xhcCB7CiAgd2lkdGg6IDA7CiAgcG9zaXRpb246IHJlbGF0aXZlOwp9Ci5rYXRleCAubGxhcCA+IC5pbm5lciwKLmthdGV4IC5ybGFwID4gLmlubmVyLAoua2F0ZXggLmNsYXAgPiAuaW5uZXIgewogIHBvc2l0aW9uOiBhYnNvbHV0ZTsKfQoua2F0ZXggLmxsYXAgPiAuZml4LAoua2F0ZXggLnJsYXAgPiAuZml4LAoua2F0ZXggLmNsYXAgPiAuZml4IHsKICBkaXNwbGF5OiBpbmxpbmUtYmxvY2s7Cn0KLmthdGV4IC5sbGFwID4gLmlubmVyIHsKICByaWdodDogMDsKfQoua2F0ZXggLnJsYXAgPiAuaW5uZXIsCi5rYXRleCAuY2xhcCA+IC5pbm5lciB7CiAgbGVmdDogMDsKfQoua2F0ZXggLmNsYXAgPiAuaW5uZXIgPiBzcGFuIHsKICBtYXJnaW4tbGVmdDogLTUwJTsKICBtYXJnaW4tcmlnaHQ6IDUwJTsKfQoua2F0ZXggLnJ1bGUgewogIGRpc3BsYXk6IGlubGluZS1ibG9jazsKICBib3JkZXI6IHNvbGlkIDA7CiAgcG9zaXRpb246IHJlbGF0aXZlOwp9Ci5rYXRleCAub3ZlcmxpbmUgLm92ZXJsaW5lLWxpbmUsCi5rYXRleCAudW5kZXJsaW5lIC51bmRlcmxpbmUtbGluZSwKLmthdGV4IC5obGluZSB7CiAgZGlzcGxheTogaW5saW5lLWJsb2NrOwogIHdpZHRoOiAxMDAlOwogIGJvcmRlci1ib3R0b20tc3R5bGU6IHNvbGlkOwp9Ci5rYXRleCAuaGRhc2hsaW5lIHsKICBkaXNwbGF5OiBpbmxpbmUtYmxvY2s7CiAgd2lkdGg6IDEwMCU7CiAgYm9yZGVyLWJvdHRvbS1zdHlsZTogZGFzaGVkOwp9Ci5rYXRleCAuc3FydCA+IC5yb290IHsKICAvKiBUaGVzZSB2YWx1ZXMgYXJlIHRha2VuIGZyb20gdGhlIGRlZmluaXRpb24gb2YgYFxyQEB0YCwKICAgICAgICAgICAgIGBcbWtlcm4gNW11YCBhbmQgYFxta2VybiAtMTBtdWAuICovCiAgbWFyZ2luLWxlZnQ6IDAuMjc3Nzc3NzhlbTsKICBtYXJnaW4tcmlnaHQ6IC0wLjU1NTU1NTU2ZW07Cn0KLmthdGV4IC5zaXppbmcucmVzZXQtc2l6ZTEuc2l6ZTEsCi5rYXRleCAuZm9udHNpemUtZW5zdXJlci5yZXNldC1zaXplMS5zaXplMSB7CiAgZm9udC1zaXplOiAxZW07Cn0KLmthdGV4IC5zaXppbmcucmVzZXQtc2l6ZTEuc2l6ZTIsCi5rYXRleCAuZm9udHNpemUtZW5zdXJlci5yZXNldC1zaXplMS5zaXplMiB7CiAgZm9udC1zaXplOiAxLjJlbTsKfQoua2F0ZXggLnNpemluZy5yZXNldC1zaXplMS5zaXplMywKLmthdGV4IC5mb250c2l6ZS1lbnN1cmVyLnJlc2V0LXNpemUxLnNpemUzIHsKICBmb250LXNpemU6IDEuNGVtOwp9Ci5rYXRleCAuc2l6aW5nLnJlc2V0LXNpemUxLnNpemU0LAoua2F0ZXggLmZvbnRzaXplLWVuc3VyZXIucmVzZXQtc2l6ZTEuc2l6ZTQgewogIGZvbnQtc2l6ZTogMS42ZW07Cn0KLmthdGV4IC5zaXppbmcucmVzZXQtc2l6ZTEuc2l6ZTUsCi5rYXRleCAuZm9udHNpemUtZW5zdXJlci5yZXNldC1zaXplMS5zaXplNSB7CiAgZm9udC1zaXplOiAxLjhlbTsKfQoua2F0ZXggLnNpemluZy5yZXNldC1zaXplMS5zaXplNiwKLmthdGV4IC5mb250c2l6ZS1lbnN1cmVyLnJlc2V0LXNpemUxLnNpemU2IHsKICBmb250LXNpemU6IDJlbTsKfQoua2F0ZXggLnNpemluZy5yZXNldC1zaXplMS5zaXplNywKLmthdGV4IC5mb250c2l6ZS1lbnN1cmVyLnJlc2V0LXNpemUxLnNpemU3IHsKICBmb250LXNpemU6IDIuNGVtOwp9Ci5rYXRleCAuc2l6aW5nLnJlc2V0LXNpemUxLnNpemU4LAoua2F0ZXggLmZvbnRzaXplLWVuc3VyZXIucmVzZXQtc2l6ZTEuc2l6ZTggewogIGZvbnQtc2l6ZTogMi44OGVtOwp9Ci5rYXRleCAuc2l6aW5nLnJlc2V0LXNpemUxLnNpemU5LAoua2F0ZXggLmZvbnRzaXplLWVuc3VyZXIucmVzZXQtc2l6ZTEuc2l6ZTkgewogIGZvbnQtc2l6ZTogMy40NTZlbTsKfQoua2F0ZXggLnNpemluZy5yZXNldC1zaXplMS5zaXplMTAsCi5rYXRleCAuZm9udHNpemUtZW5zdXJlci5yZXNldC1zaXplMS5zaXplMTAgewogIGZvbnQtc2l6ZTogNC4xNDhlbTsKfQoua2F0ZXggLnNpemluZy5yZXNldC1zaXplMS5zaXplMTEsCi5rYXRleCAuZm9udHNpemUtZW5zdXJlci5yZXNldC1zaXplMS5zaXplMTEgewogIGZvbnQtc2l6ZTogNC45NzZlbTsKfQoua2F0ZXggLnNpemluZy5yZXNldC1zaXplMi5zaXplMSwKLmthdGV4IC5mb250c2l6ZS1lbnN1cmVyLnJlc2V0LXNpemUyLnNpemUxIHsKICBmb250LXNpemU6IDAuODMzMzMzMzNlbTsKfQoua2F0ZXggLnNpemluZy5yZXNldC1zaXplMi5zaXplMiwKLmthdGV4IC5mb250c2l6ZS1lbnN1cmVyLnJlc2V0LXNpemUyLnNpemUyIHsKICBmb250LXNpemU6IDFlbTsKfQoua2F0ZXggLnNpemluZy5yZXNldC1zaXplMi5zaXplMywKLmthdGV4IC5mb250c2l6ZS1lbnN1cmVyLnJlc2V0LXNpemUyLnNpemUzIHsKICBmb250LXNpemU6IDEuMTY2NjY2NjdlbTsKfQoua2F0ZXggLnNpemluZy5yZXNldC1zaXplMi5zaXplNCwKLmthdGV4IC5mb250c2l6ZS1lbnN1cmVyLnJlc2V0LXNpemUyLnNpemU0IHsKICBmb250LXNpemU6IDEuMzMzMzMzMzNlbTsKfQoua2F0ZXggLnNpemluZy5yZXNldC1zaXplMi5zaXplNSwKLmthdGV4IC5mb250c2l6ZS1lbnN1cmVyLnJlc2V0LXNpemUyLnNpemU1IHsKICBmb250LXNpemU6IDEuNWVtOwp9Ci5rYXRleCAuc2l6aW5nLnJlc2V0LXNpemUyLnNpemU2LAoua2F0ZXggLmZvbnRzaXplLWVuc3VyZXIucmVzZXQtc2l6ZTIuc2l6ZTYgewogIGZvbnQtc2l6ZTogMS42NjY2NjY2N2VtOwp9Ci5rYXRleCAuc2l6aW5nLnJlc2V0LXNpemUyLnNpemU3LAoua2F0ZXggLmZvbnRzaXplLWVuc3VyZXIucmVzZXQtc2l6ZTIuc2l6ZTcgewogIGZvbnQtc2l6ZTogMmVtOwp9Ci5rYXRleCAuc2l6aW5nLnJlc2V0LXNpemUyLnNpemU4LAoua2F0ZXggLmZvbnRzaXplLWVuc3VyZXIucmVzZXQtc2l6ZTIuc2l6ZTggewogIGZvbnQtc2l6ZTogMi40ZW07Cn0KLmthdGV4IC5zaXppbmcucmVzZXQtc2l6ZTIuc2l6ZTksCi5rYXRleCAuZm9udHNpemUtZW5zdXJlci5yZXNldC1zaXplMi5zaXplOSB7CiAgZm9udC1zaXplOiAyLjg4ZW07Cn0KLmthdGV4IC5zaXppbmcucmVzZXQtc2l6ZTIuc2l6ZTEwLAoua2F0ZXggLmZvbnRzaXplLWVuc3VyZXIucmVzZXQtc2l6ZTIuc2l6ZTEwIHsKICBmb250LXNpemU6IDMuNDU2NjY2NjdlbTsKfQoua2F0ZXggLnNpemluZy5yZXNldC1zaXplMi5zaXplMTEsCi5rYXRleCAuZm9udHNpemUtZW5zdXJlci5yZXNldC1zaXplMi5zaXplMTEgewogIGZvbnQtc2l6ZTogNC4xNDY2NjY2N2VtOwp9Ci5rYXRleCAuc2l6aW5nLnJlc2V0LXNpemUzLnNpemUxLAoua2F0ZXggLmZvbnRzaXplLWVuc3VyZXIucmVzZXQtc2l6ZTMuc2l6ZTEgewogIGZvbnQtc2l6ZTogMC43MTQyODU3MWVtOwp9Ci5rYXRleCAuc2l6aW5nLnJlc2V0LXNpemUzLnNpemUyLAoua2F0ZXggLmZvbnRzaXplLWVuc3VyZXIucmVzZXQtc2l6ZTMuc2l6ZTIgewogIGZvbnQtc2l6ZTogMC44NTcxNDI4NmVtOwp9Ci5rYXRleCAuc2l6aW5nLnJlc2V0LXNpemUzLnNpemUzLAoua2F0ZXggLmZvbnRzaXplLWVuc3VyZXIucmVzZXQtc2l6ZTMuc2l6ZTMgewogIGZvbnQtc2l6ZTogMWVtOwp9Ci5rYXRleCAuc2l6aW5nLnJlc2V0LXNpemUzLnNpemU0LAoua2F0ZXggLmZvbnRzaXplLWVuc3VyZXIucmVzZXQtc2l6ZTMuc2l6ZTQgewogIGZvbnQtc2l6ZTogMS4xNDI4NTcxNGVtOwp9Ci5rYXRleCAuc2l6aW5nLnJlc2V0LXNpemUzLnNpemU1LAoua2F0ZXggLmZvbnRzaXplLWVuc3VyZXIucmVzZXQtc2l6ZTMuc2l6ZTUgewogIGZvbnQtc2l6ZTogMS4yODU3MTQyOWVtOwp9Ci5rYXRleCAuc2l6aW5nLnJlc2V0LXNpemUzLnNpemU2LAoua2F0ZXggLmZvbnRzaXplLWVuc3VyZXIucmVzZXQtc2l6ZTMuc2l6ZTYgewogIGZvbnQtc2l6ZTogMS40Mjg1NzE0M2VtOwp9Ci5rYXRleCAuc2l6aW5nLnJlc2V0LXNpemUzLnNpemU3LAoua2F0ZXggLmZvbnRzaXplLWVuc3VyZXIucmVzZXQtc2l6ZTMuc2l6ZTcgewogIGZvbnQtc2l6ZTogMS43MTQyODU3MWVtOwp9Ci5rYXRleCAuc2l6aW5nLnJlc2V0LXNpemUzLnNpemU4LAoua2F0ZXggLmZvbnRzaXplLWVuc3VyZXIucmVzZXQtc2l6ZTMuc2l6ZTggewogIGZvbnQtc2l6ZTogMi4wNTcxNDI4NmVtOwp9Ci5rYXRleCAuc2l6aW5nLnJlc2V0LXNpemUzLnNpemU5LAoua2F0ZXggLmZvbnRzaXplLWVuc3VyZXIucmVzZXQtc2l6ZTMuc2l6ZTkgewogIGZvbnQtc2l6ZTogMi40Njg1NzE0M2VtOwp9Ci5rYXRleCAuc2l6aW5nLnJlc2V0LXNpemUzLnNpemUxMCwKLmthdGV4IC5mb250c2l6ZS1lbnN1cmVyLnJlc2V0LXNpemUzLnNpemUxMCB7CiAgZm9udC1zaXplOiAyLjk2Mjg1NzE0ZW07Cn0KLmthdGV4IC5zaXppbmcucmVzZXQtc2l6ZTMuc2l6ZTExLAoua2F0ZXggLmZvbnRzaXplLWVuc3VyZXIucmVzZXQtc2l6ZTMuc2l6ZTExIHsKICBmb250LXNpemU6IDMuNTU0Mjg1NzFlbTsKfQoua2F0ZXggLnNpemluZy5yZXNldC1zaXplNC5zaXplMSwKLmthdGV4IC5mb250c2l6ZS1lbnN1cmVyLnJlc2V0LXNpemU0LnNpemUxIHsKICBmb250LXNpemU6IDAuNjI1ZW07Cn0KLmthdGV4IC5zaXppbmcucmVzZXQtc2l6ZTQuc2l6ZTIsCi5rYXRleCAuZm9udHNpemUtZW5zdXJlci5yZXNldC1zaXplNC5zaXplMiB7CiAgZm9udC1zaXplOiAwLjc1ZW07Cn0KLmthdGV4IC5zaXppbmcucmVzZXQtc2l6ZTQuc2l6ZTMsCi5rYXRleCAuZm9udHNpemUtZW5zdXJlci5yZXNldC1zaXplNC5zaXplMyB7CiAgZm9udC1zaXplOiAwLjg3NWVtOwp9Ci5rYXRleCAuc2l6aW5nLnJlc2V0LXNpemU0LnNpemU0LAoua2F0ZXggLmZvbnRzaXplLWVuc3VyZXIucmVzZXQtc2l6ZTQuc2l6ZTQgewogIGZvbnQtc2l6ZTogMWVtOwp9Ci5rYXRleCAuc2l6aW5nLnJlc2V0LXNpemU0LnNpemU1LAoua2F0ZXggLmZvbnRzaXplLWVuc3VyZXIucmVzZXQtc2l6ZTQuc2l6ZTUgewogIGZvbnQtc2l6ZTogMS4xMjVlbTsKfQoua2F0ZXggLnNpemluZy5yZXNldC1zaXplNC5zaXplNiwKLmthdGV4IC5mb250c2l6ZS1lbnN1cmVyLnJlc2V0LXNpemU0LnNpemU2IHsKICBmb250LXNpemU6IDEuMjVlbTsKfQoua2F0ZXggLnNpemluZy5yZXNldC1zaXplNC5zaXplNywKLmthdGV4IC5mb250c2l6ZS1lbnN1cmVyLnJlc2V0LXNpemU0LnNpemU3IHsKICBmb250LXNpemU6IDEuNWVtOwp9Ci5rYXRleCAuc2l6aW5nLnJlc2V0LXNpemU0LnNpemU4LAoua2F0ZXggLmZvbnRzaXplLWVuc3VyZXIucmVzZXQtc2l6ZTQuc2l6ZTggewogIGZvbnQtc2l6ZTogMS44ZW07Cn0KLmthdGV4IC5zaXppbmcucmVzZXQtc2l6ZTQuc2l6ZTksCi5rYXRleCAuZm9udHNpemUtZW5zdXJlci5yZXNldC1zaXplNC5zaXplOSB7CiAgZm9udC1zaXplOiAyLjE2ZW07Cn0KLmthdGV4IC5zaXppbmcucmVzZXQtc2l6ZTQuc2l6ZTEwLAoua2F0ZXggLmZvbnRzaXplLWVuc3VyZXIucmVzZXQtc2l6ZTQuc2l6ZTEwIHsKICBmb250LXNpemU6IDIuNTkyNWVtOwp9Ci5rYXRleCAuc2l6aW5nLnJlc2V0LXNpemU0LnNpemUxMSwKLmthdGV4IC5mb250c2l6ZS1lbnN1cmVyLnJlc2V0LXNpemU0LnNpemUxMSB7CiAgZm9udC1zaXplOiAzLjExZW07Cn0KLmthdGV4IC5zaXppbmcucmVzZXQtc2l6ZTUuc2l6ZTEsCi5rYXRleCAuZm9udHNpemUtZW5zdXJlci5yZXNldC1zaXplNS5zaXplMSB7CiAgZm9udC1zaXplOiAwLjU1NTU1NTU2ZW07Cn0KLmthdGV4IC5zaXppbmcucmVzZXQtc2l6ZTUuc2l6ZTIsCi5rYXRleCAuZm9udHNpemUtZW5zdXJlci5yZXNldC1zaXplNS5zaXplMiB7CiAgZm9udC1zaXplOiAwLjY2NjY2NjY3ZW07Cn0KLmthdGV4IC5zaXppbmcucmVzZXQtc2l6ZTUuc2l6ZTMsCi5rYXRleCAuZm9udHNpemUtZW5zdXJlci5yZXNldC1zaXplNS5zaXplMyB7CiAgZm9udC1zaXplOiAwLjc3Nzc3Nzc4ZW07Cn0KLmthdGV4IC5zaXppbmcucmVzZXQtc2l6ZTUuc2l6ZTQsCi5rYXRleCAuZm9udHNpemUtZW5zdXJlci5yZXNldC1zaXplNS5zaXplNCB7CiAgZm9udC1zaXplOiAwLjg4ODg4ODg5ZW07Cn0KLmthdGV4IC5zaXppbmcucmVzZXQtc2l6ZTUuc2l6ZTUsCi5rYXRleCAuZm9udHNpemUtZW5zdXJlci5yZXNldC1zaXplNS5zaXplNSB7CiAgZm9udC1zaXplOiAxZW07Cn0KLmthdGV4IC5zaXppbmcucmVzZXQtc2l6ZTUuc2l6ZTYsCi5rYXRleCAuZm9udHNpemUtZW5zdXJlci5yZXNldC1zaXplNS5zaXplNiB7CiAgZm9udC1zaXplOiAxLjExMTExMTExZW07Cn0KLmthdGV4IC5zaXppbmcucmVzZXQtc2l6ZTUuc2l6ZTcsCi5rYXRleCAuZm9udHNpemUtZW5zdXJlci5yZXNldC1zaXplNS5zaXplNyB7CiAgZm9udC1zaXplOiAxLjMzMzMzMzMzZW07Cn0KLmthdGV4IC5zaXppbmcucmVzZXQtc2l6ZTUuc2l6ZTgsCi5rYXRleCAuZm9udHNpemUtZW5zdXJlci5yZXNldC1zaXplNS5zaXplOCB7CiAgZm9udC1zaXplOiAxLjZlbTsKfQoua2F0ZXggLnNpemluZy5yZXNldC1zaXplNS5zaXplOSwKLmthdGV4IC5mb250c2l6ZS1lbnN1cmVyLnJlc2V0LXNpemU1LnNpemU5IHsKICBmb250LXNpemU6IDEuOTJlbTsKfQoua2F0ZXggLnNpemluZy5yZXNldC1zaXplNS5zaXplMTAsCi5rYXRleCAuZm9udHNpemUtZW5zdXJlci5yZXNldC1zaXplNS5zaXplMTAgewogIGZvbnQtc2l6ZTogMi4zMDQ0NDQ0NGVtOwp9Ci5rYXRleCAuc2l6aW5nLnJlc2V0LXNpemU1LnNpemUxMSwKLmthdGV4IC5mb250c2l6ZS1lbnN1cmVyLnJlc2V0LXNpemU1LnNpemUxMSB7CiAgZm9udC1zaXplOiAyLjc2NDQ0NDQ0ZW07Cn0KLmthdGV4IC5zaXppbmcucmVzZXQtc2l6ZTYuc2l6ZTEsCi5rYXRleCAuZm9udHNpemUtZW5zdXJlci5yZXNldC1zaXplNi5zaXplMSB7CiAgZm9udC1zaXplOiAwLjVlbTsKfQoua2F0ZXggLnNpemluZy5yZXNldC1zaXplNi5zaXplMiwKLmthdGV4IC5mb250c2l6ZS1lbnN1cmVyLnJlc2V0LXNpemU2LnNpemUyIHsKICBmb250LXNpemU6IDAuNmVtOwp9Ci5rYXRleCAuc2l6aW5nLnJlc2V0LXNpemU2LnNpemUzLAoua2F0ZXggLmZvbnRzaXplLWVuc3VyZXIucmVzZXQtc2l6ZTYuc2l6ZTMgewogIGZvbnQtc2l6ZTogMC43ZW07Cn0KLmthdGV4IC5zaXppbmcucmVzZXQtc2l6ZTYuc2l6ZTQsCi5rYXRleCAuZm9udHNpemUtZW5zdXJlci5yZXNldC1zaXplNi5zaXplNCB7CiAgZm9udC1zaXplOiAwLjhlbTsKfQoua2F0ZXggLnNpemluZy5yZXNldC1zaXplNi5zaXplNSwKLmthdGV4IC5mb250c2l6ZS1lbnN1cmVyLnJlc2V0LXNpemU2LnNpemU1IHsKICBmb250LXNpemU6IDAuOWVtOwp9Ci5rYXRleCAuc2l6aW5nLnJlc2V0LXNpemU2LnNpemU2LAoua2F0ZXggLmZvbnRzaXplLWVuc3VyZXIucmVzZXQtc2l6ZTYuc2l6ZTYgewogIGZvbnQtc2l6ZTogMWVtOwp9Ci5rYXRleCAuc2l6aW5nLnJlc2V0LXNpemU2LnNpemU3LAoua2F0ZXggLmZvbnRzaXplLWVuc3VyZXIucmVzZXQtc2l6ZTYuc2l6ZTcgewogIGZvbnQtc2l6ZTogMS4yZW07Cn0KLmthdGV4IC5zaXppbmcucmVzZXQtc2l6ZTYuc2l6ZTgsCi5rYXRleCAuZm9udHNpemUtZW5zdXJlci5yZXNldC1zaXplNi5zaXplOCB7CiAgZm9udC1zaXplOiAxLjQ0ZW07Cn0KLmthdGV4IC5zaXppbmcucmVzZXQtc2l6ZTYuc2l6ZTksCi5rYXRleCAuZm9udHNpemUtZW5zdXJlci5yZXNldC1zaXplNi5zaXplOSB7CiAgZm9udC1zaXplOiAxLjcyOGVtOwp9Ci5rYXRleCAuc2l6aW5nLnJlc2V0LXNpemU2LnNpemUxMCwKLmthdGV4IC5mb250c2l6ZS1lbnN1cmVyLnJlc2V0LXNpemU2LnNpemUxMCB7CiAgZm9udC1zaXplOiAyLjA3NGVtOwp9Ci5rYXRleCAuc2l6aW5nLnJlc2V0LXNpemU2LnNpemUxMSwKLmthdGV4IC5mb250c2l6ZS1lbnN1cmVyLnJlc2V0LXNpemU2LnNpemUxMSB7CiAgZm9udC1zaXplOiAyLjQ4OGVtOwp9Ci5rYXRleCAuc2l6aW5nLnJlc2V0LXNpemU3LnNpemUxLAoua2F0ZXggLmZvbnRzaXplLWVuc3VyZXIucmVzZXQtc2l6ZTcuc2l6ZTEgewogIGZvbnQtc2l6ZTogMC40MTY2NjY2N2VtOwp9Ci5rYXRleCAuc2l6aW5nLnJlc2V0LXNpemU3LnNpemUyLAoua2F0ZXggLmZvbnRzaXplLWVuc3VyZXIucmVzZXQtc2l6ZTcuc2l6ZTIgewogIGZvbnQtc2l6ZTogMC41ZW07Cn0KLmthdGV4IC5zaXppbmcucmVzZXQtc2l6ZTcuc2l6ZTMsCi5rYXRleCAuZm9udHNpemUtZW5zdXJlci5yZXNldC1zaXplNy5zaXplMyB7CiAgZm9udC1zaXplOiAwLjU4MzMzMzMzZW07Cn0KLmthdGV4IC5zaXppbmcucmVzZXQtc2l6ZTcuc2l6ZTQsCi5rYXRleCAuZm9udHNpemUtZW5zdXJlci5yZXNldC1zaXplNy5zaXplNCB7CiAgZm9udC1zaXplOiAwLjY2NjY2NjY3ZW07Cn0KLmthdGV4IC5zaXppbmcucmVzZXQtc2l6ZTcuc2l6ZTUsCi5rYXRleCAuZm9udHNpemUtZW5zdXJlci5yZXNldC1zaXplNy5zaXplNSB7CiAgZm9udC1zaXplOiAwLjc1ZW07Cn0KLmthdGV4IC5zaXppbmcucmVzZXQtc2l6ZTcuc2l6ZTYsCi5rYXRleCAuZm9udHNpemUtZW5zdXJlci5yZXNldC1zaXplNy5zaXplNiB7CiAgZm9udC1zaXplOiAwLjgzMzMzMzMzZW07Cn0KLmthdGV4IC5zaXppbmcucmVzZXQtc2l6ZTcuc2l6ZTcsCi5rYXRleCAuZm9udHNpemUtZW5zdXJlci5yZXNldC1zaXplNy5zaXplNyB7CiAgZm9udC1zaXplOiAxZW07Cn0KLmthdGV4IC5zaXppbmcucmVzZXQtc2l6ZTcuc2l6ZTgsCi5rYXRleCAuZm9udHNpemUtZW5zdXJlci5yZXNldC1zaXplNy5zaXplOCB7CiAgZm9udC1zaXplOiAxLjJlbTsKfQoua2F0ZXggLnNpemluZy5yZXNldC1zaXplNy5zaXplOSwKLmthdGV4IC5mb250c2l6ZS1lbnN1cmVyLnJlc2V0LXNpemU3LnNpemU5IHsKICBmb250LXNpemU6IDEuNDRlbTsKfQoua2F0ZXggLnNpemluZy5yZXNldC1zaXplNy5zaXplMTAsCi5rYXRleCAuZm9udHNpemUtZW5zdXJlci5yZXNldC1zaXplNy5zaXplMTAgewogIGZvbnQtc2l6ZTogMS43MjgzMzMzM2VtOwp9Ci5rYXRleCAuc2l6aW5nLnJlc2V0LXNpemU3LnNpemUxMSwKLmthdGV4IC5mb250c2l6ZS1lbnN1cmVyLnJlc2V0LXNpemU3LnNpemUxMSB7CiAgZm9udC1zaXplOiAyLjA3MzMzMzMzZW07Cn0KLmthdGV4IC5zaXppbmcucmVzZXQtc2l6ZTguc2l6ZTEsCi5rYXRleCAuZm9udHNpemUtZW5zdXJlci5yZXNldC1zaXplOC5zaXplMSB7CiAgZm9udC1zaXplOiAwLjM0NzIyMjIyZW07Cn0KLmthdGV4IC5zaXppbmcucmVzZXQtc2l6ZTguc2l6ZTIsCi5rYXRleCAuZm9udHNpemUtZW5zdXJlci5yZXNldC1zaXplOC5zaXplMiB7CiAgZm9udC1zaXplOiAwLjQxNjY2NjY3ZW07Cn0KLmthdGV4IC5zaXppbmcucmVzZXQtc2l6ZTguc2l6ZTMsCi5rYXRleCAuZm9udHNpemUtZW5zdXJlci5yZXNldC1zaXplOC5zaXplMyB7CiAgZm9udC1zaXplOiAwLjQ4NjExMTExZW07Cn0KLmthdGV4IC5zaXppbmcucmVzZXQtc2l6ZTguc2l6ZTQsCi5rYXRleCAuZm9udHNpemUtZW5zdXJlci5yZXNldC1zaXplOC5zaXplNCB7CiAgZm9udC1zaXplOiAwLjU1NTU1NTU2ZW07Cn0KLmthdGV4IC5zaXppbmcucmVzZXQtc2l6ZTguc2l6ZTUsCi5rYXRleCAuZm9udHNpemUtZW5zdXJlci5yZXNldC1zaXplOC5zaXplNSB7CiAgZm9udC1zaXplOiAwLjYyNWVtOwp9Ci5rYXRleCAuc2l6aW5nLnJlc2V0LXNpemU4LnNpemU2LAoua2F0ZXggLmZvbnRzaXplLWVuc3VyZXIucmVzZXQtc2l6ZTguc2l6ZTYgewogIGZvbnQtc2l6ZTogMC42OTQ0NDQ0NGVtOwp9Ci5rYXRleCAuc2l6aW5nLnJlc2V0LXNpemU4LnNpemU3LAoua2F0ZXggLmZvbnRzaXplLWVuc3VyZXIucmVzZXQtc2l6ZTguc2l6ZTcgewogIGZvbnQtc2l6ZTogMC44MzMzMzMzM2VtOwp9Ci5rYXRleCAuc2l6aW5nLnJlc2V0LXNpemU4LnNpemU4LAoua2F0ZXggLmZvbnRzaXplLWVuc3VyZXIucmVzZXQtc2l6ZTguc2l6ZTggewogIGZvbnQtc2l6ZTogMWVtOwp9Ci5rYXRleCAuc2l6aW5nLnJlc2V0LXNpemU4LnNpemU5LAoua2F0ZXggLmZvbnRzaXplLWVuc3VyZXIucmVzZXQtc2l6ZTguc2l6ZTkgewogIGZvbnQtc2l6ZTogMS4yZW07Cn0KLmthdGV4IC5zaXppbmcucmVzZXQtc2l6ZTguc2l6ZTEwLAoua2F0ZXggLmZvbnRzaXplLWVuc3VyZXIucmVzZXQtc2l6ZTguc2l6ZTEwIHsKICBmb250LXNpemU6IDEuNDQwMjc3NzhlbTsKfQoua2F0ZXggLnNpemluZy5yZXNldC1zaXplOC5zaXplMTEsCi5rYXRleCAuZm9udHNpemUtZW5zdXJlci5yZXNldC1zaXplOC5zaXplMTEgewogIGZvbnQtc2l6ZTogMS43Mjc3Nzc3OGVtOwp9Ci5rYXRleCAuc2l6aW5nLnJlc2V0LXNpemU5LnNpemUxLAoua2F0ZXggLmZvbnRzaXplLWVuc3VyZXIucmVzZXQtc2l6ZTkuc2l6ZTEgewogIGZvbnQtc2l6ZTogMC4yODkzNTE4NWVtOwp9Ci5rYXRleCAuc2l6aW5nLnJlc2V0LXNpemU5LnNpemUyLAoua2F0ZXggLmZvbnRzaXplLWVuc3VyZXIucmVzZXQtc2l6ZTkuc2l6ZTIgewogIGZvbnQtc2l6ZTogMC4zNDcyMjIyMmVtOwp9Ci5rYXRleCAuc2l6aW5nLnJlc2V0LXNpemU5LnNpemUzLAoua2F0ZXggLmZvbnRzaXplLWVuc3VyZXIucmVzZXQtc2l6ZTkuc2l6ZTMgewogIGZvbnQtc2l6ZTogMC40MDUwOTI1OWVtOwp9Ci5rYXRleCAuc2l6aW5nLnJlc2V0LXNpemU5LnNpemU0LAoua2F0ZXggLmZvbnRzaXplLWVuc3VyZXIucmVzZXQtc2l6ZTkuc2l6ZTQgewogIGZvbnQtc2l6ZTogMC40NjI5NjI5NmVtOwp9Ci5rYXRleCAuc2l6aW5nLnJlc2V0LXNpemU5LnNpemU1LAoua2F0ZXggLmZvbnRzaXplLWVuc3VyZXIucmVzZXQtc2l6ZTkuc2l6ZTUgewogIGZvbnQtc2l6ZTogMC41MjA4MzMzM2VtOwp9Ci5rYXRleCAuc2l6aW5nLnJlc2V0LXNpemU5LnNpemU2LAoua2F0ZXggLmZvbnRzaXplLWVuc3VyZXIucmVzZXQtc2l6ZTkuc2l6ZTYgewogIGZvbnQtc2l6ZTogMC41Nzg3MDM3ZW07Cn0KLmthdGV4IC5zaXppbmcucmVzZXQtc2l6ZTkuc2l6ZTcsCi5rYXRleCAuZm9udHNpemUtZW5zdXJlci5yZXNldC1zaXplOS5zaXplNyB7CiAgZm9udC1zaXplOiAwLjY5NDQ0NDQ0ZW07Cn0KLmthdGV4IC5zaXppbmcucmVzZXQtc2l6ZTkuc2l6ZTgsCi5rYXRleCAuZm9udHNpemUtZW5zdXJlci5yZXNldC1zaXplOS5zaXplOCB7CiAgZm9udC1zaXplOiAwLjgzMzMzMzMzZW07Cn0KLmthdGV4IC5zaXppbmcucmVzZXQtc2l6ZTkuc2l6ZTksCi5rYXRleCAuZm9udHNpemUtZW5zdXJlci5yZXNldC1zaXplOS5zaXplOSB7CiAgZm9udC1zaXplOiAxZW07Cn0KLmthdGV4IC5zaXppbmcucmVzZXQtc2l6ZTkuc2l6ZTEwLAoua2F0ZXggLmZvbnRzaXplLWVuc3VyZXIucmVzZXQtc2l6ZTkuc2l6ZTEwIHsKICBmb250LXNpemU6IDEuMjAwMjMxNDhlbTsKfQoua2F0ZXggLnNpemluZy5yZXNldC1zaXplOS5zaXplMTEsCi5rYXRleCAuZm9udHNpemUtZW5zdXJlci5yZXNldC1zaXplOS5zaXplMTEgewogIGZvbnQtc2l6ZTogMS40Mzk4MTQ4MWVtOwp9Ci5rYXRleCAuc2l6aW5nLnJlc2V0LXNpemUxMC5zaXplMSwKLmthdGV4IC5mb250c2l6ZS1lbnN1cmVyLnJlc2V0LXNpemUxMC5zaXplMSB7CiAgZm9udC1zaXplOiAwLjI0MTA4MDA0ZW07Cn0KLmthdGV4IC5zaXppbmcucmVzZXQtc2l6ZTEwLnNpemUyLAoua2F0ZXggLmZvbnRzaXplLWVuc3VyZXIucmVzZXQtc2l6ZTEwLnNpemUyIHsKICBmb250LXNpemU6IDAuMjg5Mjk2MDVlbTsKfQoua2F0ZXggLnNpemluZy5yZXNldC1zaXplMTAuc2l6ZTMsCi5rYXRleCAuZm9udHNpemUtZW5zdXJlci5yZXNldC1zaXplMTAuc2l6ZTMgewogIGZvbnQtc2l6ZTogMC4zMzc1MTIwNWVtOwp9Ci5rYXRleCAuc2l6aW5nLnJlc2V0LXNpemUxMC5zaXplNCwKLmthdGV4IC5mb250c2l6ZS1lbnN1cmVyLnJlc2V0LXNpemUxMC5zaXplNCB7CiAgZm9udC1zaXplOiAwLjM4NTcyODA2ZW07Cn0KLmthdGV4IC5zaXppbmcucmVzZXQtc2l6ZTEwLnNpemU1LAoua2F0ZXggLmZvbnRzaXplLWVuc3VyZXIucmVzZXQtc2l6ZTEwLnNpemU1IHsKICBmb250LXNpemU6IDAuNDMzOTQ0MDdlbTsKfQoua2F0ZXggLnNpemluZy5yZXNldC1zaXplMTAuc2l6ZTYsCi5rYXRleCAuZm9udHNpemUtZW5zdXJlci5yZXNldC1zaXplMTAuc2l6ZTYgewogIGZvbnQtc2l6ZTogMC40ODIxNjAwOGVtOwp9Ci5rYXRleCAuc2l6aW5nLnJlc2V0LXNpemUxMC5zaXplNywKLmthdGV4IC5mb250c2l6ZS1lbnN1cmVyLnJlc2V0LXNpemUxMC5zaXplNyB7CiAgZm9udC1zaXplOiAwLjU3ODU5MjA5ZW07Cn0KLmthdGV4IC5zaXppbmcucmVzZXQtc2l6ZTEwLnNpemU4LAoua2F0ZXggLmZvbnRzaXplLWVuc3VyZXIucmVzZXQtc2l6ZTEwLnNpemU4IHsKICBmb250LXNpemU6IDAuNjk0MzEwNTFlbTsKfQoua2F0ZXggLnNpemluZy5yZXNldC1zaXplMTAuc2l6ZTksCi5rYXRleCAuZm9udHNpemUtZW5zdXJlci5yZXNldC1zaXplMTAuc2l6ZTkgewogIGZvbnQtc2l6ZTogMC44MzMxNzI2MWVtOwp9Ci5rYXRleCAuc2l6aW5nLnJlc2V0LXNpemUxMC5zaXplMTAsCi5rYXRleCAuZm9udHNpemUtZW5zdXJlci5yZXNldC1zaXplMTAuc2l6ZTEwIHsKICBmb250LXNpemU6IDFlbTsKfQoua2F0ZXggLnNpemluZy5yZXNldC1zaXplMTAuc2l6ZTExLAoua2F0ZXggLmZvbnRzaXplLWVuc3VyZXIucmVzZXQtc2l6ZTEwLnNpemUxMSB7CiAgZm9udC1zaXplOiAxLjE5OTYxNDI3ZW07Cn0KLmthdGV4IC5zaXppbmcucmVzZXQtc2l6ZTExLnNpemUxLAoua2F0ZXggLmZvbnRzaXplLWVuc3VyZXIucmVzZXQtc2l6ZTExLnNpemUxIHsKICBmb250LXNpemU6IDAuMjAwOTY0NjNlbTsKfQoua2F0ZXggLnNpemluZy5yZXNldC1zaXplMTEuc2l6ZTIsCi5rYXRleCAuZm9udHNpemUtZW5zdXJlci5yZXNldC1zaXplMTEuc2l6ZTIgewogIGZvbnQtc2l6ZTogMC4yNDExNTc1NmVtOwp9Ci5rYXRleCAuc2l6aW5nLnJlc2V0LXNpemUxMS5zaXplMywKLmthdGV4IC5mb250c2l6ZS1lbnN1cmVyLnJlc2V0LXNpemUxMS5zaXplMyB7CiAgZm9udC1zaXplOiAwLjI4MTM1MDQ4ZW07Cn0KLmthdGV4IC5zaXppbmcucmVzZXQtc2l6ZTExLnNpemU0LAoua2F0ZXggLmZvbnRzaXplLWVuc3VyZXIucmVzZXQtc2l6ZTExLnNpemU0IHsKICBmb250LXNpemU6IDAuMzIxNTQzNDFlbTsKfQoua2F0ZXggLnNpemluZy5yZXNldC1zaXplMTEuc2l6ZTUsCi5rYXRleCAuZm9udHNpemUtZW5zdXJlci5yZXNldC1zaXplMTEuc2l6ZTUgewogIGZvbnQtc2l6ZTogMC4zNjE3MzYzM2VtOwp9Ci5rYXRleCAuc2l6aW5nLnJlc2V0LXNpemUxMS5zaXplNiwKLmthdGV4IC5mb250c2l6ZS1lbnN1cmVyLnJlc2V0LXNpemUxMS5zaXplNiB7CiAgZm9udC1zaXplOiAwLjQwMTkyOTI2ZW07Cn0KLmthdGV4IC5zaXppbmcucmVzZXQtc2l6ZTExLnNpemU3LAoua2F0ZXggLmZvbnRzaXplLWVuc3VyZXIucmVzZXQtc2l6ZTExLnNpemU3IHsKICBmb250LXNpemU6IDAuNDgyMzE1MTFlbTsKfQoua2F0ZXggLnNpemluZy5yZXNldC1zaXplMTEuc2l6ZTgsCi5rYXRleCAuZm9udHNpemUtZW5zdXJlci5yZXNldC1zaXplMTEuc2l6ZTggewogIGZvbnQtc2l6ZTogMC41Nzg3NzgxNGVtOwp9Ci5rYXRleCAuc2l6aW5nLnJlc2V0LXNpemUxMS5zaXplOSwKLmthdGV4IC5mb250c2l6ZS1lbnN1cmVyLnJlc2V0LXNpemUxMS5zaXplOSB7CiAgZm9udC1zaXplOiAwLjY5NDUzMzc2ZW07Cn0KLmthdGV4IC5zaXppbmcucmVzZXQtc2l6ZTExLnNpemUxMCwKLmthdGV4IC5mb250c2l6ZS1lbnN1cmVyLnJlc2V0LXNpemUxMS5zaXplMTAgewogIGZvbnQtc2l6ZTogMC44MzM2MDEyOWVtOwp9Ci5rYXRleCAuc2l6aW5nLnJlc2V0LXNpemUxMS5zaXplMTEsCi5rYXRleCAuZm9udHNpemUtZW5zdXJlci5yZXNldC1zaXplMTEuc2l6ZTExIHsKICBmb250LXNpemU6IDFlbTsKfQoua2F0ZXggLmRlbGltc2l6aW5nLnNpemUxIHsKICBmb250LWZhbWlseTogS2FUZVhfU2l6ZTE7Cn0KLmthdGV4IC5kZWxpbXNpemluZy5zaXplMiB7CiAgZm9udC1mYW1pbHk6IEthVGVYX1NpemUyOwp9Ci5rYXRleCAuZGVsaW1zaXppbmcuc2l6ZTMgewogIGZvbnQtZmFtaWx5OiBLYVRlWF9TaXplMzsKfQoua2F0ZXggLmRlbGltc2l6aW5nLnNpemU0IHsKICBmb250LWZhbWlseTogS2FUZVhfU2l6ZTQ7Cn0KLmthdGV4IC5kZWxpbXNpemluZy5tdWx0IC5kZWxpbS1zaXplMSA+IHNwYW4gewogIGZvbnQtZmFtaWx5OiBLYVRlWF9TaXplMTsKfQoua2F0ZXggLmRlbGltc2l6aW5nLm11bHQgLmRlbGltLXNpemU0ID4gc3BhbiB7CiAgZm9udC1mYW1pbHk6IEthVGVYX1NpemU0Owp9Ci5rYXRleCAubnVsbGRlbGltaXRlciB7CiAgZGlzcGxheTogaW5saW5lLWJsb2NrOwogIHdpZHRoOiAwLjEyZW07Cn0KLmthdGV4IC5kZWxpbWNlbnRlciB7CiAgcG9zaXRpb246IHJlbGF0aXZlOwp9Ci5rYXRleCAub3Atc3ltYm9sIHsKICBwb3NpdGlvbjogcmVsYXRpdmU7Cn0KLmthdGV4IC5vcC1zeW1ib2wuc21hbGwtb3AgewogIGZvbnQtZmFtaWx5OiBLYVRlWF9TaXplMTsKfQoua2F0ZXggLm9wLXN5bWJvbC5sYXJnZS1vcCB7CiAgZm9udC1mYW1pbHk6IEthVGVYX1NpemUyOwp9Ci5rYXRleCAub3AtbGltaXRzID4gLnZsaXN0LXQgewogIHRleHQtYWxpZ246IGNlbnRlcjsKfQoua2F0ZXggLmFjY2VudCA+IC52bGlzdC10IHsKICB0ZXh0LWFsaWduOiBjZW50ZXI7Cn0KLmthdGV4IC5hY2NlbnQgLmFjY2VudC1ib2R5IHsKICBwb3NpdGlvbjogcmVsYXRpdmU7Cn0KLmthdGV4IC5hY2NlbnQgLmFjY2VudC1ib2R5Om5vdCguYWNjZW50LWZ1bGwpIHsKICB3aWR0aDogMDsKfQoua2F0ZXggLm92ZXJsYXkgewogIGRpc3BsYXk6IGJsb2NrOwp9Ci5rYXRleCAubXRhYmxlIC52ZXJ0aWNhbC1zZXBhcmF0b3IgewogIGRpc3BsYXk6IGlubGluZS1ibG9jazsKICBtaW4td2lkdGg6IDFweDsKfQoua2F0ZXggLm10YWJsZSAuYXJyYXljb2xzZXAgewogIGRpc3BsYXk6IGlubGluZS1ibG9jazsKfQoua2F0ZXggLm10YWJsZSAuY29sLWFsaWduLWMgPiAudmxpc3QtdCB7CiAgdGV4dC1hbGlnbjogY2VudGVyOwp9Ci5rYXRleCAubXRhYmxlIC5jb2wtYWxpZ24tbCA+IC52bGlzdC10IHsKICB0ZXh0LWFsaWduOiBsZWZ0Owp9Ci5rYXRleCAubXRhYmxlIC5jb2wtYWxpZ24tciA+IC52bGlzdC10IHsKICB0ZXh0LWFsaWduOiByaWdodDsKfQoua2F0ZXggLnN2Zy1hbGlnbiB7CiAgdGV4dC1hbGlnbjogbGVmdDsKfQoua2F0ZXggc3ZnIHsKICBkaXNwbGF5OiBibG9jazsKICBwb3NpdGlvbjogYWJzb2x1dGU7CiAgd2lkdGg6IDEwMCU7CiAgaGVpZ2h0OiBpbmhlcml0OwogIGZpbGw6IGN1cnJlbnRDb2xvcjsKICBzdHJva2U6IGN1cnJlbnRDb2xvcjsKICBmaWxsLXJ1bGU6IG5vbnplcm87CiAgZmlsbC1vcGFjaXR5OiAxOwogIHN0cm9rZS13aWR0aDogMTsKICBzdHJva2UtbGluZWNhcDogYnV0dDsKICBzdHJva2UtbGluZWpvaW46IG1pdGVyOwogIHN0cm9rZS1taXRlcmxpbWl0OiA0OwogIHN0cm9rZS1kYXNoYXJyYXk6IG5vbmU7CiAgc3Ryb2tlLWRhc2hvZmZzZXQ6IDA7CiAgc3Ryb2tlLW9wYWNpdHk6IDE7Cn0KLmthdGV4IHN2ZyBwYXRoIHsKICBzdHJva2U6IG5vbmU7Cn0KLmthdGV4IGltZyB7CiAgYm9yZGVyLXN0eWxlOiBub25lOwogIG1pbi13aWR0aDogMDsKICBtaW4taGVpZ2h0OiAwOwogIG1heC13aWR0aDogbm9uZTsKICBtYXgtaGVpZ2h0OiBub25lOwp9Ci5rYXRleCAuc3RyZXRjaHkgewogIHdpZHRoOiAxMDAlOwogIGRpc3BsYXk6IGJsb2NrOwogIHBvc2l0aW9uOiByZWxhdGl2ZTsKICBvdmVyZmxvdzogaGlkZGVuOwp9Ci5rYXRleCAuc3RyZXRjaHk6OmJlZm9yZSwKLmthdGV4IC5zdHJldGNoeTo6YWZ0ZXIgewogIGNvbnRlbnQ6ICIiOwp9Ci5rYXRleCAuaGlkZS10YWlsIHsKICB3aWR0aDogMTAwJTsKICBwb3NpdGlvbjogcmVsYXRpdmU7CiAgb3ZlcmZsb3c6IGhpZGRlbjsKfQoua2F0ZXggLmhhbGZhcnJvdy1sZWZ0IHsKICBwb3NpdGlvbjogYWJzb2x1dGU7CiAgbGVmdDogMDsKICB3aWR0aDogNTAuMiU7CiAgb3ZlcmZsb3c6IGhpZGRlbjsKfQoua2F0ZXggLmhhbGZhcnJvdy1yaWdodCB7CiAgcG9zaXRpb246IGFic29sdXRlOwogIHJpZ2h0OiAwOwogIHdpZHRoOiA1MC4yJTsKICBvdmVyZmxvdzogaGlkZGVuOwp9Ci5rYXRleCAuYnJhY2UtbGVmdCB7CiAgcG9zaXRpb246IGFic29sdXRlOwogIGxlZnQ6IDA7CiAgd2lkdGg6IDI1LjElOwogIG92ZXJmbG93OiBoaWRkZW47Cn0KLmthdGV4IC5icmFjZS1jZW50ZXIgewogIHBvc2l0aW9uOiBhYnNvbHV0ZTsKICBsZWZ0OiAyNSU7CiAgd2lkdGg6IDUwJTsKICBvdmVyZmxvdzogaGlkZGVuOwp9Ci5rYXRleCAuYnJhY2UtcmlnaHQgewogIHBvc2l0aW9uOiBhYnNvbHV0ZTsKICByaWdodDogMDsKICB3aWR0aDogMjUuMSU7CiAgb3ZlcmZsb3c6IGhpZGRlbjsKfQoua2F0ZXggLngtYXJyb3ctcGFkIHsKICBwYWRkaW5nOiAwIDAuNWVtOwp9Ci5rYXRleCAuY2QtYXJyb3ctcGFkIHsKICBwYWRkaW5nOiAwIDAuNTU1NTZlbSAwIDAuMjc3NzhlbTsKfQoua2F0ZXggLngtYXJyb3csCi5rYXRleCAubW92ZXIsCi5rYXRleCAubXVuZGVyIHsKICB0ZXh0LWFsaWduOiBjZW50ZXI7Cn0KLmthdGV4IC5ib3hwYWQgewogIHBhZGRpbmc6IDAgMC4zZW0gMCAwLjNlbTsKfQoua2F0ZXggLmZib3gsCi5rYXRleCAuZmNvbG9yYm94IHsKICBib3gtc2l6aW5nOiBib3JkZXItYm94OwogIGJvcmRlcjogMC4wNGVtIHNvbGlkOwp9Ci5rYXRleCAuY2FuY2VsLXBhZCB7CiAgcGFkZGluZzogMCAwLjJlbSAwIDAuMmVtOwp9Ci5rYXRleCAuY2FuY2VsLWxhcCB7CiAgbWFyZ2luLWxlZnQ6IC0wLjJlbTsKICBtYXJnaW4tcmlnaHQ6IC0wLjJlbTsKfQoua2F0ZXggLnNvdXQgewogIGJvcmRlci1ib3R0b20tc3R5bGU6IHNvbGlkOwogIGJvcmRlci1ib3R0b20td2lkdGg6IDAuMDhlbTsKfQoua2F0ZXggLmFuZ2wgewogIGJveC1zaXppbmc6IGJvcmRlci1jb250ZW50OwogIGJvcmRlci10b3A6IDAuMDQ5ZW0gc29saWQ7CiAgYm9yZGVyLXJpZ2h0OiAwLjA0OWVtIHNvbGlkOwogIG1hcmdpbi1yaWdodDogMC4wMzg4OWVtOwp9Ci5rYXRleCAuYW5nbHBhZCB7CiAgcGFkZGluZzogMCAwLjAzODg5ZW0gMCAwLjAzODg5ZW07Cn0KLmthdGV4IC5lcW4tbnVtOjpiZWZvcmUgewogIGNvdW50ZXItaW5jcmVtZW50OiBrYXRleEVxbk5vOwogIGNvbnRlbnQ6ICIoIiBjb3VudGVyKGthdGV4RXFuTm8pICIpIjsKfQoua2F0ZXggLm1tbC1lcW4tbnVtOjpiZWZvcmUgewogIGNvdW50ZXItaW5jcmVtZW50OiBtbWxFcW5ObzsKICBjb250ZW50OiAiKCIgY291bnRlcihtbWxFcW5ObykgIikiOwp9Ci5rYXRleCAubXRyLWdsdWUgewogIHdpZHRoOiA1MCU7Cn0KLmthdGV4IC5jZC12ZXJ0LWFycm93IHsKICBkaXNwbGF5OiBpbmxpbmUtYmxvY2s7CiAgcG9zaXRpb246IHJlbGF0aXZlOwp9Ci5rYXRleCAuY2QtbGFiZWwtbGVmdCB7CiAgZGlzcGxheTogaW5saW5lLWJsb2NrOwogIHBvc2l0aW9uOiBhYnNvbHV0ZTsKICByaWdodDogLXdlYmtpdC1jYWxjKDUwJSArIDAuM2VtKTsKICByaWdodDogY2FsYyg1MCUgKyAwLjNlbSk7CiAgdGV4dC1hbGlnbjogbGVmdDsKfQoua2F0ZXggLmNkLWxhYmVsLXJpZ2h0IHsKICBkaXNwbGF5OiBpbmxpbmUtYmxvY2s7CiAgcG9zaXRpb246IGFic29sdXRlOwogIGxlZnQ6IC13ZWJraXQtY2FsYyg1MCUgKyAwLjNlbSk7CiAgbGVmdDogY2FsYyg1MCUgKyAwLjNlbSk7CiAgdGV4dC1hbGlnbjogcmlnaHQ7Cn0KLmthdGV4LWRpc3BsYXkgewogIGRpc3BsYXk6IGJsb2NrOwogIG1hcmdpbjogMWVtIDA7CiAgdGV4dC1hbGlnbjogY2VudGVyOwp9Ci5rYXRleC1kaXNwbGF5ID4gLmthdGV4IHsKICBkaXNwbGF5OiBibG9jazsKICB0ZXh0LWFsaWduOiBjZW50ZXI7CiAgd2hpdGUtc3BhY2U6IG5vd3JhcDsKfQoua2F0ZXgtZGlzcGxheSA+IC5rYXRleCA+IC5rYXRleC1odG1sIHsKICBkaXNwbGF5OiBibG9jazsKICBwb3NpdGlvbjogcmVsYXRpdmU7Cn0KLmthdGV4LWRpc3BsYXkgPiAua2F0ZXggPiAua2F0ZXgtaHRtbCA+IC50YWcgewogIHBvc2l0aW9uOiBhYnNvbHV0ZTsKICByaWdodDogMDsKfQoua2F0ZXgtZGlzcGxheS5sZXFubyA+IC5rYXRleCA+IC5rYXRleC1odG1sID4gLnRhZyB7CiAgbGVmdDogMDsKICByaWdodDogYXV0bzsKfQoua2F0ZXgtZGlzcGxheS5mbGVxbiA+IC5rYXRleCB7CiAgdGV4dC1hbGlnbjogbGVmdDsKICBwYWRkaW5nLWxlZnQ6IDJlbTsKfQpib2R5IHsKICBjb3VudGVyLXJlc2V0OiBrYXRleEVxbk5vIG1tbEVxbk5vOwp9CgoKLkJELVRlWC1wbHVnaW4gewogIC0tZm9udC1zaXplOiAxLjVlbTsKICAtLWltYWdlLWZvbnQtc2l6ZTogMjRweDsKICAtLXRleHQtY29sb3I6IHJnYigyMTcsIDEwOSwgMjIpOwogIGNvbG9yOiB2YXIoLS10ZXh0LWNvbG9yKTsKICBiYWNrZ3JvdW5kOiByZ2JhKDAsIDAsIDAsIDAuNSk7CiAgcGFkZGluZzogMTBweDsKICBtaW4taGVpZ2h0OiBjYWxjKHZhcigtLWZvbnQtc2l6ZSkgKiAxLjI1KTsKfQouQkQtVGVYLXBsdWdpbi5CRC1UZVgtcGx1Z2luLkJELVRlWC1wbHVnaW4gLmthdGV4LmthdGV4LmthdGV4IHsKICBmb250OiBub3JtYWwgdmFyKC0tZm9udC1zaXplKSBLYVRlWF9NYWluLCBUaW1lcyBOZXcgUm9tYW4sIHNlcmlmICFpbXBvcnRhbnQ7Cn0KCi5CRC1UZVgtcGx1Z2luLkJELVRlWC1wbHVnaW4taW1hZ2UtcmVuZGVyIHsKICBiYWNrZ3JvdW5kOiBub25lOwogIHBhZGRpbmc6IDJweDsKICB3aWR0aDogZml0LWNvbnRlbnQ7CiAgbWluLWhlaWdodDogdW5zZXQ7Cn0KLkJELVRlWC1wbHVnaW4uQkQtVGVYLXBsdWdpbi5CRC1UZVgtcGx1Z2luLkJELVRlWC1wbHVnaW4taW1hZ2UtcmVuZGVyIC5rYXRleC5rYXRleC5rYXRleCB7CiAgZm9udDogbm9ybWFsIHZhcigtLWltYWdlLWZvbnQtc2l6ZSkgS2FUZVhfTWFpbiwgVGltZXMgTmV3IFJvbWFuLCBzZXJpZiAhaW1wb3J0YW50Owp9Cmh0bWwgLkJELVRlWC1wbHVnaW4taW1hZ2UtcmVuZGVyIHsKICB2aXNpYmlsaXR5OiBoaWRkZW47Cn0KCi5CRC1UZVgtcGx1Z2luLWJ1dHRvbi1pY29uIHsKICBmaWxsOiBjdXJyZW50Q29sb3I7Cn0KCg=='); +const texIconSVG = atob("PHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgd2lkdGg9IjUxMiIgaGVpZ2h0PSI1MTIiPjxwYXRoIGQ9Ik01MDcuNzEyIDMxMS43NGMtNy45MDMgMC0xMy40OTMtMC43NTctMTYuNzcyLTIuMjdzLTYuNTk5LTQuNDU2LTkuOTYyLTguODI3bC02Mi4yOTgtODkuMjg0IDM5Ljg1LTU3Ljc1N2MzLjM2My00LjU0IDcuOTQ1LTguNjYgMTMuNzQ2LTEyLjM1OHMxNS4zNDMtNS41NDkgMjguNjI3LTUuNTQ5di04LjgyOGgtNjguODU1djguODI4YzUuNTQ5IDAgOS44NzggMS4zODcgMTIuOTg5IDQuMTYxczQuNjY2IDUuODQzIDQuNjY2IDkuMjA2YzAgMS4xNzctMC4wODQgMi4zMTItMC4yNTIgMy40MDVzLTAuODQxIDIuMjI4LTIuMDE4IDMuNDA1bC0zMy4yOTMgNDguOTMtMzcuODMyLTU1Ljc0LTEuMTM1LTEuMzg3YzAgMC0wLjM3OC0wLjk2Ny0xLjEzNS0yLjkgMC0yLjM1NCAxLjU5OC00LjQ1NiA0Ljc5Mi02LjMwNXM3LjU2Ny0yLjc3NCAxMy4xMTUtMi43NzR2LTguODI4aC04MC4yMDV2OC44MjhoNC41NGM3LjczNSAwIDEzLjUzNiAwLjY3MyAxNy40MDMgMi4wMThzNi44OTQgMy42OTkgOS4wODAgNy4wNjJsNTMuNDcgNzkuOTUyLTQ2LjY2IDY5LjEwOGMtMi4xODYgMy4zNjMtNi4xOCA3LjE4OC0xMS45ODEgMTEuNDc2cy0xNS45MzIgNi40MzItMzAuMzkyIDYuNDMydjguODI3aDE2LjQxM2MtMy4zMTkgMTcuNzM5LTcuOTQ3IDMxLjExNC0xMy44OTIgNDAuMTAzLTYuODk0IDEwLjQyNS0yMi41MzEgMTUuNjM3LTQ2LjkxMyAxNS42MzdoLTM3LjgzM2MtNi43MjYgMC0xMC42MzUtMC43NTctMTEuNzI4LTIuMjdzLTEuNjM5LTQuNDU2LTEuNjM5LTguODI3di03NS45MTdoMjQuNDY1YzEzLjI4NCAwIDIxLjc3NCAyLjUyMiAyNS40NzQgNy41NjdzNS41NDkgMTMuNzA0IDUuNTQ5IDI1Ljk3OGg2Ljgxdi03NS42NjVoLTYuODFjMCAxMi4yNzQtMS41NTUgMjAuODkyLTQuNjY2IDI1Ljg1MnMtMTEuODk2IDcuNDQtMjYuMzU3IDcuNDRoLTI0LjQ2NXYtNjkuMTA4YzAtNC4zNzIgMC41NDctNy4zMTUgMS42MzktOC44MjdzNS4wMDItMi4yNyAxMS43MjgtMi4yN2gzNS41NjNjMjIuMTk1IDAgMzYuNDQ1IDQuMjg3IDQyLjc1IDEyLjg2M3MxMC42MzUgMjIuODY4IDEyLjk4OSA0Mi44NzdoNi41NThsLTguODI3LTY0LjU2N2gtMTI5LjI5NGwtNC4zODEtNjIuMjk4aC0xNzUuNzk1bC00LjU0IDY0LjU2N2g2LjU1OGMyLjM1NC0yMy4zNzIgNi4yMjItMzguNTA1IDExLjYwMi00NS4zOTlzMTguNjY0LTEwLjM0MSAzOS44NS0xMC4zNDFoMTUuMzg2YzMuMzYzIDAgNS4zMzggMS4wMDkgNS45MjcgMy4wMjZzMC44ODMgNC43MDggMC44ODMgOC4wNzF2MTQ5LjMxM2MwIDQuMzcyLTEuMjE5IDcuNjkyLTMuNjU3IDkuOTYycy0xMC44ODggMy40MDUtMjUuMzQ4IDMuNDA1aC0xMy4zNjd2OC44MjdoMTA2Ljk0di04LjgyN2gtOS4wODBjLTE0LjQ2IDAtMjIuODY4LTEuMTM1LTI1LjIyMS0zLjQwNXMtMy41MzEtNS41OTEtMy41MzEtOS45NjJ2LTE0OS4zMTNjMC0zLjM2MyAwLjE2OC01Ljc1OSAwLjUwNC03LjE4OHMxLjU5Ny0yLjczMiAzLjc4NC0zLjkxaDE1LjYzOGMyMS4xODYgMCAzNC40NjkgMy40NDcgMzkuODUgMTAuMzQxIDUuMjAyIDYuNjY2IDguOTA3IDIxLjA1OCAxMS4xMjMgNDMuMTI5aC0yMS45Njh2OC44MjdoNi41NThjMTEuMjY2IDAgMTcuODIzIDAuODQxIDE5LjY3MyAyLjUyMnMyLjc3NCA1LjI5NyAyLjc3NCAxMC44NDV2MTQ5LjMxM2MwIDUuNTQ5LTAuOTI1IDkuMTY0LTIuNzc0IDEwLjg0NnMtOC40MDcgMi41MjItMTkuNjczIDIuNTIyaC02LjU1OHY4LjgyN2gxNjQuNjk3bDkuNzYzLTY0LjU2N2g0OC4yNDd2LTguODI3Yy02LjcyNiAwLTExLjM5Mi0xLjY4Mi0xMy45OTgtNS4wNDVzLTMuOTA5LTYuMTM3LTMuOTA5LTguMzIzYzAtMS4xNzcgMC4wODQtMi4zMTIgMC4yNTItMy40MDVzMC44NDEtMi4yMjggMi4wMTctMy40MDVsNDAuMTAzLTYwLjAyOCA0NC4zOSA2OS4xMDdjMCAxLjAwOSAwLjM3OCAxLjU5OCAxLjEzNSAxLjc2NWwxLjEzNSAwLjI1MmMwIDIuMzU0LTEuNTU1IDQuNDU2LTQuNjY2IDYuMzA1cy03LjQ0IDIuNzc0LTEyLjk4OSAyLjc3NHY4LjgyN2g3OS45NTJ2LTguODI3aC00LjI4OHoiPjwvcGF0aD48L3N2Zz4="); + +const React = BdApi.React; + +class TeXModal extends React.PureComponent { + constructor(props) { + super(props); + this.textboxRef = React.createRef(); + this.katexRef = React.createRef(); + this.state = { + texInput: typeof props.initialValue === "string" ? props.initialValue : "" + }; + } + componentDidMount() { + this.disableSpellcheck(); + katex.render(this.state.texInput, this.katexRef.current, { + throwOnError: false, + strict: false, + trust: true + }); + } + componentDidUpdate() { + this.disableSpellcheck(); + } + disableSpellcheck() { + const textbox = BdApi.ReactDOM.findDOMNode(this.textboxRef.current).querySelector("textarea"); + textbox.setAttribute("autocomplete", "off"); + textbox.setAttribute("autocorrect", "off"); + textbox.setAttribute("autocapitalize", "off"); + textbox.setAttribute("spellcheck", "false"); + } + render() { + return React.createElement("div", null, React.createElement(BdApi.findModuleByDisplayName("TextArea"), { + ref: this.textboxRef, + value: this.state.texInput, + rows: 8, + onChange: val => { + this.setState({ + texInput: val + }); + katex.render(val, this.katexRef.current, { + throwOnError: false, + strict: false, + trust: true + }); + this.props?.onUpdate?.(val); + } + }), React.createElement("div", { + className: "BD-TeX-plugin", + ref: this.katexRef + })); + } +} + +function createTeXButton({onClick}) { + const ButtonData = ZeresPluginLibrary.DiscordModules.ButtonData; + const {button, contents, grow} = ZeresPluginLibrary.WebpackModules.find(m => m.button && m.grow); + const discordButton = ZeresPluginLibrary.WebpackModules.getByProps("icon", "hoverScale"); + const texButton = document.createElement("button"); + texButton.classList.add(ButtonData.ButtonColors.BRAND, ButtonData.ButtonLooks.BLANK, button, grow, "BD-TeX-plugin-button"); + const div = document.createElement("div"); + div.classList.add(ZeresPluginLibrary.DiscordClasses.Textarea.button, discordButton.button, contents); + const innerDiv = document.createElement("div"); + innerDiv.classList.add(discordButton.buttonWrapper); + innerDiv.innerHTML = texIconSVG.trim(); + const svg = innerDiv.firstElementChild; + svg.classList.add(discordButton.icon, "BD-TeX-plugin-button-icon"); + svg.setAttribute("width", "24"); + svg.setAttribute("height", "24"); + svg.setAttribute("viewBox", "0 0 512 512"); + innerDiv.append(svg); + div.append(innerDiv); + texButton.append(div); + texButton.onclick = onClick; + return texButton; +} + +class TeX { + constructor() { + this.settings = {}; + this.texInput = ""; + this.canvas = document.createElement("canvas"); + this.texButton = null; + } + async start() { + if (typeof ZeresPluginLibrary === "undefined") { + try { + await this.askToDownloadZeresPluginLibrary(); + // Wait for ZeresPluginLibrary to load if it didn't load yet + while (typeof ZeresPluginLibrary === "undefined") { + await this.delay(500); + } + } catch (e) { + console.error(`${this.constructor.name}: `, e); + return BdApi.showToast(`${this.constructor.name}: "ZeresPluginLibrary" was not downloaded, or the download failed. This plugin cannot start.`, {type: "error"}); + } + } + BdApi.injectCSS(this.constructor.name, css); + ZeresPluginLibrary.Patcher.after(this.constructor.name, ZeresPluginLibrary.WebpackModules.getByProps("instantBatchUpload"), "upload", () => { + this.texInput = ""; + }); + this.texButton = createTeXButton({ + onClick: () => this.showTeXModal() + }); + ZeresPluginLibrary?.PluginUpdater?.checkForUpdate?.(this.constructor.name, version, `https://raw.githubusercontent.com/Mega-Mewthree/BetterDiscordPlugins/master/Plugins/${this.constructor.name}/${this.constructor.name}.plugin.js`); + BdApi.showToast(`${this.constructor.name} has started!`); + const Textarea = ZeresPluginLibrary.DiscordClasses.Textarea; + while (!document.querySelector(`.${Textarea.channelTextArea} .${Textarea.buttons}`)) { + await this.delay(500); + } + this.injectButton(); + } + stop() { + this.texButton?.remove?.(); + BdApi.clearCSS(this.constructor.name); + ZeresPluginLibrary?.Patcher?.unpatchAll?.(this.constructor.name); + BdApi.showToast(`${this.constructor.name} has stopped!`); + } + onSwitch() { + this.injectButton(); + } + showTeXModal() { + BdApi.showConfirmationModal( + "TeX Input", + React.createElement(TeXModal, { + initialValue: this.texInput, + onUpdate: val => this.texInput = val + }), + { + confirmText: "Attach", + onConfirm: async () => { + if (this.texInput.length) { + const blob = await this.generateTeXImage(); + this.attachImage(blob); + } + } + } + ); + } + async htmlToCanvas(html, canvas, width, height) { + canvas.width = width; + canvas.height = height; + if (!this.svgDocument) { + this.svgDocument = document.implementation.createHTMLDocument(); + this.svgDocument.write(html); + this.svgDocument.close(); + this.svgDocument.documentElement.setAttribute("xmlns", "http://www.w3.org/2000/svg"); + this.svgDocument.body.setAttribute("style", "margin:0"); + } else { + this.svgDocument.body.innerHTML = html; + } + const serialized = new XMLSerializer().serializeToString(this.svgDocument.body).replace(/#/g, "%23"); + const svgData = `data:image/svg+xml;charset=utf-8,${serialized}`; + const image = new Image(); + image.src = svgData; + await image.decode(); + canvas.getContext("2d").drawImage(image, 0, 0); + } + async generateTeXImage() { + const renderTarget = document.createElement("div"); + renderTarget.classList.add("BD-TeX-plugin"); + renderTarget.classList.add("BD-TeX-plugin-image-render"); + katex.render(this.texInput, renderTarget, { + throwOnError: false, + strict: false, + trust: true + }); + document.body.append(renderTarget); + const html = `` + renderTarget.outerHTML; + const rect = renderTarget.getBoundingClientRect(); + // Fix some issues with non-integer widths + rect.width = Math.ceil(rect.width) + renderTarget.querySelector(".katex-html").children.length - 1; + rect.height = Math.ceil(rect.height); + renderTarget.parentNode.removeChild(renderTarget); + await this.htmlToCanvas(html, this.canvas, rect.width, rect.height); + return new Promise((resolve, reject) => { + try { + this.canvas.toBlob(blob => resolve(blob), "image/png"); + } catch (e) { + reject(e); + } + }); + } + attachImage(blob) { + const fileList = [ + new File([blob], `tex-output-${Date.now()}.png`, { + type: "image/png" + }) + ]; + BdApi.findModuleByProps("promptToUpload").promptToUpload( + fileList, + ZeresPluginLibrary.DiscordModules.ChannelStore.getChannel( + ZeresPluginLibrary.DiscordModules.SelectedChannelStore.getChannelId() + ), + false, + true + ); + } + injectButton() { + const Textarea = ZeresPluginLibrary.DiscordClasses.Textarea; + const buttonsContainer = document.querySelector(`.${Textarea.channelTextArea} .${Textarea.buttons}`); + if (!buttonsContainer || buttonsContainer.getElementsByClassName("BD-TeX-plugin-icon").length) return; + buttonsContainer.prepend(this.texButton); + } + askToDownloadZeresPluginLibrary() { + return new Promise((resolve, reject) => { + BdApi.showConfirmationModal("Library Missing", `The library plugin needed for ${this.constructor.name} is missing. Please click Download Now to install it.`, { + confirmText: "Download Now", + cancelText: "Cancel", + onConfirm: () => { + require("request").get("https://rauenzi.github.io/BDPluginLibrary/release/0PluginLibrary.plugin.js", async (error, response, body) => { + if (error) { + console.error(`${this.constructor.name}: `, e); + require("electron").shell.openExternal("https://betterdiscord.net/ghdl?url=https://raw.githubusercontent.com/rauenzi/BDPluginLibrary/master/release/0PluginLibrary.plugin.js"); + return reject(); + } + try { + await new Promise(r => require("fs").writeFile(require("path").join(BdApi.Plugins.folder, "0PluginLibrary.plugin.js"), body, r)); + resolve(); + } catch (e) { + console.error(`${this.constructor.name}: `, e); + reject(); + } + }); + }, + onCancel: reject + }); + }); + } + delay(ms) { + return new Promise(resolve => { + setTimeout(resolve, ms); + }); + } +} + +module.exports = TeX; diff --git a/BetterDiscord/plugins/ThemeRepo.config.json b/BetterDiscord/plugins/ThemeRepo.config.json new file mode 100644 index 0000000..9f78499 --- /dev/null +++ b/BetterDiscord/plugins/ThemeRepo.config.json @@ -0,0 +1,16 @@ +{ + "all": { + "cached": "1 3 16 18 22 23 26 32 33 39 40 41 45 46 47 48 49 52 53 56 112 124 125 140 142 144 147 153 155 156 161 166 172 174 177 204 205 209 211 212 213 216 218 222 226 231 233 252 256 257 258 286 296 300 308 320 329 342 359 362 369 371 374 385 394 399 405 411 412 422 439 468 483 485 507 508 515 541 553 572 576 580 581 597 601 607 613 626 636 637 641 642 643 653 659 662 664 665 695 712 713 714 719 722 733 734 735 736 737 746 752 753 770 781 813 823 848 865", + "filters": { + "updated": true, + "outdated": true, + "downloadable": true + }, + "general": { + "notifyOutdated": true, + "notifyNewEntries": true, + "startDownloaded": false, + "startUpdated": false + } + } +} \ No newline at end of file diff --git a/BetterDiscord/plugins/ThemeRepo.plugin.js b/BetterDiscord/plugins/ThemeRepo.plugin.js new file mode 100644 index 0000000..25b101a --- /dev/null +++ b/BetterDiscord/plugins/ThemeRepo.plugin.js @@ -0,0 +1,1238 @@ +/** + * @name ThemeRepo + * @author DevilBro + * @authorId 278543574059057154 + * @version 2.5.4s + * @description Allows you to download all Themes from BD's Website within Discord + * @invite Jx3TjNS + * @donate https://www.paypal.me/MircoWittrien + * @patreon https://www.patreon.com/MircoWittrien + * @website https://mwittrien.github.io/ + * @source https://github.com/mwittrien/BetterDiscordAddons/tree/master/Plugins/ThemeRepo/ + * @updateUrl https://mwittrien.github.io/BetterDiscordAddons/Plugins/ThemeRepo/ThemeRepo.plugin.js + */ + +module.exports = (_ => { + const changeLog = { + + }; + + return !window.BDFDB_Global || (!window.BDFDB_Global.loaded && !window.BDFDB_Global.started) ? class { + constructor (meta) {for (let key in meta) this[key] = meta[key];} + getName () {return this.name;} + getAuthor () {return this.author;} + getVersion () {return this.version;} + getDescription () {return `The Library Plugin needed for ${this.name} is missing. Open the Plugin Settings to download it. \n\n${this.description}`;} + + downloadLibrary () { + require("request").get("https://mwittrien.github.io/BetterDiscordAddons/Library/0BDFDB.plugin.js", (e, r, b) => { + if (!e && b && r.statusCode == 200) require("fs").writeFile(require("path").join(BdApi.Plugins.folder, "0BDFDB.plugin.js"), b, _ => BdApi.showToast("Finished downloading BDFDB Library", {type: "success"})); + else BdApi.alert("Error", "Could not download BDFDB Library Plugin. Try again later or download it manually from GitHub: https://mwittrien.github.io/downloader/?library"); + }); + } + + load () { + if (!window.BDFDB_Global || !Array.isArray(window.BDFDB_Global.pluginQueue)) window.BDFDB_Global = Object.assign({}, window.BDFDB_Global, {pluginQueue: []}); + if (!window.BDFDB_Global.downloadModal) { + window.BDFDB_Global.downloadModal = true; + BdApi.showConfirmationModal("Library Missing", `The Library Plugin needed for ${this.name} is missing. Please click "Download Now" to install it.`, { + confirmText: "Download Now", + cancelText: "Cancel", + onCancel: _ => {delete window.BDFDB_Global.downloadModal;}, + onConfirm: _ => { + delete window.BDFDB_Global.downloadModal; + this.downloadLibrary(); + } + }); + } + if (!window.BDFDB_Global.pluginQueue.includes(this.name)) window.BDFDB_Global.pluginQueue.push(this.name); + } + start () {this.load();} + stop () {} + getSettingsPanel () { + let template = document.createElement("template"); + template.innerHTML = `
The Library Plugin needed for ${this.name} is missing.\nPlease click Download Now to install it.
`; + template.content.firstElementChild.querySelector("a").addEventListener("click", this.downloadLibrary); + return template.content.firstElementChild; + } + } : (([Plugin, BDFDB]) => { + var _this; + + var list; + + var loading, cachedThemes, grabbedThemes, generatorThemes, updateInterval; + var searchString, searchTimeout, forcedSort, forcedOrder, showOnlyOutdated; + var updateGeneratorTimeout, forceRerenderGenerator, nativeCSS, nativeCSSvars; + + var favorites = []; + + const themeStates = { + INSTALLED: 0, + OUTDATED: 1, + DOWNLOADABLE: 2 + }; + const buttonData = { + INSTALLED: { + backgroundColor: "var(--status-positive)", + icon: "CHECKMARK", + text: "installed" + }, + OUTDATED: { + backgroundColor: "var(--status-danger)", + icon: "CLOSE", + text: "outdated" + }, + DOWNLOADABLE: { + backgroundColor: "var(--bdfdb-blurple)", + icon: "DOWNLOAD", + text: "download" + } + }; + const reverseSorts = [ + "RELEASEDATE", "DOWNLOADS", "LIKES", "FAV" + ]; + const sortKeys = { + NAME: "Name", + AUTHORNAME: "Author", + VERSION: "Version", + DESCRIPTION: "Description", + RELEASEDATE: "Release Date", + STATE: "Update State", + DOWNLOADS: "Downloads", + LIKES: "Likes", + FAV: "Favorites" + }; + const orderKeys = { + ASC: "ascending", + DESC: "descending" + }; + + const themeRepoIcon = ``; + + const RepoListComponent = class ThemeList extends BdApi.React.Component { + componentDidMount() { + list = this; + BDFDB.TimeUtils.timeout(_ => { + forcedSort = null; + forcedOrder = null; + showOnlyOutdated = false; + }, 5000); + } + componentWillUnmount() { + list = null; + } + filterThemes() { + let themes = grabbedThemes.map(theme => { + if (theme.failed) return; + const installedTheme = _this.getInstalledTheme(theme); + const state = installedTheme ? (theme.version && _this.compareVersions(theme.version, _this.getString(installedTheme.version)) ? themeStates.OUTDATED : themeStates.INSTALLED) : themeStates.DOWNLOADABLE; + return Object.assign(theme, { + search: [theme.name, theme.version, theme.authorname, theme.description, theme.tags].flat(10).filter(n => typeof n == "string").join(" ").toUpperCase(), + description: theme.description || "No Description found", + fav: favorites.includes(theme.id) && 1, + new: state == themeStates.DOWNLOADABLE && !cachedThemes.includes(theme.id) && 1, + state: state + }); + }).filter(n => n); + if (!this.props.updated) themes = themes.filter(theme => theme.state != themeStates.INSTALLED); + if (!this.props.outdated) themes = themes.filter(theme => theme.state != themeStates.OUTDATED); + if (!this.props.downloadable) themes = themes.filter(theme => theme.state != themeStates.DOWNLOADABLE); + if (searchString) { + let usedSearchString = searchString.toUpperCase(); + let spacelessUsedSearchString = usedSearchString.replace(/\s/g, ""); + themes = themes.filter(theme => theme.search.indexOf(usedSearchString) > -1 || theme.search.indexOf(spacelessUsedSearchString) > -1); + } + + BDFDB.ArrayUtils.keySort(themes, this.props.sortKey.toLowerCase()); + if (this.props.orderKey == "DESC") themes.reverse(); + if (reverseSorts.includes(this.props.sortKey)) themes.reverse(); + return themes; + } + createThemeFile(name, filename, body, autoloadKey) { + return new Promise(callback => BDFDB.LibraryRequires.fs.writeFile(BDFDB.LibraryRequires.path.join(BDFDB.BDUtils.getThemesFolder(), filename), body, error => { + if (error) { + callback(true); + BDFDB.NotificationUtils.toast(BDFDB.LanguageUtils.LibraryStringsFormat("save_fail", `Theme "${name}"`), {type: "danger"}); + } + else { + BDFDB.NotificationUtils.toast(BDFDB.LanguageUtils.LibraryStringsFormat("save_success", `Theme "${name}"`), {type: "success"}); + if (_this.settings.general[autoloadKey]) BDFDB.TimeUtils.timeout(_ => { + if (BDFDB.BDUtils.isThemeEnabled(name) == false) { + BDFDB.BDUtils.enableTheme(name, false); + BDFDB.LogUtils.log(BDFDB.LanguageUtils.LibraryStringsFormat("toast_plugin_started", name), _this); + } + }, 3000); + callback(); + } + })); + } + generateTheme(css) { + if (!css || !BDFDB.ObjectUtils.is(this.props.generatorValues)) return ""; + for (let inputId in this.props.generatorValues) if (this.props.generatorValues[inputId].value && this.props.generatorValues[inputId].value.trim() && this.props.generatorValues[inputId].value != this.props.generatorValues[inputId].oldValue) css = css.replace(new RegExp(`--${BDFDB.StringUtils.regEscape(inputId)}(\\s*):(\\s*)${BDFDB.StringUtils.regEscape(this.props.generatorValues[inputId].oldValue)}`,"g"),`--${inputId}$1: $2${this.props.generatorValues[inputId].value}`); + return css; + } + createFixerCSS(body) { + let oldCSS = body.replace(/\n/g, "\\n").replace(/\t/g, "\\t").replace(/\r/g, "\\r").split("REPLACE_CLASS_"); + let newCSS = oldCSS.shift(); + for (let str of oldCSS) { + let reg = /([A-z0-9_]+)(.*)/.exec(str); + newCSS += BDFDB.dotCN[reg[1]] + reg[2]; + } + return newCSS.replace(/\\n/g, "\n").replace(/\\t/g, "\t").replace(/\\r/g, "\r"); + } + render() { + if (!this.props.tab) this.props.tab = "Themes"; + + const entries = (!loading.is && grabbedThemes.length ? this.filterThemes() : []); + + if (forceRerenderGenerator && this.props.tab == "Generator") BDFDB.TimeUtils.timeout(_ => { + forceRerenderGenerator = false; + BDFDB.ReactUtils.forceUpdate(this); + }); + + return BDFDB.ReactUtils.createElement("div", { + className: BDFDB.disCN._repo, + children: [ + BDFDB.ReactUtils.createElement("div", { + className: BDFDB.disCNS._repolistheader + BDFDB.disCN.settingswindowcontentcolumndefault, + children: [ + BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.Flex, { + className: BDFDB.disCN.marginbottom4, + align: BDFDB.LibraryComponents.Flex.Align.CENTER, + children: [ + BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.Flex.Child, { + grow: 1, + shrink: 0, + children: BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.FormComponents.FormTitle, { + tag: BDFDB.LibraryComponents.FormComponents.FormTags.H1, + className: BDFDB.disCN.marginreset, + children: `Theme Repo — ${loading.is ? 0 : entries.length || 0}/${loading.is ? 0 : grabbedThemes.length}` + }) + }), + BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.Flex.Child, { + children: BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.SearchBar, { + autoFocus: true, + query: searchString, + onChange: value => { + if (loading.is) return; + BDFDB.TimeUtils.clear(searchTimeout); + searchTimeout = BDFDB.TimeUtils.timeout(_ => { + searchString = value.replace(/[<|>]/g, ""); + BDFDB.ReactUtils.forceUpdate(this); + }, 1000); + }, + onClear: _ => { + if (loading.is) return; + searchString = ""; + BDFDB.ReactUtils.forceUpdate(this); + } + }) + }), + BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.Button, { + size: BDFDB.LibraryComponents.Button.Sizes.TINY, + children: BDFDB.LanguageUtils.LibraryStrings.check_for_updates, + onClick: _ => { + if (loading.is) return; + loading = {is: false, timeout: null, amount: 0}; + _this.loadThemes(true); + } + }) + ] + }), + BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.Flex, { + className: BDFDB.disCNS.tabbarcontainer + BDFDB.disCN.tabbarcontainerbottom, + align: BDFDB.LibraryComponents.Flex.Align.CENTER, + children: [ + BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.Flex.Child, { + children: BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.TabBar, { + className: BDFDB.disCN.tabbar, + itemClassName: BDFDB.disCN.tabbaritem, + type: BDFDB.LibraryComponents.TabBar.Types.TOP, + selectedItem: this.props.tab, + items: [{value: "Themes"}, {value: "Generator"}, {value: BDFDB.LanguageUtils.LanguageStrings.SETTINGS}], + onItemSelect: value => { + this.props.tab = value; + BDFDB.ReactUtils.forceUpdate(this); + } + }) + }), + BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.Flex.Child, { + children: BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.QuickSelect, { + label: BDFDB.LanguageUtils.LibraryStrings.sort_by + ":", + value: { + label: sortKeys[this.props.sortKey], + value: this.props.sortKey + }, + options: Object.keys(sortKeys).map(key => ({ + label: sortKeys[key], + value: key + })), + onChange: key => { + this.props.sortKey = key; + BDFDB.ReactUtils.forceUpdate(this); + } + }) + }), + BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.Flex.Child, { + children: BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.QuickSelect, { + label: BDFDB.LanguageUtils.LibraryStrings.order + ":", + value: { + label: BDFDB.LanguageUtils.LibraryStrings[orderKeys[this.props.orderKey]], + value: this.props.orderKey + }, + options: Object.keys(orderKeys).map(key => ({ + label: BDFDB.LanguageUtils.LibraryStrings[orderKeys[key]], + value: key + })), + onChange: key => { + this.props.orderKey = key; + BDFDB.ReactUtils.forceUpdate(this); + } + }) + }) + ] + }) + ] + }), + BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.Scrollers.Auto, { + className: BDFDB.disCNS._repolistscroller + BDFDB.disCN.settingswindowcontentcolumndefault, + children: [ + BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.ModalComponents.ModalTabContent, { + tab: "Themes", + open: this.props.tab == "Themes", + render: false, + children: loading.is ? BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.Flex, { + direction: BDFDB.LibraryComponents.Flex.Direction.VERTICAL, + justify: BDFDB.LibraryComponents.Flex.Justify.CENTER, + style: {marginTop: "50%"}, + children: [ + BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.SpinnerComponents.Spinner, { + type: BDFDB.LibraryComponents.SpinnerComponents.Types.WANDERING_CUBES + }), + BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.TextElement, { + className: BDFDB.disCN.margintop20, + style: {textAlign: "center"}, + children: `${BDFDB.LanguageUtils.LibraryStringsFormat("loading", "Theme Repo")} - ${BDFDB.LanguageUtils.LibraryStrings.please_wait}` + }) + ] + }) : BDFDB.ReactUtils.createElement("div", { + className: BDFDB.disCN.discoverycards, + children: entries.map(theme => BDFDB.ReactUtils.createElement(RepoCardComponent, { + data: theme + })).filter(n => n) + }) + }), + BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.ModalComponents.ModalTabContent, { + tab: "Generator", + open: this.props.tab == "Generator", + render: false, + children: [ + BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.SettingsItem, { + type: "Select", + margin: 20, + label: "Choose a Generator Theme", + basis: "60%", + value: this.props.currentGenerator || "-----", + options: [{value: "-----", label: "-----"}, nativeCSSvars && {value: "nativediscord", label: "Discord"}].concat(generatorThemes.map(t => ({value: t.id, label: t.name || "-----"})).sort((x, y) => (x.label < y.label ? -1 : x.label > y.label ? 1 : 0))).filter(n => n), + onChange: value => { + let generatorTheme = generatorThemes.find(t => t.id == value); + if (generatorTheme || value == "nativediscord") { + if (this.props.currentGenerator) forceRerenderGenerator = true; + this.props.currentGenerator = value; + this.props.currentGeneratorIsNative = value == "nativediscord"; + this.props.generatorValues = {}; + } + else { + delete this.props.currentGenerator; + delete this.props.currentGeneratorIsNative; + delete this.props.generatorValues; + } + delete this.props.currentTheme; + BDFDB.ReactUtils.forceUpdate(this); + } + }), + !this.props.currentGenerator ? null : (forceRerenderGenerator ? BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.Flex, { + direction: BDFDB.LibraryComponents.Flex.Direction.VERTICAL, + justify: BDFDB.LibraryComponents.Flex.Justify.CENTER, + style: {marginTop: "50%"}, + children: BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.SpinnerComponents.Spinner, { + type: BDFDB.LibraryComponents.SpinnerComponents.Types.WANDERING_CUBES + }) + }) : [ + BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.SettingsItem, { + className: BDFDB.disCN.marginbottom20, + type: "Button", + label: "Download generated Theme", + children: "Download", + onClick: _ => { + if (this.props.currentGeneratorIsNative) { + this.createThemeFile("Discord", "Discord.theme.css", `/**\n * @name Discord\n * @description Allow you to easily customize Discord's native Look \n * @author DevilBro\n * @version 1.0.0\n * @authorId 278543574059057154\n * @invite Jx3TjNS\n * @donate https://www.paypal.me/MircoWittrien\n * @patreon https://www.patreon.com/MircoWittrien\n */\n\n` + this.generateTheme(nativeCSSvars), "startDownloaded"); + } + else { + let generatorTheme = generatorThemes.find(t => t.id == this.props.currentGenerator); + if (generatorTheme) this.createThemeFile(generatorTheme.name, generatorTheme.rawSourceUrl.split("/").pop(), this.generateTheme(generatorTheme.fullCSS), "startDownloaded"); + } + } + }), + BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.FormComponents.FormDivider, { + className: BDFDB.disCN.marginbottom20 + }), + (_ => { + let generatorTheme = generatorThemes.find(t => t.id == this.props.currentGenerator); + let vars = this.props.currentGeneratorIsNative ? nativeCSSvars.split(".theme-dark, .theme-light") : ((generatorTheme || {}).fullCSS || "").split(":root"); + if (vars.length < 2) return null; + vars = vars[1].replace(/\t\(/g, " (").replace(/\r|\t| {2,}/g, "").replace(/\/\*\n*((?!\/\*|\*\/).|\n)*\n+((?!\/\*|\*\/).|\n)*\n*\*\//g, "").replace(/\n\/\*.*?\*\//g, "").replace(/\n/g, ""); + vars = vars.split("{"); + vars.shift(); + vars = vars.join("{").replace(/\s*(:|;|--|\*)\s*/g, "$1"); + vars = vars.split("}")[0]; + vars = (vars.endsWith(";") ? vars.slice(0, -1) : vars).slice(2).split(/;--|\*\/--/); + let inputRefs = []; + for (let varStr of vars) { + varStr = varStr.split(":"); + let varName = varStr.shift().trim(); + varStr = varStr.join(":").split(/;[^A-z0-9]|\/\*/); + let oldValue = varStr.shift().trim(); + if (oldValue) { + let childType = "text", childMode = ""; + let isColor = BDFDB.ColorUtils.getType(oldValue); + let isComp = !isColor && /^[0-9 ]+,[0-9 ]+,[0-9 ]+$/g.test(oldValue); + if (isColor || isComp) { + childType = "color"; + childMode = isComp && "comp"; + } + else { + let isUrlFile = /url\(.+\)/gi.test(oldValue); + let isFile = !isUrlFile && /(http(s)?):\/\/[(www\.)?a-zA-Z0-9@:%._\+~#=]{2,256}\.[a-z]{2,6}\b([-a-zA-Z0-9@:%_\+.~#?&//=]*)/.test(oldValue); + if (isFile || isUrlFile) { + childType = "file"; + childMode = isUrlFile && "url"; + } + } + let varDescription = varStr.join("").replace(/\*\/|\/\*/g, "").replace(/:/g, ": ").replace(/: \//g, ":/").replace(/--/g, " --").replace(/\( --/g, "(--").trim(); + this.props.generatorValues[varName] = {value: oldValue, oldValue}; + inputRefs.push(BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.SettingsItem, { + dividerBottom: vars[vars.length-1] != varStr, + type: "TextInput", + childProps: { + type: childType, + mode: childMode, + filter: childType == "file" && "image" + }, + label: varName.split("-").map(BDFDB.StringUtils.upperCaseFirstChar).join(" "), + note: varDescription && varDescription.indexOf("*") == 0 ? varDescription.slice(1) : varDescription, + basis: "70%", + value: oldValue, + placeholder: oldValue, + onChange: value => { + BDFDB.TimeUtils.clear(updateGeneratorTimeout); + updateGeneratorTimeout = BDFDB.TimeUtils.timeout(_ => this.props.generatorValues[varName] = {value, oldValue}, 1000); + } + })); + } + } + return inputRefs; + })() + ]) + ].flat(10).filter(n => n) + }), + BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.ModalComponents.ModalTabContent, { + tab: BDFDB.LanguageUtils.LanguageStrings.SETTINGS, + open: this.props.tab == BDFDB.LanguageUtils.LanguageStrings.SETTINGS, + render: false, + children: [ + BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.SettingsPanelList, { + title: "Show following Themes", + children: Object.keys(_this.defaults.filters).map(key => BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.SettingsSaveItem, { + type: "Switch", + plugin: _this, + keys: ["filters", key], + label: _this.defaults.filters[key].description, + value: _this.settings.filters[key], + onChange: value => { + this.props[key] = _this.settings.filters[key] = value; + BDFDB.ReactUtils.forceUpdate(this); + } + })) + }), + Object.keys(_this.defaults.general).map(key => BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.SettingsSaveItem, { + type: "Switch", + plugin: _this, + keys: ["general", key], + label: _this.defaults.general[key].description, + value: _this.settings.general[key], + onChange: value => { + _this.settings.general[key] = value; + BDFDB.ReactUtils.forceUpdate(this); + } + })) + ].flat(10).filter(n => n) + }) + ] + }) + ] + }); + } + }; + + const RepoCardComponent = class ThemeCard extends BdApi.React.Component { + render() { + return BDFDB.ReactUtils.createElement("div", { + className: BDFDB.disCN.discoverycard, + onMouseEnter: _ => { + this.props.hovered = true; + BDFDB.ReactUtils.forceUpdate(this); + }, + children: [ + BDFDB.ReactUtils.createElement("div", { + className: BDFDB.disCN.discoverycardheader, + children: [ + BDFDB.ReactUtils.createElement("div", { + className: BDFDB.disCN.discoverycardcoverwrapper, + children: [ + this.props.data.thumbnailUrl && this.props.hovered && BDFDB.ReactUtils.createElement("img", { + className: BDFDB.disCN.discoverycardcover, + src: this.props.data.thumbnailUrl, + onClick: _ => { + const url = this.props.data.thumbnailUrl; + const img = document.createElement("img"); + img.addEventListener("load", function() { + BDFDB.LibraryModules.ModalUtils.openModal(modalData => BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.ModalComponents.ModalRoot, Object.assign({ + className: BDFDB.disCN.imagemodal + }, modalData, { + size: BDFDB.LibraryComponents.ModalComponents.ModalSize.DYNAMIC, + "aria-label": BDFDB.LanguageUtils.LanguageStrings.IMAGE, + children: BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.ImageModal, { + animated: false, + src: url, + original: url, + width: this.width, + height: this.height, + className: BDFDB.disCN.imagemodalimage, + shouldAnimate: true, + renderLinkComponent: props => BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.Anchor, props) + }) + }), true)); + }); + img.src = url; + } + }), + this.props.data.new && BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.Badges.TextBadge, { + className: BDFDB.disCN.discoverycardcoverbadge, + style: { + borderRadius: 3, + textTransform: "uppercase", + background: BDFDB.DiscordConstants.Colors.YELLOW + }, + text: BDFDB.LanguageUtils.LanguageStrings.NEW + }) + ] + }), + BDFDB.ReactUtils.createElement("div", { + className: BDFDB.disCN.discoverycardiconwrapper, + children: BDFDB.ReactUtils.createElement("div", { + className: BDFDB.disCN.discoverycardicon, + children: BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.SvgIcon, { + nativeClass: true, + iconSVG: `` + }) + }) + }) + ] + }), + BDFDB.ReactUtils.createElement("div", { + className: BDFDB.disCN.discoverycardinfo, + children: [ + BDFDB.ReactUtils.createElement("div", { + className: BDFDB.disCN.discoverycardtitle, + children: [ + BDFDB.ReactUtils.createElement("div", { + className: BDFDB.disCN.discoverycardname, + children: this.props.data.name + }), + this.props.data.latestSourceUrl && + BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.TooltipContainer, { + text: BDFDB.LanguageUtils.LanguageStrings.SCREENSHARE_SOURCE, + children: BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.Clickable, { + className: BDFDB.disCN.discoverycardtitlebutton, + children: BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.SvgIcon, { + nativeClass: true, + width: 16, + height: 16, + name: BDFDB.LibraryComponents.SvgIcon.Names.GITHUB + }) + }), + onClick: _ => BDFDB.DiscordUtils.openLink(this.props.data.latestSourceUrl) + }), + BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.FavButton, { + className: BDFDB.disCN.discoverycardtitlebutton, + isFavorite: this.props.data.fav, + onClick: value => { + this.props.data.fav = value && 1; + if (value) favorites.push(this.props.data.id); + else BDFDB.ArrayUtils.remove(favorites, this.props.data.id, true); + BDFDB.DataUtils.save(BDFDB.ArrayUtils.numSort(favorites).join(" "), _this, "favorites"); + } + }) + ] + }), + BDFDB.ReactUtils.createElement("div", { + className: BDFDB.disCN.discoverycardauthor, + children: `by ${this.props.data.authorname}` + }), + BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.Scrollers.Thin, { + className: BDFDB.disCN.discoverycarddescription, + children: this.props.data.description + }), + BDFDB.ReactUtils.createElement("div", { + className: BDFDB.disCN.discoverycardfooter, + children: [ + BDFDB.ArrayUtils.is(this.props.data.tags) && this.props.data.tags.length && BDFDB.ReactUtils.createElement("div", { + className: BDFDB.disCN.discoverycardtags, + children: this.props.data.tags.map(tag => BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.Badges.TextBadge, { + className: BDFDB.disCN.discoverycardtag, + style: {background: "var(--background-accent)"}, + text: tag + })) + }), + BDFDB.ReactUtils.createElement("div", { + className: BDFDB.disCN.discoverycardcontrols, + children: [ + BDFDB.ReactUtils.createElement("div", { + className: BDFDB.disCN.discoverycardstats, + children: [ + BDFDB.ReactUtils.createElement("div", { + className: BDFDB.disCN.discoverycardstat, + children: [ + BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.SvgIcon, { + className: BDFDB.disCN.discoverycardstaticon, + name: BDFDB.LibraryComponents.SvgIcon.Names.DOWNLOAD + }), + this.props.data.downloads + ] + }), + BDFDB.ReactUtils.createElement("div", { + className: BDFDB.disCN.discoverycardstat, + children: [ + BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.SvgIcon, { + className: BDFDB.disCN.discoverycardstaticon, + name: BDFDB.LibraryComponents.SvgIcon.Names.HEART + }), + this.props.data.likes + ] + }) + ] + }), + BDFDB.ReactUtils.createElement(RepoCardDownloadButtonComponent, { + ...buttonData[(Object.entries(themeStates).find(n => n[1] == this.props.data.state) || [])[0]], + installed: this.props.data.state == themeStates.INSTALLED, + outdated: this.props.data.state == themeStates.OUTDATED, + onDownload: _ => { + if (!list || this.props.downloading) return; + this.props.downloading = true; + let loadingToast = BDFDB.NotificationUtils.toast(`${BDFDB.LanguageUtils.LibraryStringsFormat("loading", this.props.data.name)} - ${BDFDB.LanguageUtils.LibraryStrings.please_wait}`, {timeout: 0, ellipsis: true}); + let autoloadKey = this.props.data.state == themeStates ? "startUpdated" : "startDownloaded"; + BDFDB.LibraryRequires.request(this.props.data.rawSourceUrl, (error, response, body) => { + if (error || !body) { + delete this.props.downloading; + loadingToast.close(); + BDFDB.NotificationUtils.toast(BDFDB.LanguageUtils.LibraryStringsFormat("download_fail", `Theme "${this.props.data.name}"`), {type: "danger"}); + } + else list.createThemeFile(this.props.data.name, this.props.data.rawSourceUrl.split("/").pop(), body, autoloadKey).then(error2 => { + delete this.props.downloading; + loadingToast.close(); + if (!error2) { + this.props.data.state = themeStates.INSTALLED; + BDFDB.ReactUtils.forceUpdate(this); + } + }); + }); + }, + onDelete: _ => { + if (this.props.deleting) return; + this.props.deleting = true; + BDFDB.LibraryRequires.fs.unlinkSync(BDFDB.LibraryRequires.path.join(BDFDB.BDUtils.getThemesFolder(), this.props.data.rawSourceUrl.split("/").pop())); + delete this.props.deleting; + BDFDB.NotificationUtils.toast(BDFDB.LanguageUtils.LibraryStringsFormat("delete_success", `Theme "${this.props.data.name}"`)); + this.props.data.state = themeStates.DOWNLOADABLE; + BDFDB.ReactUtils.forceUpdate(this); + } + }) + ] + }) + ] + }) + ] + }) + ] + }); + } + }; + + const RepoCardDownloadButtonComponent = class ThemeCardDownloadButton extends BdApi.React.Component { + render() { + const backgroundColor = this.props.doDelete ? buttonData.OUTDATED.backgroundColor : this.props.doUpdate ? buttonData.INSTALLED.backgroundColor : this.props.backgroundColor; + return BDFDB.ReactUtils.createElement("button", { + className: BDFDB.disCN.discoverycardbutton, + style: {backgroundColor: BDFDB.DiscordConstants.Colors[backgroundColor] || backgroundColor}, + children: [ + BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.SvgIcon, { + className: BDFDB.disCN.discoverycardstaticon, + width: 16, + height: 16, + name: this.props.doDelete ? BDFDB.LibraryComponents.SvgIcon.Names.TRASH : this.props.doUpdate ? BDFDB.LibraryComponents.SvgIcon.Names.DOWNLOAD : BDFDB.LibraryComponents.SvgIcon.Names[this.props.icon] + }), + this.props.doDelete ? BDFDB.LanguageUtils.LanguageStrings.APPLICATION_CONTEXT_MENU_UNINSTALL : this.props.doUpdate ? BDFDB.LanguageUtils.LanguageStrings.GAME_ACTION_BUTTON_UPDATE : (BDFDB.LanguageUtils.LibraryStringsCheck[this.props.text] ? BDFDB.LanguageUtils.LibraryStrings[this.props.text] : BDFDB.LanguageUtils.LanguageStrings[this.props.text]) + ], + onClick: _ => { + if (this.props.doDelete) typeof this.props.onDelete == "function" && this.props.onDelete(); + else if (!this.props.installed) typeof this.props.onDownload == "function" && this.props.onDownload(); + }, + onMouseEnter: this.props.installed ? (_ => { + this.props.doDelete = true; + BDFDB.ReactUtils.forceUpdate(this); + }) : this.props.outdated ? (_ => { + this.props.doUpdate = true; + BDFDB.ReactUtils.forceUpdate(this); + }) : (_ => {}), + onMouseLeave: this.props.installed ? (_ => { + this.props.doDelete = false; + BDFDB.ReactUtils.forceUpdate(this); + }) : this.props.outdated ? (_ => { + this.props.doUpdate = false; + BDFDB.ReactUtils.forceUpdate(this); + }) : (_ => {}) + }); + } + }; + + return class ThemeRepo extends Plugin { + onLoad () { + _this = this; + + loading = {is: false, timeout: null, amount: 0}; + + cachedThemes = []; + grabbedThemes = []; + generatorThemes = []; + searchString = ""; + + this.defaults = { + general: { + notifyOutdated: {value: true, autoload: false, description: "Shows a Notification when one of your Themes is outdated"}, + notifyNewEntries: {value: true, autoload: false, description: "Shows a Notification when there are new Entries in the Repo"}, + startDownloaded: {value: false, autoload: true, description: "Starts new Themes after Download"}, + startUpdated: {value: false, autoload: true, description: "Starts updated Themes after Download"} + }, + filters: { + updated: {value: true, description: "Updated"}, + outdated: {value: true, description: "Outdated"}, + downloadable: {value: true, description: "Downloadable"}, + } + }; + + this.modulePatches = { + before: [ + "SettingsView", + "StandardSidebarView" + ], + componentWillUnmount: [ + "SettingsView" + ] + }; + } + + onStart () { + this.forceUpdateAll(); + + this.loadThemes(); + + updateInterval = BDFDB.TimeUtils.interval(_ => this.checkForNewThemes(), 1000*60*30); + } + + onStop () { + BDFDB.TimeUtils.clear(updateInterval); + BDFDB.TimeUtils.clear(loading.timeout); + + this.forceUpdateAll(); + + BDFDB.DOMUtils.remove(BDFDB.dotCN._themereponotice, BDFDB.dotCN._themerepoloadingicon); + } + + onSettingsClosed () { + if (this.SettingsUpdated) { + delete this.SettingsUpdated; + this.forceUpdateAll(); + } + } + + forceUpdateAll () { + favorites = BDFDB.DataUtils.load(this, "favorites"); + favorites = (typeof favorites == "string" ? favorites.split(" ") : []).map(n => parseInt(n)).filter(n => !isNaN(n)); + + BDFDB.PatchUtils.forceAllUpdates(this); + } + + onUserSettingsCogContextMenu (e) { + BDFDB.TimeUtils.timeout(_ => { + let [children, index] = BDFDB.ReactUtils.findParent(e.returnvalue, {props: [["label", ["BandagedBD", "BetterDiscord"]]]}); + if (index > -1 && BDFDB.ArrayUtils.is(children[index].props.children)) children[index].props.children.push(BDFDB.ContextMenuUtils.createItem(BDFDB.LibraryComponents.MenuItems.MenuItem, { + label: "Theme Repo", + id: BDFDB.ContextMenuUtils.createItemId(this.name, "repo"), + action: _ => BDFDB.LibraryModules.UserSettingsUtils.open("themerepo") + })); + }); + } + + processSettingsView (e) { + if (e.node) searchString = ""; + else if (e.component.prototype && !BDFDB.PatchUtils.isPatched(this, e.component.prototype, "getPredicateSections")) { + BDFDB.PatchUtils.patch(this, e.component.prototype, "getPredicateSections", {after: e2 => { + if (BDFDB.ArrayUtils.is(e2.returnValue) && e2.returnValue.findIndex(n => n.section && (n.section.toLowerCase() == "changelog" || n.section == BDFDB.DiscordConstants.UserSettingsSections.CHANGE_LOG || n.section.toLowerCase() == "logout" || n.section == BDFDB.DiscordConstants.UserSettingsSections.LOGOUT))) { + e2.returnValue = e2.returnValue.filter(n => n.section != "themerepo"); + let index = e2.returnValue.indexOf(e2.returnValue.find(n => n.section == "pluginrepo") || e2.returnValue.find(n => n.section == "themes") || e2.returnValue.find(n => n.section == BDFDB.DiscordConstants.UserSettingsSections.DEVELOPER_OPTIONS) || e2.returnValue.find(n => n.section == BDFDB.DiscordConstants.UserSettingsSections.HYPESQUAD_ONLINE)); + if (index > -1) { + e2.returnValue.splice(index + 1, 0, { + className: "themerepo-tab", + section: "themerepo", + label: "Theme Repo", + element: _ => { + let options = Object.assign({}, this.settings.filters); + options.updated = options.updated && !showOnlyOutdated; + options.outdated = options.outdated || showOnlyOutdated; + options.downloadable = options.downloadable && !showOnlyOutdated; + options.sortKey = forcedSort || Object.keys(sortKeys)[0]; + options.orderKey = forcedOrder || Object.keys(orderKeys)[0]; + options.useLightMode = BDFDB.DiscordUtils.getTheme() == BDFDB.disCN.themelight; + options.useThemeFixer = false; + options.useCustomCSS = false; + + return BDFDB.ReactUtils.createElement(RepoListComponent, options); + } + }); + if (!e2.returnValue.find(n => n.section == "plugins" || n.section == "pluginrepo")) e2.returnValue.splice(index + 1, 0, {section: "DIVIDER"}); + } + } + }}); + } + } + + processStandardSidebarView (e) { + if (e.instance.props && e.instance.props.section == "themerepo") e.instance.props.contentType = "custom"; + } + + generateTheme (fullCSS, generatorValues) { + if (!fullCSS || !BDFDB.ObjectUtils.is(generatorValues)) return ""; + for (let inputId in generatorValues) if (generatorValues[inputId].value && generatorValues[inputId].value.trim() && generatorValues[inputId].value != generatorValues[inputId].oldValue) fullCSS = fullCSS.replace(new RegExp(`--${BDFDB.StringUtils.regEscape(inputId)}(\\s*):(\\s*)${BDFDB.StringUtils.regEscape(generatorValues[inputId].oldValue)}`,"g"),`--${inputId}$1: $2${generatorValues[inputId].value}`); + return fullCSS; + } + + loadThemes (forceBanner) { + BDFDB.DOMUtils.remove(BDFDB.dotCN._themerepoloadingicon); + cachedThemes = BDFDB.DataUtils.load(this, "cached"); + cachedThemes = (typeof cachedThemes == "string" ? cachedThemes.split(" ") : []).map(n => parseInt(n)).filter(n => !isNaN(n)); + + let loadingIcon; + let newEntries = 0, outdatedEntries = 0, checkIndex = 0, checksRunning = 0, callbackCalled = false; + + const checkTheme = _ => { + if (checksRunning > 20) return; + else if (grabbedThemes.every(t => t.loaded || (!t.latestSourceUrl && !t.latest_source_url)) || !this.started || !loading.is) { + if (!callbackCalled) { + callbackCalled = true; + if (!this.started) return BDFDB.TimeUtils.clear(loading.timeout); + BDFDB.TimeUtils.clear(loading.timeout); + BDFDB.DOMUtils.remove(loadingIcon, BDFDB.dotCN._themerepoloadingicon); + loading = {is: false, timeout: null, amount: loading.amount}; + + BDFDB.LogUtils.log("Finished fetching Themes", this); + BDFDB.ReactUtils.forceUpdate(list); + + if ((this.settings.general.notifyOutdated || forceBanner) && outdatedEntries > 0) { + let notice = document.querySelector(BDFDB.dotCN._themerepooutdatednotice); + if (notice) notice.close(); + BDFDB.NotificationUtils.notice(this.labels.notice_outdated_themes.replace("{{var0}}", outdatedEntries), { + type: "danger", + className: BDFDB.disCNS._themereponotice + BDFDB.disCN._themerepooutdatednotice, + customIcon: themeRepoIcon.replace(/COLOR_[0-9]+/gi, "currentColor"), + buttons: [{ + contents: BDFDB.LanguageUtils.LanguageStrings.OPEN, + close: true, + onClick: _ => { + showOnlyOutdated = true; + BDFDB.LibraryModules.UserSettingsUtils.open("themerepo"); + } + }] + }); + } + + if (this.settings.general.notifyNewEntries && newEntries > 0) { + let notice = document.querySelector(BDFDB.dotCN._themereponewentriesnotice); + if (notice) notice.close(); + BDFDB.NotificationUtils.notice(this.labels.notice_new_themes.replace("{{var0}}", newEntries), { + type: "success", + className: BDFDB.disCNS._themereponotice + BDFDB.disCN._themereponewentriesnotice, + customIcon: themeRepoIcon.replace(/COLOR_[0-9]+/gi, "currentColor"), + buttons: [{ + contents: BDFDB.LanguageUtils.LanguageStrings.OPEN, + close: true, + onClick: _ => { + forcedSort = "RELEASEDATE"; + forcedOrder = "ASC"; + BDFDB.LibraryModules.UserSettingsUtils.open("themerepo"); + } + }] + }); + } + + BDFDB.LibraryRequires.request("https://mwittrien.github.io/BetterDiscordAddons/Plugins/ThemeRepo/_res/GeneratorList.txt", (error, response, body) => { + if (!error && body) for (let id of body.replace(/[\r\t]/g, "").split(" ").map(n => parseInt(n)).filter(n => n != null)) { + let theme = grabbedThemes.find(t => t.id == id); + if (theme) generatorThemes.push(theme); + } + }); + + BDFDB.LibraryRequires.request(document.querySelector("head link[rel='stylesheet'][href*='assets/app.'], body link[rel='stylesheet'][href*='assets/app.']").href, (error, response, body) => { + let nativeCSS = !error && body; + if (nativeCSS) { + let theme = BDFDB.DiscordUtils.getTheme(); + let vars = (nativeCSS.split(`.${theme}{`)[1] || "").split("}")[0]; + nativeCSSvars = vars ? `.theme-dark, .theme-light {${vars}}` : ""; + } + else nativeCSS = nativeCSSvars = ""; + }); + } + return; + } + else if (checkIndex > grabbedThemes.length) return; + + const theme = grabbedThemes[checkIndex++]; + if (!theme || (!theme.latestSourceUrl && !theme.latest_source_url)) checkTheme(); + else { + checksRunning++; + theme.releasedate = new Date(theme.releaseDate || theme.release_date || 0).getTime(); + theme.latestSourceUrl = theme.latestSourceUrl || theme.latest_source_url; + theme.rawSourceUrl = theme.latestSourceUrl.replace("https://github.com/", "https://raw.githubusercontent.com/").replace(/\/blob\/(.{32,})/i, "/$1"); + theme.thumbnailUrl = theme.thumbnailUrl || theme.thumbnail_url; + theme.thumbnailUrl = theme.thumbnailUrl ? (theme.thumbnailUrl.startsWith("https://") ? theme.thumbnailUrl : `https://betterdiscord.app${theme.thumbnailUrl}`) : ""; + delete theme.release_date; + delete theme.latest_source_url; + delete theme.thumbnail_url; + BDFDB.LibraryRequires.request(theme.rawSourceUrl, (error, response, body) => { + if (error || !body || body.indexOf("404: Not Found") == 0) theme.failed = true; + else { + const META = body.split("*/")[0]; + theme.name = BDFDB.StringUtils.upperCaseFirstChar((/@name\s+([^\t^\r^\n]+)|\/\/\**META.*["']name["']\s*:\s*["'](.+?)["']/i.exec(META) || []).filter(n => n)[1] || theme.name || ""); + theme.authorname = (/@author\s+(.+)|\/\/\**META.*["']author["']\s*:\s*["'](.+?)["']/i.exec(META) || []).filter(n => n)[1] || theme.author.display_name || theme.author; + const version = (/@version\s+(.+)|\/\/\**META.*["']version["']\s*:\s*["'](.+?)["']/i.exec(META) || []).filter(n => n)[1]; + if (version) theme.version = version; + if (theme.version) { + const installedTheme = this.getInstalledTheme(theme); + if (installedTheme && this.compareVersions(version, this.getString(installedTheme.version))) outdatedEntries++; + } + let text = body.trim(); + let hasMETAline = text.replace(/\s/g, "").indexOf("//META{"), newMeta = ""; + if (hasMETAline < 20 && hasMETAline > -1) { + let i = 0, j = 0, metaString = ""; + try { + for (let c of `{${text.split("{").slice(1).join("{")}`) { + metaString += c; + if (c == "{") i++; + else if (c == "}") j++; + if (i > 0 && i == j) break; + } + let metaObj = JSON.parse(metaString); + newMeta = "/**\n"; + for (let key in metaObj) newMeta += ` * @${key} ${metaObj[key]}\n`; + newMeta += "*/"; + } + catch (err) {newMeta = "";} + } + theme.fullCSS = [newMeta, newMeta ? text.split("\n").slice(1).join("\n") : text].filter(n => n).join("\n"); + theme.css = (hasMETAline < 20 && hasMETAline > -1 ? text.split("\n").slice(1).join("\n") : text).replace(/[\r|\n|\t]/g, ""); + if (!cachedThemes.includes(theme.id)) newEntries++; + } + + theme.loaded = true; + + let loadingTooltip = document.querySelector(BDFDB.dotCN._themerepoloadingtooltip); + if (loadingTooltip) loadingTooltip.update(this.getLoadingTooltipText()); + + checksRunning--; + checkTheme(); + }); + } + }; + + BDFDB.TimeUtils.timeout(_ => BDFDB.LibraryRequires.request("https://api.betterdiscord.app/v1/store/themes", {bdVersion: true}, (error, response, body) => { + if (!error && body && response.statusCode == 200) try { + grabbedThemes = BDFDB.ArrayUtils.keySort(JSON.parse(body).filter(n => n), "name"); + + BDFDB.DataUtils.save(BDFDB.ArrayUtils.numSort(grabbedThemes.map(n => n.id)).join(" "), this, "cached"); + + loading = {is: true, timeout: BDFDB.TimeUtils.timeout(_ => { + BDFDB.TimeUtils.clear(loading.timeout); + if (this.started) { + if (loading.is && loading.amount < 4) BDFDB.TimeUtils.timeout(_ => this.loadThemes(), 10000); + loading = {is: false, timeout: null, amount: loading.amount}; + } + }, 1200000), amount: loading.amount + 1}; + + loadingIcon = BDFDB.DOMUtils.create(themeRepoIcon.replace(/COLOR_1/gi, "var(--bdfdb-blurple)").replace(/COLOR_2/gi, "#72767d")); + BDFDB.DOMUtils.addClass(loadingIcon, BDFDB.disCN._themerepoloadingicon); + loadingIcon.addEventListener("mouseenter", _ => { + BDFDB.TooltipUtils.create(loadingIcon, this.getLoadingTooltipText(), { + type: "left", + className: BDFDB.disCN._themerepoloadingtooltip, + delay: 500, + style: "max-width: unset;" + }); + }); + BDFDB.PluginUtils.addLoadingIcon(loadingIcon); + + BDFDB.ReactUtils.forceUpdate(list); + + for (let i = 0; i <= 20; i++) checkTheme(); + } + catch (err) {BDFDB.NotificationUtils.toast("Failed to load Theme Store", {type: "danger"});} + if (response && response.statusCode == 403) BDFDB.NotificationUtils.toast("Failed to fetch Theme Store from the Website Api due to DDoS Protection", {type: "danger"}); + else if (response && response.statusCode == 404) BDFDB.NotificationUtils.toast("Failed to fetch Theme Store from the Website Api due to Connection Issue", {type: "danger"}); + }), 10000); + } + + getLoadingTooltipText () { + return BDFDB.LanguageUtils.LibraryStringsFormat("loading", `Theme Repo - [${grabbedThemes.filter(n => n.loaded).length}/${grabbedThemes.length}]`); + } + + getString (obj) { + let string = ""; + if (typeof obj == "string") string = obj; + else if (obj && obj.props) { + if (typeof obj.props.children == "string") string = obj.props.children; + else if (Array.isArray(obj.props.children)) for (let c of obj.props.children) string += typeof c == "string" ? c : this.getString(c); + } + return string; + } + + compareVersions (v1, v2) { + return !(v1 == v2 || !BDFDB.NumberUtils.compareVersions(v1, v2)); + } + + getInstalledTheme (theme) { + if (!theme || typeof theme.authorname != "string") return; + const iTheme = BDFDB.BDUtils.getTheme(theme.name, false, true); + if (iTheme && (theme.authorname.toUpperCase().indexOf(this.getString(iTheme.author).toUpperCase()) > -1 || this.getString(iTheme.author).toUpperCase().indexOf(theme.authorname.toUpperCase()) > -1)) return iTheme; + else if (theme.rawSourceUrl && window.BdApi && BdApi.Themes && typeof BdApi.Themes.getAll == "function") { + const filename = theme.rawSourceUrl.split("/").pop(); + for (let t of BdApi.Themes.getAll()) if (t.filename == filename && (theme.authorname.toUpperCase().indexOf(this.getString(t.author).toUpperCase()) > -1 || this.getString(t.author).toUpperCase().indexOf(theme.authorname.toUpperCase()) > -1)) return t; + } + } + + checkForNewThemes () { + BDFDB.LibraryRequires.request("https://api.betterdiscord.app/v1/store/themes", {bdVersion: true}, (error, response, body) => { + if (!error && body) try { + if (JSON.parse(body).filter(n => n).length != grabbedThemes.length) { + loading = {is: false, timeout: null, amount: 0}; + this.loadThemes(); + } + } + catch (err) {BDFDB.NotificationUtils.toast("Failed to load Theme Store", {type: "danger"});} + }); + } + + setLabelsByLanguage () { + switch (BDFDB.LanguageUtils.getLanguage().id) { + case "bg": // Bulgarian + return { + list: "Списък", + notice_failed_themes: "Някои Themes [{{var0}}] не можаха да бъдат заредени", + notice_new_themes: "Новите Themes [{{var0}}] бяха добавени към Theme Repo", + notice_outdated_themes: "Някои Themes [{{var0}}] са остарели" + }; + case "da": // Danish + return { + list: "Liste", + notice_failed_themes: "Nogle Themes [{{var0}}] kunne ikke indlæses", + notice_new_themes: "Nye Themes [{{var0}}] er blevet føjet til Theme Repo", + notice_outdated_themes: "Nogle Themes [{{var0}}] er forældede" + }; + case "de": // German + return { + list: "Liste", + notice_failed_themes: "Einige Themes [{{var0}}] konnten nicht geladen werden", + notice_new_themes: "Neue Themes [{{var0}}] wurden zur Theme Repo hinzugefügt", + notice_outdated_themes: "Einige Themes [{{var0}}] sind veraltet" + }; + case "el": // Greek + return { + list: "Λίστα", + notice_failed_themes: "Ορισμένα Θέματα [{{var0}}] δεν μπορούν να φορτωθούν", + notice_new_themes: "Νέα Θέματα [{{var0}}] προστέθηκαν στο Απωθετήριο Θεμάτων", + notice_outdated_themes: "Ορισμένα Θέματα [{{var0}}] είναι παλαιά" + }; + case "es": // Spanish + return { + list: "Lista", + notice_failed_themes: "Algunos Themes [{{var0}}] no se pudieron cargar", + notice_new_themes: "Se han agregado nuevos Themes [{{var0}}] a Theme Repo", + notice_outdated_themes: "Algunas Themes [{{var0}}] están desactualizadas" + }; + case "fi": // Finnish + return { + list: "Lista", + notice_failed_themes: "Joitain kohdetta Themes [{{var0}}] ei voitu ladata", + notice_new_themes: "Uusi Themes [{{var0}}] on lisätty Theme Repo", + notice_outdated_themes: "Jotkut Themes [{{var0}}] ovat vanhentuneita" + }; + case "fr": // French + return { + list: "Liste", + notice_failed_themes: "Certains Themes [{{var0}}] n'ont pas pu être chargés", + notice_new_themes: "De nouveaux Themes [{{var0}}] ont été ajoutés à Theme Repo", + notice_outdated_themes: "Certains Themes [{{var0}}] sont obsolètes" + }; + case "hr": // Croatian + return { + list: "Popis", + notice_failed_themes: "Neke datoteke Themes [{{var0}}] nije moguće učitati", + notice_new_themes: "Novi Themes [{{var0}}] dodani su u Theme Repo", + notice_outdated_themes: "Neki su Themes [{{var0}}] zastarjeli" + }; + case "hu": // Hungarian + return { + list: "Lista", + notice_failed_themes: "Néhány Themes [{{var0}}] nem sikerült betölteni", + notice_new_themes: "Új Themes [{{var0}}] hozzáadva a következőhöz: Theme Repo", + notice_outdated_themes: "Néhány Themes [{{var0}}] elavult" + }; + case "it": // Italian + return { + list: "Elenco", + notice_failed_themes: "Impossibile caricare alcuni Themes [{{var0}}] ", + notice_new_themes: "Il nuovo Themes [{{var0}}] è stato aggiunto a Theme Repo", + notice_outdated_themes: "Alcuni Themes [{{var0}}] non sono aggiornati" + }; + case "ja": // Japanese + return { + list: "リスト", + notice_failed_themes: "一部の Themes [{{var0}}] を読み込めませんでした", + notice_new_themes: "新しい Themes [{{var0}}] が Theme Repo に追加されました", + notice_outdated_themes: "一部の Themes [{{var0}}] は古くなっています" + }; + case "ko": // Korean + return { + list: "명부", + notice_failed_themes: "일부 Themes [{{var0}}] 을 (를)로드 할 수 없습니다.", + notice_new_themes: "새 Themes [{{var0}}] 이 Theme Repo 에 추가되었습니다.", + notice_outdated_themes: "일부 Themes [{{var0}}] 이 오래되었습니다." + }; + case "lt": // Lithuanian + return { + list: "Sąrašas", + notice_failed_themes: "Kai kurių Themes [{{var0}}] nepavyko įkelti", + notice_new_themes: "Naujas Themes [{{var0}}] pridėtas prie Theme Repo", + notice_outdated_themes: "Kai kurie Themes [{{var0}}] yra pasenę" + }; + case "nl": // Dutch + return { + list: "Lijst", + notice_failed_themes: "Sommige Themes [{{var0}}] konden niet worden geladen", + notice_new_themes: "Nieuwe Themes [{{var0}}] zijn toegevoegd aan de Theme Repo", + notice_outdated_themes: "Sommige Themes [{{var0}}] zijn verouderd" + }; + case "no": // Norwegian + return { + list: "Liste", + notice_failed_themes: "Noen Themes [{{var0}}] kunne ikke lastes inn", + notice_new_themes: "Nye Themes [{{var0}}] er lagt til i Theme Repo", + notice_outdated_themes: "Noen Themes [{{var0}}] er utdaterte" + }; + case "pl": // Polish + return { + list: "Lista", + notice_failed_themes: "Nie można załadować niektórych Themes [{{var0}}] ", + notice_new_themes: "Nowe Themes [{{var0}}] zostały dodane do Theme Repo", + notice_outdated_themes: "Niektóre Themes [{{var0}}] są nieaktualne" + }; + case "pt-BR": // Portuguese (Brazil) + return { + list: "Lista", + notice_failed_themes: "Algum Themes [{{var0}}] não pôde ser carregado", + notice_new_themes: "Novo Themes [{{var0}}] foi adicionado ao Theme Repo", + notice_outdated_themes: "Alguns Themes [{{var0}}] estão desatualizados" + }; + case "ro": // Romanian + return { + list: "Listă", + notice_failed_themes: "Unele Themes [{{var0}}] nu au putut fi încărcate", + notice_new_themes: "Themes [{{var0}}] nou au fost adăugate la Theme Repo", + notice_outdated_themes: "Unele Themes [{{var0}}] sunt învechite" + }; + case "ru": // Russian + return { + list: "Список", + notice_failed_themes: "Не удалось загрузить некоторые Themes [{{var0}}] ", + notice_new_themes: "Новые Themes [{{var0}}] добавлены в Theme Repo", + notice_outdated_themes: "Некоторые Themes [{{var0}}] устарели" + }; + case "sv": // Swedish + return { + list: "Lista", + notice_failed_themes: "Vissa Themes [{{var0}}] kunde inte laddas", + notice_new_themes: "Nya Themes [{{var0}}] har lagts till i Theme Repo", + notice_outdated_themes: "Vissa Themes [{{var0}}] är föråldrade" + }; + case "th": // Thai + return { + list: "รายการ", + notice_failed_themes: "ไม่สามารถโหลด Themes [{{var0}}] บางรายการได้", + notice_new_themes: "เพิ่ม Themes [{{var0}}] ใหม่ใน Theme Repo แล้ว", + notice_outdated_themes: "Themes [{{var0}}] บางรายการล้าสมัย" + }; + case "tr": // Turkish + return { + list: "Liste", + notice_failed_themes: "Bazı Themes [{{var0}}] yüklenemedi", + notice_new_themes: "Yeni Themes [{{var0}}], Theme Repo 'ye eklendi", + notice_outdated_themes: "Bazı Themes [{{var0}}] güncel değil" + }; + case "uk": // Ukrainian + return { + list: "Список", + notice_failed_themes: "Деякі Themes [{{var0}}] не вдалося завантажити", + notice_new_themes: "Нові Themes [{{var0}}] були додані до Theme Repo", + notice_outdated_themes: "Деякі Themes [{{var0}}] застарілі" + }; + case "vi": // Vietnamese + return { + list: "Danh sách", + notice_failed_themes: "Không thể tải một số Themes [{{var0}}] ", + notice_new_themes: "Themes [{{var0}}] mới đã được thêm vào Theme Repo", + notice_outdated_themes: "Một số Themes [{{var0}}] đã lỗi thời" + }; + case "zh-CN": // Chinese (China) + return { + list: "清单", + notice_failed_themes: "某些 Themes [{{var0}}] 无法加载", + notice_new_themes: "新的 Themes [{{var0}}] 已添加到 Theme Repo", + notice_outdated_themes: "一些 Themes [{{var0}}] 已过时" + }; + case "zh-TW": // Chinese (Taiwan) + return { + list: "清單", + notice_failed_themes: "某些 Themes [{{var0}}] 無法加載", + notice_new_themes: "新的 Themes [{{var0}}] 已添加到 Theme Repo", + notice_outdated_themes: "一些 Themes [{{var0}}] 已過時" + }; + default: // English + return { + list: "List", + notice_failed_themes: "Some Themes [{{var0}}] could not be loaded", + notice_new_themes: "New Themes [{{var0}}] have been added to the Theme Repo", + notice_outdated_themes: "Some Themes [{{var0}}] are outdated" + }; + } + } + }; + })(window.BDFDB_Global.PluginUtils.buildPlugin(changeLog)); +})(); \ No newline at end of file diff --git a/BetterDiscord/plugins/TypingUsersAvatars.config.json b/BetterDiscord/plugins/TypingUsersAvatars.config.json new file mode 100644 index 0000000..26f4bb6 --- /dev/null +++ b/BetterDiscord/plugins/TypingUsersAvatars.config.json @@ -0,0 +1,6 @@ +{ + "currentVersionInfo": { + "version": "1.0.5", + "hasShownChangelog": true + } +} \ No newline at end of file diff --git a/BetterDiscord/plugins/TypingUsersAvatars.plugin.js b/BetterDiscord/plugins/TypingUsersAvatars.plugin.js new file mode 100644 index 0000000..9d8b7ba --- /dev/null +++ b/BetterDiscord/plugins/TypingUsersAvatars.plugin.js @@ -0,0 +1,194 @@ +/** +* @name TypingUsersAvatars +* @source https://github.com/QWERTxD/BetterDiscordPlugins/blob/main/TypingUsersAvatars/TypingUsersAvatars.plugin.js +* @updateUrl https://raw.githubusercontent.com/QWERTxD/BetterDiscordPlugins/main/TypingUsersAvatars/TypingUsersAvatars.plugin.js +* @website https://github.com/QWERTxD/BetterDiscordPlugins/tree/main/TypingUsersAvatars +* @version 1.0.5 +* @description Shows avatars of typing users. +*/ + +const request = require('request'); +const fs = require('fs'); +const path = require('path'); + +const config = { + info: { + name: 'TypingUsersAvatars', + authors: [ + { + name: 'QWERT' + } + ], + version: '1.0.5', + description: 'Shows avatars of typing users.', + github_raw: "https://raw.githubusercontent.com/QWERTxD/BetterDiscordPlugins/main/TypingUsersAvatars/TypingUsersAvatars.plugin.js", + }, + changelog: [ + { + title: 'What\'s new?', + type: 'added', + items: [ + 'Added option to show guild avatars of typing users', + ] + } + ], + defaultConfig: [ + { + type: 'switch', + name: 'Show users status', + id: 'showStatus', + value: true + }, + { + type: 'switch', + name: 'Show guild avatars', + id: 'showGuildAvatar', + value: true + } + ] +}; + +module.exports = !global.ZeresPluginLibrary ? class { + constructor() { + this._config = config; + } + + load() { + BdApi.showConfirmationModal('Library plugin is needed', + `The library plugin needed for AQWERT'sPluginBuilder is missing. Please click Download Now to install it.`, { + confirmText: 'Download', + cancelText: 'Cancel', + onConfirm: () => { + request.get('https://rauenzi.github.io/BDPluginLibrary/release/0PluginLibrary.plugin.js', (error, response, body) => { + if (error) + return electron.shell.openExternal('https://betterdiscord.net/ghdl?url=https://raw.githubusercontent.com/rauenzi/BDPluginLibrary/master/release/0PluginLibrary.plugin.js'); + + fs.writeFileSync(path.join(BdApi.Plugins.folder, '0PluginLibrary.plugin.js'), body); + }); + } + }); + } + + start() { } + + stop() { } +} : (([Plugin, Library]) => { + const { DiscordModules, WebpackModules, PluginUtilities, Patcher, ReactComponents, Popouts, Utilities, DiscordSelectors } = Library; + const { React, UserStore, RelationshipStore, UserStatusStore, Strings } = DiscordModules; + const AssetUtils = WebpackModules.getByProps("getUserBannerURL"); + const Avatar = WebpackModules.getByProps('AnimatedAvatar'); + const VoiceUserSummary = WebpackModules.findByDisplayName("VoiceUserSummaryItem") + + class AvatarComponent extends React.Component { + render() { + const { user, status, guildId } = this.props; + const guildAvatar = AssetUtils.getGuildMemberAvatarURL({ guildId: guildId, userId: user.id, avatar: user.guildMemberAvatars[guildId] }); + return React.createElement(Avatar.default, { + src: user?.guildMemberAvatars[guildId] ? guildAvatar : user.getAvatarURL(), + status: status, + size: Avatar.Sizes.SIZE_16, + onClick() { + Popouts.showUserPopout(document.getElementById(`typing-user-${user.id}`), user) + } + }) + } + } + + class plugin extends Plugin { + constructor() { + super(); + this.getSettingsPanel = () => { + return this.buildSettingsPanel().getElement(); + }; + } + + onStart() { + Utilities.suppressErrors(this.patch.bind(this))(); + + PluginUtilities.addStyle('TypingUsersAvatars', ` + .typing-2J1mQU .text-3S7XCz { + margin: 0; + } + + .typing-2J1mQU .wrapper-1VLyxH { + display: flex; + margin: 0 4px; + } + + .typing-2J1mQU .text-3S7XCz, + .typing-2J1mQU .text-3S7XCz > strong { + display: contents; + } + + .several-users { + margin-left: 5px; + margin-right: 5px; + } + + .several-users .avatarSize-1KpZ5E { + margin: 0; + } + `) + } + + onStop() { + Patcher.unpatchAll(); + PluginUtilities.removeStyle('TypingUsersAvatars'); + } + + filter(users) { + return Object.keys(users).filter((user) => { + return user != UserStore.getCurrentUser().id && !RelationshipStore.isBlocked(user); + }) + } + + /* code highly inspired by https://github.com/rauenzi/BetterDiscordAddons/blob/master/Plugins/BetterRoleColors/BetterRoleColors.plugin.js */ + async patch() { + const TypingUsers = await ReactComponents.getComponentByName('TypingUsers', DiscordSelectors.Typing.typing); + Patcher.after(TypingUsers.component.prototype, 'render', (thisObject, [props], ret) => { + const typingUsers = this.filter({ ...thisObject.props.typingUsers }); + const guildId = thisObject.props?.guildId; + + for (let u = 0; u < typingUsers.length; u++) { + const user = UserStore.getUser(typingUsers[u]); + const status = this.settings.showStatus ? UserStatusStore.getStatus(user.id) : null; + + if (ret.props.children[0].props.children[1]?.props.children !== Strings.Messages.SEVERAL_USERS_TYPING) { + const usersComponent = ret.props.children[0].props.children[1].props.children.filter(user => user.props); + + usersComponent[u].props.children.unshift(React.createElement("div", { + id: `typing-user-${user.id}`, + children: React.createElement(AvatarComponent, { user, status, guildId }) + })); + } else { + ret.props.children[0].props.children = [ + React.createElement(VoiceUserSummary, { + className: "several-users", + users: typingUsers.map(UserStore.getUser), + max: 3 + }), + ret.props.children[0].props.children + ] + } + } + + if (!ret) return; + const tree = ret.props.children; + if (!tree) return; + + tree.map((child) => { + const children = child?.props?.children; + if (!children || typeof children !== 'object') return; + + child.props.style = { + display: 'flex', + alignItems: 'center' + }; + }) + }) + } + + } + + return plugin; +})(global.ZeresPluginLibrary.buildPlugin(config)); diff --git a/BetterDiscord/plugins/ZeresPluginLibrary.config.json b/BetterDiscord/plugins/ZeresPluginLibrary.config.json new file mode 100644 index 0000000..e0d93ed --- /dev/null +++ b/BetterDiscord/plugins/ZeresPluginLibrary.config.json @@ -0,0 +1,6 @@ +{ + "currentVersionInfo": { + "version": "2.0.19", + "hasShownChangelog": true + } +} \ No newline at end of file diff --git a/BetterDiscord/plugins/gh-redirect?id=9 b/BetterDiscord/plugins/gh-redirect?id=9 new file mode 100644 index 0000000..9fbd038 --- /dev/null +++ b/BetterDiscord/plugins/gh-redirect?id=9 @@ -0,0 +1,6321 @@ +/** + * @name ZeresPluginLibrary + * @description Gives other plugins utility functions. + * @version 2.0.17 + * @author Zerebos + * @source https://github.com/rauenzi/BDPluginLibrary + */ + +/*@cc_on +@if (@_jscript) + + // Offer to self-install for clueless users that try to run this directly. + var shell = WScript.CreateObject("WScript.Shell"); + var fs = new ActiveXObject("Scripting.FileSystemObject"); + var pathPlugins = shell.ExpandEnvironmentStrings("%APPDATA%\\BetterDiscord\\plugins"); + var pathSelf = WScript.ScriptFullName; + // Put the user at ease by addressing them in the first person + shell.Popup("It looks like you've mistakenly tried to run me directly. \n(Don't do that!)", 0, "I'm a plugin for BetterDiscord", 0x30); + if (fs.GetParentFolderName(pathSelf) === fs.GetAbsolutePathName(pathPlugins)) { + shell.Popup("I'm in the correct folder already.", 0, "I'm already installed", 0x40); + } else if (!fs.FolderExists(pathPlugins)) { + shell.Popup("I can't find the BetterDiscord plugins folder.\nAre you sure it's even installed?", 0, "Can't install myself", 0x10); + } else if (shell.Popup("Should I copy myself to BetterDiscord's plugins folder for you?", 0, "Do you need some help?", 0x34) === 6) { + fs.CopyFile(pathSelf, fs.BuildPath(pathPlugins, fs.GetFileName(pathSelf)), true); + // Show the user where to put plugins in the future + shell.Exec("explorer " + pathPlugins); + shell.Popup("I'm installed!", 0, "Successfully installed", 0x40); + } + WScript.Quit(); + +@else@*/ + +/******/ (() => { // webpackBootstrap +/******/ var __webpack_modules__ = ({ + +/***/ "./src/styles/settings.css": +/*!*********************************!*\ + !*** ./src/styles/settings.css ***! + \*********************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__) +/* harmony export */ }); +/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (".plugin-input-group {\r\n margin-top: 5px;\r\n}\r\n\r\n.plugin-input-group .button-collapse {\r\n background: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4NCjwhLS0gR2VuZXJhdG9yOiBBZG9iZSBJbGx1c3RyYXRvciAxOS4wLjAsIFNWRyBFeHBvcnQgUGx1Zy1JbiAuIFNWRyBWZXJzaW9uOiA2LjAwIEJ1aWxkIDApICAtLT4NCjxzdmcgdmVyc2lvbj0iMS4xIiBpZD0iQ2FscXVlXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4Ig0KCSB2aWV3Qm94PSItOTUwIDUzMiAxOCAxOCIgc3R5bGU9ImVuYWJsZS1iYWNrZ3JvdW5kOm5ldyAtOTUwIDUzMiAxOCAxODsiIHhtbDpzcGFjZT0icHJlc2VydmUiPg0KPHN0eWxlIHR5cGU9InRleHQvY3NzIj4NCgkuc3Qwe2ZpbGw6bm9uZTt9DQoJLnN0MXtmaWxsOm5vbmU7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLXdpZHRoOjEuNTtzdHJva2UtbWl0ZXJsaW1pdDoxMDt9DQo8L3N0eWxlPg0KPHBhdGggY2xhc3M9InN0MCIgZD0iTS05MzIsNTMydjE4aC0xOHYtMThILTkzMnoiLz4NCjxwb2x5bGluZSBjbGFzcz0ic3QxIiBwb2ludHM9Ii05MzYuNiw1MzguOCAtOTQxLDU0My4yIC05NDUuNCw1MzguOCAiLz4NCjwvc3ZnPg0K);\r\n height: 16px;\r\n width: 16px;\r\n display: inline-block;\r\n vertical-align: bottom;\r\n transition: transform .3s ease;\r\n transform: rotate(0);\r\n}\r\n\r\n.plugin-input-group .button-collapse.collapsed {\r\n transition: transform .3s ease;\r\n transform: rotate(-90deg);\r\n}\r\n\r\n.plugin-input-group h2 {\r\n font-size: 14px;\r\n}\r\n\r\n.plugin-input-group .plugin-input-group h2 {\r\n margin-left: 16px;\r\n}\r\n\r\n.plugin-inputs {\r\n height: auto;\r\n overflow: hidden;\r\n transition: height 300ms cubic-bezier(0.47, 0, 0.745, 0.715);\r\n}\r\n\r\n.plugin-inputs.collapsed {\r\n height: 0px;\r\n}\r\n\r\n.file-input {\r\n color: var(--text-normal);\r\n background-color: var(--input-background);\r\n width: 100%;\r\n border-radius: 5px;\r\n padding: 10px;\r\n height: 40px;\r\n box-sizing: border-box;\r\n overflow: hidden;\r\n }\r\n \r\n .file-input::-webkit-file-upload-button {\r\n color: white;\r\n background: #7289DA;\r\n outline: 0;\r\n border: 0;\r\n padding: 12px!important;\r\n margin-top: -10px;\r\n margin-left: -10px;\r\n margin-right: 10px;\r\n bottom: 0;\r\n border-radius: 3px 0 0 3px;\r\n font-size: 14px;\r\n font-weight: 500;\r\n font-family: Whitney,Helvetica Neue,Helvetica,Arial,sans-serif;\r\n cursor: pointer;\r\n }\r\n\r\n.color-input {\r\n background: none;\r\n padding: 0;\r\n border: none;\r\n}\r\n\r\n.color-input:hover {\r\n opacity: 0.8;\r\n}\r\n\r\n\r\n.z-select {\r\n position: relative;\r\n cursor: pointer;\r\n color: var(--text-normal);\r\n font-size: 14px;\r\n display: flex;\r\n align-items: center;\r\n justify-content: space-between;\r\n background-color: var(--deprecated-text-input-bg);\r\n border: 1px solid var(--deprecated-text-input-border);\r\n border-radius: 3px;\r\n padding: 8px 8px 8px 12px;\r\n transition: 150ms ease border-color;\r\n }\r\n \r\n .z-select:hover,\r\n .z-select.menu-open {\r\n border-color: var(--background-tertiary);\r\n }\r\n \r\n .z-select.z-select-transparent {\r\n align-items: flex-start;\r\n background: none;\r\n border: none;\r\n padding: 0;\r\n }\r\n \r\n .z-select-icons {\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n }\r\n \r\n .z-select-clear,\r\n .z-select-arrow {\r\n margin-left: 5px;\r\n fill: var(--interactive-normal);\r\n }\r\n \r\n .z-select .z-select-options {\r\n position: absolute;\r\n background: var(--background-secondary);\r\n border-radius: 0 0 3px 3px;\r\n max-height: 125px;\r\n min-width: 100%;\r\n overflow-y: auto;\r\n box-shadow: rgba(0, 0, 0, 0.3) 0 1px 5px 0;\r\n border: 1px solid rgba(0, 0, 0, 0.3);\r\n border-top: 0;\r\n margin-top: -1px;\r\n margin-left: -13px;\r\n z-index: 2;\r\n top: 100%;\r\n }\r\n \r\n .z-select-transparent .z-select-options {\r\n border: 1px solid rgba(0, 0, 0, 0.3);\r\n margin-top: 3px;\r\n border-radius: 3px;\r\n }\r\n \r\n .z-select .z-select-option {\r\n padding: 8px 12px;\r\n cursor: pointer;\r\n white-space: pre;\r\n }\r\n \r\n .z-select .z-select-option:hover {\r\n background: rgba(0, 0, 0, 0.1);\r\n }\r\n \r\n .z-select .z-select-option.selected {\r\n background: rgba(0, 0, 0, 0.2);\r\n }\r\n\r\n\r\n\r\n .z-keybind-wrapper {\r\n display: flex;\r\n justify-content: space-between;\r\n align-items: center;\r\n }\r\n \r\n .z-keybind-wrapper > :first-child {\r\n flex-grow: 1;\r\n }\r\n \r\n .z-keybind-clear {\r\n margin-left: 5px;\r\n fill: var(--interactive-normal);\r\n cursor: pointer;\r\n }\r\n\r\n.plugin-input-container > .container-31PmuA {\r\n margin: 10px 0;\r\n}"); + +/***/ }), + +/***/ "./src/styles/toasts.css": +/*!*******************************!*\ + !*** ./src/styles/toasts.css ***! + \*******************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__) +/* harmony export */ }); +/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (".toasts {\r\n position: fixed;\r\n display: flex;\r\n top: 0;\r\n flex-direction: column;\r\n align-items: center;\r\n justify-content: flex-end;\r\n pointer-events: none;\r\n z-index: 4000;\r\n}\r\n\r\n@keyframes toast-up {\r\n from {\r\n transform: translateY(0);\r\n opacity: 0;\r\n }\r\n}\r\n\r\n.toast {\r\n animation: toast-up 300ms ease;\r\n transform: translateY(-10px);\r\n background: #36393F;\r\n padding: 10px;\r\n border-radius: 5px;\r\n box-shadow: 0 0 0 1px rgba(32,34,37,.6), 0 2px 10px 0 rgba(0,0,0,.2);\r\n font-weight: 500;\r\n color: #fff;\r\n user-select: text;\r\n font-size: 14px;\r\n opacity: 1;\r\n margin-top: 10px;\r\n display: flex;\r\n justify-content: center;\r\n align-items: center;\r\n}\r\n\r\n@keyframes toast-down {\r\n to {\r\n transform: translateY(0px);\r\n opacity: 0;\r\n }\r\n}\r\n\r\n.toast.closing {\r\n animation: toast-down 200ms ease;\r\n animation-fill-mode: forwards;\r\n opacity: 1;\r\n transform: translateY(-10px);\r\n}\r\n\r\n.toast.toast-info {\r\n background-color: #4a90e2;\r\n}\r\n\r\n.toast.toast-success {\r\n background-color: #43b581;\r\n}\r\n\r\n.toast.toast-danger,\r\n.toast.toast-error {\r\n background-color: #f04747;\r\n}\r\n\r\n.toast.toast-warning,\r\n.toast.toast-warn {\r\n background-color: #FFA600;\r\n}\r\n\r\n.toast-icon {\r\n margin-right: 5px;\r\n fill: white;\r\n border-radius: 50%;\r\n overflow: hidden;\r\n height: 20px;\r\n width: 20px;\r\n}\r\n\r\n.toast-text {\r\n line-height: 20px;\r\n}"); + +/***/ }), + +/***/ "./src/styles/updates.css": +/*!********************************!*\ + !*** ./src/styles/updates.css ***! + \********************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__) +/* harmony export */ }); +/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = ("#outdated-plugins {\r\n font-weight: 700;\r\n}\r\n#outdated-plugins > span {\r\n -webkit-app-region: no-drag;\r\n color: #fff;\r\n cursor: pointer;\r\n}\r\n#outdated-plugins > span:hover {\r\n text-decoration: underline;\r\n}"); + +/***/ }), + +/***/ "./src/config.js": +/*!***********************!*\ + !*** ./src/config.js ***! + \***********************/ +/***/ ((module) => { + +// Use non-ES6 so build script can require() +// Options: added, improved, fixed, progress. +module.exports = { + id: "9", + name: "ZeresPluginLibrary", + author: "Zerebos", + version: "2.0.17", + description: "Gives other plugins utility functions.", + source: "https://github.com/rauenzi/BDPluginLibrary", + github_raw: "https://raw.githubusercontent.com/rauenzi/BDPluginLibrary/master/release/0PluginLibrary.plugin.js", + changelog: [ + {title: "Changes", type: "progress", items: ["Library no longer tries to update itself. Update it through BetterDiscord's updater.", "The library still checks for updates for other plugins currently."]}, + {title: "Fixed", type: "fixed", items: ["Fixed update looping when updating library.", "Fixed Switch settings not working causing some panels to not render at all.", "Fixed toasts not working in some cases."]}, + ], + main: "index.js" +}; + + +/***/ }), + +/***/ "./src/modules/colorconverter.js": +/*!***************************************!*\ + !*** ./src/modules/colorconverter.js ***! + \***************************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "default": () => (/* binding */ ColorConverter) +/* harmony export */ }); +/** + * Helpful utilities for dealing with colors. + * @module ColorConverter + */ + + +const validHexRegex = /#([a-fA-F0-9]{1,2})([a-fA-F0-9]{1,2})([a-fA-F0-9]{1,2})/; + +class ColorConverter { + + static getDarkness(color) { + const [red, green, blue] = this.getRGB(color); + return 1 - (0.299 * red + 0.587 * green + 0.114 * blue) / 255; + } + + static hex2int(color) { + if (color.length === 4) color = `#${color[1]}${color[1]}${color[2]}${color[2]}${color[3]}${color[3]}`; + return parseInt(color.slice(1), 16); + } + + static hex2rgb(color) { + const [red, green, blue] = this.getRGB(color); + return `rgb(${red}, ${green}, ${blue})`; + } + + static int2hex(color) { + const red = color >> 16 & 255; + const green = color >> 8 & 255; + const blue = color & 255; + return `#${red.toString(16)}${green.toString(16)}${blue.toString(16)}`; + } + + static int2rgba(color, alpha) { + return `rgba(${color >> 16 & 255}, ${color >> 8 & 255}, ${color & 255}, ${alpha})`; + } + + static isValidHex(color) { + return color.match(validHexRegex) != null; + } + + /** + * Will get the red green and blue values of any color string. + * @param {string} color - the color to obtain the red, green and blue values of. Can be in any of these formats: #fff, #ffffff, rgb, rgba + * @returns {Array} - array containing the red, green, and blue values + */ + static getRGB(color) { + let result = /rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/.exec(color); + if (result) return [parseInt(result[1]), parseInt(result[2]), parseInt(result[3])]; + + result = /rgb\(\s*([0-9]+(?:\.[0-9]+)?)%\s*,\s*([0-9]+(?:\.[0-9]+)?)%\s*,\s*([0-9]+(?:\.[0-9]+)?)%\s*\)/.exec(color); + if (result) return [parseFloat(result[1]) * 2.55, parseFloat(result[2]) * 2.55, parseFloat(result[3]) * 2.55]; + + result = /#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec(color); + if (result) return [parseInt(result[1], 16), parseInt(result[2], 16), parseInt(result[3], 16)]; + + result = /#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/.exec(color); + if (result) return [parseInt(result[1] + result[1], 16), parseInt(result[2] + result[2], 16), parseInt(result[3] + result[3], 16)]; + } + + /** + * Will get the darken the color by a certain percent + * @param {string} color - Can be in any of these formats: #fff, #ffffff, rgb, rgba + * @param {number} percent - percent to darken the color by (0-100) + * @returns {string} - new color in rgb format + */ + static darkenColor(color, percent) { + const rgb = this.getRGB(color); + if (!rgb) return color; + for (let i = 0; i < rgb.length; i++) rgb[i] = Math.round(Math.max(0, rgb[i] - rgb[i] * (percent / 100))); + return "rgb(" + rgb[0] + "," + rgb[1] + "," + rgb[2] + ")"; + } + + /** + * Will get the lighten the color by a certain percent + * @param {string} color - Can be in any of these formats: #fff, #ffffff, rgb, rgba + * @param {number} percent - percent to lighten the color by (0-100) + * @returns {string} - new color in rgb format + */ + static lightenColor(color, percent) { + const rgb = this.getRGB(color); + if (!rgb) return color; + for (let i = 0; i < rgb.length; i++) rgb[i] = Math.round(Math.min(255, rgb[i] + rgb[i] * (percent / 100))); + return "rgb(" + rgb[0] + "," + rgb[1] + "," + rgb[2] + ")"; + } + + /** + * Converts a color to rgba format string + * @param {string} color - Can be in any of these formats: #fff, #ffffff, rgb, rgba + * @param {number} alpha - alpha level for the new color + * @returns {string} - new color in rgb format + */ + static rgbToAlpha(color, alpha) { + const rgb = this.getRGB(color); + if (!rgb) return color; + return "rgba(" + rgb[0] + "," + rgb[1] + "," + rgb[2] + "," + alpha + ")"; + } + +} + +/***/ }), + +/***/ "./src/modules/discordclasses.js": +/*!***************************************!*\ + !*** ./src/modules/discordclasses.js ***! + \***************************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__) +/* harmony export */ }); +/* harmony import */ var _discordclassmodules__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./discordclassmodules */ "./src/modules/discordclassmodules.js"); +/* harmony import */ var _domtools__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./domtools */ "./src/modules/domtools.js"); + + + +const getRaw = function(prop) { + if (!this.hasOwnProperty(prop)) return ""; + return this[prop]; +}; + +const getClass = function(prop) { + if (!this.hasOwnProperty(prop)) return ""; + return this[prop].split(" ")[0]; +}; + +/** + * Proxy for all the class packages, allows us to safely attempt + * to retrieve nested things without error. Also wraps the class in + * {@link module:DOMTools.ClassName} which adds features but can still + * be used in native function. + * + * For a list of all available class namespaces check out {@link module:DiscordClassModules}. + * + * @see module:DiscordClassModules + * @module DiscordClasses + */ +const DiscordModules = new Proxy(_discordclassmodules__WEBPACK_IMPORTED_MODULE_0__["default"], { + get: function(list, item) { + if (item == "getRaw" || item == "getClass") return (module, prop) => DiscordModules[module][item]([prop]); + if (list[item] === undefined) return new Proxy({}, {get: function() {return "";}}); + return new Proxy(list[item], { + get: function(obj, prop) { + if (prop == "getRaw") return getRaw.bind(obj); + if (prop == "getClass") return getClass.bind(obj); + if (!obj.hasOwnProperty(prop)) return ""; + return new _domtools__WEBPACK_IMPORTED_MODULE_1__["default"].ClassName(obj[prop]); + } + }); + } +}); +/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (DiscordModules); + +/***/ }), + +/***/ "./src/modules/discordclassmodules.js": +/*!********************************************!*\ + !*** ./src/modules/discordclassmodules.js ***! + \********************************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__) +/* harmony export */ }); +/* harmony import */ var _utilities__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./utilities */ "./src/modules/utilities.js"); +/* harmony import */ var _webpackmodules__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./webpackmodules */ "./src/modules/webpackmodules.js"); + + + +/** + * A large list of known and labelled classes in discord. + * Click the source link down below to view more info. Otherwise, if you + * have the library installed or have a plugin using this library, + * do `Object.keys(ZLibrary.DiscordClassModules)` in console for a list of modules. + * + * You can use this directly, however the preferred way of doing this is to use {@link module:DiscordClasses} or {@link module:DiscordSelectors} + * + * @see module:DiscordClasses + * @see module:DiscordSelectors + * @module DiscordClassModules + */ +/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (_utilities__WEBPACK_IMPORTED_MODULE_0__["default"].memoizeObject({ + get ContextMenu() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("menu", "item");}, + get Scrollers() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("thin", "scrollerBase", "content");}, + get AccountDetails() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("container", "avatar", "hasBuildOverride");}, + get Typing() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("typing", "text");}, + get UserPopout() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("userPopout");}, + get PopoutRoles() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("roleCircle");}, + get UserModal() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("profileBadge");}, + get Textarea() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("channelTextArea", "textArea");}, + get Popouts() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("popouts", "popout");}, // broken, popouts element has been removed. + get App() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("app", "mobileApp");}, + get Titles() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("defaultMarginh5");}, + get Notices() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("notice", "colorInfo");}, + get Backdrop() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("backdrop");}, + get Modals() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("hideOnFullscreen", "root");}, + get AuditLog() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("userHook");}, + get ChannelList() {return Object.assign({}, _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("containerDefault"), _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("name", "unread"), _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("sidebar", "hasNotice"));}, + get MemberList() {return Object.assign({}, _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("member", "memberInner"), _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("members", "membersWrap"));}, + get TitleWrap() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("titleWrapper");}, + get Titlebar() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("titleBar");}, + get Embeds() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("embed", "embedAuthor");}, + get Layers() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("layers", "layer");}, + get TooltipLayers() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("layerContainer", "layer");}, + get Margins() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getModule(m => !m.title && m.marginBottom40 && m.marginTop40);}, + get Dividers() {return Object.assign({}, _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("dividerDefault"), _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getModule(m => Object.keys(m).length == 1 && m.divider));}, + get Changelog() {return Object.assign({}, _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("container", "added"), _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("content", "modal", "size"));}, + get BasicInputs() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("inputDefault", "copyInput");}, + get Messages() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("message", "containerCozy");}, + get Guilds() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("guildsWrapper");}, + get EmojiPicker() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("emojiPicker", "emojiItem");}, + get Reactions() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("reaction", "reactionInner");}, + get Checkbox() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("checkbox", "checkboxInner");}, + get Tooltips() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("tooltip", "tooltipBlack");} +})); + + + +/***/ }), + +/***/ "./src/modules/discordmodules.js": +/*!***************************************!*\ + !*** ./src/modules/discordmodules.js ***! + \***************************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__) +/* harmony export */ }); +/* harmony import */ var _utilities__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./utilities */ "./src/modules/utilities.js"); +/* harmony import */ var _webpackmodules__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./webpackmodules */ "./src/modules/webpackmodules.js"); +/** + * A large list of known and useful webpack modules internal to Discord. + * Click the source link down below to view more info. Otherwise, if you + * have the library installed or have a plugin using this library, + * do `Object.keys(ZLibrary.DiscordModules)` in console for a list of modules. + * @module DiscordModules + */ + + + +/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (_utilities__WEBPACK_IMPORTED_MODULE_0__["default"].memoizeObject({ + get React() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("createElement", "cloneElement");}, + get ReactDOM() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("render", "findDOMNode");}, + get Events() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByPrototypes("setMaxListeners", "emit");}, + + /* Guild Info, Stores, and Utilities */ + get GuildStore() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("getGuild", "getGuilds");}, + get SortedGuildStore() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("getSortedGuilds");}, + get SelectedGuildStore() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("getLastSelectedGuildId");}, + get GuildSync() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("getSyncedGuilds");}, + get GuildInfo() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("getAcronym");}, + get GuildChannelsStore() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("getChannels", "getDefaultChannel");}, + get GuildMemberStore() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("getMember");}, + get MemberCountStore() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("getMemberCounts");}, + get GuildEmojiStore() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("getEmojis");}, + get GuildActions() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("requestMembers");}, + get GuildPermissions() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("getGuildPermissions");}, + + /* Channel Store & Actions */ + get ChannelStore() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("getChannel", "getDMFromUserId");}, + get SelectedChannelStore() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("getLastSelectedChannelId");}, + get ChannelActions() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("selectChannel");}, + get PrivateChannelActions() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("openPrivateChannel");}, + + /* Current User Info, State and Settings */ + get UserInfoStore() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("getSessionId");}, + get UserSettingsStore() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("guildPositions");}, + get StreamerModeStore() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("hidePersonalInformation");}, + get UserSettingsUpdater() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("updateRemoteSettings");}, + get OnlineWatcher() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("isOnline");}, + get CurrentUserIdle() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("isIdle");}, + get RelationshipStore() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("isBlocked", "getFriendIDs");}, + get RelationshipManager() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("addRelationship");}, + get MentionStore() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("getMentions");}, + + /* User Stores and Utils */ + get UserStore() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("getCurrentUser", "getUser");}, + get UserStatusStore() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("getStatus", "getState");}, + get UserTypingStore() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("isTyping");}, + get UserActivityStore() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("getActivity");}, + get UserNameResolver() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("getName");}, + get UserNoteStore() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("getNote");}, + get UserNoteActions() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("updateNote");}, + + /* Emoji Store and Utils */ + get EmojiInfo() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("isEmojiDisabled");}, + get EmojiUtils() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("getGuildEmoji");}, + get EmojiStore() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("getByCategory", "EMOJI_NAME_RE");}, + + /* Invite Store and Utils */ + get InviteStore() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("getInvites");}, + get InviteResolver() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("resolveInvite");}, + get InviteActions() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("acceptInvite");}, + + /* Discord Objects & Utils */ + get DiscordConstants() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("Permissions", "ActivityTypes", "StatusTypes");}, + get DiscordPermissions() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getModule(m => m.ADD_REACTIONS, {searchExports: true});}, + get Permissions() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("computePermissions");}, + get ColorConverter() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getModule(m => Object.values(m).some(v => v?.toString().includes(`"rgba("`)));}, + get ColorShader() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("darken");}, + get TinyColor() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByPrototypes("toRgb");}, + get ClassResolver() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("getClass");}, + get ButtonData() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getModule(m => m.BorderColors, {searchExports: true});}, + get NavigationUtils() { + return { + transitionToGuild: _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("transitionToGuildSync")?.transitionToGuildSync, + transitionTo: _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getModule(m => m?.toString?.().includes(`"transitionTo - Transitioning to "`), {searchExports: true}), + replaceWith: _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getModule(m => m?.toString?.().includes(`"Replacing route with "`), {searchExports: true}) + }; + }, + get KeybindStore() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("keyToCode");}, + + /* Discord Messages */ + get MessageStore() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("getMessage", "getMessages");}, + get ReactionsStore() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("getReactions", "_dispatcher");}, + get MessageActions() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("jumpToMessage", "_sendMessage");}, + get MessageQueue() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("enqueue");}, + get MessageParser() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getModule(m => Object.keys(m).length && Object.keys(m).every(k => k === "parse" || k === "unparse"));}, + + /* Experiments */ + get ExperimentStore() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("getExperimentOverrides");}, + get ExperimentsManager() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("isDeveloper");}, + get CurrentExperiment() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("getExperimentId");}, + + /* Streams */ + get StreamStore() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("getAllActiveStreams", "getStreamForUser");}, + get StreamPreviewStore() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("getIsPreviewLoading", "getPreviewURL");}, + + /* Images, Avatars and Utils */ + get ImageResolver() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("getUserAvatarURL", "getGuildIconURL");}, + get ImageUtils() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("getSizedImageSrc");}, + get AvatarDefaults() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("getUserAvatarURL", "DEFAULT_AVATARS");}, + + /* Drag & Drop */ + get DNDSources() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("addTarget");}, + get DNDObjects() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("DragSource");}, + + /* Electron & Other Internals with Utils*/ + get ElectronModule() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("setBadge");}, + get Flux() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("Store", "connectStores");}, + get Dispatcher() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("dispatch", "subscribe");}, + get PathUtils() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("hasBasename");}, + get NotificationModule() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("showNotification");}, + get RouterModule() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("Router");}, + get APIModule() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("getAPIBaseURL");}, + get AnalyticEvents() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("AnalyticEventConfigs");}, + get KeyGenerator() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByRegex(/"binary"/);}, + get Buffers() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("Buffer", "kMaxLength");}, + get DeviceStore() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("getDevices");}, + get SoftwareInfo() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("os");}, + get i18n() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("Messages", "languages");}, + + /* Media Stuff (Audio/Video) */ + get MediaDeviceInfo() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("Codecs", "MediaEngineContextTypes");}, + get MediaInfo() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("getOutputVolume");}, + get MediaEngineInfo() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("determineMediaEngine");}, + get VoiceInfo() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("getEchoCancellation");}, + get SoundModule() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("playSound");}, + + /* Window, DOM, HTML */ + get WindowInfo() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("isFocused", "windowSize");}, + get DOMInfo() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("canUseDOM");}, + + /* Locale/Location and Time */ + get LocaleManager() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getModule(m => m.Messages && Object.keys(m.Messages).length);}, + get Moment() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("parseZone");}, + get LocationManager() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("createLocation");}, + get Timestamps() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("fromTimestamp");}, + + /* Strings and Utils */ + get Strings() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getModule(m => m.Messages && Object.keys(m.Messages).length && m.Messages.COPY_ID);}, + get StringFormats() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("a", "z");}, + get StringUtils() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("toASCII");}, + + /* URLs and Utils */ + get URLParser() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("Url", "parse");}, + get ExtraURLs() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("getArticleURL");}, + + /* Text Processing */ + get hljs() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("highlight", "highlightBlock");}, + get SimpleMarkdown() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("parseBlock", "parseInline", "defaultOutput");}, + + /* DOM/React Components */ + /* ==================== */ + get LayerManager() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("popLayer", "pushLayer");}, + get UserSettingsWindow() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("open", "updateAccount");}, + get ChannelSettingsWindow() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("open", "updateChannel");}, + get GuildSettingsWindow() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("open", "updateGuild");}, + + /* Modals */ + get ModalActions() { + return { + openModal: _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getModule(m => typeof(m) === "function" && m?.toString().includes("onCloseCallback") && m?.toString().includes("Layer"), {searchExports: true}), + closeModal: _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getModule(m => typeof(m) === "function" && m?.toString().includes("onCloseCallback()"), {searchExports: true}) + }; + }, + get ModalStack() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("push", "update", "pop", "popWithKey");}, + get UserProfileModals() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("fetchMutualFriends", "setSection");}, + get AlertModal() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByPrototypes("handleCancel", "handleSubmit");}, + get ConfirmationModal() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getModule(m => m?.toString?.()?.includes(".confirmButtonColor"), {searchExports: true});}, + get ChangeNicknameModal() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("open", "changeNickname");}, + get CreateChannelModal() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("open", "createChannel");}, + get PruneMembersModal() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("open", "prune");}, + get NotificationSettingsModal() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("open", "updateNotificationSettings");}, + get PrivacySettingsModal() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getModule(m => m.open && m.open.toString().includes("PRIVACY_SETTINGS_MODAL"));}, + get Changelog() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getModule((m => m.defaultProps && m.defaultProps.selectable == false));}, + get ModalRoot() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getModule(m => m?.toString?.()?.includes("ENTERING") && m?.toString?.()?.includes("headerId"), {searchExports: true});}, + + /* Popouts */ + get PopoutStack() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("open", "close", "closeAll");}, + get PopoutOpener() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("openPopout");}, + get UserPopout() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getModule(m => m?.type?.toString?.().includes('Unexpected missing user'), {searchExports: true});}, + + /* Context Menus */ + get ContextMenuActions() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("openContextMenu");}, + get ContextMenuItemsGroup() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByRegex(/itemGroup/);}, + get ContextMenuItem() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByRegex(/\.label\b.*\.hint\b.*\.action\b/);}, + + /* Misc */ + get ExternalLink() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByRegex(/trusted/);}, + get TextElement() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getModule(m => m?.Sizes?.SIZE_32 && m.Colors);}, + get Anchor() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByDisplayName("Anchor");}, + get Flex() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByDisplayName("Flex");}, + get FlexChild() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("Child");}, + get Clickable() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByDisplayName("Clickable");}, + get Titles() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("Tags", "Sizes");}, + get HeaderBar() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByDisplayName("HeaderBar");}, + get TabBar() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByDisplayName("TabBar");}, + get Tooltip() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByPrototypes("renderTooltip");}, + get Spinner() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByDisplayName("Spinner");}, + + /* Forms */ + get FormTitle() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByDisplayName("FormTitle");}, + get FormSection() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByDisplayName("FormSection");}, + get FormNotice() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByDisplayName("FormNotice");}, + + /* Scrollers */ + get ScrollerThin() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("ScrollerThin").ScrollerThin;}, + get ScrollerAuto() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("ScrollerAuto").ScrollerAuto;}, + get AdvancedScrollerThin() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("AdvancedScrollerThin").AdvancedScrollerThin;}, + get AdvancedScrollerAuto() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("AdvancedScrollerAuto").AdvancedScrollerAuto;}, + get AdvancedScrollerNone() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("AdvancedScrollerNone").AdvancedScrollerNone;}, + + /* Settings */ + get SettingsWrapper() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getModule(m => m?.render?.toString?.().includes("required") && m?.render?.toString?.().includes("titleClassName"), {searchExports: true});}, + get SettingsNote() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getModule(m => m?.Types?.DESCRIPTION, {searchExports: true});}, + get SettingsDivider() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getModule(m => !m.defaultProps && m.prototype && m.prototype.render && m.prototype.render.toString().includes("default.divider"));}, + + get ColorPicker() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getModule(m => m?.displayName === "ColorPicker" && m?.defaultProps);}, + get Dropdown() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByProps("SingleSelect").SingleSelect;}, + get Keybind() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getByPrototypes("handleComboChange");}, + get RadioGroup() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getModule(m => m?.Sizes && m?.toString?.().includes("radioItemClassName"), {searchExports: true});}, + get Slider() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getModule(m => m?.defaultProps?.maxValue == 100 && m?.prototype?.renderMark, {searchExports: true});}, + get SwitchRow() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getModule(m => m?.toString?.().includes("tooltipNote"), {searchExports: true});}, + get Textbox() {return _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"].getModule(m => m?.defaultProps && m?.defaultProps?.type == "text", {searchExports: true});}, +})); + + +/***/ }), + +/***/ "./src/modules/discordselectors.js": +/*!*****************************************!*\ + !*** ./src/modules/discordselectors.js ***! + \*****************************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__) +/* harmony export */ }); +/* harmony import */ var _discordclassmodules__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./discordclassmodules */ "./src/modules/discordclassmodules.js"); +/* harmony import */ var _domtools__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./domtools */ "./src/modules/domtools.js"); + + + +const getSelectorAll = function(prop) { + if (!this.hasOwnProperty(prop)) return ""; + return `.${this[prop].split(" ").join(".")}`; +}; + +const getSelector = function(prop) { + if (!this.hasOwnProperty(prop)) return ""; + return `.${this[prop].split(" ")[0]}`; +}; + +/** + * Gives us a way to retrieve the internal classes as selectors without + * needing to concatenate strings or use string templates. Wraps the + * selector in {@link module:DOMTools.Selector} which adds features but can + * still be used in native function. + * + * For a list of all available class namespaces check out {@link module:DiscordClassModules}. + * + * @see module:DiscordClassModules + * @module DiscordSelectors + */ +const DiscordSelectors = new Proxy(_discordclassmodules__WEBPACK_IMPORTED_MODULE_0__["default"], { + get: function(list, item) { + if (item == "getSelectorAll" || item == "getSelector") return (module, prop) => DiscordSelectors[module][item]([prop]); + if (list[item] === undefined) return new Proxy({}, {get: function() {return "";}}); + return new Proxy(list[item], { + get: function(obj, prop) { + if (prop == "getSelectorAll") return getSelectorAll.bind(obj); + if (prop == "getSelector") return getSelector.bind(obj); + if (!obj.hasOwnProperty(prop)) return ""; + return new _domtools__WEBPACK_IMPORTED_MODULE_1__["default"].Selector(obj[prop]); + } + }); + } +}); + +/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (DiscordSelectors); + +/***/ }), + +/***/ "./src/modules/domtools.js": +/*!*********************************!*\ + !*** ./src/modules/domtools.js ***! + \*********************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "default": () => (/* binding */ DOMTools) +/* harmony export */ }); +/* harmony import */ var structs__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! structs */ "./src/structs/structs.js"); +/** + * Helpful utilities for dealing with DOM operations. + * + * This module also extends `HTMLElement` to add a set of utility functions, + * the same as the ones available in the module itself, but with the `element` + * parameter bound to `this`. + * @module DOMTools + */ + + + +/** + * @interface + * @name Offset + * @property {number} top - Top offset of the target element. + * @property {number} right - Right offset of the target element. + * @property {number} bottom - Bottom offset of the target element. + * @property {number} left - Left offset of the target element. + * @property {number} height - Outer height of the target element. + * @property {number} width - Outer width of the target element. + */ + + /** + * Function that automatically removes added listener. + * @callback module:DOMTools~CancelListener + */ + +class DOMTools { + + static get Selector() {return structs__WEBPACK_IMPORTED_MODULE_0__.Selector;} + static get ClassName() {return structs__WEBPACK_IMPORTED_MODULE_0__.ClassName;} + static get DOMObserver() {return structs__WEBPACK_IMPORTED_MODULE_0__.DOMObserver;} + + /** + * Default DOMObserver for global usage. + * + * @see DOMObserver + */ + static get observer() { + return this._observer || (this._observer = new structs__WEBPACK_IMPORTED_MODULE_0__.DOMObserver()); + } + + /** Document/window width */ + static get screenWidth() {return Math.max(document.documentElement.clientWidth, window.innerWidth || 0);} + + /** Document/window height */ + static get screenHeight() {return Math.max(document.documentElement.clientHeight, window.innerHeight || 0);} + + static animate({timing = _ => _, update, duration}) { + // https://javascript.info/js-animation + const start = performance.now(); + + requestAnimationFrame(function renderFrame(time) { + // timeFraction goes from 0 to 1 + let timeFraction = (time - start) / duration; + if (timeFraction > 1) timeFraction = 1; + + // calculate the current animation state + const progress = timing(timeFraction); + + update(progress); // draw it + + if (timeFraction < 1) requestAnimationFrame(renderFrame); + }); + } + + /** + * Adds a style to the document. + * @param {string} id - identifier to use as the element id + * @param {string} css - css to add to the document + */ + static addStyle(id, css) { + document.head.append(DOMTools.createElement(``)); + } + + /** + * Removes a style from the document. + * @param {string} id - original identifier used + */ + static removeStyle(id) { + const element = document.getElementById(id); + if (element && element.tagName === "STYLE") element.remove(); + } + + /** + * Adds/requires a remote script to be loaded + * @param {string} id - identifier to use for this script + * @param {string} url - url from which to load the script + * @returns {Promise} promise that resolves when the script is loaded + */ + static addScript(id, url) { + return new Promise(resolve => { + const script = document.createElement("script"); + script.id = id; + script.src = url; + script.type = "text/javascript"; + script.onload = resolve; + document.head.append(script); + }); + } + + /** + * Removes a remote script from the document. + * @param {string} id - original identifier used + */ + static removeScript(id) { + const element = document.getElementById(id); + if (element && element.tagName === "SCRIPT") element.remove(); + } + + /** + * This is my shit version of not having to use `$` from jQuery. Meaning + * that you can pass a selector and it will automatically run {@link module:DOMTools.query}. + * It also means that you can pass a string of html and it will perform and return `parseHTML`. + * @see module:DOMTools.parseHTML + * @see module:DOMTools.query + * @param {string} selector - Selector to query or HTML to parse + * @returns {(DocumentFragment|NodeList|HTMLElement)} - Either the result of `parseHTML` or `query` + */ + static Q(selector) { + const element = this.parseHTML(selector); + const isHTML = element instanceof NodeList ? Array.from(element).some(n => n.nodeType === 1) : element.nodeType === 1; + if (isHTML) return element; + return this.query(selector); + } + + /** + * Essentially a shorthand for `document.querySelector`. If the `baseElement` is not provided + * `document` is used by default. + * @param {string} selector - Selector to query + * @param {Element} [baseElement] - Element to base the query from + * @returns {(Element|null)} - The found element or null if not found + */ + static query(selector, baseElement) { + if (!baseElement) baseElement = document; + return baseElement.querySelector(selector); + } + + /** + * Essentially a shorthand for `document.querySelectorAll`. If the `baseElement` is not provided + * `document` is used by default. + * @param {string} selector - Selector to query + * @param {Element} [baseElement] - Element to base the query from + * @returns {Array} - Array of all found elements + */ + static queryAll(selector, baseElement) { + if (!baseElement) baseElement = document; + return baseElement.querySelectorAll(selector); + } + + /** + * Parses a string of HTML and returns the results. If the second parameter is true, + * the parsed HTML will be returned as a document fragment {@see https://developer.mozilla.org/en-US/docs/Web/API/DocumentFragment}. + * This is extremely useful if you have a list of elements at the top level, they can then be appended all at once to another node. + * + * If the second parameter is false, then the return value will be the list of parsed + * nodes and there were multiple top level nodes, otherwise the single node is returned. + * @param {string} html - HTML to be parsed + * @param {boolean} [fragment=false] - Whether or not the return should be the raw `DocumentFragment` + * @returns {(DocumentFragment|NodeList|HTMLElement)} - The result of HTML parsing + */ + static parseHTML(html, fragment = false) { + const template = document.createElement("template"); + template.innerHTML = html; + const node = template.content.cloneNode(true); + if (fragment) return node; + return node.childNodes.length > 1 ? node.childNodes : node.childNodes[0]; + } + + /** Alternate name for {@link module:DOMTools.parseHTML} */ + static createElement(html, fragment = false) {return this.parseHTML(html, fragment);} + + /** + * Takes a string of html and escapes it using the brower's own escaping mechanism. + * @param {String} html - html to be escaped + */ + static escapeHTML(html) { + const textNode = document.createTextNode(""); + const spanElement = document.createElement("span"); + spanElement.append(textNode); + textNode.nodeValue = html; + return spanElement.innerHTML; + } + + /** + * Takes a string and escapes it for use as a DOM id. + * @param {String} id - string to be escaped + */ + static escapeID(id) { + return id.replace(/^[^a-z]+|[^\w-]+/gi, "-"); + } + + /** + * Adds a list of classes from the target element. + * @param {Element} element - Element to edit classes of + * @param {...string} classes - Names of classes to add + * @returns {Element} - `element` to allow for chaining + */ + static addClass(element, ...classes) { + classes = classes.flat().filter(c => c); + for (let c = 0; c < classes.length; c++) classes[c] = classes[c].toString().split(" "); + classes = classes.flat().filter(c => c); + element.classList.add(...classes); + return element; + } + + /** + * Removes a list of classes from the target element. + * @param {Element} element - Element to edit classes of + * @param {...string} classes - Names of classes to remove + * @returns {Element} - `element` to allow for chaining + */ + static removeClass(element, ...classes) { + for (let c = 0; c < classes.length; c++) classes[c] = classes[c].toString().split(" "); + classes = classes.flat().filter(c => c); + element.classList.remove(...classes); + return element; + } + + /** + * When only one argument is present: Toggle class value; + * i.e., if class exists then remove it and return false, if not, then add it and return true. + * When a second argument is present: + * If the second argument evaluates to true, add specified class value, and if it evaluates to false, remove it. + * @param {Element} element - Element to edit classes of + * @param {string} classname - Name of class to toggle + * @param {boolean} [indicator] - Optional indicator for if the class should be toggled + * @returns {Element} - `element` to allow for chaining + */ + static toggleClass(element, classname, indicator) { + classname = classname.toString().split(" ").filter(c => c); + if (typeof(indicator) !== "undefined") classname.forEach(c => element.classList.toggle(c, indicator)); + else classname.forEach(c => element.classList.toggle(c)); + return element; + } + + /** + * Checks if an element has a specific class + * @param {Element} element - Element to edit classes of + * @param {string} classname - Name of class to check + * @returns {boolean} - `true` if the element has the class, `false` otherwise. + */ + static hasClass(element, classname) { + return classname.toString().split(" ").filter(c => c).every(c => element.classList.contains(c)); + } + + /** + * Replaces one class with another + * @param {Element} element - Element to edit classes of + * @param {string} oldName - Name of class to replace + * @param {string} newName - New name for the class + * @returns {Element} - `element` to allow for chaining + */ + static replaceClass(element, oldName, newName) { + element.classList.replace(oldName, newName); + return element; + } + + /** + * Appends `thisNode` to `thatNode` + * @param {Node} thisNode - Node to be appended to another node + * @param {Node} thatNode - Node for `thisNode` to be appended to + * @returns {Node} - `thisNode` to allow for chaining + */ + static appendTo(thisNode, thatNode) { + if (typeof(thatNode) == "string") thatNode = this.query(thatNode); + if (!thatNode) return null; + thatNode.append(thisNode); + return thisNode; + } + + /** + * Prepends `thisNode` to `thatNode` + * @param {Node} thisNode - Node to be prepended to another node + * @param {Node} thatNode - Node for `thisNode` to be prepended to + * @returns {Node} - `thisNode` to allow for chaining + */ + static prependTo(thisNode, thatNode) { + if (typeof(thatNode) == "string") thatNode = this.query(thatNode); + if (!thatNode) return null; + thatNode.prepend(thisNode); + return thisNode; + } + + /** + * Insert after a specific element, similar to jQuery's `thisElement.insertAfter(otherElement)`. + * @param {Node} thisNode - The node to insert + * @param {Node} targetNode - Node to insert after in the tree + * @returns {Node} - `thisNode` to allow for chaining + */ + static insertAfter(thisNode, targetNode) { + targetNode.parentNode.insertBefore(thisNode, targetNode.nextSibling); + return thisNode; + } + + /** + * Insert after a specific element, similar to jQuery's `thisElement.after(newElement)`. + * @param {Node} thisNode - The node to insert + * @param {Node} newNode - Node to insert after in the tree + * @returns {Node} - `thisNode` to allow for chaining + */ + static after(thisNode, newNode) { + thisNode.parentNode.insertBefore(newNode, thisNode.nextSibling); + return thisNode; + } + + /** + * Gets the next sibling element that matches the selector. + * @param {Element} element - Element to get the next sibling of + * @param {string} [selector=""] - Optional selector + * @returns {Element} - The sibling element + */ + static next(element, selector = "") { + return selector ? element.querySelector("+ " + selector) : element.nextElementSibling; + } + + /** + * Gets all subsequent siblings. + * @param {Element} element - Element to get next siblings of + * @returns {NodeList} - The list of siblings + */ + static nextAll(element) { + return element.querySelectorAll("~ *"); + } + + /** + * Gets the subsequent siblings until an element matches the selector. + * @param {Element} element - Element to get the following siblings of + * @param {string} selector - Selector to stop at + * @returns {Array} - The list of siblings + */ + static nextUntil(element, selector) { + const next = []; + while (element.nextElementSibling && !element.nextElementSibling.matches(selector)) next.push(element = element.nextElementSibling); + return next; + } + + /** + * Gets the previous sibling element that matches the selector. + * @param {Element} element - Element to get the previous sibling of + * @param {string} [selector=""] - Optional selector + * @returns {Element} - The sibling element + */ + static previous(element, selector = "") { + const previous = element.previousElementSibling; + if (selector) return previous && previous.matches(selector) ? previous : null; + return previous; + } + + /** + * Gets all preceeding siblings. + * @param {Element} element - Element to get preceeding siblings of + * @returns {NodeList} - The list of siblings + */ + static previousAll(element) { + const previous = []; + while (element.previousElementSibling) previous.push(element = element.previousElementSibling); + return previous; + } + + /** + * Gets the preceeding siblings until an element matches the selector. + * @param {Element} element - Element to get the preceeding siblings of + * @param {string} selector - Selector to stop at + * @returns {Array} - The list of siblings + */ + static previousUntil(element, selector) { + const previous = []; + while (element.previousElementSibling && !element.previousElementSibling.matches(selector)) previous.push(element = element.previousElementSibling); + return previous; + } + + /** + * Find which index in children a certain node is. Similar to jQuery's `$.index()` + * @param {HTMLElement} node - The node to find its index in parent + * @returns {number} Index of the node + */ + static indexInParent(node) { + const children = node.parentNode.childNodes; + let num = 0; + for (let i = 0; i < children.length; i++) { + if (children[i] == node) return num; + if (children[i].nodeType == 1) num++; + } + return -1; + } + + /** Shorthand for {@link module:DOMTools.indexInParent} */ + static index(node) {return this.indexInParent(node);} + + /** + * Gets the parent of the element if it matches the selector, + * otherwise returns null. + * @param {Element} element - Element to get parent of + * @param {string} [selector=""] - Selector to match parent + * @returns {(Element|null)} - The sibling element or null + */ + static parent(element, selector = "") { + return !selector || element.parentElement.matches(selector) ? element.parentElement : null; + } + + /** + * Gets all children of Element that match the selector if provided. + * @param {Element} element - Element to get all children of + * @param {string} selector - Selector to match the children to + * @returns {Array} - The list of children + */ + static findChild(element, selector) { + return element.querySelector(":scope > " + selector); + } + + /** + * Gets all children of Element that match the selector if provided. + * @param {Element} element - Element to get all children of + * @param {string} selector - Selector to match the children to + * @returns {Array} - The list of children + */ + static findChildren(element, selector) { + return element.querySelectorAll(":scope > " + selector); + } + + /** + * Gets all ancestors of Element that match the selector if provided. + * @param {Element} element - Element to get all parents of + * @param {string} [selector=""] - Selector to match the parents to + * @returns {Array} - The list of parents + */ + static parents(element, selector = "") { + const parents = []; + if (selector) while (element.parentElement && element.parentElement.closest(selector)) parents.push(element = element.parentElement.closest(selector)); + else while (element.parentElement) parents.push(element = element.parentElement); + return parents; + } + + /** + * Gets the ancestors until an element matches the selector. + * @param {Element} element - Element to get the ancestors of + * @param {string} selector - Selector to stop at + * @returns {Array} - The list of parents + */ + static parentsUntil(element, selector) { + const parents = []; + while (element.parentElement && !element.parentElement.matches(selector)) parents.push(element = element.parentElement); + return parents; + } + + /** + * Gets all siblings of the element that match the selector. + * @param {Element} element - Element to get all siblings of + * @param {string} [selector="*"] - Selector to match the siblings to + * @returns {Array} - The list of siblings + */ + static siblings(element, selector = "*") { + return Array.from(element.parentElement.children).filter(e => e != element && e.matches(selector)); + } + + /** + * Sets or gets css styles for a specific element. If `value` is provided + * then it sets the style and returns the element to allow for chaining, + * otherwise returns the style. + * @param {Element} element - Element to set the CSS of + * @param {string} attribute - Attribute to get or set + * @param {string} [value] - Value to set for attribute + * @returns {Element|string} - When setting a value, element is returned for chaining, otherwise the value is returned. + */ + static css(element, attribute, value) { + if (typeof(value) == "undefined") return global.getComputedStyle(element)[attribute]; + element.style[attribute] = value; + return element; + } + + /** + * Sets or gets the width for a specific element. If `value` is provided + * then it sets the width and returns the element to allow for chaining, + * otherwise returns the width. + * @param {Element} element - Element to set the CSS of + * @param {string} [value] - Width to set + * @returns {Element|string} - When setting a value, element is returned for chaining, otherwise the value is returned. + */ + static width(element, value) { + if (typeof(value) == "undefined") return parseInt(getComputedStyle(element).width); + element.style.width = value; + return element; + } + + /** + * Sets or gets the height for a specific element. If `value` is provided + * then it sets the height and returns the element to allow for chaining, + * otherwise returns the height. + * @param {Element} element - Element to set the CSS of + * @param {string} [value] - Height to set + * @returns {Element|string} - When setting a value, element is returned for chaining, otherwise the value is returned. + */ + static height(element, value) { + if (typeof(value) == "undefined") return parseInt(getComputedStyle(element).height); + element.style.height = value; + return element; + } + + /** + * Sets the inner text of an element if given a value, otherwise returns it. + * @param {Element} element - Element to set the text of + * @param {string} [text] - Content to set + * @returns {string} - Either the string set by this call or the current text content of the node. + */ + static text(element, text) { + if (typeof(text) == "undefined") return element.textContent; + return element.textContent = text; + } + + /** + * Returns the innerWidth of the element. + * @param {Element} element - Element to retrieve inner width of + * @return {number} - The inner width of the element. + */ + static innerWidth(element) { + return element.clientWidth; + } + + /** + * Returns the innerHeight of the element. + * @param {Element} element - Element to retrieve inner height of + * @return {number} - The inner height of the element. + */ + static innerHeight(element) { + return element.clientHeight; + } + + /** + * Returns the outerWidth of the element. + * @param {Element} element - Element to retrieve outer width of + * @return {number} - The outer width of the element. + */ + static outerWidth(element) { + return element.offsetWidth; + } + + /** + * Returns the outerHeight of the element. + * @param {Element} element - Element to retrieve outer height of + * @return {number} - The outer height of the element. + */ + static outerHeight(element) { + return element.offsetHeight; + } + + /** + * Gets the offset of the element in the page. + * @param {Element} element - Element to get offset of + * @return {Offset} - The offset of the element + */ + static offset(element) { + return element.getBoundingClientRect(); + } + + static get listeners() {return this._listeners || (this._listeners = {});} + + /** + * This is similar to jQuery's `on` function and can *hopefully* be used in the same way. + * + * Rather than attempt to explain, I'll show some example usages. + * + * The following will add a click listener (in the `myPlugin` namespace) to `element`. + * `DOMTools.on(element, "click.myPlugin", () => {console.log("clicked!");});` + * + * The following will add a click listener (in the `myPlugin` namespace) to `element` that only fires when the target is a `.block` element. + * `DOMTools.on(element, "click.myPlugin", ".block", () => {console.log("clicked!");});` + * + * The following will add a click listener (without namespace) to `element`. + * `DOMTools.on(element, "click", () => {console.log("clicked!");});` + * + * The following will add a click listener (without namespace) to `element` that only fires once. + * `const cancel = DOMTools.on(element, "click", () => {console.log("fired!"); cancel();});` + * + * @param {Element} element - Element to add listener to + * @param {string} event - Event to listen to with option namespace (e.g. "event.namespace") + * @param {(string|callable)} delegate - Selector to run on element to listen to + * @param {callable} [callback] - Function to fire on event + * @returns {module:DOMTools~CancelListener} - A function that will undo the listener + */ + static on(element, event, delegate, callback) { + const [type, namespace] = event.split("."); + const hasDelegate = delegate && callback; + if (!callback) callback = delegate; + const eventFunc = !hasDelegate ? callback : function(ev) { + if (ev.target.matches(delegate)) { + callback(ev); + } + }; + + element.addEventListener(type, eventFunc); + const cancel = () => { + element.removeEventListener(type, eventFunc); + }; + if (namespace) { + if (!this.listeners[namespace]) this.listeners[namespace] = []; + const newCancel = () => { + cancel(); + this.listeners[namespace].splice(this.listeners[namespace].findIndex(l => l.event == type && l.element == element), 1); + }; + this.listeners[namespace].push({ + event: type, + element: element, + cancel: newCancel + }); + return newCancel; + } + return cancel; + } + + /** + * Functionality for this method matches {@link module:DOMTools.on} but automatically cancels itself + * and removes the listener upon the first firing of the desired event. + * + * @param {Element} element - Element to add listener to + * @param {string} event - Event to listen to with option namespace (e.g. "event.namespace") + * @param {(string|callable)} delegate - Selector to run on element to listen to + * @param {callable} [callback] - Function to fire on event + * @returns {module:DOMTools~CancelListener} - A function that will undo the listener + */ + static once(element, event, delegate, callback) { + const [type, namespace] = event.split("."); + const hasDelegate = delegate && callback; + if (!callback) callback = delegate; + const eventFunc = !hasDelegate ? function(ev) { + callback(ev); + element.removeEventListener(type, eventFunc); + } : function(ev) { + if (!ev.target.matches(delegate)) return; + callback(ev); + element.removeEventListener(type, eventFunc); + }; + + element.addEventListener(type, eventFunc); + const cancel = () => { + element.removeEventListener(type, eventFunc); + }; + if (namespace) { + if (!this.listeners[namespace]) this.listeners[namespace] = []; + const newCancel = () => { + cancel(); + this.listeners[namespace].splice(this.listeners[namespace].findIndex(l => l.event == type && l.element == element), 1); + }; + this.listeners[namespace].push({ + event: type, + element: element, + cancel: newCancel + }); + return newCancel; + } + return cancel; + } + + static __offAll(event, element) { + const [type, namespace] = event.split("."); + let matchFilter = listener => listener.event == type, defaultFilter = _ => _; + if (element) { + matchFilter = l => l.event == type && l.element == element; + defaultFilter = l => l.element == element; + } + const listeners = this.listeners[namespace] || []; + const list = type ? listeners.filter(matchFilter) : listeners.filter(defaultFilter); + for (let c = 0; c < list.length; c++) list[c].cancel(); + } + + /** + * This is similar to jQuery's `off` function and can *hopefully* be used in the same way. + * + * Rather than attempt to explain, I'll show some example usages. + * + * The following will remove a click listener called `onClick` (in the `myPlugin` namespace) from `element`. + * `DOMTools.off(element, "click.myPlugin", onClick);` + * + * The following will remove a click listener called `onClick` (in the `myPlugin` namespace) from `element` that only fired when the target is a `.block` element. + * `DOMTools.off(element, "click.myPlugin", ".block", onClick);` + * + * The following will remove a click listener (without namespace) from `element`. + * `DOMTools.off(element, "click", onClick);` + * + * The following will remove all listeners in namespace `myPlugin` from `element`. + * `DOMTools.off(element, ".myPlugin");` + * + * The following will remove all click listeners in namespace `myPlugin` from *all elements*. + * `DOMTools.off("click.myPlugin");` + * + * The following will remove all listeners in namespace `myPlugin` from *all elements*. + * `DOMTools.off(".myPlugin");` + * + * @param {(Element|string)} element - Element to remove listener from + * @param {string} [event] - Event to listen to with option namespace (e.g. "event.namespace") + * @param {(string|callable)} [delegate] - Selector to run on element to listen to + * @param {callable} [callback] - Function to fire on event + * @returns {Element} - The original element to allow for chaining + */ + static off(element, event, delegate, callback) { + if (typeof(element) == "string") return this.__offAll(element); + const [type, namespace] = event.split("."); + if (namespace) return this.__offAll(event, element); + + const hasDelegate = delegate && callback; + if (!callback) callback = delegate; + const eventFunc = !hasDelegate ? callback : function(ev) { + if (ev.target.matches(delegate)) { + callback(ev); + } + }; + + element.removeEventListener(type, eventFunc); + return element; + } + + /** + * Adds a listener for when the node is added/removed from the document body. + * The listener is automatically removed upon firing. + * @param {HTMLElement} node - node to wait for + * @param {callable} callback - function to be performed on event + * @param {boolean} onMount - determines if it should fire on Mount or on Unmount + */ + static onMountChange(node, callback, onMount = true) { + const wrappedCallback = () => { + this.observer.unsubscribe(wrappedCallback); + callback(); + }; + this.observer.subscribe(wrappedCallback, mutation => { + const nodes = Array.from(onMount ? mutation.addedNodes : mutation.removedNodes); + const directMatch = nodes.indexOf(node) > -1; + const parentMatch = nodes.some(parent => parent.contains(node)); + return directMatch || parentMatch; + }); + return node; + } + + /** Shorthand for {@link module:DOMTools.onMountChange} with third parameter `true` */ + static onMount(node, callback) {return this.onMountChange(node, callback);} + + /** Shorthand for {@link module:DOMTools.onMountChange} with third parameter `false` */ + static onUnmount(node, callback) {return this.onMountChange(node, callback, false);} + + /** Alias for {@link module:DOMTools.onMount} */ + static onAdded(node, callback) {return this.onMount(node, callback);} + + /** Alias for {@link module:DOMTools.onUnmount} */ + static onRemoved(node, callback) {return this.onUnmount(node, callback, false);} + + /** + * Helper function which combines multiple elements into one parent element + * @param {Array} elements - array of elements to put into a single parent + */ + static wrap(elements) { + const domWrapper = this.parseHTML(`
`); + for (let e = 0; e < elements.length; e++) domWrapper.appendChild(elements[e]); + return domWrapper; + } + + /** + * Resolves the node to an HTMLElement. This is mainly used by library modules. + * @param {(jQuery|Element)} node - node to resolve + */ + static resolveElement(node) { + try { + if (!(node instanceof window.jQuery) && !(node instanceof Element)) return undefined; + return node instanceof window.jQuery ? node[0] : node; + } + catch { + return node; + } + } +} + +/***/ }), + +/***/ "./src/modules/logger.js": +/*!*******************************!*\ + !*** ./src/modules/logger.js ***! + \*******************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "LogTypes": () => (/* binding */ LogTypes), +/* harmony export */ "default": () => (/* binding */ Logger) +/* harmony export */ }); +/** + * Simple logger for the lib and plugins. + * + * @module Logger + */ + +/* eslint-disable no-console */ + +/** + * List of logging types. + */ +const LogTypes = { + /** Alias for error */ + err: "error", + error: "error", + /** Alias for debug */ + dbg: "debug", + debug: "debug", + log: "log", + warn: "warn", + info: "info" +}; + +class Logger { + + /** + * Logs an error using a collapsed error group with stacktrace. + * + * @param {string} module - Name of the calling module. + * @param {string} message - Message or error to have logged. + * @param {Error} error - Error object to log with the message. + */ + static stacktrace(module, message, error) { + console.error(`%c[${module}]%c ${message}\n\n%c`, "color: #3a71c1; font-weight: 700;", "color: red; font-weight: 700;", "color: red;", error); + } + + /** + * Logs using error formatting. For logging an actual error object consider {@link module:Logger.stacktrace} + * + * @param {string} module - Name of the calling module. + * @param {string} message - Messages to have logged. + */ + static err(module, ...message) {Logger._log(module, message, "error");} + + /** + * Logs a warning message. + * + * @param {string} module - Name of the calling module. + * @param {...any} message - Messages to have logged. + */ + static warn(module, ...message) {Logger._log(module, message, "warn");} + + /** + * Logs an informational message. + * + * @param {string} module - Name of the calling module. + * @param {...any} message - Messages to have logged. + */ + static info(module, ...message) {Logger._log(module, message, "info");} + + /** + * Logs used for debugging purposes. + * + * @param {string} module - Name of the calling module. + * @param {...any} message - Messages to have logged. + */ + static debug(module, ...message) {Logger._log(module, message, "debug");} + + /** + * Logs used for basic loggin. + * + * @param {string} module - Name of the calling module. + * @param {...any} message - Messages to have logged. + */ + static log(module, ...message) {Logger._log(module, message);} + + /** + * Logs strings using different console levels and a module label. + * + * @param {string} module - Name of the calling module. + * @param {any|Array} message - Messages to have logged. + * @param {module:Logger.LogTypes} type - Type of log to use in console. + */ + static _log(module, message, type = "log") { + type = Logger.parseType(type); + if (!Array.isArray(message)) message = [message]; + console[type](`%c[${module}]%c`, "color: #3a71c1; font-weight: 700;", "", ...message); + } + + static parseType(type) { + return LogTypes.hasOwnProperty(type) ? LogTypes[type] : "log"; + } + +} + +/***/ }), + +/***/ "./src/modules/modules.js": +/*!********************************!*\ + !*** ./src/modules/modules.js ***! + \********************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "ColorConverter": () => (/* reexport safe */ _colorconverter__WEBPACK_IMPORTED_MODULE_3__["default"]), +/* harmony export */ "DOMTools": () => (/* reexport safe */ _domtools__WEBPACK_IMPORTED_MODULE_4__["default"]), +/* harmony export */ "DiscordClassModules": () => (/* reexport safe */ _discordclassmodules__WEBPACK_IMPORTED_MODULE_13__["default"]), +/* harmony export */ "DiscordClasses": () => (/* reexport safe */ _discordclasses__WEBPACK_IMPORTED_MODULE_5__["default"]), +/* harmony export */ "DiscordModules": () => (/* reexport safe */ _discordmodules__WEBPACK_IMPORTED_MODULE_2__["default"]), +/* harmony export */ "DiscordSelectors": () => (/* reexport safe */ _discordselectors__WEBPACK_IMPORTED_MODULE_6__["default"]), +/* harmony export */ "Filters": () => (/* reexport safe */ _webpackmodules__WEBPACK_IMPORTED_MODULE_1__.Filters), +/* harmony export */ "Logger": () => (/* reexport safe */ _logger__WEBPACK_IMPORTED_MODULE_9__["default"]), +/* harmony export */ "Patcher": () => (/* reexport safe */ _patcher__WEBPACK_IMPORTED_MODULE_10__["default"]), +/* harmony export */ "PluginUpdater": () => (/* reexport safe */ _pluginupdater__WEBPACK_IMPORTED_MODULE_11__["default"]), +/* harmony export */ "PluginUtilities": () => (/* reexport safe */ _pluginutilities__WEBPACK_IMPORTED_MODULE_12__["default"]), +/* harmony export */ "ReactComponents": () => (/* reexport safe */ _reactcomponents__WEBPACK_IMPORTED_MODULE_8__["default"]), +/* harmony export */ "ReactTools": () => (/* reexport safe */ _reacttools__WEBPACK_IMPORTED_MODULE_7__["default"]), +/* harmony export */ "Structs": () => (/* reexport module object */ structs__WEBPACK_IMPORTED_MODULE_14__), +/* harmony export */ "Utilities": () => (/* reexport safe */ _utilities__WEBPACK_IMPORTED_MODULE_0__["default"]), +/* harmony export */ "WebpackModules": () => (/* reexport safe */ _webpackmodules__WEBPACK_IMPORTED_MODULE_1__["default"]) +/* harmony export */ }); +/* harmony import */ var _utilities__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./utilities */ "./src/modules/utilities.js"); +/* harmony import */ var _webpackmodules__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./webpackmodules */ "./src/modules/webpackmodules.js"); +/* harmony import */ var _discordmodules__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./discordmodules */ "./src/modules/discordmodules.js"); +/* harmony import */ var _colorconverter__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./colorconverter */ "./src/modules/colorconverter.js"); +/* harmony import */ var _domtools__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./domtools */ "./src/modules/domtools.js"); +/* harmony import */ var _discordclasses__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./discordclasses */ "./src/modules/discordclasses.js"); +/* harmony import */ var _discordselectors__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./discordselectors */ "./src/modules/discordselectors.js"); +/* harmony import */ var _reacttools__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./reacttools */ "./src/modules/reacttools.js"); +/* harmony import */ var _reactcomponents__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ./reactcomponents */ "./src/modules/reactcomponents.js"); +/* harmony import */ var _logger__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ./logger */ "./src/modules/logger.js"); +/* harmony import */ var _patcher__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! ./patcher */ "./src/modules/patcher.js"); +/* harmony import */ var _pluginupdater__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(/*! ./pluginupdater */ "./src/modules/pluginupdater.js"); +/* harmony import */ var _pluginutilities__WEBPACK_IMPORTED_MODULE_12__ = __webpack_require__(/*! ./pluginutilities */ "./src/modules/pluginutilities.js"); +/* harmony import */ var _discordclassmodules__WEBPACK_IMPORTED_MODULE_13__ = __webpack_require__(/*! ./discordclassmodules */ "./src/modules/discordclassmodules.js"); +/* harmony import */ var structs__WEBPACK_IMPORTED_MODULE_14__ = __webpack_require__(/*! structs */ "./src/structs/structs.js"); + + + + + + + + + + + + + + + + + + + + +/***/ }), + +/***/ "./src/modules/patcher.js": +/*!********************************!*\ + !*** ./src/modules/patcher.js ***! + \********************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "default": () => (/* binding */ Patcher) +/* harmony export */ }); +/** + * Patcher that can patch other functions allowing you to run code before, after or + * instead of the original function. Can also alter arguments and return values. + * + * This is a modified version of what we have been working on in BDv2. {@link https://github.com/JsSucks/BetterDiscordApp/blob/master/client/src/modules/patcher.js} + * + * @module Patcher + */ + +class Patcher { + + // Use window._patches instead of local variables in case something tries to whack the lib + static get patches() {return [];} + + /** + * Returns all the patches done by a specific caller + * @param {string} name - Name of the patch caller + * @method + */ + static getPatchesByCaller(name) { + return BdApi.Patcher.getPatchesByCaller(name); + } + + /** + * Unpatches all patches passed, or when a string is passed unpatches all + * patches done by that specific caller. + * @param {Array|string} patches - Either an array of patches to unpatch or a caller name + */ + static unpatchAll(patches) { + BdApi.Patcher.unpatchAll(patches); + } + + /** + * Function with no arguments and no return value that may be called to revert changes made by {@link module:Patcher}, restoring (unpatching) original method. + * @callback module:Patcher~unpatch + */ + + /** + * A callback that modifies method logic. This callback is called on each call of the original method and is provided all data about original call. Any of the data can be modified if necessary, but do so wisely. + * + * The third argument for the callback will be `undefined` for `before` patches. `originalFunction` for `instead` patches and `returnValue` for `after` patches. + * + * @callback module:Patcher~patchCallback + * @param {object} thisObject - `this` in the context of the original function. + * @param {args} args - The original arguments of the original function. + * @param {(function|*)} extraValue - For `instead` patches, this is the original function from the module. For `after` patches, this is the return value of the function. + * @return {*} Makes sense only when using an `instead` or `after` patch. If something other than `undefined` is returned, the returned value replaces the value of `returnValue`. If used for `before` the return value is ignored. + */ + + /** + * This method patches onto another function, allowing your code to run beforehand. + * Using this, you are also able to modify the incoming arguments before the original method is run. + * + * @param {string} caller - Name of the caller of the patch function. Using this you can undo all patches with the same name using {@link module:Patcher.unpatchAll}. Use `""` if you don't care. + * @param {object} moduleToPatch - Object with the function to be patched. Can also patch an object's prototype. + * @param {string} functionName - Name of the method to be patched + * @param {module:Patcher~patchCallback} callback - Function to run before the original method + * @return {module:Patcher~unpatch} Function with no arguments and no return value that should be called to cancel (unpatch) this patch. You should save and run it when your plugin is stopped. + */ + static before(caller, moduleToPatch, functionName, callback) {return BdApi.Patcher.before(caller, moduleToPatch, functionName, callback);} + + /** + * This method patches onto another function, allowing your code to run after. + * Using this, you are also able to modify the return value, using the return of your code instead. + * + * @param {string} caller - Name of the caller of the patch function. Using this you can undo all patches with the same name using {@link module:Patcher.unpatchAll}. Use `""` if you don't care. + * @param {object} moduleToPatch - Object with the function to be patched. Can also patch an object's prototype. + * @param {string} functionName - Name of the method to be patched + * @param {module:Patcher~patchCallback} callback - Function to run instead of the original method + * @return {module:Patcher~unpatch} Function with no arguments and no return value that should be called to cancel (unpatch) this patch. You should save and run it when your plugin is stopped. + */ + static after(caller, moduleToPatch, functionName, callback) {return BdApi.Patcher.after(caller, moduleToPatch, functionName, callback);} + + /** + * This method patches onto another function, allowing your code to run instead. + * Using this, you are also able to modify the return value, using the return of your code instead. + * + * @param {string} caller - Name of the caller of the patch function. Using this you can undo all patches with the same name using {@link module:Patcher.unpatchAll}. Use `""` if you don't care. + * @param {object} moduleToPatch - Object with the function to be patched. Can also patch an object's prototype. + * @param {string} functionName - Name of the method to be patched + * @param {module:Patcher~patchCallback} callback - Function to run after the original method + * @return {module:Patcher~unpatch} Function with no arguments and no return value that should be called to cancel (unpatch) this patch. You should save and run it when your plugin is stopped. + */ + static instead(caller, moduleToPatch, functionName, callback) {return BdApi.Patcher.instead(caller, moduleToPatch, functionName, callback);} + +} + +/***/ }), + +/***/ "./src/modules/pluginupdater.js": +/*!**************************************!*\ + !*** ./src/modules/pluginupdater.js ***! + \**************************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "default": () => (/* binding */ PluginUpdater) +/* harmony export */ }); +/* harmony import */ var _domtools__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./domtools */ "./src/modules/domtools.js"); +/* harmony import */ var ui__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ui */ "./src/ui/ui.js"); +/* harmony import */ var _styles_updates_css__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../styles/updates.css */ "./src/styles/updates.css"); + + + + + +const fileSystem = require("fs"); +const path = require("path"); +const request = require("request"); + +/** + * Comparator that takes the current version and the remote version, + * then compares them returning `true` if there is an update and `false` otherwise. + * @param {string} currentVersion - the current version of the plugin + * @param {string} remoteVersion - the remote version of the plugin + * @returns {boolean} - whether the plugin has an update or not + * @callback module:PluginUpdater~comparator + */ + +const splitRegex = /[^\S\r\n]*?\r?(?:\r\n|\n)[^\S\r\n]*?\*[^\S\r\n]?/; +const escapedAtRegex = /^\\@/; +const HOUR_IN_MILLISECONDS = 1000 * 60 * 60; +const pluginId = name => name + "-update-notice"; +const pending = []; +const banner = {}; + + +/** + * Functions that check for and update existing plugins. + * @module PluginUpdater + * @deprecated It is recommended to go through the approval process instead. + */ +class PluginUpdater { + + static get CSS() {return _styles_updates_css__WEBPACK_IMPORTED_MODULE_2__["default"];} + static get state() {return window.__PLUGIN_UPDATES__;} + static getPlugin(link) {return this.state.plugins[link];} + static setPlugin(name, raw, version, comparator) {this.state.plugins[raw] = {name, raw, version, comparator};} + static clearPending() { + delete banner.close; + delete banner.notice; + pending.splice(0, pending.length); + } + + /** + * Checks for updates for the specified plugin at the specified link. The final + * parameter should link to the raw text of the plugin and will compare semantic + * versions. + * @param {string} pluginName - name of the plugin + * @param {string} currentVersion - current version (semantic versioning only) + * @param {string} updateURL - url to check for update + * @param {module:PluginUpdater~comparator} [comparator] - comparator that determines if there is an update. If not provided uses {@link module:PluginUpdater.defaultComparator}. + */ + static async checkForUpdate(pluginName, currentVersion, addonId, comparator) { + if (!pluginName || !currentVersion || !addonId) return; + let isUrl = false; + try { + // eslint-disable-next-line no-new + new URL(addonId); + isUrl = true; + } + catch { + isUrl = false; + } + let updateLink = `https://betterdiscord.app/gh-redirect?id=${addonId}`; + if (isUrl) updateLink = addonId; + if (typeof(comparator) != "function") comparator = this.defaultComparator; + this.setPlugin(pluginName, updateLink, currentVersion, comparator); + + const hasUpdate = await this.hasUpdate(updateLink); + if (!hasUpdate) return; + pending.push(updateLink); + this.showUpdateNotice(updateLink); + } + + static async checkAllPlugins() { + for (const link in this.state.plugins) { + const hasUpdate = await this.hasUpdate(link); + if (!hasUpdate) return; + pending.push(link); + this.showUpdateNotice(link); + } + } + + /** + * Will check for updates and automatically show or remove the update notice + * bar based on the internal result. Better not to call this directly and to + * instead use {@link module:PluginUpdater.checkForUpdate}. + * @param {string} pluginName - name of the plugin to check + * @param {string} updateLink - link to the raw text version of the plugin + */ + static async hasUpdate(updateLink) { + const doit = (resolve, result) => { + try { + const plugin = this.getPlugin(updateLink); + const meta = this.parseMeta(result); + plugin.remoteVersion = meta.version; + const hasUpdate = plugin.comparator(plugin.version, plugin.remoteVersion); + if (hasUpdate) plugin.remote = result; + resolve(hasUpdate); + } + catch (err) { + resolve(false); + } + }; + return new Promise(resolve => { + request(updateLink, (err, resp, result) => { + if (err) return resolve(false); + + // If a direct url was used + if (resp.statusCode === 200) return doit(resolve, result); + + // If an addon id and redirect was used + if (resp.statusCode === 302) { + request(resp.headers.location, (error, response, body) => { + if (error || response.statusCode !== 200) return resolve(false); + return doit(resolve, body); + }); + } + }); + }); + } + + /** + * @param {string} pluginName - name of the plugin to download + * @param {string} updateLink - link to the raw text version of the plugin + */ + static async updatePlugin(updateLink) { + const plugin = this.getPlugin(updateLink); + + let filename = updateLink.split("/"); + filename = filename[filename.length - 1]; + const file = path.join(BdApi.Plugins.folder, filename); + await new Promise(r => fileSystem.writeFile(file, plugin.remote, r)); + ui__WEBPACK_IMPORTED_MODULE_1__.Toasts.success(`${plugin.name} ${plugin.version} has been replaced by ${plugin.name} ${plugin.remoteVersion}`); + } + + /** + * Will show the update notice top bar seen in Discord. Better not to call + * this directly and to instead use {@link module:PluginUpdater.checkForUpdate}. + * @param {string} pluginName - name of the plugin + * @param {string} updateLink - link to the raw text version of the plugin + */ + static showUpdateNotice(updateLink) { + const plugin = this.getPlugin(updateLink); + const pluginNoticeID = pluginId(plugin.name); + if (document.getElementById(pluginNoticeID)) return; // This plugin already shown + if (!document.getElementById("plugin-update-notice-message")) { + banner.notice = _domtools__WEBPACK_IMPORTED_MODULE_0__["default"].parseHTML(`The following plugins have updates:  `); + banner.close = BdApi.showNotice(banner.notice, { + timeout: 0, + buttons: [{ + label: "Update All", + onClick: async () => { + for (const link of pending) await this.updatePlugin(link); + banner.close(); + } + }] + }); + _domtools__WEBPACK_IMPORTED_MODULE_0__["default"].onRemoved(banner.notice, this.clearPending); + } + + const outdatedPlugins = document.getElementById("outdated-plugins"); + const pluginNoticeElement = _domtools__WEBPACK_IMPORTED_MODULE_0__["default"].parseHTML(`${plugin.name}`); + pluginNoticeElement.addEventListener("click", async () => { + await this.updatePlugin(updateLink); + this.removeUpdateNotice(updateLink); + }); + if (outdatedPlugins.querySelectorAll("span").length) outdatedPlugins.append(_domtools__WEBPACK_IMPORTED_MODULE_0__["default"].createElement(", ")); + outdatedPlugins.append(pluginNoticeElement); + ui__WEBPACK_IMPORTED_MODULE_1__.Tooltip.create(pluginNoticeElement, "Click To Update!", {side: "bottom"}); + } + + /** + * Will remove the plugin from the update notice top bar seen in Discord. + * Better not to call this directly and to instead use {@link module:PluginUpdater.checkForUpdate}. + * @param {string} pluginName - name of the plugin + */ + static removeUpdateNotice(updateLink) { + const plugin = this.getPlugin(updateLink); + if (!document.getElementById("outdated-plugins")) return; + const notice = document.getElementById(pluginId(plugin.name)); + if (notice) { + if (notice.nextElementSibling && notice.nextElementSibling.matches(".separator")) notice.nextElementSibling.remove(); + else if (notice.previousElementSibling && notice.previousElementSibling.matches(".separator")) notice.previousElementSibling.remove(); + notice.remove(); + } + + if (!document.getElementById("outdated-plugins").querySelectorAll("span").length) { + banner?.close(); + } + } + + static parseMeta(fileContent) { + const block = fileContent.split("/**", 2)[1].split("*/", 1)[0]; + const out = {}; + let field = ""; + let accum = ""; + for (const line of block.split(splitRegex)) { + if (line.length === 0) continue; + if (line.charAt(0) === "@" && line.charAt(1) !== " ") { + out[field] = accum; + const l = line.indexOf(" "); + field = line.substring(1, l); + accum = line.substring(l + 1); + } + else { + accum += " " + line.replace("\\n", "\n").replace(escapedAtRegex, "@"); + } + } + out[field] = accum.trim(); + delete out[""]; + out.format = "jsdoc"; + return out; + } + + /** + * The default comparator used as {@link module:PluginUpdater~comparator} for {@link module:PluginUpdater.checkForUpdate}. + * This solely compares remote > local. You do not need to provide this as a comparator if your plugin adheres + * to this style as this will be used as default. + * @param {string} currentVersion + * @param {string} content + */ + static defaultComparator(currentVersion, remoteVersion) { + return remoteVersion > currentVersion; + } +} + +if (typeof(window.__PLUGIN_UPDATES__) === "undefined") window.__PLUGIN_UPDATES__ = {plugins: {}}; +if (window.__PLUGIN_UPDATES__.interval) clearInterval(window.__PLUGIN_UPDATES__.interval); + +window.__PLUGIN_UPDATES__.interval = setInterval(PluginUpdater.checkAllPlugins.bind(PluginUpdater), HOUR_IN_MILLISECONDS * 2); + +// Transition +if (window.PluginUpdates) { + if (window.PluginUpdates.interval) clearInterval(window.PluginUpdates.interval); + Object.assign(window.__PLUGIN_UPDATES__.plugins, window.PluginUpdates.plugins); + delete window.PluginUpdates; +} + +/***/ }), + +/***/ "./src/modules/pluginutilities.js": +/*!****************************************!*\ + !*** ./src/modules/pluginutilities.js ***! + \****************************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "default": () => (/* binding */ PluginUtilities) +/* harmony export */ }); +/* harmony import */ var _utilities__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./utilities */ "./src/modules/utilities.js"); +/* harmony import */ var _domtools__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./domtools */ "./src/modules/domtools.js"); + + + +/** + * A series of useful functions for BetterDiscord plugins. + * @module PluginUtilities + * @deprecated 1/21/22 Use Alternatives + */ + + + class PluginUtilities { + + /** + * Loads data through BetterDiscord's API. + * @param {string} name - name for the file (usually plugin name) + * @param {string} key - which key the data is saved under + * @param {object} defaultData - default data to populate the object with + * @returns {object} the combined saved and default data + * @deprecated 1/21/22 Use Utilities or BdApi directly + */ + static loadData(name, key, defaultData) {return _utilities__WEBPACK_IMPORTED_MODULE_0__["default"].loadData(name, key, defaultData);} + + /** + * Saves data through BetterDiscord's API. + * @param {string} name - name for the file (usually plugin name) + * @param {string} key - which key the data should be saved under + * @param {object} data - data to save + * @deprecated 1/21/22 Use Utilities or BdApi directly + */ + static saveData(name, key, data) {return _utilities__WEBPACK_IMPORTED_MODULE_0__["default"].saveData(name, key, data);} + + /** + * Loads settings through BetterDiscord's API. + * @param {string} name - name for the file (usually plugin name) + * @param {object} defaultData - default data to populate the object with + * @returns {object} the combined saved and default settings + * @deprecated 1/21/22 Use Utilities or BdApi directly + */ + static loadSettings(name, defaultSettings) {return _utilities__WEBPACK_IMPORTED_MODULE_0__["default"].loadSettings(name, defaultSettings);} + + /** + * Saves settings through BetterDiscord's API. + * @param {string} name - name for the file (usually plugin name) + * @param {object} data - settings to save + * @deprecated 1/21/22 Use Utilities or BdApi directly + */ + static saveSettings(name, data) {return _utilities__WEBPACK_IMPORTED_MODULE_0__["default"].saveSettings(name, data);} + + /** + * Get the full path to the BetterDiscord folder. + * @returns {string} full path to the BetterDiscord folder + * @deprecated 1/21/22 Use BdApi + */ + static getBDFolder(subtarget = "") { + const process = require("process"); + const path = require("path"); + if (process.env.injDir) return path.resolve(process.env.injDir, subtarget); + switch (process.platform) { + case "win32": + return path.resolve(process.env.APPDATA, "BetterDiscord/", subtarget); + case "darwin": + return path.resolve(process.env.HOME, "Library/Application Support/", "BetterDiscord/", subtarget); + default: + return path.resolve(process.env.XDG_CONFIG_HOME ? process.env.XDG_CONFIG_HOME : process.env.HOME + "/.config", "BetterDiscord/", subtarget); + } + } + + /** + * Get the full path to the plugins folder. + * @returns {string} full path to the plugins folder + * @deprecated 1/21/22 Use BdApi + */ + static getPluginsFolder() {return BdApi.Plugins.folder;} + + /** + * Get the full path to the themes folder. + * @returns {string} full path to the themes folder + * @deprecated 1/21/22 Use BdApi + */ + static getThemesFolder() {return BdApi.Themes.folder;} + + /** + * Adds a callback to a set of listeners for onSwitch. + * @param {callable} callback - basic callback to happen on channel switch + * @deprecated 1/21/22 Use onSwitch + */ + static addOnSwitchListener() {} + + /** + * Removes the listener added by {@link InternalUtilities.addOnSwitchListener}. + * @param {callable} callback - callback to remove from the listener list + * @deprecated 1/21/22 Use onSwitch + */ + static removeOnSwitchListener() {} + + /** + * Adds a style to the document. + * @param {string} id - identifier to use as the element id + * @param {string} css - css to add to the document + * @deprecated 1/21/22 Use DOMTools + */ + static addStyle(id, css) {return _domtools__WEBPACK_IMPORTED_MODULE_1__["default"].addStyle(id, css);} + + /** + * Removes a style from the document. + * @param {string} id - original identifier used + * @deprecated 1/21/22 Use DOMTools + */ + static removeStyle(id) {return _domtools__WEBPACK_IMPORTED_MODULE_1__["default"].removeStyle(id);} + + /** + * Adds/requires a remote script to be loaded + * @param {string} id - identifier to use for this script + * @param {string} url - url from which to load the script + * @returns {Promise} promise that resolves when the script is loaded + * @deprecated 1/21/22 Use DOMTools + */ + static addScript(id, url) {return _domtools__WEBPACK_IMPORTED_MODULE_1__["default"].addScript(id, url);} + + /** + * Removes a remote script from the document. + * @param {string} id - original identifier used + * @deprecated 1/21/22 Use DOMTools + */ + static removeScript(id) {return _domtools__WEBPACK_IMPORTED_MODULE_1__["default"].removeScript(id);} +} + + + + +/***/ }), + +/***/ "./src/modules/reactcomponents.js": +/*!****************************************!*\ + !*** ./src/modules/reactcomponents.js ***! + \****************************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "default": () => (/* binding */ ReactComponents) +/* harmony export */ }); +/* harmony import */ var _domtools__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./domtools */ "./src/modules/domtools.js"); +/* harmony import */ var _reacttools__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./reacttools */ "./src/modules/reacttools.js"); +/* harmony import */ var _utilities__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./utilities */ "./src/modules/utilities.js"); +/** + * BetterDiscord React Component Manipulations + * Original concept and some code by samogot - https://github.com/samogot / https://github.com/samogot/betterdiscord-plugins/tree/master/v2/1Lib%20Discord%20Internals + * + * Copyright (c) 2015-present JsSucks - https://github.com/JsSucks + * All rights reserved. + * https://github.com/JsSucks - https://betterdiscord.net + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. +*/ + + + + + +class ReactComponent { + constructor(id, component, selector, filter) { + this.id = id; + this.component = component; + this.selector = selector; + this.filter = filter; + } + + forceUpdateAll() { + if (!this.selector) return; + for (const e of document.querySelectorAll(this.selector)) { + const stateNode = _utilities__WEBPACK_IMPORTED_MODULE_2__["default"].findInTree(_reacttools__WEBPACK_IMPORTED_MODULE_1__["default"].getReactInstance(e), m => m && m.forceUpdate, {walkable: ["return", "stateNode"]}); + if (!stateNode) continue; + stateNode.forceUpdate(); + } + } +} + +/** + * Methods for obtaining and interacting with react components. + * @module ReactComponents + */ +class ReactComponents { + static get components() {return this._components || (this._components = new Map());} + static get unknownComponents() {return this._unknownComponents || (this._unknownComponents = new Set());} + static get listeners() {return this._listeners || (this._listeners = new Map());} + static get nameSetters() {return this._nameSetters || (this._nameSetters = new Set());} + + static get ReactComponent() {return ReactComponent;} + + static push(component, selector, filter) { + if (typeof(component) !== "function") return null; + const {displayName} = component; + if (!displayName) return this.processUnknown(component); + + const have = this.components.get(displayName); + if (have) { + if (!have.selector) have.selector = selector; + if (!have.filter) have.filter = filter; + return component; + } + + const c = new ReactComponent(displayName, component, selector, filter); + this.components.set(c.id, c); + + const listener = this.listeners.get(displayName); + if (listener) { + for (const l of listener.children) l(c); + this.listeners.delete(listener); + } + + return c; + } + + /** + * Finds a component from the components array or by waiting for it to be mounted. + * @param {String} name The component's name + * @param {Object} selector A selector to look for + * @return {Promise} + */ + static async getComponentByName(name, selector) { + return this.getComponent(name, selector, m => m.displayName == name); + } + + /** + * Finds a component from the components array or by waiting for it to be mounted. + * @param {String} name The component's name + * @param {Object} selector A selector to look for + * @param {Function} filter A function to filter components if a single element is rendered by multiple components + * @return {Promise} + */ + static async getComponent(name, selector, filter) { + const have = this.components.get(name); + if (have) { + if (!have.selector) have.selector = selector; + if (!have.filter) have.filter = filter; + return have; + } + + if (selector) { + const callback = () => { + if (this.components.get(name)) { + _domtools__WEBPACK_IMPORTED_MODULE_0__["default"].observer.unsubscribe(observerSubscription); + return; + } + + const elements = document.querySelectorAll(selector); + if (!elements.length) return; + + let component; + for (const element of elements) { + const componentsFound = _reacttools__WEBPACK_IMPORTED_MODULE_1__["default"].getComponents(element); + component = filter ? componentsFound.find(filter) : componentsFound[0]; + if (component) break; + } + + if (!component && filter) return; + + _domtools__WEBPACK_IMPORTED_MODULE_0__["default"].observer.unsubscribe(observerSubscription); + + if (!component) return; + + if (!component.displayName) component.displayName = name; + + this.push(component, selector, filter); + }; + + const observerSubscription = _domtools__WEBPACK_IMPORTED_MODULE_0__["default"].observer.subscribeToQuerySelector(callback, selector, null, true); + setTimeout(callback, 0); + } + + let listener = this.listeners.get(name); + if (!listener) { + listener = { + id: name, + children: [], + filter + }; + this.listeners.set(name, listener); + } + + + return new Promise(resolve => { + listener.children.push(resolve); + }); + } + + static setName(name, filter) { + const have = this.components.get(name); + if (have) return have; + + for (const component of this.unknownComponents.entries()) { + if (!filter(component)) continue; + component.displayName = name; + this.unknownComponents.delete(component); + return this.push(component); + } + return this.nameSetters.add({name, filter}); + } + + static processUnknown(component) { + const have = this.unknownComponents.has(component); + for (const setter of this.nameSetters.entries()) { + if (setter.filter.filter(component)) { + component.displayName = setter.name; + this.nameSetters.delete(setter); + return this.push(component); + } + } + if (have) return have; + this.unknownComponents.add(component); + return component; + } + + static *recursiveComponents(internalInstance = _reacttools__WEBPACK_IMPORTED_MODULE_1__["default"].rootInstance) { + if (internalInstance.stateNode) yield internalInstance.stateNode; + if (internalInstance.sibling) yield* this.recursiveComponents(internalInstance.sibling); + if (internalInstance.child) yield* this.recursiveComponents(internalInstance.child); + } +} + + +/***/ }), + +/***/ "./src/modules/reacttools.js": +/*!***********************************!*\ + !*** ./src/modules/reacttools.js ***! + \***********************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "default": () => (/* binding */ ReactTools) +/* harmony export */ }); +/* harmony import */ var _domtools__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./domtools */ "./src/modules/domtools.js"); +/* harmony import */ var _discordmodules__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./discordmodules */ "./src/modules/discordmodules.js"); +/* harmony import */ var _utilities__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./utilities */ "./src/modules/utilities.js"); +/** + * Helpful utilities for dealing with getting react information from DOM objects. + * @module ReactTools + */ + + + + + +class ReactTools { + + static get rootInstance() {return document.getElementById("app-mount")._reactRootContainer._internalRoot.current;} + + /** + * Grabs the react internal instance of a specific node. + * @param {(HTMLElement|jQuery)} node - node to obtain react instance of + * @return {object} the internal react instance + */ + static getReactInstance(node) { + const domNode = _domtools__WEBPACK_IMPORTED_MODULE_0__["default"].resolveElement(node); + if (!(domNode instanceof Element)) return undefined; + return domNode[Object.keys(domNode).find((key) => key.startsWith("__reactInternalInstance") || key.startsWith("__reactFiber"))]; + } + + /** + * Grabs a value from the react internal instance. Allows you to grab + * long depth values safely without accessing no longer valid properties. + * @param {(HTMLElement|jQuery)} node - node to obtain react instance of + * @param {string} path - path to the requested value + * @return {(*|undefined)} the value requested or undefined if not found. + */ + static getReactProperty(node, path) { + return _utilities__WEBPACK_IMPORTED_MODULE_2__["default"].getNestedProp(this.getReactInstance(node), path); + } + + /** + * Grabs a value from the react internal instance. Allows you to grab + * long depth values safely without accessing no longer valid properties. + * @param {(HTMLElement|jQuery)} node - node to obtain react instance of + * @param {object} options - options for the search + * @param {array} [options.include] - list of items to include from the search + * @param {array} [options.exclude=["Popout", "Tooltip", "Scroller", "BackgroundFlash"]] - list of items to exclude from the search + * @param {callable} [options.filter=_=>_] - filter to check the current instance with (should return a boolean) + * @return {(*|null)} the owner instance or undefined if not found. + */ + static getOwnerInstance(node, {include, exclude = ["Popout", "Tooltip", "Scroller", "BackgroundFlash"], filter = _ => _} = {}) { + if (node === undefined) return undefined; + const excluding = include === undefined; + const nameFilter = excluding ? exclude : include; + function getDisplayName(owner) { + const type = owner.type; + if (!type) return null; + return type.displayName || type.name || null; + } + function classFilter(owner) { + const name = getDisplayName(owner); + return (name !== null && !!(nameFilter.includes(name) ^ excluding)); + } + + let curr = this.getReactInstance(node); + for (curr = curr && curr.return; !_utilities__WEBPACK_IMPORTED_MODULE_2__["default"].isNil(curr); curr = curr.return) { + if (_utilities__WEBPACK_IMPORTED_MODULE_2__["default"].isNil(curr)) continue; + const owner = curr.stateNode; + if (!_utilities__WEBPACK_IMPORTED_MODULE_2__["default"].isNil(owner) && !(owner instanceof HTMLElement) && classFilter(curr) && filter(owner)) return owner; + } + + return null; + } + + /** + * Grabs the react internal state node trees of a specific node. + * @param {(HTMLElement|jQuery)} node - node to obtain state nodes of + * @return {Array} list of found state nodes + */ + static getStateNodes(node) { + const instance = this.getReactInstance(node); + const stateNodes = []; + let lastInstance = instance; + while (lastInstance && lastInstance.return) { + if (lastInstance.return.stateNode instanceof HTMLElement) break; + if (lastInstance.return.stateNode) stateNodes.push(lastInstance.return.stateNode); + lastInstance = lastInstance.return; + } + return stateNodes; + } + + /** + * Grabs the react internal component tree of a specific node. + * @param {(HTMLElement|jQuery)} node - node to obtain react components of + * @return {Array} list of found react components + */ + static getComponents(node) { + const instance = this.getReactInstance(node); + const components = []; + let lastInstance = instance; + while (lastInstance && lastInstance.return) { + if (typeof lastInstance.return.type === "string") break; + if (lastInstance.return.type) components.push(lastInstance.return.type); + lastInstance = lastInstance.return; + } + return components; + } + + /** + * Creates and renders a react element that wraps dom elements. + * @param {(HTMLElement|Array)} element - element or array of elements to wrap into a react element + * @returns {object} - rendered react element + */ + static createWrappedElement(element) { + if (Array.isArray(element)) element = _domtools__WEBPACK_IMPORTED_MODULE_0__["default"].wrap(element); + return _discordmodules__WEBPACK_IMPORTED_MODULE_1__["default"].React.createElement(this.wrapElement(element)); + } + + /** + * Creates an unrendered react component that wraps dom elements. + * @param {(HTMLElement|Array)} element - element or array of elements to wrap into a react component + * @returns {object} - unrendered react component + */ + static wrapElement(element) { + if (Array.isArray(element)) element = _domtools__WEBPACK_IMPORTED_MODULE_0__["default"].wrap(element); + return class ReactWrapper extends _discordmodules__WEBPACK_IMPORTED_MODULE_1__["default"].React.Component { + constructor(props) { + super(props); + this.element = element; + } + + componentDidMount() {this.refs.element.appendChild(this.element);} + render() {return _discordmodules__WEBPACK_IMPORTED_MODULE_1__["default"].React.createElement("div", {className: "react-wrapper", ref: "element"});} + }; + } +} + +/***/ }), + +/***/ "./src/modules/utilities.js": +/*!**********************************!*\ + !*** ./src/modules/utilities.js ***! + \**********************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "default": () => (/* binding */ Utilities) +/* harmony export */ }); +/* harmony import */ var _logger__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./logger */ "./src/modules/logger.js"); +/** + * Random set of utilities that didn't fit elsewhere. + * @module Utilities + */ + + + +class Utilities { + + /** + * Stably sorts arrays since `.sort()` has issues. + * @param {Array} list - array to sort + * @param {function} comparator - comparator to sort by + */ + static stableSort(list, comparator) { + const entries = Array(list.length); + + // wrap values with initial indices + for (let index = 0; index < list.length; index++) { + entries[index] = [index, list[index]]; + } + + // sort with fallback based on initial indices + entries.sort(function (a, b) { + const comparison = Number(this(a[1], b[1])); + return comparison || a[0] - b[0]; + }.bind(comparator)); + + // re-map original array to stable sorted values + for (let index = 0; index < list.length; index++) { + list[index] = entries[index][1]; + } + } + + /** + * Generates an automatically memoizing version of an object. + * @param {Object} object - object to memoize + * @returns {Proxy} the proxy to the object that memoizes properties + */ + static memoizeObject(object) { + const proxy = new Proxy(object, { + get: function(obj, mod) { + if (!obj.hasOwnProperty(mod)) return undefined; + if (Object.getOwnPropertyDescriptor(obj, mod).get) { + const value = obj[mod]; + delete obj[mod]; + obj[mod] = value; + } + return obj[mod]; + }, + set: function(obj, mod, value) { + if (obj.hasOwnProperty(mod)) return _logger__WEBPACK_IMPORTED_MODULE_0__["default"].err("MemoizedObject", "Trying to overwrite existing property"); + obj[mod] = value; + return obj[mod]; + } + }); + + Object.defineProperty(proxy, "hasOwnProperty", {value: function(prop) { + return this[prop] !== undefined; + }}); + + return proxy; + } + + /** + * Wraps the method in a `try..catch` block. + * @param {callable} method - method to wrap + * @param {string} description - description of method + * @returns {callable} wrapped version of method + */ + static suppressErrors(method, description) { + return (...params) => { + try {return method(...params);} + catch (e) {_logger__WEBPACK_IMPORTED_MODULE_0__["default"].err("Suppression", "Error occurred in " + description, e);} + }; + } + + /** + * This only exists because Samo relied on lodash being there... fuck lodash. + * @param {*} anything - whatever you want + */ + static isNil(anything) { + return anything === null; + } + + /** + * Format template strings with placeholders (`${placeholder}`) into full strings. + * Quick example: `Utilities.formatString("Hello, ${user}", {user: "Zerebos"})` + * would return "Hello, Zerebos". + * @param {string} string - string to format + * @param {object} values - object literal of placeholders to replacements + * @returns {string} the properly formatted string + */ + static formatTString(string, values) { + for (const val in values) { + let replacement = values[val]; + if (Array.isArray(replacement)) replacement = JSON.stringify(replacement); + if (typeof(replacement) === "object" && replacement !== null) replacement = replacement.toString(); + string = string.replace(new RegExp(`\\$\\{${val}\\}`, "g"), replacement); + } + return string; + } + + /** + * Format strings with placeholders (`{{placeholder}}`) into full strings. + * Quick example: `Utilities.formatString("Hello, {{user}}", {user: "Zerebos"})` + * would return "Hello, Zerebos". + * @param {string} string - string to format + * @param {object} values - object literal of placeholders to replacements + * @returns {string} the properly formatted string + */ + static formatString(string, values) { + for (const val in values) { + let replacement = values[val]; + if (Array.isArray(replacement)) replacement = JSON.stringify(replacement); + if (typeof(replacement) === "object" && replacement !== null) replacement = replacement.toString(); + string = string.replace(new RegExp(`{{${val}}}`, "g"), replacement); + } + return string; + } + + /** + * Finds a value, subobject, or array from a tree that matches a specific filter. Great for patching render functions. + * @param {object} tree React tree to look through. Can be a rendered object or an internal instance. + * @param {callable} searchFilter Filter function to check subobjects against. + */ + static findInReactTree(tree, searchFilter) { + return this.findInTree(tree, searchFilter, {walkable: ["props", "children", "child", "sibling"]}); + } + + /** + * Finds a value, subobject, or array from a tree that matches a specific filter. + * @param {object} tree Tree that should be walked + * @param {callable} searchFilter Filter to check against each object and subobject + * @param {object} options Additional options to customize the search + * @param {Array|null} [options.walkable=null] Array of strings to use as keys that are allowed to be walked on. Null value indicates all keys are walkable + * @param {Array} [options.ignore=[]] Array of strings to use as keys to exclude from the search, most helpful when `walkable = null`. + */ + static findInTree(tree, searchFilter, {walkable = null, ignore = []} = {}) { + if (typeof searchFilter === "string") { + if (tree.hasOwnProperty(searchFilter)) return tree[searchFilter]; + } + else if (searchFilter(tree)) { + return tree; + } + + if (typeof tree !== "object" || tree == null) return undefined; + + let tempReturn; + if (Array.isArray(tree)) { + for (const value of tree) { + tempReturn = this.findInTree(value, searchFilter, {walkable, ignore}); + if (typeof tempReturn != "undefined") return tempReturn; + } + } + else { + const toWalk = walkable == null ? Object.keys(tree) : walkable; + for (const key of toWalk) { + if (!tree.hasOwnProperty(key) || ignore.includes(key)) continue; + tempReturn = this.findInTree(tree[key], searchFilter, {walkable, ignore}); + if (typeof tempReturn != "undefined") return tempReturn; + } + } + return tempReturn; + } + + /** + * Gets a nested property (if it exists) safely. Path should be something like `prop.prop2.prop3`. + * Numbers can be used for arrays as well like `prop.prop2.array.0.id`. + * @param {Object} obj - object to get nested property of + * @param {string} path - representation of the property to obtain + */ + static getNestedProp(obj, path) { + return path.split(".").reduce(function(ob, prop) { + return ob && ob[prop]; + }, obj); + } + + /** + * Builds a classname string from any number of arguments. This includes arrays and objects. + * When given an array all values from the array are added to the list. + * When given an object they keys are added as the classnames if the value is truthy. + * Copyright (c) 2018 Jed Watson https://github.com/JedWatson/classnames MIT License + * @param {...Any} argument - anything that should be used to add classnames. + */ + static className() { + const classes = []; + const hasOwn = {}.hasOwnProperty; + + for (let i = 0; i < arguments.length; i++) { + const arg = arguments[i]; + if (!arg) continue; + + const argType = typeof arg; + + if (argType === "string" || argType === "number") { + classes.push(arg); + } + else if (Array.isArray(arg) && arg.length) { + const inner = this.classNames.apply(null, arg); + if (inner) { + classes.push(inner); + } + } + else if (argType === "object") { + for (const key in arg) { + if (hasOwn.call(arg, key) && arg[key]) { + classes.push(key); + } + } + } + } + + return classes.join(" "); + } + + /** + * Safely adds to the prototype of an existing object by checking if the + * property exists on the prototype. + * @param {object} object - Object whose prototype to extend + * @param {string} prop - Name of the prototype property to add + * @param {callable} func - Function to run + */ + static addToPrototype(object, prop, func) { + if (!object.prototype) return; + if (object.prototype[prop]) return; + return object.prototype[prop] = func; + } + + /** + * Deep extends an object with a set of other objects. Objects later in the list + * of `extenders` have priority, that is to say if one sets a key to be a primitive, + * it will be overwritten with the next one with the same key. If it is an object, + * and the keys match, the object is extended. This happens recursively. + * @param {object} extendee - Object to be extended + * @param {...object} extenders - Objects to extend with + * @returns {object} - A reference to `extendee` + */ + static extend(extendee, ...extenders) { + for (let i = 0; i < extenders.length; i++) { + for (const key in extenders[i]) { + if (extenders[i].hasOwnProperty(key)) { + if (Array.isArray(extendee[key]) && Array.isArray(extenders[i][key])) this.extend(extendee[key], extenders[i][key]); + else if (this.isNil(extenders[i][key])) extendee[key] = extenders[i][key]; + else if (typeof extendee[key] === "object" && typeof extenders[i][key] === "object") this.extend(extendee[key], extenders[i][key]); + else if (Array.isArray(extenders[i][key])) extendee[key] = [], this.extend(extendee[key], extenders[i][key]); // eslint-disable-line no-sequences + else if (typeof extenders[i][key] === "object") extendee[key] = {}, this.extend(extendee[key], extenders[i][key]); // eslint-disable-line no-sequences + else extendee[key] = extenders[i][key]; + } + } + } + return extendee; + } + + /* Code below comes from our work on BDv2: + * https://github.com/JsSucks/BetterDiscordApp/blob/master/common/modules/utils.js + */ + + /** + * Clones an object and all it's properties. + * @param {Any} value The value to clone + * @return {Any} The cloned value + */ + static deepclone(value) { + if (this.isNil(value)) return value; + if (typeof value === "object") { + if (Array.isArray(value)) return value.map(i => this.deepclone(i)); + + const clone = Object.assign({}, value); + + for (const key in clone) { + clone[key] = this.deepclone(clone[key]); + } + + return clone; + } + + return value; + } + + /** + * Freezes an object and all it's properties. + * @param {Any} object The object to freeze + * @param {Function} exclude A function to filter object that shouldn't be frozen + */ + static deepfreeze(object, exclude) { + if (exclude && exclude(object)) return; + + if (typeof object === "object" && object !== null) { + const properties = Object.getOwnPropertyNames(object); + + for (const property of properties) { + this.deepfreeze(object[property], exclude); + } + + Object.freeze(object); + } + + return object; + } + + /** + * Removes an item from an array. This differs from Array.prototype.filter as it mutates the original array instead of creating a new one. + * @param {Array} array The array to filter + * @param {Any} item The item to remove from the array + * @return {Array} + */ + static removeFromArray(array, item, filter) { + let index; + while ((index = filter ? array.findIndex(item) : array.indexOf(item)) > -1) array.splice(index, 1); + return array; + } + + /** + * Returns a function, that, as long as it continues to be invoked, will not + * be triggered. The function will be called after it stops being called for + * N milliseconds. + * + * Adapted from the version by David Walsh (https://davidwalsh.name/javascript-debounce-function) + * + * @param {function} executor + * @param {number} delay + */ + static debounce(executor, delay) { + let timeout; + return function(...args) { + const callback = () => { + timeout = null; + Reflect.apply(executor, null, args); + }; + clearTimeout(timeout); + timeout = setTimeout(callback, delay); + }; + } + + /** + * Loads data through BetterDiscord's API. + * @param {string} name - name for the file (usually plugin name) + * @param {string} key - which key the data is saved under + * @param {object} defaultData - default data to populate the object with + * @returns {object} the combined saved and default data + */ + static loadData(name, key, defaultData = {}) { + const defaults = this.deepclone(defaultData); + try { + const storedData = BdApi.getData(name, key); + if (typeof(defaults) === "object") return this.extend(defaults, storedData); + return this.isNil(storedData) || typeof(storedData) === "undefined" ? defaults : storedData; + } + catch (err) { + _logger__WEBPACK_IMPORTED_MODULE_0__["default"].err(name, "Unable to load data: ", err); + } + return defaults; + } + + /** + * Saves data through BetterDiscord's API. + * @param {string} name - name for the file (usually plugin name) + * @param {string} key - which key the data should be saved under + * @param {object} data - data to save + */ + static saveData(name, key, data) { + try {BdApi.setData(name, key, data);} + catch (err) {_logger__WEBPACK_IMPORTED_MODULE_0__["default"].err(name, "Unable to save data: ", err);} + } + + /** + * Loads settings through BetterDiscord's API. + * @param {string} name - name for the file (usually plugin name) + * @param {object} defaultData - default data to populate the object with + * @returns {object} the combined saved and default settings + */ + static loadSettings(name, defaultSettings) { + return this.loadData(name, "settings", defaultSettings); + } + + /** + * Saves settings through BetterDiscord's API. + * @param {string} name - name for the file (usually plugin name) + * @param {object} data - settings to save + */ + static saveSettings(name, data) { + this.saveData(name, "settings", data); + } + +} + +/***/ }), + +/***/ "./src/modules/webpackmodules.js": +/*!***************************************!*\ + !*** ./src/modules/webpackmodules.js ***! + \***************************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "Filters": () => (/* binding */ Filters), +/* harmony export */ "default": () => (/* binding */ WebpackModules) +/* harmony export */ }); +/* harmony import */ var _discordmodules__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./discordmodules */ "./src/modules/discordmodules.js"); +/* harmony import */ var _logger__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./logger */ "./src/modules/logger.js"); +/** + * Random set of utilities that didn't fit elsewhere. + * @module WebpackModules + */ + + + + /** + * Checks if a given module matches a set of parameters. + * @callback module:WebpackModules.Filters~filter + * @param {*} module - module to check + * @returns {boolean} - True if the module matches the filter, false otherwise + */ + +/** + * Filters for use with {@link module:WebpackModules} but may prove useful elsewhere. + */ +class Filters { + /** + * Generates a {@link module:WebpackModules.Filters~filter} that filters by a set of properties. + * @param {Array} props - Array of property names + * @param {module:WebpackModules.Filters~filter} filter - Additional filter + * @returns {module:WebpackModules.Filters~filter} - A filter that checks for a set of properties + */ + static byProperties(props, filter = m => m) { + return module => { + const component = filter(module); + if (!component) return false; + for (let p = 0; p < props.length; p++) { + if (module[props[p]] === undefined) return false; + } + return true; + }; + } + + /** + * Generates a {@link module:WebpackModules.Filters~filter} that filters by a set of properties on the object's prototype. + * @param {Array} fields - Array of property names + * @param {module:WebpackModules.Filters~filter} filter - Additional filter + * @returns {module:WebpackModules.Filters~filter} - A filter that checks for a set of properties on the object's prototype + */ + static byPrototypeFields(fields, filter = m => m) { + return module => { + const component = filter(module); + if (!component) return false; + if (!component.prototype) return false; + for (let f = 0; f < fields.length; f++) { + if (module.prototype[fields[f]] === undefined) return false; + } + return true; + }; + } + + /** + * Generates a {@link module:WebpackModules.Filters~filter} that filters by a regex. + * @param {RegExp} search - A RegExp to check on the module + * @param {module:WebpackModules.Filters~filter} filter - Additional filter + * @returns {module:WebpackModules.Filters~filter} - A filter that checks for a set of properties + */ + static byCode(search, filter = m => m) { + return module => { + const method = filter(module); + if (!method) return false; + let methodString = ""; + try {methodString = method.toString([]);} + catch (err) {methodString = method.toString();} + return methodString.search(search) !== -1; + }; + } + + /** + * Generates a {@link module:WebpackModules.Filters~filter} that filters by strings. + * @param {...String} search - A RegExp to check on the module + * @returns {module:WebpackModules.Filters~filter} - A filter that checks for a set of strings + */ + static byString(...strings) { + return module => { + let moduleString = ""; + try {moduleString = module.toString([]);} + catch (err) {moduleString = module.toString();} + for (const s of strings) { + if (!moduleString.includes(s)) return false; + } + return true; + }; + } + + /** + * Generates a {@link module:WebpackModules.Filters~filter} that filters by a set of properties. + * @param {string} name - Name the module should have + * @param {module:WebpackModules.Filters~filter} filter - Additional filter + * @returns {module:WebpackModules.Filters~filter} - A filter that checks for a set of properties + */ + static byDisplayName(name) { + return module => { + return module && module.displayName === name; + }; + } + + /** + * Generates a combined {@link module:WebpackModules.Filters~filter} from a list of filters. + * @param {...module:WebpackModules.Filters~filter} filters - A list of filters + * @returns {module:WebpackModules.Filters~filter} - Combinatory filter of all arguments + */ + static combine(...filters) { + return module => { + return filters.every(filter => filter(module)); + }; + } +} + +class WebpackModules { + + static find(filter, first = true) {return this.getModule(filter, first);} + static findAll(filter) {return this.getModule(filter, false);} + static findByUniqueProperties(props, first = true) {return first ? this.getByProps(...props) : this.getAllByProps(...props);} + static findByDisplayName(name) {return this.getByDisplayName(name);} + + /** + * Finds a module using a filter function. + * @param {Function} filter A function to use to filter modules + * @param {Boolean|object} first Whether to return only the first matching module or options object matching BD's options + * @return {Any} + */ + static getModule(filter, first = true) { + const options = typeof(first) === "object" ? first : {first}; + return BdApi.Webpack.getModule(filter, options); + } + + static getIndex() { + return null; + } + + static getIndexByModule() { + return null; + } + + /** + * Finds all modules matching a filter function. + * @param {Function} filter A function to use to filter modules + */ + static getModules(filter) {return this.getModule(filter, false);} + + /** + * Finds a module by its name. + * @param {String} name The name of the module + * @param {Function} fallback A function to use to filter modules if not finding a known module + * @return {Any} + */ + static getModuleByName(name, fallback) { + if (_discordmodules__WEBPACK_IMPORTED_MODULE_0__["default"].hasOwnProperty(name)) return _discordmodules__WEBPACK_IMPORTED_MODULE_0__["default"][name]; + if (!fallback) return undefined; + const module = this.getModule(fallback); + return module ? _discordmodules__WEBPACK_IMPORTED_MODULE_0__["default"][name] = module : undefined; + } + + /** + * Finds a module by its display name. + * @param {String} name The display name of the module + * @return {Any} + */ + static getByDisplayName(name) { + return this.getModule(Filters.byDisplayName(name)); + } + + /** + * Finds a module using its code. + * @param {RegEx} regex A regular expression to use to filter modules + * @param {Boolean} first Whether to return the only the first matching module + * @return {Any} + */ + static getByRegex(regex, first = true) { + return this.getModule(Filters.byCode(regex), first); + } + + /** + * Finds a single module using properties on its prototype. + * @param {...string} prototypes Properties to use to filter modules + * @return {Any} + */ + static getByPrototypes(...prototypes) { + return this.getModule(Filters.byPrototypeFields(prototypes), true); + } + + /** + * Finds all modules with a set of properties of its prototype. + * @param {...string} prototypes Properties to use to filter modules + * @return {Any} + */ + static getAllByPrototypes(...prototypes) { + return this.getModule(Filters.byPrototypeFields(prototypes), false); + } + + /** + * Finds a single module using its own properties. + * @param {...string} props Properties to use to filter modules + * @return {Any} + */ + static getByProps(...props) { + return this.getModule(Filters.byProperties(props), true); + } + + /** + * Finds all modules with a set of properties. + * @param {...string} props Properties to use to filter modules + * @return {Any} + */ + static getAllByProps(...props) { + return this.getModule(Filters.byProperties(props), false); + } + + /** + * Finds a single module using a set of strings. + * @param {...String} props Strings to use to filter modules + * @return {Any} + */ + static getByString(...strings) { + return this.getModule(Filters.byString(...strings), true); + } + + /** + * Finds all modules with a set of strings. + * @param {...String} strings Strings to use to filter modules + * @return {Any} + */ + static getAllByString(...strings) { + return this.getModule(Filters.byString(...strings), false); + } + + /** + * Gets a specific module by index of the webpack require cache. + * Best used in combination with getIndex in order to patch a + * specific function. + * + * Note: this gives the **raw** module, meaning the actual module + * is in returnValue.exports. This is done in order to be able + * to patch modules which export a single function directly. + * @param {Number} index Index into the webpack require cache + * @return {Any} + */ + static getByIndex(index) { + return WebpackModules.require.c[index].exports; + } + + /** + * Discord's __webpack_require__ function. + */ + static get require() { + if (this._require) return this._require; + const __nested_webpack_require_9242__ = window.webpackChunkdiscord_app.push([[Symbol()], {}, r=> r]); + window.webpackChunkdiscord_app.pop(); + return this._require = __nested_webpack_require_9242__; + } + + /** + * Returns all loaded modules. + * @return {Array} + */ + static getAllModules() { + return this.require.c; + } + + + + // Webpack Chunk Observing + static get chunkName() {return "webpackChunkdiscord_app";} + + static initialize() { + this.handlePush = this.handlePush.bind(this); + this.listeners = new Set(); + + this.__ORIGINAL_PUSH__ = window[this.chunkName].push; + Object.defineProperty(window[this.chunkName], "push", { + configurable: true, + get: () => this.handlePush, + set: (newPush) => { + this.__ORIGINAL_PUSH__ = newPush; + + Object.defineProperty(window[this.chunkName], "push", { + value: this.handlePush, + configurable: true, + writable: true + }); + } + }); + } + + /** + * Adds a listener for when discord loaded a chunk. Useful for subscribing to lazy loaded modules. + * @param {Function} listener - Function to subscribe for chunks + * @returns {Function} A cancelling function + */ + static addListener(listener) { + this.listeners.add(listener); + return this.removeListener.bind(this, listener); + } + + /** + * Removes a listener for when discord loaded a chunk. + * @param {Function} listener + * @returns {boolean} + */ + static removeListener(listener) {return this.listeners.delete(listener);} + + static handlePush(chunk) { + const [, modules] = chunk; + + for (const moduleId in modules) { + const originalModule = modules[moduleId]; + + modules[moduleId] = (module, exports, require) => { + try { + Reflect.apply(originalModule, null, [module, exports, require]); + + const listeners = [...this.listeners]; + for (let i = 0; i < listeners.length; i++) { + try {listeners[i](exports, originalModule, moduleId);} + catch (error) { + _logger__WEBPACK_IMPORTED_MODULE_1__["default"].err("WebpackModules", "Could not fire callback listener:", error); + } + } + } + catch (error) { + _logger__WEBPACK_IMPORTED_MODULE_1__["default"].stacktrace("WebpackModules", "Error patching chunked module push", error); + } + }; + + Object.assign(modules[moduleId], originalModule, { + toString: () => originalModule.toString() + }); + } + + return Reflect.apply(this.__ORIGINAL_PUSH__, window[this.chunkName], [chunk]); + } + +} + +WebpackModules.initialize(); + +/***/ }), + +/***/ "./src/structs/dom/classname.js": +/*!**************************************!*\ + !*** ./src/structs/dom/classname.js ***! + \**************************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__) +/* harmony export */ }); +/* harmony import */ var _selector__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./selector */ "./src/structs/dom/selector.js"); + + +/** + * Representation of a Class Name + * @memberof module:DOMTools + **/ +class ClassName { + /** + * + * @param {string} name - name of the class to represent + */ + constructor(name) { + this.value = name; + } + + /** + * Concatenates new class names to the current one using spaces. + * @param {string} classNames - list of class names to add to this class name + * @returns {ClassName} returns self to allow chaining + */ + add(...classNames) { + for (let i = 0; i < classNames.length; i++) this.value += " " + classNames[i]; + return this; + } + + /** + * Returns the raw class name, this is how native function get the value. + * @returns {string} raw class name. + */ + toString() { + return this.value; + } + + /** + * Returns the raw class name, this is how native function get the value. + * @returns {string} raw class name. + */ + valueOf() { + return this.value; + } + + /** + * Returns the classname represented as {@link module:DOMTools.Selector}. + * @returns {Selector} selector representation of this class name. + */ + get selector() { + return new _selector__WEBPACK_IMPORTED_MODULE_0__["default"](this.value); + } + + get single() { + return this.value.split(" ")[0]; + } + + get first() { + return this.value.split(" ")[0]; + } +} + +/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (ClassName); + +/***/ }), + +/***/ "./src/structs/dom/observer.js": +/*!*************************************!*\ + !*** ./src/structs/dom/observer.js ***! + \*************************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__) +/* harmony export */ }); +/* harmony import */ var modules__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! modules */ "./src/modules/modules.js"); +/** + * BetterDiscord Client DOM Module + * Copyright (c) 2015-present JsSucks - https://github.com/JsSucks + * All rights reserved. + * https://betterdiscord.net + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. +*/ + + + +/* eslint-disable operator-linebreak */ + +/** + * Representation of a MutationObserver but with helpful utilities. + * @memberof module:DOMTools + **/ +class DOMObserver { + constructor(root, options) { + this.observe = this.observe.bind(this); + this.subscribe = this.subscribe.bind(this); + this.observerCallback = this.observerCallback.bind(this); + + this.active = false; + this.root = root || document.getElementById("app-mount"); + this.options = options || {attributes: true, childList: true, subtree: true}; + + this.observer = new MutationObserver(this.observerCallback); + this.observe(); + } + + observerCallback(mutations) { + for (const sub of Array.from(this.subscriptions)) { + try { + const filteredMutations = sub.filter ? mutations.filter(sub.filter) : mutations; + + if (sub.group) { + if (!filteredMutations.length) continue; + sub.callback.call(sub.bind || sub, filteredMutations); + } + else { + for (const mutation of filteredMutations) sub.callback.call(sub.bind || sub, mutation); + } + } + catch (err) { + modules__WEBPACK_IMPORTED_MODULE_0__.Logger.stacktrace("DOMObserver", "Error in observer callback", err); + } + } + } + + /** + * Starts observing the element. This will be called when attaching a callback. + * You don't need to call this manually. + */ + observe() { + if (this.active) return; + this.observer.observe(this.root, this.options); + this.active = true; + } + + /** + * Disconnects this observer. This stops callbacks being called, but does not unbind them. + * You probably want to use observer.unsubscribeAll instead. + */ + disconnect() { + if (!this.active) return; + this.observer.disconnect(); + this.active = false; + } + + reconnect() { + if (this.active) { + this.disconnect(); + this.observe(); + } + } + + get root() {return this._root;} + set root(root) {this._root = root; this.reconnect();} + + get options() {return this._options;} + set options(options) {this._options = options; this.reconnect();} + + get subscriptions() { + return this._subscriptions || (this._subscriptions = []); + } + + /** + * Subscribes to mutations. + * @param {Function} callback A function to call when on a mutation + * @param {Function} filter A function to call to filter mutations + * @param {Any} bind Something to bind the callback to + * @param {Boolean} group Whether to call the callback with an array of mutations instead of a single mutation + * @return {Object} + */ + subscribe(callback, filter, bind, group) { + const subscription = {callback, filter, bind, group}; + this.subscriptions.push(subscription); + this.observe(); + return subscription; + } + + /** + * Removes a subscription and disconnect if there are none left. + * @param {Object} subscription A subscription object returned by observer.subscribe + */ + unsubscribe(subscription) { + if (!this.subscriptions.includes(subscription)) subscription = this.subscriptions.find(s => s.callback === subscription); + modules__WEBPACK_IMPORTED_MODULE_0__.Utilities.removeFromArray(this.subscriptions, subscription); + if (!this.subscriptions.length) this.disconnect(); + } + + unsubscribeAll() { + this.subscriptions.splice(0, this.subscriptions.length); + this.disconnect(); + } + + /** + * Subscribes to mutations that affect an element matching a selector. + * @param {Function} callback A function to call when on a mutation + * @param {Function} filter A function to call to filter mutations + * @param {Any} bind Something to bind the callback to + * @param {Boolean} group Whether to call the callback with an array of mutations instead of a single mutation + * @return {Object} + */ + subscribeToQuerySelector(callback, selector, bind, group) { + return this.subscribe(callback, mutation => { + return mutation.target.matches(selector) // If the target matches the selector + || Array.from(mutation.addedNodes).concat(Array.from(mutation.removedNodes)) // Or if either an added or removed node + .find(n => n instanceof Element && (n.matches(selector) || n.querySelector(selector))); // match or contain an element matching the selector + }, bind, group); + } +} + +/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (DOMObserver); + +/***/ }), + +/***/ "./src/structs/dom/selector.js": +/*!*************************************!*\ + !*** ./src/structs/dom/selector.js ***! + \*************************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__) +/* harmony export */ }); +/** + * Representation of a Selector + * @memberof module:DOMTools + **/ +class Selector { + /** + * + * @param {string} classname - class to create selector for + */ + constructor(className) { + this.value = " ." + className.split(" ").join("."); + } + + /** + * Returns the raw selector, this is how native function get the value. + * @returns {string} raw selector. + */ + toString() { + return this.value; + } + + /** + * Returns the raw selector, this is how native function get the value. + * @returns {string} raw selector. + */ + valueOf() { + return this.value; + } + + selector(symbol, other) { + this.value = `${this.toString()} ${symbol} ${other.toString()}`; + return this; + } + + /** + * Adds another selector as a direct child `>` to this one. + * @param {string|DOMTools.Selector} other - Selector to add as child + * @returns {DOMTools.Selector} returns self to allow chaining + */ + child(other) { + return this.selector(">", other); + } + + /** + * Adds another selector as a adjacent sibling `+` to this one. + * @param {string|DOMTools.Selector} other - Selector to add as adjacent sibling + * @returns {DOMTools.Selector} returns self to allow chaining + */ + adjacent(other) { + return this.selector("+", other); + } + + /** + * Adds another selector as a general sibling `~` to this one. + * @param {string|DOMTools.Selector} other - Selector to add as sibling + * @returns {DOMTools.Selector} returns self to allow chaining + */ + sibling(other) { + return this.selector("~", other); + } + + /** + * Adds another selector as a descendent `(space)` to this one. + * @param {string|DOMTools.Selector} other - Selector to add as descendent + * @returns {DOMTools.Selector} returns self to allow chaining + */ + descend(other) { + return this.selector(" ", other); + } + + /** + * Adds another selector to this one via `,`. + * @param {string|DOMTools.Selector} other - Selector to add + * @returns {DOMTools.Selector} returns self to allow chaining + */ + and(other) { + return this.selector(",", other); + } +} + +/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (Selector); + +/***/ }), + +/***/ "./src/structs/listenable.js": +/*!***********************************!*\ + !*** ./src/structs/listenable.js ***! + \***********************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__) +/* harmony export */ }); +/** + * Acts as an interface for anything that should be listenable. + */ +class Listenable { + + constructor() { + this.listeners = []; + } + + /** + * Adds a listener to the current object. + * @param {callable} callback - callback for when the event occurs + * @returns {callable} - a way to cancel the listener without needing to call `removeListener` + */ + addListener(callback) { + if (typeof(callback) !== "function") return; + this.listeners.push(callback); + return () => { + this.listeners.splice(this.listeners.indexOf(callback), 1); + }; + } + + /** + * Removes a listener from the current object. + * @param {callable} callback - callback that was originally registered + */ + removeListener(callback) { + if (typeof(callback) !== "function") return; + this.listeners.splice(this.listeners.indexOf(callback), 1); + } + + /** + * Alerts the listeners that an event occurred. Data passed is optional + * @param {*} [...data] - Any data desired to be passed to listeners + */ + alertListeners(...data) { + for (let l = 0; l < this.listeners.length; l++) this.listeners[l](...data); + } +} + +/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (Listenable); + +/***/ }), + +/***/ "./src/structs/plugin.js": +/*!*******************************!*\ + !*** ./src/structs/plugin.js ***! + \*******************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "default": () => (/* binding */ Plugin), +/* harmony export */ "wrapPluginBase": () => (/* binding */ wrapPluginBase) +/* harmony export */ }); +/* harmony import */ var _modules_pluginupdater__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../modules/pluginupdater */ "./src/modules/pluginupdater.js"); +/* harmony import */ var _modules_logger__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../modules/logger */ "./src/modules/logger.js"); +/* harmony import */ var _modules_reacttools__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../modules/reacttools */ "./src/modules/reacttools.js"); +/* harmony import */ var _ui_modals__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../ui/modals */ "./src/ui/modals.js"); +/* harmony import */ var _modules_utilities__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../modules/utilities */ "./src/modules/utilities.js"); +/* harmony import */ var _modules_discordmodules__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../modules/discordmodules */ "./src/modules/discordmodules.js"); +/* harmony import */ var _ui_settings__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ../ui/settings */ "./src/ui/settings/index.js"); + + + + + + + + +class Plugin { + + get name() {return this._config.name ?? this._config.info.name.replace(" ", "");} + get description() {return this._config.description ?? this._config.info.description;} + get version() {return this._config.version ?? this._config.info.version;} + get author() {return this._config.author ?? this._config.info.authors.map(a => a.name).join(", ");} + getName() {return this._config.name ?? this._config.info.name.replace(" ", "");} + getDescription() {return this._config.description ?? this._config.info.description;} + getVersion() {return this._config.version ?? this._config.info.version;} + getAuthor() {return this._config.author ?? this._config.info.authors.map(a => a.name).join(", ");} + get isEnabled() {return this._enabled;} + + get strings() { + if (!this._config.strings) return {}; + const locale = _modules_discordmodules__WEBPACK_IMPORTED_MODULE_5__["default"].LocaleManager?.getLocale().split("-")[0] ?? "en"; + if (this._config.strings.hasOwnProperty(locale)) return this._config.strings[locale]; + if (this._config.strings.hasOwnProperty("en")) return this._config.strings.en; + return this._config.strings; + } + + set strings(strings) { + this._config.strings = strings; + } + + constructor(zplConfig) { + this._config = zplConfig; + this._enabled = false; + + // Build the settings model from the default if it exists + if (typeof(this._config.defaultConfig) !== "undefined") { + this.defaultSettings = {}; + for (let s = 0; s < this._config.defaultConfig.length; s++) { + const current = this._config.defaultConfig[s]; + if (current.type != "category") {this.defaultSettings[current.id] = current.value;} + else { + this.defaultSettings[current.id] = {}; + for (let si = 0; si < current.settings.length; si++) { + const subCurrent = current.settings[si]; + this.defaultSettings[current.id][subCurrent.id] = subCurrent.value; + } + } + } + + // Clone the default settings to the current ones + this.settings = _modules_utilities__WEBPACK_IMPORTED_MODULE_4__["default"].deepclone(this.defaultSettings); + } + + // Load previously stored info to check if changelog is needed then check for update + const currentVersionInfo = _modules_utilities__WEBPACK_IMPORTED_MODULE_4__["default"].loadData(this.name, "currentVersionInfo", {version: this.version, hasShownChangelog: false}); + if (currentVersionInfo.version != this.version || !currentVersionInfo.hasShownChangelog) { + this.showChangelog(); + _modules_utilities__WEBPACK_IMPORTED_MODULE_4__["default"].saveData(this.name, "currentVersionInfo", {version: this.version, hasShownChangelog: true}); + } + + // Do not check updates for self + if (this._config?.id === "9") return; + _modules_pluginupdater__WEBPACK_IMPORTED_MODULE_0__["default"].checkForUpdate(this.name, this.version, this._config.id ?? this._config.github_raw ?? this._config?.info.github_raw); + } + + async start() { + _modules_logger__WEBPACK_IMPORTED_MODULE_1__["default"].info(this.name, `version ${this.version} has started.`); + if (this.defaultSettings) this.settings = this.loadSettings(); + this._enabled = true; + if (typeof(this.onStart) == "function") this.onStart(); + } + + stop() { + _modules_logger__WEBPACK_IMPORTED_MODULE_1__["default"].info(this.name, `version ${this.version} has stopped.`); + this._enabled = false; + if (typeof(this.onStop) == "function") this.onStop(); + } + + showSettingsModal() { + if (typeof(this.getSettingsPanel) != "function") return; + _ui_modals__WEBPACK_IMPORTED_MODULE_3__["default"].showModal(this.name + " Settings", _modules_reacttools__WEBPACK_IMPORTED_MODULE_2__["default"].createWrappedElement(this.getSettingsPanel()), { + cancelText: "", + confirmText: "Done", + size: _ui_modals__WEBPACK_IMPORTED_MODULE_3__["default"].ModalSizes.MEDIUM + }); + } + + showChangelog(footer) { + if (typeof(this._config.changelog) == "undefined") return; + _ui_modals__WEBPACK_IMPORTED_MODULE_3__["default"].showChangelogModal(this.name + " Changelog", this.version, this._config.changelog, footer); + } + + saveSettings(settings) { + _modules_utilities__WEBPACK_IMPORTED_MODULE_4__["default"].saveSettings(this.name, this.settings ? this.settings : settings); + } + + loadSettings(defaultSettings) { + // loadSettings -> loadData -> defaultSettings gets deep cloned + return _modules_utilities__WEBPACK_IMPORTED_MODULE_4__["default"].loadSettings(this.name, this.defaultSettings ? this.defaultSettings : defaultSettings); + } + + buildSetting(data) { + const {name, note, type, value, onChange, id} = data; + let setting = null; + if (type == "color") setting = new _ui_settings__WEBPACK_IMPORTED_MODULE_6__.ColorPicker(name, note, value, onChange, {disabled: data.disabled, presetColors: data.presetColors}); + else if (type == "dropdown") setting = new _ui_settings__WEBPACK_IMPORTED_MODULE_6__.Dropdown(name, note, value, data.options, onChange); + else if (type == "file") setting = new _ui_settings__WEBPACK_IMPORTED_MODULE_6__.FilePicker(name, note, onChange); + else if (type == "keybind") setting = new _ui_settings__WEBPACK_IMPORTED_MODULE_6__.Keybind(name, note, value, onChange); + else if (type == "radio") setting = new _ui_settings__WEBPACK_IMPORTED_MODULE_6__.RadioGroup(name, note, value, data.options, onChange, {disabled: data.disabled}); + else if (type == "slider") setting = new _ui_settings__WEBPACK_IMPORTED_MODULE_6__.Slider(name, note, data.min, data.max, value, onChange, data); + else if (type == "switch") setting = new _ui_settings__WEBPACK_IMPORTED_MODULE_6__.Switch(name, note, value, onChange, {disabled: data.disabled}); + else if (type == "textbox") setting = new _ui_settings__WEBPACK_IMPORTED_MODULE_6__.Textbox(name, note, value, onChange, {placeholder: data.placeholder || ""}); + if (id) setting.id = id; + return setting; + } + + buildSettingsPanel() { + const config = this._config.defaultConfig; + const buildGroup = (group) => { + const {name, id, collapsible, shown, settings} = group; + // this.settings[id] = {}; + + const list = []; + for (let s = 0; s < settings.length; s++) { + const current = Object.assign({}, settings[s]); + current.value = this.settings[id][current.id]; + current.onChange = (value) => { + this.settings[id][current.id] = value; + }; + if (Object.keys(this.strings).length && this.strings.settings && this.strings.settings[id] && this.strings.settings[id][current.id]) { + const {settingName = name, note} = this.strings.settings[id][current.id]; + current.name = settingName; + current.note = note; + } + list.push(this.buildSetting(current)); + } + + const settingGroup = new _ui_settings__WEBPACK_IMPORTED_MODULE_6__.SettingGroup(name, {shown, collapsible}).append(...list); + settingGroup.id = id; + return settingGroup; + }; + const list = []; + for (let s = 0; s < config.length; s++) { + const current = Object.assign({}, config[s]); + if (current.type != "category") { + current.value = this.settings[current.id]; + current.onChange = (value) => { + this.settings[current.id] = value; + }; + if (Object.keys(this.strings).length && this.strings.settings && this.strings.settings[current.id]) { + const {name, note} = this.strings.settings[current.id]; + current.name = name; + current.note = note; + } + list.push(this.buildSetting(current)); + } + else { + list.push(buildGroup(current)); + } + } + + return new _ui_settings__WEBPACK_IMPORTED_MODULE_6__.SettingPanel(this.saveSettings.bind(this), ...list); + } +} + +const wrapPluginBase = (conf) => { + return class BoundPlugin extends Plugin { + constructor() { + super(conf); + } + }; +}; + +/***/ }), + +/***/ "./src/structs/screen.js": +/*!*******************************!*\ + !*** ./src/structs/screen.js ***! + \*******************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__) +/* harmony export */ }); +/** + * Representation of the screen such as width and height. + * @deprecated 1/21/22 Use DOMTools + */ +class Screen { + /** Document/window width */ + static get width() {return Math.max(document.documentElement.clientWidth, window.innerWidth || 0);} + /** Document/window height */ + static get height() {return Math.max(document.documentElement.clientHeight, window.innerHeight || 0);} +} + +/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (Screen); + +/***/ }), + +/***/ "./src/structs/structs.js": +/*!********************************!*\ + !*** ./src/structs/structs.js ***! + \********************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "ClassName": () => (/* reexport safe */ _dom_classname__WEBPACK_IMPORTED_MODULE_2__["default"]), +/* harmony export */ "DOMObserver": () => (/* reexport safe */ _dom_observer__WEBPACK_IMPORTED_MODULE_3__["default"]), +/* harmony export */ "Listenable": () => (/* reexport safe */ _listenable__WEBPACK_IMPORTED_MODULE_4__["default"]), +/* harmony export */ "Plugin": () => (/* reexport safe */ _plugin__WEBPACK_IMPORTED_MODULE_5__["default"]), +/* harmony export */ "Screen": () => (/* reexport safe */ _screen__WEBPACK_IMPORTED_MODULE_0__["default"]), +/* harmony export */ "Selector": () => (/* reexport safe */ _dom_selector__WEBPACK_IMPORTED_MODULE_1__["default"]) +/* harmony export */ }); +/* harmony import */ var _screen__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./screen */ "./src/structs/screen.js"); +/* harmony import */ var _dom_selector__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./dom/selector */ "./src/structs/dom/selector.js"); +/* harmony import */ var _dom_classname__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./dom/classname */ "./src/structs/dom/classname.js"); +/* harmony import */ var _dom_observer__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./dom/observer */ "./src/structs/dom/observer.js"); +/* harmony import */ var _listenable__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./listenable */ "./src/structs/listenable.js"); +/* harmony import */ var _plugin__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./plugin */ "./src/structs/plugin.js"); + + + + + + + + +/***/ }), + +/***/ "./src/ui/colorpicker.js": +/*!*******************************!*\ + !*** ./src/ui/colorpicker.js ***! + \*******************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "default": () => (/* binding */ ColorPicker) +/* harmony export */ }); +/* harmony import */ var modules__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! modules */ "./src/modules/modules.js"); + + +const React = modules__WEBPACK_IMPORTED_MODULE_0__.DiscordModules.React; + +const Popout = modules__WEBPACK_IMPORTED_MODULE_0__.WebpackModules.getByDisplayName("Popout"); +const ColorPickerComponents = modules__WEBPACK_IMPORTED_MODULE_0__.WebpackModules.getByProps("CustomColorPicker"); +const Swatch = ColorPickerComponents?.CustomColorButton.prototype.render.call({props: {}}).type; +const Tooltip = modules__WEBPACK_IMPORTED_MODULE_0__.WebpackModules.getByPrototypes("renderTooltip"); +const LocaleManager = modules__WEBPACK_IMPORTED_MODULE_0__.DiscordModules.LocaleManager; + +class ColorPicker extends React.Component { + constructor(props) { + super(props); + + this.state = { + value: props.value || 0 + }; + + this.onChange = this.onChange.bind(this); + this.swatchRef = React.createRef(); + } + + get canCustom() {return this.props.acceptsCustom || true;} + + onChange(value) { + this.setState({value: value}, () => { + if (typeof(this.props.onChange) === "function") this.props.onChange(this.state.value); + }); + } + + render() { + const renderPopout = () => { + return React.createElement(ColorPickerComponents.CustomColorPicker, { + value: this.state.value, + onChange: this.onChange, + }); + }; + + return React.createElement(ColorPickerComponents.default, { + value: this.state.value, + onChange: this.onChange, + colors: this.props.colors, + renderDefaultButton: props => React.createElement(Tooltip, { + position: Tooltip.Positions.BOTTOM, + text: LocaleManager.Messages.DEFAULT + }, tooltipProps => React.createElement("div", Object.assign(tooltipProps, { + className: "defaultButtonWrapper", + }), React.createElement(ColorPickerComponents.DefaultColorButton, Object.assign(props, {color: this.props.defaultColor})))), + renderCustomButton: () => React.createElement(Popout, { + renderPopout: renderPopout, + animation: Popout.Animation.TRANSLATE, + align: Popout.Align.CENTER, + position: Popout.Positions.BOTTOM + }, props => React.createElement(Tooltip, { + position: Tooltip.Positions.BOTTOM, + text: LocaleManager.Messages.PICK_A_COLOR + }, tooltipProps => React.createElement("div", Object.assign({}, tooltipProps, props, { + className: "colorPickerButtonWrapper" + }), React.createElement(Swatch, { + isCustom: true, + color: this.state.value, + isSelected: !this.props.colors.includes(this.state.value) && this.props.defaultColor !== this.state.value, + disabled: !this.canCustom + })))) + }); + } +} + +/***/ }), + +/***/ "./src/ui/discordcontextmenu.js": +/*!**************************************!*\ + !*** ./src/ui/discordcontextmenu.js ***! + \**************************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "default": () => (/* binding */ DiscordContextMenu) +/* harmony export */ }); +/* harmony import */ var _modules_webpackmodules__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../modules/webpackmodules */ "./src/modules/webpackmodules.js"); +/* harmony import */ var _modules_reacttools__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../modules/reacttools */ "./src/modules/reacttools.js"); +/* harmony import */ var _modules_utilities__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../modules/utilities */ "./src/modules/utilities.js"); +/* harmony import */ var _modules_discordclasses__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../modules/discordclasses */ "./src/modules/discordclasses.js"); + + + + + +// d = e.label, +// f = e.icon, +// h = e.imageUrl, +// v = e.hint, +// m = e.subtext, +// g = e.hasSubmenu, +// y = e.disabled, +// E = e.isFocused, +// S = e.menuItemProps, +// T = e.action, +// b = e.onClose, + + +/** + * Fires when the item is clicked. + * @param {MouseEvent} event - The event generated on click + * @callback module:DiscordContextMenu~MenuItemOnClick + */ + +/** + * @interface + * @name module:DiscordContextMenu~MenuItem + * @description + * This is the generic context menu item component. It is very extensible and will adapt + * it's type depending on the props. + * + * Note: The item ID should be unique to this item across the entire menu. If no `id` is + * provided, the system will use the `label`. Plugins should ensure there are no `label` + * conflicts if they do not wish to provide `id`. `label` conflicts (when not using + * unique `id`s) can cause multiple items to be hovered at once. + * + * @param {object} props - props to pass to the react renderer + * @param {string} props.label - label to show on the menu item + * @param {string} [props.id] - specific id used for this item + * @param {string} [props.hint] - hint to show on the right hand side (usually keyboard combo) + * @param {string} [props.subtext] - description to show underneath + * @param {string} [props.image] - link to image to show on the side + * @param {function} [props.icon] - react component to render on the side + * @param {function} [props.render] - render function for custom rendering the menu item + * @param {module:DiscordContextMenu~MenuItemOnClick} [props.action] - function to perform on click + * @param {module:DiscordContextMenu~MenuItemOnClick} [props.onClick] - function to perform on click (alias of `action`) + * @param {function} [props.onClose] - function to run when this is closed + * @param {boolean} [props.danger=false] - should the item show as danger (red) + * @param {boolean} [props.disabled=false] - should the item be disabled/unclickable + * + * @param {object} [props.style] - allows you to add custom styles + * @param {boolean} [props.closeOnClick] - allows you to prevent closing on click + */ + +/** + * @interface + * @name module:DiscordContextMenu~MenuToggleItem + * @extends module:DiscordContextMenu~MenuItem + * @description + * This item is used for creating checkboxes in menus. Properties shown here are additional + * to those of the main MenuItem {@link module:DiscordContextMenu~MenuItem} + * + * + * @param {boolean} [props.checked=false] - should the checkbox be checked + * @param {boolean} [props.active=false] - alias of `checked` + */ + +/** + * @interface + * @name module:DiscordContextMenu~MenuRadioItem + * @extends module:DiscordContextMenu~MenuItem + * @description + * This item is used for creating radio selections in menus. Properties shown here are additional + * to those of the main MenuItem {@link module:DiscordContextMenu~MenuItem} + * + * Note: for the `forceUpdate` option... Without this enabled, you will manually need to + * manage the state for the functional component. If you do not the toggle will appear + * to not update. @see {@link https://reactjs.org/docs/hooks-reference.html#usestate} + * + * @param {boolean} [props.checked=false] - should the checkbox be checked + * @param {boolean} [props.active=false] - alias of `checked` + * @param {boolean} [props.forceUpdate=true] - should the menu be force-updated after click + */ + +/** + * @interface + * @name module:DiscordContextMenu~SubMenuItem + * @extends module:DiscordContextMenu~MenuItem + * @description + * This item is used for creating nested submenus. Properties shown here are additional + * to those of the main MenuItem {@link module:DiscordContextMenu~MenuItem} + * + * @param {Array} [props.render] - array of items to render in the submenu + * @param {Array} [props.items] - alias of `render` + * @param {Array} [props.children] - Already rendered elements + */ + +/** + * @interface + * @name module:DiscordContextMenu~MenuControlItem + * @extends module:DiscordContextMenu~MenuItem + * @description + * This item is used for adding custom controls like sliders to the context menu. + * Properties shown here are additional to those of the main MenuItem {@link module:DiscordContextMenu~MenuItem} + * + * @param {function} [props.control] - control function that renders the component + */ + + +/** + * A utility for building and rendering Discord's own menus. + * @module DiscordContextMenu + */ +class DiscordContextMenu { + + /** + * Builds a single menu item. The only prop shown here is the type, the rest should + * match the actual component being built. View those to see what options exist + * for each, they often have less in common than you might think. See {@link module:DiscordContextMenu.MenuItem} + * for the majority of props commonly available. Check the documentation for the + * rest of the components. + * + * @param {object} props - props used to build the item + * @param {string} [props.type="text"] - type of the item, options: text, submenu, toggle, radio, custom, separator + * @returns {object} the created component + * + * @see {@link module:DiscordContextMenu~MenuItem} + * @see {@link module:DiscordContextMenu~MenuToggleItem} + * @see {@link module:DiscordContextMenu~MenuRadioItem} + * @see {@link module:DiscordContextMenu~SubMenuItem} + * @see {@link module:DiscordContextMenu~MenuControlItem} + * + * @example + * // Creates a single menu item that prints "MENU ITEM" on click + * DiscordContextMenu.buildMenuItem({ + * label: "Menu Item", + * action: () => {console.log("MENU ITEM");} + * }); + * + * @example + * // Creates a single toggle item that starts unchecked + * // and print the new value on every toggle + * DiscordContextMenu.buildMenuItem({ + * type: "toggle", + * label: "Item Toggle", + * checked: false, + * action: (newValue) => {console.log(newValue);} + * }); + */ + static buildMenuItem(props) { + return window.BdApi.ContextMenu.buildItem(props); + } + + /** + * Creates the all the items **and groups** of a context menu recursively. + * There is no hard limit to the number of groups within groups or number + * of items in a menu. + * @param {Array} setup - array of item props used to build items. See {@link module:DiscordContextMenu.buildMenuItem} + * @returns {Array} array of the created component + * + * @example + * // Creates a single item group item with a toggle item + * DiscordContextMenu.buildMenuChildren([{ + * type: "group", + * items: [{ + * type: "toggle", + * label: "Item Toggle", + * active: false, + * action: (newValue) => {console.log(newValue);} + * }] + * }]); + * + * @example + * // Creates two item groups with a single toggle item each + * DiscordContextMenu.buildMenuChildren([{ + * type: "group", + * items: [{ + * type: "toggle", + * label: "Item Toggle", + * active: false, + * action: (newValue) => { + * console.log(newValue); + * } + * }] + * }, { + * type: "group", + * items: [{ + * type: "toggle", + * label: "Item Toggle", + * active: false, + * action: (newValue) => { + * console.log(newValue); + * } + * }] + * }]); + */ + static buildMenuChildren(setup) { + return window.BdApi.ContextMenu.buildMenuChildren(setup); + } + + /** + * Creates the menu *component* including the wrapping `ContextMenu`. + * Calls {@link module:DiscordContextMenu.buildMenuChildren} under the covers. + * Used to call in combination with {@link module:DiscordContextMenu.openContextMenu}. + * @param {Array} setup - array of item props used to build items. See {@link module:DiscordContextMenu.buildMenuChildren} + * @returns {function} the unique context menu component + */ + static buildMenu(setup) { + return window.BdApi.ContextMenu.buildMenu(setup); + } + + /** + * + * @param {MouseEvent} event - The context menu event. This can be emulated, requires target, and all X, Y locations. + * @param {function} menuComponent - Component to render. This can be any react component or output of {@link module:DiscordContextMenu.buildMenu} + * @param {object} config - configuration/props for the context menu + * @param {string} [config.position="right"] - default position for the menu, options: "left", "right" + * @param {string} [config.align="top"] - default alignment for the menu, options: "bottom", "top" + * @param {function} [config.onClose] - function to run when the menu is closed + * @param {boolean} [config.noBlurEvent=false] - No clue + */ + static openContextMenu(event, menuComponent, config) { + return window.BdApi.ContextMenu.open(event, menuComponent, config); + } + + /** + * Attempts to find and return a specific context menu type's module. Useful + * when patching the render of these menus. + * @param {string | Function} nameOrFilter - name of the context menu type + * @returns {Promise} the webpack module the menu was found in + * @deprecated + */ + static getDiscordMenu(nameOrFilter) { + if (typeof(nameOrFilter) !== "function") { + const displayName = nameOrFilter; + nameOrFilter = (m) => m && m.displayName === displayName; + } + + const directMatch = _modules_webpackmodules__WEBPACK_IMPORTED_MODULE_0__["default"].getModule(m => m.default && nameOrFilter(m.default)); + if (directMatch) return Promise.resolve(directMatch); + + return new Promise(resolve => { + const cancel = _modules_webpackmodules__WEBPACK_IMPORTED_MODULE_0__["default"].addListener(module => { + if (!module.default || !nameOrFilter(module.default)) return; + resolve(module); + cancel(); + }); + }); + } + + /** + * Calls `forceUpdate()` on all context menus it can find. Useful for + * after patching a menu. + */ + static forceUpdateMenus() { + const menus = document.querySelectorAll(`.${_modules_discordclasses__WEBPACK_IMPORTED_MODULE_3__["default"].ContextMenu.menu.first}`); + for (const menu of menus) { + const stateNode = _modules_utilities__WEBPACK_IMPORTED_MODULE_2__["default"].findInTree(_modules_reacttools__WEBPACK_IMPORTED_MODULE_1__["default"].getReactInstance(menu), m=>m && m.forceUpdate && m.updatePosition, {walkable: ["return", "stateNode"]}); + if (!stateNode) continue; + stateNode.forceUpdate(); + stateNode.updatePosition(); + } + } +} + +/***/ }), + +/***/ "./src/ui/errorboundary.js": +/*!*********************************!*\ + !*** ./src/ui/errorboundary.js ***! + \*********************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "WrapBoundary": () => (/* binding */ WrapBoundary), +/* harmony export */ "default": () => (/* binding */ ErrorBoundary) +/* harmony export */ }); +/* harmony import */ var _modules_discordmodules__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../modules/discordmodules */ "./src/modules/discordmodules.js"); + + +const React = _modules_discordmodules__WEBPACK_IMPORTED_MODULE_0__["default"].React; +const ce = React.createElement; + +class ErrorBoundary extends React.Component { + constructor(props) { + super(props); + this.state = {hasError: false}; + } + + componentDidCatch() { + this.setState({hasError: true}); + } + + render() { + if (this.state.hasError) return this.props.errorChildren ? this.props.errorChildren : ce("div", {className: "error"}, "Component Error"); + return this.props.children; + } +} + +function WrapBoundary(Original) { + return class ErrorBoundaryWrapper extends React.Component { + render() { + return ce(ErrorBoundary, null, ce(Original, this.props)); + } + }; +} + +/***/ }), + +/***/ "./src/ui/icons.js": +/*!*************************!*\ + !*** ./src/ui/icons.js ***! + \*************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "IconError": () => (/* reexport safe */ _icons_error__WEBPACK_IMPORTED_MODULE_0__["default"]), +/* harmony export */ "IconInfo": () => (/* reexport safe */ _icons_info__WEBPACK_IMPORTED_MODULE_1__["default"]), +/* harmony export */ "IconSuccess": () => (/* reexport safe */ _icons_success__WEBPACK_IMPORTED_MODULE_2__["default"]), +/* harmony export */ "IconWarning": () => (/* reexport safe */ _icons_warning__WEBPACK_IMPORTED_MODULE_3__["default"]) +/* harmony export */ }); +/* harmony import */ var _icons_error__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./icons/error */ "./src/ui/icons/error.js"); +/* harmony import */ var _icons_info__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./icons/info */ "./src/ui/icons/info.js"); +/* harmony import */ var _icons_success__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./icons/success */ "./src/ui/icons/success.js"); +/* harmony import */ var _icons_warning__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./icons/warning */ "./src/ui/icons/warning.js"); + + + + + +/***/ }), + +/***/ "./src/ui/icons/error.js": +/*!*******************************!*\ + !*** ./src/ui/icons/error.js ***! + \*******************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "default": () => (/* export default binding */ __WEBPACK_DEFAULT_EXPORT__) +/* harmony export */ }); +/** + * Error Icon + * @param {number} size - Size of the icon. + */ +/* harmony default export */ function __WEBPACK_DEFAULT_EXPORT__(size) { + return ` + + `; +} + +/***/ }), + +/***/ "./src/ui/icons/info.js": +/*!******************************!*\ + !*** ./src/ui/icons/info.js ***! + \******************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "default": () => (/* export default binding */ __WEBPACK_DEFAULT_EXPORT__) +/* harmony export */ }); +/** + * Info Icon + * @param {number} size - Size of the icon. + */ +/* harmony default export */ function __WEBPACK_DEFAULT_EXPORT__(size) { + return ` + + + `; +} + +/***/ }), + +/***/ "./src/ui/icons/success.js": +/*!*********************************!*\ + !*** ./src/ui/icons/success.js ***! + \*********************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "default": () => (/* export default binding */ __WEBPACK_DEFAULT_EXPORT__) +/* harmony export */ }); +/** + * Success Icon + * @param {number} size - Size of the icon. + */ +/* harmony default export */ function __WEBPACK_DEFAULT_EXPORT__(size) { + return ` + + + `; +} + +/***/ }), + +/***/ "./src/ui/icons/warning.js": +/*!*********************************!*\ + !*** ./src/ui/icons/warning.js ***! + \*********************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "default": () => (/* export default binding */ __WEBPACK_DEFAULT_EXPORT__) +/* harmony export */ }); +/** + * Warning Icon + * @param {number} size - Size of the icon. + */ +/* harmony default export */ function __WEBPACK_DEFAULT_EXPORT__(size) { + return ` + + + `; +} + +/***/ }), + +/***/ "./src/ui/modals.js": +/*!**************************!*\ + !*** ./src/ui/modals.js ***! + \**************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "default": () => (/* binding */ Modals) +/* harmony export */ }); +/* harmony import */ var modules__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! modules */ "./src/modules/modules.js"); +/** + * Allows an easy way to create and show modals. + * @module Modals + */ + + + +const React = modules__WEBPACK_IMPORTED_MODULE_0__.DiscordModules.React; +const ce = React.createElement; +const Markdown = modules__WEBPACK_IMPORTED_MODULE_0__.WebpackModules.getModule(m => m.rules); +const MarkdownParser = modules__WEBPACK_IMPORTED_MODULE_0__.WebpackModules.getByProps("defaultRules", "parse"); + +class Modals { + + /** Sizes of modals. */ + static get ModalSizes() {return {};} + + /** + * Shows the user profile modal for a given user. + * @param {string} userId - id of the user to show profile for + */ + static showUserProfile(userId) { + return modules__WEBPACK_IMPORTED_MODULE_0__.DiscordModules.UserProfileModal.open(userId); + } + + /** + * Acts as a wrapper for {@link module:Modals.showModal} where the `children` is a text element. + * @param {string} title - title of the modal + * @param {string} content - text to show inside the modal. Can be markdown. + * @param {object} [options] - see {@link module:Modals.showModal} + * @see module:Modals.showModal + */ + static showConfirmationModal(title, content, options = {}) { + this.showModal(title, ce(Markdown, null, content), options); + } + + /** + * Shows a very simple alert modal that has title, content and an okay button. + * @param {string} title - title of the modal + * @param {string} body - text to show inside the modal + */ + static showAlertModal(title, body) { + this.showConfirmationModal(title, body, {cancelText: null}); + } + + /** + * Shows a generic but very customizable modal. + * @param {string} title - title of the modal + * @param {(ReactElement|Array)} children - a single or array of rendered react elements to act as children + * @param {object} [options] - options to modify the modal + * @param {boolean} [options.danger=false] - whether the main button should be red or not + * @param {string} [options.confirmText=Okay] - text for the confirmation/submit button + * @param {string} [options.cancelText=Cancel] - text for the cancel button + * @param {callable} [options.onConfirm=NOOP] - callback to occur when clicking the submit button + * @param {callable} [options.onCancel=NOOP] - callback to occur when clicking the cancel button + */ + static showModal(title, children, options = {}) { + const {danger = false, confirmText = "Okay", cancelText = "Cancel", onConfirm = () => {}, onCancel = () => {}} = options; + return modules__WEBPACK_IMPORTED_MODULE_0__.DiscordModules.ModalActions.openModal(props => { + return React.createElement(modules__WEBPACK_IMPORTED_MODULE_0__.DiscordModules.ConfirmationModal, Object.assign({ + header: title, + confirmButtonColor: danger ? modules__WEBPACK_IMPORTED_MODULE_0__.DiscordModules.ButtonData.Colors.RED : modules__WEBPACK_IMPORTED_MODULE_0__.DiscordModules.ButtonData.Colors.BRAND, + confirmText: confirmText, + cancelText: cancelText, + onConfirm: onConfirm, + onCancel: onCancel + }, props), children); + }); + } + + /** + * @interface + * @name module:Modals~Changelog + * @property {string} title - title of the changelog section + * @property {string} [type=added] - type information of the section. Options: added, improved, fixed, progress. + * @property {Array} items - itemized list of items to show in that section. Can use markdown. + */ + + /** + * Shows a changelog modal based on changelog data. + * @param {string} title - title of the modal + * @param {string} version - subtitle (usually version or date) of the modal + * @param {module:Modals~Changelog} changelog - changelog to show inside the modal + * @param {string} footer - either an html element or text to show in the footer of the modal. Can use markdown. + */ + static showChangelogModal(title, version, changelog, footer) { + const TextElement = modules__WEBPACK_IMPORTED_MODULE_0__.DiscordModules.TextElement; + const ChangelogModalClasses = modules__WEBPACK_IMPORTED_MODULE_0__.WebpackModules.getModule(m => m.modal && m.maxModalWidth); + if (!TextElement || !ChangelogModalClasses || !modules__WEBPACK_IMPORTED_MODULE_0__.DiscordModules.FlexChild || !modules__WEBPACK_IMPORTED_MODULE_0__.DiscordModules.ModalRoot || !modules__WEBPACK_IMPORTED_MODULE_0__.DiscordModules.ModalActions) return modules__WEBPACK_IMPORTED_MODULE_0__.Logger.warn("Modals", "Unable to show changelog modal--missing modules"); + const changelogItems = []; + for (let c = 0; c < changelog.length; c++) { + const entry = changelog[c]; + const type = modules__WEBPACK_IMPORTED_MODULE_0__.DiscordClasses.Changelog[entry.type] ? modules__WEBPACK_IMPORTED_MODULE_0__.DiscordClasses.Changelog[entry.type] : modules__WEBPACK_IMPORTED_MODULE_0__.DiscordClasses.Changelog.added; + const margin = c == 0 ? modules__WEBPACK_IMPORTED_MODULE_0__.DiscordClasses.Changelog.marginTop : ""; + changelogItems.push(ce("h1", {className: `${type} ${margin}`,}, entry.title)); + const list = ce("ul", null, entry.items.map(i => ce("li", null, MarkdownParser.parse(i)))); + changelogItems.push(list); + } + const renderHeader = function() { + return ce(modules__WEBPACK_IMPORTED_MODULE_0__.DiscordModules.FlexChild, {className: modules__WEBPACK_IMPORTED_MODULE_0__.DiscordClasses.Modals.header.toString(), grow: 0, shrink: 0, direction: modules__WEBPACK_IMPORTED_MODULE_0__.DiscordModules.FlexChild.Direction.VERTICAL}, + ce(TextElement, {tag: "h1", size: TextElement.Sizes.SIZE_20, strong: true}, title), + ce(TextElement, {size: TextElement.Sizes.SIZE_12, color: TextElement.Colors.STANDARD, className: modules__WEBPACK_IMPORTED_MODULE_0__.DiscordClasses.Changelog.date.toString()}, "Version " + version) + ); + }; + const renderFooter = footer ? function() { + return ce(Markdown, null, footer); + } : null; + + const body = ce("div", { + className: `${modules__WEBPACK_IMPORTED_MODULE_0__.DiscordClasses.Modals.content} ${modules__WEBPACK_IMPORTED_MODULE_0__.DiscordClasses.Changelog.container} ${ChangelogModalClasses.content} ${modules__WEBPACK_IMPORTED_MODULE_0__.DiscordClasses.Scrollers.thin}` + }, changelogItems); + + // return DiscordModules.ModalActions.openModal(props => { + // return ce(WebpackModules.getModule(m => m?.toString()?.includes("confirmText")), Object.assign({ + // className: DiscordClasses.Changelog.container.toString(), + // selectable: true, + // onScroll: _ => _, + // onClose: _ => _, + // renderHeader: renderHeader, + // renderFooter: renderFooter, + // }, props), changelogItems); + // }); + return modules__WEBPACK_IMPORTED_MODULE_0__.DiscordModules.ModalActions.openModal(props => { + return React.createElement(modules__WEBPACK_IMPORTED_MODULE_0__.DiscordModules.ModalRoot, Object.assign({ + className: `bd-changelog-modal ${modules__WEBPACK_IMPORTED_MODULE_0__.DiscordClasses.Modals.root} ${modules__WEBPACK_IMPORTED_MODULE_0__.DiscordClasses.Modals.small} ${ChangelogModalClasses.modal}`, + selectable: true, + onScroll: _ => _, + onClose: _ => _ + }, props), [renderHeader(), body, renderFooter?.()]); + }); + // return Modals.showModal(`${title} v${version}`, [renderHeader(), changelogItems, renderFooter?.()], {cancelText: null}); + } +} + +/***/ }), + +/***/ "./src/ui/popouts.js": +/*!***************************!*\ + !*** ./src/ui/popouts.js ***! + \***************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "default": () => (/* binding */ Popouts) +/* harmony export */ }); +/* harmony import */ var modules__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! modules */ "./src/modules/modules.js"); +/** + * Allows an easy way to create and show popouts. + * @module Popouts + */ + + + +const {React, ReactDOM} = modules__WEBPACK_IMPORTED_MODULE_0__.DiscordModules; +const {useReducer, useEffect, useRef} = React; +const AppLayer = modules__WEBPACK_IMPORTED_MODULE_0__.WebpackModules.getModule(m => Object.values(m).some(m => m?.displayName === "AppLayer")); +const ReferencePositionLayer = modules__WEBPACK_IMPORTED_MODULE_0__.WebpackModules.getModule(m => m?.prototype?.calculatePositionStyle, {searchExports: true}); +// const PopoutCSSAnimator = WebpackModules.getByDisplayName("PopoutCSSAnimator"); +const LayerProvider = Object.values(AppLayer).find(m => m.displayName === "AppLayerProvider")?.().props.layerContext.Provider; // eslint-disable-line new-cap +const ComponentDispatch = modules__WEBPACK_IMPORTED_MODULE_0__.WebpackModules.getModule(m => m.toString?.().includes("useContext") && m.toString?.().includes("windowDispatch"), {searchExports: true}); +const ComponentActions = modules__WEBPACK_IMPORTED_MODULE_0__.WebpackModules.getModule(m => m.POPOUT_SHOW, {searchExports: true}); +const Popout = modules__WEBPACK_IMPORTED_MODULE_0__.WebpackModules.getModule(m => m?.defaultProps && m?.Animation, {searchExports: true}); +const ThemeContext = modules__WEBPACK_IMPORTED_MODULE_0__.WebpackModules.getModule(m => m?.toString?.().includes("amoled:") && m?.toString?.().includes("Provider"), {searchExports: true}); +const useStateFromStores = modules__WEBPACK_IMPORTED_MODULE_0__.WebpackModules.getModule(m => m.toString?.().includes("useStateFromStores")); +const ThemeStore = modules__WEBPACK_IMPORTED_MODULE_0__.WebpackModules.getModule(m => m.theme); + +const createStore = state => { + const listeners = new Set(); + + const setState = function (getter = _ => _) { + const partial = getter(state); + if (partial === state) return; + + state = partial; + + [...listeners].forEach(e => e()); + }; + + setState.getState = () => state; + + function storeListener(getter = _ => _) { + const [, forceUpdate] = useReducer(n => !n, true); + + useEffect(() => { + const dispatch = () => {forceUpdate();}; + + listeners.add(dispatch); + + return () => {listeners.delete(dispatch);}; + }); + + return getter(state); + } + + return [ + setState, + storeListener + ]; +}; + +const [setPopouts, usePopouts] = createStore([]); + +// const AnimationTypes = {FADE: 3, SCALE: 2, TRANSLATE: 1}; + +class Popouts { + + // static get AnimationTypes() {return AnimationTypes;} + + static initialize() { + this.dispose(); + this.popouts = 0; + + this.container = Object.assign(document.createElement("div"), { + className: "ZeresPluginLibraryPopoutsRenderer", + style: "display: none;" + }); + + this.layerContainer = Object.assign(document.createElement("div"), { + id: "ZeresPluginLibraryPopouts", + className: modules__WEBPACK_IMPORTED_MODULE_0__.DiscordClassModules.TooltipLayers.layerContainer + }); + + document.body.append(this.container, this.layerContainer); + ReactDOM.render(React.createElement(PopoutsContainer), this.layerContainer); + } + + /** + * Shows the user popout for a user relative to a target element + * @param {HTMLElement} target - Element to show the popout in relation to + * @param {object} user - Discord User object for the user to show + * @param {object} [options] - Options to modify the request + * @param {string} [options.guild="currentGuildId"] - Id of the guild (uses current if not specified) + * @param {string} [options.channel="currentChannelId"] - Id of the channel (uses current if not specified) + * @param {string} [options.position="right"] - Positioning relative to element + * @param {string} [options.align="top"] - Positioning relative to element + */ + static showUserPopout(target, user, options = {}) { + const {position = "right", align = "top", guild = modules__WEBPACK_IMPORTED_MODULE_0__.DiscordModules.SelectedGuildStore.getGuildId(), channel = modules__WEBPACK_IMPORTED_MODULE_0__.DiscordModules.SelectedChannelStore.getChannelId()} = options; + target = modules__WEBPACK_IMPORTED_MODULE_0__.DOMTools.resolveElement(target); + // if (target.getBoundingClientRect().right + 250 >= DOMTools.screenWidth && options.autoInvert) position = "left"; + // if (target.getBoundingClientRect().bottom + 400 >= DOMTools.screenHeight && options.autoInvert) align = "bottom"; + // if (target.getBoundingClientRect().top - 400 >= DOMTools.screenHeight && options.autoInvert) align = "top"; + this.openPopout(target, { + position: position, + align: align, + // animation: options.animation || Popouts.AnimationTypes.TRANSLATE, + autoInvert: options.autoInvert, + nudgeAlignIntoViewport: options.nudgeAlignIntoViewport, + spacing: options.spacing, + render: (props) => { + return modules__WEBPACK_IMPORTED_MODULE_0__.DiscordModules.React.createElement(modules__WEBPACK_IMPORTED_MODULE_0__.DiscordModules.UserPopout, Object.assign({}, props, { + userId: user.id, + guildId: guild, + channelId: channel, + closePopout: () => this.closePopout(props.popoutId) + })); + } + }); + } + + /** + * Shows a react popout relative to a target element + * @param {HTMLElement} target - Element to show the popout in relation to + * @param {object} [options] - Options to modify the request + * @param {string} [options.position="right"] - General position relative to element + * @param {string} [options.align="top"] - Alignment relative to element + * @param {boolean} [options.autoInvert=true] - Try to automatically adjust the position if it overflows the screen + * @param {boolean} [options.nudgeAlignIntoViewport=true] - Try to automatically adjust the alignment if it overflows the screen + * @param {number} [options.spacing=8] - Spacing between target and popout + */ + static openPopout(target, options) { + const id = this.popouts++; + + setPopouts(popouts => popouts.concat({ + id: id, + element: React.createElement(PopoutWrapper, Object.assign({}, Popout.defaultProps, { + reference: {current: target}, + popoutId: id, + key: "popout_" + id, + spacing: 50 + }, options)) + })); + + return id; + } + + static closePopout(id) { + const popout = setPopouts.getState().find(e => e.id === id); + + if (!popout) return null; + + setPopouts(popouts => { + const clone = [...popouts]; + clone.splice(clone.indexOf(popout), 1); + return clone; + }); + } + + static dispose() { + modules__WEBPACK_IMPORTED_MODULE_0__.Patcher.unpatchAll("Popouts"); + const container = document.querySelector(".ZeresPluginLibraryPopoutsRenderer"); + const layerContainer = document.querySelector("#ZeresPluginLibraryPopouts"); + if (container) ReactDOM.unmountComponentAtNode(container); + if (container) container.remove(); + if (layerContainer) layerContainer.remove(); + } +} + +function DiscordProviders({children, container}) { + const theme = useStateFromStores([ThemeStore], () => ThemeStore.theme); + + return React.createElement(LayerProvider, {value: [container]}, + React.createElement(ThemeContext, {theme}, children) + ); +} + +function PopoutsContainer() { + const popouts = usePopouts(); + + return React.createElement(DiscordProviders, + {container: Popouts.layerContainer}, + popouts.map((popout) => popout.element) + ); +} + +function PopoutWrapper({render, popoutId, ...props}) { + const popoutRef = useRef(); + + useEffect(() => { + if (!popoutRef.current) return; + + const node = ReactDOM.findDOMNode(popoutRef.current); + + const handleClick = ({target}) => { + if (target === node || node.contains(target)) return; + + Popouts.closePopout(popoutId); + }; + + const target = modules__WEBPACK_IMPORTED_MODULE_0__.Utilities.findInTree(node.__reactFiber$, m => m?.stateNode?.updatePosition, {walkable: ["return"]}); + setTimeout(() => target?.stateNode?.updatePosition(), 1); + + document.addEventListener("click", handleClick); + + return () => { + document.removeEventListener("click", handleClick); + }; + }, [popoutRef]); + + // switch (animation) { + // case PopoutCSSAnimator.Types.FADE: + // case PopoutCSSAnimator.Types.SCALE: + // case PopoutCSSAnimator.Types.TRANSLATE: { + // const renderPopout = render; + // render = (renderProps) => { + // return React.createElement(PopoutCSSAnimator, { + // position: renderProps.position, + // type: animation + // }, renderPopout(renderProps)); + // }; + // } + // } + + // eslint-disable-next-line new-cap + const ComponentDispatcher = ComponentDispatch(); + + return React.createElement(ReferencePositionLayer, Object.assign(props, { + ref: popoutRef, + positionKey: "0", + autoInvert: true, + nudgeAlignIntoViewport: true, + id: "popout_" + popoutId, + animation: 2, + onMount() { + ComponentDispatcher.dispatch(ComponentActions.POPOUT_SHOW); + }, + onUnmount() { + ComponentDispatcher.dispatch(ComponentActions.POPOUT_HIDE); + }, + children: (props, ...p) => React.createElement( + "div", + { + style: {transform: "translateZ(0)"}, // for z-index to work properly for sub-popouts + }, + render({popoutId, ...props}, ...p) + ) + })); +} + + + + +/***/ }), + +/***/ "./src/ui/settings/index.js": +/*!**********************************!*\ + !*** ./src/ui/settings/index.js ***! + \**********************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "CSS": () => (/* reexport safe */ _styles_settings_css__WEBPACK_IMPORTED_MODULE_0__["default"]), +/* harmony export */ "ColorPicker": () => (/* reexport safe */ _types_color__WEBPACK_IMPORTED_MODULE_5__["default"]), +/* harmony export */ "Dropdown": () => (/* reexport safe */ _types_dropdown__WEBPACK_IMPORTED_MODULE_9__["default"]), +/* harmony export */ "FilePicker": () => (/* reexport safe */ _types_file__WEBPACK_IMPORTED_MODULE_6__["default"]), +/* harmony export */ "Keybind": () => (/* reexport safe */ _types_keybind__WEBPACK_IMPORTED_MODULE_10__["default"]), +/* harmony export */ "RadioGroup": () => (/* reexport safe */ _types_radiogroup__WEBPACK_IMPORTED_MODULE_11__["default"]), +/* harmony export */ "ReactSetting": () => (/* reexport safe */ _settingfield__WEBPACK_IMPORTED_MODULE_1__.ReactSetting), +/* harmony export */ "SettingField": () => (/* reexport safe */ _settingfield__WEBPACK_IMPORTED_MODULE_1__["default"]), +/* harmony export */ "SettingGroup": () => (/* reexport safe */ _settinggroup__WEBPACK_IMPORTED_MODULE_2__["default"]), +/* harmony export */ "SettingPanel": () => (/* reexport safe */ _settingpanel__WEBPACK_IMPORTED_MODULE_3__["default"]), +/* harmony export */ "Slider": () => (/* reexport safe */ _types_slider__WEBPACK_IMPORTED_MODULE_7__["default"]), +/* harmony export */ "Switch": () => (/* reexport safe */ _types_switch__WEBPACK_IMPORTED_MODULE_8__["default"]), +/* harmony export */ "Textbox": () => (/* reexport safe */ _types_textbox__WEBPACK_IMPORTED_MODULE_4__["default"]) +/* harmony export */ }); +/* harmony import */ var _styles_settings_css__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../styles/settings.css */ "./src/styles/settings.css"); +/* harmony import */ var _settingfield__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./settingfield */ "./src/ui/settings/settingfield.js"); +/* harmony import */ var _settinggroup__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./settinggroup */ "./src/ui/settings/settinggroup.js"); +/* harmony import */ var _settingpanel__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./settingpanel */ "./src/ui/settings/settingpanel.js"); +/* harmony import */ var _types_textbox__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./types/textbox */ "./src/ui/settings/types/textbox.js"); +/* harmony import */ var _types_color__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./types/color */ "./src/ui/settings/types/color.js"); +/* harmony import */ var _types_file__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./types/file */ "./src/ui/settings/types/file.js"); +/* harmony import */ var _types_slider__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./types/slider */ "./src/ui/settings/types/slider.js"); +/* harmony import */ var _types_switch__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ./types/switch */ "./src/ui/settings/types/switch.js"); +/* harmony import */ var _types_dropdown__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ./types/dropdown */ "./src/ui/settings/types/dropdown.js"); +/* harmony import */ var _types_keybind__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! ./types/keybind */ "./src/ui/settings/types/keybind.js"); +/* harmony import */ var _types_radiogroup__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(/*! ./types/radiogroup */ "./src/ui/settings/types/radiogroup.js"); +/** + * An object that makes generating settings panel 10x easier. + * @module Settings + */ + + + + + + + + + + + + + + + + +/***/ }), + +/***/ "./src/ui/settings/settingfield.js": +/*!*****************************************!*\ + !*** ./src/ui/settings/settingfield.js ***! + \*****************************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "ReactSetting": () => (/* binding */ ReactSetting), +/* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__) +/* harmony export */ }); +/* harmony import */ var _structs_listenable__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../structs/listenable */ "./src/structs/listenable.js"); +/* harmony import */ var modules__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! modules */ "./src/modules/modules.js"); + + + + +/** + * Setting field to extend to create new settings + * @memberof module:Settings + */ +class SettingField extends _structs_listenable__WEBPACK_IMPORTED_MODULE_0__["default"] { + /** + * @param {string} name - name label of the setting + * @param {string} note - help/note to show underneath or above the setting + * @param {callable} onChange - callback to perform on setting change + * @param {(ReactComponent|HTMLElement)} settingtype - actual setting to render + * @param {object} [props] - object of props to give to the setting and the settingtype + * @param {boolean} [props.noteOnTop=false] - determines if the note should be shown above the element or not. + */ + constructor(name, note, onChange, settingtype, props = {}) { + super(); + this.name = name; + this.note = note; + if (typeof(onChange) == "function") this.addListener(onChange); + this.inputWrapper = modules__WEBPACK_IMPORTED_MODULE_1__.DOMTools.parseHTML(`
`); + this.type = typeof(settingtype) == "function" ? settingtype : modules__WEBPACK_IMPORTED_MODULE_1__.ReactTools.wrapElement(settingtype); + this.props = props; + modules__WEBPACK_IMPORTED_MODULE_1__.DOMTools.onAdded(this.getElement(), () => {this.onAdded();}); + modules__WEBPACK_IMPORTED_MODULE_1__.DOMTools.onRemoved(this.getElement(), () => {this.onRemoved();}); + } + + /** @returns {HTMLElement} - root element for setting */ + getElement() {return this.inputWrapper;} + + /** Fires onchange to listeners */ + onChange() { + this.alertListeners(...arguments); + } + + /** Fired when root node added to DOM */ + onAdded() { + const reactElement = modules__WEBPACK_IMPORTED_MODULE_1__.DiscordModules.ReactDOM.render(modules__WEBPACK_IMPORTED_MODULE_1__.DiscordModules.React.createElement(ReactSetting, Object.assign({ + title: this.name, + type: this.type, + note: this.note, + }, this.props)), this.getElement()); + + if (this.props.onChange) reactElement.props.onChange = this.props.onChange(reactElement); + reactElement.forceUpdate(); + } + + /** Fired when root node removed from DOM */ + onRemoved() { + modules__WEBPACK_IMPORTED_MODULE_1__.DiscordModules.ReactDOM.unmountComponentAtNode(this.getElement()); + } +} + +/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (SettingField); + +class ReactSetting extends modules__WEBPACK_IMPORTED_MODULE_1__.DiscordModules.React.Component { + get noteElement() { + const className = this.props.noteOnTop ? modules__WEBPACK_IMPORTED_MODULE_1__.DiscordClasses.Margins.marginBottom8 : modules__WEBPACK_IMPORTED_MODULE_1__.DiscordClasses.Margins.marginTop8; + return modules__WEBPACK_IMPORTED_MODULE_1__.DiscordModules.React.createElement(modules__WEBPACK_IMPORTED_MODULE_1__.DiscordModules.SettingsNote, {children: this.props.note, type: "description", className: className.toString()}); + } + + get dividerElement() {return modules__WEBPACK_IMPORTED_MODULE_1__.DiscordModules.React.createElement("div", {className: modules__WEBPACK_IMPORTED_MODULE_1__.DiscordClasses.Dividers.divider.add(modules__WEBPACK_IMPORTED_MODULE_1__.DiscordClasses.Margins.marginTop20).toString()});} + + render() { + const ce = modules__WEBPACK_IMPORTED_MODULE_1__.DiscordModules.React.createElement; + const SettingElement = ce(this.props.type, this.props); + if (this.props.inline) { + const Flex = modules__WEBPACK_IMPORTED_MODULE_1__.DiscordModules.FlexChild; + const titleDefault = modules__WEBPACK_IMPORTED_MODULE_1__.WebpackModules.getByProps("titleDefault") ? modules__WEBPACK_IMPORTED_MODULE_1__.WebpackModules.getByProps("titleDefault").title : "titleDefault-a8-ZSr title-31JmR4"; + return ce(Flex, {direction: Flex.Direction.VERTICAL, className: modules__WEBPACK_IMPORTED_MODULE_1__.DiscordClasses.Margins.marginTop20.toString()}, + ce(Flex, {align: Flex.Align.START}, + ce(Flex.Child, {wrap: !0}, + ce("div", {className: titleDefault}, this.props.title) + ), + ce(Flex.Child, {grow: 0, shrink: 0}, SettingElement) + ), + this.noteElement, + this.dividerElement + ); + } + + return ce(modules__WEBPACK_IMPORTED_MODULE_1__.DiscordModules.SettingsWrapper, { + className: modules__WEBPACK_IMPORTED_MODULE_1__.DiscordClasses.Margins.marginTop20.toString(), + title: this.props.title, + children: [ + this.props.noteOnTop ? this.noteElement : SettingElement, + this.props.noteOnTop ? SettingElement : this.noteElement, + this.dividerElement + ] + }); + } +} + + + +/***/ }), + +/***/ "./src/ui/settings/settinggroup.js": +/*!*****************************************!*\ + !*** ./src/ui/settings/settinggroup.js ***! + \*****************************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__) +/* harmony export */ }); +/* harmony import */ var _structs_listenable__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../structs/listenable */ "./src/structs/listenable.js"); +/* harmony import */ var modules__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! modules */ "./src/modules/modules.js"); +/* harmony import */ var _settingfield__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./settingfield */ "./src/ui/settings/settingfield.js"); + + + + +/** + * Grouping of controls for easier management in settings panels. + * @memberof module:Settings + */ +class SettingGroup extends _structs_listenable__WEBPACK_IMPORTED_MODULE_0__["default"] { + /** + * @param {string} groupName - title for the group of settings + * @param {object} [options] - additional options for the group + * @param {callback} [options.callback] - callback called on settings changed + * @param {boolean} [options.collapsible=true] - determines if the group should be collapsible + * @param {boolean} [options.shown=false] - determines if the group should be expanded by default + */ + constructor(groupName, options = {}) { + super(); + const {collapsible = true, shown = false, callback = () => {}} = options; + this.addListener(callback); + this.onChange = this.onChange.bind(this); + + const collapsed = shown || !collapsible ? "" : "collapsed"; + const group = modules__WEBPACK_IMPORTED_MODULE_1__.DOMTools.parseHTML(`
+

+ ${groupName} +

+
+
`); + const label = group.querySelector("h2"); + const controls = group.querySelector(".plugin-inputs"); + + this.group = group; + this.label = label; + this.controls = controls; + + if (!collapsible) return; + label.addEventListener("click", async () => { + const button = label.querySelector(".button-collapse"); + const wasCollapsed = button.classList.contains("collapsed"); + group.parentElement.querySelectorAll(":scope > .plugin-input-group > .collapsible:not(.collapsed)").forEach((element) => { + element.style.setProperty("height", element.scrollHeight + "px"); + element.classList.add("collapsed"); + setImmediate(() => {element.style.setProperty("height", "");}); + }); + group.parentElement.querySelectorAll(":scope > .plugin-input-group > h2 > .button-collapse").forEach(e => e.classList.add("collapsed")); + if (!wasCollapsed) return; + controls.style.setProperty("height", controls.scrollHeight + "px"); + controls.classList.remove("collapsed"); + button.classList.remove("collapsed"); + await new Promise(resolve => setTimeout(resolve, 300)); + controls.style.setProperty("height", ""); + }); + } + + /** @returns {HTMLElement} - root node for the group. */ + getElement() {return this.group;} + + /** + * Adds multiple nodes to this group. + * @param {(...HTMLElement|...jQuery|...module:Settings.SettingField|...module:Settings.SettingGroup)} nodes - list of nodes to add to the group container + * @returns {module:Settings.SettingGroup} - returns self for chaining + */ + append(...nodes) { + for (let i = 0; i < nodes.length; i++) { + if (modules__WEBPACK_IMPORTED_MODULE_1__.DOMTools.resolveElement(nodes[i]) instanceof Element) this.controls.append(nodes[i]); + else if (nodes[i] instanceof _settingfield__WEBPACK_IMPORTED_MODULE_2__["default"] || nodes[i] instanceof SettingGroup) this.controls.append(nodes[i].getElement()); + if (nodes[i] instanceof _settingfield__WEBPACK_IMPORTED_MODULE_2__["default"]) { + nodes[i].addListener(((node) => (value) => { + this.onChange(node.id || node.name, value); + })(nodes[i])); + } + else if (nodes[i] instanceof SettingGroup) { + nodes[i].addListener(((node) => (settingId, value) => { + this.onChange(node.id || node.name, settingId, value); + })(nodes[i])); + } + } + return this; + } + + /** + * Appends this node to another + * @param {HTMLElement} node - node to attach the group to. + * @returns {module:Settings.SettingGroup} - returns self for chaining + */ + appendTo(node) { + node.append(this.group); + return this; + } + + /** Fires onchange to listeners */ + onChange() { + this.alertListeners(...arguments); + } +} + +/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (SettingGroup); + +/***/ }), + +/***/ "./src/ui/settings/settingpanel.js": +/*!*****************************************!*\ + !*** ./src/ui/settings/settingpanel.js ***! + \*****************************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__) +/* harmony export */ }); +/* harmony import */ var _structs_listenable__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../structs/listenable */ "./src/structs/listenable.js"); +/* harmony import */ var modules__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! modules */ "./src/modules/modules.js"); +/* harmony import */ var _settingfield__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./settingfield */ "./src/ui/settings/settingfield.js"); +/* harmony import */ var _settinggroup__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./settinggroup */ "./src/ui/settings/settinggroup.js"); + + + + + +/** + * Grouping of controls for easier management in settings panels. + * @memberof module:Settings + */ +class SettingPanel extends _structs_listenable__WEBPACK_IMPORTED_MODULE_0__["default"] { + + /** + * Creates a new settings panel + * @param {callable} onChange - callback to fire when settings change + * @param {(...HTMLElement|...jQuery|...module:Settings.SettingField|...module:Settings.SettingGroup)} nodes - list of nodes to add to the panel container + */ + constructor(onChange, ...nodes) { + super(); + this.element = modules__WEBPACK_IMPORTED_MODULE_1__.DOMTools.parseHTML(`
`); + if (typeof(onChange) == "function") this.addListener(onChange); + this.onChange = this.onChange.bind(this); + this.append(...nodes); + } + + /** + * Creates a new settings panel + * @param {callable} onChange - callback to fire when settings change + * @param {(...HTMLElement|...jQuery|...module:Settings.SettingField|...module:Settings.SettingGroup)} nodes - list of nodes to add to the panel container + * @returns {HTMLElement} - root node for the panel. + */ + static build(onChange, ...nodes) { + return (new SettingPanel(onChange, ...nodes)).getElement(); + } + + /** @returns {HTMLElement} - root node for the panel. */ + getElement() {return this.element;} + + /** + * Adds multiple nodes to this panel. + * @param {(...HTMLElement|...jQuery|...SettingField|...SettingGroup)} nodes - list of nodes to add to the panel container + * @returns {module:Settings.SettingPanel} - returns self for chaining + */ + append(...nodes) { + for (let i = 0; i < nodes.length; i++) { + if (modules__WEBPACK_IMPORTED_MODULE_1__.DOMTools.resolveElement(nodes[i]) instanceof Element) this.element.append(nodes[i]); + else if (nodes[i] instanceof _settingfield__WEBPACK_IMPORTED_MODULE_2__["default"] || nodes[i] instanceof _settinggroup__WEBPACK_IMPORTED_MODULE_3__["default"]) this.element.append(nodes[i].getElement()); + if (nodes[i] instanceof _settingfield__WEBPACK_IMPORTED_MODULE_2__["default"]) { + nodes[i].addListener(((node) => (value) => { + this.onChange(node.id || node.name, value); + })(nodes[i])); + } + else if (nodes[i] instanceof _settinggroup__WEBPACK_IMPORTED_MODULE_3__["default"]) { + nodes[i].addListener(((node) => (settingId, value) => { + this.onChange(node.id || node.name, settingId, value); + })(nodes[i])); + } + } + return this; + } + + /** Fires onchange to listeners */ + onChange() { + this.alertListeners(...arguments); + } +} + +/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (SettingPanel); + +/***/ }), + +/***/ "./src/ui/settings/types/color.js": +/*!****************************************!*\ + !*** ./src/ui/settings/types/color.js ***! + \****************************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__) +/* harmony export */ }); +/* harmony import */ var _settingfield__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../settingfield */ "./src/ui/settings/settingfield.js"); +/* harmony import */ var modules__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! modules */ "./src/modules/modules.js"); +/* harmony import */ var _colorpicker__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../../colorpicker */ "./src/ui/colorpicker.js"); + + + + + + +const presetColors = [1752220, 3066993, 3447003, 10181046, 15277667, 15844367, 15105570, 15158332, 9807270, 6323595, 1146986, 2067276, 2123412, 7419530, 11342935, 12745742, 11027200, 10038562, 9936031, 5533306]; + +/** + * Creates a color picker using Discord's built in color picker + * as a base. Input and output using hex strings. + * @memberof module:Settings + * @extends module:Settings.SettingField + */ +class ColorPicker extends _settingfield__WEBPACK_IMPORTED_MODULE_0__["default"] { + /** + * @param {string} name - name label of the setting + * @param {string} note - help/note to show underneath or above the setting + * @param {string} value - current hex color + * @param {callable} onChange - callback to perform on setting change, callback receives hex string + * @param {object} [options] - object of options to give to the setting + * @param {boolean} [options.disabled=false] - should the setting be disabled + * @param {string} [options.defaultColor] - default color to show as large option + * @param {Array} [options.colors] - preset colors to show in swatch + */ + constructor(name, note, value, onChange, options = {}) { + const ColorPickerComponents = modules__WEBPACK_IMPORTED_MODULE_1__.WebpackModules.getByProps("CustomColorPicker"); + if (ColorPickerComponents) { + const defaultColor = options.defaultColor; + super(name, note, onChange, _colorpicker__WEBPACK_IMPORTED_MODULE_2__["default"], { + disabled: !!options.disabled, + onChange: reactElement => color => { + reactElement.props.value = color; + reactElement.forceUpdate(); + this.onChange(modules__WEBPACK_IMPORTED_MODULE_1__.ColorConverter.int2hex(color)); + }, + colors: Array.isArray(options.colors) ? options.colors : presetColors, + defaultColor: defaultColor && typeof(defaultColor) !== "number" ? modules__WEBPACK_IMPORTED_MODULE_1__.ColorConverter.hex2int(defaultColor) : defaultColor, + value: typeof(value) == "number" ? value : modules__WEBPACK_IMPORTED_MODULE_1__.ColorConverter.hex2int(value), + customPickerPosition: "right" + }); + } + else { + const classes = ["color-input"]; + if (options.disabled) classes.push(modules__WEBPACK_IMPORTED_MODULE_1__.DiscordClasses.BasicInputs.disabled); + const ReactColorPicker = modules__WEBPACK_IMPORTED_MODULE_1__.DOMTools.parseHTML(``); + if (options.disabled) ReactColorPicker.setAttribute("disabled", ""); + if (value) ReactColorPicker.setAttribute("value", value); + ReactColorPicker.addEventListener("change", (event) => { + this.onChange(event.target.value); + }); + super(name, note, onChange, ReactColorPicker, {inline: true}); + } + } + + /** Default colors for ColorPicker */ + static get presetColors() {return presetColors;} +} + + + +/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (ColorPicker); + +/***/ }), + +/***/ "./src/ui/settings/types/dropdown.js": +/*!*******************************************!*\ + !*** ./src/ui/settings/types/dropdown.js ***! + \*******************************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__) +/* harmony export */ }); +/* harmony import */ var _settingfield__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../settingfield */ "./src/ui/settings/settingfield.js"); +/* harmony import */ var modules__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! modules */ "./src/modules/modules.js"); + + + +/** + * @interface + * @name module:Settings~DropdownItem + * @property {string|ReactElement} label - label to show in the dropdown + * @property {*} value - actual value represented by label (this is passed via onChange) + */ + + const React = modules__WEBPACK_IMPORTED_MODULE_1__.DiscordModules.React; + + class CloseButton extends React.Component { + render() { + const size = this.props.size || "14px"; + return React.createElement("svg", {className: this.props.className || "", fill: "currentColor", viewBox: "0 0 24 24", style: {width: size, height: size}, onClick: this.props.onClick}, + React.createElement("path", {d: "M18.4 4L12 10.4L5.6 4L4 5.6L10.4 12L4 18.4L5.6 20L12 13.6L18.4 20L20 18.4L13.6 12L20 5.6L18.4 4Z"}) + ); + } +} + + class DownArrow extends React.Component { + render() { + const size = this.props.size || "16px"; + return React.createElement("svg", {className: this.props.className || "", fill: "currentColor", viewBox: "0 0 24 24", style: {width: size, height: size}, onClick: this.props.onClick}, + React.createElement("path", {d: "M8.12 9.29L12 13.17l3.88-3.88c.39-.39 1.02-.39 1.41 0 .39.39.39 1.02 0 1.41l-4.59 4.59c-.39.39-1.02.39-1.41 0L6.7 10.7c-.39-.39-.39-1.02 0-1.41.39-.38 1.03-.39 1.42 0z"}) + ); + } +} + +// + +class Select extends React.Component { + constructor(props) { + super(props); + this.state = {open: false, value: this.props.value}; + this.dropdown = React.createRef(); + this.onChange = this.onChange.bind(this); + this.showMenu = this.showMenu.bind(this); + this.hideMenu = this.hideMenu.bind(this); + this.clear = this.clear.bind(this); + } + + showMenu(event) { + event.preventDefault(); + event.stopPropagation(); + + this.setState((state) => ({open: !state.open}), () => { + if (!this.state.open) return; + + document.addEventListener("click", this.hideMenu); + }); + } + + hideMenu() { + this.setState({open: false}, () => { + document.removeEventListener("click", this.hideMenu); + }); + } + + onChange(value) { + this.setState({value}); + if (this.props.onChange) this.props.onChange(value); + } + + get selected() {return this.props.options.find(o => o.value == this.state.value);} + + get options() { + const selected = this.selected; + return React.createElement("div", {className: "z-select-options"}, + this.props.options.map(opt => + React.createElement("div", {className: `z-select-option${selected?.value == opt.value ? " selected" : ""}`, onClick: this.onChange.bind(this, opt.value)}, opt.label) + ) + ); + } + + clear(event) { + event.stopPropagation(); + this.onChange(null); + } + + render() { + const style = this.props.style == "transparent" ? " z-select-transparent" : ""; + const isOpen = this.state.open ? " menu-open" : ""; + return React.createElement("div", {className: `z-select${style}${isOpen}`, ref: this.dropdown, onClick: this.showMenu}, [ + React.createElement("div", {className: "z-select-value"}, this?.selected?.label ?? this.props.placeholder), + React.createElement("div", {className: "z-select-icons"}, + this.props.clearable && this.selected && React.createElement(CloseButton, {className: "z-select-clear", onClick: this.clear}), + React.createElement(DownArrow, {className: "z-select-arrow"}), + ), + this.state.open && this.options + ]); + } +} + +/** + * Creates a dropdown using discord's built in dropdown. + * @memberof module:Settings + * @extends module:Settings.SettingField + */ +class Dropdown extends _settingfield__WEBPACK_IMPORTED_MODULE_0__["default"] { + /** + * @param {string} name - name label of the setting + * @param {string} note - help/note to show underneath or above the setting + * @param {*} defaultValue - currently selected value + * @param {Array} values - array of all options available + * @param {callable} onChange - callback to perform on setting change, callback item value + * @param {object} [options] - object of options to give to the setting + * @param {boolean} [options.clearable=false] - should be able to empty the field value + * @param {string} [options.placeholder=""] - Placeholder to show when no option is selected, useful when clearable + * @param {boolean} [options.disabled=false] - should the setting be disabled + */ + constructor(name, note, defaultValue, values, onChange, options = {}) { + const {clearable = false, disabled = false, placeholder = ""} = options; + super(name, note, onChange, Select, { + placeholder: placeholder, + clearable: clearable, + disabled: disabled, + options: values, + onChange: dropdown => value => { + dropdown.props.value = value; + dropdown.forceUpdate(); + this.onChange(value); + }, + value: defaultValue + }); + } +} + +/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (Dropdown); + +/***/ }), + +/***/ "./src/ui/settings/types/file.js": +/*!***************************************!*\ + !*** ./src/ui/settings/types/file.js ***! + \***************************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__) +/* harmony export */ }); +/* harmony import */ var _settingfield__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../settingfield */ "./src/ui/settings/settingfield.js"); +/* harmony import */ var modules__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! modules */ "./src/modules/modules.js"); + + + +/** + * Creates a file picker using chromium's default. + * @memberof module:Settings + * @extends module:Settings.SettingField + */ +class FilePicker extends _settingfield__WEBPACK_IMPORTED_MODULE_0__["default"] { + /** + * @param {string} name - name label of the setting + * @param {string} note - help/note to show underneath or above the setting + * @param {callable} onChange - callback to perform on setting change, callback receives File object + * @param {object} [options] - object of options to give to the setting + * @param {boolean} [options.disabled=false] - should the setting be disabled + * @param {Array|string} [options.accept] - what file types should be accepted + * @param {boolean} [options.multiple=false] - should multiple files be accepted + */ + constructor(name, note, onChange, options = {}) { + const classes = modules__WEBPACK_IMPORTED_MODULE_1__.DiscordClasses.BasicInputs.inputDefault.add("file-input"); + if (options.disabled) classes.add(modules__WEBPACK_IMPORTED_MODULE_1__.DiscordClasses.BasicInputs.disabled); + const ReactFilePicker = modules__WEBPACK_IMPORTED_MODULE_1__.DOMTools.parseHTML(``); + if (options.disabled) ReactFilePicker.setAttribute("disabled", ""); + if (options.multiple) ReactFilePicker.setAttribute("multiple", ""); + if (options.accept) ReactFilePicker.setAttribute("accept", Array.isArray(options.accept) ? options.accept.join(",") : options.accept); + ReactFilePicker.addEventListener("change", (event) => { + this.onChange(event.target.files[0]); + }); + super(name, note, onChange, ReactFilePicker); + } +} + +/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (FilePicker); + +/***/ }), + +/***/ "./src/ui/settings/types/keybind.js": +/*!******************************************!*\ + !*** ./src/ui/settings/types/keybind.js ***! + \******************************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__) +/* harmony export */ }); +/* harmony import */ var _settingfield__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../settingfield */ "./src/ui/settings/settingfield.js"); +/* harmony import */ var modules__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! modules */ "./src/modules/modules.js"); + + + +const React = modules__WEBPACK_IMPORTED_MODULE_1__.DiscordModules.React; + +class CloseButton extends React.Component { + render() { + const size = this.props.size || "16px"; + return React.createElement("svg", {className: this.props.className || "", fill: "currentColor", viewBox: "0 0 24 24", style: {width: size, height: size}, onClick: this.props.onClick}, + React.createElement("path", {d: "M18.4 4L12 10.4L5.6 4L4 5.6L10.4 12L4 18.4L5.6 20L12 13.6L18.4 20L20 18.4L13.6 12L20 5.6L18.4 4Z"}) + ); + } +} + +const toCombo = modules__WEBPACK_IMPORTED_MODULE_1__.WebpackModules.getModule(m => m?.toString?.()?.includes("numpad plus"), {searchExports: true}) ?? (() => [[0, 0], [0, 0]]); +const toEvent = modules__WEBPACK_IMPORTED_MODULE_1__.WebpackModules.getModule(m => m?.toString?.()?.includes("keyCode") && m?.toString?.()?.includes("BROWSER"), {searchExports: true}) ?? (() => ({})); + +class ClearableKeybind extends React.Component { + constructor(props) { + super(props); + + this.state = {value: this.props.defaultValue}; + this.clear = this.clear.bind(this); + } + + clear() { + this.setState({value: []}); + this.props.onChange([]); + } + + render() { + return React.createElement("div", {className: "z-keybind-wrapper"}, + React.createElement(modules__WEBPACK_IMPORTED_MODULE_1__.DiscordModules.Keybind, { + disabled: this.props.disabled, + defaultValue: this.state.value, + onChange: this.props.onChange + }), + React.createElement(CloseButton, {className: "z-keybind-clear", onClick: this.clear}) + ); + } +} + +/** + * Creates a keybind setting using discord's built in keybind recorder. + * @memberof module:Settings= + * @extends module:Settings.SettingField + */ +class Keybind extends _settingfield__WEBPACK_IMPORTED_MODULE_0__["default"] { + /** + * @param {string} name - name label of the setting + * @param {string} note - help/note to show underneath or above the setting + * @param {Array} value - array of key names + * @param {callable} onChange - callback to perform on setting change, callback receives array of keycodes + * @param {object} [options] - object of options to give to the setting + * @param {boolean} [options.disabled=false] - should the setting be disabled + */ + constructor(label, help, value, onChange, options = {}) { + const {disabled = false} = options; + if (!Array.isArray(value) || value.some(v => typeof(v) !== "string")) value = []; // if non-strings present, not a valid combo + super(label, help, onChange, ClearableKeybind, { + disabled: disabled, + defaultValue: toCombo(value.join("+")) ?? [], + onChange: element => val => { + if (!Array.isArray(val)) return; + element.props.value = val; + this.onChange(toEvent(val)); + } + }); + } +} + +/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (Keybind); + +/***/ }), + +/***/ "./src/ui/settings/types/radiogroup.js": +/*!*********************************************!*\ + !*** ./src/ui/settings/types/radiogroup.js ***! + \*********************************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__) +/* harmony export */ }); +/* harmony import */ var _settingfield__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../settingfield */ "./src/ui/settings/settingfield.js"); +/* harmony import */ var modules__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! modules */ "./src/modules/modules.js"); + + + +/** + * @interface + * @name module:Settings~RadioItem + * @property {string} name - label to show in the dropdown + * @property {*} value - actual value represented by label (this is passed via onChange) + * @property {string} desc - description/help text to show below name + * @property {string} color - hex string to color the item + */ + +/** + * Creates a radio group using discord's built in radios. + * @memberof module:Settings + * @extends module:Settings.SettingField + */ +class RadioGroup extends _settingfield__WEBPACK_IMPORTED_MODULE_0__["default"] { + /** + * @param {string} name - name label of the setting + * @param {string} note - help/note to show underneath or above the setting + * @param {*} defaultValue - currently selected value + * @param {Array} values - array of all options available + * @param {callable} onChange - callback to perform on setting change, callback item value + * @param {object} [options] - object of options to give to the setting + * @param {boolean} [options.disabled=false] - should the setting be disabled + */ + constructor(name, note, defaultValue, values, onChange, options = {}) { + super(name, note, onChange, modules__WEBPACK_IMPORTED_MODULE_1__.DiscordModules.RadioGroup, { + noteOnTop: true, + disabled: !!options.disabled, + options: values, + onChange: reactElement => option => { + reactElement.props.value = option.value; + reactElement.forceUpdate(); + this.onChange(option.value); + }, + value: defaultValue + }); + } +} + +/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (RadioGroup); + + + +/***/ }), + +/***/ "./src/ui/settings/types/slider.js": +/*!*****************************************!*\ + !*** ./src/ui/settings/types/slider.js ***! + \*****************************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__) +/* harmony export */ }); +/* harmony import */ var _settingfield__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../settingfield */ "./src/ui/settings/settingfield.js"); +/* harmony import */ var modules__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! modules */ "./src/modules/modules.js"); + + + +/** + * Used to render the marker. + * @param {Number} value - The value to render + * @returns {string} the text to show in the marker + * @callback module:Settings~SliderMarkerValue + */ + +/** + * Used to render the grabber tooltip. + * @param {Number} value - The value to render + * @returns {string} the text to show in the tooltip + * @callback module:Settings~SliderRenderValue + */ + +/** + * Creates a slider/range using discord's built in slider. + * @memberof module:Settings + * @extends module:Settings.SettingField + */ +class Slider extends _settingfield__WEBPACK_IMPORTED_MODULE_0__["default"] { + /** + * + * @param {string} name - name label of the setting + * @param {string} note - help/note to show underneath or above the setting + * @param {number} min - minimum value allowed + * @param {number} max - maximum value allowed + * @param {number} value - currently selected value + * @param {callable} onChange - callback to fire when setting is changed, callback receives number + * @param {object} [options] - object of options to give to the setting + * @param {boolean} [options.disabled=false] - should the setting be disabled + * @param {object} [options.fillStyles] - object of css styles to add to active slider + * @param {number} [options.defaultValue] - value highlighted as default + * @param {number} [options.keyboardStep] - step moved when using arrow keys + * @param {Array} [options.markers] - array of vertical markers to show on the slider + * @param {boolean} [options.stickToMarkers] - should the slider be forced to use markers + * @param {boolean} [options.equidistant] - should the markers be scaled to be equidistant + * @param {module:Settings~SliderMarkerValue} [options.onMarkerRender] - function to call to render the value in the marker + * @param {module:Settings~SliderMarkerValue} [options.renderMarker] - alias of `onMarkerRender` + * @param {module:Settings~SliderRenderValue} [options.onValueRender] - function to call to render the value in the tooltip + * @param {module:Settings~SliderRenderValue} [options.renderValue] - alias of `onValueRender` + * @param {string} [options.units] - can be used in place of `onValueRender` will use this string and render Math.round(value) + units + */ + constructor(name, note, min, max, value, onChange, options = {}) { + const props = { + onChange: _ => _, + initialValue: value, + disabled: !!options.disabled, + minValue: min, + maxValue: max, + handleSize: 10 + }; + if (options.fillStyles) props.fillStyles = options.fillStyles; + if (typeof(options.defaultValue) !== "undefined") props.defaultValue = options.defaultValue; + if (options.keyboardStep) props.keyboardStep = options.keyboardStep; + if (options.markers) props.markers = options.markers; + if (options.stickToMarkers) props.stickToMarkers = options.stickToMarkers; + if (typeof(options.equidistant) != "undefined") props.equidistant = options.equidistant; + if (options.units) { + const renderValueLabel = (val) => `${Math.round(val)}${options.units}`; + props.onMarkerRender = renderValueLabel; + props.onValueRender = renderValueLabel; + } + if (options.onMarkerRender || options.renderMarker) props.onMarkerRender = options.onMarkerRender || options.renderMarker; + if (options.onValueRender || options.renderValue) props.onValueRender = options.onValueRender || options.renderValue; + super(name, note, onChange, modules__WEBPACK_IMPORTED_MODULE_1__.DiscordModules.Slider, Object.assign(props, {onValueChange: v => this.onChange(v)})); + } +} + +/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (Slider); + +/***/ }), + +/***/ "./src/ui/settings/types/switch.js": +/*!*****************************************!*\ + !*** ./src/ui/settings/types/switch.js ***! + \*****************************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__) +/* harmony export */ }); +/* harmony import */ var _settingfield__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../settingfield */ "./src/ui/settings/settingfield.js"); +/* harmony import */ var modules__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! modules */ "./src/modules/modules.js"); + + + + +const {useCallback, useState, createElement} = modules__WEBPACK_IMPORTED_MODULE_1__.DiscordModules.React; + +function SwitchComponent({id, checked: initialValue, disabled, onChange}) { + const [checked, setChecked] = useState(initialValue); + const change = useCallback(() => { + onChange?.(!checked); + setChecked(!checked); + }, [checked, onChange]); + + const enabledClass = disabled ? " bd-switch-disabled" : ""; + const checkedClass = checked ? " bd-switch-checked" : ""; + return createElement("div", {className: `bd-switch` + enabledClass + checkedClass}, + createElement("input", {id: id, type: "checkbox", disabled: disabled, checked: checked, onChange: change}), + createElement("div", {className: "bd-switch-body"}, + createElement("svg", {className: "bd-switch-slider", viewBox: "0 0 28 20", preserveAspectRatio: "xMinYMid meet"}, + createElement("rect", {className: "bd-switch-handle", fill: "white", x: "4", y: "0", height: "20", width: "20", rx: "10"}), + createElement("svg", {className: "bd-switch-symbol", viewBox: "0 0 20 20", fill: "none"}, + createElement("path"), + createElement("path") + ) + ) + ) + ); +} + +/** + * Creates a switch using discord's built in switch. + * @memberof module:Settings + * @extends module:Settings.SettingField + */ +class Switch extends _settingfield__WEBPACK_IMPORTED_MODULE_0__["default"] { + /** + * @param {string} name - name label of the setting + * @param {string} note - help/note to show underneath or above the setting + * @param {boolean} isChecked - should switch be checked + * @param {callable} onChange - callback to perform on setting change, callback receives boolean + * @param {object} [options] - object of options to give to the setting + * @param {boolean} [options.disabled=false] - should the setting be disabled + */ + constructor(name, note, isChecked, onChange, options = {}) { + const props = { + disabled: !!options.disabled, + checked: !!isChecked, + onChange: () => value => this.onChange(value), + inline: true + }; + + super(name, note, onChange, SwitchComponent, props); + } +} + +/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (Switch); + + +/***/ }), + +/***/ "./src/ui/settings/types/textbox.js": +/*!******************************************!*\ + !*** ./src/ui/settings/types/textbox.js ***! + \******************************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__) +/* harmony export */ }); +/* harmony import */ var _settingfield__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../settingfield */ "./src/ui/settings/settingfield.js"); +/* harmony import */ var modules__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! modules */ "./src/modules/modules.js"); + + + +/** + * Creates a textbox using discord's built in textbox. + * @memberof module:Settings + * @extends module:Settings.SettingField + */ +class Textbox extends _settingfield__WEBPACK_IMPORTED_MODULE_0__["default"] { + /** + * @param {string} name - name label of the setting + * @param {string} note - help/note to show underneath or above the setting + * @param {string} value - current text in box + * @param {callable} onChange - callback to perform on setting change, callback receives text + * @param {object} [options] - object of options to give to the setting + * @param {string} [options.placeholder=""] - placeholder for when textbox is empty + * @param {boolean} [options.disabled=false] - should the setting be disabled + */ + constructor(name, note, value, onChange, options = {}) { + const {placeholder = "", disabled = false} = options; + super(name, note, onChange, modules__WEBPACK_IMPORTED_MODULE_1__.DiscordModules.Textbox, { + onChange: textbox => val => { + textbox.props.value = val; + textbox.forceUpdate(); + this.onChange(val); + }, + value: value, + disabled: disabled, + placeholder: placeholder || "" + }); + } +} + +/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (Textbox); + +/***/ }), + +/***/ "./src/ui/toasts.js": +/*!**************************!*\ + !*** ./src/ui/toasts.js ***! + \**************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "default": () => (/* binding */ Toasts) +/* harmony export */ }); +/* harmony import */ var modules__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! modules */ "./src/modules/modules.js"); +/* harmony import */ var ui__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ui */ "./src/ui/ui.js"); +/* harmony import */ var _styles_toasts_css__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../styles/toasts.css */ "./src/styles/toasts.css"); +/** + * Toast maker similar to Android. + * + * @module Toasts + */ + + + + + +class Toasts { + + static get CSS() {return _styles_toasts_css__WEBPACK_IMPORTED_MODULE_2__["default"];} + + /** Shorthand for `type = "success"` for {@link module:Toasts.show} */ + static async success(content, options = {}) {return this.show(content, Object.assign(options, {type: "success"}));} + + /** Shorthand for `type = "info"` for {@link module:Toasts.show} */ + static async info(content, options = {}) {return this.show(content, Object.assign(options, {type: "info"}));} + + /** Shorthand for `type = "warning"` for {@link module:Toasts.show} */ + static async warning(content, options = {}) {return this.show(content, Object.assign(options, {type: "warning"}));} + + /** Shorthand for `type = "error"` for {@link module:Toasts.show} */ + static async error(content, options = {}) {return this.show(content, Object.assign(options, {type: "error"}));} + + /** Shorthand for `type = "default"` for {@link module:Toasts.show} */ + static async default(content, options = {}) {return this.show(content, Object.assign(options, {type: "default"}));} + + + /** + * Shows a simple toast, similar to Android, centered over + * the textarea if it exists, and center screen otherwise. + * Vertically it shows towards the bottom like in Android. + * @param {string} content - The string to show in the toast. + * @param {object} options - additional options for the toast + * @param {string} [options.type] - Changes the type of the toast stylistically and semantically. {@link module:Toasts.ToastTypes} + * @param {string} [options.icon] - URL to an optional icon + * @param {number} [options.timeout=3000] - Adjusts the time (in ms) the toast should be shown for before disappearing automatically + * @returns {Promise} - Promise that resolves when the toast is removed from the DOM + */ + static async show(content, options = {}) { + const {type = "", icon = "", timeout = 3000} = options; + this.ensureContainer(); + const toast = modules__WEBPACK_IMPORTED_MODULE_0__.DOMTools.parseHTML(this.buildToast(content, this.parseType(type), icon)); + document.querySelector(".toasts").appendChild(toast); + await new Promise(resolve => setTimeout(resolve, timeout)); + toast.classList.add("closing"); + await new Promise(resolve => setTimeout(resolve, 300)); + toast.remove(); + if (!document.querySelectorAll(".toasts .toast").length) document.querySelector(".toasts").remove(); + } + + static buildToast(message, type, icon) { + const hasIcon = type || icon; + const className = `toast ${hasIcon ? "toast-has-icon" : ""} ${type && type != "default" ? `toast-${type}` : ""}`; + if (!icon && type) icon = type; + return modules__WEBPACK_IMPORTED_MODULE_0__.Utilities.formatString(`
{{icon}}
{{message}}
`, { + className: className, + icon: hasIcon ? this.getIcon(icon) : "", + message: message + }); + } + + static getIcon(icon) { + let iconInner = ``; + switch (icon) { + case "success": iconInner = ui__WEBPACK_IMPORTED_MODULE_1__.Icons.IconSuccess(20); break; // eslint-disable-line new-cap + case "warning": iconInner = ui__WEBPACK_IMPORTED_MODULE_1__.Icons.IconWarning(20); break; // eslint-disable-line new-cap + case "info": iconInner = ui__WEBPACK_IMPORTED_MODULE_1__.Icons.IconInfo(20); break; // eslint-disable-line new-cap + case "error": iconInner = ui__WEBPACK_IMPORTED_MODULE_1__.Icons.IconError(20); // eslint-disable-line new-cap + } + return modules__WEBPACK_IMPORTED_MODULE_0__.Utilities.formatString(`
{{icon}}
`, {icon: iconInner}); + } + + static ensureContainer() { + if (document.querySelector(".toasts")) return; + const channelClass = modules__WEBPACK_IMPORTED_MODULE_0__.DiscordSelectors.ChannelList.sidebar; + const container = channelClass ? document.querySelector(`${channelClass} ~ div:not([style])`) : null; + const memberlist = container ? container.querySelector(modules__WEBPACK_IMPORTED_MODULE_0__.DiscordSelectors.MemberList.membersWrap) : null; + const form = container ? container.querySelector("form") : null; + const left = container ? container.getBoundingClientRect().left : 310; + const right = memberlist ? memberlist.getBoundingClientRect().left : 0; + const width = right ? right - container.getBoundingClientRect().left : (container?.offsetWidth ?? document.body.offsetWidth / 2); + const bottom = form ? form.offsetHeight : 80; + const toastWrapper = document.createElement("div"); + toastWrapper.classList.add("toasts"); + toastWrapper.style.setProperty("left", left + "px"); + toastWrapper.style.setProperty("width", width + "px"); + toastWrapper.style.setProperty("bottom", bottom + "px"); + document.querySelector("#app-mount").appendChild(toastWrapper); + } + + static parseType(type) { + return this.ToastTypes.hasOwnProperty(type) ? this.ToastTypes[type] : ""; + } + + /** + * Enumeration of accepted types. + */ + static get ToastTypes() { + return { + "default": "", + "error": "error", + "success": "success", + "warning": "warning", + "info": "info" + }; + } +} + +/***/ }), + +/***/ "./src/ui/tooltip.js": +/*!***************************!*\ + !*** ./src/ui/tooltip.js ***! + \***************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "default": () => (/* binding */ Tooltip) +/* harmony export */ }); +/* harmony import */ var modules__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! modules */ "./src/modules/modules.js"); +/** + * Tooltip that automatically show and hide themselves on mouseenter and mouseleave events. + * Will also remove themselves if the node to watch is removed from DOM through + * a MutationObserver. + * + * Note this is not using Discord's internals but normal DOM manipulation and emulates + * Discord's own tooltips as closely as possible. + * + * @module Tooltip + */ + + + +const getClass = function(sideOrColor) { + const upperCase = sideOrColor[0].toUpperCase() + sideOrColor.slice(1); + const tooltipClass = modules__WEBPACK_IMPORTED_MODULE_0__.DiscordClasses.Tooltips[`tooltip${upperCase}`]; + if (tooltipClass) return tooltipClass.value; + return null; +}; + +const classExists = function(sideOrColor) { + return !!getClass(sideOrColor); +}; + +const toPx = function(value) { + return `${value}px`; +}; + +/*
+
+
+ User Settings +
+
*/ + +class Tooltip { + /** + * + * @constructor + * @param {(HTMLElement|jQuery)} node - DOM node to monitor and show the tooltip on + * @param {string} tip - string to show in the tooltip + * @param {object} options - additional options for the tooltip + * @param {string} [options.style=black] - correlates to the discord styling/colors (black, brand, green, grey, red, yellow) + * @param {string} [options.side=top] - can be any of top, right, bottom, left + * @param {boolean} [options.preventFlip=false] - prevents moving the tooltip to the opposite side if it is too big or goes offscreen + * @param {boolean} [options.isTimestamp=false] - adds the timestampTooltip class (disables text wrapping) + * @param {boolean} [options.disablePointerEvents=false] - disables pointer events + * @param {boolean} [options.disabled=false] - whether the tooltip should be disabled from showing on hover + */ + constructor(node, text, options = {}) { + const {style = "black", side = "top", preventFlip = false, isTimestamp = false, disablePointerEvents = false, disabled = false} = options; + this.node = modules__WEBPACK_IMPORTED_MODULE_0__.DOMTools.resolveElement(node); + this.label = text; + this.style = style.toLowerCase(); + this.side = side.toLowerCase(); + this.preventFlip = preventFlip; + this.isTimestamp = isTimestamp; + this.disablePointerEvents = disablePointerEvents; + this.disabled = disabled; + this.active = false; + + if (!classExists(this.side)) return modules__WEBPACK_IMPORTED_MODULE_0__.Logger.err("Tooltip", `Side ${this.side} does not exist.`); + if (!classExists(this.style)) return modules__WEBPACK_IMPORTED_MODULE_0__.Logger.err("Tooltip", `Style ${this.style} does not exist.`); + + this.element = modules__WEBPACK_IMPORTED_MODULE_0__.DOMTools.createElement(`
`); + this.tooltipElement = modules__WEBPACK_IMPORTED_MODULE_0__.DOMTools.createElement(`
${this.label}
`); + this.labelElement = this.tooltipElement.childNodes[1]; + this.element.append(this.tooltipElement); + + if (this.disablePointerEvents) { + this.element.classList.add(modules__WEBPACK_IMPORTED_MODULE_0__.DiscordClasses.TooltipLayers.disabledPointerEvents); + this.tooltipElement.classList.add(modules__WEBPACK_IMPORTED_MODULE_0__.DiscordClasses.Tooltips.tooltipDisablePointerEvents); + } + if (this.isTimestamp) this.tooltipElement.classList.add(modules__WEBPACK_IMPORTED_MODULE_0__.WebpackModules.getByProps("timestampTooltip").timestampTooltip); + + + this.node.addEventListener("mouseenter", () => { + if (this.disabled) return; + this.show(); + }); + + this.node.addEventListener("mouseleave", () => { + this.hide(); + }); + } + + /** Alias for the constructor */ + static create(node, text, options = {}) {return new Tooltip(node, text, options);} + + /** Container where the tooltip will be appended. */ + get container() {return document.querySelector(modules__WEBPACK_IMPORTED_MODULE_0__.DiscordSelectors.App.app.sibling(modules__WEBPACK_IMPORTED_MODULE_0__.DiscordSelectors.TooltipLayers.layerContainer));} + /** Boolean representing if the tooltip will fit on screen above the element */ + get canShowAbove() {return this.node.getBoundingClientRect().top - this.element.offsetHeight >= 0;} + /** Boolean representing if the tooltip will fit on screen below the element */ + get canShowBelow() {return this.node.getBoundingClientRect().top + this.node.offsetHeight + this.element.offsetHeight <= modules__WEBPACK_IMPORTED_MODULE_0__.DOMTools.screenHeight;} + /** Boolean representing if the tooltip will fit on screen to the left of the element */ + get canShowLeft() {return this.node.getBoundingClientRect().left - this.element.offsetWidth >= 0;} + /** Boolean representing if the tooltip will fit on screen to the right of the element */ + get canShowRight() {return this.node.getBoundingClientRect().left + this.node.offsetWidth + this.element.offsetWidth <= modules__WEBPACK_IMPORTED_MODULE_0__.DOMTools.screenWidth;} + + /** Hides the tooltip. Automatically called on mouseleave. */ + hide() { + /** Don't rehide if already inactive */ + if (!this.active) return; + this.active = false; + this.element.remove(); + this.tooltipElement.className = this._className; + } + + /** Shows the tooltip. Automatically called on mouseenter. Will attempt to flip if position was wrong. */ + show() { + /** Don't reshow if already active */ + if (this.active) return; + this.active = true; + this.tooltipElement.className = `${modules__WEBPACK_IMPORTED_MODULE_0__.DiscordClasses.Tooltips.tooltip} ${getClass(this.style)}`; + if (this.disablePointerEvents) this.tooltipElement.classList.add(modules__WEBPACK_IMPORTED_MODULE_0__.DiscordClasses.Tooltips.tooltipDisablePointerEvents); + if (this.isTimestamp) this.tooltipElement.classList.add(modules__WEBPACK_IMPORTED_MODULE_0__.WebpackModules.getByProps("timestampTooltip").timestampTooltip); + this.labelElement.textContent = this.label; + this.container.append(this.element); + + if (this.side == "top") { + if (this.canShowAbove || (!this.canShowAbove && this.preventFlip)) this.showAbove(); + else this.showBelow(); + } + + if (this.side == "bottom") { + if (this.canShowBelow || (!this.canShowBelow && this.preventFlip)) this.showBelow(); + else this.showAbove(); + } + + if (this.side == "left") { + if (this.canShowLeft || (!this.canShowLeft && this.preventFlip)) this.showLeft(); + else this.showRight(); + } + + if (this.side == "right") { + if (this.canShowRight || (!this.canShowRight && this.preventFlip)) this.showRight(); + else this.showLeft(); + } + + /** Do not create a new observer each time if one already exists! */ + if (this.observer) return; + /** Use an observer in show otherwise you'll cause unclosable tooltips */ + this.observer = new MutationObserver((mutations) => { + mutations.forEach((mutation) => { + const nodes = Array.from(mutation.removedNodes); + const directMatch = nodes.indexOf(this.node) > -1; + const parentMatch = nodes.some(parent => parent.contains(this.node)); + if (directMatch || parentMatch) { + this.hide(); + this.observer.disconnect(); + } + }); + }); + + this.observer.observe(document.body, {subtree: true, childList: true}); + } + + /** Force showing the tooltip above the node. */ + showAbove() { + this.tooltipElement.classList.add(getClass("top")); + this.element.style.setProperty("top", toPx(this.node.getBoundingClientRect().top - this.element.offsetHeight - 10)); + this.centerHorizontally(); + } + + /** Force showing the tooltip below the node. */ + showBelow() { + this.tooltipElement.classList.add(getClass("bottom")); + this.element.style.setProperty("top", toPx(this.node.getBoundingClientRect().top + this.node.offsetHeight + 10)); + this.centerHorizontally(); + } + + /** Force showing the tooltip to the left of the node. */ + showLeft() { + this.tooltipElement.classList.add(getClass("left")); + this.element.style.setProperty("left", toPx(this.node.getBoundingClientRect().left - this.element.offsetWidth - 10)); + this.centerVertically(); + } + + /** Force showing the tooltip to the right of the node. */ + showRight() { + this.tooltipElement.classList.add(getClass("right")); + this.element.style.setProperty("left", toPx(this.node.getBoundingClientRect().left + this.node.offsetWidth + 10)); + this.centerVertically(); + } + + centerHorizontally() { + const nodecenter = this.node.getBoundingClientRect().left + (this.node.offsetWidth / 2); + this.element.style.setProperty("left", toPx(nodecenter - (this.element.offsetWidth / 2))); + } + + centerVertically() { + const nodecenter = this.node.getBoundingClientRect().top + (this.node.offsetHeight / 2); + this.element.style.setProperty("top", toPx(nodecenter - (this.element.offsetHeight / 2))); + } +} + +/***/ }), + +/***/ "./src/ui/ui.js": +/*!**********************!*\ + !*** ./src/ui/ui.js ***! + \**********************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "ColorPicker": () => (/* reexport safe */ _colorpicker__WEBPACK_IMPORTED_MODULE_8__["default"]), +/* harmony export */ "DiscordContextMenu": () => (/* reexport safe */ _discordcontextmenu__WEBPACK_IMPORTED_MODULE_6__["default"]), +/* harmony export */ "ErrorBoundary": () => (/* reexport safe */ _errorboundary__WEBPACK_IMPORTED_MODULE_7__["default"]), +/* harmony export */ "Icons": () => (/* reexport module object */ _icons__WEBPACK_IMPORTED_MODULE_1__), +/* harmony export */ "Modals": () => (/* reexport safe */ _modals__WEBPACK_IMPORTED_MODULE_5__["default"]), +/* harmony export */ "Popouts": () => (/* reexport safe */ _popouts__WEBPACK_IMPORTED_MODULE_4__["default"]), +/* harmony export */ "Settings": () => (/* reexport module object */ _settings__WEBPACK_IMPORTED_MODULE_0__), +/* harmony export */ "Toasts": () => (/* reexport safe */ _toasts__WEBPACK_IMPORTED_MODULE_3__["default"]), +/* harmony export */ "Tooltip": () => (/* reexport safe */ _tooltip__WEBPACK_IMPORTED_MODULE_2__["default"]) +/* harmony export */ }); +/* harmony import */ var _settings__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./settings */ "./src/ui/settings/index.js"); +/* harmony import */ var _icons__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./icons */ "./src/ui/icons.js"); +/* harmony import */ var _tooltip__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./tooltip */ "./src/ui/tooltip.js"); +/* harmony import */ var _toasts__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./toasts */ "./src/ui/toasts.js"); +/* harmony import */ var _popouts__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./popouts */ "./src/ui/popouts.js"); +/* harmony import */ var _modals__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./modals */ "./src/ui/modals.js"); +/* harmony import */ var _discordcontextmenu__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./discordcontextmenu */ "./src/ui/discordcontextmenu.js"); +/* harmony import */ var _errorboundary__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./errorboundary */ "./src/ui/errorboundary.js"); +/* harmony import */ var _colorpicker__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ./colorpicker */ "./src/ui/colorpicker.js"); + + + + + + + + + + + + +/***/ }) + +/******/ }); +/************************************************************************/ +/******/ // The module cache +/******/ var __webpack_module_cache__ = {}; +/******/ +/******/ // The require function +/******/ function __webpack_require__(moduleId) { +/******/ // Check if module is in cache +/******/ var cachedModule = __webpack_module_cache__[moduleId]; +/******/ if (cachedModule !== undefined) { +/******/ return cachedModule.exports; +/******/ } +/******/ // Create a new module (and put it into the cache) +/******/ var module = __webpack_module_cache__[moduleId] = { +/******/ // no module.id needed +/******/ // no module.loaded needed +/******/ exports: {} +/******/ }; +/******/ +/******/ // Execute the module function +/******/ __webpack_modules__[moduleId](module, module.exports, __webpack_require__); +/******/ +/******/ // Return the exports of the module +/******/ return module.exports; +/******/ } +/******/ +/************************************************************************/ +/******/ /* webpack/runtime/define property getters */ +/******/ (() => { +/******/ // define getter functions for harmony exports +/******/ __webpack_require__.d = (exports, definition) => { +/******/ for(var key in definition) { +/******/ if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) { +/******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] }); +/******/ } +/******/ } +/******/ }; +/******/ })(); +/******/ +/******/ /* webpack/runtime/hasOwnProperty shorthand */ +/******/ (() => { +/******/ __webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop)) +/******/ })(); +/******/ +/******/ /* webpack/runtime/make namespace object */ +/******/ (() => { +/******/ // define __esModule on exports +/******/ __webpack_require__.r = (exports) => { +/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { +/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); +/******/ } +/******/ Object.defineProperty(exports, '__esModule', { value: true }); +/******/ }; +/******/ })(); +/******/ +/************************************************************************/ +var __webpack_exports__ = {}; +// This entry need to be wrapped in an IIFE because it need to be in strict mode. +(() => { +"use strict"; +/*!**********************!*\ + !*** ./src/index.js ***! + \**********************/ +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__) +/* harmony export */ }); +/* harmony import */ var modules__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! modules */ "./src/modules/modules.js"); +/* harmony import */ var ui__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ui */ "./src/ui/ui.js"); +/* harmony import */ var _structs_plugin__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./structs/plugin */ "./src/structs/plugin.js"); + + + + +const LibraryConfig = __webpack_require__(/*! ./config */ "./src/config.js"); // Use cjs require to prevent polyfill + +const Library = {}; +Library.DCM = ui__WEBPACK_IMPORTED_MODULE_1__.DiscordContextMenu; +Library.ContextMenu = ui__WEBPACK_IMPORTED_MODULE_1__.DiscordContextMenu; +Library.Tooltip = ui__WEBPACK_IMPORTED_MODULE_1__.Tooltip; +Library.Toasts = ui__WEBPACK_IMPORTED_MODULE_1__.Toasts; +Library.Settings = ui__WEBPACK_IMPORTED_MODULE_1__.Settings; +Library.Popouts = ui__WEBPACK_IMPORTED_MODULE_1__.Popouts; +Library.Modals = ui__WEBPACK_IMPORTED_MODULE_1__.Modals; +for (const mod in modules__WEBPACK_IMPORTED_MODULE_0__) Library[mod] = modules__WEBPACK_IMPORTED_MODULE_0__[mod]; + +Library.Components = {ErrorBoundary: ui__WEBPACK_IMPORTED_MODULE_1__.ErrorBoundary, ColorPicker: ui__WEBPACK_IMPORTED_MODULE_1__.ColorPicker}; + +// export default LibraryPlugin(Library.Structs.Plugin, Library); // eslint-disable-line new-cap + +class PluginLibrary extends _structs_plugin__WEBPACK_IMPORTED_MODULE_2__["default"] { + get Library() {return Library;} + + constructor() { + super(LibraryConfig); + + const wasLibLoaded = !!document.getElementById("ZLibraryCSS"); + const isBDLoading = document.getElementById("bd-loading-icon"); + modules__WEBPACK_IMPORTED_MODULE_0__.DOMTools.removeStyle("ZLibraryCSS"); + modules__WEBPACK_IMPORTED_MODULE_0__.DOMTools.addStyle("ZLibraryCSS", ui__WEBPACK_IMPORTED_MODULE_1__.Settings.CSS + ui__WEBPACK_IMPORTED_MODULE_1__.Toasts.CSS + modules__WEBPACK_IMPORTED_MODULE_0__.PluginUpdater.CSS); + ui__WEBPACK_IMPORTED_MODULE_1__.Popouts.initialize(); + + /** + * Checking if this is the library first being loaded during init + * This means that subsequent loads will cause dependents to reload + * This also means first load when installing for the first time + * will automatically reload the dependent plugins. This is needed + * for those plugins that prompt to download and install the lib. + */ + + if (!wasLibLoaded && isBDLoading) return; // If the this is the lib's first load AND this is BD's initialization + + /** + * Now we can go ahead and reload any dependent plugins by checking + * for any with instance._config. Both plugins using buildPlugin() + * and plugin skeletons that prompt for download should have this + * instance property. + */ + + // Temporarily disable toasts so people don't get bombarded + const wasEnabled = BdApi.isSettingEnabled("settings", "general", "showToasts"); + if (wasEnabled) BdApi.disableSetting("settings", "general", "showToasts"); + this._reloadPlugins(); + if (wasEnabled) BdApi.enableSetting("settings", "general", "showToasts"); + } + + _reloadPlugins() { + const list = BdApi.Plugins.getAll().reduce((acc, val) => { + if (!val.instance || !val.instance._config) return acc; + const name = val.id || val.instance?.getName(); + if (name === "ZeresPluginLibrary") return acc; + acc.push(name); + return acc; + }, []); + for (let p = 0; p < list.length; p++) BdApi.Plugins.reload(list[p]); + } + + static bindLibrary(name) { + const BoundAPI = { + Logger: { + stacktrace: (message, error) => modules__WEBPACK_IMPORTED_MODULE_0__.Logger.stacktrace(name, message, error), + log: (...message) => modules__WEBPACK_IMPORTED_MODULE_0__.Logger.log(name, ...message), + error: (...message) => modules__WEBPACK_IMPORTED_MODULE_0__.Logger.err(name, ...message), + err: (...message) => modules__WEBPACK_IMPORTED_MODULE_0__.Logger.err(name, ...message), + warn: (...message) => modules__WEBPACK_IMPORTED_MODULE_0__.Logger.warn(name, ...message), + info: (...message) => modules__WEBPACK_IMPORTED_MODULE_0__.Logger.info(name, ...message), + debug: (...message) => modules__WEBPACK_IMPORTED_MODULE_0__.Logger.debug(name, ...message) + }, + Patcher: { + getPatchesByCaller: () => {return modules__WEBPACK_IMPORTED_MODULE_0__.Patcher.getPatchesByCaller(name);}, + unpatchAll: () => {return modules__WEBPACK_IMPORTED_MODULE_0__.Patcher.unpatchAll(name);}, + before: (moduleToPatch, functionName, callback, options = {}) => {return modules__WEBPACK_IMPORTED_MODULE_0__.Patcher.before(name, moduleToPatch, functionName, callback, options);}, + instead: (moduleToPatch, functionName, callback, options = {}) => {return modules__WEBPACK_IMPORTED_MODULE_0__.Patcher.instead(name, moduleToPatch, functionName, callback, options);}, + after: (moduleToPatch, functionName, callback, options = {}) => {return modules__WEBPACK_IMPORTED_MODULE_0__.Patcher.after(name, moduleToPatch, functionName, callback, options);} + } + }; + const BoundLib = Object.assign({}, Library); + BoundLib.Logger = BoundAPI.Logger; + BoundLib.Patcher = BoundAPI.Patcher; + return BoundLib; + } + + static buildPlugin(config) { + return [(0,_structs_plugin__WEBPACK_IMPORTED_MODULE_2__.wrapPluginBase)(config), this.bindLibrary(config.name ?? config.info.name)]; // eslint-disable-line new-cap + } +} + +Object.assign(PluginLibrary, Library); +Library.bindLibrary = PluginLibrary.bindLibrary; +Library.buildPlugin = PluginLibrary.buildPlugin; +window.ZLibrary = Library; +window.ZeresPluginLibrary = PluginLibrary; +/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (PluginLibrary); + +})(); + +module.exports.ZeresPluginLibrary = __webpack_exports__["default"]; +/******/ })() +; +/*@end@*/ \ No newline at end of file diff --git a/BetterDiscord/themes/ClearVision_v6.theme.css b/BetterDiscord/themes/ClearVision_v6.theme.css new file mode 100644 index 0000000..ed0be2e --- /dev/null +++ b/BetterDiscord/themes/ClearVision_v6.theme.css @@ -0,0 +1,115 @@ +/** + * @name ClearVision + * @author ClearVision Team + * @version 6.3.0 + * @description Highly customizable theme for BetterDiscord. + * @source https://github.com/ClearVision/ClearVision-v6 + * @website https://clearvision.github.io + * @invite dHaSxn3 + */ + +/* Credits to Zerthox for making the original theme. */ + +/* IMPORT CSS */ +@import url(https://clearvision.github.io/ClearVision-v6/main.css); + +/* SETTINGS */ +:root { + /* ACCENT COLORS */ + --main-color: #2780e6; /* main accent color (hex, rgb or hsl) [default: #2780e6] */ + --hover-color: #1e63b3; /* hover accent color (hex, rgb or hsl) [default: #1e63b3] */ + --success-color: #43b581; /* success accent color (hex, rgb or hsl) [default: #43b581] */ + --danger-color: #982929; /* danger accent color (hex, rgb or hsl) [default: #982929] */ + --url-color: var(--main-color); /*The color of url links [default: var(--main-color)]*/ + + /* STATUS COLORS */ + --online-color: #43b581; /* online status color (hex, rgb or hsl) [default: #43b581] */ + --idle-color: #faa61a; /* idle status color (hex, rgb or hsl) [default: #faa61a] */ + --dnd-color: #982929; /* dnd status color (hex, rgb or hsl) [default: #982929] */ + --streaming-color: #593695; /* streaming status color (hex, rgb or hsl) [default: #593695] */ + --offline-color: #808080; /* offline/invisible status color (hex, rgb or hsl) [default: #808080] */ + + /* GENERAL */ + --main-font: JetBrains Mono, Whitney, gg sans, Helvetica Neue, Helvetica, Arial, sans-serif; /* main font for app (font must be installed) [default: Whitney, Helvetica Neue, Helvetica, Arial, sans-serif] */ + --code-font: JetBrains Mono, Consolas, Liberation Mono, Menlo, Courier, monospace; /* font for codeblocks (font must be installed) [default: Consolas, Liberation Mono, Menlo, Courier, monospace] */ + --text-normal: rgb(220, 221, 222); /* color of default discord text */ + --text-muted: rgb(114, 118, 125); /* color of default discord muted text (e.g.text found in input fields before typing).*/ + --channels-width: 220px; /* channel list width (240px for Discord default) [default: 220px] */ + --members-width: 240px; /* member list width [default: 240px] */ + + /* APP BACKGROUND */ + --background-shading: 100%; /* app background shading (0 for complete smoothness) [default: 100%] */ + --background-overlay: rgba(0, 0, 0, 0.0); /* app background overlay color/gradient [default: rgba(0, 0, 0, 0.6)] */ + --background-image: rgba(40, 42, 46, 0.6); /* app background image (link must be HTTPS) [default: url(https://clearvision.github.io/images/sapphire.jpg)]*/ + --background-position: center; /* app background position [default: center] */ + --background-size: cover; /* app background size [default: cover] */ + --background-repeat: no-repeat; /* app background repeat [default: no-repeat] */ + --background-attachment: fixed; /* app background attachment [default: fixed] */ + --background-brightness: 100%; /* app background brightness (< 100% for darken, > 100% for lighten) [default: 100%] */ + --background-contrast: 100%; /* app background contrast [default: 100%] */ + --background-saturation: 100%; /* app background saturation [default: 100%] */ + --background-invert: 0%; /* app background invert (0 - 100%) [default: 0%] */ + --background-grayscale: 0%; /* app background grayscale ( 0 - 100%) [default: 0%] */ + --background-sepia: 0%; /* app background sepia (0 - 100%) [default: 0%] */ + --background-blur: 0px; /* app background blur [default: 0px] */ + + /* HOME BUTTON ICON */ + --home-icon: url(https://clearvision.github.io/icons/discord.svg); /* home button icon (link must be HTTPS) [default: url(https://clearvision.github.io/icons/discord.svg)]*/ + --home-position: center; /* home button icon position [default: center] */ + --home-size: 40px; /* home button icon size [default: 40px] */ + + /* CHANNEL COLORS */ + --channel-unread: var(--main-color); /* Unread Server channel color. [default: var(--main-color)] THIS OVERRIDES YOUR MAIN COLOR*/ + --channel-color: rgba(255, 255, 255, 0.3); /*Read Server channel color [default: rgba(255,255,255,0.3);]*/ + --channel-text-selected: #fff; /* Selected channel text color, CV default is #fff */ + --muted-color: rgba(255, 255, 255, 0.1); /*Muted channel color [default: rgba(255,255,255,0.1);]*/ + + /* MODAL BACKDROP */ + --backdrop-overlay: rgba(0, 0, 0, 0.8); /* modal backdrop overlay color/gradient [default: rgba(0, 0, 0, 0.8)] */ + --backdrop-image: var(--background-image); /* modal backdrop image (link must be HTTPS) [default: var(--background-image)] */ + --backdrop-position: var(--background-position); /* modal backdrop position [default: var(--background-position)] */ + --backdrop-size: var(--background-size); /* modal backdrop size [default: var(--background-size)] */ + --backdrop-repeat: var(--background-repeat); /* modal backdrop repeat [default: var(--background-repeat)] */ + --backdrop-attachment: var(--background-attachment); /* modal backdrop attachment [default: var(--background-attachment)] */ + --backdrop-brightness: var(--background-brightness); /* modal backdrop brightness (< 100% for darken, > 100% for lighten) [default: var(--background-brightness)] */ + --backdrop-contrast: var(--background-contrast); /* modal backdrop contrast [default: var(--background-contrast)] */ + --backdrop-saturation: var(--background-saturation); /* modal backdrop saturation [default: var(--background-saturation)] */ + --backdrop-invert: var(--background-invert); /* modal backdrop invert (0 - 100%) [default: var(--background-invert)] */ + --backdrop-grayscale: var(--background-grayscale); /* modal backdrop grayscale ( 0 - 100%) [default: var(--background-grayscale)] */ + --backdrop-sepia: var(--background-sepia); /* modal backdrop sepia (0 - 100%) [default: var(--background-sepia)] */ + --backdrop-blur: var(--background-blur); /* modal backdrop blur [default: var(--background-blur)] */ + + /* USER POPOUT BACKGROUND */ + --user-popout-image: var(--background-image); /* app background image (link must be HTTPS) [default: var(--background-image)] */ + --user-popout-position: var(--background-position); /* user popout background position [default: var(--background-position)] */ + --user-popout-size: var(--background-size); /* user popout background size [default: var(--background-size)] */ + --user-popout-repeat: var(--background-repeat); /* user popout background repeat [default: var(--background-repeat)] */ + --user-popout-attachment: var(--background-attachment); /* user popout background attachment [default: var(--background-attachment)] */ + --user-popout-brightness: var(--background-brightness); /* user popout background brightness (< 100% for darken, > 100% for lighten) [default: var(--background-brightness)] */ + --user-popout-contrast: var(--background-contrast); /* user popout background contrast [default: var(--background-contrast)] */ + --user-popout-saturation: var(--background-saturation); /* user popout background saturation [default: var(--background-saturation)] */ + --user-popout-invert: var(--background-invert); /* user popout background invert (0 - 100%) [default: var(--background-invert)] */ + --user-popout-grayscale: var(--background-grayscale); /* user popout background grayscale (0 - 100%) [default: var(--background-grayscale)] */ + --user-popout-sepia: var(--background-sepia); /* user popout background sepia (0 - 100%) [default: var(--background-sepia)] */ + --user-popout-blur: calc(var(--background-blur) + 3px); /* user popout background blur [default: calc(var(--background-blur) + 3px)] */ + --user-popout-overlay: rgba(0, 0, 0, 0.6); /* user popout overlay color [default: rgba(0, 0, 0, .6)] */ + + /* USER MODAL BACKGROUND */ + --user-modal-image: var(--background-image); /* app background image (link must be HTTPS) [default: var(--background-image)] */ + --user-modal-position: var(--background-position); /* user modal background position [default: var(--background-position)] */ + --user-modal-size: var(--background-size); /* user modal background size [default: var(--background-size)] */ + --user-modal-repeat: var(--background-repeat); /* user modal background repeat [default: var(--background-repeat)] */ + --user-modal-attachment: var(--background-attachment); /* user modal background attachment [default: var(--background-attachment)] */ + --user-modal-brightness: var(--background-brightness); /* user modal background brightness (< 100% for darken, > 100% for lighten) [default: var(--background-brightness)] */ + --user-modal-contrast: var(--background-contrast); /* user modal background contrast [default: var(--background-contrast)] */ + --user-modal-saturation: var(--background-saturation); /* user modal background saturation [default: var(--background-saturation)] */ + --user-modal-invert: var(--background-invert); /* user modal background invert (0 - 100%) [default: var(--background-invert)] */ + --user-modal-grayscale: var(--background-grayscale); /* user modal background grayscale (0 - 100%) [default: var(--background-grayscale)] */ + --user-modal-sepia: var(--background-sepia); /* user modal background sepia (0 - 100%) [default: var(--background-sepia)] */ + --user-modal-blur: calc(var(--background-blur) + 3px); /* user modal background blur [default: calc(var(--background-blur) + 3px)] */ + + /* THEME BD COLORS */ + --bd-blue: var(--main-color); /* betterdiscord main color [default: var(--main-color)] */ + --bd-blue-hover: var(--hover-color); /* betterdiscord hover color [default: var(--hover-color)] */ + --bd-blue-active: var(--hover-color); /* betterdiscord active color [default: var(--hover-color)] */ +} diff --git a/BetterDiscord/themes/DarkMatter.theme.css b/BetterDiscord/themes/DarkMatter.theme.css new file mode 100644 index 0000000..56867c7 --- /dev/null +++ b/BetterDiscord/themes/DarkMatter.theme.css @@ -0,0 +1,21 @@ +/** + * @name Dark Matter + * @author Tropical#8908, Hammock#3110 + * @version 3.0.0 + * @description A cold, dark & frosty theme. + * @source https://github.com/DiscordStyles/DarkMatter/ +*/ + +@import url('https://DiscordStyles.github.io/DarkMatter/src/base.css'); + +/* Variables */ +:root { + --avatar-size: 32px; + --background-image: url('https://i.imgur.com/7SbtKvw.png'); + --home-image: url('https://i.imgur.com/233d55Y.gif'); + --background-solid: #161921; + --background-solid-dark: #101218; + --background-solid-darker: #0c0e12; + --accent: 37, 172, 232; + --accent-alt: 29, 101, 134; +} diff --git a/BetterDiscord/themes/DiscordPlus.theme.css b/BetterDiscord/themes/DiscordPlus.theme.css new file mode 100644 index 0000000..16aa25e --- /dev/null +++ b/BetterDiscord/themes/DiscordPlus.theme.css @@ -0,0 +1,91 @@ +/** + * @name Discord+ + * @author PlusInsta + * @authorId 309931975102300160 + * @authorLink https://plusinsta.xyz + * @version 3.2.1 + * @description A sleek, customizable Discord theme. + * @donate https://ko-fi.com/plusinsta + * @website https://plusinsta.github.io/discord-plus + * @source https://github.com/PlusInsta/discord-plus + * @updateUrl https://plusinsta.github.io/discord-plus/DiscordPlus.theme.css + * @invite 2Jwh2nS +*/ +@import url(https://plusinsta.github.io/discord-plus/src/DiscordPlus-source.theme.css); + +/* I've commented most of these values so you can change them yourself. If you're having trouble, or you want to do more than what these values allow for, a volunteer might be able to help you in my server. */ + +.theme-dark { +/* Backdrop image which is shown behind the app. Can also be an HTML color. Falls back to accent color if invalid. + To use an image from your computer, upload it somewhere first, copy its link, then paste it below. */ + --dplus-backdrop: url(https://i.imgur.com/3AiL3yN.png); + +/* Accent color, used to decorate the UI with colors. */ + --dplus-accent-color-hue: 320; + --dplus-accent-color-saturation: 60%; + --dplus-accent-color-lightness: 31%; + +/* Foreground color: For things like text and icons. */ + --dplus-foreground-color-hue-base: 210; + --dplus-foreground-color-hue-links: 197; + --dplus-foreground-color-saturation-amount: 1; + --dplus-foreground-color-lightness-amount: 1; + +/* Background color: Covers the backdrop so the app is easier to read. */ + --dplus-background-color-hue: 320; + --dplus-background-color-saturation-amount: 1; + --dplus-background-color-lightness-amount: 1; + --dplus-background-color-alpha: 0.8; +} + +/* If you use light theme, you can give it unique settings that are separate from dark theme. */ +.theme-light { + --dplus-backdrop: url(https://i.imgur.com/3AiL3yN.png); + + --dplus-accent-color-hue: 320; + --dplus-accent-color-saturation: 60%; + --dplus-accent-color-lightness: 31%; + + --dplus-foreground-color-hue-base: 210; + --dplus-foreground-color-hue-links: 197; + --dplus-foreground-color-saturation-amount: 1; + --dplus-foreground-color-lightness-amount: 1; + + --dplus-background-color-hue: 320; + --dplus-background-color-saturation-amount: 1; + --dplus-background-color-lightness-amount: 1; + --dplus-background-color-alpha: 0.8; +} + +:root { +/* Fonts */ +/* You can use any installed font on your device. */ + --dplus-font-ui: 'Poppins'; + --dplus-font-body: 'Roboto'; + --dplus-font-header: 'Righteous'; + +/* Round corner sizes, measured in pixel radius. +Set avatar/server radius to 50% to make them circular. + UI radius must be in pixels, otherwise things break. */ + --dplus-radius-ui: 10px; + --dplus-radius-avatar: 20%; + --dplus-radius-server: 20%; + +/* This decides how much breathing room there should be between things like buttons and content. */ + --dplus-spacing-ui: 10px; + +/* This sets how much space there should be between the app contents and the edges of the window. It also affects the distance between major UI elements, such as the server list to the channel list and channel list to the chat. */ + --dplus-spacing-app: 10px; + +/* Size of user and server icons */ +--dplus-icon-avatar-chat: 64px; +--dplus-icon-avatar-list: 32px; +--dplus-icon-avatar-profile: 80px; + +--dplus-icon-server-sidebar: 48px; +--dplus-icon-server-list: 32px; + +/* The Home icon on the top left */ +--dplus-icon-home-dark: url(https://plusinsta.github.io/discord-plus/assets/discord/home_dark.svg); +--dplus-icon-home-light: url(https://plusinsta.github.io/discord-plus/assets/discord/home_light.svg); +} diff --git a/BetterDiscord/themes/FrostedGlass.theme.css b/BetterDiscord/themes/FrostedGlass.theme.css new file mode 100644 index 0000000..9084ec2 --- /dev/null +++ b/BetterDiscord/themes/FrostedGlass.theme.css @@ -0,0 +1,69 @@ +/** + * @name Frosted Glass + * @author Gibbu#1211 + * @version 2.0.0 + * @description Display your picture of choice with adjustable blur and brightness. Dark theme is required. + * @invite ZHthyCw + * @donate https://paypal.me/IanRGibson + * @authorId 174868361040232448 + * @source https://github.com/DiscordStyles/FrostedGlass + * @website https://gibbu.me/ +*/ + +@import url("https://discordstyles.github.io/FrostedGlass/dist/FrostedGlass.css"); + +/* BlackBox tags */ +@import url('https://monstrousdev.github.io/themes/addons/user-tags.css'); + +/* Old Windows titlebar - remove this @import if you wish to use default titlebar */ +@import url("https://gibbu.github.io/BetterDiscord-Themes/addons/windows-titlebar.css"); + +:root { + /* Background image variables */ + --background-image: url('https://i.imgur.com/kYW2H5C.jpg'); /* Main background image | URL MUST BE A DIRECT LINK (ending in .jpg, .jpeg, .png, .gif) */ + --background-image-blur: 5px; /* Blur intensity of --background-image | Must end in px | DEFAULT: 5px */ + --background-image-size: cover; /* Size of the background image | DEFAUT: cover | OPTIONS: cover, contain */ + --background-image-position: center; /* Position of background image | DEAFULT: center | OPTIONS: top, right, bottom, left, center */ + + /* Popout & Modal variables */ + --popout-modal-image: var(--background-image); /* Background image for popouts and modals | URL MUST BE A DIRECT LINK (ending in .jpg, .jpeg, .png, .gif) */ + --popout-modal-blur: 5px; /* Blur intensity of --popout-modal-image | Must end in px | DEFAULT: 5px */ + --popout-modal-size: cover; /* Size of the popout/modal image | DEFAUT: cover | OPTIONS: cover, contain */ + --popout-modal-position: center; /* Position of popout/modal image | DEAFULT: center | OPTIONS: top, right, bottom, left, center */ + + /* Home image variables */ + --home-button-image: url('https://gibbu.github.io/BetterDiscord-Themes/FrostedGlass/assets/discord.svg'); /* Home button image | URL MUST BE A DIRECT LINK (ending in .jpg, .jpeg, .png, .gif) */ + --home-button-size: cover; /* Size of the home button image | DEFAUT: cover */ + --home-button-position: center; /* Position of home button image | DEAFULT: center */ + + /* Brightness variables */ + --serverlist-brightness: 0.8; /* Brightness for serverlist | 0 - 1 (decimals allowed) | DEFAULT: 0.8 */ + --left-brightness: 0.8; /* Channels and DM list brightness | 0 - 1 (decimals allowed) | DEFAULT: 0.8 */ + --middle-brightness: 0.6; /* Chat brightness | 0 - 1 (decimals allowed) | DEFAULT: 0.6 */ + --right-brightness: 0; /* Members and Now Playing brightness | 0 - 1 (decimals allowed) | DEFAULT: 0 */ + --popout-modal-brightness: 0.75; /* Brightness for popouts and modals | 0 - 1 (decimals allowed) | DEFAULT: 0.75 */ + + /* Gradient variables */ + --gradient-primary: 103,58,183; /* DEFAULT: 103,58,183 */ + --gradient-secondary: 63,81,181; /* DEFAULT: 63,81,181 */ + --gradient-direction: 320deg; /* DEFAULT: 320deg */ + + /* Tint variables */ + --tint-colour: 255,51,159; /* Colour of tint | DEAFULT: 255,51,159 */ + --tint-brightness: 0; /* Brightness of --tint-colour | 0 - 1 (decimals allowed) | DEFAULT: 0 */ + + /* Other variables */ + --window-padding: 20px; /* Spacing around the Discord window | DEFAULT: 20px */ + --window-roundness: 10px; /* Roundness of Discord | DEFAULT: 10px */ + --scrollbar-colour: rgba(255,255,255,0.05); /* DEFAULT: rgba(255,255,255,0.05) */ + --link-colour: #00b0f4; /* DEFAULT: #00b0f4 */ + + /* + Visit https://fonts.google.com and select one to your liking. + Now just follow this tutorial: https://imgur.com/a/CNbw7xC + */ + --font: 'Whitney'; + + /* Do not touch */ + --update-notice-1: none; +} diff --git a/X11/xresources b/X11/xresources new file mode 100644 index 0000000..13e6900 --- /dev/null +++ b/X11/xresources @@ -0,0 +1 @@ +Xcursor.size: 16 diff --git a/bash/.bash_logout b/bash/.bash_logout new file mode 100644 index 0000000..0e4e4f1 --- /dev/null +++ b/bash/.bash_logout @@ -0,0 +1,3 @@ +# +# ~/.bash_logout +# diff --git a/bash/.bash_profile b/bash/.bash_profile new file mode 100644 index 0000000..5545f00 --- /dev/null +++ b/bash/.bash_profile @@ -0,0 +1,5 @@ +# +# ~/.bash_profile +# + +[[ -f ~/.bashrc ]] && . ~/.bashrc diff --git a/bash/.bashrc b/bash/.bashrc new file mode 100644 index 0000000..a355b0c --- /dev/null +++ b/bash/.bashrc @@ -0,0 +1,9 @@ +# +# ~/.bashrc +# + +# If not running interactively, don't do anything +[[ $- != *i* ]] && return + +alias ls='ls --color=auto' +PS1='[\u@\h \W]\$ ' diff --git a/bombadil.toml b/bombadil.toml index 5908dda..955c4ef 100644 --- a/bombadil.toml +++ b/bombadil.toml @@ -1,11 +1,41 @@ dotfiles_dir = "dev/dotfiles" [settings] -posthooks = [ "i3-msg restart" ] +posthooks = [ + "i3-msg restart", +] [settings.dots] templates = { source = "Templates", target = "Templates" } scripts = { source = "scripts", target = "scripts" } + editorconfig = { source = "home/editorconfig", target = ".editorconfig" } xinitrc = { source = "home/xinitrc", target = ".xinitrc" } + picom = { source = "config/picom.conf", target = ".config/picom.conf" } +git = { source = "git/config", target = ".config/git/config" } + +bash = { source = "bash", target = ".config/bash" } +betterdiscord = { source = "BetterDiscord", target = ".config/BetterDiscord" } +btop = { source = "btop", target = ".config/btop" } +dunst = { source = "dunst", target = ".config/dunst" } +fastfetch = { source = "fastfetch", target = ".config/fastfetch" } +gtk2 = { source = "gtk/gtk-2.0", target = ".config/gtk-2.0" } +gtk3 = { source = "gtk/gtk-3.0", target = ".config/gtk-3.0" } +gtk4 = { source = "gtk/gtk-4.0", target = ".config/gtk-4.0" } +gtk5 = { source = "gtk/gtk-5.0", target = ".config/gtk-5.0" } +gtk6 = { source = "gtk/gtk-6.0", target = ".config/gtk-6.0" } +i3 = { source = "i3", target = ".config/i3" } +kitty = { source = "kitty", target = ".config/kitty" } +lvim = { source = "lvim/config.lua", target = ".config/lvim/config.lua" } +nvim = { source = "nvim", target = ".config/nvim" } +pipewire = { source = "pipewire", target = ".config/pipewire" } +polybar = { source = "polybar", target = ".config/polybar" } +ranger = { source = "ranger", target = ".config/ranger" } +rofi = { source = "rofi", target = ".config/rofi" } +roficafmenu = { source = "rofi-cafmenu", target = ".config/rofi-cafmenu" } +screenlayout = { source = "screenlayout", target = ".config/screenlayout" } +vim = { source = "vim", target = ".config/vim" } +X11 = { source = "X11", target = ".config/X11" } +zsh = { source = "zsh", target = ".config/zsh" } + diff --git a/btop/btop.conf b/btop/btop.conf new file mode 100755 index 0000000..da6f116 --- /dev/null +++ b/btop/btop.conf @@ -0,0 +1,212 @@ +#? Config file for btop v. 1.2.13 + +#* Name of a btop++/bpytop/bashtop formatted ".theme" file, "Default" and "TTY" for builtin themes. +#* Themes should be placed in "../share/btop/themes" relative to binary or "$HOME/.config/btop/themes" +color_theme = "Default" + +#* If the theme set background should be shown, set to False if you want terminal background transparency. +theme_background = True + +#* Sets if 24-bit truecolor should be used, will convert 24-bit colors to 256 color (6x6x6 color cube) if false. +truecolor = True + +#* Set to true to force tty mode regardless if a real tty has been detected or not. +#* Will force 16-color mode and TTY theme, set all graph symbols to "tty" and swap out other non tty friendly symbols. +force_tty = False + +#* Define presets for the layout of the boxes. Preset 0 is always all boxes shown with default settings. Max 9 presets. +#* Format: "box_name:P:G,box_name:P:G" P=(0 or 1) for alternate positions, G=graph symbol to use for box. +#* Use whitespace " " as separator between different presets. +#* Example: "cpu:0:default,mem:0:tty,proc:1:default cpu:0:braille,proc:0:tty" +presets = "cpu:1:default,proc:0:default cpu:0:default,mem:0:default,net:0:default cpu:0:block,net:0:tty" + +#* Set to True to enable "h,j,k,l,g,G" keys for directional control in lists. +#* Conflicting keys for h:"help" and k:"kill" is accessible while holding shift. +vim_keys = False + +#* Rounded corners on boxes, is ignored if TTY mode is ON. +rounded_corners = True + +#* Default symbols to use for graph creation, "braille", "block" or "tty". +#* "braille" offers the highest resolution but might not be included in all fonts. +#* "block" has half the resolution of braille but uses more common characters. +#* "tty" uses only 3 different symbols but will work with most fonts and should work in a real TTY. +#* Note that "tty" only has half the horizontal resolution of the other two, so will show a shorter historical view. +graph_symbol = "braille" + +# Graph symbol to use for graphs in cpu box, "default", "braille", "block" or "tty". +graph_symbol_cpu = "default" + +# Graph symbol to use for graphs in cpu box, "default", "braille", "block" or "tty". +graph_symbol_mem = "default" + +# Graph symbol to use for graphs in cpu box, "default", "braille", "block" or "tty". +graph_symbol_net = "default" + +# Graph symbol to use for graphs in cpu box, "default", "braille", "block" or "tty". +graph_symbol_proc = "default" + +#* Manually set which boxes to show. Available values are "cpu mem net proc", separate values with whitespace. +shown_boxes = "cpu net proc mem" + +#* Update time in milliseconds, recommended 2000 ms or above for better sample times for graphs. +update_ms = 2000 + +#* Processes sorting, "pid" "program" "arguments" "threads" "user" "memory" "cpu lazy" "cpu direct", +#* "cpu lazy" sorts top process over time (easier to follow), "cpu direct" updates top process directly. +proc_sorting = "memory" + +#* Reverse sorting order, True or False. +proc_reversed = False + +#* Show processes as a tree. +proc_tree = True + +#* Use the cpu graph colors in the process list. +proc_colors = True + +#* Use a darkening gradient in the process list. +proc_gradient = True + +#* If process cpu usage should be of the core it's running on or usage of the total available cpu power. +proc_per_core = False + +#* Show process memory as bytes instead of percent. +proc_mem_bytes = True + +#* Show cpu graph for each process. +proc_cpu_graphs = True + +#* Use /proc/[pid]/smaps for memory information in the process info box (very slow but more accurate) +proc_info_smaps = False + +#* Show proc box on left side of screen instead of right. +proc_left = False + +#* (Linux) Filter processes tied to the Linux kernel(similar behavior to htop). +proc_filter_kernel = True + +#* Sets the CPU stat shown in upper half of the CPU graph, "total" is always available. +#* Select from a list of detected attributes from the options menu. +cpu_graph_upper = "total" + +#* Sets the CPU stat shown in lower half of the CPU graph, "total" is always available. +#* Select from a list of detected attributes from the options menu. +cpu_graph_lower = "total" + +#* Toggles if the lower CPU graph should be inverted. +cpu_invert_lower = True + +#* Set to True to completely disable the lower CPU graph. +cpu_single_graph = False + +#* Show cpu box at bottom of screen instead of top. +cpu_bottom = False + +#* Shows the system uptime in the CPU box. +show_uptime = True + +#* Show cpu temperature. +check_temp = True + +#* Which sensor to use for cpu temperature, use options menu to select from list of available sensors. +cpu_sensor = "Auto" + +#* Show temperatures for cpu cores also if check_temp is True and sensors has been found. +show_coretemp = True + +#* Set a custom mapping between core and coretemp, can be needed on certain cpus to get correct temperature for correct core. +#* Use lm-sensors or similar to see which cores are reporting temperatures on your machine. +#* Format "x:y" x=core with wrong temp, y=core with correct temp, use space as separator between multiple entries. +#* Example: "4:0 5:1 6:3" +cpu_core_map = "" + +#* Which temperature scale to use, available values: "celsius", "fahrenheit", "kelvin" and "rankine". +temp_scale = "celsius" + +#* Use base 10 for bits/bytes sizes, KB = 1000 instead of KiB = 1024. +base_10_sizes = False + +#* Show CPU frequency. +show_cpu_freq = True + +#* Draw a clock at top of screen, formatting according to strftime, empty string to disable. +#* Special formatting: /host = hostname | /user = username | /uptime = system uptime +clock_format = "%X" + +#* Update main ui in background when menus are showing, set this to false if the menus is flickering too much for comfort. +background_update = True + +#* Custom cpu model name, empty string to disable. +custom_cpu_name = "" + +#* Optional filter for shown disks, should be full path of a mountpoint, separate multiple values with whitespace " ". +#* Begin line with "exclude=" to change to exclude filter, otherwise defaults to "most include" filter. Example: disks_filter="exclude=/boot /home/user". +disks_filter = "exclude=/boot" + +#* Show graphs instead of meters for memory values. +mem_graphs = True + +#* Show mem box below net box instead of above. +mem_below_net = False + +#* Count ZFS ARC in cached and available memory. +zfs_arc_cached = True + +#* If swap memory should be shown in memory box. +show_swap = True + +#* Show swap as a disk, ignores show_swap value above, inserts itself after first disk. +swap_disk = True + +#* If mem box should be split to also show disks info. +show_disks = True + +#* Filter out non physical disks. Set this to False to include network disks, RAM disks and similar. +only_physical = True + +#* Read disks list from /etc/fstab. This also disables only_physical. +use_fstab = True + +#* Setting this to True will hide all datasets, and only show ZFS pools. (IO stats will be calculated per-pool) +zfs_hide_datasets = False + +#* Set to true to show available disk space for privileged users. +disk_free_priv = False + +#* Toggles if io activity % (disk busy time) should be shown in regular disk usage view. +show_io_stat = True + +#* Toggles io mode for disks, showing big graphs for disk read/write speeds. +io_mode = False + +#* Set to True to show combined read/write io graphs in io mode. +io_graph_combined = False + +#* Set the top speed for the io graphs in MiB/s (100 by default), use format "mountpoint:speed" separate disks with whitespace " ". +#* Example: "/mnt/media:100 /:20 /boot:1". +io_graph_speeds = "" + +#* Set fixed values for network graphs in Mebibits. Is only used if net_auto is also set to False. +net_download = 100 + +net_upload = 100 + +#* Use network graphs auto rescaling mode, ignores any values set above and rescales down to 10 Kibibytes at the lowest. +net_auto = True + +#* Sync the auto scaling for download and upload to whichever currently has the highest scale. +net_sync = False + +#* Starts with the Network Interface specified here. +net_iface = "" + +#* Show battery stats in top right if battery is present. +show_battery = True + +#* Which battery to use if multiple are present. "Auto" for auto detection. +selected_battery = "Auto" + +#* Set loglevel for "~/.config/btop/btop.log" levels are: "ERROR" "WARNING" "INFO" "DEBUG". +#* The level set includes all lower levels, i.e. "DEBUG" will show all logging info. +log_level = "WARNING" diff --git a/dunst/dunstrc b/dunst/dunstrc new file mode 100644 index 0000000..c24b916 --- /dev/null +++ b/dunst/dunstrc @@ -0,0 +1,462 @@ +# See dunst(5) for all configuration options + +[global] + ### Display ### + + # Which monitor should the notifications be displayed on. + monitor = 1 + + # Display notification on focused monitor. Possible modes are: + # mouse: follow mouse pointer + # keyboard: follow window with keyboard focus + # none: don't follow anything + # + # "keyboard" needs a window manager that exports the + # _NET_ACTIVE_WINDOW property. + # This should be the case for almost all modern window managers. + # + # If this option is set to mouse or keyboard, the monitor option + # will be ignored. + follow = none + + ### Geometry ### + + # dynamic width from 0 to 300 + # width = (0, 300) + # constant width of 300 + width = 300 + + # The maximum height of a single notification, excluding the frame. + height = 300 + + # Position the notification in the top right corner + #origin = top-right + origin = top-left + + # Offset from the origin + offset = 3x3 + + # Scale factor. It is auto-detected if value is 0. + scale = 0 + + # Maximum number of notification (0 means no limit) + notification_limit = 0 + + ### Progress bar ### + + # Turn on the progess bar. It appears when a progress hint is passed with + # for example dunstify -h int:value:12 + progress_bar = true + + # Set the progress bar height. This includes the frame, so make sure + # it's at least twice as big as the frame width. + progress_bar_height = 10 + + # Set the frame width of the progress bar + progress_bar_frame_width = 1 + + # Set the minimum width for the progress bar + progress_bar_min_width = 150 + + # Set the maximum width for the progress bar + progress_bar_max_width = 300 + + + # Show how many messages are currently hidden (because of + # notification_limit). + indicate_hidden = yes + + # The transparency of the window. Range: [0; 100]. + # This option will only work if a compositing window manager is + # present (e.g. xcompmgr, compiz, etc.). (X11 only) + transparency = 0 + + # Draw a line of "separator_height" pixel height between two + # notifications. + # Set to 0 to disable. + # If gap_size is greater than 0, this setting will be ignored. + separator_height = 2 + + # Padding between text and separator. + padding = 8 + + # Horizontal padding. + horizontal_padding = 8 + + # Padding between text and icon. + text_icon_padding = 0 + + # Defines width in pixels of frame around the notification window. + # Set to 0 to disable. + frame_width = 2 + + # Defines color of the frame around the notification window. + #frame_color = "#c5c8c6" + #frame_color = "#f0c674" + + background = "#282a2e" + + # Size of gap to display between notifications - requires a compositor. + # If value is greater than 0, separator_height will be ignored and a border + # of size frame_width will be drawn around each notification instead. + # Click events on gaps do not currently propagate to applications below. + gap_size = 0 + + # Define a color for the separator. + # possible values are: + # * auto: dunst tries to find a color fitting to the background; + # * foreground: use the same color as the foreground; + # * frame: use the same color as the frame; + # * anything else will be interpreted as a X color. + separator_color = frame + + # Sort messages by urgency. + sort = yes + + # Don't remove messages, if the user is idle (no mouse or keyboard input) + # for longer than idle_threshold seconds. + # Set to 0 to disable. + # A client can set the 'transient' hint to bypass this. See the rules + # section for how to disable this if necessary + # idle_threshold = 120 + + ### Text ### + + font = JetBrains Mono 10 + + # The spacing between lines. If the height is smaller than the + # font height, it will get raised to the font height. + line_height = 0 + + # Possible values are: + # full: Allow a small subset of html markup in notifications: + # bold + # italic + # strikethrough + # underline + # + # For a complete reference see + # . + # + # strip: This setting is provided for compatibility with some broken + # clients that send markup even though it's not enabled on the + # server. Dunst will try to strip the markup but the parsing is + # simplistic so using this option outside of matching rules for + # specific applications *IS GREATLY DISCOURAGED*. + # + # no: Disable markup parsing, incoming notifications will be treated as + # plain text. Dunst will not advertise that it has the body-markup + # capability if this is set as a global setting. + # + # It's important to note that markup inside the format option will be parsed + # regardless of what this is set to. + markup = full + + # The format of the message. Possible variables are: + # %a appname + # %s summary + # %b body + # %i iconname (including its path) + # %I iconname (without its path) + # %p progress value if set ([ 0%] to [100%]) or nothing + # %n progress value if set without any extra characters + # %% Literal % + # Markup is allowed + format = "%s\n%b" + + # Alignment of message text. + # Possible values are "left", "center" and "right". + alignment = left + + # Vertical alignment of message text and icon. + # Possible values are "top", "center" and "bottom". + vertical_alignment = center + + # Show age of message if message is older than show_age_threshold + # seconds. + # Set to -1 to disable. + show_age_threshold = 60 + + # Specify where to make an ellipsis in long lines. + # Possible values are "start", "middle" and "end". + ellipsize = middle + + # Ignore newlines '\n' in notifications. + ignore_newline = no + + # Stack together notifications with the same content + stack_duplicates = true + + # Hide the count of stacked notifications with the same content + hide_duplicate_count = false + + # Display indicators for URLs (U) and actions (A). + show_indicators = yes + + ### Icons ### + + # Recursive icon lookup. You can set a single theme, instead of having to + # define all lookup paths. + enable_recursive_icon_lookup = true + + # Set icon theme (only used for recursive icon lookup) + icon_theme = "Papirus, Adwaita" + # You can also set multiple icon themes, with the leftmost one being used first. + # icon_theme = "Adwaita, breeze" + + # Align icons left/right/top/off + icon_position = left + + # Scale small icons up to this size, set to 0 to disable. Helpful + # for e.g. small files or high-dpi screens. In case of conflict, + # max_icon_size takes precedence over this. + min_icon_size = 32 + + # Scale larger icons down to this size, set to 0 to disable + #max_icon_size = 64 + max_icon_size = 48 + + # Paths to default icons (only neccesary when not using recursive icon lookup) + #icon_path = /usr/share/icons/gnome/16x16/status/:/usr/share/icons/gnome/16x16/devices/ + + ### History ### + + # Should a notification popped up from history be sticky or timeout + # as if it would normally do. + sticky_history = yes + + # Maximum amount of notifications kept in history + history_length = 20 + + ### Misc/Advanced ### + + # dmenu path. + dmenu = /usr/bin/rofi -theme dmenu -p dunst: + + # Browser for opening urls in context menu. +# browser = /usr/bin/xdg-open + browser = /usr/bin/firefox + # Always run rule-defined scripts, even if the notification is suppressed + always_run_script = true + + # Define the title of the windows spawned by dunst + title = Dunst + + # Define the class of the windows spawned by dunst + class = Dunst + + # Define the corner radius of the notification window + # in pixel size. If the radius is 0, you have no rounded + # corners. + # The radius will be automatically lowered if it exceeds half of the + # notification height to avoid clipping text and/or icons. + corner_radius = 8 + + # Ignore the dbus closeNotification message. + # Useful to enforce the timeout set by dunst configuration. Without this + # parameter, an application may close the notification sent before the + # user defined timeout. + ignore_dbusclose = false + + ### Wayland ### + # These settings are Wayland-specific. They have no effect when using X11 + + # Uncomment this if you want to let notications appear under fullscreen + # applications (default: overlay) + # layer = top + + # Set this to true to use X11 output on Wayland. + force_xwayland = false + + ### Legacy + + # Use the Xinerama extension instead of RandR for multi-monitor support. + # This setting is provided for compatibility with older nVidia drivers that + # do not support RandR and using it on systems that support RandR is highly + # discouraged. + # + # By enabling this setting dunst will not be able to detect when a monitor + # is connected or disconnected which might break follow mode if the screen + # layout changes. + force_xinerama = false + + ### mouse + + # Defines list of actions for each mouse event + # Possible values are: + # * none: Don't do anything. + # * do_action: Invoke the action determined by the action_name rule. If there is no + # such action, open the context menu. + # * open_url: If the notification has exactly one url, open it. If there are multiple + # ones, open the context menu. + # * close_current: Close current notification. + # * close_all: Close all notifications. + # * context: Open context menu for the notification. + # * context_all: Open context menu for all notifications. + # These values can be strung together for each mouse event, and + # will be executed in sequence. + mouse_left_click = do_action, close_current + mouse_middle_click = close_current + mouse_right_click = close_all + +# Experimental features that may or may not work correctly. Do not expect them +# to have a consistent behaviour across releases. +[experimental] + # Calculate the dpi to use on a per-monitor basis. + # If this setting is enabled the Xft.dpi value will be ignored and instead + # dunst will attempt to calculate an appropriate dpi value for each monitor + # using the resolution and physical size. This might be useful in setups + # where there are multiple screens with very different dpi values. + per_monitor_dpi = false + + +[urgency_low] + # IMPORTANT: colors have to be defined in quotation marks. + # Otherwise the "#" and following would be interpreted as a comment. + #background = "#282a2e" + #foreground = "#707880" + foreground = "#c5c8c6" + frame_color = "#c5c8c6" + timeout = 10 + # Icon for notifications with low urgency, uncomment to enable + #default_icon = /path/to/icon + +[urgency_normal] + #background = "#282a2e" + foreground = "#c5c8c6" + frame_color = "#f0c674" + timeout = 10 + # Icon for notifications with normal urgency, uncomment to enable + #default_icon = /path/to/icon + +[urgency_critical] + #background = "#282a2e" + foreground = "#c5c8c6" + frame_color = "#ff0000" + timeout = 0 + # Icon for notifications with critical urgency, uncomment to enable + #default_icon = /path/to/icon + +# Every section that isn't one of the above is interpreted as a rules to +# override settings for certain messages. +# +# Messages can be matched by +# appname (discouraged, see desktop_entry) +# body +# category +# desktop_entry +# icon +# match_transient +# msg_urgency +# stack_tag +# summary +# +# and you can override the +# background +# foreground +# format +# frame_color +# fullscreen +# new_icon +# set_stack_tag +# set_transient +# set_category +# timeout +# urgency +# icon_position +# skip_display +# history_ignore +# action_name +# word_wrap +# ellipsize +# alignment +# hide_text +# +# Shell-like globbing will get expanded. +# +# Instead of the appname filter, it's recommended to use the desktop_entry filter. +# GLib based applications export their desktop-entry name. In comparison to the appname, +# the desktop-entry won't get localized. +# +# SCRIPTING +# You can specify a script that gets run when the rule matches by +# setting the "script" option. +# The script will be called as follows: +# script appname summary body icon urgency +# where urgency can be "LOW", "NORMAL" or "CRITICAL". +# +# NOTE: It might be helpful to run dunst -print in a terminal in order +# to find fitting options for rules. + +# Disable the transient hint so that idle_threshold cannot be bypassed from the +# client +#[transient_disable] +# match_transient = yes +# set_transient = no +# +# Make the handling of transient notifications more strict by making them not +# be placed in history. +#[transient_history_ignore] +# match_transient = yes +# history_ignore = yes + +# fullscreen values +# show: show the notifications, regardless if there is a fullscreen window opened +# delay: displays the new notification, if there is no fullscreen window active +# If the notification is already drawn, it won't get undrawn. +# pushback: same as delay, but when switching into fullscreen, the notification will get +# withdrawn from screen again and will get delayed like a new notification +#[fullscreen_delay_everything] +# fullscreen = delay +#[fullscreen_show_critical] +# msg_urgency = critical +# fullscreen = show + +#[espeak] +# summary = "*" +# script = dunst_espeak.sh + +#[script-test] +# summary = "*script*" +# script = dunst_test.sh + +#[ignore] +# # This notification will not be displayed +# summary = "foobar" +# skip_display = true + +#[history-ignore] +# # This notification will not be saved in history +# summary = "foobar" +# history_ignore = yes + +#[skip-display] +# # This notification will not be displayed, but will be included in the history +# summary = "foobar" +# skip_display = yes + +#[signed_on] +# appname = Pidgin +# summary = "*signed on*" +# urgency = low +# +#[signed_off] +# appname = Pidgin +# summary = *signed off* +# urgency = low +# +#[says] +# appname = Pidgin +# summary = *says* +# urgency = critical +# +#[twitter] +# appname = Pidgin +# summary = *twitter.com* +# urgency = normal +# +#[stack-volumes] +# appname = "some_volume_notifiers" +# set_stack_tag = "volume" +# +# vim: ft=cfg diff --git a/fastfetch/config.conf b/fastfetch/config.conf new file mode 100755 index 0000000..ca9321c --- /dev/null +++ b/fastfetch/config.conf @@ -0,0 +1,300 @@ +# Fastfetch configuration +# Write every argument in different lines. +# Direct arguments will overwrite the corresponding ones in this file. +# Argument keys are not case sensitive. +# Whitespaces are trimmed at the beginning and the end. +# Empty lines or lines starting with # are ignored. + +# This file was shipped with 1.5.5. +# Use fastfetch --print-config > ~/.config/fastfetch/config.conf to overwrite this with the current defaults + +# Below some often usefull options are listed. Uncomment and modify them so they take affect. +# Note that there are a lot more options than the ones listed here, take a look at "fastfetch --help". +# Of course all of them can be made persistent here too. + +# Config option: +# Load additional config files. +# Some are shipped with fastfetch, list them with "fastfetch --list-presets". +# Must be a path to a config file or the name of a shipped preset. +# The config file is completly loaded before continuing in the current file, so the placement of this option matters, as later options overwrite already set ones. +# Can be used multiple times to load multiple config files / presets. +#--load-config /path/to/config.txt + +# Structure option: +# Sets the modules to use and their order. +# Must be a list of module names, separated by colons. +# List available modules with "fastfetch --list-modules". +# Get the default structure with "fastfetch --print-structure". +--structure Title:Separator:OS:Kernel:Packages:Shell:Resolution:WM:Terminal:TerminalFont:CPU:GPU:Memory:Disk:Break:Colors + +# Multithreading option: +# Sets if fastfetch should use multiple threads to detect the values. +# Must be true or false. +# Default is true. +#--multithreading true + +# Slow operations option: +# Sets if fastfetch is allowed to use known slow operations to detect more / better values. +# Must be true or false. +# Default is false. +#--allow-slow-operations false + +# Linewrap option: +# Sets if fastfetch should disable linewrap during the run. +# Must be true or false. +# Default is true. +--disable-linewrap false + +# Cursor option: +# Sets if fastfetch should hide the console cursor during the run. +# Must be true or false. +# Default is true. +#--hide-cursor true + +# Logo option: +# Sets the logo to use. +# List available logos with "fastfetch --list-logos". +# Print available logos with "fastfetch --print-logos". +# Must be the name of an available logo or a path to a text file containing a custom logo. +# Default is the current distribution. +#--logo arch + +# Logo type option: +# Sets the logo type to use. +# Must be auto, builtin, file, raw, sixel, kitty or chafa. +# Default is auto. +#--logo-type auto + +# Logo width option: +# Sets the width of the logo (in characters) if the logo is an image. +# Must be a positive integer. +# Default is 65. +#--logo-width 65 + +# Logo height option: +# Sets the height of the logo (in characters) if the logo is an image. +# Must be a positive integer. +# Default is 0 (keeps aspect ration). +#--logo-height 0 + +# Logo color options: +# Overwrite a color in the logo. Also works for user provided logos. +# In the user logo, they replace $[1-9]. Use $$ to print a single $ sign. +# Must be linux console color codes or the name of a color. +# Default is the one specified by the logo. +# Use "fastfetch --help color" to learn more and see examples. +# Valid index range is [1-9]. +#--logo-color-1 red +#--logo-color-2 32 +# [...] +#--logo-color-9 yellow + +# Logo padding option: +# Adds a padding to the left and the right side of the logo. +# Must be a positive integer. +# Default is 0. +#--logo-padding 0 + +# Logo padding left option: +# Adds a padding to the left side of the logo. +# Must be a positive integer. +# Default is 0. +#--logo-padding-left 0 + +# Logo padding right option: +# Adds a padding to the right side of the logo. +# Must be a positive integer. +# Default is 0. +#--logo-padding-right 0 + +# Logo print remaining option: +# Sets if the remaining logo should be printed, it is has more lines than modules to show. +# Must be true or false. +# Default is true. +#--logo-print-remaining true + +# Color option: +# Sets the color of the keys. +# Must be linux console color codes or the name of a color. +# Default is the primary color of the logo. +# Use "fastfetch --help color" to learn more and see examples. +#--color magenta + +# Separator option: +# Sets the string placed between a key and its value. +# Can be any string. +# Default is ": ". +#--separator ": " + +# Separator string option: +# Sets the string printed by the "separator" module (usually between title and rest of output) +# Must be any string. It is repated / cut to fit perfectly. +# Default is "-" +#--separator-string - + +# Public IP timeout option: +# Sets the time to wait for the public ip server to respond. +# Must be a positive integer. +# Default is 0 (disabled). +#--public-ip-timeout 0 + +# OS file option +# Sets the path to the file containing the operating system information. +# Should be a valid path to an existing file. +# Note that you might need to run fastfetch with --recache once for it to take affect. +# Default is /etc/os-release. +#--os-file /etc/os-release + +# Player name option +# Sets the name of the player. This is also used in song detection +# Must be the exact name of the player or a dbus address (e.g. org.mpris.MediaPlayer2.spotify) +# Default is the first match starting with org.mpris.MediaPlayer2. +#--player-name spotify + +# Escape bedrock option +# Sets if fastfetch should escape the bedrock jail, if it detectes that it is running in one +# Must be true or false. +# Default is true. +#--escape-bedrock true + +# GL option +# Sets with opengl context creation library to use +# Must be either auto, egl, glx or osmesa +# Default is auto. +#--gl auto + +# Key options: +# Sets the displayed key of a module +# Can be any string. Some of theme take an argument like a format string. See "fastfetch --help format" for help. +#--os-key OS +#--host-key Host +#--kernel-key Kernel +#--uptime-key Uptime +#--processes-key Processes +#--packages-key Packages +#--shell-key Shell +#--resolution-key Resolution {1} +#--de-key DE +#--wm-key WM +#--wm-theme-key WM Theme +#--theme-key Theme +#--icons-key Icons +#--font-key Font +#--cursor-key Cursor +#--terminal-key Terminal +#--terminal-font-key Terminal Font +#--cpu-key CPU +#--cpu-usage-key CPU Usage +#--gpu-key GPU {1} +#--memory-key Memory +#--disk-key Disk ({1}) +#--battery-key Battery {1} +#--locale-key Locale +#--local-ip-key Local IP ({1}) +#--public-ip-key Public IP +#--player-key Media Player +#--song-key Song +#--datetime-key Date Time +#--vulkan-key Vulkan +#--opengl-key OpenGL +#--opencl-key OpenCL + +# Format options: +# Sets the format string for module values. +# For information on format strings, see "fastfetch --help format". +# To see the parameter they take and their default value, see "fastfetch --help *-format", e.g. "fastfetch --help os-format". +# An empty format string (As they are currently below) will behave as if it was not set. +--os-format {3} +#--host-format +#--kernel-format +#--uptime-format +#--processes-format +#--packages-format +#--shell-format +#--resolution-format +#--de-format +#--wm-format +#--wm-theme-format +#--theme-format +#--icons-format +#--font-format +#--cursor-format +#--terminal-format +#--terminal-font-format +--cpu-format {1} +#--cpu-usage-format +#--gpu-format +#--memory-format +#--disk-format +#--battery-format +#--locale-format +#--local-ip-format +#--public-ip-format +#--player-format +#--song-format +#--datetime-format +#--vulkan-format +#--opengl-format +#--opencl-format + +# Error options: +# Sets the format string to use if an error occured +# For information on format strings, see "fastfetch --help format". +# Each of them take the error as first and only argument. +# If one of them is set, the module will appear, even if --show-errors is not given. +#--os-error +#--host-error +#--kernel-error +#--uptime-error +#--processes-error +#--packages-error +#--shell-error +#--resolution-error +#--de-error +#--wm-error +#--wm-theme-error +#--theme-error +#--icons-error +#--font-error +#--cursor-error +#--terminal-error +#--terminal-font-error +#--cpu-error +#--cpu-usage-error +#--gpu-error +#--memory-error +#--disk-error +#--battery-error +#--locale-error +#--local-ip-error +#--public-ip-error +#--player-error +#--song-error +#--datetime-error +#--vulkan-error +#--opengl-error +#--opencl-error + +# Library options: +# Sets an user specific path to a library to load. +# Must be a valid path to a library. +#--lib-PCI /usr/lib/libpci.so +#--lib-vulkan /usr/lib/libvulkan.so +#--lib-wayland /usr/lib/libwayland-client.so +#--lib-xcb-randr /usr/lib/libxcb-randr.so +#--lib-xcb /usr/lib/libxcb.so +#--lib-Xrandr /usr/lib/libXrandr.so +#--lib-X11 /usr/lib/libX11.so +#--lib-gio /usr/lib/libgio-2.0.so +#--lib-DConf /usr/lib/libdconf.so +#--lib-DBus /usr/lib/libdbus-1.so +#--lib-XFConf /usr/lib/libxfconf-0.so +#--lib-sqlite3 /usr/lib/libsqlite3.so +#--lib-rpm /usr/lib/librpm.so +#--lib-imagemagick /usr/lib/libMagickCore-7.Q16HDRI.so +#--lib-z /usr/lib/libz.so +#--lib-chafa /usr/lib/libchafa.so +#--lib-egl /usr/lib/libEGL.so +#--lib-glx /usr/lib/libGLX.so +#--lib-osmesa /usr/lib/libOSMesa.so +#--lib-opencl /usr/lib/libOpenCL.so diff --git a/git/config b/git/config new file mode 100755 index 0000000..1091d8f --- /dev/null +++ b/git/config @@ -0,0 +1,16 @@ +[user] + name = Michael Bradley +[includeIf "gitdir:~/dev/"] + path = ./config-personal +[includeIf "gitdir:~/school/"] + path = ./config-personal +[includeIf "gitdir:~/work/"] + path = ./config-work +[core] + editor = lvim +[credential "https://github.com"] + helper = !/usr/bin/gh auth git-credential +[credential "https://gist.github.com"] + helper = !/usr/bin/gh auth git-credential +[init] + defaultBranch = main diff --git a/gtk/gtk-2.0/gtkfilechooser.ini b/gtk/gtk-2.0/gtkfilechooser.ini new file mode 100644 index 0000000..facf39e --- /dev/null +++ b/gtk/gtk-2.0/gtkfilechooser.ini @@ -0,0 +1,11 @@ +[Filechooser Settings] +LocationMode=path-bar +ShowHidden=false +ShowSizeColumn=true +GeometryX=2166 +GeometryY=49 +GeometryWidth=1428 +GeometryHeight=1023 +SortColumn=name +SortOrder=ascending +StartupMode=recent diff --git a/gtk/gtk-3.0/bookmarks b/gtk/gtk-3.0/bookmarks new file mode 100644 index 0000000..45597bc --- /dev/null +++ b/gtk/gtk-3.0/bookmarks @@ -0,0 +1 @@ +file:///home/mbradley/school/3 diff --git a/gtk/gtk-3.0/settings.ini b/gtk/gtk-3.0/settings.ini new file mode 100755 index 0000000..29322c1 --- /dev/null +++ b/gtk/gtk-3.0/settings.ini @@ -0,0 +1,2 @@ +[Settings] +gtk-application-prefer-dark-theme=1 diff --git a/gtk/gtk-4.0/settings.ini b/gtk/gtk-4.0/settings.ini new file mode 100755 index 0000000..29322c1 --- /dev/null +++ b/gtk/gtk-4.0/settings.ini @@ -0,0 +1,2 @@ +[Settings] +gtk-application-prefer-dark-theme=1 diff --git a/gtk/gtk-5.0/settings.ini b/gtk/gtk-5.0/settings.ini new file mode 100755 index 0000000..29322c1 --- /dev/null +++ b/gtk/gtk-5.0/settings.ini @@ -0,0 +1,2 @@ +[Settings] +gtk-application-prefer-dark-theme=1 diff --git a/gtk/gtk-6.0/settings.ini b/gtk/gtk-6.0/settings.ini new file mode 100755 index 0000000..29322c1 --- /dev/null +++ b/gtk/gtk-6.0/settings.ini @@ -0,0 +1,2 @@ +[Settings] +gtk-application-prefer-dark-theme=1 diff --git a/i3/config b/i3/config new file mode 100755 index 0000000..1e3f4dc --- /dev/null +++ b/i3/config @@ -0,0 +1,204 @@ +set $mod Mod4 + +# Font for window titles. Will also be used by the bar unless a different font is used in the bar {} block below. +font pango:JetBrains Mono 5 + +smart_borders on +gaps inner 3 + +#Setup displays +exec --no-startup-id ~/.config/screenlayout/monitor.sh +exec --no-startup-id dex --autostart --environment i3 + +# Use pactl to adjust volume in PulseAudio. +set $refresh_i3status killall -SIGUSR1 i3status +bindsym XF86AudioRaiseVolume exec --no-startup-id pactl set-sink-volume @DEFAULT_SINK@ +10% && $refresh_i3status +bindsym XF86AudioLowerVolume exec --no-startup-id pactl set-sink-volume @DEFAULT_SINK@ -10% && $refresh_i3status +bindsym XF86AudioMute exec --no-startup-id pactl set-sink-mute @DEFAULT_SINK@ toggle && $refresh_i3status +bindsym XF86AudioMicMute exec --no-startup-id pactl set-source-mute @DEFAULT_SOURCE@ toggle && $refresh_i3status + +# Use Mouse+$mod to drag floating windows to their wanted position +floating_modifier $mod +mouse_warping output + +bindsym $mod+Return exec --no-startup-id "kitty" +bindsym $mod+Shift+q kill +bindsym Ctrl+Tab exec --no-startup-id "rofi -show drun file-browser-extended calc emoji cafmenu" +bindsym $mod+Shift+s exec --no-startup-id "escrotum -s -C" +bindsym $mod+Shift+p exec --no-startup-id peek +bindsym $mod+Shift+l exec --no-startup-id i3lock-fancy-multimonitor -b=0x3 +bindsym $mod+Shift+z exec --no-startup-id firefox +bindsym $mod+Shift+x exec --no-startup-id discord +bindsym $mod+Shift+c exec --no-startup-id nautilus +bindsym $mod+Shift+v exec --no-startup-id vlc +bindsym $mod+Shift+g exec --no-startup-id /home/mbradley/scripts/steam.sh + +# change focus +#bindsym $mod+j focus left +#bindsym $mod+k focus down +#bindsym $mod+l focus up +#bindsym $mod+semicolon focus right + +# alternatively, you can use the cursor keys: +bindsym $mod+Left focus left +bindsym $mod+Down focus down +bindsym $mod+Up focus up +bindsym $mod+Right focus right + +# move focused window +#bindsym $mod+Shift+j move left +#bindsym $mod+Shift+k move down +#bindsym $mod+Shift+l move up +#bindsym $mod+Shift+semicolon move right + +# alternatively, you can use the cursor keys: +bindsym $mod+Shift+Left move left +bindsym $mod+Shift+Down move down +bindsym $mod+Shift+Up move up +bindsym $mod+Shift+Right move right + +# split in horizontal/vertical orientation +bindsym $mod+h split h +bindsym $mod+v split v + +bindsym $mod+f fullscreen toggle + +# change container layout (stacked, tabbed, toggle split) +bindsym $mod+s layout stacking +bindsym $mod+w layout tabbed +bindsym $mod+e layout toggle split + +# toggle tiling / floating +bindsym $mod+Shift+space floating toggle + +# change focus between tiling / floating windows +bindsym $mod+space focus mode_toggle + +# Define names for default workspaces for which we configure key bindings later on. +# We use variable names to avoid repeating the names in multiple places. +set $ws1 "1" +set $ws2 "2" +set $ws3 "3" +set $ws4 "4" +set $ws5 "5" +set $ws6 "6" +set $ws7 "7" +set $ws8 "8" +set $ws9 "9" +set $ws10 "10" + +# switch to workspace +bindsym $mod+1 workspace number $ws1 +bindsym $mod+2 workspace number $ws2 +bindsym $mod+3 workspace number $ws3 +bindsym $mod+4 workspace number $ws4 +bindsym $mod+5 workspace number $ws5 +bindsym $mod+6 workspace number $ws6 +bindsym $mod+7 workspace number $ws7 +bindsym $mod+8 workspace number $ws8 +bindsym $mod+9 workspace number $ws9 +bindsym $mod+0 workspace number $ws10 + +#set $monitor_left "DisplayPort-1" +#set $monitor_right "DisplayPort-0" + +#workspace $ws1 output $monitor_left +#workspace $ws2 output $monitor_right +#workspace $ws3 output $monitor_left +#workspace $ws4 output $monitor_right +#workspace $ws5 output $monitor_left +#workspace $ws6 output $monitor_right +#workspace $ws7 output $monitor_left +#workspace $ws8 output $monitor_right +#workspace $ws9 output $monitor_left +#workspace $ws10 output $monitor_right + +set $monitor_left "DisplayPort-1" +set $monitor_right "DisplayPort-0" +set $monitor_up "HDMI-A-0" + +workspace $ws1 output $monitor_left +workspace $ws2 output $monitor_right +workspace $ws3 output $monitor_up +workspace $ws4 output $monitor_left +workspace $ws5 output $monitor_right +workspace $ws6 output $monitor_up +workspace $ws7 output $monitor_left +workspace $ws8 output $monitor_right +workspace $ws9 output $monitor_up +workspace $ws10 output $monitor_left + +# focus the parent container +#bindsym $mod+a focus parent +#bindsym $mod+Shift+1 move container to workspace number $ws1 +#bindsym $mod+Shift+2 move container to workspace number $ws2 +#bindsym $mod+Shift+3 move container to workspace number $ws3 +#bindsym $mod+Shift+4 move container to workspace number $ws4 +#bindsym $mod+Shift+5 move container to workspace number $ws5 +#bindsym $mod+Shift+6 move container to workspace number $ws6 +#bindsym $mod+Shift+7 move container to workspace number $ws7 +#bindsym $mod+Shift+8 move container to workspace number $ws8 +#bindsym $mod+Shift+9 move container to workspace number $ws9 +#bindsym $mod+Shift+0 move container to workspace number $ws10 + +bindsym $mod+Shift+1 move container to workspace number $ws1, workspace number $ws1 +bindsym $mod+Shift+2 move container to workspace number $ws2, workspace number $ws2 +bindsym $mod+Shift+3 move container to workspace number $ws3, workspace number $ws3 +bindsym $mod+Shift+4 move container to workspace number $ws4, workspace number $ws4 +bindsym $mod+Shift+5 move container to workspace number $ws5, workspace number $ws5 +bindsym $mod+Shift+6 move container to workspace number $ws6, workspace number $ws6 +bindsym $mod+Shift+7 move container to workspace number $ws7, workspace number $ws7 +bindsym $mod+Shift+8 move container to workspace number $ws8, workspace number $ws8 +bindsym $mod+Shift+9 move container to workspace number $ws9, workspace number $ws9 +bindsym $mod+Shift+0 move container to workspace number $ws10, workspace number $ws10 + +bindsym $mod+Shift+r restart + +# resize window (you can also use the mouse for that) +mode "resize" { + # These bindings trigger as soon as you enter the resize mode + + # Pressing left will shrink the window’s width. + # Pressing right will grow the window’s width. + # Pressing up will shrink the window’s height. + # Pressing down will grow the window’s height. + #bindsym j resize shrink width 10 px or 10 ppt + #bindsym k resize grow height 10 px or 10 ppt + #bindsym l resize shrink height 10 px or 10 ppt + #bindsym semicolon resize grow width 10 px or 10 ppt + + # same bindings, but for the arrow keys + bindsym Left resize shrink width 1 px or 1 ppt + bindsym Down resize grow height 1 px or 1 ppt + bindsym Up resize shrink height 1 px or 1 ppt + bindsym Right resize grow width 1 px or 1 ppt + + # back to normal: Enter or Escape or $mod+r + bindsym Return mode "default" + bindsym Escape mode "default" + bindsym $mod+r mode "default" +} + +bindsym $mod+r mode "resize" + +default_border none +default_floating_border none +new_window none +for_window [all] border none +hide_edge_borders both + +# other commands +exec_always --no-startup-id picom -b +exec_always --no-startup-id /home/mbradley/.config/i3/feh.sh +exec --no-startup-id redshift-gtk -l 44.631:-77.955 + +exec_always --no-startup-id polybar-msg cmd quit +exec_always --no-startup-id polybar -r main +exec_always --no-startup-id polybar -r other +exec_always --no-startup-id polybar -r last +exec --no-startup-id /usr/bin/dunst + +exec --no-startup-id /home/mbradley/.config/i3/startup.sh + +exec --no-startup-id /usr/bin/birdtray + diff --git a/i3/feh.sh b/i3/feh.sh new file mode 100755 index 0000000..903932c --- /dev/null +++ b/i3/feh.sh @@ -0,0 +1,3 @@ +#!/bin/sh +feh --no-fehbg --bg-fill --randomize /usr/share/backgrounds/Wallpapers/* + diff --git a/i3/startup.json b/i3/startup.json new file mode 100755 index 0000000..ab35415 --- /dev/null +++ b/i3/startup.json @@ -0,0 +1,105 @@ +// vim:ts=4:sw=4:et +{ + "border": "none", + "current_border_width": 0, + "floating": "auto_off", + "geometry": { + "height": 1022, + "width": 948, + "x": 0, + "y": 0 + }, + "marks": [], + "name": "i3-save-tree --workspace 2 > ~/.config/i3/startup.json", + "percent": 0.5, + "swallows": [ + { + "class": "^kitty$" + // "instance": "^kitty$", + // "machine": "^hpomen$", + //"title": "clear" + } + ], + "type": "con" +} + +{ + // splitv split container with 2 children + "border": "none", + "floating": "auto_off", + "layout": "splitv", + "marks": [], + "percent": 0.5, + "type": "con", + "nodes": [ + { + // splith split container with 1 children + "border": "none", + "floating": "auto_off", + "layout": "splith", + "marks": [], + "percent": 0.5, + "type": "con", + "nodes": [ + { + // splith split container with 1 children + "border": "none", + "floating": "auto_off", + "layout": "splith", + "marks": [], + "percent": 1, + "type": "con", + "nodes": [ + { + "border": "none", + "current_border_width": 0, + "floating": "auto_off", + "geometry": { + "height": 1022, + "width": 948, + "x": 0, + "y": 0 + }, + "marks": [], + "name": "mbradley@hpomen:~", + "percent": 1, + "swallows": [ + { + "class": "^kitty$" + // "instance": "^kitty$", + // "machine": "^hpomen$", + //"title": "c" + } + ], + "type": "con" + } + ] + } + ] + }, + { + "border": "none", + "current_border_width": 0, + "floating": "auto_off", + "geometry": { + "height": 1022, + "width": 948, + "x": 0, + "y": 0 + }, + "marks": [], + "name": "btop", + "percent": 0.5, + "swallows": [ + { + "class": "^kitty$" + // "instance": "^kitty$", + // "machine": "^hpomen$", + //"title": "^btop$" + } + ], + "type": "con" + } + ] +} + diff --git a/i3/startup.sh b/i3/startup.sh new file mode 100755 index 0000000..4a00553 --- /dev/null +++ b/i3/startup.sh @@ -0,0 +1,21 @@ +#!/bin/zsh + +function cmd() { + control_file="unix:/tmp/kittycommand-$(date +%s%N)-$*" + kitty -o allow_remote_control="yes" --listen-on "$control_file" & + sleep 1 + kitty @ --to "$control_file" send-text "$*\n" +} + +# /home/mbradley/scripts/mouse_sens.sh + +i3-msg 'workspace 3; append_layout /home/mbradley/.config/i3/startup.json' +sleep 0.1 + +cmd clear +cmd c +sleep 0.5 +cmd btop + +i3-msg 'workspace 1' + diff --git a/kitty/README.md b/kitty/README.md new file mode 100644 index 0000000..1bf048a --- /dev/null +++ b/kitty/README.md @@ -0,0 +1,4 @@ +# Kitty theme + +Sourced from: https://github.com/dexpota/kitty-themes/blob/master/themes/3024_Night.conf + diff --git a/kitty/kitty.conf b/kitty/kitty.conf new file mode 100755 index 0000000..a467cc2 --- /dev/null +++ b/kitty/kitty.conf @@ -0,0 +1,2169 @@ +# vim:fileencoding=utf-8:foldmethod=marker + +#: Fonts {{{ + +#: kitty has very powerful font management. You can configure +#: individual font faces and even specify special fonts for particular +#: characters. + +font_family JetBrains Mono +# bold_font auto +# italic_font auto +# bold_italic_font auto + +#: You can specify different fonts for the bold/italic/bold-italic +#: variants. To get a full list of supported fonts use the `kitty +#: +list-fonts` command. By default they are derived automatically, by +#: the OSes font system. When bold_font or bold_italic_font is set to +#: auto on macOS, the priority of bold fonts is semi-bold, bold, +#: heavy. Setting them manually is useful for font families that have +#: many weight variants like Book, Medium, Thick, etc. For example:: + +#: font_family Operator Mono Book +#: bold_font Operator Mono Medium +#: italic_font Operator Mono Book Italic +#: bold_italic_font Operator Mono Medium Italic + +# font_size 11.0 + +#: Font size (in pts) + +# force_ltr no + +#: kitty does not support BIDI (bidirectional text), however, for RTL +#: scripts, words are automatically displayed in RTL. That is to say, +#: in an RTL script, the words "HELLO WORLD" display in kitty as +#: "WORLD HELLO", and if you try to select a substring of an RTL- +#: shaped string, you will get the character that would be there had +#: the the string been LTR. For example, assuming the Hebrew word +#: ירושלים, selecting the character that on the screen appears to be ם +#: actually writes into the selection buffer the character י. kitty's +#: default behavior is useful in conjunction with a filter to reverse +#: the word order, however, if you wish to manipulate RTL glyphs, it +#: can be very challenging to work with, so this option is provided to +#: turn it off. Furthermore, this option can be used with the command +#: line program GNU FriBidi +#: to get BIDI +#: support, because it will force kitty to always treat the text as +#: LTR, which FriBidi expects for terminals. + +# adjust_line_height 0 +# adjust_column_width 0 + +#: Change the size of each character cell kitty renders. You can use +#: either numbers, which are interpreted as pixels or percentages +#: (number followed by %), which are interpreted as percentages of the +#: unmodified values. You can use negative pixels or percentages less +#: than 100% to reduce sizes (but this might cause rendering +#: artifacts). + +# adjust_baseline 0 + +#: Adjust the vertical alignment of text (the height in the cell at +#: which text is positioned). You can use either numbers, which are +#: interpreted as pixels or percentages (number followed by %), which +#: are interpreted as the percentage of the line height. A positive +#: value moves the baseline up, and a negative value moves them down. +#: The underline and strikethrough positions are adjusted accordingly. + +# symbol_map + +#: E.g. symbol_map U+E0A0-U+E0A3,U+E0C0-U+E0C7 PowerlineSymbols + +#: Map the specified Unicode codepoints to a particular font. Useful +#: if you need special rendering for some symbols, such as for +#: Powerline. Avoids the need for patched fonts. Each Unicode code +#: point is specified in the form `U+`. You +#: can specify multiple code points, separated by commas and ranges +#: separated by hyphens. This option can be specified multiple times. +#: The syntax is:: + +#: symbol_map codepoints Font Family Name + +# narrow_symbols + +#: E.g. narrow_symbols U+E0A0-U+E0A3,U+E0C0-U+E0C7 1 + +#: Usually, for Private Use Unicode characters and some symbol/dingbat +#: characters, if the character is followed by one or more spaces, +#: kitty will use those extra cells to render the character larger, if +#: the character in the font has a wide aspect ratio. Using this +#: option you can force kitty to restrict the specified code points to +#: render in the specified number of cells (defaulting to one cell). +#: This option can be specified multiple times. The syntax is:: + +#: narrow_symbols codepoints [optionally the number of cells] + +# disable_ligatures never + +#: Choose how you want to handle multi-character ligatures. The +#: default is to always render them. You can tell kitty to not render +#: them when the cursor is over them by using cursor to make editing +#: easier, or have kitty never render them at all by using always, if +#: you don't like them. The ligature strategy can be set per-window +#: either using the kitty remote control facility or by defining +#: shortcuts for it in kitty.conf, for example:: + +#: map alt+1 disable_ligatures_in active always +#: map alt+2 disable_ligatures_in all never +#: map alt+3 disable_ligatures_in tab cursor + +#: Note that this refers to programming ligatures, typically +#: implemented using the calt OpenType feature. For disabling general +#: ligatures, use the font_features option. + +# font_features + +#: E.g. font_features none + +#: Choose exactly which OpenType features to enable or disable. This +#: is useful as some fonts might have features worthwhile in a +#: terminal. For example, Fira Code includes a discretionary feature, +#: zero, which in that font changes the appearance of the zero (0), to +#: make it more easily distinguishable from Ø. Fira Code also includes +#: other discretionary features known as Stylistic Sets which have the +#: tags ss01 through ss20. + +#: For the exact syntax to use for individual features, see the +#: HarfBuzz documentation . + +#: Note that this code is indexed by PostScript name, and not the font +#: family. This allows you to define very precise feature settings; +#: e.g. you can disable a feature in the italic font but not in the +#: regular font. + +#: On Linux, font features are first read from the FontConfig database +#: and then this option is applied, so they can be configured in a +#: single, central place. + +#: To get the PostScript name for a font, use `kitty +list-fonts +#: --psnames`: + +#: .. code-block:: sh + +#: $ kitty +list-fonts --psnames | grep Fira +#: Fira Code +#: Fira Code Bold (FiraCode-Bold) +#: Fira Code Light (FiraCode-Light) +#: Fira Code Medium (FiraCode-Medium) +#: Fira Code Regular (FiraCode-Regular) +#: Fira Code Retina (FiraCode-Retina) + +#: The part in brackets is the PostScript name. + +#: Enable alternate zero and oldstyle numerals:: + +#: font_features FiraCode-Retina +zero +onum + +#: Enable only alternate zero in the bold font:: + +#: font_features FiraCode-Bold +zero + +#: Disable the normal ligatures, but keep the calt feature which (in +#: this font) breaks up monotony:: + +#: font_features TT2020StyleB-Regular -liga +calt + +#: In conjunction with force_ltr, you may want to disable Arabic +#: shaping entirely, and only look at their isolated forms if they +#: show up in a document. You can do this with e.g.:: + +#: font_features UnifontMedium +isol -medi -fina -init + +# box_drawing_scale 0.001, 1, 1.5, 2 + +#: The sizes of the lines used for the box drawing Unicode characters. +#: These values are in pts. They will be scaled by the monitor DPI to +#: arrive at a pixel value. There must be four values corresponding to +#: thin, normal, thick, and very thick lines. + +#: }}} + +#: Cursor customization {{{ + +# cursor #cccccc + +#: Default cursor color. If set to the special value none the cursor +#: will be rendered with a "reverse video" effect. It's color will be +#: the color of the text in the cell it is over and the text will be +#: rendered with the background color of the cell. Note that if the +#: program running in the terminal sets a cursor color, this takes +#: precedence. Also, the cursor colors are modified if the cell +#: background and foreground colors have very low contrast. + +# cursor_text_color #111111 + +#: The color of text under the cursor. If you want it rendered with +#: the background color of the cell underneath instead, use the +#: special keyword: background. Note that if cursor is set to none +#: then this option is ignored. + +# cursor_shape beam + +#: The cursor shape can be one of block, beam, underline. Note that +#: when reloading the config this will be changed only if the cursor +#: shape has not been set by the program running in the terminal. This +#: sets the default cursor shape, applications running in the terminal +#: can override it. In particular, shell integration +#: in kitty sets +#: the cursor shape to beam at shell prompts. You can avoid this by +#: setting shell_integration to no-cursor. + +# cursor_beam_thickness 1.5 + +#: The thickness of the beam cursor (in pts). + +# cursor_underline_thickness 2.0 + +#: The thickness of the underline cursor (in pts). + +# cursor_blink_interval -1 + +#: The interval to blink the cursor (in seconds). Set to zero to +#: disable blinking. Negative values mean use system default. Note +#: that the minimum interval will be limited to repaint_delay. + +# cursor_stop_blinking_after 15.0 + +#: Stop blinking cursor after the specified number of seconds of +#: keyboard inactivity. Set to zero to never stop blinking. + +#: }}} + +#: Scrollback {{{ + +scrollback_lines -1 + +#: Number of lines of history to keep in memory for scrolling back. +#: Memory is allocated on demand. Negative numbers are (effectively) +#: infinite scrollback. Note that using very large scrollback is not +#: recommended as it can slow down performance of the terminal and +#: also use large amounts of RAM. Instead, consider using +#: scrollback_pager_history_size. Note that on config reload if this +#: is changed it will only affect newly created windows, not existing +#: ones. + +# scrollback_pager less --chop-long-lines --RAW-CONTROL-CHARS +INPUT_LINE_NUMBER + +#: Program with which to view scrollback in a new window. The +#: scrollback buffer is passed as STDIN to this program. If you change +#: it, make sure the program you use can handle ANSI escape sequences +#: for colors and text formatting. INPUT_LINE_NUMBER in the command +#: line above will be replaced by an integer representing which line +#: should be at the top of the screen. Similarly CURSOR_LINE and +#: CURSOR_COLUMN will be replaced by the current cursor position or +#: set to 0 if there is no cursor, for example, when showing the last +#: command output. + +# scrollback_pager_history_size 0 + +#: Separate scrollback history size (in MB), used only for browsing +#: the scrollback buffer with pager. This separate buffer is not +#: available for interactive scrolling but will be piped to the pager +#: program when viewing scrollback buffer in a separate window. The +#: current implementation stores the data in UTF-8, so approximatively +#: 10000 lines per megabyte at 100 chars per line, for pure ASCII, +#: unformatted text. A value of zero or less disables this feature. +#: The maximum allowed size is 4GB. Note that on config reload if this +#: is changed it will only affect newly created windows, not existing +#: ones. + +# scrollback_fill_enlarged_window no + +#: Fill new space with lines from the scrollback buffer after +#: enlarging a window. + +# wheel_scroll_multiplier 5.0 + +#: Multiplier for the number of lines scrolled by the mouse wheel. +#: Note that this is only used for low precision scrolling devices, +#: not for high precision scrolling devices on platforms such as macOS +#: and Wayland. Use negative numbers to change scroll direction. See +#: also wheel_scroll_min_lines. + +# wheel_scroll_min_lines 1 + +#: The minimum number of lines scrolled by the mouse wheel. The scroll +#: multiplier only takes effect after it +#: reaches this number. Note that this is only used for low precision +#: scrolling devices like wheel mice that scroll by very small amounts +#: when using the wheel. With a negative number, the minimum number of +#: lines will always be added. + +# touch_scroll_multiplier 1.0 + +#: Multiplier for the number of lines scrolled by a touchpad. Note +#: that this is only used for high precision scrolling devices on +#: platforms such as macOS and Wayland. Use negative numbers to change +#: scroll direction. + +#: }}} + +#: Mouse {{{ + +# mouse_hide_wait 3.0 + +#: Hide mouse cursor after the specified number of seconds of the +#: mouse not being used. Set to zero to disable mouse cursor hiding. +#: Set to a negative value to hide the mouse cursor immediately when +#: typing text. Disabled by default on macOS as getting it to work +#: robustly with the ever-changing sea of bugs that is Cocoa is too +#: much effort. + +# url_color #0087bd +# url_style curly + +#: The color and style for highlighting URLs on mouse-over. url_style +#: can be one of: none, straight, double, curly, dotted, dashed. + +# open_url_with default + +#: The program to open clicked URLs. The special value default means +#: to use the operating system's default URL handler (open on macOS +#: and xdg-open on Linux). + +# url_prefixes file ftp ftps gemini git gopher http https irc ircs kitty mailto news sftp ssh + +#: The set of URL prefixes to look for when detecting a URL under the +#: mouse cursor. + +# detect_urls yes + +#: Detect URLs under the mouse. Detected URLs are highlighted with an +#: underline and the mouse cursor becomes a hand over them. Even if +#: this option is disabled, URLs are still clickable. + +# url_excluded_characters + +#: Additional characters to be disallowed from URLs, when detecting +#: URLs under the mouse cursor. By default, all characters that are +#: legal in URLs are allowed. + +# copy_on_select no + +#: Copy to clipboard or a private buffer on select. With this set to +#: clipboard, selecting text with the mouse will cause the text to be +#: copied to clipboard. Useful on platforms such as macOS that do not +#: have the concept of primary selection. You can instead specify a +#: name such as a1 to copy to a private kitty buffer. Map a shortcut +#: with the paste_from_buffer action to paste from this private +#: buffer. For example:: + +#: copy_on_select a1 +#: map shift+cmd+v paste_from_buffer a1 + +#: Note that copying to the clipboard is a security risk, as all +#: programs, including websites open in your browser can read the +#: contents of the system clipboard. + +# paste_actions quote-urls-at-prompt + +#: A comma separated list of actions to take when pasting text into +#: the terminal. The supported paste actions are: + +#: quote-urls-at-prompt: +#: If the text being pasted is a URL and the cursor is at a shell prompt, +#: automatically quote the URL (needs shell_integration). +#: confirm: +#: Confirm the paste if bracketed paste mode is not active or there is more +#: a large amount of text being pasted. +#: filter: +#: Run the filter_paste() function from the file paste-actions.py in +#: the kitty config directory on the pasted text. The text returned by the +#: function will be actually pasted. + +strip_trailing_spaces always + +#: Remove spaces at the end of lines when copying to clipboard. A +#: value of smart will do it when using normal selections, but not +#: rectangle selections. A value of always will always do it. + +# select_by_word_characters @-./_~?&=%+# + +#: Characters considered part of a word when double clicking. In +#: addition to these characters any character that is marked as an +#: alphanumeric character in the Unicode database will be matched. + +# select_by_word_characters_forward + +#: Characters considered part of a word when extending the selection +#: forward on double clicking. In addition to these characters any +#: character that is marked as an alphanumeric character in the +#: Unicode database will be matched. + +#: If empty (default) select_by_word_characters will be used for both +#: directions. + +# click_interval -1.0 + +#: The interval between successive clicks to detect double/triple +#: clicks (in seconds). Negative numbers will use the system default +#: instead, if available, or fallback to 0.5. + +focus_follows_mouse yes + +#: Set the active window to the window under the mouse when moving the +#: mouse around. + +# pointer_shape_when_grabbed arrow + +#: The shape of the mouse pointer when the program running in the +#: terminal grabs the mouse. Valid values are: arrow, beam and hand. + +# default_pointer_shape beam + +#: The default shape of the mouse pointer. Valid values are: arrow, +#: beam and hand. + +# pointer_shape_when_dragging beam + +#: The default shape of the mouse pointer when dragging across text. +#: Valid values are: arrow, beam and hand. + +#: Mouse actions {{{ + +#: Mouse buttons can be mapped to perform arbitrary actions. The +#: syntax is: + +#: .. code-block:: none + +#: mouse_map button-name event-type modes action + +#: Where button-name is one of left, middle, right, b1 ... b8 with +#: added keyboard modifiers. For example: ctrl+shift+left refers to +#: holding the Ctrl+Shift keys while clicking with the left mouse +#: button. The value b1 ... b8 can be used to refer to up to eight +#: buttons on a mouse. + +#: event-type is one of press, release, doublepress, triplepress, +#: click, doubleclick. modes indicates whether the action is performed +#: when the mouse is grabbed by the program running in the terminal, +#: or not. The values are grabbed or ungrabbed or a comma separated +#: combination of them. grabbed refers to when the program running in +#: the terminal has requested mouse events. Note that the click and +#: double click events have a delay of click_interval to disambiguate +#: from double and triple presses. + +#: You can run kitty with the kitty --debug-input command line option +#: to see mouse events. See the builtin actions below to get a sense +#: of what is possible. + +#: If you want to unmap an action, map it to no_op. For example, to +#: disable opening of URLs with a plain click:: + +#: mouse_map left click ungrabbed no_op + +#: See all the mappable actions including mouse actions here +#: . + +#: .. note:: +#: Once a selection is started, releasing the button that started it will +#: automatically end it and no release event will be dispatched. + +# clear_all_mouse_actions no + +#: Remove all mouse action definitions up to this point. Useful, for +#: instance, to remove the default mouse actions. + +#: Click the link under the mouse or move the cursor + +# mouse_map left click ungrabbed mouse_handle_click selection link prompt + +#:: First check for a selection and if one exists do nothing. Then +#:: check for a link under the mouse cursor and if one exists, click +#:: it. Finally check if the click happened at the current shell +#:: prompt and if so, move the cursor to the click location. Note +#:: that this requires shell integration +#:: to work. + +#: Click the link under the mouse or move the cursor even when grabbed + +# mouse_map shift+left click grabbed,ungrabbed mouse_handle_click selection link prompt + +#:: Same as above, except that the action is performed even when the +#:: mouse is grabbed by the program running in the terminal. + +#: Click the link under the mouse cursor + +# mouse_map ctrl+shift+left release grabbed,ungrabbed mouse_handle_click link + +#:: Variant with Ctrl+Shift is present because the simple click based +#:: version has an unavoidable delay of click_interval, to +#:: disambiguate clicks from double clicks. + +#: Discard press event for link click + +# mouse_map ctrl+shift+left press grabbed discard_event + +#:: Prevent this press event from being sent to the program that has +#:: grabbed the mouse, as the corresponding release event is used to +#:: open a URL. + +#: Paste from the primary selection + +# mouse_map middle release ungrabbed paste_from_selection + +#: Start selecting text + +# mouse_map left press ungrabbed mouse_selection normal + +#: Start selecting text in a rectangle + +# mouse_map ctrl+alt+left press ungrabbed mouse_selection rectangle + +#: Select a word + +# mouse_map left doublepress ungrabbed mouse_selection word + +#: Select a line + +# mouse_map left triplepress ungrabbed mouse_selection line + +#: Select line from point + +# mouse_map ctrl+alt+left triplepress ungrabbed mouse_selection line_from_point + +#:: Select from the clicked point to the end of the line. + +#: Extend the current selection + +# mouse_map right press ungrabbed mouse_selection extend + +#:: If you want only the end of the selection to be moved instead of +#:: the nearest boundary, use move-end instead of extend. + +#: Paste from the primary selection even when grabbed + +# mouse_map shift+middle release ungrabbed,grabbed paste_selection +# mouse_map shift+middle press grabbed discard_event + +#: Start selecting text even when grabbed + +# mouse_map shift+left press ungrabbed,grabbed mouse_selection normal + +#: Start selecting text in a rectangle even when grabbed + +# mouse_map ctrl+shift+alt+left press ungrabbed,grabbed mouse_selection rectangle + +#: Select a word even when grabbed + +# mouse_map shift+left doublepress ungrabbed,grabbed mouse_selection word + +#: Select a line even when grabbed + +# mouse_map shift+left triplepress ungrabbed,grabbed mouse_selection line + +#: Select line from point even when grabbed + +# mouse_map ctrl+shift+alt+left triplepress ungrabbed,grabbed mouse_selection line_from_point + +#:: Select from the clicked point to the end of the line even when +#:: grabbed. + +#: Extend the current selection even when grabbed + +# mouse_map shift+right press ungrabbed,grabbed mouse_selection extend + +#: Show clicked command output in pager + +# mouse_map ctrl+shift+right press ungrabbed mouse_show_command_output + +#:: Requires shell integration +#:: to work. + +#: }}} + +#: }}} + +#: Performance tuning {{{ + +# repaint_delay 10 + +#: Delay between screen updates (in milliseconds). Decreasing it, +#: increases frames-per-second (FPS) at the cost of more CPU usage. +#: The default value yields ~100 FPS which is more than sufficient for +#: most uses. Note that to actually achieve 100 FPS, you have to +#: either set sync_to_monitor to no or use a monitor with a high +#: refresh rate. Also, to minimize latency when there is pending input +#: to be processed, this option is ignored. + +# input_delay 3 + +#: Delay before input from the program running in the terminal is +#: processed (in milliseconds). Note that decreasing it will increase +#: responsiveness, but also increase CPU usage and might cause flicker +#: in full screen programs that redraw the entire screen on each loop, +#: because kitty is so fast that partial screen updates will be drawn. + +sync_to_monitor yes + +#: Sync screen updates to the refresh rate of the monitor. This +#: prevents screen tearing +#: when scrolling. +#: However, it limits the rendering speed to the refresh rate of your +#: monitor. With a very high speed mouse/high keyboard repeat rate, +#: you may notice some slight input latency. If so, set this to no. + +#: }}} + +#: Terminal bell {{{ + +# enable_audio_bell yes + +#: The audio bell. Useful to disable it in environments that require +#: silence. + +# visual_bell_duration 0.0 + +#: The visual bell duration (in seconds). Flash the screen when a bell +#: occurs for the specified number of seconds. Set to zero to disable. + +# visual_bell_color none + +#: The color used by visual bell. Set to none will fall back to +#: selection background color. If you feel that the visual bell is too +#: bright, you can set it to a darker color. + +# window_alert_on_bell yes + +#: Request window attention on bell. Makes the dock icon bounce on +#: macOS or the taskbar flash on linux. + +# bell_on_tab "🔔 " + +#: Some text or a Unicode symbol to show on the tab if a window in the +#: tab that does not have focus has a bell. If you want to use leading +#: or trailing spaces, surround the text with quotes. See +#: tab_title_template for how this is rendered. + +#: For backwards compatibility, values of yes, y and true are +#: converted to the default bell symbol and no, n, false and none are +#: converted to the empty string. + +# command_on_bell none + +#: Program to run when a bell occurs. The environment variable +#: KITTY_CHILD_CMDLINE can be used to get the program running in the +#: window in which the bell occurred. + +# bell_path none + +#: Path to a sound file to play as the bell sound. If set to none, the +#: system default bell sound is used. Must be in a format supported by +#: the operating systems sound API, such as WAV or OGA on Linux +#: (libcanberra) or AIFF, MP3 or WAV on macOS (NSSound) + +#: }}} + +#: Window layout {{{ + +# remember_window_size yes +# initial_window_width 640 +# initial_window_height 400 + +#: If enabled, the window size will be remembered so that new +#: instances of kitty will have the same size as the previous +#: instance. If disabled, the window will initially have size +#: configured by initial_window_width/height, in pixels. You can use a +#: suffix of "c" on the width/height values to have them interpreted +#: as number of cells instead of pixels. + +# enabled_layouts * + +#: The enabled window layouts. A comma separated list of layout names. +#: The special value all means all layouts. The first listed layout +#: will be used as the startup layout. Default configuration is all +#: layouts in alphabetical order. For a list of available layouts, see +#: the layouts . + +# window_resize_step_cells 2 +# window_resize_step_lines 2 + +#: The step size (in units of cell width/cell height) to use when +#: resizing kitty windows in a layout with the shortcut +#: start_resizing_window. The cells value is used for horizontal +#: resizing, and the lines value is used for vertical resizing. + +# window_border_width 0.5pt + +#: The width of window borders. Can be either in pixels (px) or pts +#: (pt). Values in pts will be rounded to the nearest number of pixels +#: based on screen resolution. If not specified, the unit is assumed +#: to be pts. Note that borders are displayed only when more than one +#: window is visible. They are meant to separate multiple windows. + +# draw_minimal_borders yes + +#: Draw only the minimum borders needed. This means that only the +#: borders that separate the inactive window from a neighbor are +#: drawn. Note that setting a non-zero window_margin_width overrides +#: this and causes all borders to be drawn. + +# window_margin_width 0 + +#: The window margin (in pts) (blank area outside the border). A +#: single value sets all four sides. Two values set the vertical and +#: horizontal sides. Three values set top, horizontal and bottom. Four +#: values set top, right, bottom and left. + +# single_window_margin_width -1 + +#: The window margin to use when only a single window is visible (in +#: pts). Negative values will cause the value of window_margin_width +#: to be used instead. A single value sets all four sides. Two values +#: set the vertical and horizontal sides. Three values set top, +#: horizontal and bottom. Four values set top, right, bottom and left. + +# window_padding_width 0 + +#: The window padding (in pts) (blank area between the text and the +#: window border). A single value sets all four sides. Two values set +#: the vertical and horizontal sides. Three values set top, horizontal +#: and bottom. Four values set top, right, bottom and left. + +# placement_strategy center + +#: When the window size is not an exact multiple of the cell size, the +#: cell area of the terminal window will have some extra padding on +#: the sides. You can control how that padding is distributed with +#: this option. Using a value of center means the cell area will be +#: placed centrally. A value of top-left means the padding will be +#: only at the bottom and right edges. + +# active_border_color #00ff00 + +#: The color for the border of the active window. Set this to none to +#: not draw borders around the active window. + +# inactive_border_color #cccccc + +#: The color for the border of inactive windows. + +# bell_border_color #ff5a00 + +#: The color for the border of inactive windows in which a bell has +#: occurred. + +# inactive_text_alpha 1.0 + +#: Fade the text in inactive windows by the specified amount (a number +#: between zero and one, with zero being fully faded). + +# hide_window_decorations no + +#: Hide the window decorations (title-bar and window borders) with +#: yes. On macOS, titlebar-only can be used to only hide the titlebar. +#: Whether this works and exactly what effect it has depends on the +#: window manager/operating system. Note that the effects of changing +#: this option when reloading config are undefined. + +# window_logo_path none + +#: Path to a logo image. Must be in PNG format. Relative paths are +#: interpreted relative to the kitty config directory. The logo is +#: displayed in a corner of every kitty window. The position is +#: controlled by window_logo_position. Individual windows can be +#: configured to have different logos either using the launch action +#: or the remote control facility. + +# window_logo_position bottom-right + +#: Where to position the window logo in the window. The value can be +#: one of: top-left, top, top-right, left, center, right, bottom-left, +#: bottom, bottom-right. + +# window_logo_alpha 0.5 + +#: The amount the logo should be faded into the background. With zero +#: being fully faded and one being fully opaque. + +# resize_debounce_time 0.1 + +#: The time to wait before redrawing the screen when a resize event is +#: received (in seconds). On platforms such as macOS, where the +#: operating system sends events corresponding to the start and end of +#: a resize, this number is ignored. + +# resize_draw_strategy static + +#: Choose how kitty draws a window while a resize is in progress. A +#: value of static means draw the current window contents, mostly +#: unchanged. A value of scale means draw the current window contents +#: scaled. A value of blank means draw a blank window. A value of size +#: means show the window size in cells. + +# resize_in_steps no + +#: Resize the OS window in steps as large as the cells, instead of +#: with the usual pixel accuracy. Combined with initial_window_width +#: and initial_window_height in number of cells, this option can be +#: used to keep the margins as small as possible when resizing the OS +#: window. Note that this does not currently work on Wayland. + +# visual_window_select_characters 1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ + +#: The list of characters for visual window selection. For example, +#: for selecting a window to focus on with focus_visible_window. The +#: value should be a series of unique numbers or alphabets, case +#: insensitive, from the set [0-9A-Z]. Specify your preference as a +#: string of characters. + +# confirm_os_window_close -1 + +#: Ask for confirmation when closing an OS window or a tab with at +#: least this number of kitty windows in it by window manager (e.g. +#: clicking the window close button or pressing the operating system +#: shortcut to close windows) or by the close_tab action. A value of +#: zero disables confirmation. This confirmation also applies to +#: requests to quit the entire application (all OS windows, via the +#: quit action). Negative values are converted to positive ones, +#: however, with shell_integration enabled, using negative values +#: means windows sitting at a shell prompt are not counted, only +#: windows where some command is currently running. Note that if you +#: want confirmation when closing individual windows, you can map the +#: close_window_with_confirmation action. + +#: }}} + +#: Tab bar {{{ + +# tab_bar_edge bottom + +#: The edge to show the tab bar on, top or bottom. + +# tab_bar_margin_width 0.0 + +#: The margin to the left and right of the tab bar (in pts). + +# tab_bar_margin_height 0.0 0.0 + +#: The margin above and below the tab bar (in pts). The first number +#: is the margin between the edge of the OS Window and the tab bar. +#: The second number is the margin between the tab bar and the +#: contents of the current tab. + +# tab_bar_style fade + +#: The tab bar style, can be one of: + +#: fade +#: Each tab's edges fade into the background color. (See also tab_fade) +#: slant +#: Tabs look like the tabs in a physical file. +#: separator +#: Tabs are separated by a configurable separator. (See also +#: tab_separator) +#: powerline +#: Tabs are shown as a continuous line with "fancy" separators. +#: (See also tab_powerline_style) +#: custom +#: A user-supplied Python function called draw_tab is loaded from the file +#: tab_bar.py in the kitty config directory. For examples of how to +#: write such a function, see the functions named draw_tab_with_* in +#: kitty's source code: kitty/tab_bar.py. See also +#: this discussion https://github.com/kovidgoyal/kitty/discussions/4447 +#: for examples from kitty users. +#: hidden +#: The tab bar is hidden. If you use this, you might want to create a mapping +#: for the select_tab action which presents you with a list of tabs and +#: allows for easy switching to a tab. + +# tab_bar_align left + +#: The horizontal alignment of the tab bar, can be one of: left, +#: center, right. + +# tab_bar_min_tabs 2 + +#: The minimum number of tabs that must exist before the tab bar is +#: shown. + +# tab_switch_strategy previous + +#: The algorithm to use when switching to a tab when the current tab +#: is closed. The default of previous will switch to the last used +#: tab. A value of left will switch to the tab to the left of the +#: closed tab. A value of right will switch to the tab to the right of +#: the closed tab. A value of last will switch to the right-most tab. + +# tab_fade 0.25 0.5 0.75 1 + +#: Control how each tab fades into the background when using fade for +#: the tab_bar_style. Each number is an alpha (between zero and one) +#: that controls how much the corresponding cell fades into the +#: background, with zero being no fade and one being full fade. You +#: can change the number of cells used by adding/removing entries to +#: this list. + +# tab_separator " ┇" + +#: The separator between tabs in the tab bar when using separator as +#: the tab_bar_style. + +# tab_powerline_style angled + +#: The powerline separator style between tabs in the tab bar when +#: using powerline as the tab_bar_style, can be one of: angled, +#: slanted, round. + +# tab_activity_symbol none + +#: Some text or a Unicode symbol to show on the tab if a window in the +#: tab that does not have focus has some activity. If you want to use +#: leading or trailing spaces, surround the text with quotes. See +#: tab_title_template for how this is rendered. + +# tab_title_template "{fmt.fg.red}{bell_symbol}{activity_symbol}{fmt.fg.tab}{title}" + +#: A template to render the tab title. The default just renders the +#: title with optional symbols for bell and activity. If you wish to +#: include the tab-index as well, use something like: {index}:{title}. +#: Useful if you have shortcuts mapped for goto_tab N. If you prefer +#: to see the index as a superscript, use {sup.index}. In addition you +#: can use {layout_name} for the current layout name, {num_windows} +#: for the number of windows in the tab and {num_window_groups} for +#: the number of window groups (not counting overlay windows) in the +#: tab. Note that formatting is done by Python's string formatting +#: machinery, so you can use, for instance, {layout_name[:2].upper()} +#: to show only the first two letters of the layout name, upper-cased. +#: If you want to style the text, you can use styling directives, for +#: example: +#: `{fmt.fg.red}red{fmt.fg.tab}normal{fmt.bg._00FF00}greenbg{fmt.bg.tab}`. +#: Similarly, for bold and italic: +#: `{fmt.bold}bold{fmt.nobold}normal{fmt.italic}italic{fmt.noitalic}`. +#: Note that for backward compatibility, if {bell_symbol} or +#: {activity_symbol} are not present in the template, they are +#: prepended to it. + +# active_tab_title_template none + +#: Template to use for active tabs. If not specified falls back to +#: tab_title_template. + +# active_tab_foreground #000 +# active_tab_background #eee +# active_tab_font_style bold-italic +# inactive_tab_foreground #444 +# inactive_tab_background #999 +# inactive_tab_font_style normal + +#: Tab bar colors and styles. + +# tab_bar_background none + +#: Background color for the tab bar. Defaults to using the terminal +#: background color. + +# tab_bar_margin_color none + +#: Color for the tab bar margin area. Defaults to using the terminal +#: background color. + +#: }}} + +#: Color scheme {{{ + +include ./theme.conf + +# foreground #dddddd +# background #000000 + +#: The foreground and background colors. + +background_opacity 0.9 + +#: The opacity of the background. A number between zero and one, where +#: one is opaque and zero is fully transparent. This will only work if +#: supported by the OS (for instance, when using a compositor under +#: X11). Note that it only sets the background color's opacity in +#: cells that have the same background color as the default terminal +#: background, so that things like the status bar in vim, powerline +#: prompts, etc. still look good. But it means that if you use a color +#: theme with a background color in your editor, it will not be +#: rendered as transparent. Instead you should change the default +#: background color in your kitty config and not use a background +#: color in the editor color scheme. Or use the escape codes to set +#: the terminals default colors in a shell script to launch your +#: editor. Be aware that using a value less than 1.0 is a (possibly +#: significant) performance hit. If you want to dynamically change +#: transparency of windows, set dynamic_background_opacity to yes +#: (this is off by default as it has a performance cost). Changing +#: this option when reloading the config will only work if +#: dynamic_background_opacity was enabled in the original config. + +# background_image none + +#: Path to a background image. Must be in PNG format. + +# background_image_layout tiled + +#: Whether to tile, scale or clamp the background image. The value can +#: be one of tiled, mirror-tiled, scaled, clamped. + +# background_image_linear no + +#: When background image is scaled, whether linear interpolation +#: should be used. + +# dynamic_background_opacity no + +#: Allow changing of the background_opacity dynamically, using either +#: keyboard shortcuts (increase_background_opacity and +#: decrease_background_opacity) or the remote control facility. +#: Changing this option by reloading the config is not supported. + +# background_tint 0.0 + +#: How much to tint the background image by the background color. The +#: tint is applied only under the text area, not margin/borders. This +#: option makes it easier to read the text. Tinting is done using the +#: current background color for each window. This option applies only +#: if background_opacity is set and transparent windows are supported +#: or background_image is set. + +# dim_opacity 0.75 + +#: How much to dim text that has the DIM/FAINT attribute set. One +#: means no dimming and zero means fully dimmed (i.e. invisible). + +# selection_foreground #000000 +# selection_background #fffacd + +#: The foreground and background colors for text selected with the +#: mouse. Setting both of these to none will cause a "reverse video" +#: effect for selections, where the selection will be the cell text +#: color and the text will become the cell background color. Setting +#: only selection_foreground to none will cause the foreground color +#: to be used unchanged. Note that these colors can be overridden by +#: the program running in the terminal. + +#: The color table {{{ + +#: The 256 terminal colors. There are 8 basic colors, each color has a +#: dull and bright version, for the first 16 colors. You can set the +#: remaining 240 colors as color16 to color255. + +#color0 #000000 +#color8 #7f7f7f + +#: black + +#color1 #cd0000 +#color9 #ff0000 + +#: red + +#color2 #00cd00 +#color10 #00ff00 + +#: green + +#color3 #cdcd00 +#color11 #ffff00 + +#: yellow + +#color4 #0000ee +#color12 #5c5cff + +#: blue + +#color5 #cd00cd +#color13 #ff00ff + +#: magenta + +#color6 #00cdcd +#color14 #00ffff + +#: cyan + +#color7 #e5e5e5 +#color15 #ffffff + +#: white + +# mark1_foreground black + +#: Color for marks of type 1 + +# mark1_background #98d3cb + +#: Color for marks of type 1 (light steel blue) + +# mark2_foreground black + +#: Color for marks of type 2 + +# mark2_background #f2dcd3 + +#: Color for marks of type 1 (beige) + +# mark3_foreground black + +#: Color for marks of type 3 + +# mark3_background #f274bc + +#: Color for marks of type 3 (violet) + +#: }}} + +#: }}} + +#: Advanced {{{ + +# shell . + +#: The shell program to execute. The default value of . means to use +#: whatever shell is set as the default shell for the current user. +#: Note that on macOS if you change this, you might need to add +#: --login and --interactive to ensure that the shell starts in +#: interactive mode and reads its startup rc files. + +# editor . + +#: The terminal based text editor (such as vim or nano) to use when +#: editing the kitty config file or similar tasks. + +#: The default value of . means to use the environment variables +#: VISUAL and EDITOR in that order. If these variables aren't set, +#: kitty will run your shell ($SHELL -l -i -c env) to see if your +#: shell startup rc files set VISUAL or EDITOR. If that doesn't work, +#: kitty will cycle through various known editors (vim, emacs, etc.) +#: and take the first one that exists on your system. + +# close_on_child_death no + +#: Close the window when the child process (shell) exits. With the +#: default value no, the terminal will remain open when the child +#: exits as long as there are still processes outputting to the +#: terminal (for example disowned or backgrounded processes). When +#: enabled with yes, the window will close as soon as the child +#: process exits. Note that setting it to yes means that any +#: background processes still using the terminal can fail silently +#: because their stdout/stderr/stdin no longer work. + +# allow_remote_control no + +#: Allow other programs to control kitty. If you turn this on, other +#: programs can control all aspects of kitty, including sending text +#: to kitty windows, opening new windows, closing windows, reading the +#: content of windows, etc. Note that this even works over SSH +#: connections. You can choose to either allow any program running +#: within kitty to control it with yes, or only allow programs that +#: connect to the socket (specified with the listen_on config option +#: or kitty --listen-on command line option) with the value socket- +#: only. The latter is useful if you want to prevent programs running +#: on a remote computer over SSH from controlling kitty. Reloading the +#: config will not affect this option. + +# listen_on none + +#: Listen to the specified UNIX socket for remote control connections. +#: Note that this will apply to all kitty instances. It can be +#: overridden by the kitty --listen-on command line option, which +#: supports listening on TCP socket. This option accepts only UNIX +#: sockets, such as unix:${TEMP}/mykitty or unix:@mykitty (on Linux). +#: Environment variables are expanded and relative paths are resolved +#: with respect to the temporary directory. If {kitty_pid} is present, +#: then it is replaced by the PID of the kitty process, otherwise the +#: PID of the kitty process is appended to the value, with a hyphen. +#: This option is ignored unless you also set allow_remote_control to +#: enable remote control. See the help for kitty --listen-on for more +#: details. Changing this option by reloading the config is not +#: supported. + +# env + +#: Specify the environment variables to be set in all child processes. +#: Using the name with an equal sign (e.g. env VAR=) will set it to +#: the empty string. Specifying only the name (e.g. env VAR) will +#: remove the variable from the child process' environment. Note that +#: environment variables are expanded recursively, for example:: + +#: env VAR1=a +#: env VAR2=${HOME}/${VAR1}/b + +#: The value of VAR2 will be /a/b. + +# watcher + +#: Path to python file which will be loaded for watchers +#: . Can be +#: specified more than once to load multiple watchers. The watchers +#: will be added to every kitty window. Relative paths are resolved +#: relative to the kitty config directory. Note that reloading the +#: config will only affect windows created after the reload. + +# exe_search_path + +#: Control where kitty finds the programs to run. The default search +#: order is: First search the system wide PATH, then ~/.local/bin and +#: ~/bin. If still not found, the PATH defined in the login shell +#: after sourcing all its startup files is tried. Finally, if present, +#: the PATH specified by the env option is tried. + +#: This option allows you to prepend, append, or remove paths from +#: this search order. It can be specified multiple times for multiple +#: paths. A simple path will be prepended to the search order. A path +#: that starts with the + sign will be append to the search order, +#: after ~/bin above. A path that starts with the - sign will be +#: removed from the entire search order. For example:: + +#: exe_search_path /some/prepended/path +#: exe_search_path +/some/appended/path +#: exe_search_path -/some/excluded/path + +# update_check_interval 24 + +#: The interval to periodically check if an update to kitty is +#: available (in hours). If an update is found, a system notification +#: is displayed informing you of the available update. The default is +#: to check every 24 hours, set to zero to disable. Update checking is +#: only done by the official binary builds. Distro packages or source +#: builds do not do update checking. Changing this option by reloading +#: the config is not supported. + +# startup_session none + +#: Path to a session file to use for all kitty instances. Can be +#: overridden by using the kitty --session command line option for +#: individual instances. See sessions +#: in the +#: kitty documentation for details. Note that relative paths are +#: interpreted with respect to the kitty config directory. Environment +#: variables in the path are expanded. Changing this option by +#: reloading the config is not supported. + +# clipboard_control write-clipboard write-primary read-clipboard-ask read-primary-ask + +#: Allow programs running in kitty to read and write from the +#: clipboard. You can control exactly which actions are allowed. The +#: possible actions are: write-clipboard, read-clipboard, write- +#: primary, read-primary, read-clipboard-ask, read-primary-ask. The +#: default is to allow writing to the clipboard and primary selection +#: and to ask for permission when a program tries to read from the +#: clipboard. Note that disabling the read confirmation is a security +#: risk as it means that any program, even the ones running on a +#: remote server via SSH can read your clipboard. See also +#: clipboard_max_size. + +# clipboard_max_size 64 + +#: The maximum size (in MB) of data from programs running in kitty +#: that will be stored for writing to the system clipboard. A value of +#: zero means no size limit is applied. See also clipboard_control. + +# file_transfer_confirmation_bypass + +#: The password that can be supplied to the file transfer kitten +#: to skip the +#: transfer confirmation prompt. This should only be used when +#: initiating transfers from trusted computers, over trusted networks +#: or encrypted transports, as it allows any programs running on the +#: remote machine to read/write to the local filesystem, without +#: permission. + +# allow_hyperlinks yes + +#: Process hyperlink escape sequences (OSC 8). If disabled OSC 8 +#: escape sequences are ignored. Otherwise they become clickable +#: links, that you can click with the mouse or by using the hints +#: kitten . The +#: special value of ask means that kitty will ask before opening the +#: link when clicked. + +# shell_integration enabled + +#: Enable shell integration on supported shells. This enables features +#: such as jumping to previous prompts, browsing the output of the +#: previous command in a pager, etc. on supported shells. Set to +#: disabled to turn off shell integration, completely. It is also +#: possible to disable individual features, set to a space separated +#: list of these values: no-rc, no-cursor, no-title, no-cwd, no- +#: prompt-mark, no-complete. See Shell integration +#: for details. + +# allow_cloning ask + +#: Control whether programs running in the terminal can request new +#: windows to be created. The canonical example is clone-in-kitty +#: . +#: By default, kitty will ask for permission for each clone request. +#: Allowing cloning unconditionally gives programs running in the +#: terminal (including over SSH) permission to execute arbitrary code, +#: as the user who is running the terminal, on the computer that the +#: terminal is running on. + +# clone_source_strategies venv,conda,env_var,path + +#: Control what shell code is sourced when running clone-in-kitty in +#: the newly cloned window. The supported strategies are: + +#: venv +#: Source the file $VIRTUAL_ENV/bin/activate. This is used by the +#: Python stdlib venv module and allows cloning venvs automatically. +#: conda +#: Run conda activate $CONDA_DEFAULT_ENV. This supports the virtual +#: environments created by conda. +#: env_var +#: Execute the contents of the environment variable +#: KITTY_CLONE_SOURCE_CODE with eval. +#: path +#: Source the file pointed to by the environment variable +#: KITTY_CLONE_SOURCE_PATH. + +#: This option must be a comma separated list of the above values. +#: This only source the first valid one in the above order. + +# term xterm-kitty + +#: The value of the TERM environment variable to set. Changing this +#: can break many terminal programs, only change it if you know what +#: you are doing, not because you read some advice on "Stack Overflow" +#: to change it. The TERM variable is used by various programs to get +#: information about the capabilities and behavior of the terminal. If +#: you change it, depending on what programs you run, and how +#: different the terminal you are changing it to is, various things +#: from key-presses, to colors, to various advanced features may not +#: work. Changing this option by reloading the config will only affect +#: newly created windows. + +#: }}} + +#: OS specific tweaks {{{ + +# wayland_titlebar_color system + +#: The color of the kitty window's titlebar on Wayland systems with +#: client side window decorations such as GNOME. A value of system +#: means to use the default system color, a value of background means +#: to use the background color of the currently active window and +#: finally you can use an arbitrary color, such as #12af59 or red. + +# macos_titlebar_color system + +#: The color of the kitty window's titlebar on macOS. A value of +#: system means to use the default system color, light or dark can +#: also be used to set it explicitly. A value of background means to +#: use the background color of the currently active window and finally +#: you can use an arbitrary color, such as #12af59 or red. WARNING: +#: This option works by using a hack when arbitrary color (or +#: background) is configured, as there is no proper Cocoa API for it. +#: It sets the background color of the entire window and makes the +#: titlebar transparent. As such it is incompatible with +#: background_opacity. If you want to use both, you are probably +#: better off just hiding the titlebar with hide_window_decorations. + +# macos_option_as_alt no + +#: Use the Option key as an Alt key on macOS. With this set to no, +#: kitty will use the macOS native Option+Key to enter Unicode +#: character behavior. This will break any Alt+Key keyboard shortcuts +#: in your terminal programs, but you can use the macOS Unicode input +#: technique. You can use the values: left, right or both to use only +#: the left, right or both Option keys as Alt, instead. Note that +#: kitty itself always treats Option the same as Alt. This means you +#: cannot use this option to configure different kitty shortcuts for +#: Option+Key vs. Alt+Key. Also, any kitty shortcuts using +#: Option/Alt+Key will take priority, so that any such key presses +#: will not be passed to terminal programs running inside kitty. +#: Changing this option by reloading the config is not supported. + +# macos_hide_from_tasks no + +#: Hide the kitty window from running tasks on macOS (⌘+Tab and the +#: Dock). Changing this option by reloading the config is not +#: supported. + +# macos_quit_when_last_window_closed no + +#: Have kitty quit when all the top-level windows are closed on macOS. +#: By default, kitty will stay running, even with no open windows, as +#: is the expected behavior on macOS. + +# macos_window_resizable yes + +#: Disable this if you want kitty top-level OS windows to not be +#: resizable on macOS. Changing this option by reloading the config +#: will only affect newly created OS windows. + +# macos_thicken_font 0 + +#: Draw an extra border around the font with the given width, to +#: increase legibility at small font sizes on macOS. For example, a +#: value of 0.75 will result in rendering that looks similar to sub- +#: pixel antialiasing at common font sizes. + +# macos_traditional_fullscreen no + +#: Use the macOS traditional full-screen transition, that is faster, +#: but less pretty. + +# macos_show_window_title_in all + +#: Control where the window title is displayed on macOS. A value of +#: window will show the title of the currently active window at the +#: top of the macOS window. A value of menubar will show the title of +#: the currently active window in the macOS global menu bar, making +#: use of otherwise wasted space. A value of all will show the title +#: in both places, and none hides the title. See +#: macos_menubar_title_max_length for how to control the length of the +#: title in the menu bar. + +# macos_menubar_title_max_length 0 + +#: The maximum number of characters from the window title to show in +#: the macOS global menu bar. Values less than one means that there is +#: no maximum limit. + +# macos_custom_beam_cursor no + +#: Use a custom mouse cursor for macOS that is easier to see on both +#: light and dark backgrounds. Nowadays, the default macOS cursor +#: already comes with a white border. WARNING: this might make your +#: mouse cursor invisible on dual GPU machines. Changing this option +#: by reloading the config is not supported. + +# macos_colorspace srgb + +#: The colorspace in which to interpret terminal colors. The default +#: of srgb will cause colors to match those seen in web browsers. The +#: value of default will use whatever the native colorspace of the +#: display is. The value of displayp3 will use Apple's special +#: snowflake display P3 color space, which will result in over +#: saturated (brighter) colors with some color shift. Reloading +#: configuration will change this value only for newly created OS +#: windows. + +# linux_display_server auto + +#: Choose between Wayland and X11 backends. By default, an appropriate +#: backend based on the system state is chosen automatically. Set it +#: to x11 or wayland to force the choice. Changing this option by +#: reloading the config is not supported. + +#: }}} + +#: Keyboard shortcuts {{{ + +#: Keys are identified simply by their lowercase Unicode characters. +#: For example: a for the A key, [ for the left square bracket key, +#: etc. For functional keys, such as Enter or Escape, the names are +#: present at Functional key definitions +#: . For modifier keys, the names are ctrl (control, ⌃), +#: shift (⇧), alt (opt, option, ⌥), super (cmd, command, ⌘). See also: +#: GLFW mods + +#: On Linux you can also use XKB key names to bind keys that are not +#: supported by GLFW. See XKB keys +#: for a list of key names. The name to use is the part +#: after the XKB_KEY_ prefix. Note that you can only use an XKB key +#: name for keys that are not known as GLFW keys. + +#: Finally, you can use raw system key codes to map keys, again only +#: for keys that are not known as GLFW keys. To see the system key +#: code for a key, start kitty with the kitty --debug-input option, +#: kitty will output some debug text for every key event. In that text +#: look for native_code, the value of that becomes the key name in the +#: shortcut. For example: + +#: .. code-block:: none + +#: on_key_input: glfw key: 0x61 native_code: 0x61 action: PRESS mods: none text: 'a' + +#: Here, the key name for the A key is 0x61 and you can use it with:: + +#: map ctrl+0x61 something + +#: to map Ctrl+A to something. + +#: You can use the special action no_op to unmap a keyboard shortcut +#: that is assigned in the default configuration:: + +#: map kitty_mod+space no_op + +#: If you would like kitty to completely ignore a key event, not even +#: sending it to the program running in the terminal, map it to +#: discard_event:: + +#: map kitty_mod+f1 discard_event + +#: You can combine multiple actions to be triggered by a single +#: shortcut with combine action, using the syntax below:: + +#: map key combine action1 action2 action3 ... + +#: For example:: + +#: map kitty_mod+e combine : new_window : next_layout + +#: This will create a new window and switch to the next available +#: layout. + +#: You can use multi-key shortcuts with the syntax shown below:: + +#: map key1>key2>key3 action + +#: For example:: + +#: map ctrl+f>2 set_font_size 20 + +#: The full list of actions that can be mapped to key presses is +#: available here . + +# kitty_mod ctrl+shift + +#: Special modifier key alias for default shortcuts. You can change +#: the value of this option to alter all default shortcuts that use +#: kitty_mod. + +# clear_all_shortcuts no + +#: Remove all shortcut definitions up to this point. Useful, for +#: instance, to remove the default shortcuts. + +# action_alias + +#: E.g. action_alias launch_tab launch --type=tab --cwd=current + +#: Define action aliases to avoid repeating the same options in +#: multiple mappings. Aliases can be defined for any action and will +#: be expanded recursively. For example, the above alias allows you to +#: create mappings to launch a new tab in the current working +#: directory without duplication:: + +#: map f1 launch_tab vim +#: map f2 launch_tab emacs + +#: Similarly, to alias kitten invocation:: + +#: action_alias hints kitten hints --hints-offset=0 + +# kitten_alias + +#: E.g. kitten_alias hints hints --hints-offset=0 + +#: Like action_alias above, but specifically for kittens. Generally, +#: prefer to use action_alias. This option is a legacy version, +#: present for backwards compatibility. It causes all invocations of +#: the aliased kitten to be substituted. So the example above will +#: cause all invocations of the hints kitten to have the --hints- +#: offset=0 option applied. + +#: Clipboard {{{ + +#: Copy to clipboard + +# map kitty_mod+c copy_to_clipboard +# map cmd+c copy_to_clipboard + +#:: There is also a copy_or_interrupt action that can be optionally +#:: mapped to Ctrl+C. It will copy only if there is a selection and +#:: send an interrupt otherwise. Similarly, +#:: copy_and_clear_or_interrupt will copy and clear the selection or +#:: send an interrupt if there is no selection. + +#: Paste from clipboard + +# map kitty_mod+v paste_from_clipboard +# map cmd+v paste_from_clipboard + +#: Paste from selection + +# map kitty_mod+s paste_from_selection +# map shift+insert paste_from_selection + +#: Pass selection to program + +# map kitty_mod+o pass_selection_to_program + +#:: You can also pass the contents of the current selection to any +#:: program with pass_selection_to_program. By default, the system's +#:: open program is used, but you can specify your own, the selection +#:: will be passed as a command line argument to the program. For +#:: example:: + +#:: map kitty_mod+o pass_selection_to_program firefox + +#:: You can pass the current selection to a terminal program running +#:: in a new kitty window, by using the @selection placeholder:: + +#:: map kitty_mod+y new_window less @selection + +#: }}} + +#: Scrolling {{{ + +#: Scroll line up + +# map kitty_mod+up scroll_line_up +# map kitty_mod+k scroll_line_up +# map opt+cmd+page_up scroll_line_up +# map cmd+up scroll_line_up + +#: Scroll line down + +# map kitty_mod+down scroll_line_down +# map kitty_mod+j scroll_line_down +# map opt+cmd+page_down scroll_line_down +# map cmd+down scroll_line_down + +#: Scroll page up + +# map kitty_mod+page_up scroll_page_up +# map cmd+page_up scroll_page_up + +#: Scroll page down + +# map kitty_mod+page_down scroll_page_down +# map cmd+page_down scroll_page_down + +#: Scroll to top + +# map kitty_mod+home scroll_home +# map cmd+home scroll_home + +#: Scroll to bottom + +# map kitty_mod+end scroll_end +# map cmd+end scroll_end + +#: Scroll to previous shell prompt + +# map kitty_mod+z scroll_to_prompt -1 + +#:: Use a parameter of 0 for scroll_to_prompt to scroll to the last +#:: jumped to or the last clicked position. Requires shell +#:: integration +#:: to work. + +#: Scroll to next shell prompt + +# map kitty_mod+x scroll_to_prompt 1 + +#: Browse scrollback buffer in pager + +# map kitty_mod+h show_scrollback + +#:: You can pipe the contents of the current screen and history +#:: buffer as STDIN to an arbitrary program using launch --stdin- +#:: source. For example, the following opens the scrollback buffer in +#:: less in an overlay window:: + +#:: map f1 launch --stdin-source=@screen_scrollback --stdin-add-formatting --type=overlay less +G -R + +#:: For more details on piping screen and buffer contents to external +#:: programs, see launch . + +#: Browse output of the last shell command in pager + +# map kitty_mod+g show_last_command_output + +#:: You can also define additional shortcuts to get the command +#:: output. For example, to get the first command output on screen:: + +#:: map f1 show_first_command_output_on_screen + +#:: To get the command output that was last accessed by a keyboard +#:: action or mouse action:: + +#:: map f1 show_last_visited_command_output + +#:: You can pipe the output of the last command run in the shell +#:: using the launch action. For example, the following opens the +#:: output in less in an overlay window:: + +#:: map f1 launch --stdin-source=@last_cmd_output --stdin-add-formatting --type=overlay less +G -R + +#:: To get the output of the first command on the screen, use +#:: @first_cmd_output_on_screen. To get the output of the last jumped +#:: to command, use @last_visited_cmd_output. + +#:: Requires shell integration +#:: to work. + +#: }}} + +#: Window management {{{ + +#: New window + +# map kitty_mod+enter new_window +# map cmd+enter new_window + +#:: You can open a new kitty window running an arbitrary program, for +#:: example:: + +#:: map kitty_mod+y launch mutt + +#:: You can open a new window with the current working directory set +#:: to the working directory of the current window using:: + +#:: map ctrl+alt+enter launch --cwd=current + +#:: You can open a new window that is allowed to control kitty via +#:: the kitty remote control facility with launch --allow-remote- +#:: control. Any programs running in that window will be allowed to +#:: control kitty. For example:: + +#:: map ctrl+enter launch --allow-remote-control some_program + +#:: You can open a new window next to the currently active window or +#:: as the first window, with:: + +#:: map ctrl+n launch --location=neighbor +#:: map ctrl+f launch --location=first + +#:: For more details, see launch +#:: . + +#: New OS window + +# map kitty_mod+n new_os_window +# map cmd+n new_os_window + +#:: Works like new_window above, except that it opens a top-level OS +#:: window. In particular you can use new_os_window_with_cwd to open +#:: a window with the current working directory. + +#: Close window + +# map kitty_mod+w close_window +# map shift+cmd+d close_window + +#: Next window + +# map kitty_mod+] next_window + +#: Previous window + +# map kitty_mod+[ previous_window + +#: Move window forward + +# map kitty_mod+f move_window_forward + +#: Move window backward + +# map kitty_mod+b move_window_backward + +#: Move window to top + +# map kitty_mod+` move_window_to_top + +#: Start resizing window + +# map kitty_mod+r start_resizing_window +# map cmd+r start_resizing_window + +#: First window + +# map kitty_mod+1 first_window +# map cmd+1 first_window + +#: Second window + +# map kitty_mod+2 second_window +# map cmd+2 second_window + +#: Third window + +# map kitty_mod+3 third_window +# map cmd+3 third_window + +#: Fourth window + +# map kitty_mod+4 fourth_window +# map cmd+4 fourth_window + +#: Fifth window + +# map kitty_mod+5 fifth_window +# map cmd+5 fifth_window + +#: Sixth window + +# map kitty_mod+6 sixth_window +# map cmd+6 sixth_window + +#: Seventh window + +# map kitty_mod+7 seventh_window +# map cmd+7 seventh_window + +#: Eight window + +# map kitty_mod+8 eighth_window +# map cmd+8 eighth_window + +#: Ninth window + +# map kitty_mod+9 ninth_window +# map cmd+9 ninth_window + +#: Tenth window + +# map kitty_mod+0 tenth_window + +#: Visually select and focus window + +# map kitty_mod+f7 focus_visible_window + +#:: Display overlay numbers and alphabets on the window, and switch +#:: the focus to the window when you press the key. When there are +#:: only two windows, the focus will be switched directly without +#:: displaying the overlay. You can change the overlay characters and +#:: their order with option visual_window_select_characters. + +#: Visually swap window with another + +# map kitty_mod+f8 swap_with_window + +#:: Works like focus_visible_window above, but swaps the window. + +#: }}} + +#: Tab management {{{ + +#: Next tab + +# map kitty_mod+right next_tab +# map shift+cmd+] next_tab +# map ctrl+tab next_tab + +#: Previous tab + +# map kitty_mod+left previous_tab +# map shift+cmd+[ previous_tab +# map ctrl+shift+tab previous_tab + +#: New tab + +# map kitty_mod+t new_tab +# map cmd+t new_tab + +#: Close tab + +# map kitty_mod+q close_tab +# map cmd+w close_tab + +#: Close OS window + +# map shift+cmd+w close_os_window + +#: Move tab forward + +# map kitty_mod+. move_tab_forward + +#: Move tab backward + +# map kitty_mod+, move_tab_backward + +#: Set tab title + +# map kitty_mod+alt+t set_tab_title +# map shift+cmd+i set_tab_title + + +#: You can also create shortcuts to go to specific tabs, with 1 being +#: the first tab, 2 the second tab and -1 being the previously active +#: tab, and any number larger than the last tab being the last tab:: + +#: map ctrl+alt+1 goto_tab 1 +#: map ctrl+alt+2 goto_tab 2 + +#: Just as with new_window above, you can also pass the name of +#: arbitrary commands to run when using new_tab and new_tab_with_cwd. +#: Finally, if you want the new tab to open next to the current tab +#: rather than at the end of the tabs list, use:: + +#: map ctrl+t new_tab !neighbor [optional cmd to run] +#: }}} + +#: Layout management {{{ + +#: Next layout + +# map kitty_mod+l next_layout + + +#: You can also create shortcuts to switch to specific layouts:: + +#: map ctrl+alt+t goto_layout tall +#: map ctrl+alt+s goto_layout stack + +#: Similarly, to switch back to the previous layout:: + +#: map ctrl+alt+p last_used_layout + +#: There is also a toggle_layout action that switches to the named +#: layout or back to the previous layout if in the named layout. +#: Useful to temporarily "zoom" the active window by switching to the +#: stack layout:: + +#: map ctrl+alt+z toggle_layout stack +#: }}} + +#: Font sizes {{{ + +#: You can change the font size for all top-level kitty OS windows at +#: a time or only the current one. + +#: Increase font size + +# map kitty_mod+equal change_font_size all +2.0 +# map kitty_mod+plus change_font_size all +2.0 +# map kitty_mod+kp_add change_font_size all +2.0 +# map cmd+plus change_font_size all +2.0 +# map cmd+equal change_font_size all +2.0 +# map shift+cmd+equal change_font_size all +2.0 + +#: Decrease font size + +# map kitty_mod+minus change_font_size all -2.0 +# map kitty_mod+kp_subtract change_font_size all -2.0 +# map cmd+minus change_font_size all -2.0 +# map shift+cmd+minus change_font_size all -2.0 + +#: Reset font size + +# map kitty_mod+backspace change_font_size all 0 +# map cmd+0 change_font_size all 0 + + +#: To setup shortcuts for specific font sizes:: + +#: map kitty_mod+f6 change_font_size all 10.0 + +#: To setup shortcuts to change only the current OS window's font +#: size:: + +#: map kitty_mod+f6 change_font_size current 10.0 +#: }}} + +#: Select and act on visible text {{{ + +#: Use the hints kitten to select text and either pass it to an +#: external program or insert it into the terminal or copy it to the +#: clipboard. + +#: Open URL + +# map kitty_mod+e open_url_with_hints + +#:: Open a currently visible URL using the keyboard. The program used +#:: to open the URL is specified in open_url_with. + +#: Insert selected path + +# map kitty_mod+p>f kitten hints --type path --program - + +#:: Select a path/filename and insert it into the terminal. Useful, +#:: for instance to run git commands on a filename output from a +#:: previous git command. + +#: Open selected path + +# map kitty_mod+p>shift+f kitten hints --type path + +#:: Select a path/filename and open it with the default open program. + +#: Insert selected line + +# map kitty_mod+p>l kitten hints --type line --program - + +#:: Select a line of text and insert it into the terminal. Useful for +#:: the output of things like: `ls -1`. + +#: Insert selected word + +# map kitty_mod+p>w kitten hints --type word --program - + +#:: Select words and insert into terminal. + +#: Insert selected hash + +# map kitty_mod+p>h kitten hints --type hash --program - + +#:: Select something that looks like a hash and insert it into the +#:: terminal. Useful with git, which uses SHA1 hashes to identify +#:: commits. + +#: Open the selected file at the selected line + +# map kitty_mod+p>n kitten hints --type linenum + +#:: Select something that looks like filename:linenum and open it in +#:: vim at the specified line number. + +#: Open the selected hyperlink + +# map kitty_mod+p>y kitten hints --type hyperlink + +#:: Select a hyperlink (i.e. a URL that has been marked as such by +#:: the terminal program, for example, by `ls --hyperlink=auto`). + + +#: The hints kitten has many more modes of operation that you can map +#: to different shortcuts. For a full description see hints kitten +#: . +#: }}} + +#: Miscellaneous {{{ + +#: Toggle fullscreen + +# map kitty_mod+f11 toggle_fullscreen +# map ctrl+cmd+f toggle_fullscreen + +#: Toggle maximized + +# map kitty_mod+f10 toggle_maximized + +#: Toggle macOS secure keyboard entry + +# map opt+cmd+s toggle_macos_secure_keyboard_entry + +#: Unicode input + +# map kitty_mod+u kitten unicode_input +# map ctrl+cmd+space kitten unicode_input + +#: Edit config file + +# map kitty_mod+f2 edit_config_file +# map cmd+, edit_config_file + +#: Open the kitty command shell + +# map kitty_mod+escape kitty_shell window + +#:: Open the kitty shell in a new window / tab / overlay / os_window +#:: to control kitty using commands. + +#: Increase background opacity + +# map kitty_mod+a>m set_background_opacity +0.1 + +#: Decrease background opacity + +# map kitty_mod+a>l set_background_opacity -0.1 + +#: Make background fully opaque + +# map kitty_mod+a>1 set_background_opacity 1 + +#: Reset background opacity + +# map kitty_mod+a>d set_background_opacity default + +#: Reset the terminal + +# map kitty_mod+delete clear_terminal reset active +# map opt+cmd+r clear_terminal reset active + +#:: You can create shortcuts to clear/reset the terminal. For +#:: example:: + +#:: # Reset the terminal +#:: map f1 clear_terminal reset active +#:: # Clear the terminal screen by erasing all contents +#:: map f1 clear_terminal clear active +#:: # Clear the terminal scrollback by erasing it +#:: map f1 clear_terminal scrollback active +#:: # Scroll the contents of the screen into the scrollback +#:: map f1 clear_terminal scroll active +#:: # Clear everything up to the line with the cursor +#:: map f1 clear_terminal to_cursor active + +#:: If you want to operate on all kitty windows instead of just the +#:: current one, use all instead of active. + +#:: It is also possible to remap Ctrl+L to both scroll the current +#:: screen contents into the scrollback buffer and clear the screen, +#:: instead of just clearing the screen, for example, for ZSH add the +#:: following to ~/.zshrc: + +#:: .. code-block:: zsh + +#:: scroll-and-clear-screen() { +#:: printf '\n%.0s' {1..$LINES} +#:: zle clear-screen +#:: } +#:: zle -N scroll-and-clear-screen +#:: bindkey '^l' scroll-and-clear-screen + +#: Clear up to cursor line + +# map cmd+k clear_terminal to_cursor active + +#: Reload kitty.conf + +# map kitty_mod+f5 load_config_file +# map ctrl+cmd+, load_config_file + +#:: Reload kitty.conf, applying any changes since the last time it +#:: was loaded. Note that a handful of options cannot be dynamically +#:: changed and require a full restart of kitty. Particularly, when +#:: changing shortcuts for actions located on the macOS global menu +#:: bar, a full restart is needed. You can also map a keybinding to +#:: load a different config file, for example:: + +#:: map f5 load_config /path/to/alternative/kitty.conf + +#:: Note that all options from the original kitty.conf are discarded, +#:: in other words the new configuration *replace* the old ones. + +#: Debug kitty configuration + +# map kitty_mod+f6 debug_config +# map opt+cmd+, debug_config + +#:: Show details about exactly what configuration kitty is running +#:: with and its host environment. Useful for debugging issues. + +#: Send arbitrary text on key presses + +#:: E.g. map ctrl+shift+alt+h send_text all Hello World + +#:: You can tell kitty to send arbitrary (UTF-8) encoded text to the +#:: client program when pressing specified shortcut keys. For +#:: example:: + +#:: map ctrl+alt+a send_text all Special text + +#:: This will send "Special text" when you press the Ctrl+Alt+A key +#:: combination. The text to be sent is a python string literal so +#:: you can use escapes like \x1b to send control codes or \u21fb to +#:: send Unicode characters (or you can just input the Unicode +#:: characters directly as UTF-8 text). You can use `kitty +kitten +#:: show_key` to get the key escape codes you want to emulate. + +#:: The first argument to send_text is the keyboard modes in which to +#:: activate the shortcut. The possible values are normal, +#:: application, kitty or a comma separated combination of them. The +#:: modes normal and application refer to the DECCKM cursor key mode +#:: for terminals, and kitty refers to the kitty extended keyboard +#:: protocol. The special value all means all of them. + +#:: Some more examples:: + +#:: # Output a word and move the cursor to the start of the line (like typing and pressing Home) +#:: map ctrl+alt+a send_text normal Word\x1b[H +#:: map ctrl+alt+a send_text application Word\x1bOH +#:: # Run a command at a shell prompt (like typing the command and pressing Enter) +#:: map ctrl+alt+a send_text normal,application some command with arguments\r + +#: Open kitty Website + +# map shift+cmd+/ open_url https://sw.kovidgoyal.net/kitty/ + +#: }}} + +#: }}} diff --git a/kitty/kitty/kitty.conf b/kitty/kitty/kitty.conf new file mode 100755 index 0000000..a467cc2 --- /dev/null +++ b/kitty/kitty/kitty.conf @@ -0,0 +1,2169 @@ +# vim:fileencoding=utf-8:foldmethod=marker + +#: Fonts {{{ + +#: kitty has very powerful font management. You can configure +#: individual font faces and even specify special fonts for particular +#: characters. + +font_family JetBrains Mono +# bold_font auto +# italic_font auto +# bold_italic_font auto + +#: You can specify different fonts for the bold/italic/bold-italic +#: variants. To get a full list of supported fonts use the `kitty +#: +list-fonts` command. By default they are derived automatically, by +#: the OSes font system. When bold_font or bold_italic_font is set to +#: auto on macOS, the priority of bold fonts is semi-bold, bold, +#: heavy. Setting them manually is useful for font families that have +#: many weight variants like Book, Medium, Thick, etc. For example:: + +#: font_family Operator Mono Book +#: bold_font Operator Mono Medium +#: italic_font Operator Mono Book Italic +#: bold_italic_font Operator Mono Medium Italic + +# font_size 11.0 + +#: Font size (in pts) + +# force_ltr no + +#: kitty does not support BIDI (bidirectional text), however, for RTL +#: scripts, words are automatically displayed in RTL. That is to say, +#: in an RTL script, the words "HELLO WORLD" display in kitty as +#: "WORLD HELLO", and if you try to select a substring of an RTL- +#: shaped string, you will get the character that would be there had +#: the the string been LTR. For example, assuming the Hebrew word +#: ירושלים, selecting the character that on the screen appears to be ם +#: actually writes into the selection buffer the character י. kitty's +#: default behavior is useful in conjunction with a filter to reverse +#: the word order, however, if you wish to manipulate RTL glyphs, it +#: can be very challenging to work with, so this option is provided to +#: turn it off. Furthermore, this option can be used with the command +#: line program GNU FriBidi +#: to get BIDI +#: support, because it will force kitty to always treat the text as +#: LTR, which FriBidi expects for terminals. + +# adjust_line_height 0 +# adjust_column_width 0 + +#: Change the size of each character cell kitty renders. You can use +#: either numbers, which are interpreted as pixels or percentages +#: (number followed by %), which are interpreted as percentages of the +#: unmodified values. You can use negative pixels or percentages less +#: than 100% to reduce sizes (but this might cause rendering +#: artifacts). + +# adjust_baseline 0 + +#: Adjust the vertical alignment of text (the height in the cell at +#: which text is positioned). You can use either numbers, which are +#: interpreted as pixels or percentages (number followed by %), which +#: are interpreted as the percentage of the line height. A positive +#: value moves the baseline up, and a negative value moves them down. +#: The underline and strikethrough positions are adjusted accordingly. + +# symbol_map + +#: E.g. symbol_map U+E0A0-U+E0A3,U+E0C0-U+E0C7 PowerlineSymbols + +#: Map the specified Unicode codepoints to a particular font. Useful +#: if you need special rendering for some symbols, such as for +#: Powerline. Avoids the need for patched fonts. Each Unicode code +#: point is specified in the form `U+`. You +#: can specify multiple code points, separated by commas and ranges +#: separated by hyphens. This option can be specified multiple times. +#: The syntax is:: + +#: symbol_map codepoints Font Family Name + +# narrow_symbols + +#: E.g. narrow_symbols U+E0A0-U+E0A3,U+E0C0-U+E0C7 1 + +#: Usually, for Private Use Unicode characters and some symbol/dingbat +#: characters, if the character is followed by one or more spaces, +#: kitty will use those extra cells to render the character larger, if +#: the character in the font has a wide aspect ratio. Using this +#: option you can force kitty to restrict the specified code points to +#: render in the specified number of cells (defaulting to one cell). +#: This option can be specified multiple times. The syntax is:: + +#: narrow_symbols codepoints [optionally the number of cells] + +# disable_ligatures never + +#: Choose how you want to handle multi-character ligatures. The +#: default is to always render them. You can tell kitty to not render +#: them when the cursor is over them by using cursor to make editing +#: easier, or have kitty never render them at all by using always, if +#: you don't like them. The ligature strategy can be set per-window +#: either using the kitty remote control facility or by defining +#: shortcuts for it in kitty.conf, for example:: + +#: map alt+1 disable_ligatures_in active always +#: map alt+2 disable_ligatures_in all never +#: map alt+3 disable_ligatures_in tab cursor + +#: Note that this refers to programming ligatures, typically +#: implemented using the calt OpenType feature. For disabling general +#: ligatures, use the font_features option. + +# font_features + +#: E.g. font_features none + +#: Choose exactly which OpenType features to enable or disable. This +#: is useful as some fonts might have features worthwhile in a +#: terminal. For example, Fira Code includes a discretionary feature, +#: zero, which in that font changes the appearance of the zero (0), to +#: make it more easily distinguishable from Ø. Fira Code also includes +#: other discretionary features known as Stylistic Sets which have the +#: tags ss01 through ss20. + +#: For the exact syntax to use for individual features, see the +#: HarfBuzz documentation . + +#: Note that this code is indexed by PostScript name, and not the font +#: family. This allows you to define very precise feature settings; +#: e.g. you can disable a feature in the italic font but not in the +#: regular font. + +#: On Linux, font features are first read from the FontConfig database +#: and then this option is applied, so they can be configured in a +#: single, central place. + +#: To get the PostScript name for a font, use `kitty +list-fonts +#: --psnames`: + +#: .. code-block:: sh + +#: $ kitty +list-fonts --psnames | grep Fira +#: Fira Code +#: Fira Code Bold (FiraCode-Bold) +#: Fira Code Light (FiraCode-Light) +#: Fira Code Medium (FiraCode-Medium) +#: Fira Code Regular (FiraCode-Regular) +#: Fira Code Retina (FiraCode-Retina) + +#: The part in brackets is the PostScript name. + +#: Enable alternate zero and oldstyle numerals:: + +#: font_features FiraCode-Retina +zero +onum + +#: Enable only alternate zero in the bold font:: + +#: font_features FiraCode-Bold +zero + +#: Disable the normal ligatures, but keep the calt feature which (in +#: this font) breaks up monotony:: + +#: font_features TT2020StyleB-Regular -liga +calt + +#: In conjunction with force_ltr, you may want to disable Arabic +#: shaping entirely, and only look at their isolated forms if they +#: show up in a document. You can do this with e.g.:: + +#: font_features UnifontMedium +isol -medi -fina -init + +# box_drawing_scale 0.001, 1, 1.5, 2 + +#: The sizes of the lines used for the box drawing Unicode characters. +#: These values are in pts. They will be scaled by the monitor DPI to +#: arrive at a pixel value. There must be four values corresponding to +#: thin, normal, thick, and very thick lines. + +#: }}} + +#: Cursor customization {{{ + +# cursor #cccccc + +#: Default cursor color. If set to the special value none the cursor +#: will be rendered with a "reverse video" effect. It's color will be +#: the color of the text in the cell it is over and the text will be +#: rendered with the background color of the cell. Note that if the +#: program running in the terminal sets a cursor color, this takes +#: precedence. Also, the cursor colors are modified if the cell +#: background and foreground colors have very low contrast. + +# cursor_text_color #111111 + +#: The color of text under the cursor. If you want it rendered with +#: the background color of the cell underneath instead, use the +#: special keyword: background. Note that if cursor is set to none +#: then this option is ignored. + +# cursor_shape beam + +#: The cursor shape can be one of block, beam, underline. Note that +#: when reloading the config this will be changed only if the cursor +#: shape has not been set by the program running in the terminal. This +#: sets the default cursor shape, applications running in the terminal +#: can override it. In particular, shell integration +#: in kitty sets +#: the cursor shape to beam at shell prompts. You can avoid this by +#: setting shell_integration to no-cursor. + +# cursor_beam_thickness 1.5 + +#: The thickness of the beam cursor (in pts). + +# cursor_underline_thickness 2.0 + +#: The thickness of the underline cursor (in pts). + +# cursor_blink_interval -1 + +#: The interval to blink the cursor (in seconds). Set to zero to +#: disable blinking. Negative values mean use system default. Note +#: that the minimum interval will be limited to repaint_delay. + +# cursor_stop_blinking_after 15.0 + +#: Stop blinking cursor after the specified number of seconds of +#: keyboard inactivity. Set to zero to never stop blinking. + +#: }}} + +#: Scrollback {{{ + +scrollback_lines -1 + +#: Number of lines of history to keep in memory for scrolling back. +#: Memory is allocated on demand. Negative numbers are (effectively) +#: infinite scrollback. Note that using very large scrollback is not +#: recommended as it can slow down performance of the terminal and +#: also use large amounts of RAM. Instead, consider using +#: scrollback_pager_history_size. Note that on config reload if this +#: is changed it will only affect newly created windows, not existing +#: ones. + +# scrollback_pager less --chop-long-lines --RAW-CONTROL-CHARS +INPUT_LINE_NUMBER + +#: Program with which to view scrollback in a new window. The +#: scrollback buffer is passed as STDIN to this program. If you change +#: it, make sure the program you use can handle ANSI escape sequences +#: for colors and text formatting. INPUT_LINE_NUMBER in the command +#: line above will be replaced by an integer representing which line +#: should be at the top of the screen. Similarly CURSOR_LINE and +#: CURSOR_COLUMN will be replaced by the current cursor position or +#: set to 0 if there is no cursor, for example, when showing the last +#: command output. + +# scrollback_pager_history_size 0 + +#: Separate scrollback history size (in MB), used only for browsing +#: the scrollback buffer with pager. This separate buffer is not +#: available for interactive scrolling but will be piped to the pager +#: program when viewing scrollback buffer in a separate window. The +#: current implementation stores the data in UTF-8, so approximatively +#: 10000 lines per megabyte at 100 chars per line, for pure ASCII, +#: unformatted text. A value of zero or less disables this feature. +#: The maximum allowed size is 4GB. Note that on config reload if this +#: is changed it will only affect newly created windows, not existing +#: ones. + +# scrollback_fill_enlarged_window no + +#: Fill new space with lines from the scrollback buffer after +#: enlarging a window. + +# wheel_scroll_multiplier 5.0 + +#: Multiplier for the number of lines scrolled by the mouse wheel. +#: Note that this is only used for low precision scrolling devices, +#: not for high precision scrolling devices on platforms such as macOS +#: and Wayland. Use negative numbers to change scroll direction. See +#: also wheel_scroll_min_lines. + +# wheel_scroll_min_lines 1 + +#: The minimum number of lines scrolled by the mouse wheel. The scroll +#: multiplier only takes effect after it +#: reaches this number. Note that this is only used for low precision +#: scrolling devices like wheel mice that scroll by very small amounts +#: when using the wheel. With a negative number, the minimum number of +#: lines will always be added. + +# touch_scroll_multiplier 1.0 + +#: Multiplier for the number of lines scrolled by a touchpad. Note +#: that this is only used for high precision scrolling devices on +#: platforms such as macOS and Wayland. Use negative numbers to change +#: scroll direction. + +#: }}} + +#: Mouse {{{ + +# mouse_hide_wait 3.0 + +#: Hide mouse cursor after the specified number of seconds of the +#: mouse not being used. Set to zero to disable mouse cursor hiding. +#: Set to a negative value to hide the mouse cursor immediately when +#: typing text. Disabled by default on macOS as getting it to work +#: robustly with the ever-changing sea of bugs that is Cocoa is too +#: much effort. + +# url_color #0087bd +# url_style curly + +#: The color and style for highlighting URLs on mouse-over. url_style +#: can be one of: none, straight, double, curly, dotted, dashed. + +# open_url_with default + +#: The program to open clicked URLs. The special value default means +#: to use the operating system's default URL handler (open on macOS +#: and xdg-open on Linux). + +# url_prefixes file ftp ftps gemini git gopher http https irc ircs kitty mailto news sftp ssh + +#: The set of URL prefixes to look for when detecting a URL under the +#: mouse cursor. + +# detect_urls yes + +#: Detect URLs under the mouse. Detected URLs are highlighted with an +#: underline and the mouse cursor becomes a hand over them. Even if +#: this option is disabled, URLs are still clickable. + +# url_excluded_characters + +#: Additional characters to be disallowed from URLs, when detecting +#: URLs under the mouse cursor. By default, all characters that are +#: legal in URLs are allowed. + +# copy_on_select no + +#: Copy to clipboard or a private buffer on select. With this set to +#: clipboard, selecting text with the mouse will cause the text to be +#: copied to clipboard. Useful on platforms such as macOS that do not +#: have the concept of primary selection. You can instead specify a +#: name such as a1 to copy to a private kitty buffer. Map a shortcut +#: with the paste_from_buffer action to paste from this private +#: buffer. For example:: + +#: copy_on_select a1 +#: map shift+cmd+v paste_from_buffer a1 + +#: Note that copying to the clipboard is a security risk, as all +#: programs, including websites open in your browser can read the +#: contents of the system clipboard. + +# paste_actions quote-urls-at-prompt + +#: A comma separated list of actions to take when pasting text into +#: the terminal. The supported paste actions are: + +#: quote-urls-at-prompt: +#: If the text being pasted is a URL and the cursor is at a shell prompt, +#: automatically quote the URL (needs shell_integration). +#: confirm: +#: Confirm the paste if bracketed paste mode is not active or there is more +#: a large amount of text being pasted. +#: filter: +#: Run the filter_paste() function from the file paste-actions.py in +#: the kitty config directory on the pasted text. The text returned by the +#: function will be actually pasted. + +strip_trailing_spaces always + +#: Remove spaces at the end of lines when copying to clipboard. A +#: value of smart will do it when using normal selections, but not +#: rectangle selections. A value of always will always do it. + +# select_by_word_characters @-./_~?&=%+# + +#: Characters considered part of a word when double clicking. In +#: addition to these characters any character that is marked as an +#: alphanumeric character in the Unicode database will be matched. + +# select_by_word_characters_forward + +#: Characters considered part of a word when extending the selection +#: forward on double clicking. In addition to these characters any +#: character that is marked as an alphanumeric character in the +#: Unicode database will be matched. + +#: If empty (default) select_by_word_characters will be used for both +#: directions. + +# click_interval -1.0 + +#: The interval between successive clicks to detect double/triple +#: clicks (in seconds). Negative numbers will use the system default +#: instead, if available, or fallback to 0.5. + +focus_follows_mouse yes + +#: Set the active window to the window under the mouse when moving the +#: mouse around. + +# pointer_shape_when_grabbed arrow + +#: The shape of the mouse pointer when the program running in the +#: terminal grabs the mouse. Valid values are: arrow, beam and hand. + +# default_pointer_shape beam + +#: The default shape of the mouse pointer. Valid values are: arrow, +#: beam and hand. + +# pointer_shape_when_dragging beam + +#: The default shape of the mouse pointer when dragging across text. +#: Valid values are: arrow, beam and hand. + +#: Mouse actions {{{ + +#: Mouse buttons can be mapped to perform arbitrary actions. The +#: syntax is: + +#: .. code-block:: none + +#: mouse_map button-name event-type modes action + +#: Where button-name is one of left, middle, right, b1 ... b8 with +#: added keyboard modifiers. For example: ctrl+shift+left refers to +#: holding the Ctrl+Shift keys while clicking with the left mouse +#: button. The value b1 ... b8 can be used to refer to up to eight +#: buttons on a mouse. + +#: event-type is one of press, release, doublepress, triplepress, +#: click, doubleclick. modes indicates whether the action is performed +#: when the mouse is grabbed by the program running in the terminal, +#: or not. The values are grabbed or ungrabbed or a comma separated +#: combination of them. grabbed refers to when the program running in +#: the terminal has requested mouse events. Note that the click and +#: double click events have a delay of click_interval to disambiguate +#: from double and triple presses. + +#: You can run kitty with the kitty --debug-input command line option +#: to see mouse events. See the builtin actions below to get a sense +#: of what is possible. + +#: If you want to unmap an action, map it to no_op. For example, to +#: disable opening of URLs with a plain click:: + +#: mouse_map left click ungrabbed no_op + +#: See all the mappable actions including mouse actions here +#: . + +#: .. note:: +#: Once a selection is started, releasing the button that started it will +#: automatically end it and no release event will be dispatched. + +# clear_all_mouse_actions no + +#: Remove all mouse action definitions up to this point. Useful, for +#: instance, to remove the default mouse actions. + +#: Click the link under the mouse or move the cursor + +# mouse_map left click ungrabbed mouse_handle_click selection link prompt + +#:: First check for a selection and if one exists do nothing. Then +#:: check for a link under the mouse cursor and if one exists, click +#:: it. Finally check if the click happened at the current shell +#:: prompt and if so, move the cursor to the click location. Note +#:: that this requires shell integration +#:: to work. + +#: Click the link under the mouse or move the cursor even when grabbed + +# mouse_map shift+left click grabbed,ungrabbed mouse_handle_click selection link prompt + +#:: Same as above, except that the action is performed even when the +#:: mouse is grabbed by the program running in the terminal. + +#: Click the link under the mouse cursor + +# mouse_map ctrl+shift+left release grabbed,ungrabbed mouse_handle_click link + +#:: Variant with Ctrl+Shift is present because the simple click based +#:: version has an unavoidable delay of click_interval, to +#:: disambiguate clicks from double clicks. + +#: Discard press event for link click + +# mouse_map ctrl+shift+left press grabbed discard_event + +#:: Prevent this press event from being sent to the program that has +#:: grabbed the mouse, as the corresponding release event is used to +#:: open a URL. + +#: Paste from the primary selection + +# mouse_map middle release ungrabbed paste_from_selection + +#: Start selecting text + +# mouse_map left press ungrabbed mouse_selection normal + +#: Start selecting text in a rectangle + +# mouse_map ctrl+alt+left press ungrabbed mouse_selection rectangle + +#: Select a word + +# mouse_map left doublepress ungrabbed mouse_selection word + +#: Select a line + +# mouse_map left triplepress ungrabbed mouse_selection line + +#: Select line from point + +# mouse_map ctrl+alt+left triplepress ungrabbed mouse_selection line_from_point + +#:: Select from the clicked point to the end of the line. + +#: Extend the current selection + +# mouse_map right press ungrabbed mouse_selection extend + +#:: If you want only the end of the selection to be moved instead of +#:: the nearest boundary, use move-end instead of extend. + +#: Paste from the primary selection even when grabbed + +# mouse_map shift+middle release ungrabbed,grabbed paste_selection +# mouse_map shift+middle press grabbed discard_event + +#: Start selecting text even when grabbed + +# mouse_map shift+left press ungrabbed,grabbed mouse_selection normal + +#: Start selecting text in a rectangle even when grabbed + +# mouse_map ctrl+shift+alt+left press ungrabbed,grabbed mouse_selection rectangle + +#: Select a word even when grabbed + +# mouse_map shift+left doublepress ungrabbed,grabbed mouse_selection word + +#: Select a line even when grabbed + +# mouse_map shift+left triplepress ungrabbed,grabbed mouse_selection line + +#: Select line from point even when grabbed + +# mouse_map ctrl+shift+alt+left triplepress ungrabbed,grabbed mouse_selection line_from_point + +#:: Select from the clicked point to the end of the line even when +#:: grabbed. + +#: Extend the current selection even when grabbed + +# mouse_map shift+right press ungrabbed,grabbed mouse_selection extend + +#: Show clicked command output in pager + +# mouse_map ctrl+shift+right press ungrabbed mouse_show_command_output + +#:: Requires shell integration +#:: to work. + +#: }}} + +#: }}} + +#: Performance tuning {{{ + +# repaint_delay 10 + +#: Delay between screen updates (in milliseconds). Decreasing it, +#: increases frames-per-second (FPS) at the cost of more CPU usage. +#: The default value yields ~100 FPS which is more than sufficient for +#: most uses. Note that to actually achieve 100 FPS, you have to +#: either set sync_to_monitor to no or use a monitor with a high +#: refresh rate. Also, to minimize latency when there is pending input +#: to be processed, this option is ignored. + +# input_delay 3 + +#: Delay before input from the program running in the terminal is +#: processed (in milliseconds). Note that decreasing it will increase +#: responsiveness, but also increase CPU usage and might cause flicker +#: in full screen programs that redraw the entire screen on each loop, +#: because kitty is so fast that partial screen updates will be drawn. + +sync_to_monitor yes + +#: Sync screen updates to the refresh rate of the monitor. This +#: prevents screen tearing +#: when scrolling. +#: However, it limits the rendering speed to the refresh rate of your +#: monitor. With a very high speed mouse/high keyboard repeat rate, +#: you may notice some slight input latency. If so, set this to no. + +#: }}} + +#: Terminal bell {{{ + +# enable_audio_bell yes + +#: The audio bell. Useful to disable it in environments that require +#: silence. + +# visual_bell_duration 0.0 + +#: The visual bell duration (in seconds). Flash the screen when a bell +#: occurs for the specified number of seconds. Set to zero to disable. + +# visual_bell_color none + +#: The color used by visual bell. Set to none will fall back to +#: selection background color. If you feel that the visual bell is too +#: bright, you can set it to a darker color. + +# window_alert_on_bell yes + +#: Request window attention on bell. Makes the dock icon bounce on +#: macOS or the taskbar flash on linux. + +# bell_on_tab "🔔 " + +#: Some text or a Unicode symbol to show on the tab if a window in the +#: tab that does not have focus has a bell. If you want to use leading +#: or trailing spaces, surround the text with quotes. See +#: tab_title_template for how this is rendered. + +#: For backwards compatibility, values of yes, y and true are +#: converted to the default bell symbol and no, n, false and none are +#: converted to the empty string. + +# command_on_bell none + +#: Program to run when a bell occurs. The environment variable +#: KITTY_CHILD_CMDLINE can be used to get the program running in the +#: window in which the bell occurred. + +# bell_path none + +#: Path to a sound file to play as the bell sound. If set to none, the +#: system default bell sound is used. Must be in a format supported by +#: the operating systems sound API, such as WAV or OGA on Linux +#: (libcanberra) or AIFF, MP3 or WAV on macOS (NSSound) + +#: }}} + +#: Window layout {{{ + +# remember_window_size yes +# initial_window_width 640 +# initial_window_height 400 + +#: If enabled, the window size will be remembered so that new +#: instances of kitty will have the same size as the previous +#: instance. If disabled, the window will initially have size +#: configured by initial_window_width/height, in pixels. You can use a +#: suffix of "c" on the width/height values to have them interpreted +#: as number of cells instead of pixels. + +# enabled_layouts * + +#: The enabled window layouts. A comma separated list of layout names. +#: The special value all means all layouts. The first listed layout +#: will be used as the startup layout. Default configuration is all +#: layouts in alphabetical order. For a list of available layouts, see +#: the layouts . + +# window_resize_step_cells 2 +# window_resize_step_lines 2 + +#: The step size (in units of cell width/cell height) to use when +#: resizing kitty windows in a layout with the shortcut +#: start_resizing_window. The cells value is used for horizontal +#: resizing, and the lines value is used for vertical resizing. + +# window_border_width 0.5pt + +#: The width of window borders. Can be either in pixels (px) or pts +#: (pt). Values in pts will be rounded to the nearest number of pixels +#: based on screen resolution. If not specified, the unit is assumed +#: to be pts. Note that borders are displayed only when more than one +#: window is visible. They are meant to separate multiple windows. + +# draw_minimal_borders yes + +#: Draw only the minimum borders needed. This means that only the +#: borders that separate the inactive window from a neighbor are +#: drawn. Note that setting a non-zero window_margin_width overrides +#: this and causes all borders to be drawn. + +# window_margin_width 0 + +#: The window margin (in pts) (blank area outside the border). A +#: single value sets all four sides. Two values set the vertical and +#: horizontal sides. Three values set top, horizontal and bottom. Four +#: values set top, right, bottom and left. + +# single_window_margin_width -1 + +#: The window margin to use when only a single window is visible (in +#: pts). Negative values will cause the value of window_margin_width +#: to be used instead. A single value sets all four sides. Two values +#: set the vertical and horizontal sides. Three values set top, +#: horizontal and bottom. Four values set top, right, bottom and left. + +# window_padding_width 0 + +#: The window padding (in pts) (blank area between the text and the +#: window border). A single value sets all four sides. Two values set +#: the vertical and horizontal sides. Three values set top, horizontal +#: and bottom. Four values set top, right, bottom and left. + +# placement_strategy center + +#: When the window size is not an exact multiple of the cell size, the +#: cell area of the terminal window will have some extra padding on +#: the sides. You can control how that padding is distributed with +#: this option. Using a value of center means the cell area will be +#: placed centrally. A value of top-left means the padding will be +#: only at the bottom and right edges. + +# active_border_color #00ff00 + +#: The color for the border of the active window. Set this to none to +#: not draw borders around the active window. + +# inactive_border_color #cccccc + +#: The color for the border of inactive windows. + +# bell_border_color #ff5a00 + +#: The color for the border of inactive windows in which a bell has +#: occurred. + +# inactive_text_alpha 1.0 + +#: Fade the text in inactive windows by the specified amount (a number +#: between zero and one, with zero being fully faded). + +# hide_window_decorations no + +#: Hide the window decorations (title-bar and window borders) with +#: yes. On macOS, titlebar-only can be used to only hide the titlebar. +#: Whether this works and exactly what effect it has depends on the +#: window manager/operating system. Note that the effects of changing +#: this option when reloading config are undefined. + +# window_logo_path none + +#: Path to a logo image. Must be in PNG format. Relative paths are +#: interpreted relative to the kitty config directory. The logo is +#: displayed in a corner of every kitty window. The position is +#: controlled by window_logo_position. Individual windows can be +#: configured to have different logos either using the launch action +#: or the remote control facility. + +# window_logo_position bottom-right + +#: Where to position the window logo in the window. The value can be +#: one of: top-left, top, top-right, left, center, right, bottom-left, +#: bottom, bottom-right. + +# window_logo_alpha 0.5 + +#: The amount the logo should be faded into the background. With zero +#: being fully faded and one being fully opaque. + +# resize_debounce_time 0.1 + +#: The time to wait before redrawing the screen when a resize event is +#: received (in seconds). On platforms such as macOS, where the +#: operating system sends events corresponding to the start and end of +#: a resize, this number is ignored. + +# resize_draw_strategy static + +#: Choose how kitty draws a window while a resize is in progress. A +#: value of static means draw the current window contents, mostly +#: unchanged. A value of scale means draw the current window contents +#: scaled. A value of blank means draw a blank window. A value of size +#: means show the window size in cells. + +# resize_in_steps no + +#: Resize the OS window in steps as large as the cells, instead of +#: with the usual pixel accuracy. Combined with initial_window_width +#: and initial_window_height in number of cells, this option can be +#: used to keep the margins as small as possible when resizing the OS +#: window. Note that this does not currently work on Wayland. + +# visual_window_select_characters 1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ + +#: The list of characters for visual window selection. For example, +#: for selecting a window to focus on with focus_visible_window. The +#: value should be a series of unique numbers or alphabets, case +#: insensitive, from the set [0-9A-Z]. Specify your preference as a +#: string of characters. + +# confirm_os_window_close -1 + +#: Ask for confirmation when closing an OS window or a tab with at +#: least this number of kitty windows in it by window manager (e.g. +#: clicking the window close button or pressing the operating system +#: shortcut to close windows) or by the close_tab action. A value of +#: zero disables confirmation. This confirmation also applies to +#: requests to quit the entire application (all OS windows, via the +#: quit action). Negative values are converted to positive ones, +#: however, with shell_integration enabled, using negative values +#: means windows sitting at a shell prompt are not counted, only +#: windows where some command is currently running. Note that if you +#: want confirmation when closing individual windows, you can map the +#: close_window_with_confirmation action. + +#: }}} + +#: Tab bar {{{ + +# tab_bar_edge bottom + +#: The edge to show the tab bar on, top or bottom. + +# tab_bar_margin_width 0.0 + +#: The margin to the left and right of the tab bar (in pts). + +# tab_bar_margin_height 0.0 0.0 + +#: The margin above and below the tab bar (in pts). The first number +#: is the margin between the edge of the OS Window and the tab bar. +#: The second number is the margin between the tab bar and the +#: contents of the current tab. + +# tab_bar_style fade + +#: The tab bar style, can be one of: + +#: fade +#: Each tab's edges fade into the background color. (See also tab_fade) +#: slant +#: Tabs look like the tabs in a physical file. +#: separator +#: Tabs are separated by a configurable separator. (See also +#: tab_separator) +#: powerline +#: Tabs are shown as a continuous line with "fancy" separators. +#: (See also tab_powerline_style) +#: custom +#: A user-supplied Python function called draw_tab is loaded from the file +#: tab_bar.py in the kitty config directory. For examples of how to +#: write such a function, see the functions named draw_tab_with_* in +#: kitty's source code: kitty/tab_bar.py. See also +#: this discussion https://github.com/kovidgoyal/kitty/discussions/4447 +#: for examples from kitty users. +#: hidden +#: The tab bar is hidden. If you use this, you might want to create a mapping +#: for the select_tab action which presents you with a list of tabs and +#: allows for easy switching to a tab. + +# tab_bar_align left + +#: The horizontal alignment of the tab bar, can be one of: left, +#: center, right. + +# tab_bar_min_tabs 2 + +#: The minimum number of tabs that must exist before the tab bar is +#: shown. + +# tab_switch_strategy previous + +#: The algorithm to use when switching to a tab when the current tab +#: is closed. The default of previous will switch to the last used +#: tab. A value of left will switch to the tab to the left of the +#: closed tab. A value of right will switch to the tab to the right of +#: the closed tab. A value of last will switch to the right-most tab. + +# tab_fade 0.25 0.5 0.75 1 + +#: Control how each tab fades into the background when using fade for +#: the tab_bar_style. Each number is an alpha (between zero and one) +#: that controls how much the corresponding cell fades into the +#: background, with zero being no fade and one being full fade. You +#: can change the number of cells used by adding/removing entries to +#: this list. + +# tab_separator " ┇" + +#: The separator between tabs in the tab bar when using separator as +#: the tab_bar_style. + +# tab_powerline_style angled + +#: The powerline separator style between tabs in the tab bar when +#: using powerline as the tab_bar_style, can be one of: angled, +#: slanted, round. + +# tab_activity_symbol none + +#: Some text or a Unicode symbol to show on the tab if a window in the +#: tab that does not have focus has some activity. If you want to use +#: leading or trailing spaces, surround the text with quotes. See +#: tab_title_template for how this is rendered. + +# tab_title_template "{fmt.fg.red}{bell_symbol}{activity_symbol}{fmt.fg.tab}{title}" + +#: A template to render the tab title. The default just renders the +#: title with optional symbols for bell and activity. If you wish to +#: include the tab-index as well, use something like: {index}:{title}. +#: Useful if you have shortcuts mapped for goto_tab N. If you prefer +#: to see the index as a superscript, use {sup.index}. In addition you +#: can use {layout_name} for the current layout name, {num_windows} +#: for the number of windows in the tab and {num_window_groups} for +#: the number of window groups (not counting overlay windows) in the +#: tab. Note that formatting is done by Python's string formatting +#: machinery, so you can use, for instance, {layout_name[:2].upper()} +#: to show only the first two letters of the layout name, upper-cased. +#: If you want to style the text, you can use styling directives, for +#: example: +#: `{fmt.fg.red}red{fmt.fg.tab}normal{fmt.bg._00FF00}greenbg{fmt.bg.tab}`. +#: Similarly, for bold and italic: +#: `{fmt.bold}bold{fmt.nobold}normal{fmt.italic}italic{fmt.noitalic}`. +#: Note that for backward compatibility, if {bell_symbol} or +#: {activity_symbol} are not present in the template, they are +#: prepended to it. + +# active_tab_title_template none + +#: Template to use for active tabs. If not specified falls back to +#: tab_title_template. + +# active_tab_foreground #000 +# active_tab_background #eee +# active_tab_font_style bold-italic +# inactive_tab_foreground #444 +# inactive_tab_background #999 +# inactive_tab_font_style normal + +#: Tab bar colors and styles. + +# tab_bar_background none + +#: Background color for the tab bar. Defaults to using the terminal +#: background color. + +# tab_bar_margin_color none + +#: Color for the tab bar margin area. Defaults to using the terminal +#: background color. + +#: }}} + +#: Color scheme {{{ + +include ./theme.conf + +# foreground #dddddd +# background #000000 + +#: The foreground and background colors. + +background_opacity 0.9 + +#: The opacity of the background. A number between zero and one, where +#: one is opaque and zero is fully transparent. This will only work if +#: supported by the OS (for instance, when using a compositor under +#: X11). Note that it only sets the background color's opacity in +#: cells that have the same background color as the default terminal +#: background, so that things like the status bar in vim, powerline +#: prompts, etc. still look good. But it means that if you use a color +#: theme with a background color in your editor, it will not be +#: rendered as transparent. Instead you should change the default +#: background color in your kitty config and not use a background +#: color in the editor color scheme. Or use the escape codes to set +#: the terminals default colors in a shell script to launch your +#: editor. Be aware that using a value less than 1.0 is a (possibly +#: significant) performance hit. If you want to dynamically change +#: transparency of windows, set dynamic_background_opacity to yes +#: (this is off by default as it has a performance cost). Changing +#: this option when reloading the config will only work if +#: dynamic_background_opacity was enabled in the original config. + +# background_image none + +#: Path to a background image. Must be in PNG format. + +# background_image_layout tiled + +#: Whether to tile, scale or clamp the background image. The value can +#: be one of tiled, mirror-tiled, scaled, clamped. + +# background_image_linear no + +#: When background image is scaled, whether linear interpolation +#: should be used. + +# dynamic_background_opacity no + +#: Allow changing of the background_opacity dynamically, using either +#: keyboard shortcuts (increase_background_opacity and +#: decrease_background_opacity) or the remote control facility. +#: Changing this option by reloading the config is not supported. + +# background_tint 0.0 + +#: How much to tint the background image by the background color. The +#: tint is applied only under the text area, not margin/borders. This +#: option makes it easier to read the text. Tinting is done using the +#: current background color for each window. This option applies only +#: if background_opacity is set and transparent windows are supported +#: or background_image is set. + +# dim_opacity 0.75 + +#: How much to dim text that has the DIM/FAINT attribute set. One +#: means no dimming and zero means fully dimmed (i.e. invisible). + +# selection_foreground #000000 +# selection_background #fffacd + +#: The foreground and background colors for text selected with the +#: mouse. Setting both of these to none will cause a "reverse video" +#: effect for selections, where the selection will be the cell text +#: color and the text will become the cell background color. Setting +#: only selection_foreground to none will cause the foreground color +#: to be used unchanged. Note that these colors can be overridden by +#: the program running in the terminal. + +#: The color table {{{ + +#: The 256 terminal colors. There are 8 basic colors, each color has a +#: dull and bright version, for the first 16 colors. You can set the +#: remaining 240 colors as color16 to color255. + +#color0 #000000 +#color8 #7f7f7f + +#: black + +#color1 #cd0000 +#color9 #ff0000 + +#: red + +#color2 #00cd00 +#color10 #00ff00 + +#: green + +#color3 #cdcd00 +#color11 #ffff00 + +#: yellow + +#color4 #0000ee +#color12 #5c5cff + +#: blue + +#color5 #cd00cd +#color13 #ff00ff + +#: magenta + +#color6 #00cdcd +#color14 #00ffff + +#: cyan + +#color7 #e5e5e5 +#color15 #ffffff + +#: white + +# mark1_foreground black + +#: Color for marks of type 1 + +# mark1_background #98d3cb + +#: Color for marks of type 1 (light steel blue) + +# mark2_foreground black + +#: Color for marks of type 2 + +# mark2_background #f2dcd3 + +#: Color for marks of type 1 (beige) + +# mark3_foreground black + +#: Color for marks of type 3 + +# mark3_background #f274bc + +#: Color for marks of type 3 (violet) + +#: }}} + +#: }}} + +#: Advanced {{{ + +# shell . + +#: The shell program to execute. The default value of . means to use +#: whatever shell is set as the default shell for the current user. +#: Note that on macOS if you change this, you might need to add +#: --login and --interactive to ensure that the shell starts in +#: interactive mode and reads its startup rc files. + +# editor . + +#: The terminal based text editor (such as vim or nano) to use when +#: editing the kitty config file or similar tasks. + +#: The default value of . means to use the environment variables +#: VISUAL and EDITOR in that order. If these variables aren't set, +#: kitty will run your shell ($SHELL -l -i -c env) to see if your +#: shell startup rc files set VISUAL or EDITOR. If that doesn't work, +#: kitty will cycle through various known editors (vim, emacs, etc.) +#: and take the first one that exists on your system. + +# close_on_child_death no + +#: Close the window when the child process (shell) exits. With the +#: default value no, the terminal will remain open when the child +#: exits as long as there are still processes outputting to the +#: terminal (for example disowned or backgrounded processes). When +#: enabled with yes, the window will close as soon as the child +#: process exits. Note that setting it to yes means that any +#: background processes still using the terminal can fail silently +#: because their stdout/stderr/stdin no longer work. + +# allow_remote_control no + +#: Allow other programs to control kitty. If you turn this on, other +#: programs can control all aspects of kitty, including sending text +#: to kitty windows, opening new windows, closing windows, reading the +#: content of windows, etc. Note that this even works over SSH +#: connections. You can choose to either allow any program running +#: within kitty to control it with yes, or only allow programs that +#: connect to the socket (specified with the listen_on config option +#: or kitty --listen-on command line option) with the value socket- +#: only. The latter is useful if you want to prevent programs running +#: on a remote computer over SSH from controlling kitty. Reloading the +#: config will not affect this option. + +# listen_on none + +#: Listen to the specified UNIX socket for remote control connections. +#: Note that this will apply to all kitty instances. It can be +#: overridden by the kitty --listen-on command line option, which +#: supports listening on TCP socket. This option accepts only UNIX +#: sockets, such as unix:${TEMP}/mykitty or unix:@mykitty (on Linux). +#: Environment variables are expanded and relative paths are resolved +#: with respect to the temporary directory. If {kitty_pid} is present, +#: then it is replaced by the PID of the kitty process, otherwise the +#: PID of the kitty process is appended to the value, with a hyphen. +#: This option is ignored unless you also set allow_remote_control to +#: enable remote control. See the help for kitty --listen-on for more +#: details. Changing this option by reloading the config is not +#: supported. + +# env + +#: Specify the environment variables to be set in all child processes. +#: Using the name with an equal sign (e.g. env VAR=) will set it to +#: the empty string. Specifying only the name (e.g. env VAR) will +#: remove the variable from the child process' environment. Note that +#: environment variables are expanded recursively, for example:: + +#: env VAR1=a +#: env VAR2=${HOME}/${VAR1}/b + +#: The value of VAR2 will be /a/b. + +# watcher + +#: Path to python file which will be loaded for watchers +#: . Can be +#: specified more than once to load multiple watchers. The watchers +#: will be added to every kitty window. Relative paths are resolved +#: relative to the kitty config directory. Note that reloading the +#: config will only affect windows created after the reload. + +# exe_search_path + +#: Control where kitty finds the programs to run. The default search +#: order is: First search the system wide PATH, then ~/.local/bin and +#: ~/bin. If still not found, the PATH defined in the login shell +#: after sourcing all its startup files is tried. Finally, if present, +#: the PATH specified by the env option is tried. + +#: This option allows you to prepend, append, or remove paths from +#: this search order. It can be specified multiple times for multiple +#: paths. A simple path will be prepended to the search order. A path +#: that starts with the + sign will be append to the search order, +#: after ~/bin above. A path that starts with the - sign will be +#: removed from the entire search order. For example:: + +#: exe_search_path /some/prepended/path +#: exe_search_path +/some/appended/path +#: exe_search_path -/some/excluded/path + +# update_check_interval 24 + +#: The interval to periodically check if an update to kitty is +#: available (in hours). If an update is found, a system notification +#: is displayed informing you of the available update. The default is +#: to check every 24 hours, set to zero to disable. Update checking is +#: only done by the official binary builds. Distro packages or source +#: builds do not do update checking. Changing this option by reloading +#: the config is not supported. + +# startup_session none + +#: Path to a session file to use for all kitty instances. Can be +#: overridden by using the kitty --session command line option for +#: individual instances. See sessions +#: in the +#: kitty documentation for details. Note that relative paths are +#: interpreted with respect to the kitty config directory. Environment +#: variables in the path are expanded. Changing this option by +#: reloading the config is not supported. + +# clipboard_control write-clipboard write-primary read-clipboard-ask read-primary-ask + +#: Allow programs running in kitty to read and write from the +#: clipboard. You can control exactly which actions are allowed. The +#: possible actions are: write-clipboard, read-clipboard, write- +#: primary, read-primary, read-clipboard-ask, read-primary-ask. The +#: default is to allow writing to the clipboard and primary selection +#: and to ask for permission when a program tries to read from the +#: clipboard. Note that disabling the read confirmation is a security +#: risk as it means that any program, even the ones running on a +#: remote server via SSH can read your clipboard. See also +#: clipboard_max_size. + +# clipboard_max_size 64 + +#: The maximum size (in MB) of data from programs running in kitty +#: that will be stored for writing to the system clipboard. A value of +#: zero means no size limit is applied. See also clipboard_control. + +# file_transfer_confirmation_bypass + +#: The password that can be supplied to the file transfer kitten +#: to skip the +#: transfer confirmation prompt. This should only be used when +#: initiating transfers from trusted computers, over trusted networks +#: or encrypted transports, as it allows any programs running on the +#: remote machine to read/write to the local filesystem, without +#: permission. + +# allow_hyperlinks yes + +#: Process hyperlink escape sequences (OSC 8). If disabled OSC 8 +#: escape sequences are ignored. Otherwise they become clickable +#: links, that you can click with the mouse or by using the hints +#: kitten . The +#: special value of ask means that kitty will ask before opening the +#: link when clicked. + +# shell_integration enabled + +#: Enable shell integration on supported shells. This enables features +#: such as jumping to previous prompts, browsing the output of the +#: previous command in a pager, etc. on supported shells. Set to +#: disabled to turn off shell integration, completely. It is also +#: possible to disable individual features, set to a space separated +#: list of these values: no-rc, no-cursor, no-title, no-cwd, no- +#: prompt-mark, no-complete. See Shell integration +#: for details. + +# allow_cloning ask + +#: Control whether programs running in the terminal can request new +#: windows to be created. The canonical example is clone-in-kitty +#: . +#: By default, kitty will ask for permission for each clone request. +#: Allowing cloning unconditionally gives programs running in the +#: terminal (including over SSH) permission to execute arbitrary code, +#: as the user who is running the terminal, on the computer that the +#: terminal is running on. + +# clone_source_strategies venv,conda,env_var,path + +#: Control what shell code is sourced when running clone-in-kitty in +#: the newly cloned window. The supported strategies are: + +#: venv +#: Source the file $VIRTUAL_ENV/bin/activate. This is used by the +#: Python stdlib venv module and allows cloning venvs automatically. +#: conda +#: Run conda activate $CONDA_DEFAULT_ENV. This supports the virtual +#: environments created by conda. +#: env_var +#: Execute the contents of the environment variable +#: KITTY_CLONE_SOURCE_CODE with eval. +#: path +#: Source the file pointed to by the environment variable +#: KITTY_CLONE_SOURCE_PATH. + +#: This option must be a comma separated list of the above values. +#: This only source the first valid one in the above order. + +# term xterm-kitty + +#: The value of the TERM environment variable to set. Changing this +#: can break many terminal programs, only change it if you know what +#: you are doing, not because you read some advice on "Stack Overflow" +#: to change it. The TERM variable is used by various programs to get +#: information about the capabilities and behavior of the terminal. If +#: you change it, depending on what programs you run, and how +#: different the terminal you are changing it to is, various things +#: from key-presses, to colors, to various advanced features may not +#: work. Changing this option by reloading the config will only affect +#: newly created windows. + +#: }}} + +#: OS specific tweaks {{{ + +# wayland_titlebar_color system + +#: The color of the kitty window's titlebar on Wayland systems with +#: client side window decorations such as GNOME. A value of system +#: means to use the default system color, a value of background means +#: to use the background color of the currently active window and +#: finally you can use an arbitrary color, such as #12af59 or red. + +# macos_titlebar_color system + +#: The color of the kitty window's titlebar on macOS. A value of +#: system means to use the default system color, light or dark can +#: also be used to set it explicitly. A value of background means to +#: use the background color of the currently active window and finally +#: you can use an arbitrary color, such as #12af59 or red. WARNING: +#: This option works by using a hack when arbitrary color (or +#: background) is configured, as there is no proper Cocoa API for it. +#: It sets the background color of the entire window and makes the +#: titlebar transparent. As such it is incompatible with +#: background_opacity. If you want to use both, you are probably +#: better off just hiding the titlebar with hide_window_decorations. + +# macos_option_as_alt no + +#: Use the Option key as an Alt key on macOS. With this set to no, +#: kitty will use the macOS native Option+Key to enter Unicode +#: character behavior. This will break any Alt+Key keyboard shortcuts +#: in your terminal programs, but you can use the macOS Unicode input +#: technique. You can use the values: left, right or both to use only +#: the left, right or both Option keys as Alt, instead. Note that +#: kitty itself always treats Option the same as Alt. This means you +#: cannot use this option to configure different kitty shortcuts for +#: Option+Key vs. Alt+Key. Also, any kitty shortcuts using +#: Option/Alt+Key will take priority, so that any such key presses +#: will not be passed to terminal programs running inside kitty. +#: Changing this option by reloading the config is not supported. + +# macos_hide_from_tasks no + +#: Hide the kitty window from running tasks on macOS (⌘+Tab and the +#: Dock). Changing this option by reloading the config is not +#: supported. + +# macos_quit_when_last_window_closed no + +#: Have kitty quit when all the top-level windows are closed on macOS. +#: By default, kitty will stay running, even with no open windows, as +#: is the expected behavior on macOS. + +# macos_window_resizable yes + +#: Disable this if you want kitty top-level OS windows to not be +#: resizable on macOS. Changing this option by reloading the config +#: will only affect newly created OS windows. + +# macos_thicken_font 0 + +#: Draw an extra border around the font with the given width, to +#: increase legibility at small font sizes on macOS. For example, a +#: value of 0.75 will result in rendering that looks similar to sub- +#: pixel antialiasing at common font sizes. + +# macos_traditional_fullscreen no + +#: Use the macOS traditional full-screen transition, that is faster, +#: but less pretty. + +# macos_show_window_title_in all + +#: Control where the window title is displayed on macOS. A value of +#: window will show the title of the currently active window at the +#: top of the macOS window. A value of menubar will show the title of +#: the currently active window in the macOS global menu bar, making +#: use of otherwise wasted space. A value of all will show the title +#: in both places, and none hides the title. See +#: macos_menubar_title_max_length for how to control the length of the +#: title in the menu bar. + +# macos_menubar_title_max_length 0 + +#: The maximum number of characters from the window title to show in +#: the macOS global menu bar. Values less than one means that there is +#: no maximum limit. + +# macos_custom_beam_cursor no + +#: Use a custom mouse cursor for macOS that is easier to see on both +#: light and dark backgrounds. Nowadays, the default macOS cursor +#: already comes with a white border. WARNING: this might make your +#: mouse cursor invisible on dual GPU machines. Changing this option +#: by reloading the config is not supported. + +# macos_colorspace srgb + +#: The colorspace in which to interpret terminal colors. The default +#: of srgb will cause colors to match those seen in web browsers. The +#: value of default will use whatever the native colorspace of the +#: display is. The value of displayp3 will use Apple's special +#: snowflake display P3 color space, which will result in over +#: saturated (brighter) colors with some color shift. Reloading +#: configuration will change this value only for newly created OS +#: windows. + +# linux_display_server auto + +#: Choose between Wayland and X11 backends. By default, an appropriate +#: backend based on the system state is chosen automatically. Set it +#: to x11 or wayland to force the choice. Changing this option by +#: reloading the config is not supported. + +#: }}} + +#: Keyboard shortcuts {{{ + +#: Keys are identified simply by their lowercase Unicode characters. +#: For example: a for the A key, [ for the left square bracket key, +#: etc. For functional keys, such as Enter or Escape, the names are +#: present at Functional key definitions +#: . For modifier keys, the names are ctrl (control, ⌃), +#: shift (⇧), alt (opt, option, ⌥), super (cmd, command, ⌘). See also: +#: GLFW mods + +#: On Linux you can also use XKB key names to bind keys that are not +#: supported by GLFW. See XKB keys +#: for a list of key names. The name to use is the part +#: after the XKB_KEY_ prefix. Note that you can only use an XKB key +#: name for keys that are not known as GLFW keys. + +#: Finally, you can use raw system key codes to map keys, again only +#: for keys that are not known as GLFW keys. To see the system key +#: code for a key, start kitty with the kitty --debug-input option, +#: kitty will output some debug text for every key event. In that text +#: look for native_code, the value of that becomes the key name in the +#: shortcut. For example: + +#: .. code-block:: none + +#: on_key_input: glfw key: 0x61 native_code: 0x61 action: PRESS mods: none text: 'a' + +#: Here, the key name for the A key is 0x61 and you can use it with:: + +#: map ctrl+0x61 something + +#: to map Ctrl+A to something. + +#: You can use the special action no_op to unmap a keyboard shortcut +#: that is assigned in the default configuration:: + +#: map kitty_mod+space no_op + +#: If you would like kitty to completely ignore a key event, not even +#: sending it to the program running in the terminal, map it to +#: discard_event:: + +#: map kitty_mod+f1 discard_event + +#: You can combine multiple actions to be triggered by a single +#: shortcut with combine action, using the syntax below:: + +#: map key combine action1 action2 action3 ... + +#: For example:: + +#: map kitty_mod+e combine : new_window : next_layout + +#: This will create a new window and switch to the next available +#: layout. + +#: You can use multi-key shortcuts with the syntax shown below:: + +#: map key1>key2>key3 action + +#: For example:: + +#: map ctrl+f>2 set_font_size 20 + +#: The full list of actions that can be mapped to key presses is +#: available here . + +# kitty_mod ctrl+shift + +#: Special modifier key alias for default shortcuts. You can change +#: the value of this option to alter all default shortcuts that use +#: kitty_mod. + +# clear_all_shortcuts no + +#: Remove all shortcut definitions up to this point. Useful, for +#: instance, to remove the default shortcuts. + +# action_alias + +#: E.g. action_alias launch_tab launch --type=tab --cwd=current + +#: Define action aliases to avoid repeating the same options in +#: multiple mappings. Aliases can be defined for any action and will +#: be expanded recursively. For example, the above alias allows you to +#: create mappings to launch a new tab in the current working +#: directory without duplication:: + +#: map f1 launch_tab vim +#: map f2 launch_tab emacs + +#: Similarly, to alias kitten invocation:: + +#: action_alias hints kitten hints --hints-offset=0 + +# kitten_alias + +#: E.g. kitten_alias hints hints --hints-offset=0 + +#: Like action_alias above, but specifically for kittens. Generally, +#: prefer to use action_alias. This option is a legacy version, +#: present for backwards compatibility. It causes all invocations of +#: the aliased kitten to be substituted. So the example above will +#: cause all invocations of the hints kitten to have the --hints- +#: offset=0 option applied. + +#: Clipboard {{{ + +#: Copy to clipboard + +# map kitty_mod+c copy_to_clipboard +# map cmd+c copy_to_clipboard + +#:: There is also a copy_or_interrupt action that can be optionally +#:: mapped to Ctrl+C. It will copy only if there is a selection and +#:: send an interrupt otherwise. Similarly, +#:: copy_and_clear_or_interrupt will copy and clear the selection or +#:: send an interrupt if there is no selection. + +#: Paste from clipboard + +# map kitty_mod+v paste_from_clipboard +# map cmd+v paste_from_clipboard + +#: Paste from selection + +# map kitty_mod+s paste_from_selection +# map shift+insert paste_from_selection + +#: Pass selection to program + +# map kitty_mod+o pass_selection_to_program + +#:: You can also pass the contents of the current selection to any +#:: program with pass_selection_to_program. By default, the system's +#:: open program is used, but you can specify your own, the selection +#:: will be passed as a command line argument to the program. For +#:: example:: + +#:: map kitty_mod+o pass_selection_to_program firefox + +#:: You can pass the current selection to a terminal program running +#:: in a new kitty window, by using the @selection placeholder:: + +#:: map kitty_mod+y new_window less @selection + +#: }}} + +#: Scrolling {{{ + +#: Scroll line up + +# map kitty_mod+up scroll_line_up +# map kitty_mod+k scroll_line_up +# map opt+cmd+page_up scroll_line_up +# map cmd+up scroll_line_up + +#: Scroll line down + +# map kitty_mod+down scroll_line_down +# map kitty_mod+j scroll_line_down +# map opt+cmd+page_down scroll_line_down +# map cmd+down scroll_line_down + +#: Scroll page up + +# map kitty_mod+page_up scroll_page_up +# map cmd+page_up scroll_page_up + +#: Scroll page down + +# map kitty_mod+page_down scroll_page_down +# map cmd+page_down scroll_page_down + +#: Scroll to top + +# map kitty_mod+home scroll_home +# map cmd+home scroll_home + +#: Scroll to bottom + +# map kitty_mod+end scroll_end +# map cmd+end scroll_end + +#: Scroll to previous shell prompt + +# map kitty_mod+z scroll_to_prompt -1 + +#:: Use a parameter of 0 for scroll_to_prompt to scroll to the last +#:: jumped to or the last clicked position. Requires shell +#:: integration +#:: to work. + +#: Scroll to next shell prompt + +# map kitty_mod+x scroll_to_prompt 1 + +#: Browse scrollback buffer in pager + +# map kitty_mod+h show_scrollback + +#:: You can pipe the contents of the current screen and history +#:: buffer as STDIN to an arbitrary program using launch --stdin- +#:: source. For example, the following opens the scrollback buffer in +#:: less in an overlay window:: + +#:: map f1 launch --stdin-source=@screen_scrollback --stdin-add-formatting --type=overlay less +G -R + +#:: For more details on piping screen and buffer contents to external +#:: programs, see launch . + +#: Browse output of the last shell command in pager + +# map kitty_mod+g show_last_command_output + +#:: You can also define additional shortcuts to get the command +#:: output. For example, to get the first command output on screen:: + +#:: map f1 show_first_command_output_on_screen + +#:: To get the command output that was last accessed by a keyboard +#:: action or mouse action:: + +#:: map f1 show_last_visited_command_output + +#:: You can pipe the output of the last command run in the shell +#:: using the launch action. For example, the following opens the +#:: output in less in an overlay window:: + +#:: map f1 launch --stdin-source=@last_cmd_output --stdin-add-formatting --type=overlay less +G -R + +#:: To get the output of the first command on the screen, use +#:: @first_cmd_output_on_screen. To get the output of the last jumped +#:: to command, use @last_visited_cmd_output. + +#:: Requires shell integration +#:: to work. + +#: }}} + +#: Window management {{{ + +#: New window + +# map kitty_mod+enter new_window +# map cmd+enter new_window + +#:: You can open a new kitty window running an arbitrary program, for +#:: example:: + +#:: map kitty_mod+y launch mutt + +#:: You can open a new window with the current working directory set +#:: to the working directory of the current window using:: + +#:: map ctrl+alt+enter launch --cwd=current + +#:: You can open a new window that is allowed to control kitty via +#:: the kitty remote control facility with launch --allow-remote- +#:: control. Any programs running in that window will be allowed to +#:: control kitty. For example:: + +#:: map ctrl+enter launch --allow-remote-control some_program + +#:: You can open a new window next to the currently active window or +#:: as the first window, with:: + +#:: map ctrl+n launch --location=neighbor +#:: map ctrl+f launch --location=first + +#:: For more details, see launch +#:: . + +#: New OS window + +# map kitty_mod+n new_os_window +# map cmd+n new_os_window + +#:: Works like new_window above, except that it opens a top-level OS +#:: window. In particular you can use new_os_window_with_cwd to open +#:: a window with the current working directory. + +#: Close window + +# map kitty_mod+w close_window +# map shift+cmd+d close_window + +#: Next window + +# map kitty_mod+] next_window + +#: Previous window + +# map kitty_mod+[ previous_window + +#: Move window forward + +# map kitty_mod+f move_window_forward + +#: Move window backward + +# map kitty_mod+b move_window_backward + +#: Move window to top + +# map kitty_mod+` move_window_to_top + +#: Start resizing window + +# map kitty_mod+r start_resizing_window +# map cmd+r start_resizing_window + +#: First window + +# map kitty_mod+1 first_window +# map cmd+1 first_window + +#: Second window + +# map kitty_mod+2 second_window +# map cmd+2 second_window + +#: Third window + +# map kitty_mod+3 third_window +# map cmd+3 third_window + +#: Fourth window + +# map kitty_mod+4 fourth_window +# map cmd+4 fourth_window + +#: Fifth window + +# map kitty_mod+5 fifth_window +# map cmd+5 fifth_window + +#: Sixth window + +# map kitty_mod+6 sixth_window +# map cmd+6 sixth_window + +#: Seventh window + +# map kitty_mod+7 seventh_window +# map cmd+7 seventh_window + +#: Eight window + +# map kitty_mod+8 eighth_window +# map cmd+8 eighth_window + +#: Ninth window + +# map kitty_mod+9 ninth_window +# map cmd+9 ninth_window + +#: Tenth window + +# map kitty_mod+0 tenth_window + +#: Visually select and focus window + +# map kitty_mod+f7 focus_visible_window + +#:: Display overlay numbers and alphabets on the window, and switch +#:: the focus to the window when you press the key. When there are +#:: only two windows, the focus will be switched directly without +#:: displaying the overlay. You can change the overlay characters and +#:: their order with option visual_window_select_characters. + +#: Visually swap window with another + +# map kitty_mod+f8 swap_with_window + +#:: Works like focus_visible_window above, but swaps the window. + +#: }}} + +#: Tab management {{{ + +#: Next tab + +# map kitty_mod+right next_tab +# map shift+cmd+] next_tab +# map ctrl+tab next_tab + +#: Previous tab + +# map kitty_mod+left previous_tab +# map shift+cmd+[ previous_tab +# map ctrl+shift+tab previous_tab + +#: New tab + +# map kitty_mod+t new_tab +# map cmd+t new_tab + +#: Close tab + +# map kitty_mod+q close_tab +# map cmd+w close_tab + +#: Close OS window + +# map shift+cmd+w close_os_window + +#: Move tab forward + +# map kitty_mod+. move_tab_forward + +#: Move tab backward + +# map kitty_mod+, move_tab_backward + +#: Set tab title + +# map kitty_mod+alt+t set_tab_title +# map shift+cmd+i set_tab_title + + +#: You can also create shortcuts to go to specific tabs, with 1 being +#: the first tab, 2 the second tab and -1 being the previously active +#: tab, and any number larger than the last tab being the last tab:: + +#: map ctrl+alt+1 goto_tab 1 +#: map ctrl+alt+2 goto_tab 2 + +#: Just as with new_window above, you can also pass the name of +#: arbitrary commands to run when using new_tab and new_tab_with_cwd. +#: Finally, if you want the new tab to open next to the current tab +#: rather than at the end of the tabs list, use:: + +#: map ctrl+t new_tab !neighbor [optional cmd to run] +#: }}} + +#: Layout management {{{ + +#: Next layout + +# map kitty_mod+l next_layout + + +#: You can also create shortcuts to switch to specific layouts:: + +#: map ctrl+alt+t goto_layout tall +#: map ctrl+alt+s goto_layout stack + +#: Similarly, to switch back to the previous layout:: + +#: map ctrl+alt+p last_used_layout + +#: There is also a toggle_layout action that switches to the named +#: layout or back to the previous layout if in the named layout. +#: Useful to temporarily "zoom" the active window by switching to the +#: stack layout:: + +#: map ctrl+alt+z toggle_layout stack +#: }}} + +#: Font sizes {{{ + +#: You can change the font size for all top-level kitty OS windows at +#: a time or only the current one. + +#: Increase font size + +# map kitty_mod+equal change_font_size all +2.0 +# map kitty_mod+plus change_font_size all +2.0 +# map kitty_mod+kp_add change_font_size all +2.0 +# map cmd+plus change_font_size all +2.0 +# map cmd+equal change_font_size all +2.0 +# map shift+cmd+equal change_font_size all +2.0 + +#: Decrease font size + +# map kitty_mod+minus change_font_size all -2.0 +# map kitty_mod+kp_subtract change_font_size all -2.0 +# map cmd+minus change_font_size all -2.0 +# map shift+cmd+minus change_font_size all -2.0 + +#: Reset font size + +# map kitty_mod+backspace change_font_size all 0 +# map cmd+0 change_font_size all 0 + + +#: To setup shortcuts for specific font sizes:: + +#: map kitty_mod+f6 change_font_size all 10.0 + +#: To setup shortcuts to change only the current OS window's font +#: size:: + +#: map kitty_mod+f6 change_font_size current 10.0 +#: }}} + +#: Select and act on visible text {{{ + +#: Use the hints kitten to select text and either pass it to an +#: external program or insert it into the terminal or copy it to the +#: clipboard. + +#: Open URL + +# map kitty_mod+e open_url_with_hints + +#:: Open a currently visible URL using the keyboard. The program used +#:: to open the URL is specified in open_url_with. + +#: Insert selected path + +# map kitty_mod+p>f kitten hints --type path --program - + +#:: Select a path/filename and insert it into the terminal. Useful, +#:: for instance to run git commands on a filename output from a +#:: previous git command. + +#: Open selected path + +# map kitty_mod+p>shift+f kitten hints --type path + +#:: Select a path/filename and open it with the default open program. + +#: Insert selected line + +# map kitty_mod+p>l kitten hints --type line --program - + +#:: Select a line of text and insert it into the terminal. Useful for +#:: the output of things like: `ls -1`. + +#: Insert selected word + +# map kitty_mod+p>w kitten hints --type word --program - + +#:: Select words and insert into terminal. + +#: Insert selected hash + +# map kitty_mod+p>h kitten hints --type hash --program - + +#:: Select something that looks like a hash and insert it into the +#:: terminal. Useful with git, which uses SHA1 hashes to identify +#:: commits. + +#: Open the selected file at the selected line + +# map kitty_mod+p>n kitten hints --type linenum + +#:: Select something that looks like filename:linenum and open it in +#:: vim at the specified line number. + +#: Open the selected hyperlink + +# map kitty_mod+p>y kitten hints --type hyperlink + +#:: Select a hyperlink (i.e. a URL that has been marked as such by +#:: the terminal program, for example, by `ls --hyperlink=auto`). + + +#: The hints kitten has many more modes of operation that you can map +#: to different shortcuts. For a full description see hints kitten +#: . +#: }}} + +#: Miscellaneous {{{ + +#: Toggle fullscreen + +# map kitty_mod+f11 toggle_fullscreen +# map ctrl+cmd+f toggle_fullscreen + +#: Toggle maximized + +# map kitty_mod+f10 toggle_maximized + +#: Toggle macOS secure keyboard entry + +# map opt+cmd+s toggle_macos_secure_keyboard_entry + +#: Unicode input + +# map kitty_mod+u kitten unicode_input +# map ctrl+cmd+space kitten unicode_input + +#: Edit config file + +# map kitty_mod+f2 edit_config_file +# map cmd+, edit_config_file + +#: Open the kitty command shell + +# map kitty_mod+escape kitty_shell window + +#:: Open the kitty shell in a new window / tab / overlay / os_window +#:: to control kitty using commands. + +#: Increase background opacity + +# map kitty_mod+a>m set_background_opacity +0.1 + +#: Decrease background opacity + +# map kitty_mod+a>l set_background_opacity -0.1 + +#: Make background fully opaque + +# map kitty_mod+a>1 set_background_opacity 1 + +#: Reset background opacity + +# map kitty_mod+a>d set_background_opacity default + +#: Reset the terminal + +# map kitty_mod+delete clear_terminal reset active +# map opt+cmd+r clear_terminal reset active + +#:: You can create shortcuts to clear/reset the terminal. For +#:: example:: + +#:: # Reset the terminal +#:: map f1 clear_terminal reset active +#:: # Clear the terminal screen by erasing all contents +#:: map f1 clear_terminal clear active +#:: # Clear the terminal scrollback by erasing it +#:: map f1 clear_terminal scrollback active +#:: # Scroll the contents of the screen into the scrollback +#:: map f1 clear_terminal scroll active +#:: # Clear everything up to the line with the cursor +#:: map f1 clear_terminal to_cursor active + +#:: If you want to operate on all kitty windows instead of just the +#:: current one, use all instead of active. + +#:: It is also possible to remap Ctrl+L to both scroll the current +#:: screen contents into the scrollback buffer and clear the screen, +#:: instead of just clearing the screen, for example, for ZSH add the +#:: following to ~/.zshrc: + +#:: .. code-block:: zsh + +#:: scroll-and-clear-screen() { +#:: printf '\n%.0s' {1..$LINES} +#:: zle clear-screen +#:: } +#:: zle -N scroll-and-clear-screen +#:: bindkey '^l' scroll-and-clear-screen + +#: Clear up to cursor line + +# map cmd+k clear_terminal to_cursor active + +#: Reload kitty.conf + +# map kitty_mod+f5 load_config_file +# map ctrl+cmd+, load_config_file + +#:: Reload kitty.conf, applying any changes since the last time it +#:: was loaded. Note that a handful of options cannot be dynamically +#:: changed and require a full restart of kitty. Particularly, when +#:: changing shortcuts for actions located on the macOS global menu +#:: bar, a full restart is needed. You can also map a keybinding to +#:: load a different config file, for example:: + +#:: map f5 load_config /path/to/alternative/kitty.conf + +#:: Note that all options from the original kitty.conf are discarded, +#:: in other words the new configuration *replace* the old ones. + +#: Debug kitty configuration + +# map kitty_mod+f6 debug_config +# map opt+cmd+, debug_config + +#:: Show details about exactly what configuration kitty is running +#:: with and its host environment. Useful for debugging issues. + +#: Send arbitrary text on key presses + +#:: E.g. map ctrl+shift+alt+h send_text all Hello World + +#:: You can tell kitty to send arbitrary (UTF-8) encoded text to the +#:: client program when pressing specified shortcut keys. For +#:: example:: + +#:: map ctrl+alt+a send_text all Special text + +#:: This will send "Special text" when you press the Ctrl+Alt+A key +#:: combination. The text to be sent is a python string literal so +#:: you can use escapes like \x1b to send control codes or \u21fb to +#:: send Unicode characters (or you can just input the Unicode +#:: characters directly as UTF-8 text). You can use `kitty +kitten +#:: show_key` to get the key escape codes you want to emulate. + +#:: The first argument to send_text is the keyboard modes in which to +#:: activate the shortcut. The possible values are normal, +#:: application, kitty or a comma separated combination of them. The +#:: modes normal and application refer to the DECCKM cursor key mode +#:: for terminals, and kitty refers to the kitty extended keyboard +#:: protocol. The special value all means all of them. + +#:: Some more examples:: + +#:: # Output a word and move the cursor to the start of the line (like typing and pressing Home) +#:: map ctrl+alt+a send_text normal Word\x1b[H +#:: map ctrl+alt+a send_text application Word\x1bOH +#:: # Run a command at a shell prompt (like typing the command and pressing Enter) +#:: map ctrl+alt+a send_text normal,application some command with arguments\r + +#: Open kitty Website + +# map shift+cmd+/ open_url https://sw.kovidgoyal.net/kitty/ + +#: }}} + +#: }}} diff --git a/kitty/kitty/theme.conf b/kitty/kitty/theme.conf new file mode 100644 index 0000000..2543819 --- /dev/null +++ b/kitty/kitty/theme.conf @@ -0,0 +1,21 @@ +background #090200 +foreground #a4a1a1 +cursor #a4a1a1 +selection_background #494542 +color0 #090200 +color8 #5b5754 +color1 #da2c20 +color9 #e8bacf +color2 #00a152 +color10 #3a3332 +color3 #fcec02 +color11 #494542 +color4 #00a0e4 +color12 #7f7c7b +color5 #a06994 +color13 #d6d4d3 +color6 #b5e4f4 +color14 #ccab53 +color7 #a4a1a1 +color15 #f7f7f7 +selection_foreground #090200 diff --git a/kitty/theme.conf b/kitty/theme.conf new file mode 120000 index 0000000..f2d053d --- /dev/null +++ b/kitty/theme.conf @@ -0,0 +1 @@ +gh/kitty-themes/themes/3024_Night.conf \ No newline at end of file diff --git a/lvim/config.lua b/lvim/config.lua new file mode 100644 index 0000000..4d9c0ca --- /dev/null +++ b/lvim/config.lua @@ -0,0 +1,171 @@ +--[[ + THESE ARE EXAMPLE CONFIGS FEEL FREE TO CHANGE TO WHATEVER YOU WANT + `lvim` is the global options object +]] + +lvim.transparent_window = true +lvim.plugins = { + { "catppuccin/nvim" }, + { "gpanders/editorconfig.nvim" }, + { + 'wfxr/minimap.vim', + build = "cargo install --locked code-minimap", + cmd = { "Minimap", "MinimapClose", "MinimapToggle", "MinimapRefresh", "MinimapUpdateHighlight" }, + config = function() + vim.cmd("let g:minimap_width = 10") + vim.cmd("let g:minimap_auto_start = 1") + vim.cmd("let g:minimap_auto_start_win_enter = 1") + end, + }, + { + "windwp/nvim-ts-autotag", + config = function() + require("nvim-ts-autotag").setup() + end, + }, + { "mrjones2014/nvim-ts-rainbow" }, + { + "romgrk/nvim-treesitter-context", + config = function() + require("treesitter-context").setup { + enable = true, -- Enable this plugin (Can be enabled/disabled later via commands) + throttle = true, -- Throttles plugin updates (may improve performance) + max_lines = 0, -- How many lines the window should span. Values <= 0 mean no limit. + patterns = { -- Match patterns for TS nodes. These get wrapped to match at word boundaries. + -- For all filetypes + -- Note that setting an entry here replaces all other patterns for this entry. + -- By setting the 'default' entry below, you can control which nodes you want to + -- appear in the context window. + default = { + 'class', + 'function', + 'method', + }, + }, + } + end + }, + { + "ray-x/lsp_signature.nvim", + event = "BufRead", + config = function() require "lsp_signature".on_attach() end, + }, + { + "folke/todo-comments.nvim", + event = "BufRead", + config = function() + require("todo-comments").setup() + end, + }, + { "ms-jpq/chadtree" }, +} + +-- vim options +--vim.opt.shiftwidth = 2 +--vim.opt.tabstop = 2 +vim.opt.relativenumber = true +vim.opt.clipboard = "unnamedplus" +vim.opt.wrap = true +vim.opt.spell = true +vim.opt.spelllang = "en" + +-- general +lvim.log.level = "info" +lvim.format_on_save = { + enabled = true, + pattern = "*.lua", + timeout = 1000, +} +-- to disable icons and use a minimalist setup, uncomment the following +-- lvim.use_icons = false + +-- keymappings +lvim.leader = "space" +-- add your own keymapping +lvim.keys.normal_mode[""] = ":w" + +-- lvim.keys.normal_mode[""] = ":BufferLineCycleNext" +-- lvim.keys.normal_mode[""] = ":BufferLineCyclePrev" + +-- -- Use which-key to add extra bindings with the leader-key prefix +-- lvim.builtin.which_key.mappings["W"] = { "noautocmd w", "Save without formatting" } +-- lvim.builtin.which_key.mappings["P"] = { "Telescope projects", "Projects" } + +-- -- Change theme settings +-- lvim.colorscheme = "lunar" + +-- After changing plugin config exit and reopen LunarVim, Run :PackerSync +lvim.builtin.alpha.active = true +lvim.builtin.alpha.mode = "dashboard" +lvim.builtin.terminal.active = true +lvim.builtin.nvimtree.setup.view.side = "left" +lvim.builtin.nvimtree.setup.renderer.icons.show.git = true + +-- Automatically install missing parsers when entering buffer +lvim.builtin.treesitter.auto_install = true +lvim.builtin.treesitter.rainbow.enable = true + +-- lvim.builtin.treesitter.ignore_install = { "haskell" } + +-- -- generic LSP settings + +-- --- disable automatic installation of servers +-- lvim.lsp.installer.setup.automatic_installation = false + +-- ---configure a server manually. IMPORTANT: Requires `:LvimCacheReset` to take effect +-- ---see the full default list `:lua =lvim.lsp.automatic_configuration.skipped_servers` +-- vim.list_extend(lvim.lsp.automatic_configuration.skipped_servers, { "pyright" }) +-- local opts = {} -- check the lspconfig documentation for a list of all possible options +-- require("lvim.lsp.manager").setup("pyright", opts) + +-- ---remove a server from the skipped list, e.g. eslint, or emmet_ls. IMPORTANT: Requires `:LvimCacheReset` to take effect +-- ---`:LvimInfo` lists which server(s) are skipped for the current filetype +-- lvim.lsp.automatic_configuration.skipped_servers = vim.tbl_filter(function(server) +-- return server ~= "emmet_ls" +-- end, lvim.lsp.automatic_configuration.skipped_servers) + +-- -- you can set a custom on_attach function that will be used for all the language servers +-- -- See +-- lvim.lsp.on_attach_callback = function(client, bufnr) +-- local function buf_set_option(...) +-- vim.api.nvim_buf_set_option(bufnr, ...) +-- end +-- --Enable completion triggered by +-- buf_set_option("omnifunc", "v:lua.vim.lsp.omnifunc") +-- end + +-- -- linters and formatters +-- local formatters = require "lvim.lsp.null-ls.formatters" +-- formatters.setup { +-- { command = "stylua" }, +-- { +-- command = "prettier", +-- extra_args = { "--print-width", "100" }, +-- filetypes = { "typescript", "typescriptreact" }, +-- }, +-- } +-- local linters = require "lvim.lsp.null-ls.linters" +-- linters.setup { +-- { command = "flake8", filetypes = { "python" } }, +-- { +-- command = "shellcheck", +-- args = { "--severity", "warning" }, +-- }, +-- } + +-- -- Additional Plugins +-- lvim.plugins = { +-- { +-- "folke/trouble.nvim", +-- cmd = "TroubleToggle", +-- }, +-- } + +-- -- Autocommands (`:help autocmd`) +-- vim.api.nvim_create_autocmd("FileType", { +-- pattern = "zsh", +-- callback = function() +-- -- let treesitter use bash highlight for zsh files as well +-- require("nvim-treesitter.highlight").attach(0, "bash") +-- end, +-- }) diff --git a/nvim/init.vim b/nvim/init.vim new file mode 100755 index 0000000..1364ddf --- /dev/null +++ b/nvim/init.vim @@ -0,0 +1,15 @@ +set number +tnoremap + +call plug#begin() +Plug 'ms-jpq/chadtree', {'branch': 'chad', 'do': 'python3 -m chadtree deps'} +"Plug 'ms-jpq/coq_nvim', {'branch': 'coq'} +"Plug 'ms-jpq/coq.artifacts', {'branch': 'artifacts'} +Plug 'navarasu/onedark.nvim' +Plug 'gpanders/editorconfig.nvim' +call plug#end() + +nnoremap CHADopen +"let g:coq_settings = { 'auto_start': 'shut-up' } +let g:onedark_config = { 'style': 'warmer', 'transparent': v:true } +colorscheme onedark diff --git a/pipewire/pipewire-pulse.conf.backup b/pipewire/pipewire-pulse.conf.backup new file mode 100644 index 0000000..17fa608 --- /dev/null +++ b/pipewire/pipewire-pulse.conf.backup @@ -0,0 +1,12 @@ +pulse.rules = [ + { + # Discord notification sounds fix + matches = [ { application.process.binary = "Discord" } ] + actions = { + update-props = { + pulse.min.quantum = 1024/48000 + } + } + } +] + diff --git a/polybar/config.ini b/polybar/config.ini new file mode 100755 index 0000000..2fa5e09 --- /dev/null +++ b/polybar/config.ini @@ -0,0 +1,422 @@ +;========================================================== +; +; +; ██████╗ ██████╗ ██╗ ██╗ ██╗██████╗ █████╗ ██████╗ +; ██╔══██╗██╔═══██╗██║ ╚██╗ ██╔╝██╔══██╗██╔══██╗██╔══██╗ +; ██████╔╝██║ ██║██║ ╚████╔╝ ██████╔╝███████║██████╔╝ +; ██╔═══╝ ██║ ██║██║ ╚██╔╝ ██╔══██╗██╔══██║██╔══██╗ +; ██║ ╚██████╔╝███████╗██║ ██████╔╝██║ ██║██║ ██║ +; ╚═╝ ╚═════╝ ╚══════╝╚═╝ ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝ +; +; +; To learn more about how to configure Polybar +; go to https://github.com/polybar/polybar +; +; The README contains a lot of information +; +;========================================================== + +[colors] +transparent = #0000 +background = #AA282A2E +background-alt = #AA373B41 +foreground = #C5C8C6 +primary = #F0C674 +secondary = #8ABEB7 +alert = #A54242 +disabled = #707880 + + +[global/wm] +margin-bottom = 0 + + +[bar/base] +width = 100% +height = 24pt +radius = 6 +; dpi = 96 + +background = ${colors.background} +foreground = ${colors.foreground} + +line-size = 3pt + +border-top-size = 2.5pt +border-bottom-size = 0pt +border-right-size = 2.5pt +border-left-size = 2.5pt +border-color = #00000000 + +padding-left = 0 +padding-right = 1 + +module-margin = 1 + +separator = | +separator-foreground = ${colors.disabled} + +font-0 = "JetBrains Mono:10;2" +font-1 = "MesloLGS NF:10;2" +font-2 = "FontAwesome:10;2" + +cursor-click = pointer +cursor-scroll = ns-resize + +enable-ipc = true + +; wm-restack = i3 +; override-redirect = true + + +[bar/main] +inherit = bar/base +monitor = DisplayPort-1 + +modules-left = xworkspaces xwindow +; modules-right = pulseaudio date menu-power +modules-right = pulseaudio dunst + + +[bar/other] +inherit = bar/base +monitor = DisplayPort-0 +monitor-fallback = DisplayPort-1 + +; modules-left = xworkspaces dunst xwindow +modules-left = xworkspaces system-usb-udev +; modules-right = xbacklight filesystem hdd-filesystem memory cpu eth date menu-power +modules-right = memory cpu eth date menu-power + + +[bar/last] +inherit = bar/base +monitor = HDMI-A-0 +monitor-fallback = DisplayPort-1 + +modules-left = xworkspaces tray +modules-right = xbacklight change-background wlan filesystem hdd-filesystem pulseaudio date + + +[module/menu-apps] +type = custom/menu + +expand-right = false + +menu-0-0 = %{F#88CC22} +menu-0-0-exec = rofi -show drun +menu-0-1 = %{F#FFF}ﬓ +menu-0-1-exec = #menu-apps.open.1 +menu-0-2 = %{F-} +menu-0-2-exec = #menu-apps.open.2 +menu-0-3 = %{F#FFFF00} +menu-0-3-exec = #menu-apps.open.3 + +menu-1-0 = %{F-} +menu-1-0-exec = escrotum -s -C +menu-1-1 = %{F#F0C674} +menu-1-1-exec = #menu-apps.open.0 + +menu-2-0 = %{F-} +menu-2-0-exec = kitty +menu-2-1 = %{F#0098FF}﬏ +menu-2-1-exec = vscodium +menu-2-2 = %{F#FE2857} +menu-2-2-exec = jetbrains-toolbox +menu-2-3 = %{F#F0C674} +menu-2-3-exec = #menu-apps.open.0 + +menu-3-0 = %{F#0A84FF} +menu-3-0-exec = thunderbird +menu-3-1 = %{F#0572EC} +menu-3-1-exec = 1password +menu-3-2 = %{F#5865F2}ﭮ +menu-3-2-exec = discord +menu-3-3 = %{F#FF9400} +menu-3-3-exec = firefox +menu-3-4 = %{F#F0C674} +menu-3-4-exec = #menu-apps.open.0 + +label-open = %{F#F0C674} +label-close = %{F#F0C674}× +label-separator = %{F#707880} %{F-} + + +[module/menu-power] +type = custom/menu + +expand-right = false + +menu-0-0 = %{F#007FFF} +menu-0-0-exec = i3lock-fancy-multimonitor -b=0x3 +#menu-0-1 = %{F#00FF7F}鈴 +#menu-0-1-exec = systemctl suspend +#menu-0-2 = %{F#7FFF00} +#menu-0-2-exec = systemctl hibernate +menu-0-1 = %{F#FF7F00}ﰇ +menu-0-1-exec = reboot +menu-0-2 = %{F#F00} +menu-0-2-exec = shutdown now + +label-open = %{F#F0C674} +label-close = %{F#F0C674}× +label-separator = "%{F-} " + +[module/xworkspaces] +type = internal/xworkspaces + +pin-workspaces = true +#enable-scroll = false + +label-active = %name% +label-active-background = ${colors.background-alt} +label-active-underline= ${colors.primary} +label-active-padding = 1 + +label-occupied = %name% +label-occupied-padding = 1 + +label-urgent = %name% +label-urgent-background = ${colors.alert} +label-urgent-padding = 1 + +label-empty = %name% +label-empty-foreground = ${colors.disabled} +label-empty-padding = 1 + + +[module/xwindow] +type = internal/xwindow +#label = %title:0:60:...% +label = %title:0:164:...% + + +[module/xbacklight] +type = internal/xbacklight +#output = HDMI-A-0 +#output = DisplayPort-1 +#output = DisplayPort-0 +label = %{colors.primary}BKL%{F-} %percentage%% + + +[module/change-background] +type = custom/script +exec = ls +format =