local d do local D,P=pcall(function()return(UserSettings()):IsUserFeatureEnabled("\085\115\101\114\082\101\109\111\118\101\077\101\115\115\097\103\101\079\110\084\101\120\116\070\105\108\116\101\114\070\097\105\108\117\114\101\115")end)d=D and P end local D=false do local d,P=pcall(function()return(UserSettings()):IsUserFeatureEnabled("\085\115\101\114\073\115\067\104\097\116\084\114\097\110\115\108\097\116\105\111\110\069\110\097\098\108\101\100\050")end)D=d and P end local P={}P.ScrollBarThickness=4 local q=script.Parent.Parent.Parent local B=game:GetService("\067\104\097\116")local y=q:WaitForChild("\067\108\105\101\110\116\067\104\097\116\077\111\100\117\108\101\115")local b=script.Parent local R=require(b:WaitForChild("\077\101\115\115\097\103\101\076\097\098\101\108\067\114\101\097\116\111\114"))local m=require(b:WaitForChild("\067\117\114\118\101\085\116\105\108"))local l=require(y:WaitForChild("\067\104\097\116\083\101\116\116\105\110\103\115"))local S=R.new()local r={}r.__index=r local function h()local d=Instance.new("\070\114\097\109\101")d.Selectable=false d.Size=UDim2.new(1,0,1,0)d.BackgroundTransparency=1 local D=Instance.new("\083\099\114\111\108\108\105\110\103\070\114\097\109\101")D.Selectable=l.GamepadNavigationEnabled D.Name="\083\099\114\111\108\108\101\114"D.BackgroundTransparency=1 D.BorderSizePixel=0 D.Position=UDim2.new(0,0,0,3)D.Size=UDim2.new(1,-4,1,-6)D.CanvasSize=UDim2.new(0,0,0,0)D.ScrollBarThickness=P.ScrollBarThickness D.Active=true D.Parent=d local q=Instance.new("\085\073\076\105\115\116\076\097\121\111\117\116")q.SortOrder=Enum.SortOrder.LayoutOrder q.Parent=D return d,D,q end function r.Destroy(d)d.GuiObject:Destroy()d.Destroyed=true end function r.SetActive(D,d)D.GuiObject.Visible=d end function r.UpdateMessageFiltered(P,D)local q=nil local B=1 local y=P.MessageObjectLog while#y>=B do local d=y[B]if d.ID==D.ID then q=d break end B=B+1 end if q then if d then if D.Message==""then P:RemoveMessageAtIndex(B)else q.UpdateTextFunction(D)P:PositionMessageLabelInWindow(q,B)end else q.UpdateTextFunction(D)P:PositionMessageLabelInWindow(q,B)end end end function r.RefreshMessagePlacement(D,d)local P=nil local q=1 local B=D.MessageObjectLog while#B>=q do local D=B[q]if D.ID==d.ID then P=D break end q=q+1 end if P then D:PositionMessageLabelInWindow(P,q)end end function r.AddMessage(P,d)P:WaitUntilParentedCorrectly()local q if D then local D=function()P:RefreshMessagePlacement(d)end q=S:CreateMessageLabel_Chat(d,P.CurrentChannelName,D)else q=S:CreateMessageLabel(d,P.CurrentChannelName)end if q==nil then return end table.insert(P.MessageObjectLog,q)P:PositionMessageLabelInWindow(q,#P.MessageObjectLog)end function r.RemoveMessageAtIndex(D,d)D:WaitUntilParentedCorrectly()local P=D.MessageObjectLog[d]if P then P:Destroy()table.remove(D.MessageObjectLog,d)end end function r.AddMessageAtIndex(q,d,P)local B if D then local D=function()q:RefreshMessagePlacement(d)end B=S:CreateMessageLabel_Chat(d,q.CurrentChannelName,D)else B=S:CreateMessageLabel(d,q.CurrentChannelName)end if B==nil then return end table.insert(q.MessageObjectLog,P,B)q:PositionMessageLabelInWindow(B,P)end function r.RemoveLastMessage(d)d:WaitUntilParentedCorrectly()local D=d.MessageObjectLog[1]D:Destroy()table.remove(d.MessageObjectLog,1)end function r.IsScrolledDown(d)local D=d.Scroller.CanvasSize.Y.Offset local P=d.Scroller.AbsoluteWindowSize.Y local q=d.Scroller.CanvasPosition.Y return D<P or D-q<=P+5 end function r.UpdateMessageTextHeight(D,d)local P=d.BaseFrame for q=1,10,1 do if d.BaseMessage.TextFits then break end local B=D.Scroller.AbsoluteSize.X-q P.Size=UDim2.new(1,0,0,d.GetHeightFunction(B))end end function r.PositionMessageLabelInWindow(P,d,D)P:WaitUntilParentedCorrectly()local q=P:IsScrolledDown()local B=d.BaseFrame local y=1 if P.MessageObjectLog[D-1]then if D==#P.MessageObjectLog then y=P.MessageObjectLog[D-1].BaseFrame.LayoutOrder+1 else y=P.MessageObjectLog[D-1].BaseFrame.LayoutOrder end end B.LayoutOrder=y B.Size=UDim2.new(1,0,0,d.GetHeightFunction(P.Scroller.AbsoluteSize.X))B.Parent=P.Scroller if d.BaseMessage then P:UpdateMessageTextHeight(d)end if q then P.Scroller.CanvasPosition=Vector2.new(0,math.max(0,P.Scroller.CanvasSize.Y.Offset-P.Scroller.AbsoluteSize.Y))end end function r.ReorderAllMessages(d)d:WaitUntilParentedCorrectly()if d.GuiObject.AbsoluteSize.Y<1 then return end local D=d.Scroller.CanvasPosition local P=d:IsScrolledDown()for D,P in pairs(d.MessageObjectLog)do d:UpdateMessageTextHeight(P)end if not P then d.Scroller.CanvasPosition=D else d.Scroller.CanvasPosition=Vector2.new(0,math.max(0,d.Scroller.CanvasSize.Y.Offset-d.Scroller.AbsoluteSize.Y))end end function r.Clear(d)for d,D in pairs(d.MessageObjectLog)do D:Destroy()end d.MessageObjectLog={}end function r.SetCurrentChannelName(D,d)D.CurrentChannelName=d end function r.FadeOutBackground(D,d) end function r.FadeInBackground(D,d) end function r.FadeOutText(D,d)for P=1,#D.MessageObjectLog,1 do if D.MessageObjectLog[P].FadeOutFunction then D.MessageObjectLog[P].FadeOutFunction(d,m)end end end function r.FadeInText(D,d)for P=1,#D.MessageObjectLog,1 do if D.MessageObjectLog[P].FadeInFunction then D.MessageObjectLog[P].FadeInFunction(d,m)end end end function r.Update(D,d)for P=1,#D.MessageObjectLog,1 do if D.MessageObjectLog[P].UpdateAnimFunction then D.MessageObjectLog[P].UpdateAnimFunction(d,m)end end end function r.WaitUntilParentedCorrectly(d)while not d.GuiObject:IsDescendantOf((game:GetService("\080\108\097\121\101\114\115")).LocalPlayer)do d.GuiObject.AncestryChanged:wait()end end function P.new()local d=setmetatable({},r)d.Destroyed=false local D,P,q=h()d.GuiObject=D d.Scroller=P d.Layout=q d.MessageObjectLog={}d.Name="\077\101\115\115\097\103\101\076\111\103\068\105\115\112\108\097\121"d.GuiObject.Name="\070\114\097\109\101\095"..d.Name d.CurrentChannelName="";(d.GuiObject:GetPropertyChangedSignal("\065\098\115\111\108\117\116\101\083\105\122\101")):Connect(function()spawn(function()d:ReorderAllMessages()end)end)local B=true;(d.Layout:GetPropertyChangedSignal("\065\098\115\111\108\117\116\101\067\111\110\116\101\110\116\083\105\122\101")):Connect(function()local D=d.Layout.AbsoluteContentSize d.Scroller.CanvasSize=UDim2.new(0,0,0,D.Y)if B then local P=d.Scroller.AbsoluteWindowSize d.Scroller.CanvasPosition=Vector2.new(0,D.Y-P.Y)end end);(d.Scroller:GetPropertyChangedSignal("\067\097\110\118\097\115\080\111\115\105\116\105\111\110")):Connect(function()B=d:IsScrolledDown()end)return d end return P
--!nonstrict
--	// FileName: MessageLogDisplay.lua
--	// Written by: Xsitsu, TheGamer101
--	// Description: ChatChannel window for displaying messages.

local UserFlagRemoveMessageOnTextFilterFailures
do
	local success, value = pcall(function()
		return UserSettings():IsUserFeatureEnabled("UserRemoveMessageOnTextFilterFailures")
	end)
	UserFlagRemoveMessageOnTextFilterFailures = success and value
end

local userIsChatTranslationEnabled = false
do
	local success, value = pcall(function()
		return UserSettings():IsUserFeatureEnabled("UserIsChatTranslationEnabled2")
	end)
	userIsChatTranslationEnabled = success and value
end

local module = {}
module.ScrollBarThickness = 4

--////////////////////////////// Include
--//////////////////////////////////////
local chatFolder = script.Parent.Parent.Parent

local Chat = game:GetService("Chat")
local clientChatModules = chatFolder:WaitForChild("ClientChatModules")
local modulesFolder = script.Parent
local moduleMessageLabelCreator = require(modulesFolder:WaitForChild("MessageLabelCreator"))
local CurveUtil = require(modulesFolder:WaitForChild("CurveUtil"))

local ChatSettings = require(clientChatModules:WaitForChild("ChatSettings"))

local MessageLabelCreator = moduleMessageLabelCreator.new()

--////////////////////////////// Methods
--//////////////////////////////////////
local methods = {}
methods.__index = methods

local function CreateGuiObjects()
	local BaseFrame = Instance.new("Frame")
	BaseFrame.Selectable = false
	BaseFrame.Size = UDim2.new(1, 0, 1, 0)
	BaseFrame.BackgroundTransparency = 1

	local Scroller = Instance.new("ScrollingFrame")
	Scroller.Selectable = ChatSettings.GamepadNavigationEnabled
	Scroller.Name = "Scroller"
	Scroller.BackgroundTransparency = 1
	Scroller.BorderSizePixel = 0
	Scroller.Position = UDim2.new(0, 0, 0, 3)
	Scroller.Size = UDim2.new(1, -4, 1, -6)
	Scroller.CanvasSize = UDim2.new(0, 0, 0, 0)
	Scroller.ScrollBarThickness = module.ScrollBarThickness
	Scroller.Active = true
	Scroller.Parent = BaseFrame

	local Layout = Instance.new("UIListLayout")
	Layout.SortOrder = Enum.SortOrder.LayoutOrder
	Layout.Parent = Scroller

	return BaseFrame, Scroller, Layout
end

function methods:Destroy()
	self.GuiObject:Destroy()
	self.Destroyed = true
end

function methods:SetActive(active)
	self.GuiObject.Visible = active
end

function methods:UpdateMessageFiltered(messageData)
	local messageObject = nil
	local searchIndex = 1
	local searchTable = self.MessageObjectLog

	while #searchTable >= searchIndex do
		local obj = searchTable[searchIndex]

		if obj.ID == messageData.ID then
			messageObject = obj
			break
		end

		searchIndex = searchIndex + 1
	end

	if messageObject then
		if UserFlagRemoveMessageOnTextFilterFailures then
			if messageData.Message == "" then
				self:RemoveMessageAtIndex(searchIndex)
			else
				messageObject.UpdateTextFunction(messageData)
				self:PositionMessageLabelInWindow(messageObject, searchIndex)
			end
		else
			messageObject.UpdateTextFunction(messageData)
			self:PositionMessageLabelInWindow(messageObject, searchIndex)
		end
	end
end

function methods:RefreshMessagePlacement(messageData)
	local messageObject = nil
	local searchIndex = 1
	local searchTable = self.MessageObjectLog

	while #searchTable >= searchIndex do
		local obj = searchTable[searchIndex]

		if obj.ID == messageData.ID then
			messageObject = obj
			break
		end

		searchIndex = searchIndex + 1
	end

	if messageObject then
		self:PositionMessageLabelInWindow(messageObject, searchIndex)
	end
end

function methods:AddMessage(messageData)
	self:WaitUntilParentedCorrectly()

	local messageObject
	if userIsChatTranslationEnabled then
		local refreshCallback = function()
			self:RefreshMessagePlacement(messageData)
		end

		messageObject =
			MessageLabelCreator:CreateMessageLabel_Chat(messageData, self.CurrentChannelName, refreshCallback)
	else
		messageObject = MessageLabelCreator:CreateMessageLabel(messageData, self.CurrentChannelName)
	end

	if messageObject == nil then
		return
	end

	table.insert(self.MessageObjectLog, messageObject)
	self:PositionMessageLabelInWindow(messageObject, #self.MessageObjectLog)
end

function methods:RemoveMessageAtIndex(index)
	self:WaitUntilParentedCorrectly()

	local removeThis = self.MessageObjectLog[index]

	if removeThis then
		removeThis:Destroy()
		table.remove(self.MessageObjectLog, index)
	end
end

function methods:AddMessageAtIndex(messageData, index)
	local messageObject
	if userIsChatTranslationEnabled then
		local refreshCallback = function()
			self:RefreshMessagePlacement(messageData)
		end

		messageObject =
			MessageLabelCreator:CreateMessageLabel_Chat(messageData, self.CurrentChannelName, refreshCallback)
	else
		messageObject = MessageLabelCreator:CreateMessageLabel(messageData, self.CurrentChannelName)
	end

	if messageObject == nil then
		return
	end

	table.insert(self.MessageObjectLog, index, messageObject)

	self:PositionMessageLabelInWindow(messageObject, index)
end

function methods:RemoveLastMessage()
	self:WaitUntilParentedCorrectly()

	local lastMessage = self.MessageObjectLog[1]

	lastMessage:Destroy()
	table.remove(self.MessageObjectLog, 1)
end

function methods:IsScrolledDown()
	local yCanvasSize = self.Scroller.CanvasSize.Y.Offset
	local yContainerSize = self.Scroller.AbsoluteWindowSize.Y
	local yScrolledPosition = self.Scroller.CanvasPosition.Y

	return (yCanvasSize < yContainerSize or yCanvasSize - yScrolledPosition <= yContainerSize + 5)
end

function methods:UpdateMessageTextHeight(messageObject)
	local baseFrame = messageObject.BaseFrame
	for i = 1, 10 do
		if messageObject.BaseMessage.TextFits then
			break
		end

		local trySize = self.Scroller.AbsoluteSize.X - i
		baseFrame.Size = UDim2.new(1, 0, 0, messageObject.GetHeightFunction(trySize))
	end
end

function methods:PositionMessageLabelInWindow(messageObject, index)
	self:WaitUntilParentedCorrectly()

	local wasScrolledDown = self:IsScrolledDown()

	local baseFrame = messageObject.BaseFrame

	local layoutOrder = 1
	if self.MessageObjectLog[index - 1] then
		if index == #self.MessageObjectLog then
			layoutOrder = self.MessageObjectLog[index - 1].BaseFrame.LayoutOrder + 1
		else
			layoutOrder = self.MessageObjectLog[index - 1].BaseFrame.LayoutOrder
		end
	end
	baseFrame.LayoutOrder = layoutOrder

	baseFrame.Size = UDim2.new(1, 0, 0, messageObject.GetHeightFunction(self.Scroller.AbsoluteSize.X))
	baseFrame.Parent = self.Scroller

	if messageObject.BaseMessage then
		self:UpdateMessageTextHeight(messageObject)
	end

	if wasScrolledDown then
		self.Scroller.CanvasPosition =
			Vector2.new(0, math.max(0, self.Scroller.CanvasSize.Y.Offset - self.Scroller.AbsoluteSize.Y))
	end
end

function methods:ReorderAllMessages()
	self:WaitUntilParentedCorrectly()

	--// Reordering / reparenting with a size less than 1 causes weird glitches to happen
	-- with scrolling as repositioning happens.
	if self.GuiObject.AbsoluteSize.Y < 1 then
		return
	end

	local oldCanvasPositon = self.Scroller.CanvasPosition
	local wasScrolledDown = self:IsScrolledDown()

	for _, messageObject in pairs(self.MessageObjectLog) do
		self:UpdateMessageTextHeight(messageObject)
	end

	if not wasScrolledDown then
		self.Scroller.CanvasPosition = oldCanvasPositon
	else
		self.Scroller.CanvasPosition =
			Vector2.new(0, math.max(0, self.Scroller.CanvasSize.Y.Offset - self.Scroller.AbsoluteSize.Y))
	end
end

function methods:Clear()
	for _, v in pairs(self.MessageObjectLog) do
		v:Destroy()
	end
	self.MessageObjectLog = {}
end

function methods:SetCurrentChannelName(name)
	self.CurrentChannelName = name
end

function methods:FadeOutBackground(duration)
	--// Do nothing
end

function methods:FadeInBackground(duration)
	--// Do nothing
end

function methods:FadeOutText(duration)
	for i = 1, #self.MessageObjectLog do
		if self.MessageObjectLog[i].FadeOutFunction then
			self.MessageObjectLog[i].FadeOutFunction(duration, CurveUtil)
		end
	end
end

function methods:FadeInText(duration)
	for i = 1, #self.MessageObjectLog do
		if self.MessageObjectLog[i].FadeInFunction then
			self.MessageObjectLog[i].FadeInFunction(duration, CurveUtil)
		end
	end
end

function methods:Update(dtScale)
	for i = 1, #self.MessageObjectLog do
		if self.MessageObjectLog[i].UpdateAnimFunction then
			self.MessageObjectLog[i].UpdateAnimFunction(dtScale, CurveUtil)
		end
	end
end

--// ToDo: Move to common modules
function methods:WaitUntilParentedCorrectly()
	while not self.GuiObject:IsDescendantOf(game:GetService("Players").LocalPlayer) do
		self.GuiObject.AncestryChanged:wait()
	end
end

--///////////////////////// Constructors
--//////////////////////////////////////

function module.new()
	local obj = setmetatable({}, methods)
	obj.Destroyed = false

	local BaseFrame, Scroller, Layout = CreateGuiObjects()
	obj.GuiObject = BaseFrame
	obj.Scroller = Scroller
	obj.Layout = Layout

	obj.MessageObjectLog = {}

	obj.Name = "MessageLogDisplay"
	obj.GuiObject.Name = "Frame_" .. obj.Name

	obj.CurrentChannelName = ""

	obj.GuiObject:GetPropertyChangedSignal("AbsoluteSize"):Connect(function()
		spawn(function()
			obj:ReorderAllMessages()
		end)
	end)

	local wasScrolledDown = true

	obj.Layout:GetPropertyChangedSignal("AbsoluteContentSize"):Connect(function()
		local size = obj.Layout.AbsoluteContentSize
		obj.Scroller.CanvasSize = UDim2.new(0, 0, 0, size.Y)
		if wasScrolledDown then
			local windowSize = obj.Scroller.AbsoluteWindowSize
			obj.Scroller.CanvasPosition = Vector2.new(0, size.Y - windowSize.Y)
		end
	end)

	obj.Scroller:GetPropertyChangedSignal("CanvasPosition"):Connect(function()
		wasScrolledDown = obj:IsScrolledDown()
	end)

	return obj
end

return module
