local H=game:GetService("\084\101\120\116\083\101\114\118\105\099\101")local A={}A.__index=A A.ClassName="\077\097\114\107\100\111\119\110\082\101\110\100\101\114"A.SpaceAfterParagraph=10 A.SpaceAfterHeader=5 A.SpaceBetweenList=2 A.TextSize=18 A.Indent=30 A.TextColor3=Color3.fromRGB(56,56,56)A.MaxHeaderLevel=3 function A.new(H,Q)local u=setmetatable({},A)u._gui=H or error("\078\111 \071\117\105")u._width=Q or error("\078\111 \119\105\100\116\104")return u end function A.WithOptions(A,H)A.TextSize=H.TextSize A.SpaceAfterParagraph=H.SpaceAfterParagraph return A end function A.Render(A,H)local Q=0 for u,T in pairs(H)do local V if type(T)=="\115\116\114\105\110\103"then V=A:_renderParagraph(T)V.Position=UDim2.new(V.Position.X,UDim.new(0,Q))Q=Q+V.Size.Y.Offset if u~=#H then Q=Q+A.SpaceAfterParagraph end elseif type(T)=="\116\097\098\108\101"then if T.Type=="\076\105\115\116"then V=A:_renderList(T)V.Position=UDim2.new(V.Position.X,UDim.new(0,Q))Q=Q+V.Size.Y.Offset local K=type(H[u+1])=="\116\097\098\108\101"and(H[u+1].Type=="\076\105\115\116"and H[u+1].Level~=T.Level)if u~=#H then if K then Q=Q+A.SpaceBetweenList else Q=Q+A.SpaceAfterParagraph end end elseif T.Type=="\072\101\097\100\101\114"then V=A:_renderHeader(T)V.Position=UDim2.new(V.Position.X,UDim.new(0,Q))Q=Q+V.Size.Y.Offset if u~=#H then Q=Q+A.SpaceAfterHeader end else error(("\066\097\100 \100\097\116\097 \116\121\112\101 \'\037\115\'"):format(tostring(T.Type)))end else error("\066\097\100 \100\097\116\097 \116\121\112\101")end if V then V.Name=("\037\100\095\037\115"):format(u,V.Name)end end A._gui.Size=UDim2.new(A._gui.Size.X,UDim.new(0,Q))end function A._getFrame(H)local A=Instance.new("\070\114\097\109\101")A.BackgroundTransparency=1 A.BorderSizePixel=0 A.Size=UDim2.new(1,0,0,0)A.ZIndex=H._gui.ZIndex return A end function A._getTextLabel(H)local A=Instance.new("\084\101\120\116\076\097\098\101\108")A.BackgroundTransparency=1 A.Size=UDim2.new(1,0,0,0)A.ZIndex=H._gui.ZIndex return H:_formatTextLabel(A)end function A._formatTextLabel(A,H)H.Font=Enum.Font.SourceSans H.TextColor3=A.TextColor3 H.TextXAlignment=Enum.TextXAlignment.Left H.TextYAlignment=Enum.TextYAlignment.Top H.TextWrapped=true H.TextSize=A.TextSize H.TextStrokeTransparency=1 return H end function A._renderParagraphLabel(u,A,Q)local T=A.Size.X.Scale*u._width+A.Size.X.Offset local V=Q:gsub("\040\037\112\043\041\036","")local K=H:GetTextSize(V,A.TextSize,A.Font,Vector2.new(T,A.TextSize*20))A.Size=UDim2.new(A.Size.X,UDim.new(0,K.Y))A.Text=Q return A end function A._renderParagraph(Q,H,A)A=A or{}local u=Q:_getTextLabel()u.Text=H u.Name="\080\097\114\097\103\114\097\112\104"u.Parent=A.Parent or Q._gui Q:_renderParagraphLabel(u,H)return u end function A._getBullet(A,H)local Q=Instance.new("\070\114\097\109\101")Q.Name="\098\117\108\108\101\116"Q.BorderSizePixel=0 Q.BackgroundColor3=A.TextColor3 Q.ZIndex=A._gui.ZIndex if H==2 then Q.Size=UDim2.new(0,6,0,1)else Q.Size=UDim2.new(0,4,0,4)end return Q end function A._renderList(A,H)assert(type(H.Level)=="\110\117\109\098\101\114"and H.Level>0)local Q=A:_getFrame()Q.Name=("\076\105\115\116\095\037\100"):format(H.Level)Q.Size=UDim2.new(1,-H.Level*A.Indent,0,0)Q.Position=UDim2.new(0,-Q.Size.X.Offset,0,0)Q.Parent=A._gui local u=0 for T,V in ipairs(H)do local K=A:_renderParagraph(V,{Parent=Q})K.Name=("\037\100\095\037\115"):format(T,K.Name)K.Position=UDim2.new(K.Position.X,UDim.new(0,u))local x=A:_getBullet(H.Level)x.AnchorPoint=Vector2.new(.5,.5)x.Position=UDim2.new(0,-A.Indent/2,0,A.TextSize/2+1)x.Parent=K u=u+K.Size.Y.Offset if T~=#H then u=u+A.SpaceBetweenList end end Q.Size=UDim2.new(Q.Size.X,UDim.new(0,u))return Q end function A._renderHeader(A,H)local Q=A:_getTextLabel()Q.Name="\072\101\097\100\101\114"..H.Level Q.TextSize=A.TextSize+(A.MaxHeaderLevel-H.Level)Q.TextYAlignment=Enum.TextYAlignment.Center Q.Parent=A._gui Q.Font=Enum.Font.SourceSansSemibold A:_renderParagraphLabel(Q,H.Text)Q.Size=UDim2.new(Q.Size.X,UDim.new(Q.Size.Y.Scale,Q.Size.Y.Offset+6))local u=A:_getFrame()u.Name="\085\110\100\101\114\108\105\110\101"u.BackgroundTransparency=0 u.BackgroundColor3=Color3.new(.9,.9,.9)u.Size=UDim2.new(1,0,0,1)u.AnchorPoint=Vector2.new(0,1)u.Position=UDim2.new(0,0,1,0)u.Parent=Q return Q end return A
--- Renders the markdown
-- See: MarkdownParser
-- @classmod MarkdownRender

local TextService = game:GetService("TextService")

local MarkdownRender = {}
MarkdownRender.__index = MarkdownRender
MarkdownRender.ClassName = "MarkdownRender"
MarkdownRender.SpaceAfterParagraph = 10
MarkdownRender.SpaceAfterHeader = 5
MarkdownRender.SpaceBetweenList = 2
MarkdownRender.TextSize = 18
MarkdownRender.Indent = 30
MarkdownRender.TextColor3 = Color3.fromRGB(56, 56, 56)
MarkdownRender.MaxHeaderLevel = 3 -- h5 is the largest

--- Creates a new markdown render
-- @tparam GuiObject gui
-- @tparam number width Width to render at
function MarkdownRender.new(gui, width)
	local self = setmetatable({}, MarkdownRender)

	self._gui = gui or error("No Gui")
	self._width = width or error("No width")

	return self
end

function MarkdownRender:WithOptions(options)
	self.TextSize = options.TextSize
	self.SpaceAfterParagraph = options.SpaceAfterParagraph

	return self
end
--- Renders the data in the given Gui
-- @param data Data from MarkdownParser
function MarkdownRender:Render(data)
	local height = 0
	for index, item in pairs(data) do
		local gui
		if type(item) == "string" then
			gui = self:_renderParagraph(item)
			gui.Position = UDim2.new(gui.Position.X, UDim.new(0, height))
			height = height + gui.Size.Y.Offset

			if index ~= #data then
				height = height + self.SpaceAfterParagraph
			end
		elseif type(item) == "table" then
			if item.Type == "List" then
				gui = self:_renderList(item)
				gui.Position = UDim2.new(gui.Position.X, UDim.new(0, height))
				height = height + gui.Size.Y.Offset

				local nextIsNestedList = (type(data[index+1]) == "table" and data[index+1].Type == "List" and data[index+1].Level ~= item.Level)
				if index ~= #data  then
					if nextIsNestedList then
						height = height + self.SpaceBetweenList
					else
						height = height + self.SpaceAfterParagraph
					end
				end
			elseif item.Type == "Header" then
				gui = self:_renderHeader(item)
				gui.Position = UDim2.new(gui.Position.X, UDim.new(0, height))
				height = height + gui.Size.Y.Offset

				if index ~= #data then
					height = height + self.SpaceAfterHeader
				end
			else
				error(("Bad data type '%s'"):format(tostring(item.Type)))
			end
		else
			error("Bad data type")
		end

		if gui then
			gui.Name = ("%d_%s"):format(index, gui.Name)
		end
	end

	self._gui.Size = UDim2.new(self._gui.Size.X, UDim.new(0, height))
end

function MarkdownRender:_getFrame()
	local frame = Instance.new("Frame")
	frame.BackgroundTransparency = 1
	frame.BorderSizePixel = 0
	frame.Size = UDim2.new(1, 0, 0, 0)
	frame.ZIndex = self._gui.ZIndex

	return frame
end

function MarkdownRender:_getTextLabel()
	local textLabel = Instance.new("TextLabel")
	textLabel.BackgroundTransparency = 1
	textLabel.Size = UDim2.new(1, 0, 0, 0)
	textLabel.ZIndex = self._gui.ZIndex

	return self:_formatTextLabel(textLabel)
end

function MarkdownRender:_formatTextLabel(textLabel)
	textLabel.Font = Enum.Font.SourceSans
	textLabel.TextColor3 = self.TextColor3
	textLabel.TextXAlignment = Enum.TextXAlignment.Left
	textLabel.TextYAlignment = Enum.TextYAlignment.Top
	textLabel.TextWrapped = true
	textLabel.TextSize = self.TextSize
	textLabel.TextStrokeTransparency = 1

	return textLabel
end

--- Strip ending punctuation which screws with roblox's wordwrapping and .TextFits
function MarkdownRender:_renderParagraphLabel(label, text)
	local labelWidth = label.Size.X.Scale*self._width + label.Size.X.Offset

	local strippedText = text:gsub("(%p+)$", "")
	local textSize = TextService:GetTextSize(strippedText, label.TextSize, label.Font,
		Vector2.new(labelWidth, label.TextSize*20))

	label.Size = UDim2.new(label.Size.X, UDim.new(0, textSize.Y))
	label.Text = text

	return label
end

function MarkdownRender:_renderParagraph(text, options)
	options = options or {}

	local label = self:_getTextLabel()
	label.Text = text
	label.Name = "Paragraph"
	label.Parent = options.Parent or self._gui

	self:_renderParagraphLabel(label, text)

	return label
end

function MarkdownRender:_getBullet(level)
	local bullet = Instance.new("Frame")
	bullet.Name = "bullet"
	bullet.BorderSizePixel = 0

	bullet.BackgroundColor3 = self.TextColor3
	bullet.ZIndex = self._gui.ZIndex

	if level == 2 then
		bullet.Size = UDim2.new(0, 6, 0, 1)
	else
		bullet.Size = UDim2.new(0, 4, 0, 4)
	end

	return bullet
end

function MarkdownRender:_renderList(listData)
	assert(type(listData.Level) == "number" and listData.Level > 0)

	local frame = self:_getFrame()
	frame.Name = ("List_%d"):format(listData.Level)
	frame.Size = UDim2.new(1, -(listData.Level)*self.Indent, 0, 0)
	frame.Position = UDim2.new(0, -frame.Size.X.Offset, 0, 0)
	frame.Parent = self._gui

	local height = 0
	for index, text in ipairs(listData) do
		local textLabel = self:_renderParagraph(text, { Parent = frame })
		textLabel.Name = ("%d_%s"):format(index, textLabel.Name)
		textLabel.Position = UDim2.new(textLabel.Position.X, UDim.new(0, height))

		local bullet = self:_getBullet(listData.Level)
		bullet.AnchorPoint = Vector2.new(0.5, 0.5)
		bullet.Position = UDim2.new(0, -self.Indent/2, 0, self.TextSize/2 + 1)
		bullet.Parent = textLabel

		height = height + textLabel.Size.Y.Offset
		if index ~= #listData then
			height = height + self.SpaceBetweenList
		end
	end

	frame.Size = UDim2.new(frame.Size.X, UDim.new(0, height))

	return frame
end

function MarkdownRender:_renderHeader(headerData)
	local label = self:_getTextLabel()
	label.Name = "Header" .. headerData.Level
	label.TextSize = self.TextSize + (self.MaxHeaderLevel-headerData.Level)
	label.TextYAlignment = Enum.TextYAlignment.Center
	label.Parent = self._gui
	label.Font = Enum.Font.SourceSansSemibold

	self:_renderParagraphLabel(label, headerData.Text)
	label.Size = UDim2.new(label.Size.X, UDim.new(label.Size.Y.Scale, label.Size.Y.Offset + 6)) -- Extra padding

	local underline = self:_getFrame()
	underline.Name = "Underline"
	underline.BackgroundTransparency = 0
	underline.BackgroundColor3 = Color3.new(0.9, 0.9, 0.9)
	underline.Size = UDim2.new(1, 0, 0, 1)
	underline.AnchorPoint = Vector2.new(0, 1)
	underline.Position = UDim2.new(0, 0, 1, 0)
	underline.Parent = label

	return label
end

return MarkdownRender