diff --git a/code/game/objects/items/devices/portable_chem_mixer.dm b/code/game/objects/items/devices/portable_chem_mixer.dm
new file mode 100644
index 00000000000..67f7edb8dd9
--- /dev/null
+++ b/code/game/objects/items/devices/portable_chem_mixer.dm
@@ -0,0 +1,226 @@
+/obj/item/storage/portable_chem_mixer
+ name = "Portable Chemical Mixer"
+ desc = "A portable device that dispenses and mixes chemicals. Requires a vortex anomaly core. All necessary reagents need to be supplied with beakers. A label indicates that a screwdriver is required to open it for refills. This device can be worn on a belt. The letters 'S&T' are imprinted on the side."
+ icon = 'icons/obj/chemical.dmi'
+ icon_state = "portablechemicalmixer_open"
+ w_class = WEIGHT_CLASS_HUGE
+ slot_flags = ITEM_SLOT_BELT
+ equip_sound = 'sound/items/equip/toolbelt_equip.ogg'
+ custom_price = 2000
+ custom_premium_price = 2000
+ var/ui_x = 645 ///tgui window width
+ var/ui_y = 550 ///tgui window height
+
+ var/obj/item/reagent_containers/beaker = null ///Creating an empty slot for a beaker that can be added to dispense into
+ var/amount = 30 ///The amount of reagent that is to be dispensed currently
+
+ var/anomaly_core_present = FALSE ///TRUE if an anomaly core has been added
+
+ var/list/dispensable_reagents = list() ///List in which all currently dispensable reagents go
+
+/obj/item/storage/portable_chem_mixer/ComponentInitialize()
+ . = ..()
+ var/datum/component/storage/STR = GetComponent(/datum/component/storage)
+ STR.max_combined_w_class = 200
+ STR.max_items = 50
+ STR.insert_preposition = "in"
+ STR.set_holdable(list(
+ /obj/item/reagent_containers/glass/beaker,
+ ))
+
+/obj/item/storage/portable_chem_mixer/Destroy()
+ QDEL_NULL(beaker)
+ return ..()
+
+/obj/item/storage/portable_chem_mixer/ex_act(severity, target)
+ if(severity < 3)
+ ..()
+
+/obj/item/storage/portable_chem_mixer/attackby(obj/item/I, mob/user, params)
+ if(istype(I, /obj/item/raw_anomaly_core/vortex) && !anomaly_core_present)
+ anomaly_core_present = TRUE
+ QDEL_NULL(I)
+ to_chat(user, "You insert the vortex anomaly core. The device is now functional. A screwdriver is needed to open and close the device for refills.")
+ return
+ if(!anomaly_core_present)
+ to_chat(user, "A vortex anomaly core has to be inserted to activate this device.")
+ return
+ var/locked = SEND_SIGNAL(src, COMSIG_IS_STORAGE_LOCKED)
+ if (I.tool_behaviour == TOOL_SCREWDRIVER)
+ SEND_SIGNAL(src, COMSIG_TRY_STORAGE_SET_LOCKSTATE, !locked)
+ if (!locked)
+ update_contents()
+ if (locked)
+ replace_beaker(user)
+ update_icon()
+ I.play_tool_sound(src, 50)
+ return
+
+ else if (istype(I, /obj/item/reagent_containers) && !(I.item_flags & ABSTRACT) && I.is_open_container() && locked)
+ var/obj/item/reagent_containers/B = I
+ . = TRUE //no afterattack
+ if(!user.transferItemToLoc(B, src))
+ return
+ replace_beaker(user, B)
+ update_icon()
+ updateUsrDialog()
+ return
+
+ return ..()
+
+/**
+ * Updates the contents of the portable chemical mixer
+ *
+ * A list of dispensable reagents is created by iterating through each source beaker in the portable chemical beaker and reading its contents
+ */
+/obj/item/storage/portable_chem_mixer/proc/update_contents()
+ dispensable_reagents.Cut()
+
+ for (var/obj/item/reagent_containers/glass/beaker/B in contents)
+ var/key = B.reagents.get_master_reagent_id()
+ if (!(key in dispensable_reagents))
+ dispensable_reagents[key] = list()
+ dispensable_reagents[key]["reagents"] = list()
+ dispensable_reagents[key]["reagents"] += B.reagents
+
+ return
+
+/obj/item/storage/portable_chem_mixer/update_icon_state()
+ var/locked = SEND_SIGNAL(src, COMSIG_IS_STORAGE_LOCKED)
+ if (!locked)
+ icon_state = "portablechemicalmixer_open"
+ else if (beaker)
+ icon_state = "portablechemicalmixer_full"
+ else
+ icon_state = "portablechemicalmixer_empty"
+
+
+/obj/item/storage/portable_chem_mixer/AltClick(mob/living/user)
+ var/locked = SEND_SIGNAL(src, COMSIG_IS_STORAGE_LOCKED)
+ if (!locked)
+ return ..()
+ if(!can_interact(user) || !user.canUseTopic(src, BE_CLOSE, FALSE, NO_TK))
+ return
+ replace_beaker(user)
+ update_icon()
+
+/**
+ * Replaces the beaker of the portable chemical mixer with another beaker, or simply adds the new beaker if none is in currently
+ *
+ * Checks if a valid user and a valid new beaker exist and attempts to replace the current beaker in the portable chemical mixer with the one in hand. Simply places the new beaker in if no beaker is currently loaded
+ * Arguments:
+ * * mob/living/user - The user who is trying to exchange beakers
+ * * obj/item/reagent_containers/new_beaker - The new beaker that the user wants to put into the device
+ */
+/obj/item/storage/portable_chem_mixer/proc/replace_beaker(mob/living/user, obj/item/reagent_containers/new_beaker)
+ if(!user)
+ return FALSE
+ if(beaker)
+ user.put_in_hands(beaker)
+ beaker = null
+ if(new_beaker)
+ beaker = new_beaker
+ return TRUE
+
+/obj/item/storage/portable_chem_mixer/attack_hand(mob/user)
+ if(!anomaly_core_present)
+ to_chat(user, "A vortex anomaly core has to be inserted to activate this device.")
+ else if(loc == user)
+ var/locked = SEND_SIGNAL(src, COMSIG_IS_STORAGE_LOCKED)
+ if (locked)
+ ui_interact(user)
+ return
+ return ..()
+
+/obj/item/storage/portable_chem_mixer/attack_self(mob/user)
+ if(!anomaly_core_present)
+ to_chat(user, "A vortex anomaly core has to be inserted to activate this device.")
+ return
+ if(loc == user)
+ var/locked = SEND_SIGNAL(src, COMSIG_IS_STORAGE_LOCKED)
+ if (locked)
+ ui_interact(user)
+ return
+ else
+ to_chat(user, "The portable chemical mixer is currently open and its contents can be accessed.")
+ return
+ return
+
+/obj/item/storage/portable_chem_mixer/MouseDrop(obj/over_object)
+ . = ..()
+ if(ismob(loc))
+ var/mob/M = loc
+ if(!M.incapacitated() && istype(over_object, /obj/screen/inventory/hand))
+ var/obj/screen/inventory/hand/H = over_object
+ M.putItemFromInventoryInHandIfPossible(src, H.held_index)
+
+/obj/item/storage/portable_chem_mixer/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \
+ datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
+ ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
+ if(!ui)
+ ui = new(user, src, ui_key, "PortableChemMixer", name, ui_x, ui_y, master_ui, state)
+ if(user.hallucinating())
+ ui.set_autoupdate(FALSE) //to not ruin the immersion by constantly changing the fake chemicals
+ ui.open()
+
+/obj/item/storage/portable_chem_mixer/ui_data(mob/user)
+ var/list/data = list()
+ data["amount"] = amount
+ data["isBeakerLoaded"] = beaker ? 1 : 0
+ data["beakerCurrentVolume"] = beaker ? beaker.reagents.total_volume : null
+ data["beakerMaxVolume"] = beaker ? beaker.volume : null
+ data["beakerTransferAmounts"] = beaker ? beaker.possible_transfer_amounts : null
+ var/chemicals[0]
+ var/is_hallucinating = user.hallucinating()
+ if(user.hallucinating())
+ is_hallucinating = TRUE
+ for(var/re in dispensable_reagents)
+ var/value = dispensable_reagents[re]
+ var/datum/reagent/temp = GLOB.chemical_reagents_list[re]
+ if(temp)
+ var/chemname = temp.name
+ var/total_volume = 0
+ for (var/datum/reagents/rs in value["reagents"])
+ total_volume += rs.total_volume
+ if(is_hallucinating && prob(5))
+ chemname = "[pick_list_replacements("hallucination.json", "chemicals")]"
+ chemicals.Add(list(list("title" = chemname, "id" = ckey(temp.name), "volume" = total_volume )))
+ data["chemicals"] = chemicals
+ var/beakerContents[0]
+ if(beaker)
+ for(var/datum/reagent/R in beaker.reagents.reagent_list)
+ beakerContents.Add(list(list("name" = R.name, "id" = ckey(R.name), "volume" = R.volume))) // list in a list because Byond merges the first list...
+ data["beakerContents"] = beakerContents
+ return data
+
+/obj/item/storage/portable_chem_mixer/ui_act(action, params)
+ if(..())
+ return
+ switch(action)
+ if("amount")
+ var/target = text2num(params["target"])
+ amount = target
+ . = TRUE
+ if("dispense")
+ var/reagent_name = params["reagent"]
+ var/datum/reagent/reagent = GLOB.name2reagent[reagent_name]
+ var/entry = dispensable_reagents[reagent]
+ if(beaker)
+ var/datum/reagents/R = beaker.reagents
+ var/actual = min(amount, 1000, R.maximum_volume - R.total_volume)
+ // todo: add check if we have enough reagent left
+ for (var/datum/reagents/source in entry["reagents"])
+ var/to_transfer = min(source.total_volume, actual)
+ source.trans_to(beaker, to_transfer)
+ actual -= to_transfer
+ if (actual <= 0)
+ break
+ . = TRUE
+ if("remove")
+ var/amount = text2num(params["amount"])
+ beaker.reagents.remove_all(amount)
+ . = TRUE
+ if("eject")
+ replace_beaker(usr)
+ update_icon()
+ . = TRUE
diff --git a/code/modules/research/designs/medical_designs.dm b/code/modules/research/designs/medical_designs.dm
index ed83702c1fd..59c277e74c3 100644
--- a/code/modules/research/designs/medical_designs.dm
+++ b/code/modules/research/designs/medical_designs.dm
@@ -281,6 +281,16 @@
build_path = /obj/item/reagent_containers/blood
category = list("Medical Designs")
+/datum/design/portable_chem_mixer
+ name = "Portable Chemical Mixer"
+ desc = "A portable device that dispenses and mixes chemicals. Reagents have to be supplied with beakers."
+ id = "portable_chem_mixer"
+ build_type = PROTOLATHE
+ departmental_flags = DEPARTMENTAL_FLAG_MEDICAL
+ materials = list(/datum/material/plastic = 5000, /datum/material/iron = 10000, /datum/material/glass = 3000)
+ build_path = /obj/item/storage/portable_chem_mixer
+ category = list("Equipment")
+
/////////////////////////////////////////
//////////Cybernetic Implants////////////
/////////////////////////////////////////
diff --git a/code/modules/research/techweb/all_nodes.dm b/code/modules/research/techweb/all_nodes.dm
index bc0d07945f3..cb42d70d6ab 100644
--- a/code/modules/research/techweb/all_nodes.dm
+++ b/code/modules/research/techweb/all_nodes.dm
@@ -57,7 +57,7 @@
display_name = "Basic Medical Equipment"
description = "Basic medical tools and equipment."
design_ids = list("cybernetic_liver", "cybernetic_heart", "cybernetic_lungs", "scalpel", "circular_saw", "bonesetter", "surgicaldrill", "retractor", "cautery", "hemostat",
- "surgical_drapes", "syringe", "plumbing_rcd", "beaker", "large_beaker", "xlarge_beaker", "dropper", "defibmountdefault", "surgical_tape")
+ "surgical_drapes", "syringe", "plumbing_rcd", "beaker", "large_beaker", "xlarge_beaker", "dropper", "defibmountdefault", "surgical_tape", "portable_chem_mixer")
/////////////////////////Biotech/////////////////////////
/datum/techweb_node/biotech
diff --git a/icons/obj/chemical.dmi b/icons/obj/chemical.dmi
index c76ca0bbf0c..31cc2934780 100644
Binary files a/icons/obj/chemical.dmi and b/icons/obj/chemical.dmi differ
diff --git a/tgstation.dme b/tgstation.dme
index 40b3e363987..760ca56a5ee 100644
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -1037,6 +1037,7 @@
#include "code\game\objects\items\devices\paicard.dm"
#include "code\game\objects\items\devices\pipe_painter.dm"
#include "code\game\objects\items\devices\polycircuit.dm"
+#include "code\game\objects\items\devices\portable_chem_mixer.dm"
#include "code\game\objects\items\devices\powersink.dm"
#include "code\game\objects\items\devices\pressureplates.dm"
#include "code\game\objects\items\devices\quantum_keycard.dm"
diff --git a/tgui/packages/tgui/interfaces/PortableChemMixer.js b/tgui/packages/tgui/interfaces/PortableChemMixer.js
new file mode 100644
index 00000000000..ba200a8cc3c
--- /dev/null
+++ b/tgui/packages/tgui/interfaces/PortableChemMixer.js
@@ -0,0 +1,111 @@
+import { toTitleCase } from 'common/string';
+import { Fragment } from 'inferno';
+import { useBackend } from '../backend';
+import { AnimatedNumber, Box, Button, LabeledList, Section } from '../components';
+import { Window } from '../layouts';
+import { sortBy } from 'common/collections';
+
+export const PortableChemMixer = (props, context) => {
+ const { act, data } = useBackend(context);
+ const recording = !!data.recordingRecipe;
+ const beakerTransferAmounts = data.beakerTransferAmounts || [];
+ const beakerContents = recording
+ && Object.keys(data.recordingRecipe)
+ .map(id => ({
+ id,
+ name: toTitleCase(id.replace(/_/, ' ')),
+ volume: data.recordingRecipe[id],
+ }))
+ || data.beakerContents
+ || [];
+ const chemicals = sortBy(chem => chem.title)(data.chemicals);
+ return (
+ "+e+"
/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 o=n(53),r=n(62),a=n(17),i=n(12),c=n(68),l=[].push,d=function(e){var t=1==e,n=2==e,d=3==e,u=4==e,s=6==e,p=5==e||s;return function(m,f,h,C){for(var g,b,N=a(m),v=r(N),V=o(f,h,3),y=i(v.length),k=0,x=C||c,_=t?x(m,y):n?x(m,0):undefined;y>k;k++)if((p||k in v)&&(b=V(g=v[k],k,N),e))if(t)_[k]=b;else if(b)switch(e){case 3:return!0;case 5:return g;case 6:return k;case 2:l.call(_,g)}else if(u)return!1;return s?-1:d||u?u:_}};e.exports={forEach:d(0),map:d(1),filter:d(2),some:d(3),every:d(4),find:d(5),findIndex:d(6)}},function(e,t,n){"use strict";var o=n(9),r=n(74),a=n(51),i=n(27),c=n(36),l=n(19),d=n(128),u=Object.getOwnPropertyDescriptor;t.f=o?u:function(e,t){if(e=i(e),t=c(t,!0),d)try{return u(e,t)}catch(n){}if(l(e,t))return a(!r.f.call(e,t),e[t])}},function(e,t,n){"use strict";e.exports=function(e){if(e==undefined)throw TypeError("Can't call method on "+e);return e}},function(e,t,n){"use strict";var o=n(7),r=n(31),a=n(19),i=n(91),c=n(92),l=n(37),d=l.get,u=l.enforce,s=String(String).split("String");(e.exports=function(e,t,n,c){var l=!!c&&!!c.unsafe,d=!!c&&!!c.enumerable,p=!!c&&!!c.noTargetGet;"function"==typeof n&&("string"!=typeof t||a(n,"name")||r(n,"name",t),u(n).source=s.join("string"==typeof t?t:"")),e!==o?(l?!p&&e[t]&&(d=!0):delete e[t],d?e[t]=n:r(e,t,n)):d?e[t]=n:i(t,n)})(Function.prototype,"toString",(function(){return"function"==typeof this&&d(this).source||c(this)}))},function(e,t,n){"use strict";var o=n(9),r=n(5),a=n(19),i=Object.defineProperty,c={},l=function(e){throw e};e.exports=function(e,t){if(a(c,e))return c[e];t||(t={});var n=[][e],d=!!a(t,"ACCESSORS")&&t.ACCESSORS,u=a(t,0)?t[0]:l,s=a(t,1)?t[1]:undefined;return c[e]=!!n&&!r((function(){if(d&&!o)return!0;var e={length:-1};d?i(e,1,{enumerable:!0,get:l}):e[1]=1,n.call(e,u,s)}))}},function(e,t,n){"use strict";function o(e,t,n,o,r,a,i){try{var c=e[a](i),l=c.value}catch(d){return void n(d)}c.done?t(l):Promise.resolve(l).then(o,r)}t.__esModule=!0,t.winset=t.winget=t.runCommand=t.callByondAsync=t.callByond=t.IS_IE8=void 0;var r=window.Byond,a=function(){var e=navigator.userAgent.match(/Trident\/(\d+).+?;/i);if(!e)return null;var t=e[1];return t?parseInt(t,10):null}(),i=null!==a&&a<=6;t.IS_IE8=i;var c=function(e,t){void 0===t&&(t={}),r.call(e,t)};t.callByond=c;var l=function(e,t){void 0===t&&(t={}),window.__callbacks__=window.__callbacks__||[];var n=window.__callbacks__.length,o=new Promise((function(e){window.__callbacks__.push(e)}));return r.call(e,Object.assign(Object.assign({},t),{},{callback:"__callbacks__["+n+"]"})),o};t.callByondAsync=l;t.runCommand=function(e){return c("winset",{command:e})};var d=function(){var e,t=(e=regeneratorRuntime.mark((function n(e,t){var o;return regeneratorRuntime.wrap((function(n){for(;;)switch(n.prev=n.next){case 0:return n.next=2,l("winget",{id:e,property:t});case 2:return o=n.sent,n.abrupt("return",o[t]);case 4:case"end":return n.stop()}}),n)})),function(){var t=this,n=arguments;return new Promise((function(r,a){var i=e.apply(t,n);function c(e){o(i,r,a,c,l,"next",e)}function l(e){o(i,r,a,c,l,"throw",e)}c(undefined)}))});return function(e,n){return t.apply(this,arguments)}}();t.winget=d;t.winset=function(e,t,n){var o;return c("winset",((o={})[e+"."+t]=n,o))}},function(e,t,n){"use strict";var o=n(62),r=n(23);e.exports=function(e){return o(r(e))}},function(e,t,n){"use strict";var o=n(132),r=n(19),a=n(138),i=n(14).f;e.exports=function(e){var t=o.Symbol||(o.Symbol={});r(t,e)||i(t,e,{value:a.f(e)})}},function(e,t,n){"use strict";var o=n(23),r=/"/g;e.exports=function(e,t,n,a){var i=String(o(e)),c="<"+t;return""!==n&&(c+=" "+n+'="'+String(a).replace(r,""")+'"'),c+">"+i+""+t+">"}},function(e,t,n){"use strict";var o=n(5);e.exports=function(e){return o((function(){var t=""[e]('"');return t!==t.toLowerCase()||t.split('"').length>3}))}},function(e,t,n){"use strict";var o=n(9),r=n(14),a=n(51);e.exports=o?function(e,t,n){return r.f(e,t,a(1,n))}:function(e,t,n){return e[t]=n,e}},function(e,t,n){"use strict";var o=Math.ceil,r=Math.floor;e.exports=function(e){return isNaN(e=+e)?0:(e>0?r:o)(e)}},function(e,t,n){"use strict";e.exports=function(e){if("function"!=typeof e)throw TypeError(String(e)+" is not a function");return e}},function(e,t,n){"use strict";function o(e){var t=0;if("undefined"==typeof Symbol||null==e[Symbol.iterator]){if(Array.isArray(e)||(e=function(e,t){if(!e)return;if("string"==typeof e)return r(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 r(e,t)}(e)))return function(){return t>=e.length?{done:!0}:{done:!1,value:e[t++]}};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(t=e[Symbol.iterator]()).next.bind(t)}function r(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,o=new Array(t);n0;for(var c in i&&(a=Ce(n))&&he(t,o,n),n)_e(c,null,n[c],o,r,a,null);i&&fe(t,e,o,n,!0,a)}function Be(e,t,n){var o=O(e.render(t,e.state,n)),r=n;return c(e.getChildContext)&&(r=u(n,e.getChildContext())),e.$CX=r,o}function Le(e,t,n,o,r,a){var i=new t(n,o),l=i.$N=Boolean(t.getDerivedStateFromProps||i.getSnapshotBeforeUpdate);if(i.$SVG=r,i.$L=a,e.children=i,i.$BS=!1,i.context=o,i.props===p&&(i.props=n),l)i.state=y(i,n,i.state);else if(c(i.componentWillMount)){i.$BR=!0,i.componentWillMount();var u=i.$PS;if(!d(u)){var s=i.state;if(d(s))i.state=u;else for(var m in u)s[m]=u[m];i.$PS=null}i.$BR=!1}return i.$LI=Be(i,n,o),i}function Se(e,t,n,o,r,a){var i=e.flags|=16384;481&i?Te(e,t,n,o,r,a):4&i?function(e,t,n,o,r,a){var i=Le(e,e.type,e.props||p,n,o,a);Se(i.$LI,t,i.$CX,o,r,a),Ee(e.ref,i,a)}(e,t,n,o,r,a):8&i?(!function(e,t,n,o,r,a){Se(e.children=O(function(e,t){return 32768&e.flags?e.type.render(e.props||p,e.ref,t):e.type(e.props||p,t)}(e,n)),t,n,o,r,a)}(e,t,n,o,r,a),Pe(e,a)):512&i||16&i?Ie(e,t,r):8192&i?function(e,t,n,o,r,a){var i=e.children,c=e.childFlags;12&c&&0===i.length&&(c=e.childFlags=2,i=e.children=P());2===c?Se(i,n,r,o,r,a):Ae(i,n,t,o,r,a)}(e,n,t,o,r,a):1024&i&&function(e,t,n,o,r){Se(e.children,e.ref,t,!1,null,r);var a=P();Ie(a,n,o),e.dom=a.dom}(e,n,t,r,a)}function Ie(e,t,n){var o=e.dom=document.createTextNode(e.children);d(t)||h(t,o,n)}function Te(e,t,n,o,r,i){var c=e.flags,l=e.props,u=e.className,s=e.children,p=e.childFlags,m=e.dom=function(e,t){return t?document.createElementNS("http://www.w3.org/2000/svg",e):document.createElement(e)}(e.type,o=o||(32&c)>0);if(a(u)||""===u||(o?m.setAttribute("class",u):m.className=u),16===p)_(m,s);else if(1!==p){var f=o&&"foreignObject"!==e.type;2===p?(16384&s.flags&&(e.children=s=E(s)),Se(s,m,n,f,null,i)):8!==p&&4!==p||Ae(s,m,n,f,null,i)}d(t)||h(t,m,r),d(l)||we(e,c,l,m,o),be(e.ref,m,i)}function Ae(e,t,n,o,r,a){for(var i=0;ii)for(p=s;p=0;--r){var a=this.tryEntries[r],i=a.completion;if("root"===a.tryLoc)return o("end");if(a.tryLoc<=this.prev){var c=n.call(a,"catchLoc"),l=n.call(a,"finallyLoc");if(c&&l){if(this.prev'+(n?e:H(e,!0))+"\n":"
\n"},t.blockquote=function(e){return""+(n?e:H(e,!0))+"\n"+e+"
\n"},t.html=function(e){return e},t.heading=function(e,t,n,o){return this.options.headerIds?"
\n":"
\n"},t.list=function(e,t,n){var o=t?"ol":"ul";return"<"+o+(t&&1!==n?' start="'+n+'"':"")+">\n"+e+""+o+">\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=W(this.options.sanitize,this.options.baseUrl,e)))return n;var o='"+n+""},t.image=function(e,t,n){if(null===(e=W(this.options.sanitize,this.options.baseUrl,e)))return n;var o='":">"},t.text=function(e){return e},e}(),G=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}(),K=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}(),q=o.defaults,$=V.unescape,Y=function(){function e(e){this.options=e||q,this.options.renderer=this.options.renderer||new U,this.renderer=this.options.renderer,this.renderer.options=this.options,this.textRenderer=new G,this.slugger=new K}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,o,r,a,i,c,l,d,u,s,p,m,f,h,C,g,b,N,v="",V=e.length;for(n=0;n
"+Z(l.message+"",!0)+"";throw l}}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,o=function(t){var o=n[t];n[t]=function(){for(var r=arguments.length,a=new Array(r),i=0;i
'+(n?e:H(e,!0))+"\n":""+(n?e:H(e,!0))+"\n"},t.blockquote=function(e){return"\n"+e+"\n"},t.html=function(e){return e},t.heading=function(e,t,n,o){return this.options.headerIds?"
"+e+"
\n"},t.table=function(e,t){return t&&(t=""+t+""),""+e+""},t.br=function(){return this.options.xhtml?""+Z(l.message+"",!0)+"";throw l}}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,o=function(t){var o=n[t];n[t]=function(){for(var r=arguments.length,a=new Array(r),i=0;i