From 971b0ba1de5606189375ee07bd660450317424de Mon Sep 17 00:00:00 2001
From: SkyratBot <59378654+SkyratBot@users.noreply.github.com>
Date: Mon, 17 Aug 2020 14:48:28 +0200
Subject: [PATCH] [MIRROR] tgchat: Hotfixes, Message Annotations (#364)
* tgchat: Hotfixes, Message Annotations (#52947)
This PR changes the message pipeline a little bit to support list-based messages, which can be annotated with custom data. Function signature of to_chat was slightly changed as well:
// Plain text message
to_chat(client,
type = MESSAGE_TYPE_INFO,
text = "foo")
// HTML message
to_chat(client,
type = MESSAGE_TYPE_INFO,
html = "foo")
Old to_chat format is still supported, but handle_whitespace, trailing_newline and confidential flags have no effect. confidential flag could still be revived though, if there is enough merit in it, for example to filter out confidential messages when saving a chat log.
The reason for using /list and not /datum, is because lists are plain faster, and there are minimal data transformations - these lists are fed directly to json_encode and sent to tgchat.
Plain text messages do not need to be HTML-escaped, which makes them safer and more performant than HTML messages. Plain text messages can be made interactive (or formatted with CSS) by custom-handling them in javscript based on message type and annotations.
It would be impossible to annotate every single message in the game (at the moment of writing, there are 9447 to_chat calls in the code), but it could be done selectively, for only those messages that are hard to classify by span classes (and there are still A LOT of them).
Please annotate more messages. Thank you.
Fixes #52943
Fixes #52908
Fixes #52816
Changelog
cl
add: tgchat: Unread message count is now smarter and won't increase on other tabs if you have already read the same message in the active tab.
add: tgchat: Admin PMs are now properly annotated and can be filtered into separate tabs.
fix: tgchat: Fix: Highlighted message overlay no longer blocks clicks. Clicking a highlighted (F) link should work as it should.
fix: tgui: Fixed NTOS bluescreen due to calling .includes() on a stylesheet href which could be null on certain browsers.
code: tgchat: Chat schema bumped to version 5. All chat-related settings were reset to avoid breakage.
/cl
* tgchat: Hotfixes, Message Annotations
Co-authored-by: Aleksej Komarov "+e+"
[msg]", confidential = TRUE)
+ to_chat(src,
+ type = MESSAGE_TYPE_ADMINPM,
+ html = "Error: Admin-PM: Client not found.",
+ confidential = TRUE)
+ to_chat(src,
+ type = MESSAGE_TYPE_ADMINPM,
+ html = "Message not sent:
[msg]",
+ confidential = TRUE)
AH.AddInteraction("No client found, message not sent:
[msg]")
return
cmd_admin_pm(whom, msg)
@@ -72,12 +90,21 @@
//Fetching a message if needed. src is the sender and C is the target client
/client/proc/cmd_admin_pm(whom, msg)
if(prefs.muted & MUTE_ADMINHELP)
- to_chat(src, "Error: Admin-PM: You are unable to use admin PM-s (muted).", confidential = TRUE)
+ to_chat(src,
+ type = MESSAGE_TYPE_ADMINPM,
+ html = "Error: Admin-PM: You are unable to use admin PM-s (muted).",
+ confidential = TRUE)
return
if(!holder && !current_ticket) //no ticket? https://www.youtube.com/watch?v=iHSPf6x1Fdo
- to_chat(src, "You can no longer reply to this ticket, please open another one by using the Adminhelp verb if need be.", confidential = TRUE)
- to_chat(src, "Message: [msg]", confidential = TRUE)
+ to_chat(src,
+ type = MESSAGE_TYPE_ADMINPM,
+ html = "You can no longer reply to this ticket, please open another one by using the Adminhelp verb if need be.",
+ confidential = TRUE)
+ to_chat(src,
+ type = MESSAGE_TYPE_ADMINPM,
+ html = "Message: [msg]",
+ confidential = TRUE)
return
var/client/recipient
@@ -95,7 +122,10 @@
recipient = whom
if(!recipient)
- to_chat(src, "Error: Admin-PM: Client not found.", confidential = TRUE)
+ to_chat(src,
+ type = MESSAGE_TYPE_ADMINPM,
+ html = "Error: Admin-PM: Client not found.",
+ confidential = TRUE)
return
recipient_ckey = recipient.ckey
@@ -110,7 +140,10 @@
if(!msg)
return
if(holder)
- to_chat(src, "Error: Use the admin IRC/Discord channel, nerd.", confidential = TRUE)
+ to_chat(src,
+ type = MESSAGE_TYPE_ADMINPM,
+ html = "Error: Use the admin IRC/Discord channel, nerd.",
+ confidential = TRUE)
return
@@ -127,8 +160,14 @@
recipient = GLOB.directory[recipient_ckey]
else
if(holder)
- to_chat(src, "Error: Admin-PM: Client not found.", confidential = TRUE)
- to_chat(src, "Message not sent:
[msg]", confidential = TRUE)
+ to_chat(src,
+ type = MESSAGE_TYPE_ADMINPM,
+ html = "Error: Admin-PM: Client not found.",
+ confidential = TRUE)
+ to_chat(src,
+ type = MESSAGE_TYPE_ADMINPM,
+ html = "Message not sent:
[msg]",
+ confidential = TRUE)
if(recipient_ticket)
recipient_ticket.AddInteraction("No client found, message not sent:
[msg]")
return
@@ -138,7 +177,10 @@
if(prefs.muted & MUTE_ADMINHELP)
- to_chat(src, "Error: Admin-PM: You are unable to use admin PM-s (muted).", confidential = TRUE)
+ to_chat(src,
+ type = MESSAGE_TYPE_ADMINPM,
+ html = "Error: Admin-PM: You are unable to use admin PM-s (muted).",
+ confidential = TRUE)
return
if (src.handle_spam_prevention(msg,MUTE_ADMINHELP))
@@ -158,7 +200,10 @@
var/keywordparsedmsg = keywords_lookup(msg)
if(external)
- to_chat(src, "PM to-Admins: [rawmsg]", confidential = TRUE)
+ to_chat(src,
+ type = MESSAGE_TYPE_ADMINPM,
+ html = "PM to-Admins: [rawmsg]",
+ confidential = TRUE)
var/datum/admin_help/AH = admin_ticket_log(src, "Reply PM from-[key_name(src, TRUE, TRUE)] to External: [keywordparsedmsg]")
externalreplyamount--
send2adminchat("[AH ? "#[AH.id] " : ""]Reply: [ckey]", rawmsg)
@@ -168,9 +213,14 @@
badmin = TRUE
if(recipient.holder && !badmin)
if(holder)
- to_chat(recipient, "Admin PM from-[key_name(src, recipient, 1)]: [keywordparsedmsg]", confidential = TRUE)
- to_chat(src, "Admin PM to-[key_name(recipient, src, 1)]: [keywordparsedmsg]", confidential = TRUE)
-
+ to_chat(recipient,
+ type = MESSAGE_TYPE_ADMINPM,
+ html = "Admin PM from-[key_name(src, recipient, 1)]: [keywordparsedmsg]",
+ confidential = TRUE)
+ to_chat(src,
+ type = MESSAGE_TYPE_ADMINPM,
+ html = "Admin PM to-[key_name(recipient, src, 1)]: [keywordparsedmsg]",
+ confidential = TRUE)
//omg this is dumb, just fill in both their tickets
var/interaction_message = "PM from-[key_name(src, recipient, 1)] to-[key_name(recipient, src, 1)]: [keywordparsedmsg]"
admin_ticket_log(src, interaction_message)
@@ -180,8 +230,14 @@
else //recipient is an admin but sender is not
var/replymsg = "Reply PM from-[key_name(src, recipient, 1)]: [keywordparsedmsg]"
admin_ticket_log(src, "[replymsg]")
- to_chat(recipient, "[replymsg]", confidential = TRUE)
- to_chat(src, "PM to-Admins: [msg]", confidential = TRUE)
+ to_chat(recipient,
+ type = MESSAGE_TYPE_ADMINPM,
+ html = "[replymsg]",
+ confidential = TRUE)
+ to_chat(src,
+ type = MESSAGE_TYPE_ADMINPM,
+ html = "PM to-Admins: [msg]",
+ confidential = TRUE)
SSblackbox.LogAhelp(current_ticket.id, "Reply", msg, recipient.ckey, src.ckey)
//play the receiving admin the adminhelp sound (if they have them enabled)
@@ -196,10 +252,22 @@
already_logged = TRUE
SSblackbox.LogAhelp(recipient.current_ticket.id, "Ticket Opened", msg, recipient.ckey, src.ckey)
- to_chat(recipient, "-- Administrator private message --", confidential = TRUE)
- to_chat(recipient, "Admin PM from-[key_name(src, recipient, 0)]: [msg]", confidential = TRUE)
- to_chat(recipient, "Click on the administrator's name to reply.", confidential = TRUE)
- to_chat(src, "Admin PM to-[key_name(recipient, src, 1)]: [msg]", confidential = TRUE)
+ to_chat(recipient,
+ type = MESSAGE_TYPE_ADMINPM,
+ html = "-- Administrator private message --",
+ confidential = TRUE)
+ to_chat(recipient,
+ type = MESSAGE_TYPE_ADMINPM,
+ html = "Admin PM from-[key_name(src, recipient, 0)]: [msg]",
+ confidential = TRUE)
+ to_chat(recipient,
+ type = MESSAGE_TYPE_ADMINPM,
+ html = "Click on the administrator's name to reply.",
+ confidential = TRUE)
+ to_chat(src,
+ type = MESSAGE_TYPE_ADMINPM,
+ html = "Admin PM to-[key_name(recipient, src, 1)]: [msg]",
+ confidential = TRUE)
admin_ticket_log(recipient, "PM From [key_name_admin(src)]: [keywordparsedmsg]")
@@ -215,20 +283,29 @@
INVOKE_ASYNC(src, .proc/popup_admin_pm, recipient, msg)
else //neither are admins
- to_chat(src, "Error: Admin-PM: Non-admin to non-admin PM communication is forbidden.", confidential = TRUE)
+ to_chat(src,
+ type = MESSAGE_TYPE_ADMINPM,
+ html = "Error: Admin-PM: Non-admin to non-admin PM communication is forbidden.",
+ confidential = TRUE)
return
if(external)
log_admin_private("PM: [key_name(src)]->External: [rawmsg]")
for(var/client/X in GLOB.admins)
- to_chat(X, "PM: [key_name(src, X, 0)]->External: [keywordparsedmsg]", confidential = TRUE)
+ to_chat(X,
+ type = MESSAGE_TYPE_ADMINPM,
+ html = "PM: [key_name(src, X, 0)]->External: [keywordparsedmsg]",
+ confidential = TRUE)
else
window_flash(recipient, ignorepref = TRUE)
log_admin_private("PM: [key_name(src)]->[key_name(recipient)]: [rawmsg]")
//we don't use message_admins here because the sender/receiver might get it too
for(var/client/X in GLOB.admins)
if(X.key!=key && X.key!=recipient.key) //check client/X is an admin and isn't the sender or recipient
- to_chat(X, "PM: [key_name(src, X, 0)]->[key_name(recipient, X, 0)]: [keywordparsedmsg]" , confidential = TRUE)
+ to_chat(X,
+ type = MESSAGE_TYPE_ADMINPM,
+ html = "PM: [key_name(src, X, 0)]->[key_name(recipient, X, 0)]: [keywordparsedmsg]" ,
+ confidential = TRUE)
/client/proc/popup_admin_pm(client/recipient, msg)
var/sender = src
@@ -318,9 +395,18 @@
log_admin_private("External PM: [sender] -> [key_name(C)] : [msg]")
msg = emoji_parse(msg)
- to_chat(C, "-- Administrator private message --", confidential = TRUE)
- to_chat(C, "Admin PM from-[adminname]: [msg]", confidential = TRUE)
- to_chat(C, "Click on the administrator's name to reply.", confidential = TRUE)
+ to_chat(C,
+ type = MESSAGE_TYPE_ADMINPM,
+ html = "-- Administrator private message --",
+ confidential = TRUE)
+ to_chat(C,
+ type = MESSAGE_TYPE_ADMINPM,
+ html = "Admin PM from-[adminname]: [msg]",
+ confidential = TRUE)
+ to_chat(C,
+ type = MESSAGE_TYPE_ADMINPM,
+ html = "Click on the administrator's name to reply.",
+ confidential = TRUE)
admin_ticket_log(C, "PM From [tgs_tagged]: [msg]")
diff --git a/code/modules/admin/verbs/adminsay.dm b/code/modules/admin/verbs/adminsay.dm
index 5793ff6d3c2..62ba62f01c3 100644
--- a/code/modules/admin/verbs/adminsay.dm
+++ b/code/modules/admin/verbs/adminsay.dm
@@ -13,7 +13,11 @@
msg = keywords_lookup(msg)
var/custom_asay_color = (CONFIG_GET(flag/allow_admin_asaycolor) && prefs.asaycolor) ? "" : ""
msg = "ADMIN: [key_name(usr, 1)] [ADMIN_FLW(mob)]: [custom_asay_color][custom_asay_color ? "":null]"
- to_chat(GLOB.admins, msg, confidential = TRUE)
+ to_chat(GLOB.admins, list(
+ "type" = MESSAGE_TYPE_ADMINCHAT,
+ "html" = msg,
+ "confidential" = TRUE,
+ ))
SSblackbox.record_feedback("tally", "admin_verb", 1, "Asay") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
diff --git a/code/modules/tgchat/message.dm b/code/modules/tgchat/message.dm
new file mode 100644
index 00000000000..2e9bfe784ec
--- /dev/null
+++ b/code/modules/tgchat/message.dm
@@ -0,0 +1,17 @@
+/**
+ * Message-related procs
+ *
+ * Message format (/list):
+ * - type - Message type, must be one of defines in `code/__DEFINES/chat.dm`
+ * - text - Plain message text
+ * - html - HTML message text
+ * - Optional metadata, can be any key/value pair.
+ *
+ * Copyright (c) 2020 Aleksej Komarov
+ * SPDX-License-Identifier: MIT
+ */
+
+/proc/message_to_html(message)
+ // Here it is possible to add a switch statement
+ // to custom-handle various message types.
+ return message["html"] || message["text"]
diff --git a/code/modules/tgchat/to_chat.dm b/code/modules/tgchat/to_chat.dm
new file mode 100644
index 00000000000..6e0691cfea3
--- /dev/null
+++ b/code/modules/tgchat/to_chat.dm
@@ -0,0 +1,71 @@
+/**
+ * Copyright (c) 2020 Aleksej Komarov
+ * SPDX-License-Identifier: MIT
+ */
+
+/**
+ * Circumvents the message queue and sends the message
+ * to the recipient (target) as soon as possible.
+ */
+/proc/to_chat_immediate(target, html,
+ type = null,
+ text = null,
+ // FIXME: These flags are now pointless and have no effect
+ handle_whitespace = TRUE,
+ trailing_newline = TRUE,
+ confidential = FALSE)
+ if(!target || (!html && !text))
+ return
+ if(target == world)
+ target = GLOB.clients
+ // Build a message
+ var/message = list()
+ if(type) message["type"] = type
+ if(text) message["text"] = text
+ if(html) message["html"] = html
+ var/message_blob = TGUI_CREATE_MESSAGE("chat/message", message)
+ var/message_html = message_to_html(message)
+ if(islist(target))
+ for(var/_target in target)
+ var/client/client = CLIENT_FROM_VAR(_target)
+ if(client)
+ // Send to tgchat
+ client.tgui_panel?.window.send_raw_message(message_blob)
+ // Send to old chat
+ SEND_TEXT(client, message_html)
+ return
+ var/client/client = CLIENT_FROM_VAR(target)
+ if(client)
+ // Send to tgchat
+ client.tgui_panel?.window.send_raw_message(message_blob)
+ // Send to old chat
+ SEND_TEXT(client, message_html)
+
+/**
+ * Sends the message to the recipient (target).
+ *
+ * Recommended way to write to_chat calls:
+ * to_chat(client,
+ * type = MESSAGE_TYPE_INFO,
+ * html = "You have found [object]")
+ */
+/proc/to_chat(target, html,
+ type = null,
+ text = null,
+ // FIXME: These flags are now pointless and have no effect
+ handle_whitespace = TRUE,
+ trailing_newline = TRUE,
+ confidential = FALSE)
+ if(Master.current_runlevel == RUNLEVEL_INIT || !SSchat?.initialized)
+ to_chat_immediate(target, html, type, text)
+ return
+ if(!target || (!html && !text))
+ return
+ if(target == world)
+ target = GLOB.clients
+ // Build a message
+ var/message = list()
+ if(type) message["type"] = type
+ if(text) message["text"] = text
+ if(html) message["html"] = html
+ SSchat.queue(target, message)
diff --git a/code/modules/tgui_panel/external.dm b/code/modules/tgui_panel/external.dm
index 57c89dc194d..3257407e9cb 100644
--- a/code/modules/tgui_panel/external.dm
+++ b/code/modules/tgui_panel/external.dm
@@ -12,7 +12,7 @@
set name = "Fix chat"
set category = "OOC"
var/action
- log_tgui(src, "tgui_panel: Started fix_chat.")
+ log_tgui(src, "tgui_panel: Started fixing.")
// Not initialized
if(!tgui_panel || !istype(tgui_panel))
log_tgui(src, "tgui_panel: datum is missing")
diff --git a/code/modules/tgui_panel/to_chat.dm b/code/modules/tgui_panel/to_chat.dm
deleted file mode 100644
index aad27d48727..00000000000
--- a/code/modules/tgui_panel/to_chat.dm
+++ /dev/null
@@ -1,71 +0,0 @@
-/**
- * Copyright (c) 2020 Aleksej Komarov
- * SPDX-License-Identifier: MIT
- */
-
-/**
- * global
- *
- * Circumvents the message queue and sends the message
- * to the recipient (target) as soon as possible.
- */
-/proc/to_chat_immediate(
- target,
- text,
- handle_whitespace = TRUE,
- trailing_newline = TRUE,
- confidential = FALSE)
- if(!target || !text)
- return
- if(target == world)
- target = GLOB.clients
- var/flags = handle_whitespace \
- | trailing_newline << 1 \
- | confidential << 2
- var/message = TGUI_CREATE_MESSAGE("chat/message", list(
- "text" = text,
- "flags" = flags,
- ))
- if(islist(target))
- for(var/_target in target)
- var/client/client = CLIENT_FROM_VAR(_target)
- if(client)
- // Send to tgchat
- client.tgui_panel?.window.send_raw_message(message)
- // Send to old chat
- SEND_TEXT(client, text)
- return
- var/client/client = CLIENT_FROM_VAR(target)
- if(client)
- // Send to tgchat
- client.tgui_panel?.window.send_raw_message(message)
- // Send to old chat
- SEND_TEXT(client, text)
-
-/**
- * global
- *
- * Sends the message to the recipient (target).
- */
-/proc/to_chat(
- target,
- text,
- handle_whitespace = TRUE,
- trailing_newline = TRUE,
- confidential = FALSE)
- if(Master.current_runlevel == RUNLEVEL_INIT || !SSchat?.initialized)
- to_chat_immediate(
- target,
- text,
- handle_whitespace,
- trailing_newline,
- confidential)
- return
- if(!target || !text)
- return
- if(target == world)
- target = GLOB.clients
- var/flags = handle_whitespace \
- | trailing_newline << 1 \
- | confidential << 2
- SSchat.queue(target, text, flags)
diff --git a/tgstation.dme b/tgstation.dme
index 32c399d1f04..e53d4b401f8 100644
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -31,6 +31,7 @@
#include "code\__DEFINES\botany.dm"
#include "code\__DEFINES\callbacks.dm"
#include "code\__DEFINES\cargo.dm"
+#include "code\__DEFINES\chat.dm"
#include "code\__DEFINES\cinematics.dm"
#include "code\__DEFINES\cleaning.dm"
#include "code\__DEFINES\colors.dm"
@@ -3110,6 +3111,8 @@
#include "code\modules\swarmers\swarmer.dm"
#include "code\modules\swarmers\swarmer_act.dm"
#include "code\modules\swarmers\swarmer_objs.dm"
+#include "code\modules\tgchat\message.dm"
+#include "code\modules\tgchat\to_chat.dm"
#include "code\modules\tgs\includes.dm"
#include "code\modules\tgui\external.dm"
#include "code\modules\tgui\states.dm"
@@ -3136,7 +3139,6 @@
#include "code\modules\tgui_panel\external.dm"
#include "code\modules\tgui_panel\telemetry.dm"
#include "code\modules\tgui_panel\tgui_panel.dm"
-#include "code\modules\tgui_panel\to_chat.dm"
#include "code\modules\tooltip\tooltip.dm"
#include "code\modules\unit_tests\_unit_tests.dm"
#include "code\modules\uplink\uplink_devices.dm"
diff --git a/tgui/package.json b/tgui/package.json
index d0ef805c470..380cbda4efa 100644
--- a/tgui/package.json
+++ b/tgui/package.json
@@ -1,7 +1,7 @@
{
"private": true,
"name": "tgui",
- "version": "4.1.0",
+ "version": "4.2.0",
"workspaces": [
"packages/*"
],
diff --git a/tgui/packages/common/package.json b/tgui/packages/common/package.json
index 22560d92f93..5e04c1d5963 100644
--- a/tgui/packages/common/package.json
+++ b/tgui/packages/common/package.json
@@ -1,6 +1,6 @@
{
"private": true,
"name": "common",
- "version": "4.1.0",
+ "version": "4.2.0",
"type": "module"
}
diff --git a/tgui/packages/tgui-dev-server/package.json b/tgui/packages/tgui-dev-server/package.json
index 3e50a97f0eb..d3063870210 100644
--- a/tgui/packages/tgui-dev-server/package.json
+++ b/tgui/packages/tgui-dev-server/package.json
@@ -1,7 +1,7 @@
{
"private": true,
"name": "tgui-dev-server",
- "version": "4.1.0",
+ "version": "4.2.0",
"type": "module",
"dependencies": {
"axios": "^0.19.2",
diff --git a/tgui/packages/tgui-panel/Panel.js b/tgui/packages/tgui-panel/Panel.js
index d86a1fc8e86..fdd7977f4e3 100644
--- a/tgui/packages/tgui-panel/Panel.js
+++ b/tgui/packages/tgui-panel/Panel.js
@@ -14,9 +14,10 @@ import { PingIndicator } from './ping';
import { SettingsPanel, useSettings } from './settings';
export const Panel = (props, context) => {
- if (Byond.IS_LTE_IE8) {
+ // IE8-10: Needs special treatment due to missing Flex support
+ if (Byond.IS_LTE_IE10) {
return (
-
/gi,"\n").replace(/<\/?[a-z0-9-_]+[^>]*>/gi,"").replace(/&(nbsp|amp|quot|lt|gt|apos);/g,(function(e,n){return t[n]})).replace(/?([0-9]+);/gi,(function(e,t){var n=parseInt(t,10);return String.fromCharCode(n)})).replace(/?([0-9a-f]+);/gi,(function(e,t){var n=parseInt(t,16);return String.fromCharCode(n)}))};t.buildQueryString=function(e){return Object.keys(e).map((function(t){return encodeURIComponent(t)+"="+encodeURIComponent(e[t])})).join("&")}},function(e,t,n){"use strict";var r={}.hasOwnProperty;e.exports=function(e,t){return r.call(e,t)}},function(e,t,n){"use strict";var r=n(53),o=n(66),i=n(18),a=n(12),c=n(72),u=[].push,s=function(e){var t=1==e,n=2==e,s=3==e,l=4==e,f=6==e,d=5==e||f;return function(p,h,g,v){for(var m,y,b=i(p),w=o(b),x=r(h,g,3),_=a(w.length),E=0,k=v||c,S=t?k(p,_):n?k(p,0):undefined;_>E;E++)if((d||E in w)&&(y=x(m=w[E],E,b),e))if(t)S[E]=y;else if(y)switch(e){case 3:return!0;case 5:return m;case 6:return E;case 2:u.call(S,m)}else if(l)return!1;return f?-1:s||l?l:S}};e.exports={forEach:s(0),map:s(1),filter:s(2),some:s(3),every:s(4),find:s(5),findIndex:s(6)}},function(e,t,n){"use strict";t.__esModule=!0,t.useSelector=t.useDispatch=t.createAction=t.combineReducers=t.applyMiddleware=t.createStore=void 0;n(0);var r=n(30);function o(e,t){var n;if("undefined"==typeof Symbol||null==e[Symbol.iterator]){if(Array.isArray(e)||(n=function(e,t){if(!e)return;if("string"==typeof e)return i(e,t);var n=Object.prototype.toString.call(e).slice(8,-1);"Object"===n&&e.constructor&&(n=e.constructor.name);if("Map"===n||"Set"===n)return Array.from(e);if("Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n))return i(e,t)}(e))||t&&e&&"number"==typeof e.length){n&&(e=n);var r=0;return function(){return r>=e.length?{done:!0}:{done:!1,value:e[r++]}}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}return(n=e[Symbol.iterator]()).next.bind(n)}function i(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=new Array(t);nu||n!=n?l*Infinity:l*n}},function(e,t,n){"use strict";var r=n(7).isFinite;e.exports=Number.isFinite||function(e){return"number"==typeof e&&r(e)}},function(e,t,n){"use strict";var r=n(7),o=n(62).trim,i=n(91),a=r.parseFloat,c=1/a(i+"-0")!=-Infinity;e.exports=c?function(e){var t=o(String(e)),n=a(t);return 0===n&&"-"==t.charAt(0)?-0:n}:a},function(e,t,n){"use strict";var r=n(36);e.exports=function(e){if("number"!=typeof e&&"Number"!=r(e))throw TypeError("Incorrect invocation");return+e}},function(e,t,n){"use strict";var r=n(9),o=n(5),i=n(71),a=n(115),c=n(81),u=n(18),s=n(66),l=Object.assign,f=Object.defineProperty;e.exports=!l||o((function(){if(r&&1!==l({b:1},l(f({},"a",{enumerable:!0,get:function(){f(this,"b",{value:3,enumerable:!1})}}),{b:2})).b)return!0;var e={},t={},n=Symbol();return e[n]=7,"abcdefghijklmnopqrst".split("").forEach((function(e){t[e]=e})),7!=l({},e)[n]||"abcdefghijklmnopqrst"!=i(l({},t)).join("")}))?function(e,t){for(var n=u(e),o=arguments.length,l=1,f=a.f,d=c.f;o>l;)for(var p,h=s(arguments[l++]),g=f?i(h).concat(f(h)):i(h),v=g.length,m=0;v>m;)p=g[m++],r&&!d.call(h,p)||(n[p]=h[p]);return n}:l},function(e,t,n){"use strict";var r=n(121),o=n(84);e.exports=r?{}.toString:function(){return"[object "+o(this)+"]"}},function(e,t,n){"use strict";var r=n(7);e.exports=function(e,t){var n=r.console;n&&n.error&&(1===arguments.length?n.error(e):n.error(e,t))}},function(e,t,n){"use strict";var r=n(37);e.exports=function(e){var t=r(e);if(t<0)throw RangeError("The argument can't be less than 0");return t}},function(e,t,n){"use strict";t.__esModule=!0,t._CI=Ae,t._HI=B,t._M=Te,t._MCCC=Le,t._ME=Ie,t._MFCC=Re,t._MP=Ce,t._MR=ye,t.__render=je,t.createComponentVNode=function(e,t,n,r,o){var a=new T(1,null,null,e=function(e,t){if(12&e)return e;if(t.prototype&&t.prototype.render)return 4;if(t.render)return 32776;return 8}(e,t),r,function(e,t,n){var r=(32768&e?t.render:t).defaultProps;if(i(r))return n;if(i(n))return l(r,null);return N(n,r)}(e,t,n),function(e,t,n){if(4&e)return n;var r=(32768&e?t.render:t).defaultHooks;if(i(r))return n;if(i(n))return r;return N(n,r)}(e,t,o),t);k.createVNode&&k.createVNode(a);return a},t.createFragment=M,t.createPortal=function(e,t){var n=B(e);return O(1024,1024,null,n,0,null,n.key,t)},t.createRef=function(){return{current:null}},t.createRenderer=function(e){return function(t,n,r,o){e||(e=t),Fe(n,e,r,o)}},t.createTextVNode=I,t.createVNode=O,t.directClone=L,t.findDOMfromVNode=b,t.forwardRef=function(e){return{render:e}},t.getFlagsForElementVnode=function(e){switch(e){case"svg":return 32;case"input":return 64;case"select":return 256;case"textarea":return 128;case"$F":return 8192;default:return 1}},t.linkEvent=function(e,t){if(c(t))return{data:e,event:t};return null},t.normalizeProps=function(e){var t=e.props;if(t){var n=e.flags;481&n&&(void 0!==t.children&&i(e.children)&&P(e,t.children),void 0!==t.className&&(e.className=t.className||null,t.className=undefined)),void 0!==t.key&&(e.key=t.key,t.key=undefined),void 0!==t.ref&&(e.ref=8&n?l(e.ref,t.ref):t.ref,t.ref=undefined)}return e},t.render=Fe,t.rerender=He,t.version=t.options=t.Fragment=t.EMPTY_OBJ=t.Component=void 0;var r=Array.isArray;function o(e){var t=typeof e;return"string"===t||"number"===t}function i(e){return null==e}function a(e){return null===e||!1===e||!0===e||void 0===e}function c(e){return"function"==typeof e}function u(e){return"string"==typeof e}function s(e){return null===e}function l(e,t){var n={};if(e)for(var r in e)n[r]=e[r];if(t)for(var o in t)n[o]=t[o];return n}function f(e){return!s(e)&&"object"==typeof e}var d={};t.EMPTY_OBJ=d;function p(e){return e.substr(2).toLowerCase()}function h(e,t){e.appendChild(t)}function g(e,t,n){s(n)?h(e,t):e.insertBefore(t,n)}function v(e,t){e.removeChild(t)}function m(e){for(var t=0;t
\n":"'+(n?e:Y(e,!0))+"
\n"},t.blockquote=function(e){return""+(n?e:Y(e,!0))+"\n"+e+"
\n"},t.html=function(e){return e},t.heading=function(e,t,n,r){return this.options.headerIds?"
\n":"
\n"},t.list=function(e,t,n){var r=t?"ol":"ul";return"<"+r+(t&&1!==n?' start="'+n+'"':"")+">\n"+e+""+r+">\n"},t.listitem=function(e){return"\n\n"+e+"\n"+t+"
\n"},t.tablerow=function(e){return"\n"+e+" \n"},t.tablecell=function(e,t){var n=t.header?"th":"td";return(t.align?"<"+n+' align="'+t.align+'">':"<"+n+">")+e+""+n+">\n"},t.strong=function(e){return""+e+""},t.em=function(e){return""+e+""},t.codespan=function(e){return""+e+""},t.br=function(){return this.options.xhtml?"
":"
"},t.del=function(e){return""+e+""},t.link=function(e,t,n){if(null===(e=z(this.options.sanitize,this.options.baseUrl,e)))return n;var r='"+n+""},t.image=function(e,t,n){if(null===(e=z(this.options.sanitize,this.options.baseUrl,e)))return n;var r='":">"},t.text=function(e){return e},e}(),$=function(){function e(){}var t=e.prototype;return t.strong=function(e){return e},t.em=function(e){return e},t.codespan=function(e){return e},t.del=function(e){return e},t.html=function(e){return e},t.text=function(e){return e},t.link=function(e,t,n){return""+n},t.image=function(e,t,n){return""+n},t.br=function(){return""},e}(),H=function(){function e(){this.seen={}}return e.prototype.slug=function(e){var t=e.toLowerCase().trim().replace(/<[!\/a-z].*?>/gi,"").replace(/[\u2000-\u206F\u2E00-\u2E7F\\'!"#$%&()*+,./:;<=>?@[\]^`{|}~]/g,"").replace(/\s/g,"-");if(this.seen.hasOwnProperty(t)){var n=t;do{this.seen[n]++,t=n+"-"+this.seen[n]}while(this.seen.hasOwnProperty(t))}return this.seen[t]=0,t},e}(),W=r.defaults,q=x.unescape,G=function(){function e(e){this.options=e||W,this.options.renderer=this.options.renderer||new U,this.renderer=this.options.renderer,this.renderer.options=this.options,this.textRenderer=new $,this.slugger=new H}e.parse=function(t,n){return new e(n).parse(t)};var t=e.prototype;return t.parse=function(e,t){void 0===t&&(t=!0);var n,r,o,i,a,c,u,s,l,f,d,p,h,g,v,m,y,b,w="",x=e.length;for(n=0;n
"+Q(u.message+"",!0)+"";throw u}}return ne.options=ne.setOptions=function(e){return X(ne.defaults,e),ee(ne.defaults),ne},ne.getDefaults=J,ne.defaults=te,ne.use=function(e){var t=X({},e);if(e.renderer&&function(){var n=ne.defaults.renderer||new U,r=function(t){var r=n[t];n[t]=function(){for(var o=arguments.length,i=new Array(o),a=0;a
'+(n?e:Y(e,!0))+"\n":""+(n?e:Y(e,!0))+"\n"},t.blockquote=function(e){return"\n"+e+"\n"},t.html=function(e){return e},t.heading=function(e,t,n,r){return this.options.headerIds?"
"+e+"
\n"},t.table=function(e,t){return t&&(t=""+t+""),""+e+""},t.br=function(){return this.options.xhtml?""+Q(u.message+"",!0)+"";throw u}}return ne.options=ne.setOptions=function(e){return X(ne.defaults,e),ee(ne.defaults),ne},ne.getDefaults=J,ne.defaults=te,ne.use=function(e){var t=X({},e);if(e.renderer&&function(){var n=ne.defaults.renderer||new U,r=function(t){var r=n[t];n[t]=function(){for(var o=arguments.length,i=new Array(o),a=0;a