-- read namespace from global env local _G = _G local Grichelde = _G.Grichelde or {} -- constants and upvalues Grichelde.LOG_LEVEL = { DEBUG = 1, TRACE = 2 } -- chat input in client is limited to 255 bytes (unicode length is less) Grichelde.INPUT_LIMIT = 255 -- safety measure in case malformed user input causes infinite loops on string parsing Grichelde.ENDLESS_LOOP_LIMIT = 10000 Grichelde.MAPPING_OFFSET = 10 Grichelde.MINIMAP_ENABLED = 1.0 Grichelde.MINIMAP_DARKENDED = 0.5 Grichelde.ICONS = { MOVE_UP = "Interface\\MainMenuBar\\UI-MainMenu-ScrollUpButton-Up", MOVE_UP_DISABLED = "Interface\\MainMenuBar\\UI-MainMenu-ScrollUpButton-Disabled", MOVE_DOWN = "Interface\\MainMenuBar\\UI-MainMenu-ScrollDownButton-Up", MOVE_DOWN_DISABLED = "Interface\\MainMenuBar\\UI-MainMenu-ScrollDownButton-Disabled", DELETE = "Interface\\Buttons\\UI-Panel-MinimizeButton-Up", DELETE_DISABLED = "Interface\\Buttons\\UI-Panel-MinimizeButton-Disabled", } -- colors: Grichelde.COLORS = { NORMAL = _G.NORMAL_FONT_COLOR, HIGHLIGHT = _G.HIGHLIGHT_FONT_COLOR, RED = _G.RED_FONT_COLOR, GREEN = _G.GREEN_FONT_COLOR, } Grichelde.COLOR_CODES = { PREFIX = "|c00FFAA00", -- https://github.com/stoneharry/Misc-WoW-Stuff/blob/master/EoC%20Interface/FrameXML/Constants.lua NORMAL = _G.NORMAL_FONT_COLOR_CODE or "|cffffd200", HIGHLIGHT = _G.HIGHLIGHT_FONT_COLOR_CODE or "|cffffffff", RED = _G.RED_FONT_COLOR_CODE or "|cffff2020", GREEN = _G.GREEN_FONT_COLOR_CODE or "|cff20ff20", LIGHTGRAY = "|cffC0C0C0", GRAY = _G.GRAY_FONT_COLOR_CODE or "|cff808080", DARKGRAY = "|cff404040", YELLOW = _G.YELLOW_FONT_COLOR_CODE or "|cffffff00", LIGHTYELLOW = _G.LIGHTYELLOW_FONT_COLOR_CODE or "|cffffff9a", ORANGE = _G.ORANGE_FONT_COLOR_CODE or "|cffff7f3f", BLUE = "|cff0000ff", HYPERLINK = "|cff4040ff", CLOSE = _G.FONT_COLOR_CODE_CLOSE or "|r", } Grichelde.SLASH_COMMANDS = { "gri", "grichelde" } Grichelde.SUPPORTED_CHAT_COMMANDS = { ["/s"] = "SAY", ["/say"] = "SAY", ["/sprechen"] = "SAY", ["/c"] = "CHANNEL", ["/csay"] = "CHANNEL", ["/e"] = "EMOTE", ["/em"] = "EMOTE", ["/me"] = "EMOTE", ["/emote"] = "EMOTE", ["/y"] = "YELL", ["/yell"] = "YELL", ["/sh"] = "YELL", ["/shout"] = "YELL", ["/schreien"] = "YELL", ["/sch"] = "YELL", ["/p"] = "PARTY", ["/party"] = "PARTY", ["/gruppe"] = "PARTY", ["/pl"] = "PARTY", ["/partyleader"] = "PARTY", ["/g"] = "GUILD", ["/gc"] = "GUILD", ["/guild"] = "GUILD", ["/gilde"] = "GUILD", ["/o"] = "OFFICER", ["/osay"] = "OFFICER", ["/officer"] = "OFFICER", ["/offizier"] = "OFFICER", ["/raid"] = "RAID", ["/rsay"] = "RAID", ["/rl"] = "RAID", ["/rsay"] = "RAID", ["/raidleader"] = "RAID", ["/schlachtzug"] = "RAID", ["/rw"] = "RAID_WARNING", ["/raidwarning"] = "RAID_WARNING", ["/i"] = "INSTANCE_CHAT", ["/instance"] = "INSTANCE_CHAT", ["/instanz"] = "INSTANCE_CHAT", ["/bg"] = "BATTLEGROUND", ["/battleground"] = "BATTLEGROUND", ["/schlachfeld"] = "BATTLEGROUND", ["/w"] = "WHISPER", ["/whisper"] = "WHISPER", ["/t"] = "WHISPER", ["/tell"] = "WHISPER", ["/send"] = "WHISPER", ["/tt"] = "WHISPER", ["/r"] = "WHISPER", ["/reply"] = "WHISPER", ["/fl\195\188stern"] = "WHISPER", ["/antworten"] = "WHISPER", } Grichelde.BLIZZ_TYPE_TO_OPTIONS = { ["SAY"] = "say", ["EMOTE"] = "emote", ["YELL"] = "yell", ["PARTY"] = "party", ["GUILD"] = "guild", ["OFFICER"] = "officer", ["RAID"] = "raid", ["RAID_WARNING"] = "raidWarning", ["INSTANCE"] = "instance", ["BATTLEGROUND"] = "battleground", ["WHISPER"] = "whisper", } -- do not replace these patterns -- combined item links in the chat will look like this: |cff9d9d9d|Hitem:3299::::::::20:257::::::|h[Fractured Canine]|h|r Grichelde.IGNORE_PATTERNS = { LINKS = { "|[Cc]%x%x%x%x%x%x%x%x.-|r", -- colored items (or links) "|H.-|h", -- item links (http://www.wowwiki.com/ItemLink) "|T.-|t", -- textures "|K.-|k", -- Battle.net "|n", -- newline }, EMOTES = { "%*.-%*", -- emotes * "%*%*.-%*%*", -- emotes ** "%<.-%>", -- emotes < > }, SUBSTITUTES = { "%%n", -- player's name "%%z", -- player's currnt zone "%%sz", -- player's current sub-zone "%%loc", -- player's map coordinates "%%t", -- name of target "%%f", -- name of focus target "%%m", -- name of mouseover unit "%%p", -- name of player pet "%%tt", -- name of player's target's target }, RAID_TARGETS = { "{rt[1-8]}", -- rumbered raid target icons, localized raid targets are handled differently }, LOCALIZED_RAID_TARGETS = { "Star", "Circle", "Diamond", "Triangle", "Moon", "Square", "Cross", "Skull", }, OOC_BRACKETS = { "%(%(.-%)%)", -- (( ... )) "%(%s*ooc[%:%s].-%)", -- ( ooc ) }, OOC_NO_BRACKETS = { "ooc[%:%s]", }, } local function nilOrEmpty(s) return s == nil or s:trim() == "" end local function spairs(tbl, orderFunc) -- collect the keys local sortedKeys = {} -- for every non-nil value for key, _ in Grichelde.F.pairs(tbl) do Grichelde.F.tInsert(sortedKeys, key) end if (orderFunc ~= nil) then Grichelde.F.tSort(sortedKeys, function(a, b) return orderFunc(sortedKeys, a, b) end) else -- lexicographical order Grichelde.F.tSort(sortedKeys) end -- return the iterator function local it = 0 return function() it = it + 1 if (sortedKeys[it] ~= nil) then return sortedKeys[it], tbl[sortedKeys[it]] else return nil end end end local function tFilter(type, condition, extract) local filtered = {} for key, value in Grichelde.F.pairs(type) do local cond = false if (condition) then local typ = Grichelde.F.type(condition) if (typ == "function") then cond = condition(typ, key, value) elseif (typ == "string") or (typ == "number") then cond = (value == condition) end end if (cond) then local val = value if (extract and (Grichelde.F.type(extract) == "function")) then val = extract(type, key, value) end Grichelde.F.tInsert(filtered, val) end end return filtered end local function tSize(tbl) local size = 0 if (tbl ~= nil) then -- for every non-nil value for _, _ in Grichelde.F.pairs(tbl) do size = size + 1 end end return size end local function tIsEmpty(tbl) if (not tbl) then return true end return Grichelde.F.tNext(tbl) == nil end local function tClone(orig) local orig_type = Grichelde.F.type(orig) local copy if (orig_type == 'table') then copy = {} -- for every non-nil value for orig_key, orig_value in Grichelde.F.tNext, orig, nil do copy[tClone(orig_key)] = tClone(orig_value) end Grichelde.F.setmetatable(copy, tClone(Grichelde.F.getmetatable(orig))) else -- number, string, boolean, etc copy = orig end return copy end local function getNextCharUtf8(word) if ((word == nil) or (Grichelde.F.type(word) ~= "string") or (Grichelde.F.length(word) < 1)) then return nil, nil end local wordLength = Grichelde.F.length(word) local c1 = Grichelde.F.toByte(word, 1) if (c1 > 0) and (c1 <= 127) then -- UTF8-1 return Grichelde.F.bytes2Char(c1), Grichelde.F.sub(word, 2) elseif (c1 >= 194) and (c1 <= 223) then -- UTF8-2 Grichelde.F.assert(wordLength >= 2, "broken UTF-8 character") local c2 = Grichelde.F.toByte(word, 2) return Grichelde.F.bytes2Char(c1, c2), Grichelde.F.sub(word, 3) elseif (c1 >= 224) and (c1 <= 239) then -- UTF8-3 Grichelde.F.assert(wordLength >= 3, "broken UTF-8 character") local c2 = Grichelde.F.toByte(word, 2) local c3 = Grichelde.F.toByte(word, 3) return Grichelde.F.bytes2Char(c1, c2, c3), Grichelde.F.sub(word, 4) elseif (c1 >= 240) and (c1 <= 244) then -- UTF8-4 Grichelde.F.assert(wordLength >= 4, "broken UTF-8 character") local c2 = Grichelde.F.toByte(word, 2) local c3 = Grichelde.F.toByte(word, 3) local c4 = Grichelde.F.toByte(word, 4) return Grichelde.F.bytes2Char(c1, c2, c3, c4), Grichelde.F.sub(word, 5) else return nil, nil end end local function isUtf8MultiByte(word) return Grichelde.F.length(word) ~= Grichelde.F.lengthUtf8(word) end local function getUtf8Sequence(word) if ((word == nil) or (Grichelde.F.type(word) ~= "string") or (Grichelde.F.lengthUtf8(word) ~= 1)) then return nil end --[[ You could use the following code snippet to iterate over UTF-8 sequences (this will simply skip over most invalid codes): for uchar in string.gmatch(ustring, "([%z\1-\127\194-\244][\128-\191]*)") do ... end ]]-- local sequence = "%z" local c1 = Grichelde.F.toByte(word, 1) sequence = sequence .. "\\" .. c1 if (c1 > 0) and (c1 <= 127) then -- UTF8-1 return sequence end local c2 = Grichelde.F.toByte(word, 2) sequence = sequence .. "\\" .. c2 if (c1 >= 194) and (c1 <= 223) then -- UTF8-2 return sequence end local c3 = Grichelde.F.toByte(word, 3) sequence = sequence .. "\\" .. c3 if (c1 >= 224) and (c1 <= 239) then -- UTF8-3 return sequence end local c4 = Grichelde.F.toByte(word, 4) sequence = sequence .. "\\" .. c4 if (c1 >= 240) and (c1 <= 244) then -- UTF8-4 return sequence end return nil end local function getUtf8Table(word) if ((word == nil) or (Grichelde.F.type(word) ~= "string") or (Grichelde.F.lengthUtf8(word) ~= 1)) then return nil end --[[ You could use the following code snippet to iterate over UTF-8 sequences (this will simply skip over most invalid codes): for uchar in string.gmatch(ustring, "([%z\1-\127\194-\244][\128-\191]*)") do ... end ]]-- local tbl = {} local c1 = Grichelde.F.toByte(word, 1) Grichelde.F.tInsert(tbl, "%z\\" .. c1) local c2 = Grichelde.F.toByte(word, 2) if (c1 >= 194) and (c1 <= 223) then -- UTF8-2 Grichelde.F.tInsert(tbl, "\\" .. c2) end local c3 = Grichelde.F.toByte(word, 3) if (c1 >= 224) and (c1 <= 239) then -- UTF8-3 Grichelde.F.tInsert(tbl, "\\" .. c3) end local c4 = Grichelde.F.toByte(word, 4) if (c1 >= 240) and (c1 <= 244) then -- UTF8-4 Grichelde.F.tInsert(tbl, "\\" .. c4) end return tbl end local function isLetter(word) local char = Grichelde.F.getNextCharUtf8(word) return (char ~= nil) and (Grichelde.F.toUpper(char) ~= Grichelde.F.toLower(char)) end local function isNumber(digit) if ((digit == nil) or (Grichelde.F.type(digit) ~= "string") or (Grichelde.F.length(digit) < 1)) then return false else return Grichelde.F.find(digit, "%d+") ~= nil end end local function isUpper(word) if ((word == nil) or (Grichelde.F.type(word) ~= "string") or (Grichelde.F.length(word) < 1)) then return false elseif (Grichelde.F.toUpper(word) == Grichelde.F.toLower(word)) then return false else return word == Grichelde.F.toUpper(word) end end local function isLower(word) if ((word == nil) or (Grichelde.F.type(word) ~= "string") or (Grichelde.F.length(word) < 1)) then return false elseif (Grichelde.F.toUpper(word) == Grichelde.F.toLower(word)) then return false else return word == Grichelde.F.toLower(word) end end local function isCapital(word) if ((word == nil) or (Grichelde.F.type(word) ~= "string") or (Grichelde.F.length(word) < 1)) then return false else local first, rest = Grichelde.F.getNextCharUtf8(word) local isCap = Grichelde.F.isUpper(first) if (rest ~= nil) then return isCap and Grichelde.F.isLower(rest) else return isCap end end end local function capitalize(word) if ((word == nil) or (Grichelde.F.type(word) ~= "string") or (Grichelde.F.length(word) < 1)) then return "" else local first, rest = Grichelde.F.getNextCharUtf8(word) local capital = Grichelde.F.toUpper(first) if (rest ~= nil) then return capital .. Grichelde.F.toLower(rest) else return capital end end end local function color(colour, text) return colour .. text .. Grichelde.COLOR_CODES.CLOSE end local function cPrefix(text) return Grichelde.F.color(Grichelde.COLOR_CODES.PREFIX, text) end local function cYellow(text) return Grichelde.F.color(Grichelde.COLOR_CODES.NORMAL, text) end local function cGray(text) return Grichelde.F.color(Grichelde.COLOR_CODES.GRAY, text) end local function cDarkgray(text) return Grichelde.F.color(Grichelde.COLOR_CODES.DARKGRAY, text) end local function cGreen(text) return Grichelde.F.color(Grichelde.COLOR_CODES.GREEN, text) end local function cOrange(text) return Grichelde.F.color(Grichelde.COLOR_CODES.ORANGE, text) end local function cRed(text) return Grichelde.F.color(Grichelde.COLOR_CODES.RED, text) end local function cHyperlink(text) return Grichelde.F.color(Grichelde.COLOR_CODES.HYPERLINK, text) end -- faster function lookups by mapping to local refs Grichelde.F = { IsAddOnLoaded = _G.IsAddOnLoaded, assert = _G.assert, type = _G.type, print = _G.print, nilOrEmpty = nilOrEmpty, pairs = _G.pairs, ipairs = _G.ipairs, spairs = spairs, tContains = _G.tContains, tFilter = tFilter, tInsert = _G.table.insert, tConcat = _G.table.concat, tSize = tSize, tIsEmpty = tIsEmpty, tSort = _G.table.sort, tClone = tClone, tNext = _G.next, tWipe = _G.wipe, setmetatable = _G.setmetatable, getmetatable = _G.getmetatable, select = _G.select, unpack = _G.unpack, find = _G.string.find, sub = _G.string.sub, gsub = _G.string.gsub, match = _G.strmatch, gmatch = _G.string.gmatch, join = _G.strjoin, split = _G.strsplit, toUpper = _G.strupper, toLower = _G.strlower, getNextCharUtf8 = getNextCharUtf8, isUtf8MultiByte = isUtf8MultiByte, getUtf8Sequence = getUtf8Sequence, getUtf8Table = getUtf8Table, isLetter = isLetter, isNumber = isNumber, isUpper = isUpper, isLower = isLower, isCapital = isCapital, capitalize = capitalize, color = color, cPrefix = cPrefix, cYellow = cYellow, cGray = cGray, cDarkgray = cDarkgray, cGreen = cGreen, cOrange = cOrange, cRed = cRed, cHyperlink = cHyperlink, toByte = _G.string.byte, bytes2Char = _G.string.char, format = _G.string.format, rep = _G.string.rep, trim = _G.strtrim, length = _G.string.len, lengthUtf8 = _G.strlenutf8, toString = _G.tostring, toNumber = _G.tonumber, max = _G.math.max, min = _G.math.min, }