--imports local WIM = WIM; local _G = _G; local table = table; local string = string; local pairs = pairs; local CreateFrame = CreateFrame; local date = date; local time = time; local select = select; local tonumber = tonumber; local DDM = WIM.libs.DropDownMenu; --set namespace setfenv(1, WIM); local History = CreateModule("History", true); -- default history settings. db_defaults.history = { preview = true, previewCount = 25, maxPer = true, maxCount = 500, ageLimit = true, maxAge = 60*60*24*7*2, whispers = { friends = true, guild = true, all = true }, chat = { preview = true, previewCount = 25, maxPer = true, maxCount = 500, ageLimit = true, maxAge = 60*60*24*7*2, } }; db_defaults.displayColors.historyIn = { r=0.4705882352941176, g=0.4705882352941176, b=0.4705882352941176 }; db_defaults.displayColors.historyOut = { r=0.7058823529411764, g=0.7058823529411764, b=0.7058823529411764 }; local dDay = 60*60*24; local dWeek = dDay*7; local dMonth = dWeek*4; local dYear = dMonth*12; local tmpTable = {}; local ViewTypes = {}; local ChannelCache = {}; local CensoredCache = {}; local function clearTmpTable() for key, _ in pairs(tmpTable) do tmpTable[key] = nil; end end local function isEmptyTable(tbl) for k, _ in pairs(tbl) do if(k ~= "info") then return false; end end return true; end local function getPlayerHistoryTable(convoName) if(history[env.realm] and history[env.realm][env.character] and history[env.realm][env.character][convoName]) then return history[env.realm][env.character][convoName]; else -- this player hasn't been set up yet. Do it now. history[env.realm] = history[env.realm] or {}; history[env.realm][env.character] = history[env.realm][env.character] or {}; history[env.realm][env.character][convoName] = history[env.realm][env.character][convoName] or {info = {}}; return history[env.realm][env.character][convoName]; end end local function createWidget() local button = _G.CreateFrame("Button"); button.SetHistory = function(self, isHistory) self.parentWindow.isHistory = isHistory; if(isHistory and modules.History.enabled) then self:SetAlpha(1); -- DisplayTutorial(L["WIM History Button"], _G.format(L["Clicking the %s button on the message window will show that user's history in WIM's History Viewer."], -- "|T"..GetSelectedSkin().message_window.widgets.history.NormalTexture..":0:0:0:0|t")); else self:SetAlpha(0); end end button.UpdateProps = function(self) self:SetHistory(self.parentWindow.isHistory); end button:SetScript("OnEnter", function(self) if(db.showToolTips == true and self.parentWindow.isHistory) then _G.GameTooltip:SetOwner(self, "ANCHOR_TOPRIGHT"); _G.GameTooltip:SetText(L["Click to view message history."]); end end); button:SetScript("OnLeave", function(self) _G.GameTooltip:Hide(); end); button:SetScript("OnClick", function(self, button) if(self.parentWindow.isHistory) then ShowHistoryViewer(self.parentWindow.theUser); end end); return button; end -- store a cached entry if a record is cached. This will be used by History:ReplaceCensoredMessage if original message is shown. local function cacheIfCensored (record, ...) local arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14, arg15, arg16, arg17 = ...; local lineID = arg11; local isChatLineCensored = _G.C_ChatInfo and _G.C_ChatInfo.IsChatLineCensored and _G.C_ChatInfo.IsChatLineCensored(lineID); if (isChatLineCensored) then CensoredCache[lineID] = record; record.censored = true; -- filter out Show Message link record.msg = record.msg:gsub('|Hcensoredmessage:[^|]+|h.-|h', ''); end return record; end -- replace a history record by lineID. Returns true is msg is updated. function History:ReplaceCensoredMessage(lineID, msg) if (lineID and msg and CensoredCache[lineID]) then -- filter out report link msg = msg:gsub('|Hreportcensoredmessage:[^|]+|h.-|h', ''); if CensoredCache[lineID].msg ~= msg then CensoredCache[lineID].msg = msg; return true end end return false; end local function safeName(user) return string.lower(user or "") end --BNet_GetValidatedCharacterName local function recordWhisper(inbound, ...) local msg, from = ...; if not from then return end local db = db.history.whispers; local win = windows.active.whisper[safeName(from)] or windows.active.chat[from] or windows.active.w2w[safeName(from)]; if (win and (lists.gm[from] or db.all or (db.friends and (lists.friends[from] or win.isBN)) or (db.guild and lists.guild[from]))) then win.widgets.history:SetHistory(true); --If realid/btag whisper, we save them under btag to avoid caching issues --(ie NAME is encoded and changes every session, we can't use that to save whispers, plus if user dumps cache, they all return unknown) local pid = _G.BNet_GetBNetIDAccount(from) if pid then local _, _, btag, _, toonName = GetBNGetFriendInfoByID(pid) from = btag or toonName or from end local history = getPlayerHistoryTable(from); history.info.gm = lists.gm[from]; table.insert(history, cacheIfCensored({ convo = from, type = 1, -- whisper inbound = inbound or false, from = inbound and from or env.character, msg = msg, time = _G.time(); }, ...)); if(WIM.db.history.maxPer) then while(WIM.db.history.maxCount < #history) do table.remove(history, 1); end end end end function History:PostEvent_Whisper(...) recordWhisper(true, ...); end function History:PostEvent_WhisperInform(...) recordWhisper(false, ...); end local function deleteOldHistory(isChat) local historyDB = isChat and db.history.chat or db.history; local count = 0; for realm, characters in pairs(history) do for character, convos in pairs(characters) do for convo, messages in pairs(convos) do for i=#messages, 1, -1 do if((time() - messages[i].time) > historyDB.maxAge and ((isChat and messages[i].type == 2) or (not isChat and messages[i].type == 1))) then dPrint("Deleting History."..realm.."."..character.."."..convo.."["..i.."]"); table.remove(messages, i); count = count + 1; end end if(isEmptyTable(messages)) then convos[convo] = nil; end end if(isEmptyTable(convos)) then characters[character] = nil; end end if(isEmptyTable(characters)) then history[realm] = nil; end end if(count > 0) then _G.DEFAULT_CHAT_FRAME:AddMessage(_G.format(L["WIM pruned %d |4message:messages; from your history."], count)); end end function History:OnEnableWIM() -- clean up history if asked to. if(db.history.ageLimit) then deleteOldHistory(); end end function History:OnEnable() RegisterWidget("history", createWidget); for widget in Widgets("history") do local win = widget.parentWindow; if(win) then local history = history[env.realm] and history[env.realm][env.character] and history[env.realm][env.character][win.theUser] and win.type == "whisper"; if(history) then widget:SetHistory(true); end end end end function History:OnDisable() if(db.modules.History.enabled) then return; end for widget in Widgets("history") do if(widget.parentWindow.type == "whisper") then widget:SetHistory(false); -- module is disabled, hide Icons. end end end function History:OnWindowDestroyed(win) win.isHistory = nil; end function History:OnWindowCreated(win) if(db.history.preview) then local history = history[env.realm] and history[env.realm][env.character] and history[env.realm][env.character][win.theUser]; if(history) then local type = win.type == "whisper" and 1; for i=#history, 1, -1 do table.insert(tmpTable, 1, history[i]); if(#tmpTable >= db.history.previewCount) then break; end end if(#tmpTable > 0) then win.isHistory = true; win.widgets.history:SetHistory(true); for i=1, #tmpTable do local color = db.displayColors[tmpTable[i].inbound and "historyIn" or "historyOut"]; win.nextStamp = tmpTable[i].time; win.nextStampColor = db.displayColors.historyOut; win:AddMessage(applyMessageFormatting(win.widgets.chat_display, "CHAT_MSG_WHISPER", tmpTable[i].msg, tmpTable[i].from, nil, nil, nil, nil, nil, nil, nil, nil, -i, "0x0300000000000000"), color.r, color.g, color.b); end win.widgets.chat_display:AddMessage(" "); end clearTmpTable(); end end end --Chat History local ChatHistory = CreateModule("HistoryChat"); -- synonymous functions ChatHistory.OnWindowDestroyed = History.OnWindowDestroyed; function ChatHistory:OnEnableWIM() -- clean up history if asked to. if(db.history.chat.ageLimit) then deleteOldHistory(true); end end function ChatHistory:OnEnable() RegisterWidget("history", createWidget); for widget in Widgets("history") do local win = widget.parentWindow; if(win) then local chatName = win.theUser local history = history[env.realm] and history[env.realm][env.character] and history[env.realm][env.character][chatName] and win.type == "chat"; if(history) then widget:SetHistory(true); end end end end function ChatHistory:OnDisable() if(modules.HistoryChat.enabled) then return; end for widget in Widgets("history") do if(widget.parentWindow.type == "chat") then widget:SetHistory(false); -- module is disabled, hide Icons. end end end local function recordChannelChat(recordAs, ChannelType, ...) local msg, from = ...; local db = db.history.whispers; local win = windows.active.chat[recordAs]; if(win) then win.widgets.history:SetHistory(true); local history = getPlayerHistoryTable(recordAs); history.info.chat = true; history.info.channelNumber = channelNumber; table.insert(history, cacheIfCensored({ event = ChannelType, channelName = recordAs, type = 2, -- chat from = from, msg = msg, time = _G.time(); }, ...)); if(WIM.db.history.chat.maxPer) then while(WIM.db.history.chat.maxCount < #history) do table.remove(history, 1); end end end end function ChatHistory:PostEvent_ChatMessage(event, ...) local arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11 = ...; event = event:gsub("CHAT_MSG_", ""); if(event == "CHANNEL") then local recordAs; local isWorld = arg7 and arg7 > 0; local chatType = isWorld and "world" or "custom"; local channelName = string.split(" - ", arg9); local channelNumber = arg8; recordAs = channelName; if(recordAs and ((isWorld and db.history.chat.world) or (not isWorld and db.history.chat.custom))) then local noHistory = db.chat[isWorld and "world" or "custom"].channelSettings[channelName] and db.chat[isWorld and "world" or "custom"].channelSettings[channelName].noHistory; if(not noHistory) then recordChannelChat(recordAs, event, ...); end end else local recordAs; local chatType; if(event == "GUILD" and db.history.chat.guild) then recordAs = _G.GUILD; chatType = "guild"; elseif(event == "OFFICER" and db.history.chat.officer) then recordAs = _G.GUILD_RANK1_DESC; chatType = "officer"; elseif((event == "PARTY" or event == "PARTY_LEADER") and db.history.chat.party) then recordAs = _G.PARTY; chatType = "party"; elseif((event == "RAID" or event == "RAID_LEADER") and db.history.chat.raid) then recordAs = _G.RAID; chatType = "raid"; elseif((event == "INSTANCE_CHAT" or event == "INSTANCE_CHAT_LEADER") and db.history.chat.battleground) then recordAs = _G.INSTANCE_CHAT; chatType = "battleground"; elseif(event == "SAY" and db.history.chat.say) then recordAs = _G.SAY; chatType = "say"; end if(recordAs) then recordChannelChat(recordAs, event, ...); end end end -------------------------------------- -- History Viewer -- -------------------------------------- local function searchResult(msg, search) search = string.lower(string.trim(search)); msg = string.lower(msg); local start, stop, match = string.find(search, "([^%s]+)",1); while(match) do if(not string.find(msg, match)) then return false; end start, stop, match = string.find(search, "([^%s]+)",stop+1); end return true; end local function createHistoryViewer() -- Changes for Patch 9.0.1 - Shadowlands, retail and classic local win = CreateFrame("Frame", "WIM3_HistoryFrame", _G.UIParent, "BackdropTemplate"); win:Hide(); win.filter = {}; -- set size and position win:SetWidth(700); win:SetHeight(505); win:SetPoint("CENTER"); -- set backdrop - changes for Patch 9.0.1 - Shadowlands win.backdropInfo = {bgFile = "Interface\\AddOns\\"..addonTocName.."\\Sources\\Options\\Textures\\Frame_Background", edgeFile = "Interface\\AddOns\\"..addonTocName.."\\Sources\\Options\\Textures\\Frame", tile = true, tileSize = 64, edgeSize = 64, insets = { left = 64, right = 64, top = 64, bottom = 64 }}; win:ApplyBackdrop(); -- set basic frame properties win:SetClampedToScreen(true); win:SetFrameStrata("DIALOG"); win:SetMovable(true); win:SetToplevel(true); win:EnableMouse(true); win:RegisterForDrag("LeftButton"); if win.SetResizeBounds then -- WoW 10.0 win:SetResizeBounds(240,240) else win:SetMinResize(600, 400); end -- set script events win:SetScript("OnDragStart", function(self) self:StartMoving(); end); win:SetScript("OnDragStop", function(self) self:StopMovingOrSizing(); end); -- create and set title bar text win.title = win:CreateFontString(win:GetName().."Title", "OVERLAY", "ChatFontNormal"); win.title:SetPoint("TOPLEFT", 50 , -20); local font = win.title:GetFont(); win.title:SetFont(font, 16, ""); win.title:SetText(L["History Viewer"]) -- create close button win.close = CreateFrame("Button", win:GetName().."Close", win); win.close:SetWidth(18); win.close:SetHeight(18); win.close:SetPoint("TOPRIGHT", -24, -20); win.close:SetNormalTexture("Interface\\AddOns\\"..addonTocName.."\\Sources\\Options\\Textures\\blipRed"); win.close:SetHighlightTexture("Interface\\AddOns\\"..addonTocName.."\\Sources\\Options\\Textures\\close", "BLEND"); win.close:SetScript("OnClick", function(self) self:GetParent():Hide(); end); -- window actions win:SetScript("OnShow", function(self) _G.PlaySound(850); self.UpdateUserList(); end); win:SetScript("OnHide", function(self) _G.PlaySound(851); end); table.insert(_G.UISpecialFrames,win:GetName()); -- create nav win.nav = CreateFrame("Frame", nil, win); win.nav.border = win.nav:CreateTexture(nil, "BACKGROUND"); win.nav.border:SetColorTexture(1,1,1,.25); win.nav.border:SetPoint("TOPRIGHT"); win.nav.border:SetPoint("BOTTOMRIGHT"); win.nav.border:SetWidth(2); win.nav:SetPoint("TOPLEFT", 18, -47); win.nav:SetPoint("BOTTOMLEFT", 18, 18); win.nav:SetWidth(200); win.nav.user = DDM.Create_DropDownMenu("WIM3_HistoryUserMenu", win.nav) win.nav.user:SetParent(win.nav) win.nav.user:SetPoint("TOPLEFT", -15, 0); DDM.UIDropDownMenu_SetWidth(win.nav.user, win.nav:GetWidth() - 25); win.nav.user.list = {}; win.nav.user.getUserList = function(self) for key, _ in pairs(self.list) do self.list[key] = nil; end local thisToon = env.realm.."/"..env.character; for realm, users in pairs(history) do table.insert(self.list, realm); for user, _ in pairs(users) do if(thisToon == realm.."/"..user) then thisToon = nil; end table.insert(self.list, realm.."/"..user); end end if(thisToon) then table.insert(self.list, 1, thisToon); end table.sort(self.list); return self.list; end win.nav.user.init = function() local self = win.nav.user; local list = self:getUserList(); for i=1, #list do local info = {}; info.text = list[i]; info.value = list[i]; info.func = function(self) self = self or _G.this; win.USER = self.value; win.CONVO = ""; win.FILTER = ""; win.UpdateUserList(); DDM.UIDropDownMenu_SetSelectedValue(win.nav.user, self.value); end; DDM.UIDropDownMenu_AddButton(info, DDM.UIDropDownMenu_MENU_LEVEL); end end win.nav.user:SetScript("OnShow", function(self) DDM.UIDropDownMenu_Initialize(self, self.init); DDM.UIDropDownMenu_SetSelectedValue(self, win.USER); end); win.nav.filters = CreateFrame("Frame", nil, win.nav); win.nav.filters:SetPoint("BOTTOMLEFT"); win.nav.filters:SetPoint("BOTTOMRIGHT", -2, 0); win.nav.filters:SetHeight(125); win.nav.filters.border = win.nav.filters:CreateTexture(nil, "BACKGROUND"); win.nav.filters.border:SetColorTexture(1, 1, 1, .25); win.nav.filters.border:SetPoint("TOPLEFT"); win.nav.filters.border:SetPoint("TOPRIGHT"); win.nav.filters.border:SetHeight(20); win.nav.filters.text = win.nav.filters:CreateFontString(nil, "OVERLAY", "ChatFontNormal"); win.nav.filters.text:SetPoint("TOPLEFT", win.nav.filters.border); win.nav.filters.text:SetPoint("BOTTOMRIGHT", win.nav.filters.border); win.nav.filters.text:SetText(L["Filters"]); win.nav.filters.text:SetTextColor(_G.GameFontNormal:GetTextColor()); win.nav.filters.scroll = CreateFrame("ScrollFrame", "WIM3_HistoryFilterListScroll", win.nav.filters, "FauxScrollFrameTemplate"); win.nav.filters.scroll:SetPoint("TOPLEFT", 0, -22); win.nav.filters.scroll:SetPoint("BOTTOMRIGHT", -23, 0); win.nav.filters.scroll.buttons = {}; win.nav.filters.scroll.createButton = function() local button = CreateFrame("Button", nil, win.nav.filters); if(#win.nav.filters.scroll.buttons > 0) then button:SetPoint("TOPLEFT", win.nav.filters.scroll.buttons[#win.nav.filters.scroll.buttons], "BOTTOMLEFT"); button:SetPoint("TOPRIGHT", win.nav.filters.scroll.buttons[#win.nav.filters.scroll.buttons], "BOTTOMRIGHT"); else button:SetPoint("TOPLEFT", win.nav.filters.scroll); button:SetPoint("TOPRIGHT", win.nav.filters.scroll); end button:SetHeight(20); button:SetHighlightTexture("Interface\\QuestFrame\\UI-QuestLogTitleHighlight" , "ADD"); button:GetHighlightTexture():SetVertexColor(.196, .388, .5); button.text = button:CreateFontString(nil, "OVERLAY", "ChatFontNormal"); button.text:SetAllPoints(); button.text:SetJustifyH("LEFT"); button.SetFilter = function(self, filter) self.filter = filter; if(_G.type(filter) == "number") then self.text:SetText(" "..date(L["_DateFormat"], filter)); else self.filter = ""; self.text:SetText(" "..filter); end end button:SetScript("OnClick", function(self) win.FILTER = self.filter; win.nav.filters.scroll.update(); win.UpdateDisplay(); end); table.insert(win.nav.filters.scroll.buttons, button); return button; end for i=1, 5 do win.nav.filters.scroll.createButton(); end win.nav.filters.scroll.update = function() local self = win.nav.filters.scroll; local offset = _G.FauxScrollFrame_GetOffset(self); for i=1, #self.buttons do local index = i + offset; if(index <= #win.FILTERLIST) then self.buttons[i]:SetFilter(win.FILTERLIST[index]); self.buttons[i]:Show(); if(self.buttons[i].filter == win.FILTER) then self.buttons[i]:LockHighlight(); else self.buttons[i]:UnlockHighlight(); end else self.buttons[i]:Hide(); end end _G.FauxScrollFrame_Update(self, #win.FILTERLIST, 5, 20); end win.nav.filters.scroll:SetScript("OnShow", function(self) self:update(); end); win.nav.filters.scroll:SetScript("OnVerticalScroll", function(self, offset) _G.FauxScrollFrame_OnVerticalScroll(self, offset, 20, win.nav.filters.scroll.update); end); win.nav.userList = CreateFrame("Frame", nil, win.nav); win.nav.userList:SetPoint("BOTTOMLEFT", win.nav.filters, "TOPLEFT", 0, 1); win.nav.userList:SetPoint("BOTTOMRIGHT", win.nav.filters, "TOPRIGHT", 0, 1); win.nav.userList:SetPoint("TOP", 0, -30); win.nav.userList.border = win.nav.userList:CreateTexture(nil, "BACKGROUND"); win.nav.userList.border:SetColorTexture(1,1,1,.25); win.nav.userList.border:SetPoint("TOPLEFT", 0, 1); win.nav.userList.border:SetPoint("TOPRIGHT", 0, 1); win.nav.userList.border:SetHeight(1); win.nav.userList.scroll = CreateFrame("ScrollFrame", "WIM3_HistoryUserListScroll", win.nav.userList, "FauxScrollFrameTemplate"); win.nav.userList.scroll:SetPoint("TOPLEFT", 0, -2); win.nav.userList.scroll:SetPoint("BOTTOMRIGHT", -23, 0); win.nav.userList.scroll.buttons = {}; win.nav.userList.scroll.createButton = function() local button = CreateFrame("Button", nil, win.nav.userList); if(#win.nav.userList.scroll.buttons > 0) then button:SetPoint("TOPLEFT", win.nav.userList.scroll.buttons[#win.nav.userList.scroll.buttons], "BOTTOMLEFT"); button:SetPoint("TOPRIGHT", win.nav.userList.scroll.buttons[#win.nav.userList.scroll.buttons], "BOTTOMRIGHT"); else button:SetPoint("TOPLEFT", win.nav.userList.scroll); button:SetPoint("TOPRIGHT", win.nav.userList.scroll); end button:SetHeight(20); button:SetHighlightTexture("Interface\\QuestFrame\\UI-QuestLogTitleHighlight" , "ADD"); button:GetHighlightTexture():SetVertexColor(.196, .388, .5); button.text = button:CreateFontString(nil, "OVERLAY", "ChatFontNormal"); button.text:SetPoint("TOPLEFT"); button.text:SetPoint("BOTTOMRIGHT", -18, 0); button.text:SetJustifyH("LEFT"); button.SetUser = function(self, user) local original, extra, color = user, ""; local gmTag user, gmTag = string.match(original, "([^*]+)(*?)$"); color = gmTag == "*" and constants.classes[L["Game Master"]].color or "ffffff"; if(string.match(original, "^*")) then extra = " |TInterface\\AddOns\\WIM\\Skins\\Default\\minimap.blp:20:20:0:0|t"; color = "fff569"; end if not user then _G.print("Your WIM history is damaged beyond repair and must be erased before it can be used again do to having conversation with a realid friend that has no battletag. This bug has been fixed in WIM but your history cannot be repaired") return end self.user = user; self.text:SetText(" |cff"..color..user.."|r"..extra..(gmTag == "*" and " |TInterface\\ChatFrame\\UI-ChatIcon-Blizz.blp:0:2:0:0|t" or "")); if(user == win.SELECT) then self:Click(); end end button:SetScript("OnClick", function(self) win:SelectConvo(self.user); win.nav.userList.scroll.update(); end); button.delete = CreateFrame("Button", nil, button); button.delete:SetNormalTexture("Interface\\AddOns\\"..addonTocName.."\\Modules\\Textures\\xNormal"); button.delete:SetPushedTexture("Interface\\AddOns\\"..addonTocName.."\\Modules\\Textures\\xPressed"); button.delete:SetWidth(16); button.delete:SetHeight(16); button.delete:SetAlpha(.5); button.delete:SetPoint("RIGHT"); button.delete:SetScript("OnClick", function(self) _G.StaticPopupDialogs["WIM_DELETE_HISTORY"] = { preferredIndex = STATICPOPUP_NUMDIALOGS, text = _G.format(L["Are you sure you want to delete all history saved for %s on %s?"], "|cff69ccf0"..self:GetParent().user.."|r", "|cff69ccf0"..win.USER.."|r" ), button1 = L["Yes"], button2 = L["No"], OnAccept = function() local realm, character = string.match(win.USER, "^([^/]+)/?(.*)$"); if(realm and character and history[realm] and history[realm][character]) then history[realm][character][self:GetParent().user] = nil; if(isEmptyTable(history[realm][character])) then history[realm][character] = nil; if(isEmptyTable(history[realm])) then history[realm] = nil; end end elseif(realm and history[realm]) then for char, convos in pairs(history[realm]) do convos[self:GetParent().user] = nil; if(isEmptyTable(convos)) then history[realm][char] = nil; end end if(isEmptyTable(history[realm])) then history[realm] = nil; end end win.nav.user:Hide(); win.nav.user:Show(); win.UpdateUserList(); end, timeout = 0, whileDead = 1, hideOnEscape = 1 }; _G.StaticPopup_Show("WIM_DELETE_HISTORY"); end); table.insert(win.nav.userList.scroll.buttons, button); return button; end win.nav.userList.scroll.update = function() local self = win.nav.userList.scroll; local maxButtons = _G.math.floor(self:GetHeight()/20); local offset = _G.FauxScrollFrame_GetOffset(self); for i=1, #self.buttons do if(i <= maxButtons) then self.buttons[i]:Show(); local index = i + offset; if(index <= #win.USERLIST) then self.buttons[i]:SetUser(win.USERLIST[index]); self.buttons[i]:Show(); if(self.buttons[i].user == win.CONVO) then self.buttons[i]:LockHighlight(); else self.buttons[i]:UnlockHighlight(); end else self.buttons[i]:Hide(); end else self.buttons[i]:Hide(); end end _G.FauxScrollFrame_Update(self, #win.USERLIST, maxButtons, 20); end win.nav.userList.scroll:SetScript("OnShow", function(self) local maxButtons = _G.math.floor(self:GetHeight()/20); if(maxButtons > #self.buttons) then local toCreate = maxButtons - #self.buttons; for i=1, toCreate do self.createButton(); end end self:update(); end); win.nav.userList.scroll:SetScript("OnVerticalScroll", function(self, offset) _G.FauxScrollFrame_OnVerticalScroll(self, offset, 20, win.nav.userList.scroll.update); end); --search bar win.search = CreateFrame("Frame", nil, win); win.search.bg = win.search:CreateTexture(nil, "BACKGROUND"); win.search.bg:SetColorTexture(1,1,1,.25); win.search.bg:SetAllPoints(); win.search:SetPoint("TOPLEFT", win.nav, "TOPRIGHT"); win.search:SetPoint("RIGHT", -18, 0); win.search:SetHeight(30); win.search.clear = CreateFrame("Button", nil, win.search); win.search.clear:SetNormalTexture("Interface\\AddOns\\"..addonTocName.."\\Modules\\Textures\\xNormal"); win.search.clear:SetPushedTexture("Interface\\AddOns\\"..addonTocName.."\\Modules\\Textures\\xPressed"); win.search.clear:SetWidth(16); win.search.clear:SetHeight(16); win.search.clear:SetPoint("RIGHT", -5, 0) win.search.clear:SetScript("OnClick", function(self) win.search.text:ClearFocus(); win.search.text:SetText(""); for key, _ in pairs(win.SEARCHLIST) do win.SEARCHLIST[key] = nil; end win.search.result:Hide(); win.UpdateFilterList(); win.UpdateDisplay(); end); win.search.text = CreateFrame("EditBox", nil, win.search); win.search.text:SetFontObject(_G.ChatFontNormal); win.search.text:SetWidth(200); win.search.text:SetHeight(15); win.search.text:SetPoint("RIGHT", win.search.clear, "LEFT", -5, 0); win.search.text:SetScript("OnEditFocusGained", function(self) self:HighlightText() end); win.search.text:SetScript("OnEditFocusLost", function(self) self:HighlightText(0, 0) end); win.search.text:SetScript("OnEnterPressed", function(self) for key, _ in pairs(win.SEARCHLIST) do win.SEARCHLIST[key] = nil; end local realm, character = string.match(win.USER, "^([^/]+)/?(.*)$"); if(realm and character and history[realm] and history[realm][character]) then for convo, tbl in pairs(history[realm][character]) do for i=1, #tbl do if(searchResult(tbl[i].msg, self:GetText())) then table.insert(win.SEARCHLIST, copyTable(tbl[i], {seq = i})); end end end elseif(realm and history[realm]) then for char, convos in pairs(history[realm]) do for convo, tbl in pairs(convos) do for i=1, #tbl do if(searchResult(tbl[i].msg, self:GetText())) then table.insert(win.SEARCHLIST, copyTable(tbl[i], {seq = i})); end end end end end table.sort(win.SEARCHLIST, function(a, b) if (a.seq and b.seq) then return tonumber(a.time.."."..a.seq) < tonumber(b.time.."."..b.seq) end return a.time < b.time; end); if(#win.SEARCHLIST > 0) then win.search.result:SetText(_G.format(L["Search resulted in %d |4message:messages;."], #win.SEARCHLIST)) else win.search.result:SetText("|cffff0000"..L["No results found!"].."|r"); end win.search.result:Show(); self:ClearFocus(); win.UpdateFilterList(); win.UpdateDisplay(); end); options.AddFramedBackdrop(win.search.text); win.search.text:SetAutoFocus(false); win.search.text:SetScript("OnEscapePressed", function(self) self:ClearFocus() end); win.search.label = win.search:CreateFontString(nil, "OVERLAY", "ChatFontNormal"); win.search.label:SetText(L["Search"]..":"); win.search.label:SetTextColor(_G.GameFontNormal:GetTextColor()); win.search.label:SetPoint("RIGHT", win.search.text, "LEFT", -5, 0); win.search.result = win.search:CreateFontString(nil, "OVERLAY", "ChatFontSmall"); win.search.result:SetPoint("LEFT"); win.search.result:SetPoint("RIGHT", win.search.label, "LEFT", -5, 0); win.search.result:Hide(); --content frame win.content = CreateFrame("Frame", nil, win); win.content.border = win.content:CreateTexture(nil, "BACKGROUND"); win.content.border:SetColorTexture(1,1,1,.25); win.content.border:SetPoint("BOTTOMLEFT"); win.content.border:SetPoint("BOTTOMRIGHT"); win.content.border:SetHeight(1); win.content:SetPoint("TOPLEFT", win.search, "BOTTOMLEFT"); win.content:SetPoint("BOTTOMRIGHT", win, "BOTTOMRIGHT", -18, 40); win.content.tabs = {}; win.content.createTab = function(self, index) local tab = CreateFrame("Button", nil, self); tab.index = index; tab.frame = ViewTypes[index].frame; tab:SetHeight(20); tab.text = tab:CreateFontString(nil, "OVERLAY", "ChatFontSmall"); tab.text:SetAllPoints(); tab.text:SetText(ViewTypes[index].text); tab.bg = tab:CreateTexture(nil, "BACKGROUND"); tab.bg:SetColorTexture(1, 1, 1, .25); tab.bg:SetAllPoints(); tab:SetWidth(tab.text:GetStringWidth() + 16); if(#self.tabs > 0) then tab:SetPoint("TOPLEFT", self.tabs[#self.tabs], "TOPRIGHT",2 ,0); else tab:SetPoint("TOPLEFT", self, "BOTTOMLEFT", 5, 0); end tab:SetScript("OnClick", function(self) for i=1, #win.content.tabs do if(win.progressBar:IsVisible()) then win.progressBar.delete:Click(); end if(self.index == i) then win.content.tabs[i]:SetAlpha(1); win.TAB = self.index; if(self.frame == "chatFrame") then win.content.chatFrame:Show(); win.content.textFrame:Hide(); else win.content.textFrame:Show(); win.content.chatFrame:Hide(); end else win.content.tabs[i]:SetAlpha(.5); end end win:UpdateDisplay(); end); table.insert(self.tabs, tab); end for i=1, #ViewTypes do win.content:createTab(i); end win.content.chatFrame = CreateFrame("ScrollingMessageFrame", "WIM3_HistoryChatFrame", win.content); win.content.chatFrame:SetPoint("TOPLEFT", 4, -4); win.content.chatFrame:SetPoint("BOTTOMRIGHT", -30, 4); win.content.chatFrame:SetFontObject("ChatFontNormal"); win.content.chatFrame:EnableMouse(true); win.content.chatFrame:EnableMouseWheel(true); win.content.chatFrame:SetJustifyH("LEFT"); win.content.chatFrame:SetFading(false); win.content.chatFrame:SetMaxLines(800); win.content.chatFrame.update = function(self) if(self:AtTop()) then self.up:Disable(); else self.up:Enable(); end if(self:AtBottom()) then self.down:Disable(); else self.down:Enable(); end end win.content.chatFrame:SetScript("OnShow", function(self) self:update(); end); win.content.chatFrame:SetScript("OnMouseWheel", function(self, ...) if(select(1, ...) > 0) then if( _G.IsControlKeyDown() ) then self:ScrollToTop(); else if( _G.IsShiftKeyDown() ) then self:PageUp(); else self:ScrollUp(); end end else if( _G.IsControlKeyDown() ) then self:ScrollToBottom(); else if( _G.IsShiftKeyDown() ) then self:PageDown(); else self:ScrollDown(); end end end self:update(); end); win.content.chatFrame:SetScript("OnHyperlinkClick", _G.ChatFrame_OnHyperlinkShow); win.content.chatFrame.up = CreateFrame("Button", nil, win.content.chatFrame); win.content.chatFrame.up:SetWidth(28); win.content.chatFrame.up:SetHeight(28); win.content.chatFrame.up:SetNormalTexture("Interface\\ChatFrame\\UI-ChatIcon-ScrollUp-Up"); win.content.chatFrame.up:SetPushedTexture("Interface\\ChatFrame\\UI-ChatIcon-ScrollUp-Down"); win.content.chatFrame.up:SetDisabledTexture("Interface\\ChatFrame\\UI-ChatIcon-ScrollUp-Disabled"); win.content.chatFrame.up:SetHighlightTexture("Interface\\Buttons\\UI-Common-MouseHilight", "ADD"); win.content.chatFrame.up:SetPoint("TOPRIGHT", 30 ,0); win.content.chatFrame.up:SetScript("OnClick", function(self) local obj = self:GetParent(); if( _G.IsControlKeyDown() ) then obj:ScrollToTop(); else if( _G.IsShiftKeyDown() ) then obj:PageUp(); else obj:ScrollUp(); end end obj:update(); end); win.content.chatFrame.down = CreateFrame("Button", nil, win.content.chatFrame); win.content.chatFrame.down:SetWidth(28); win.content.chatFrame.down:SetHeight(28); win.content.chatFrame.down:SetNormalTexture("Interface\\ChatFrame\\UI-ChatIcon-ScrollDown-Up"); win.content.chatFrame.down:SetPushedTexture("Interface\\ChatFrame\\UI-ChatIcon-ScrollDown-Down"); win.content.chatFrame.down:SetDisabledTexture("Interface\\ChatFrame\\UI-ChatIcon-ScrollDown-Disabled"); win.content.chatFrame.down:SetHighlightTexture("Interface\\Buttons\\UI-Common-MouseHilight", "ADD"); win.content.chatFrame.down:SetPoint("BOTTOMRIGHT", 30 ,-4); win.content.chatFrame.down:SetScript("OnClick", function(self) local obj = self:GetParent(); if( _G.IsControlKeyDown() ) then obj:ScrollToBottom(); else if( _G.IsShiftKeyDown() ) then obj:PageDown(); else obj:ScrollDown(); end end obj:update(); end); win.content.textFrame = CreateFrame("ScrollFrame", "WIM3_HistoryTextFrame", win.content, "UIPanelScrollFrameTemplate"); win.content.textFrame:SetPoint("TOPLEFT", win.content, "TOPLEFT", 4, -4); win.content.textFrame:SetPoint("BOTTOMRIGHT", -25, 4); win.content.textFrame.text = CreateFrame("EditBox", "WIM3_HistoryTextFrameText", win.content.textFrame); win.content.textFrame.text:SetFontObject(_G.ChatFontNormal); win.content.textFrame.text:SetMultiLine(true); win.content.textFrame:SetScrollChild(win.content.textFrame.text); win.content.textFrame.text:SetWidth(win.content.textFrame:GetWidth()); win.content.textFrame.text:SetHeight(200); win.content.textFrame.text:SetAutoFocus(false); win.content.textFrame.text:SetScript("OnEscapePressed", function(self) self:ClearFocus() end); win.content.textFrame.text:SetScript("OnTextChanged", function(self) win.content.textFrame:UpdateScrollChildRect(); end); win.content.textFrame.text.AddMessage = function(self, msg, r, g, b) local color; --if(r and g and b) then -- color = RGBPercentToHex(r, g, b); --end msg = msg:gsub("|c[0-9A-Fa-f][0-9A-Fa-f][0-9A-Fa-f][0-9A-Fa-f][0-9A-Fa-f][0-9A-Fa-f][0-9A-Fa-f][0-9A-Fa-f]", ""); msg = msg:gsub("|r", ""); --self:SetText(self:GetText()..(color and "|cff"..color or "")..msg..(color and "|r" or "").."\n"); self:SetText(self:GetText()..msg.."\n"); end; --resize win.resize = CreateFrame("Button", nil, win); win.resize:SetNormalTexture("Interface\\AddOns\\"..addonTocName.."\\Skins\\Default\\resize"); win.resize:SetHighlightTexture("Interface\\AddOns\\"..addonTocName.."\\Skins\\Default\\resize", "ADD"); win.resize:SetWidth(20); win.resize:SetHeight(20); win.resize:SetPoint("BOTTOMRIGHT", -11, 11); win.resize:SetScript("OnMouseDown", function(self) self:GetParent().isSizing = true; self:GetParent():SetResizable(true); self:GetParent():StartSizing("BOTTOMRIGHT"); self:SetScript("OnUpdate", function(self) win.nav.userList.scroll:update(); end); end); win.resize:SetScript("OnMouseUp", function(self) self:SetScript("OnUpdate", nil); self:GetParent().isSizing = false; self:GetParent():StopMovingOrSizing(); win.nav.userList.scroll:Hide(); win.nav.userList.scroll:Show(); end); win.USER = env.realm.."/"..env.character; win.USERLIST = {}; win.CONVO = ""; win.CONVOLIST = {}; win.FILTER = ""; win.FILTERLIST = {}; win.SEARCHLIST = {}; win.SelectConvo = function(self, convo) win.search.text:SetText(""); win.search.result:Hide(); for k, _ in pairs(win.SEARCHLIST) do win.SEARCHLIST[k] = nil; end win.CONVO = convo; win.FILTER = ""; win.UpdateConvoList(); win.UpdateFilterList(); win.UpdateDisplay(); end win.UpdateDisplay = function(self) if(win.displayUpdate) then win.displayUpdate:Show(); end end win.UpdateFilterList = function(self) for i=1, #win.FILTERLIST do win.FILTERLIST[i] = nil; end local theList = #win.SEARCHLIST > 0 and win.SEARCHLIST or win.CONVOLIST; for i=1, #theList do local t = theList[i].time; local tbl = date("*t", t); t = time{year=tbl.year, month=tbl.month, day=tbl.day, hour=0}; addToTableUnique(win.FILTERLIST, t); win.FILTER = t; end if(#win.FILTERLIST > 0) then table.insert(win.FILTERLIST, 1, L["Show All"]); end win.nav.filters.scroll:Hide(); win.nav.filters.scroll:Show(); end win.UpdateConvoList = function(self) for i=1, #win.CONVOLIST do win.CONVOLIST[i] = nil; end local realm, character = string.match(win.USER, "^([^/]+)/?(.*)$"); if(realm and character and history[realm] and history[realm][character]) then local tbl = history[realm][character][win.CONVO]; if tbl then for i=1, #tbl do table.insert(win.CONVOLIST, copyTable(tbl[i], {seq = i})); end else ShowHistoryViewer() end elseif(realm and history[realm]) then for char, tbl in pairs(history[realm]) do if(tbl[win.CONVO]) then for i=1, #tbl[win.CONVO] do table.insert(win.CONVOLIST, copyTable(tbl[win.CONVO][i], {seq = i})); end end end end table.sort(win.CONVOLIST, function(a, b) if (a.seq and b.seq) then return tonumber(a.time.."."..a.seq) < tonumber(b.time.."."..b.seq) end return a.time < b.time; end); end win.UpdateUserList = function(self) for i=1, #win.USERLIST do win.USERLIST[i] = nil; end local realm, character = string.match(win.USER, "^([^/]+)/?(.*)$"); if(realm and character and history[realm] and history[realm][character]) then local tbl = history[realm][character]; for convo, t in pairs(tbl) do ChannelCache[convo] = t.info and t.info.channelNumber or nil; convo = (t.info and t.info.chat and "*" or "")..convo addToTableUnique(win.USERLIST, convo..(t.info and t.info.gm and "*" or "")); end elseif(realm and (not character or character == "") and history[realm]) then for char, tbl in pairs(history[realm]) do for convo, t in pairs(tbl) do ChannelCache[convo] = t.info and t.info.channelNumber or nil; convo = (t.info and t.info.chat and "*" or "")..convo addToTableUnique(win.USERLIST, convo..(t.info and t.info.gm and "*" or "")); end end end table.sort(win.USERLIST); win.nav.userList.scroll:Hide(); win.nav.userList.scroll:Show(); if(#win.USERLIST>0) then if(not win.SELECT) then win.nav.userList.scroll.buttons[1]:Click(); else win.SELECT = nil; end else win.SELECT = nil; win:SelectConvo(""); end end win.progressBar = CreateFrame("Frame", nil, win.content); win.progressBar:SetFrameStrata("TOOLTIP"); win.progressBar:SetWidth(300); win.progressBar:SetHeight(65); win.progressBar:SetPoint("CENTER", 0, 50); options.AddFramedBackdrop(win.progressBar); win.progressBar.backdrop.bg:SetColorTexture(0, 0, 0, 1); win.progressBar.bar = CreateFrame("Frame", nil, win.progressBar); options.AddFramedBackdrop(win.progressBar.bar); win.progressBar.bar:SetWidth(win.progressBar:GetWidth()-50); win.progressBar.bar:SetHeight(15); win.progressBar.bar:SetPoint("CENTER", -10, -5); win.progressBar.bar.bg = win.progressBar.bar:CreateTexture(nil, "OVERLAY"); win.progressBar.bar.bg:SetColorTexture(1,1,1, .5); win.progressBar.bar.bg:SetPoint("TOPLEFT"); win.progressBar.bar.bg:SetPoint("BOTTOMLEFT"); win.progressBar.text = win.progressBar:CreateFontString(nil, "OVERLAY", "ChatFontNormal"); win.progressBar.text:SetPoint("BOTTOMLEFT", win.progressBar.bar, "TOPLEFT", 0, 5); win.progressBar.text:SetText(L["Loading History"].."..."); win.progressBar:SetScript("OnShow", function(self) win.content.chatFrame:SetAlpha(.5); win.content.textFrame:SetAlpha(.5); end); win.progressBar:SetScript("OnHide", function(self) win.content.chatFrame:SetAlpha(1); win.content.textFrame:SetAlpha(1); end); win.progressBar.delete = CreateFrame("Button", nil, win.progressBar); win.progressBar.delete:SetNormalTexture("Interface\\AddOns\\"..addonTocName.."\\Modules\\Textures\\xNormal"); win.progressBar.delete:SetPushedTexture("Interface\\AddOns\\"..addonTocName.."\\Modules\\Textures\\xPressed"); win.progressBar.delete:SetWidth(16); win.progressBar.delete:SetHeight(16); win.progressBar.delete:SetPoint("LEFT", win.progressBar.bar, "RIGHT", 4, 0); win.progressBar.delete:SetScript("OnClick", function(self) win.displayUpdate:Hide(); end); win.content.tabs[1]:Click(); return win; end local HistoryViewer; local function createDisplayUpdate() -- displayUpdate loads messages into the correct content frames avoiding lag from system ops. local displayUpdate = CreateFrame("Frame"); displayUpdate:Hide(); displayUpdate.firstPass = true; displayUpdate.tmpTable = {}; displayUpdate.Process = function(self) self.i = self.i or 1; if(not self.curList or not self.curList[self.i]) then self:Hide(); return; end HistoryViewer.progressBar.bar.bg:SetWidth(HistoryViewer.progressBar.bar:GetWidth()*self.i/#self.curList); -- clear tmpTable for k, _ in pairs(self.tmpTable) do self.tmpTable[k] = nil; end -- load tmpTable for k, v in pairs(self.curList[self.i]) do self.tmpTable[k] = v; end if(self.filter) then if(self.min <= self.tmpTable.time and self.max > self.tmpTable.time) then ViewTypes[HistoryViewer.TAB].func(self.frame, self.tmpTable); else self.i = self.i + 1; self:Process(); return; end else ViewTypes[HistoryViewer.TAB].func(self.frame, self.tmpTable); end self.i = self.i + 1; end; displayUpdate:SetScript("OnUpdate", function(self, elapsed) if(self.firstPass) then HistoryViewer.content.chatFrame:Clear(); HistoryViewer.content.chatFrame.lastDate = nil; HistoryViewer.content.chatFrame:SetIndentedWordWrap(db.wordwrap_indent); HistoryViewer.content.textFrame.text:SetText(""); HistoryViewer.content.textFrame.text.lastDate = nil; self.firstPass = nil; end self:Process() end); displayUpdate:SetScript("OnHide", function(self) self.firstPass = true; self.i = 1; HistoryViewer.progressBar:Hide(); HistoryViewer.content.chatFrame:update(); local buttons = HistoryViewer.nav.userList.scroll.buttons; for i=1, #buttons do buttons[i]:Enable(); buttons[i].delete:Enable(); end buttons = HistoryViewer.nav.filters.scroll.buttons; for i=1, #buttons do buttons[i]:Enable(); end end); displayUpdate:SetScript("OnShow", function(self) local buttons = HistoryViewer.nav.userList.scroll.buttons; for i=1, #buttons do buttons[i]:Disable(); buttons[i].delete:Disable(); end buttons = HistoryViewer.nav.filters.scroll.buttons; for i=1, #buttons do buttons[i]:Disable(); end HistoryViewer.progressBar:Show(); self.curList = #HistoryViewer.SEARCHLIST > 0 and HistoryViewer.SEARCHLIST or HistoryViewer.CONVOLIST; self.frame = ViewTypes[HistoryViewer.TAB].frame == "chatFrame" and HistoryViewer.content.chatFrame or HistoryViewer.content.textFrame.text; self.filter = _G.type(HistoryViewer.FILTER) == "number" or nil; self.min, self.max = 0, 0; if(self.filter) then local t = HistoryViewer.FILTER; local tbl = date("*t", t); t = time{year=tbl.year, month=tbl.month, day=tbl.day, hour=0}; self.min, self.max = t, t+dDay; end end); return displayUpdate; end local colorWhite = {r=1, g=1, b=1}; local chatFrameMsgId = -1; table.insert(ViewTypes, { text = L["Chat View"], frame = "chatFrame", func = function(frame, msg) local color; if(msg.type == 1) then color = db.displayColors[msg.inbound and "wispIn" or "wispOut"]; nextColor.r, nextColor.g, nextColor.b = color.r, color.g, color.b; elseif(msg.type == 2) then if(msg.event == CHANNEL) then color = _G.ChatTypeInfo["CHANNEL"..msg.channelNumber]; else color = _G.ChatTypeInfo[msg.event]; end color = color or colorWhite; nextColor.r, nextColor.g, nextColor.b = color.r, color.g, color.b; end frame.nextStamp = msg.time; frame:AddMessage(applyStringModifiers(applyMessageFormatting(frame, "CHAT_MSG_"..(msg.event or "WHISPER"), msg.msg, msg.from, nil, nil, nil, nil, 0, msg.channelName and ChannelCache[msg.channelName], msg.channelName, nil, chatFrameMsgId, "0x0300000000000000"), frame), color.r, color.g, color.b); chatFrameMsgId = chatFrameMsgId > -1000 and chatFrameMsgId - 1 or -1; end }); table.insert(ViewTypes, { text = L["Text View"], frame = "textFrame", func = function(frame, msg) frame.noEscapedStrings = true; if(msg.type == 1 or msg.type == 2) then local color = db.displayColors[msg.inbound and "wispIn" or "wispOut"]; nextColor.r, nextColor.g, nextColor.b = color.r, color.g, color.b; frame.nextStamp = msg.time; frame:AddMessage(applyStringModifiers(applyMessageFormatting(frame, "CHAT_MSG_WHISPER", msg.msg, msg.from, nil, nil, nil, nil, 0, msg.channelName and ChannelCache[msg.channelName], msg.channelName, "0x0300000000000000"), frame), color.r, color.g, color.b) end end }); -- stewart table.insert(ViewTypes, { text = L["BBCode"], frame = "textFrame", func = function(frame, msg) local color; if(msg.type == 1) then color = db.displayColors[msg.inbound and "wispIn" or "wispOut"]; nextColor.r, nextColor.g, nextColor.b = color.r, color.g, color.b; elseif(msg.type == 2) then if(msg.event == CHANNEL) then color = _G.ChatTypeInfo["CHANNEL"..msg.channelNumber]; else color = _G.ChatTypeInfo[msg.event]; end color = color or colorWhite; nextColor.r, nextColor.g, nextColor.b = color.r, color.g, color.b; end frame.noEscapedStrings = nil; frame.noEmoticons = true; frame.nextStamp = msg.time; local chatColor = "[color=#"..RGBPercentToHex(color.r, color.g, color.b).."]"; local chatColorPattern = "%[color%=%#"..RGBPercentToHex(color.r, color.g, color.b).."%]%s*%[%/color%]"; msg = applyMessageFormatting(frame, "CHAT_MSG_WHISPER", msg.msg, msg.from) msg = applyStringModifiers(msg, frame); msg = msg:gsub("|c[0-9A-Fa-f][0-9A-Fa-f]([0-9A-Fa-f][0-9A-Fa-f][0-9A-Fa-f][0-9A-Fa-f][0-9A-Fa-f][0-9A-Fa-f])|Hwim_url:([^|]*)|h.-|h|r", "[/color][url=%2][color=#%1]%2[/color][/url]"..chatColor); msg = msg:gsub("|c[0-9A-Fa-f][0-9A-Fa-f]([0-9A-Fa-f][0-9A-Fa-f][0-9A-Fa-f][0-9A-Fa-f][0-9A-Fa-f][0-9A-Fa-f])", "[color=#%1]"); msg = msg:gsub("|r", "[/color]"); msg = msg:gsub("(%[color%=%#[0-9A-Fa-f]+%])|Hitem:(%d+)[:%d]*|h([^|]+)|h(%[%/color%])", "[/color][url=http://www.wowhead.com/?item=%2]%1%3%4[/url]"..chatColor); msg = chatColor..msg.."[/color]"; msg = msg:gsub("(%[color%=%#[0-9A-Fa-f]+%])(%[color%=%#[0-9A-Fa-f]+%])(.-)(%[%/color%])", "%2%3%4%1"); msg = msg:gsub(chatColorPattern, ""); frame:AddMessage(msg, color.r, color.g, color.b) end }); table.insert(ViewTypes, { text = "HTML", frame = "textFrame", func = function(frame, msg) local color; if(msg.type == 1) then color = db.displayColors[msg.inbound and "wispIn" or "wispOut"]; nextColor.r, nextColor.g, nextColor.b = color.r, color.g, color.b; elseif(msg.type == 2) then if(msg.event == CHANNEL) then color = _G.ChatTypeInfo["CHANNEL"..msg.channelNumber]; else color = _G.ChatTypeInfo[msg.event]; end color = color or colorWhite; nextColor.r, nextColor.g, nextColor.b = color.r, color.g, color.b; end frame.noEscapedStrings = nil; frame.noEmoticons = true; frame.nextStamp = msg.time; local chatColor = ""; local chatColorPattern = "%%s*%<%/font%>"; msg = applyMessageFormatting(frame, "CHAT_MSG_WHISPER", msg.msg, msg.from) msg = applyStringModifiers(msg, frame); -- html escapes msg = msg:gsub("&", "&"); msg = msg:gsub("<", "<"); msg = msg:gsub(">", ">"); msg = msg:gsub("\"", """); -- color & URL handling... msg = msg:gsub("|c[0-9A-Fa-f][0-9A-Fa-f]([0-9A-Fa-f][0-9A-Fa-f][0-9A-Fa-f][0-9A-Fa-f][0-9A-Fa-f][0-9A-Fa-f])|Hwim_url:([^|]*)|h.-|h|r", "%2"..chatColor); msg = msg:gsub("|c[0-9A-Fa-f][0-9A-Fa-f]([0-9A-Fa-f][0-9A-Fa-f][0-9A-Fa-f][0-9A-Fa-f][0-9A-Fa-f][0-9A-Fa-f])", ""); msg = msg:gsub("|r", ""); msg = msg:gsub("(%)|Hitem:(%d+)[:%d]*|h([^|]+)|h(%[%/color%])", "%1%3%4"..chatColor); msg = chatColor..msg..""; msg = msg:gsub("(%)(%)(.-)(%<%/font%>)", "%2%3%4%1"); msg = "
"..msg:gsub(chatColorPattern, ""); frame:AddMessage(msg, color.r, color.g, color.b) end }); function ShowHistoryViewer(user) local exists = HistoryViewer if(HistoryViewer and not user and HistoryViewer:IsShown()) then HistoryViewer:Hide(); return; end HistoryViewer = HistoryViewer or createHistoryViewer(); HistoryViewer.displayUpdate = HistoryViewer.displayUpdate or createDisplayUpdate(); if(user) then HistoryViewer.USER = env.realm.."/"..env.character; HistoryViewer.SELECT = user; HistoryViewer.nav:Hide(); HistoryViewer.nav:Show(); HistoryViewer.UpdateUserList(); HistoryViewer:SelectConvo(user); -- DisplayTutorial(L["WIM History Viewer"], L["WIM History Viewer can be accessed any time by typing:"].." \n|cff69ccf0/wim history|r"); end HistoryViewer:Show(); if not exists and not user then --force update on first show without user HistoryViewer:Hide(); HistoryViewer:Show(); end end RegisterSlashCommand("history", function() ShowHistoryViewer(); end, L["Display history viewer."])