local E, L, V, P, G = unpack(ElvUI); --Import: Engine, Locales, PrivateDB, ProfileDB, GlobalDB local CH = E:NewModule("Chat", "AceTimer-3.0", "AceHook-3.0", "AceEvent-3.0"); local CC = E:GetModule("ClassCache"); local LSM = LibStub("LibSharedMedia-3.0"); --Cache global variables --Lua functions local _G = _G local time, difftime = time, difftime local pairs, unpack, select, tostring, next, tonumber, type, assert = pairs, unpack, select, tostring, next, tonumber, type, assert local tinsert, tremove, tsort, twipe, tconcat = table.insert, table.remove, table.sort, table.wipe, table.concat local strmatch = strmatch local gsub, find, match, gmatch, format, split = string.gsub, string.find, string.match, string.gmatch, string.format, string.split local strlower, strsub, strlen, strupper = strlower, strsub, strlen, strupper --WoW API / Variables local BetterDate = BetterDate local ChatEdit_SetLastTellTarget = ChatEdit_SetLastTellTarget local ChatFrameEditBox = ChatFrameEditBox local ChatFrame_ConfigEventHandler = ChatFrame_ConfigEventHandler local ChatFrame_SendTell = ChatFrame_SendTell local ChatFrame_SystemEventHandler = ChatFrame_SystemEventHandler local CreateFrame = CreateFrame local FCF_GetCurrentChatFrame = FCF_GetCurrentChatFrame local FCF_SetChatWindowFontSize = FCF_SetChatWindowFontSize local FloatingChatFrame_OnEvent = FloatingChatFrame_OnEvent local GetChannelName = GetChannelName local GetDefaultLanguage = GetDefaultLanguage local GetGuildRosterMOTD = GetGuildRosterMOTD local GetMouseFocus = GetMouseFocus local GetNumRaidMembers = GetNumRaidMembers local GetTime = GetTime local IsInInstance = IsInInstance local IsMouseButtonDown = IsMouseButtonDown local IsAltKeyDown = IsAltKeyDown local IsShiftKeyDown = IsShiftKeyDown local PlaySound = PlaySound local PlaySoundFile = PlaySoundFile local ShowUIPanel, HideUIPanel = ShowUIPanel, HideUIPanel local StaticPopup_Visible = StaticPopup_Visible local ToggleFrame = ToggleFrame local UnitName = UnitName local hooksecurefunc = hooksecurefunc local NUM_CHAT_WINDOWS = NUM_CHAT_WINDOWS local DEFAULT_CHAT_FRAME = DEFAULT_CHAT_FRAME local SELECTED_DOCK_FRAME = SELECTED_DOCK_FRAME local GlobalStrings = { ["AFK"] = CHAT_MSG_AFK, ["CHAT_FILTERED"] = CHAT_FILTERED, ["CHAT_IGNORED"] = CHAT_IGNORED, ["CHAT_RESTRICTED"] = CHAT_RESTRICTED, ["CHAT_TELL_ALERT_TIME"] = CHAT_TELL_ALERT_TIME, ["DND"] = CHAT_MSG_DND, ["CHAT_MSG_RAID_WARNING"] = CHAT_MSG_RAID_WARNING } local CreatedFrames = 0 local lines = {} local msgList, msgCount, msgTime = {}, {}, {} local chatFilters = {} local PLAYER_REALM = gsub(E.myrealm,"[%s%-]","") local PLAYER_NAME = E.myname.."-"..PLAYER_REALM local RAID_CLASS_COLORS = RAID_CLASS_COLORS local CUSTOM_CLASS_COLORS = CUSTOM_CLASS_COLORS local DEFAULT_STRINGS = { BATTLEGROUND = L["BG"], GUILD = L["G"], PARTY = L["P"], RAID = L["R"], OFFICER = L["O"], BATTLEGROUND_LEADER = L["BGL"], RAID_LEADER = L["RL"], } local hyperlinkTypes = { ["item"] = true, ["spell"] = true, ["unit"] = true, ["quest"] = true, ["enchant"] = true, ["instancelock"] = true, ["talent"] = true, } CH.Keywords = {} local numScrollMessages local function ChatFrame_OnMouseScroll() SELECTED_DOCK_FRAME = this numScrollMessages = CH.db.numScrollMessages or 3 if CH.db.scrollDirection == "TOP" then if arg1 < 0 then if IsShiftKeyDown() then this:ScrollToBottom() elseif IsAltKeyDown() then this:ScrollDown() else for i = 1, numScrollMessages do this:ScrollDown() end end elseif arg1 > 0 then if IsShiftKeyDown() then this:ScrollToTop() elseif IsAltKeyDown() then this:ScrollUp() else for i = 1, numScrollMessages do this:ScrollUp() end end if CH.db.scrollDownInterval ~= 0 then if this.ScrollTimer then CH:CancelTimer(this.ScrollTimer, true) end this.ScrollTimer = CH:ScheduleTimer("ScrollToBottom", CH.db.scrollDownInterval) end end else if arg1 < 0 then if IsShiftKeyDown() then this:ScrollToBottom() else for i = 1, numScrollMessages do this:ScrollDown() end end elseif arg1 > 0 then if IsShiftKeyDown() then this:ScrollToTop() else for i = 1, numScrollMessages do this:ScrollUp() end end if CH.db.scrollDownInterval ~= 0 then if this.ScrollTimer then CH:CancelTimer(this.ScrollTimer, true) end this.ScrollTimer = CH:ScheduleTimer("ScrollToBottom", CH.db.scrollDownInterval) end end end end local function ChatFrame_AddMessageEventFilter(event, filter) assert(event and filter) if chatFilters[event] then -- Only allow a filter to be added once for index, filterFunc in next, chatFilters[event] do if filterFunc == filter then return end end else chatFilters[event] = {} end tinsert(chatFilters[event], filter) end local function ChatFrame_RemoveMessageEventFilter(event, filter) assert(event and filter) if chatFilters[event] then for index, filterFunc in next, chatFilters[event] do if filterFunc == filter then tremove(chatFilters[event], index) end end if getn(chatFilters[event]) == 0 then chatFilters[event] = nil end end end local function ChatFrame_GetMessageEventFilters(event) assert(event) return chatFilters[event] end function CH:GetGroupDistribution() local inInstance, kind = IsInInstance() if inInstance and kind == "pvp" then return "/bg " end if GetNumRaidMembers() > 0 then return "/ra " end if GetNumPartyMembers() > 0 then return "/p " end return "/s " end function CH:StyleChat(frame) local name = frame:GetName() E:FontTemplate(_G[name.."TabText"], LSM:Fetch("font", self.db.tabFont), self.db.tabFontSize, self.db.tabFontOutline) if frame.styled then return end frame:SetFrameLevel(4) local id = frame:GetID() local tab = _G[name.."Tab"] tab.isDocked = frame.isDocked for i = 1, getn(CHAT_FRAME_TEXTURES) do E:Kill(_G[name..CHAT_FRAME_TEXTURES[i]]) end E:Kill(_G[name.."ResizeTop"]) E:Kill(_G[name.."ResizeTopLeft"]) E:Kill(_G[name.."ResizeTopRight"]) E:Kill(_G[name.."ResizeBottom"]) E:Kill(_G[name.."ResizeBottomLeft"]) E:Kill(_G[name.."ResizeBottomRight"]) E:Kill(_G[name.."ResizeLeft"]) E:Kill(_G[name.."ResizeRight"]) E:Kill(_G[name.."UpButton"]) E:Kill(_G[name.."DownButton"]) E:Kill(_G[name.."BottomButton"]) E:Kill(_G[name.."TabDockRegion"]) E:Kill(_G[name.."TabLeft"]) E:Kill(_G[name.."TabMiddle"]) E:Kill(_G[name.."TabRight"]) _G[name.."Tab"]:GetHighlightTexture():SetTexture(nil) if frame.isDocked or frame:IsVisible() then tab:Show() end hooksecurefunc(tab, "SetAlpha", function(t, alpha) if alpha ~= 1 and (not t.isDocked or SELECTED_CHAT_FRAME:GetID() == t:GetID()) then UIFrameFadeRemoveFrame(t) t:SetAlpha(1) elseif alpha < 0.6 then UIFrameFadeRemoveFrame(t) t:SetAlpha(0.6) end end) tab.text = _G[name.."TabText"] tab.text:SetTextColor(unpack(E["media"].rgbvaluecolor)) hooksecurefunc(tab.text, "SetTextColor", function(self, r, g, b) local rR, gG, bB = unpack(E["media"].rgbvaluecolor) if r ~= rR or g ~= gG or b ~= bB then self:SetTextColor(rR, gG, bB) end end) if id ~= 2 then tab.text:SetPoint("LEFT", _G[name.."TabLeft"], "RIGHT", 0, -4) end tab.flash = _G[name.."TabFlash"] tab.flash:ClearAllPoints() tab.flash:SetPoint("TOPLEFT", _G[name.."TabLeft"], "TOPLEFT", -3, id == 2 and -3 or -2) tab.flash:SetPoint("BOTTOMRIGHT", _G[name.."TabRight"], "BOTTOMRIGHT", 3, id == 2 and -7 or -6) --frame:SetClampRectInsets(0, 0, 0, 0) frame:SetClampedToScreen(false) --copy chat button frame.button = CreateFrame("Button", format("CopyChatButton%d", id), frame) frame.button:EnableMouse(true) frame.button:SetAlpha(0.35) frame.button:SetWidth(20) frame.button:SetHeight(22) frame.button:SetPoint("TOPRIGHT", 0, 0) frame.button:SetFrameLevel(frame:GetFrameLevel() + 5) frame.button.tex = frame.button:CreateTexture(nil, "OVERLAY") E:SetInside(frame.button.tex) frame.button.tex:SetTexture([[Interface\AddOns\ElvUI\media\textures\copy.tga]]) frame.button:SetScript("OnMouseUp", function() if arg1 == "LeftButton" then CH:CopyChat(frame) elseif arg1 == "RightButton" and id ~= 2 then ToggleFrame(ChatMenu) end end) frame.button:SetScript("OnEnter", function() this:SetAlpha(1) end) frame.button:SetScript("OnLeave", function() if _G[this:GetParent():GetName().."TabText"]:IsShown() then this:SetAlpha(0.35) else this:SetAlpha(0) end end) CreatedFrames = id frame.styled = true end function CH:UpdateSettings() ChatFrameEditBox:SetAltArrowKeyMode(CH.db.useAltKey) end local function colorizeLine(text, r, g, b) local hexCode = E:RGBToHex(r, g, b) local hexReplacement = format("|r%s", hexCode) text = gsub(text, "|r", hexReplacement) text = format("%s%s|r", hexCode, text) return text end function CH:GetLines(...) local index = 1 wipe(lines) for i = getn(arg), 1, -1 do local region = arg[i] if region:GetObjectType() == "FontString" then local line = tostring(region:GetText()) local r, g, b = region:GetTextColor() line = colorizeLine(line, r, g, b) lines[index] = line index = index + 1 end end return index - 1 end function CH:CopyChat(frame) if not CopyChatFrame:IsShown() then local _, fontSize = GetChatWindowInfo(frame:GetID()) if fontSize < 10 then fontSize = 12 end FCF_SetChatWindowFontSize(frame, 0.01) CopyChatFrame:Show() local lineCt = self:GetLines(frame:GetRegions()) local text = tconcat(lines, " \n", 1, lineCt) FCF_SetChatWindowFontSize(frame, fontSize) CopyChatFrameEditBox:SetText(text) else CopyChatFrame:Hide() end end function CH:OnEnter() _G[this:GetName().."Text"]:Show() end function CH:OnLeave() _G[this:GetName().."Text"]:Hide() end function CH:SetupChatTabs(frame, hook) if hook and (not self.hooks or not self.hooks[frame] or not self.hooks[frame].OnEnter) then self:HookScript(frame, "OnEnter") self:HookScript(frame, "OnLeave") elseif not hook and self.hooks and self.hooks[frame] and self.hooks[frame].OnEnter then self:Unhook(frame, "OnEnter") self:Unhook(frame, "OnLeave") end if not hook then _G[frame:GetName().."Text"]:Show() if frame.owner and frame.owner.button and GetMouseFocus() ~= frame.owner.button then frame.owner.button:SetAlpha(0.35) end elseif GetMouseFocus() ~= frame then _G[frame:GetName().."Text"]:Hide() if frame.owner and frame.owner.button and GetMouseFocus() ~= frame.owner.button then frame.owner.button:SetAlpha(1) end end end function CH:UpdateAnchors() local frame = _G["ChatFrameEditBox"] if not E.db.datatexts.leftChatPanel and self.db.panelBackdrop == "HIDEBOTH" or self.db.panelBackdrop == "RIGHT" then frame:ClearAllPoints() if E.db.chat.editBoxPosition == "BELOW_CHAT" then frame:SetPoint("TOPLEFT", ChatFrame1, "BOTTOMLEFT") frame:SetPoint("BOTTOMRIGHT", ChatFrame1, "BOTTOMRIGHT", 0, -LeftChatTab:GetHeight()) else frame:SetPoint("BOTTOMLEFT", ChatFrame1, "TOPLEFT") frame:SetPoint("TOPRIGHT", ChatFrame1, "TOPRIGHT", 0, LeftChatTab:GetHeight()) end else if E.db.datatexts.leftChatPanel and E.db.chat.editBoxPosition == "BELOW_CHAT" then frame:SetAllPoints(LeftChatDataPanel) else frame:SetAllPoints(LeftChatTab) end end CH:PositionChat(true) end local function FindRightChatID() local rightChatID for i = 1, NUM_CHAT_WINDOWS do local chat = _G["ChatFrame"..i] local id = chat:GetID() if E:FramesOverlap(chat, RightChatPanel) and not E:FramesOverlap(chat, LeftChatPanel) then rightChatID = id break end end return rightChatID end function CH:UpdateChatTabs() local fadeUndockedTabs = E.db["chat"].fadeUndockedTabs local fadeTabsNoBackdrop = E.db["chat"].fadeTabsNoBackdrop for i = 1, CreatedFrames do local chat = _G[format("ChatFrame%d", i)] local tab = _G[format("ChatFrame%sTab", i)] local id = chat:GetID() local isDocked = chat.isDocked if chat:IsShown() and (id == self.RightChatWindowID) then if E.db.chat.panelBackdrop == "HIDEBOTH" or E.db.chat.panelBackdrop == "LEFT" then CH:SetupChatTabs(tab, fadeTabsNoBackdrop and true or false) else CH:SetupChatTabs(tab, false) end elseif not isDocked and chat:IsShown() then CH:SetupChatTabs(tab, fadeUndockedTabs and true or false) else if E.db.chat.panelBackdrop == "HIDEBOTH" or E.db.chat.panelBackdrop == "RIGHT" then CH:SetupChatTabs(tab, fadeTabsNoBackdrop and true or false) else CH:SetupChatTabs(tab, false) end end end end function CH:PositionChat(override) if ((not override and self.initialMove) or (not override)) then return end if not RightChatPanel or not LeftChatPanel then return end RightChatPanel:SetWidth(E.db.chat.separateSizes and E.db.chat.panelWidthRight or E.db.chat.panelWidth) RightChatPanel:SetHeight(E.db.chat.separateSizes and E.db.chat.panelHeightRight or E.db.chat.panelHeight) LeftChatPanel:SetWidth(E.db.chat.panelWidth) LeftChatPanel:SetHeight(E.db.chat.panelHeight) self.RightChatWindowID = FindRightChatID() if not self.db.lockPositions or E.private.chat.enable ~= true then return end local chat, tab, id, isDocked local fadeUndockedTabs = E.db["chat"].fadeUndockedTabs local fadeTabsNoBackdrop = E.db["chat"].fadeTabsNoBackdrop for i = 1, CreatedFrames do local BASE_OFFSET = 57 + E.Spacing*3 chat = _G[format("ChatFrame%d", i)] id = chat:GetID() tab = _G[format("ChatFrame%sTab", i)] isDocked = chat.isDocked tab.isDocked = chat.isDocked tab.owner = chat if chat:IsShown() and id == self.RightChatWindowID then chat:ClearAllPoints() if E.db.datatexts.rightChatPanel then chat:SetPoint("BOTTOMLEFT", RightChatDataPanel, "TOPLEFT", 1, 3) else BASE_OFFSET = BASE_OFFSET - 24 chat:SetPoint("BOTTOMLEFT", RightChatDataPanel, "BOTTOMLEFT", 1, 1) end if id ~= 2 then chat:SetWidth((E.db.chat.separateSizes and E.db.chat.panelWidthRight or E.db.chat.panelWidth) - 11) chat:SetHeight((E.db.chat.separateSizes and E.db.chat.panelHeightRight or E.db.chat.panelHeight) - BASE_OFFSET) else chat:SetWidth(E.db.chat.panelWidth - 11, (E.db.chat.panelHeight - BASE_OFFSET)) chat:SetHeight(E.db.chat.panelWidth - 11, (E.db.chat.panelHeight - BASE_OFFSET)) end tab:SetParent(RightChatPanel) chat:SetParent(RightChatPanel) if chat:IsMovable() then chat:SetUserPlaced(true) end if E.db.chat.panelBackdrop == "HIDEBOTH" or E.db.chat.panelBackdrop == "LEFT" then CH:SetupChatTabs(tab, fadeTabsNoBackdrop and true or false) else CH:SetupChatTabs(tab, false) end elseif not isDocked and chat:IsShown() then tab:SetParent(UIParent) chat:SetParent(UIParent) CH:SetupChatTabs(tab, fadeUndockedTabs and true or false) else if id ~= 2 then chat:ClearAllPoints() if E.db.datatexts.leftChatPanel then chat:SetPoint("BOTTOMLEFT", LeftChatToggleButton, "TOPLEFT", 1, 3) else BASE_OFFSET = BASE_OFFSET - 24 chat:SetPoint("BOTTOMLEFT", LeftChatToggleButton, "BOTTOMLEFT", 1, 1) end chat:SetWidth(E.db.chat.panelWidth - 11) chat:SetHeight((E.db.chat.panelHeight - BASE_OFFSET)) end chat:SetParent(LeftChatPanel) if i > 2 then tab:SetParent(LeftChatPanel) else tab:SetParent(LeftChatPanel) end if chat:IsMovable() then chat:SetUserPlaced(true) end if E.db.chat.panelBackdrop == "HIDEBOTH" or E.db.chat.panelBackdrop == "RIGHT" then CH:SetupChatTabs(tab, fadeTabsNoBackdrop and true or false) else CH:SetupChatTabs(tab, false) end end end self.initialMove = true end local function UpdateChatTabColor(_, r, g, b) for i = 1, CreatedFrames do _G["ChatFrame"..i.."TabText"]:SetTextColor(r, g, b) end end E["valueColorUpdateFuncs"][UpdateChatTabColor] = true function CH:ScrollToBottom() SELECTED_DOCK_FRAME:ScrollToBottom() self:CancelTimer(SELECTED_DOCK_FRAME.ScrollTimer, true) end function CH:PrintURL(url) return "|cFFFFFFFF[|Hurl:"..url.."|h"..url.."|h]|r " end function CH.FindURL(msg, ...) if not msg then return end if event and event == "CHAT_MSG_WHISPER" and CH.db.whisperSound ~= "None" and not CH.SoundPlayed then PlaySoundFile(LSM:Fetch("sound", CH.db.whisperSound), "Master") CH.SoundPlayed = true CH.SoundTimer = CH:ScheduleTimer("ThrottleSound", 1) end if not CH.db.url then msg = CH:CheckKeyword(msg) return false, msg, unpack(arg) end msg = gsub(gsub(msg, "(%S)(|c.-|H.-|h.-|h|r)", '%1 %2'), "(|c.-|H.-|h.-|h|r)(%S)", "%1 %2") -- http://example.com local newMsg, found = gsub(msg, "(%a+)://(%S+)%s?", CH:PrintURL("%1://%2")) if found > 0 then return false, CH:CheckKeyword(newMsg), unpack(arg) end -- www.example.com newMsg, found = gsub(msg, "www%.([_A-Za-z0-9-]+)%.(%S+)%s?", CH:PrintURL("www.%1.%2")) if found > 0 then return false, CH:CheckKeyword(newMsg), unpack(arg) end -- example@example.com newMsg, found = gsub(msg, "([_A-Za-z0-9-%.]+)@([_A-Za-z0-9-]+)(%.+)([_A-Za-z0-9-%.]+)%s?", CH:PrintURL("%1@%2%3%4")) if found > 0 then return false, CH:CheckKeyword(newMsg), unpack(arg) end -- IP address with port 1.1.1.1:1 newMsg, found = gsub(msg, "(%d%d?%d?)%.(%d%d?%d?)%.(%d%d?%d?)%.(%d%d?%d?)(:%d+)%s?", CH:PrintURL("%1.%2.%3.%4%5")) if found > 0 then return false, CH:CheckKeyword(newMsg), unpack(arg) end -- IP address 1.1.1.1 newMsg, found = gsub(msg, "(%d%d?%d?)%.(%d%d?%d?)%.(%d%d?%d?)%.(%d%d?%d?)%s?", CH:PrintURL("%1.%2.%3.%4")) if found > 0 then return false, CH:CheckKeyword(newMsg), unpack(arg) end msg = CH:CheckKeyword(msg) return false, msg, unpack(arg) end local SetHyperlink = ItemRefTooltip.SetHyperlink function ItemRefTooltip:SetHyperlink(data, ...) if strsub(data, 1, 3) == "url" then local currentLink = strsub(data, 5) if not ChatFrameEditBox:IsShown() then ChatFrameEditBox:Show() ChatEdit_UpdateHeader(ChatFrameEditBox) end ChatFrameEditBox:Insert(currentLink) ChatFrameEditBox:HighlightText() else SetHyperlink(self, data, unpack(arg)) end end local function WIM_URLLink(link) if strsub(link, 1, 3) == "url" then local currentLink = strsub(link, 5) if not ChatFrameEditBox:IsShown() then ChatFrameEditBox:Show() ChatEdit_UpdateHeader(ChatFrameEditBox) end ChatFrameEditBox:Insert(currentLink) ChatFrameEditBox:HighlightText() return end end local hyperLinkEntered function CH:OnHyperlinkEnter() local linkToken = match(arg1, "([^:]+)") if hyperlinkTypes[linkToken] then ShowUIPanel(GameTooltip) GameTooltip:SetOwner(this, "ANCHOR_CURSOR") GameTooltip:SetHyperlink(arg1) hyperLinkEntered = this GameTooltip:Show() end end function CH:OnHyperlinkLeave() local linkToken = match(arg1, "([^:]+)") if hyperlinkTypes[linkToken] then HideUIPanel(GameTooltip) hyperLinkEntered = nil end end function CH:OnMessageScrollChanged() if hyperLinkEntered == this then HideUIPanel(GameTooltip) hyperLinkEntered = false end end function CH:EnableHyperlink() for i = 1, NUM_CHAT_WINDOWS do local frame = _G["ChatFrame"..i] if (not self.hooks or not self.hooks[frame] or not self.hooks[frame].OnHyperlinkEnter) then self:HookScript(frame, "OnHyperlinkEnter") self:HookScript(frame, "OnHyperlinkLeave") self:HookScript(frame, "OnMessageScrollChanged") end end end function CH:DisableHyperlink() for i = 1, NUM_CHAT_WINDOWS do local frame = _G["ChatFrame"..i] if self.hooks and self.hooks[frame] and self.hooks[frame].OnHyperlinkEnter then self:Unhook(frame, "OnHyperlinkEnter") self:Unhook(frame, "OnHyperlinkLeave") self:Unhook(frame, "OnMessageScrollChanged") end end end function CH:DisableChatThrottle() twipe(msgList) twipe(msgCount) twipe(msgTime) end function CH.ShortChannel() return format("|Hchannel:%s|h[%s]|h", arg8, DEFAULT_STRINGS[strupper(arg8)] or gsub(arg8, "channel:", "")) end function CH:ConcatenateTimeStamp(msg) if CH.db.timeStampFormat and CH.db.timeStampFormat ~= "NONE" then local timeStamp = BetterDate(CH.db.timeStampFormat, CH.timeOverride or time()) timeStamp = gsub(timeStamp, " ", "") timeStamp = gsub(timeStamp, "AM", " AM") timeStamp = gsub(timeStamp, "PM", " PM") if CH.db.useCustomTimeColor then local color = CH.db.customTimeColor local hexColor = E:RGBToHex(color.r, color.g, color.b) msg = format("%s[%s]|r %s", hexColor, timeStamp, msg) else msg = format("[%s] %s", timeStamp, msg) end CH.timeOverride = nil end return msg end function GetColoredName(event, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10) if not E.private.general.classCache then return arg2 end if arg2 and arg2 ~= "" then local name, realm = strsplit("-", arg2) local englishClass = CC:GetClassByName(name, realm) if englishClass then local classColorTable = CUSTOM_CLASS_COLORS and CUSTOM_CLASS_COLORS[englishClass] or RAID_CLASS_COLORS[englishClass] if not classColorTable then return arg2 end return format("\124cff%.2x%.2x%.2x", classColorTable.r*255, classColorTable.g*255, classColorTable.b*255)..arg2.."\124r" end end return arg2 end function CH:ChatFrame_MessageEventHandler(event, ...) if event == "UPDATE_CHAT_WINDOWS" then local name, fontSize, r, g, b, a, shown, locked = GetChatWindowInfo(self:GetID()) if fontSize > 0 then local fontFile, unused, fontFlags = self:GetFont() self:SetFont(fontFile, fontSize, fontFlags) end if shown then self:Show() end -- Do more stuff!!! ChatFrame_RegisterForMessages(GetChatWindowMessages(self:GetID())) ChatFrame_RegisterForChannels(GetChatWindowChannels(self:GetID())) return end if event == "PLAYER_ENTERING_WORLD" then self.defaultLanguage = GetDefaultLanguage() return end if event == "TIME_PLAYED_MSG" then ChatFrame_DisplayTimePlayed(arg1, arg2) return end if event == "PLAYER_LEVEL_UP" then -- Level up local info = ChatTypeInfo["SYSTEM"] local string = format(TEXT(LEVEL_UP), arg1) self:AddMessage(string, info.r, info.g, info.b, info.id) if arg3 > 0 then string = format(TEXT(LEVEL_UP_HEALTH_MANA), arg2, arg3) else string = format(TEXT(LEVEL_UP_HEALTH), arg2) end self:AddMessage(string, info.r, info.g, info.b, info.id) if arg4 > 0 then string = format(GetText("LEVEL_UP_CHAR_POINTS", nil, arg4), arg4) self:AddMessage(string, info.r, info.g, info.b, info.id) end if arg5 > 0 then string = format(TEXT(LEVEL_UP_STAT), TEXT(SPELL_STAT0_NAME), arg5) self:AddMessage(string, info.r, info.g, info.b, info.id) end if arg6 > 0 then string = format(TEXT(LEVEL_UP_STAT), TEXT(SPELL_STAT1_NAME), arg6) self:AddMessage(string, info.r, info.g, info.b, info.id) end if arg7 > 0 then string = format(TEXT(LEVEL_UP_STAT), TEXT(SPELL_STAT2_NAME), arg7) self:AddMessage(string, info.r, info.g, info.b, info.id) end if arg8 > 0 then string = format(TEXT(LEVEL_UP_STAT), TEXT(SPELL_STAT3_NAME), arg8) self:AddMessage(string, info.r, info.g, info.b, info.id) end if arg9 > 0 then string = format(TEXT(LEVEL_UP_STAT), TEXT(SPELL_STAT4_NAME), arg9) self:AddMessage(string, info.r, info.g, info.b, info.id) end return end if event == "CHARACTER_POINTS_CHANGED" then local info = ChatTypeInfo["SYSTEM"] if arg2 > 0 then local cp1, cp2 = UnitCharacterPoints("player") if cp2 then local string = format(GetText("LEVEL_UP_SKILL_POINTS", nil, cp2), cp2) self:AddMessage(string, info.r, info.g, info.b, info.id) end end return end if event == "GUILD_MOTD" then if arg1 and (strlen(arg1) > 0) then local info = ChatTypeInfo["GUILD"] local string = format(TEXT(GUILD_MOTD_TEMPLATE), arg1) self:AddMessage(string, info.r, info.g, info.b, info.id) end return end if event == "EXECUTE_CHAT_LINE" then self.editBox:SetText(arg1) ChatEdit_SendText(self.editBox) ChatEdit_OnEscapePressed(self.editBox) return end if event == "UPDATE_CHAT_COLOR" then local info = ChatTypeInfo[strupper(arg1)] if info then info.r = arg2 info.g = arg3 info.b = arg4 self:UpdateColorByID(info.id, info.r, info.g, info.b) if strupper(arg1) == "WHISPER" then info = ChatTypeInfo["REPLY"] if info then info.r = arg2 info.g = arg3 info.b = arg4 self:UpdateColorByID(info.id, info.r, info.g, info.b) end end end return end if strsub(event, 1, 8) == "CHAT_MSG" then local type = strsub(event, 10) local info = ChatTypeInfo[type] local arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10 = unpack(arg) local filter, newarg1, newarg2, newarg3, newarg4, newarg5, newarg6, newarg7, newarg8, newarg9, newarg10 = false if chatFilters[event] then for _, filterFunc in next, chatFilters[event] do filter, newarg1, newarg2, newarg3, newarg4, newarg5, newarg6, newarg7, newarg8, newarg9, newarg10 = filterFunc(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, event) arg1 = newarg1 or arg1 if filter then return true elseif newarg1 and newarg2 then arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10 = newarg1, newarg2, newarg3, newarg4, newarg5, newarg6, newarg7, newarg8, newarg9, newarg10 end end end local coloredName = GetColoredName(event, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10) local channelLength = arg4 and strlen(arg4) if (strsub(type, 1, 7) == "CHANNEL") and (type ~= "CHANNEL_LIST") and ((arg1 ~= "INVITE") or (type ~= "CHANNEL_NOTICE_USER")) then if arg1 == "WRONG_PASSWORD" then local staticPopup = _G[StaticPopup_Visible("CHAT_CHANNEL_PASSWORD") or ""] if staticPopup and staticPopup.data == arg9 then -- Don't display invalid password messages if we're going to prompt for a password (bug 102312) return end end local found = 0 for index, value in pairs(self.channelList) do if channelLength > strlen(value) then -- arg9 is the channel name without the number in front... if ((arg7 > 0) and (self.zoneChannelList[index] == arg7)) or (strupper(value) == strupper(arg9)) then found = 1 info = ChatTypeInfo["CHANNEL"..arg8] if (type == "CHANNEL_NOTICE") and (arg1 == "YOU_LEFT") then self.channelList[index] = nil self.zoneChannelList[index] = nil end break end end end if found == 0 or not info then return true end end if type == "SYSTEM" or type == "TEXT_EMOTE" or type == "SKILL" or type == "LOOT" or type == "MONEY" or type == "OPENING" or type == "TRADESKILLS" or type == "PET_INFO" then self:AddMessage(CH:ConcatenateTimeStamp(arg1), info.r, info.g, info.b, info.id) elseif strsub(type,1,7) == "COMBAT_" then self:AddMessage(CH:ConcatenateTimeStamp(arg1), info.r, info.g, info.b, info.id) elseif strsub(type,1,6) == "SPELL_" then self:AddMessage(CH:ConcatenateTimeStamp(arg1), info.r, info.g, info.b, info.id) elseif strsub(type,1,10) == "BG_SYSTEM_" then self:AddMessage(CH:ConcatenateTimeStamp(arg1), info.r, info.g, info.b, info.id) elseif type == "IGNORED" then self:AddMessage(format(CH:ConcatenateTimeStamp(GlobalStrings.CHAT_IGNORED), arg2), info.r, info.g, info.b, info.id) elseif type == "FILTERED" then self:AddMessage(format(CH:ConcatenateTimeStamp(GlobalStrings.CHAT_FILTERED), arg2), info.r, info.g, info.b, info.id) elseif type == "RESTRICTED" then self:AddMessage(CH:ConcatenateTimeStamp(GlobalStrings.CHAT_RESTRICTED), info.r, info.g, info.b, info.id) elseif type == "CHANNEL_LIST" then if channelLength > 0 then self:AddMessage(format(CH:ConcatenateTimeStamp(_G["CHAT_"..type.."_GET"]..arg1), arg4), info.r, info.g, info.b, info.id) else self:AddMessage(CH:ConcatenateTimeStamp(arg1), info.r, info.g, info.b, info.id) end elseif type == "CHANNEL_NOTICE_USER" then local globalstring = _G["CHAT_"..arg1.."_NOTICE"] globalstring = CH:ConcatenateTimeStamp(globalstring) if strlen(arg5) > 0 then -- TWO users in this notice (E.G. x kicked y) self:AddMessage(format(globalstring, arg4, arg2, arg5), info.r, info.g, info.b, info.id) else self:AddMessage(format(globalstring, arg4, arg2), info.r, info.g, info.b, info.id) end elseif type == "CHANNEL_NOTICE" then local globalstring = _G["CHAT_"..arg1.."_NOTICE"] if arg10 > 0 then arg4 = arg4.." "..arg10 end self:AddMessage(format(CH:ConcatenateTimeStamp(globalstring), arg4), info.r, info.g, info.b, info.id) else local body local _, fontHeight = GetChatWindowInfo(self:GetID()) if fontHeight == 0 then --fontHeight will be 0 if it's still at the default (14) fontHeight = 14 end -- Add AFK/DND flags local pflag if arg6 ~= "" then if arg6 == "DND" or arg6 == "AFK" then pflag = (pflag or "").._G["CHAT_FLAG_"..arg6] else pflag = _G["CHAT_FLAG_"..arg6] end else if pflag == true then pflag = "" end end pflag = pflag or "" local showLink = 1 if strsub(type, 1, 7) == "MONSTER" or strsub(type, 1, 9) == "RAID_BOSS" then showLink = nil else arg1 = gsub(arg1, "%%", "%%%%") end local showLink = 1 if strsub(type, 1, 7) == "MONSTER" or type == "RAID_BOSS_EMOTE" then showLink = nil else arg1 = gsub(arg1, "%%", "%%%%") end if (strlen(arg3) > 0) and (arg3 ~= "Universal") and (arg3 ~= GetDefaultLanguage()) then local languageHeader = "["..arg3.."] " if showLink and (strlen(arg2) > 0) then body = format(_G["CHAT_"..type.."_GET"]..languageHeader..arg1, pflag.."|Hplayer:"..arg2.."|h".."["..coloredName.."]".."|h") else body = format(_G["CHAT_"..type.."_GET"]..languageHeader..arg1, pflag..arg2) end else if showLink and (strlen(arg2) > 0) and (type ~= "EMOTE") then body = format(_G["CHAT_"..type.."_GET"]..arg1, pflag.."|Hplayer:"..arg2.."|h".."["..coloredName.."]".."|h") elseif showLink and (strlen(arg2) > 0) and (type == "EMOTE") then body = format(_G["CHAT_"..type.."_GET"]..arg1, pflag.."|Hplayer:"..arg2.."|h".."["..coloredName.."]".."|h") else arg1 = gsub(arg1, "%%s %%s", "%%s") body = format(_G["CHAT_"..type.."_GET"]..arg1, pflag..arg2) -- Add raid boss emote message if type == "RAID_BOSS_EMOTE" then RaidBossEmoteFrame:AddMessage(body, info.r, info.g, info.b, 1.0) PlaySound("RaidBossEmoteWarning") end end end -- Add Channel arg4 = gsub(arg4, "%s%-%s.*", "") if channelLength > 0 then body = "|Hchannel:channel:"..arg8.."|h["..arg4.."]|h "..body end if CH.db.shortChannels then body = gsub(body, "|Hchannel:(.-)|h%[(.-)%]|h", CH.ShortChannel) body = gsub(body, "CHANNEL:", "") body = gsub(body, "^(.-|h) "..L["whispers"], "%1") body = gsub(body, "^(.-|h) "..L["says"], "%1") body = gsub(body, "^(.-|h) "..L["yells"], "%1") body = gsub(body, "<"..GlobalStrings.AFK..">", "[|cffFF0000"..L["AFK"].."|r] ") body = gsub(body, "<"..GlobalStrings.DND..">", "[|cffE7E716"..L["DND"].."|r] ") body = gsub(body, "^%["..GlobalStrings.CHAT_MSG_RAID_WARNING.."%]", "["..L["RW"].."]") end self:AddMessage(CH:ConcatenateTimeStamp(body), info.r, info.g, info.b, info.id) end if type == "WHISPER" then ChatEdit_SetLastTellTarget(self.editBox, arg2) if self.tellTimer and (GetTime() > self.tellTimer) then PlaySound("TellMessage") end self.tellTimer = GetTime() + GlobalStrings.CHAT_TELL_ALERT_TIME FCF_FlashTab() end return true end end function CH:ChatFrame_OnEvent(event, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10) if CH.ChatFrame_MessageEventHandler(self, event, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10) then return end end function CH:FloatingChatFrame_OnEvent() CH.ChatFrame_OnEvent(this, event, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10) FloatingChatFrame_OnEvent(this, event, 1) end local function OnTextChanged(self) local text = self:GetText() local MIN_REPEAT_CHARACTERS = E.db.chat.numAllowedCombatRepeat if strlen(text) > MIN_REPEAT_CHARACTERS then local repeatChar = true for i = 1, MIN_REPEAT_CHARACTERS, 1 do if strsub(text,(0-i), (0-i)) ~= strsub(text, (-1-i), (-1-i)) then repeatChar = false break end end if repeatChar then self:Hide() return end end if strlen(text) < 5 then if strsub(text, 1, 4) == "/tt " then local unitname, realm = UnitName("target") if unitname and realm then unitname = unitname .. "-" .. gsub(realm, " ", "") end ChatFrame_SendTell((unitname or L["Invalid Target"]), ChatFrame1) end if strsub(text, 1, 4) == "/gr " then self:SetText(CH:GetGroupDistribution() .. strsub(text, 5)) ChatEdit_ParseText(self, 0) end end local new, found = gsub(text, "|Kf(%S+)|k(%S+)%s(%S+)|k", "%2 %3") if found > 0 then new = gsub(new, "|", "") self:SetText(new) end end function CH:SetupChat() if E.private.chat.enable ~= true then return end for i = 1, NUM_CHAT_WINDOWS do local frame = _G["ChatFrame"..i] local id = frame:GetID() local _, fontSize = GetChatWindowInfo(id) self:StyleChat(frame) frame:SetFont(LSM:Fetch("font", self.db.font), fontSize, self.db.fontOutline) if self.db.fontOutline ~= "NONE" then frame:SetShadowColor(0, 0, 0, 0.2) else frame:SetShadowColor(0, 0, 0, 1) end frame:SetTimeVisible(100) frame:SetShadowOffset((E.mult or 1), -(E.mult or 1)) frame:SetFading(self.db.fade) if not frame.scriptsSet then frame:SetScript("OnMouseWheel", ChatFrame_OnMouseScroll) frame:EnableMouseWheel(true) if id ~= 2 then frame:SetScript("OnEvent", CH.FloatingChatFrame_OnEvent) end hooksecurefunc(frame, "StopMovingOrSizing", function() CH:PositionChat(true) end) frame.scriptsSet = true end end local editbox = _G["ChatFrameEditBox"] if not editbox.isSkinned then local a, b, c = select(6, editbox:GetRegions()) E:Kill(a) E:Kill(b) E:Kill(c) E:SetTemplate(editbox, "Default", true) editbox:SetAltArrowKeyMode(CH.db.useAltKey) editbox:SetAllPoints(LeftChatDataPanel) self:SecureHook(editbox, "AddHistoryLine", "ChatEdit_AddHistory") HookScript(editbox, "OnTextChanged", function() OnTextChanged(this) end) editbox.historyLines = ElvCharacterDB.ChatEditHistory editbox.historyIndex = 0 editbox:Hide() HookScript(editbox, "OnEditFocusGained", function() this:Show() if not LeftChatPanel:IsShown() then LeftChatPanel.editboxforced = true LeftChatToggleButton:GetScript("OnEnter")(LeftChatToggleButton) end end) HookScript(editbox, "OnEditFocusLost", function() if LeftChatPanel.editboxforced then LeftChatPanel.editboxforced = nil if LeftChatPanel:IsShown() then LeftChatToggleButton:GetScript("OnLeave")(LeftChatToggleButton) end end this.historyIndex = 0 this:Hide() end) for _, text in pairs(ElvCharacterDB.ChatEditHistory) do editbox:AddHistoryLine(text) end editbox.isSkinned = true end if self.db.hyperlinkHover then self:EnableHyperlink() end DEFAULT_CHAT_FRAME:SetParent(LeftChatPanel) self:ScheduleTimer("PositionChat", 1) end local function PrepareMessage(author, message) if not author then return message end return format("%s%s", strupper(author), message) end function CH:ChatThrottleHandler(_, ...) local arg1, arg2 = unpack(arg) if arg2 and arg2 ~= "" then local message = PrepareMessage(arg2, arg1) if msgList[message] == nil then msgList[message] = true msgCount[message] = 1 msgTime[message] = time() else msgCount[message] = msgCount[message] + 1 end end end function CH.CHAT_MSG_CHANNEL(message, author, ...) if not (message and author) then return end local blockFlag = false local msg = PrepareMessage(author, message) if msg == nil then return CH.FindURL(message, author, unpack(arg)) end -- ignore player messages if author and author == UnitName("player") then return CH.FindURL(message, author, unpack(arg)) end if msgList[msg] and CH.db.throttleInterval ~= 0 then if difftime(time(), msgTime[msg]) <= CH.db.throttleInterval then blockFlag = true end end if blockFlag then return true else if CH.db.throttleInterval ~= 0 then msgTime[msg] = time() end return CH.FindURL(message, author, unpack(arg)) end end function CH.CHAT_MSG_YELL(message, author, ...) if not (message and author) then return end local blockFlag = false local msg = PrepareMessage(author, message) if msg == nil then return CH.FindURL(message, author, unpack(arg)) end -- ignore player messages if author and author == UnitName("player") then return CH.FindURL(message, author, unpack(arg)) end if msgList[msg] and msgCount[msg] > 1 and CH.db.throttleInterval ~= 0 then if difftime(time(), msgTime[msg]) <= CH.db.throttleInterval then blockFlag = true end end if blockFlag then return true else if CH.db.throttleInterval ~= 0 then msgTime[msg] = time() end return CH.FindURL(message, author, unpack(arg)) end end function CH.CHAT_MSG_SAY(message, author, ...) if not (message and author) then return end return CH.FindURL(message, author, unpack(arg)) end function CH:ThrottleSound() self.SoundPlayed = nil end local protectLinks = {} function CH:CheckKeyword(message) for itemLink in gmatch(message, "|%x+|Hitem:.-|h.-|h|r") do protectLinks[itemLink] = gsub(itemLink, "%s","|s") for keyword, _ in pairs(CH.Keywords) do if itemLink == keyword then if self.db.keywordSound ~= "None" and not self.SoundPlayed then PlaySoundFile(LSM:Fetch("sound", self.db.keywordSound), "Master") self.SoundPlayed = true self.SoundTimer = CH:ScheduleTimer("ThrottleSound", 1) end end end end for itemLink, tempLink in pairs(protectLinks) do message = gsub(message, gsub(itemLink, "([%(%)%.%%%+%-%*%?%[%^%$])","%%%1"), tempLink) end local classColorTable, tempWord, rebuiltString, lowerCaseWord, wordMatch, classMatch local isFirstWord = true for word in gmatch(message, "%s-[^%s]+%s*") do tempWord = gsub(word, "[%s%p]", "") lowerCaseWord = strlower(tempWord) for keyword, _ in pairs(CH.Keywords) do if lowerCaseWord == strlower(keyword) then word = gsub(word, tempWord, format("%s%s|r", E.media.hexvaluecolor, tempWord)) if self.db.keywordSound ~= "None" and not self.SoundPlayed then PlaySoundFile(LSM:Fetch("sound", self.db.keywordSound), "Master") self.SoundPlayed = true self.SoundTimer = CH:ScheduleTimer("ThrottleSound", 1) end end end if self.db.classColorMentionsChat and E.private.general.classCache then tempWord = gsub(word, "^[%s%p]-([^%s%p]+)([%-]?[^%s%p]-)[%s%p]*$","%1%2") classMatch = CC:GetCacheTable()[E.myrealm][tempWord] wordMatch = classMatch and lowerCaseWord if wordMatch and not E.global.chat.classColorMentionExcludedNames[wordMatch] then classColorTable = CUSTOM_CLASS_COLORS and CUSTOM_CLASS_COLORS[classMatch] or RAID_CLASS_COLORS[classMatch] word = gsub(word, gsub(tempWord, "%-","%%-"), format("\124cff%.2x%.2x%.2x%s\124r", classColorTable.r*255, classColorTable.g*255, classColorTable.b*255, tempWord)) end end if isFirstWord then rebuiltString = word isFirstWord = false else rebuiltString = format("%s%s", rebuiltString, word) end end for itemLink, tempLink in pairs(protectLinks) do rebuiltString = gsub(rebuiltString, gsub(tempLink, "([%(%)%.%%%+%-%*%?%[%^%$])","%%%1"), itemLink) protectLinks[itemLink] = nil end return rebuiltString end function CH:AddLines(lines, ...) for i = getn(arg), 1, -1 do local x = select(i, unpack(arg)) if x:GetObjectType() == "FontString" and not x:GetName() then tinsert(lines, x:GetText()) end end end function CH:ChatEdit_UpdateHeader() local type = this.chatType if type == "CHANNEL" then local id = GetChannelName(this.channelTarget) if id == 0 then this:SetBackdropBorderColor(unpack(E.media.bordercolor)) else this:SetBackdropBorderColor(ChatTypeInfo[type..id].r, ChatTypeInfo[type..id].g, ChatTypeInfo[type..id].b) end elseif type then this:SetBackdropBorderColor(ChatTypeInfo[type].r, ChatTypeInfo[type].g, ChatTypeInfo[type].b) end end function CH:ChatEdit_OnEnterPressed() local type = this.chatType if ChatTypeInfo[type].sticky == 1 then if not self.db.sticky then type = "SAY" end this.chatType = type end end function CH:SetItemRef(link, text, button) if strsub(link, 1, 7) == "channel" then if IsModifiedClick("CHATLINK") then ToggleFriendsFrame(4) elseif button == "LeftButton" then local chanLink = sub(link, 9) local chatType, chatTarget = strsplit(":", chanLink) if strupper(chatType) == "CHANNEL" then if GetChannelName(tonumber(chatTarget)) ~= 0 then ChatFrame_OpenChat("/"..chatTarget, this) end else ChatFrame_OpenChat("/"..chatType, this) end --[[ -- TODO elseif button == "RightButton" then local chanLink = sub(link, 9) local chatType, chatTarget = strsplit(":", chanLink) if not strupper(chatType) == "CHANNEL" and GetChannelName(tonumber(chatTarget)) == 0 then ChatChannelDropDown_Show(this, strupper(chatType), chatTarget, Chat_GetColoredChatName(strupper(chatType), chatTarget)) end ]] end return end return self.hooks.SetItemRef(link, text, button) end function CH:SetChatFont(chatFrame, fontSize) if not chatFrame then chatFrame = FCF_GetCurrentChatFrame() end if not fontSize then fontSize = this.value end chatFrame:SetFont(LSM:Fetch("font", self.db.font), fontSize, self.db.fontOutline) if self.db.fontOutline ~= "NONE" then chatFrame:SetShadowColor(0, 0, 0, 0.2) else chatFrame:SetShadowColor(0, 0, 0, 1) end chatFrame:SetShadowOffset((E.mult or 1), -(E.mult or 1)) end function CH:ChatEdit_AddHistory(_, line) if find(line, "/rl") then return end if strlen(line) > 0 then for _, text in pairs(ElvCharacterDB.ChatEditHistory) do if text == line then return end end tinsert(ElvCharacterDB.ChatEditHistory, getn(ElvCharacterDB.ChatEditHistory) + 1, line) if getn(ElvCharacterDB.ChatEditHistory) > 20 then tremove(ElvCharacterDB.ChatEditHistory, 1) end end end function CH:UpdateChatKeywords() twipe(CH.Keywords) local keywords = self.db.keywords keywords = gsub(keywords,",%s",",") for i = 1, getn({split(",", keywords)}) do local stringValue = select(i, split(",", keywords)) if stringValue ~= "" then CH.Keywords[stringValue] = true end end end function CH:UpdateFading() for i = 1, NUM_CHAT_WINDOWS do local frame = _G["ChatFrame"..i] if frame then frame:SetFading(self.db.fade) end end end function CH:DisplayChatHistory() local data, chat, d = ElvCharacterDB.ChatHistoryLog if not (data and next(data)) then return end CH.SoundPlayed = true for i = 1, NUM_CHAT_WINDOWS do chat = _G["ChatFrame"..i] for i = 1, getn(data) do d = data[i] if type(d) == "table" then CH.timeOverride = d[51] for _, messageType in pairs(chat.messageTypeList) do if gsub(strsub(d[50],10),"_INFORM","") == messageType then CH.ChatFrame_MessageEventHandler(chat,d[50],d[1],d[2],d[3],d[4],d[5],d[6],d[7],d[8],d[9],d[10],d[11]) end end end end end CH.SoundPlayed = nil end tremove(ChatTypeGroup["GUILD"], 2) function CH:DelayGuildMOTD() local delay, delayFrame, chat = 0, CreateFrame("Frame") tinsert(ChatTypeGroup["GUILD"], 2, "GUILD_MOTD") delayFrame:SetScript("OnUpdate", function() delay = delay + arg1 if delay < 7 then return end local msg = GetGuildRosterMOTD() for i = 1, NUM_CHAT_WINDOWS do local channel chat = _G["ChatFrame"..i] for k, v in pairs(chat.messageTypeList) do if v == "GUILD" then channel = v end end if chat and channel then if msg and strlen(msg) > 0 then local info = ChatTypeInfo["GUILD"] local string = format(GUILD_MOTD_TEMPLATE, msg) chat:AddMessage(string, info.r, info.g, info.b, info.id) end chat:RegisterEvent("GUILD_MOTD") end end this:SetScript("OnUpdate", nil) end) end function CH:SaveChatHistory(event, ...) if not self.db.chatHistory then return end local data = ElvCharacterDB.ChatHistoryLog if self.db.throttleInterval ~= 0 and (event == "CHAT_MESSAGE_SAY" or event == "CHAT_MESSAGE_YELL" or event == "CHAT_MSG_CHANNEL") then self:ChatThrottleHandler(event, unpack(arg)) local message, author = unpack(arg) local msg = PrepareMessage(author, message) if author and author ~= PLAYER_NAME and msgList[msg] then if difftime(time(), msgTime[msg]) <= CH.db.throttleInterval then return end end end local temp = {} for i = 1, getn(arg) do temp[i] = select(i, unpack(arg)) or false end if getn(temp) > 0 then temp[50] = event temp[51] = time() tinsert(data, temp) while getn(data) >= 128 do tremove(data, 1) end end temp = nil -- Destory! end function CH:ChatFrame_AddMessageEventFilter(event, filter) assert(event and filter) if chatFilters[event] then -- Only allow a filter to be added once for _, filterFunc in next, chatFilters[event] do if filterFunc == filter then return end end else chatFilters[event] = {} end tinsert(chatFilters[event], filter) end function CH:ChatFrame_RemoveMessageEventFilter(event, filter) assert(event and filter) if chatFilters[event] then for index, filterFunc in next, chatFilters[event] do if filterFunc == filter then tremove(chatFilters[event], index) end end if getn(chatFilters[event]) == 0 then chatFilters[event] = nil end end end function CH:FCF_SetWindowAlpha(frame, alpha) frame.oldAlpha = alpha or 1 end local FindURL_Events = { "CHAT_MSG_WHISPER", "CHAT_MSG_WHISPER_INFORM", "CHAT_MSG_GUILD", "CHAT_MSG_OFFICER", "CHAT_MSG_PARTY", "CHAT_MSG_RAID", "CHAT_MSG_RAID_LEADER", "CHAT_MSG_RAID_WARNING", "CHAT_MSG_BATTLEGROUND", "CHAT_MSG_BATTLEGROUND_LEADER", "CHAT_MSG_CHANNEL", "CHAT_MSG_SAY", "CHAT_MSG_YELL", "CHAT_MSG_EMOTE", "CHAT_MSG_TEXT_EMOTE", "CHAT_MSG_AFK", "CHAT_MSG_DND", } function CH:Initialize() if ElvCharacterDB.ChatHistory then ElvCharacterDB.ChatHistory = nil --Depreciated end if ElvCharacterDB.ChatLog then ElvCharacterDB.ChatLog = nil --Depreciated end self.db = E.db.chat self:DelayGuildMOTD() --Keep this before `is Chat Enabled` check if E.private.chat.enable ~= true then return end if not ElvCharacterDB.ChatEditHistory then ElvCharacterDB.ChatEditHistory = {} end if not ElvCharacterDB.ChatHistoryLog or not self.db.chatHistory then ElvCharacterDB.ChatHistoryLog = {} end self:UpdateChatKeywords() self:UpdateFading() E.Chat = self self:SecureHook("ChatEdit_UpdateHeader") self:SecureHook("ChatEdit_OnEnterPressed") self:RawHook("SetItemRef", true) E:Kill(ChatFrameMenuButton) if WIM then WIM.RegisterWidgetTrigger("chat_display", "whisper,chat,w2w,demo", "OnHyperlinkClick", function(self) CH.clickedframe = self end) WIM.RegisterItemRefHandler("url", WIM_URLLink) end self:SecureHook("FCF_SetChatWindowFontSize", "SetChatFont") self:RegisterEvent("UPDATE_CHAT_WINDOWS", "SetupChat") self:RegisterEvent("UPDATE_FLOATING_CHAT_WINDOWS", "SetupChat") self:SetupChat() self:UpdateAnchors() if not E.db.chat.lockPositions then CH:UpdateChatTabs() --It was not done in PositionChat, so do it now end --First get all pre-existing filters and copy them to our version of chatFilters using ChatFrame_GetMessageEventFilters for name, _ in pairs(ChatTypeGroup) do for i = 1, getn(ChatTypeGroup[name]) do local filterFuncTable = ChatFrame_GetMessageEventFilters(ChatTypeGroup[name][i]) if filterFuncTable then chatFilters[ChatTypeGroup[name][i]] = {} for j = 1, getn(filterFuncTable) do local filterFunc = filterFuncTable[j] tinsert(chatFilters[ChatTypeGroup[name][i]], filterFunc) end end end end --CHAT_MSG_CHANNEL isn't located inside ChatTypeGroup local filterFuncTable = ChatFrame_GetMessageEventFilters("CHAT_MSG_CHANNEL") if filterFuncTable then chatFilters["CHAT_MSG_CHANNEL"] = {} for j = 1, getn(filterFuncTable) do local filterFunc = filterFuncTable[j] tinsert(chatFilters["CHAT_MSG_CHANNEL"], filterFunc) end end --Now hook onto Blizzards functions for other addons hooksecurefunc(self, "ChatFrame_AddMessageEventFilter", ChatFrame_AddMessageEventFilter) hooksecurefunc(self, "ChatFrame_RemoveMessageEventFilter", ChatFrame_RemoveMessageEventFilter) self:SecureHook("FCF_SetWindowAlpha") ChatTypeInfo["SAY"].sticky = 1 ChatTypeInfo["EMOTE"].sticky = 1 ChatTypeInfo["YELL"].sticky = 1 ChatTypeInfo["WHISPER"].sticky = 1 ChatTypeInfo["PARTY"].sticky = 1 ChatTypeInfo["RAID"].sticky = 1 ChatTypeInfo["RAID_WARNING"].sticky = 1 ChatTypeInfo["BATTLEGROUND"].sticky = 1 ChatTypeInfo["GUILD"].sticky = 1 ChatTypeInfo["OFFICER"].sticky = 1 ChatTypeInfo["CHANNEL"].sticky = 1 if self.db.chatHistory then self:DisplayChatHistory() end for _, event in pairs(FindURL_Events) do ChatFrame_AddMessageEventFilter(event, CH[event] or CH.FindURL) local nType = strsub(event, 10) if nType ~= "AFK" and nType ~= "DND" then self:RegisterEvent(event, "SaveChatHistory") end end local S = E:GetModule("Skins") local frame = CreateFrame("Frame", "CopyChatFrame", E.UIParent) tinsert(UISpecialFrames, "CopyChatFrame") E:SetTemplate(frame, "Transparent") frame:SetWidth(700) frame:SetHeight(200) frame:SetPoint("BOTTOM", E.UIParent, "BOTTOM", 0, 3) frame:Hide() frame:SetMovable(true) frame:EnableMouse(true) frame:SetResizable(true) frame:SetMinResize(350, 100) frame:SetScript("OnMouseDown", function() if arg1 == "LeftButton" and not this.isMoving then this:StartMoving() this.isMoving = true elseif arg1 == "RightButton" and not this.isSizing then this:StartSizing() this.isSizing = true end end) frame:SetScript("OnMouseUp", function() if arg1 == "LeftButton" and this.isMoving then this:StopMovingOrSizing() this.isMoving = false elseif arg1 == "RightButton" and this.isSizing then this:StopMovingOrSizing() this.isSizing = false end end) frame:SetScript("OnHide", function() if this.isMoving or this.isSizing then this:StopMovingOrSizing() this.isMoving = false this.isSizing = false end end) frame:SetFrameStrata("DIALOG") local scrollArea = CreateFrame("ScrollFrame", "CopyChatScrollFrame", frame, "UIPanelScrollFrameTemplate") scrollArea:SetWidth(662) scrollArea:SetHeight(162) scrollArea:SetPoint("TOPLEFT", frame, "TOPLEFT", 8, -30) S:HandleScrollBar(CopyChatScrollFrameScrollBar) HookScript(scrollArea, "OnVerticalScroll", function() CopyChatFrameEditBox:SetHitRectInsets(0, 0, arg1, (CopyChatFrameEditBox:GetHeight() - arg1 - this:GetHeight())) end) local editBox = CreateFrame("EditBox", "CopyChatFrameEditBox", frame) editBox:SetMultiLine(true) editBox:SetMaxLetters(99999) editBox:EnableMouse(true) editBox:SetAutoFocus(false) editBox:SetFontObject(GameFontNormal) editBox:SetWidth(scrollArea:GetWidth()) editBox:SetHeight(200) editBox:SetScript("OnEscapePressed", function() CopyChatFrame:Hide() end) scrollArea:SetScrollChild(editBox) --[[CopyChatFrameEditBox:SetScript("OnTextChanged", function() local scrollBar = CopyChatScrollFrameScrollBar local _, max = scrollBar:GetMinMaxValues() for i = 1, max do scrollBar:SetValue(scrollBar:GetValue() + (scrollBar:GetHeight() / 2)) end end)]] frame:SetScript("OnSizeChanged", function() local width, height = this:GetWidth() - 38, this:GetHeight() - 38 scrollArea:SetWidth(width) scrollArea:SetHeight(height) CopyChatFrameEditBox:SetWidth(width) CopyChatFrameEditBox:SetHeight(height) end) local close = CreateFrame("Button", "CopyChatFrameCloseButton", frame, "UIPanelCloseButton") close:SetPoint("TOPRIGHT", 0, 0) close:SetFrameLevel(close:GetFrameLevel() + 1) S:HandleCloseButton(close) end local function InitializeCallback() CH:Initialize() end E:RegisterModule(CH:GetName(), InitializeCallback)