local MAJOR_VERSION = "LibDogTag-3.0" local MINOR_VERSION = tonumber(("@project-date-integer@"):match("%d+")) or 33333333333333 if MINOR_VERSION > _G.DogTag_MINOR_VERSION then _G.DogTag_MINOR_VERSION = MINOR_VERSION end local _G, pairs, table, ipairs, type, tostring, math, GetTime = _G, pairs, table, ipairs, type, tostring, math, GetTime -- #AUTODOC_NAMESPACE DogTag DogTag_funcs[#DogTag_funcs+1] = function(DogTag) local L = DogTag.L local newList, newDict, newSet, del = DogTag.newList, DogTag.newDict, DogTag.newSet, DogTag.del local helpFrame local wow_ver = select(4, GetBuildInfo()) local wow_1000 = wow_ver >= 100000 --[[ Notes: This opens the in-game documentation, which provides information to users on syntax as well as the available tags and modifiers. Example: LibStub("LibDogTag-3.0"):OpenHelp() ]] function DogTag:OpenHelp() helpFrame = CreateFrame("Frame", MAJOR_VERSION .. "_HelpFrame", UIParent, BackdropTemplateMixin and "BackdropTemplate" or nil) helpFrame:SetWidth(600) helpFrame:SetHeight(300) helpFrame:SetPoint("CENTER", UIParent, "CENTER") helpFrame:EnableMouse(true) helpFrame:SetMovable(true) helpFrame:SetResizable(true) if helpFrame.SetMinResize then helpFrame:SetMinResize(600, 300) elseif helpFrame.SetResizeBounds then helpFrame:SetResizeBounds(600, 300) end helpFrame:SetFrameLevel(50) helpFrame:SetFrameStrata("FULLSCREEN_DIALOG") local bg = newDict( 'bgFile', [[Interface\DialogFrame\UI-DialogBox-Background]], 'edgeFile', [[Interface\DialogFrame\UI-DialogBox-Border]], 'tile', true, 'tileSize', 32, 'edgeSize', 32, 'insets', newDict( 'left', 5, 'right', 6, 'top', 5, 'bottom', 6 ) ) helpFrame:SetBackdrop(bg) bg.insets = del(bg.insets) bg = del(bg) helpFrame:SetBackdropColor(0, 0, 0) helpFrame:SetClampedToScreen(true) local header = CreateFrame("Frame", helpFrame:GetName() .. "_Header", helpFrame) helpFrame.header = header header:SetHeight(34.56) header:SetClampedToScreen(true) local left = header:CreateTexture(header:GetName() .. "_TextureLeft", "ARTWORK") header.left = left left:SetPoint("TOPLEFT") left:SetPoint("BOTTOMLEFT") left:SetWidth(11.54) left:SetTexture([[Interface\DialogFrame\UI-DialogBox-Header]]) left:SetTexCoord(0.235, 0.28, 0.04, 0.58) local right = header:CreateTexture(header:GetName() .. "_TextureRight", "ARTWORK") header.right = right right:SetPoint("TOPRIGHT") right:SetPoint("BOTTOMRIGHT") right:SetWidth(11.54) right:SetTexture([[Interface\DialogFrame\UI-DialogBox-Header]]) right:SetTexCoord(0.715, 0.76, 0.04, 0.58) local center = header:CreateTexture(header:GetName() .. "_TextureCenter", "ARTWORK") header.center = center center:SetPoint("TOPLEFT", left, "TOPRIGHT") center:SetPoint("BOTTOMRIGHT", right, "BOTTOMLEFT") center:SetTexture([[Interface\DialogFrame\UI-DialogBox-Header]]) center:SetTexCoord(0.28, 0.715, 0.04, 0.58) local closeButton = CreateFrame("Button", helpFrame:GetName() .. "_CloseButton", helpFrame, "UIPanelCloseButton") helpFrame.closeButton = closeButton closeButton:SetFrameLevel(helpFrame:GetFrameLevel()+5) closeButton:SetScript("OnClick", function(this) this:GetParent():Hide() end) closeButton:SetPoint("TOPRIGHT", helpFrame, "TOPRIGHT", -5, -5) local isDragging header:EnableMouse(true) header:RegisterForDrag("LeftButton") header:SetScript("OnDragStart", function(this) isDragging = true this:GetParent():StartMoving() end) header:SetScript("OnDragStop", function(this) isDragging = false this:GetParent():StopMovingOrSizing() end) local titleText = header:CreateFontString(header:GetName() .. "_FontString", "OVERLAY", "GameFontNormal") helpFrame.titleText = titleText titleText:SetText(L["DogTag Help"]) titleText:SetPoint("CENTER", helpFrame, "TOP", 0, -8) titleText:SetHeight(26) titleText:SetShadowColor(0, 0, 0) titleText:SetShadowOffset(1, -1) header:SetPoint("LEFT", titleText, "LEFT", -32, 0) header:SetPoint("RIGHT", titleText, "RIGHT", 32, 0) local sizer_se = CreateFrame("Frame", helpFrame:GetName() .. "_SizerSoutheast", helpFrame) helpFrame.sizer_se = sizer_se sizer_se:SetPoint("BOTTOMRIGHT", helpFrame, "BOTTOMRIGHT", 0, 0) sizer_se:SetWidth(25) sizer_se:SetHeight(25) sizer_se:EnableMouse(true) sizer_se:RegisterForDrag("LeftButton") sizer_se:SetScript("OnDragStart", function(this) isDragging = true this:GetParent():StartSizing("BOTTOMRIGHT") end) sizer_se:SetScript("OnDragStop", function(this) isDragging = false this:GetParent():StopMovingOrSizing() end) local line1 = sizer_se:CreateTexture(sizer_se:GetName() .. "_Line1", "BACKGROUND") line1:SetWidth(14) line1:SetHeight(14) line1:SetPoint("BOTTOMRIGHT", -10, 10) line1:SetTexture("Interface\\Tooltips\\UI-Tooltip-Border") local x = 0.1 * 14/17 line1:SetTexCoord(1/32 - x, 0.5, 1/32, 0.5 + x, 1/32, 0.5 - x, 1/32 + x, 0.5) local line2 = sizer_se:CreateTexture(sizer_se:GetName() .. "_Line2", "BACKGROUND") line2:SetWidth(11) line2:SetHeight(11) line2:SetPoint("BOTTOMRIGHT", -10, 10) line2:SetTexture("Interface\\Tooltips\\UI-Tooltip-Border") local x = 0.1 * 11/17 line2:SetTexCoord(1/32 - x, 0.5, 1/32, 0.5 + x, 1/32, 0.5 - x, 1/32 + x, 0.5) local line3 = sizer_se:CreateTexture(sizer_se:GetName() .. "_Line3", "BACKGROUND") line3:SetWidth(8) line3:SetHeight(8) line3:SetPoint("BOTTOMRIGHT", -10, 10) line3:SetTexture("Interface\\Tooltips\\UI-Tooltip-Border") local x = 0.1 * 8/17 line3:SetTexCoord(1/32 - x, 0.5, 1/32, 0.5 + x, 1/32, 0.5 - x, 1/32 + x, 0.5) local treeView = CreateFrame("Frame", helpFrame:GetName() .. "_TreeView", helpFrame, BackdropTemplateMixin and "BackdropTemplate" or nil) helpFrame.treeView = treeView local bg = newDict( 'bgFile', [[Interface\Buttons\WHITE8X8]], 'edgeFile', [[Interface\Tooltips\UI-Tooltip-Border]], 'tile', true, 'tileSize', 16, 'edgeSize', 16, 'insets', newDict( 'left', 3, 'right', 3, 'top', 3, 'bottom', 3 ) ) treeView:SetBackdrop(bg) bg.insets = del(bg.insets) bg = del(bg) treeView:SetBackdropBorderColor(0.6, 0.6, 0.6) treeView:SetBackdropColor(0, 0, 0) local scrollFrame = CreateFrame("ScrollFrame", treeView:GetName() .. "_ScrollFrame", treeView) local scrollChild = CreateFrame("Frame", scrollFrame:GetName() .. "_Frame", scrollFrame) local scrollBar = CreateFrame("Slider", scrollFrame:GetName() .. "_ScrollBar", scrollFrame, "UIPanelScrollBarTemplate") treeView.scrollFrame = scrollFrame treeView.scrollChild = scrollChild treeView.scrollBar = scrollBar local html scrollFrame:SetScrollChild(scrollChild) scrollFrame:SetPoint("TOPLEFT", treeView, "TOPLEFT", 8, -12) scrollFrame:SetPoint("BOTTOMRIGHT", treeView, "BOTTOMRIGHT", -28, 12) scrollFrame:EnableMouseWheel(true) scrollFrame:SetScript("OnMouseWheel", function(this, change) local childHeight = scrollChild:CalculateHeight() local frameHeight = scrollFrame:GetHeight() if childHeight <= frameHeight then return end local diff = frameHeight - childHeight local delta = 1 if change < 0 then delta = -1 end local value = scrollBar:GetValue() + delta*(24/diff) if value < 0 then value = 0 elseif value > 1 then value = 1 end scrollBar:SetValue(value) -- will trigger OnValueChanged end) scrollChild:SetHeight(1) scrollChild:SetWidth(1) function scrollChild:CalculateHeight() local top = self:GetTop() if not top then return 0 end local current = treeView local bottom = top local children = current.children while children do local child for i,v in ipairs(children) do if v:IsShown() then local b = v:GetBottom() if b and b < bottom then bottom = b child = v end end end if not child then break end current = child children = current.children end return top - bottom end scrollBar:SetPoint("TOPLEFT", scrollFrame, "TOPRIGHT", 0, -16) scrollBar:SetPoint("BOTTOMLEFT", scrollFrame, "BOTTOMRIGHT", 0, 16) scrollBar:SetMinMaxValues(0, 1) scrollBar:SetValueStep(1e-5) scrollBar:SetValue(0) scrollBar:SetWidth(16) scrollBar:SetScript("OnValueChanged", function(this) local max = scrollChild:CalculateHeight() - scrollFrame:GetHeight() local val = scrollBar:GetValue() * max if math.abs(scrollFrame:GetVerticalScroll() - val) < 1 then return end scrollFrame:SetVerticalScroll(val) end) scrollBar:EnableMouseWheel(true) scrollBar:SetScript("OnMouseWheel", function(this, ...) scrollFrame:GetScript("OnMouseWheel")(scrollFrame, ...) end) local function reposition(children, last, depth) if not children then return last end for i, v in ipairs(children) do v:Show() v:ClearAllPoints() v:SetPoint("LEFT", scrollFrame, "LEFT", depth*16, 0) v:SetPoint("RIGHT", scrollFrame, "RIGHT") if not last then v:SetPoint("TOP", scrollChild, "TOP") else v:SetPoint("TOP", last, "BOTTOM") end last = v if v.expand and v.expand:GetNormalTexture():GetTexture() ~= [[Interface\Buttons\UI-PlusButton-Up]] then last = reposition(v.children, last, depth+1) elseif v.children then for i, child in ipairs(v.children) do child:Hide() end end end return last end treeView.children = {} function treeView:Reposition() reposition(self.children, nil, 0) local height = scrollChild:CalculateHeight() if height < scrollFrame:GetHeight() then scrollBar:Hide() scrollFrame:SetPoint("BOTTOMRIGHT", treeView, "BOTTOMRIGHT", -8, 12) scrollBar:SetValue(0) scrollFrame:SetVerticalScroll(0) else scrollBar:Show() scrollFrame:SetPoint("BOTTOMRIGHT", treeView, "BOTTOMRIGHT", -28, 12) end end local selectedTreeLine local function TreeLine_OnEnter(this) if this:IsEnabled() then this.highlight:SetAlpha(1) this.highlight:Show() end end local function TreeLine_OnLeave(this) if this ~= selectedTreeLine then this.highlight:Hide() else this.highlight:SetAlpha(0.7) end GameTooltip:Hide() end local function TreeLine_Unselect(this) this.highlight:Hide() selectedTreeLine = nil end local function TreeLine_OnExpandClick(this, openOnly) local treeLine = this:GetParent() local tex = this:GetNormalTexture():GetTexture() local expand = (tex == [[Interface\Buttons\UI-PlusButton-Up]]) if openOnly == true or openOnly == false then if openOnly then if not expand then return end else if expand then return end end end if expand then this:SetNormalTexture([[Interface\Buttons\UI-MinusButton-UP]]) this:SetPushedTexture([[Interface\Buttons\UI-MinusButton-Down]]) this:SetDisabledTexture([[Interface\Buttons\UI-MinusButton-Disabled]]) this:SetHighlightTexture([[Interface\Buttons\UI-MinusButton-Hilight]]) else this:SetNormalTexture([[Interface\Buttons\UI-PlusButton-Up]]) this:SetPushedTexture([[Interface\Buttons\UI-PlusButton-Down]]) this:SetDisabledTexture([[Interface\Buttons\UI-PlusButton-Disabled]]) this:SetHighlightTexture([[Interface\Buttons\UI-PlusButton-Hilight]]) end treeView:Reposition() local scrollValue = treeView.scrollFrame:GetVerticalScroll() local max = treeView.scrollChild:CalculateHeight() - treeView.scrollFrame:GetHeight() treeView.scrollBar:SetValue(scrollValue/max) end local function TreeLine_OnClick(this) if not this:IsEnabled() then return end local last_selectedTreeLine = selectedTreeLine if selectedTreeLine then TreeLine_Unselect(selectedTreeLine) end selectedTreeLine = this if this:IsMouseMotionFocus() then this.highlight:SetAlpha(1) else this.highlight:SetAlpha(0.7) end this.highlight:Show() if this.expand then if this == last_selectedTreeLine then this.expand:Click() else TreeLine_OnExpandClick(this.expand, true) end end local data = this.htmlFunc() html.text = data html:SetText(data) helpFrame.mainPane.scrollBar:SetValue(0) end local treeLineNum = 0 local function getTreeLine(hasExpand, parent, name, htmlFunc) treeLineNum = treeLineNum + 1 local frame = CreateFrame("Button", treeView:GetName() .. "_TreeLine" .. treeLineNum, parent == treeView and treeView.scrollChild or parent) table.insert(parent.children, frame) local text = frame:CreateFontString(frame:GetName() .. "_Text", "OVERLAY", "GameFontHighlight") frame.text = text text:SetJustifyH("LEFT") frame:SetFontString(text) text:SetText(name) frame:SetNormalFontObject(GameFontHighlight) frame:SetHeight(16) frame:EnableMouse(true) frame:SetScript("OnEnter", TreeLine_OnEnter) frame:SetScript("OnLeave", TreeLine_OnLeave) frame:SetScript("OnClick", TreeLine_OnClick) local highlight = frame:CreateTexture(frame:GetName() .. "_Highlight", "BACKGROUND") frame.highlight = highlight highlight:SetTexture([[Interface\QuestFrame\UI-QuestTitleHighlight]]) highlight:SetBlendMode("ADD") highlight:SetAllPoints(frame) highlight:Hide() local expand if hasExpand then expand = CreateFrame("Button", frame:GetName() .. "_Expand", frame) frame.expand = expand expand:SetNormalTexture([[Interface\Buttons\UI-PlusButton-Up]]) expand:SetPushedTexture([[Interface\Buttons\UI-PlusButton-Down]]) expand:SetDisabledTexture([[Interface\Buttons\UI-PlusButton-Disabled]]) expand:SetHighlightTexture([[Interface\Buttons\UI-PlusButton-Hilight]]) expand:SetScript("OnClick", TreeLine_OnExpandClick) expand:SetWidth(16) expand:SetHeight(16) expand:SetPoint("LEFT", frame, "LEFT", 0, 0) text:SetPoint("LEFT", expand, "RIGHT", 1, 0) frame.children = {} else text:SetPoint("LEFT", frame, "LEFT", 17, 0) end text:SetPoint("RIGHT", frame, "RIGHT", 0, 0) frame.htmlFunc = htmlFunc return frame end local mainPane = CreateFrame("Frame", helpFrame:GetName() .. "_MainPane", helpFrame, BackdropTemplateMixin and "BackdropTemplate" or nil) helpFrame.mainPane = mainPane local bg = newDict( 'bgFile', [[Interface\Buttons\WHITE8X8]], 'edgeFile', [[Interface\Tooltips\UI-Tooltip-Border]], 'tile', true, 'tileSize', 16, 'edgeSize', 16, 'insets', newDict( 'left', 3, 'right', 3, 'top', 3, 'bottom', 3 ) ) mainPane:SetBackdrop(bg) bg.insets = del(bg.insets) bg = del(bg) mainPane:SetBackdropBorderColor(0.6, 0.6, 0.6) mainPane:SetBackdropColor(0, 0, 0) mainPane:SetPoint("TOPLEFT", treeView, "TOPRIGHT", -3, 0) mainPane:SetPoint("RIGHT", helpFrame, "RIGHT", -12, 0) mainPane:SetPoint("BOTTOM", treeView, "BOTTOM") local scrollFrame = CreateFrame("ScrollFrame", mainPane:GetName() .. "_ScrollFrame", mainPane) local scrollChild = CreateFrame("Frame", mainPane:GetName() .. "_ScrollChild", scrollFrame) local scrollBar = CreateFrame("Slider", mainPane:GetName() .. "_ScrollBar", scrollFrame, "UIPanelScrollBarTemplate") mainPane.scrollFrame = scrollFrame mainPane.scrollChild = scrollChild mainPane.scrollBar = scrollBar scrollFrame:SetScrollChild(scrollChild) scrollFrame:SetPoint("TOPLEFT", mainPane, "TOPLEFT", 9, -9) scrollFrame:SetPoint("BOTTOMRIGHT", mainPane, "BOTTOMRIGHT", -28, 12) scrollFrame:EnableMouseWheel(true) scrollFrame:SetScript("OnMouseWheel", function(this, change) local childHeight = scrollChild:CalculateHeight() local frameHeight = scrollFrame:GetHeight() if childHeight <= frameHeight then return end -- nextFreeScroll = GetTime() + 1 -- unused local diff = childHeight - frameHeight local delta = 1 if change > 0 then delta = -1 end local value = scrollBar:GetValue() + delta*24/diff if value < 0 then value = 0 elseif value > 1 then value = 1 end scrollBar:SetValue(value) -- will trigger OnValueChanged end) scrollChild:SetHeight(10) scrollChild:SetWidth(10) local first = true function scrollChild:CalculateHeight() local html = self.html local t = newList(html:GetRegions()) local top, bottom for i,v in ipairs(t) do if v:GetTop() and (not top or top < v:GetTop()) then top = v:GetTop() end if v:GetBottom() and (not bottom or bottom > v:GetBottom()) then bottom = v:GetBottom() end end t = del(t) return top and (top - bottom) or 10 end _G.scrollChild = scrollChild scrollBar:SetPoint("TOPLEFT", scrollFrame, "TOPRIGHT", 0, -16) scrollBar:SetPoint("BOTTOMLEFT", scrollFrame, "BOTTOMRIGHT", 0, 16) scrollBar:SetMinMaxValues(0, 1) scrollBar:SetValueStep(1e-5) scrollBar:SetValue(0) scrollBar:SetWidth(16) scrollBar:SetScript("OnValueChanged", function(this) local max = scrollChild:CalculateHeight() - scrollFrame:GetHeight() local val = scrollBar:GetValue() * max if math.abs(scrollFrame:GetVerticalScroll() - val) < 1 then return end scrollFrame:SetVerticalScroll(val) scrollFrame:UpdateScrollChildRect() end) scrollBar:EnableMouseWheel(true) scrollBar:SetScript("OnMouseWheel", function(this, ...) scrollFrame:GetScript("OnMouseWheel")(scrollFrame, ...) end) html = CreateFrame("SimpleHTML", scrollChild:GetName() .. "_HTML", scrollChild) scrollChild.html = html html:SetFontObject('p', GameFontNormal) html:SetSpacing('p', 3) html:SetFontObject('h1', GameFontHighlightLarge) local font, height, flags = GameFontHighlightLarge:GetFont() height = height * 1.25 html:SetFont('h1', font, height, flags) html:SetSpacing('h1', height/2) html:SetFontObject('h2', GameFontHighlightLarge) local _, height = GameFontHighlightLarge:GetFont() html:SetSpacing('h2', height/2) html:SetFontObject('h3', GameFontHighlightLarge) local _, height = GameFontHighlightLarge:GetFont() height = height * 14/16 html:SetFont('h3', font, height, flags) html:SetSpacing('h3', 0) html:SetHeight(1) html:SetWidth(400) html:SetPoint("TOPLEFT", 0, 0) local searchBox = CreateFrame("EditBox", helpFrame:GetName() .. "_SearchBox", helpFrame) searchBox:SetFontObject(ChatFontNormal) searchBox:SetHeight(17) searchBox:SetAutoFocus(false) local searchBox_bg = searchBox:CreateTexture(searchBox:GetName() .. "_Background", "BACKGROUND") searchBox_bg:SetTexture(0, 0, 0) searchBox_bg:SetPoint("BOTTOMLEFT", 1, 1) searchBox_bg:SetPoint("TOPRIGHT", -1, -1) local searchBox_line1 = searchBox:CreateTexture(searchBox:GetName() .. "_Line1", "BACKGROUND") searchBox_line1:SetTexture([[Interface\Buttons\WHITE8X8]]) searchBox_line1:SetHeight(1) searchBox_line1:SetPoint("TOPLEFT", searchBox, "BOTTOMLEFT", 0, 1) searchBox_line1:SetPoint("TOPRIGHT", searchBox, "BOTTOMRIGHT", 0, 1) searchBox_line1:SetVertexColor(3/4, 3/4, 3/4, 1) local searchBox_line2 = searchBox:CreateTexture(searchBox:GetName() .. "_Line2", "BACKGROUND") searchBox_line2:SetTexture([[Interface\Buttons\WHITE8X8]]) searchBox_line2:SetWidth(1) searchBox_line2:SetPoint("TOPLEFT", searchBox, "TOPRIGHT", -1, 0) searchBox_line2:SetPoint("BOTTOMLEFT", searchBox, "BOTTOMRIGHT", -1, 0) searchBox_line2:SetVertexColor(3/4, 3/4, 3/4, 1) local searchBox_line3 = searchBox:CreateTexture(searchBox:GetName() .. "_Line3", "BACKGROUND") searchBox_line3:SetTexture([[Interface\Buttons\WHITE8X8]]) searchBox_line3:SetHeight(1) searchBox_line3:SetPoint("BOTTOMLEFT", searchBox, "TOPLEFT", 0, -1) searchBox_line3:SetPoint("BOTTOMRIGHT", searchBox, "TOPRIGHT", 0, -1) searchBox_line3:SetVertexColor(3/8, 3/8, 3/8, 1) local searchBox_line4 = searchBox:CreateTexture(searchBox:GetName() .. "_Line4", "BACKGROUND") searchBox_line4:SetTexture([[Interface\Buttons\WHITE8X8]]) searchBox_line4:SetWidth(1) searchBox_line4:SetPoint("TOPRIGHT", searchBox, "TOPLEFT", 1, 0) searchBox_line4:SetPoint("BOTTOMRIGHT", searchBox, "BOTTOMLEFT", 1, 0) searchBox_line4:SetVertexColor(3/8, 3/8, 3/8, 1) local editBox = CreateFrame("EditBox", helpFrame:GetName() .. "_EditBox", helpFrame) editBox:SetFontObject(ChatFontNormal) editBox:SetMultiLine(true) editBox:SetAutoFocus(false) local editBox_bg = editBox:CreateTexture(editBox:GetName() .. "_Background", "BACKGROUND") editBox_bg:SetTexture(0, 0, 0) editBox_bg:SetPoint("BOTTOMLEFT", 1, 1) editBox_bg:SetPoint("TOPRIGHT", -1, -1) local editBox_label = editBox:CreateFontString(editBox:GetName() .. "_Label", "ARTWORK", "GameFontHighlightSmall") editBox_label:SetText(L["Test area:"]) editBox_label:SetPoint("BOTTOMLEFT", editBox, "TOPLEFT", 0, 3) local editBox_line1 = editBox:CreateTexture(editBox:GetName() .. "_Line1", "BACKGROUND") editBox_line1:SetTexture([[Interface\Buttons\WHITE8X8]]) editBox_line1:SetHeight(1) editBox_line1:SetPoint("TOPLEFT", editBox, "BOTTOMLEFT", 0, 1) editBox_line1:SetPoint("TOPRIGHT", editBox, "BOTTOMRIGHT", 0, 1) editBox_line1:SetVertexColor(3/4, 3/4, 3/4, 1) local editBox_line2 = editBox:CreateTexture(editBox:GetName() .. "_Line2", "BACKGROUND") editBox_line2:SetTexture([[Interface\Buttons\WHITE8X8]]) editBox_line2:SetWidth(1) editBox_line2:SetPoint("TOPLEFT", editBox, "TOPRIGHT", -1, 0) editBox_line2:SetPoint("BOTTOMLEFT", editBox, "BOTTOMRIGHT", -1, 0) editBox_line2:SetVertexColor(3/4, 3/4, 3/4, 1) local editBox_line3 = editBox:CreateTexture(editBox:GetName() .. "_Line3", "BACKGROUND") editBox_line3:SetTexture([[Interface\Buttons\WHITE8X8]]) editBox_line3:SetHeight(1) editBox_line3:SetPoint("BOTTOMLEFT", editBox, "TOPLEFT", 0, -1) editBox_line3:SetPoint("BOTTOMRIGHT", editBox, "TOPRIGHT", 0, -1) editBox_line3:SetVertexColor(3/8, 3/8, 3/8, 1) local editBox_line4 = editBox:CreateTexture(editBox:GetName() .. "_Line4", "BACKGROUND") editBox_line4:SetTexture([[Interface\Buttons\WHITE8X8]]) editBox_line4:SetWidth(1) editBox_line4:SetPoint("TOPRIGHT", editBox, "TOPLEFT", 1, 0) editBox_line4:SetPoint("BOTTOMRIGHT", editBox, "BOTTOMLEFT", 1, 0) editBox_line4:SetVertexColor(3/8, 3/8, 3/8, 1) local currentUnit = "player" local fontString_holder = CreateFrame("Frame", helpFrame:GetName() .. "_FontString_Holder", helpFrame) fontString_holder:SetPoint("LEFT", editBox, "RIGHT", 20, 0) fontString_holder:SetPoint("RIGHT", helpFrame, "RIGHT", -20, 0) fontString_holder:SetPoint("BOTTOM", editBox, "BOTTOM", 0, 0) fontString_holder:SetPoint("TOP", editBox, "TOP", 0, -20) local bg = fontString_holder:CreateTexture(fontString_holder:GetName() .. "_Background", "BACKGROUND") bg:SetPoint("BOTTOMLEFT", 1, 1) bg:SetPoint("TOPRIGHT", -1, -1) bg:SetTexture(0.1, 0.1, 0.1, 0.5) local fontString = fontString_holder:CreateFontString(helpFrame:GetName() .. "_FontString", "ARTWORK") fontString:SetAllPoints() fontString:SetFontObject(ChatFontNormal) local fontString_label = helpFrame:CreateFontString(fontString:GetName() .. "_Label", "ARTWORK", "GameFontHighlightSmall") fontString_label:SetText(L["Output:"]) fontString_label:SetPoint("BOTTOMLEFT", fontString, "TOPLEFT", 0, 3) local fontString_line1 = fontString_holder:CreateTexture(fontString:GetName() .. "_Line1", "BACKGROUND") fontString_line1:SetTexture([[Interface\Buttons\WHITE8X8]]) fontString_line1:SetHeight(1) fontString_line1:SetPoint("TOPLEFT", fontString_holder, "BOTTOMLEFT", 0, 1) fontString_line1:SetPoint("TOPRIGHT", fontString_holder, "BOTTOMRIGHT", 0, 1) fontString_line1:SetVertexColor(3/8, 3/8, 3/8, 1) local fontString_line2 = fontString_holder:CreateTexture(fontString:GetName() .. "_Line2", "BACKGROUND") fontString_line2:SetTexture([[Interface\Buttons\WHITE8X8]]) fontString_line2:SetWidth(1) fontString_line2:SetPoint("TOPLEFT", fontString_holder, "TOPRIGHT", -1, 0) fontString_line2:SetPoint("BOTTOMLEFT", fontString_holder, "BOTTOMRIGHT", -1, 0) fontString_line2:SetVertexColor(3/8, 3/8, 3/8, 1) local fontString_line3 = fontString_holder:CreateTexture(fontString:GetName() .. "_Line3", "BACKGROUND") fontString_line3:SetTexture([[Interface\Buttons\WHITE8X8]]) fontString_line3:SetHeight(1) fontString_line3:SetPoint("BOTTOMLEFT", fontString_holder, "TOPLEFT", 0, -1) fontString_line3:SetPoint("BOTTOMRIGHT", fontString_holder, "TOPRIGHT", 0, -1) fontString_line3:SetVertexColor(3/4, 3/4, 3/4, 1) local fontString_line4 = fontString_holder:CreateTexture(fontString:GetName() .. "_Line4", "BACKGROUND") fontString_line4:SetTexture([[Interface\Buttons\WHITE8X8]]) fontString_line4:SetWidth(1) fontString_line4:SetPoint("TOPRIGHT", fontString_holder, "TOPLEFT", 1, 0) fontString_line4:SetPoint("BOTTOMRIGHT", fontString_holder, "BOTTOMLEFT", 1, 0) fontString_line4:SetVertexColor(3/4, 3/4, 3/4, 1) editBox:SetScript("OnEscapePressed", function(this) this:ClearFocus() this:SetText(DogTag:CleanCode(this:GetText())) end) editBox:SetScript("OnEnterPressed", editBox:GetScript("OnEscapePressed")) local function updateFontString(text) text = text or editBox:GetText():gsub("|c%x%x%x%x%x%x%x%x", ""):gsub("|r", "") local kwargs = newList() kwargs.unit = currentUnit DogTag:AddFontString(fontString, helpFrame, text, "Unit", kwargs) kwargs = del(kwargs) end local fs = helpFrame:CreateFontString(nil, "ARTWORK") local lastText editBox:SetScript("OnTextChanged", function(this) local colorText = this:GetText() if lastText == colorText then return end local text = colorText:gsub("|c%x%x%x%x%x%x%x%x", ""):gsub("|r", "") updateFontString(text) lastText = DogTag:ColorizeCode(text):gsub("|r", "") local position = this:GetCursorPosition() local skip = 0 for i = 1, position do if colorText:byte(i) == ("|"):byte() then if colorText:byte(i+1) == ("c"):byte() then skip = skip + 10 elseif colorText:byte(i+1) == ("r"):byte() then skip = skip + 2 end end end position = position - skip this:SetText(lastText) local betterPosition = 0 for i = 1, position do betterPosition = betterPosition + 1 while lastText:byte(betterPosition) == ("|"):byte() do if lastText:byte(betterPosition+1) == ("c"):byte() then betterPosition = betterPosition + 10 elseif lastText:byte(betterPosition+1) == ("r"):byte() then betterPosition = betterPosition + 2 else break end end end this:SetCursorPosition(betterPosition) fs:SetFontObject(this:GetFontObject()) fs:SetWidth(this:GetWidth()) fs:SetText(text) local height = fs:GetHeight() + 6 if height < 42 then height = 42 end editBox:SetPoint("TOPRIGHT", helpFrame, "BOTTOM", -10, 16 + height) end) editBox:SetText("[Name]") searchBox:SetScript("OnEscapePressed", function(this) this:ClearFocus() end) searchBox:SetScript("OnEnterPressed", searchBox:GetScript("OnEscapePressed")) local dropdown = CreateFrame("Frame", helpFrame:GetName() .. "_DropDown", helpFrame, "UIDropDownMenuTemplate") local dropdown_label = dropdown:CreateFontString(dropdown:GetName() .. "_Label", "ARTWORK", "GameFontHighlightSmall") dropdown_label:SetPoint("RIGHT", dropdown, "LEFT", 20, 0) dropdown_label:SetText(L["Test on: "]) local function dropdown_OnClick(this) UIDropDownMenu_SetSelectedValue(dropdown, this.value) currentUnit = this.value updateFontString() end UIDropDownMenu_Initialize(dropdown, function() local info = newList() info.text = L["Player"] info.value = "player" info.func = dropdown_OnClick UIDropDownMenu_AddButton(info) info = del(info) local info = newList() info.text = L["Target"] info.value = "target" info.func = dropdown_OnClick UIDropDownMenu_AddButton(info) info = del(info) local info = newList() info.text = L["Pet"] info.value = "pet" info.func = dropdown_OnClick UIDropDownMenu_AddButton(info) info = del(info) end) UIDropDownMenu_SetSelectedValue(dropdown, currentUnit) scrollFrame:SetScript("OnSizeChanged", function(this) html:SetWidth(this:GetWidth()) html:SetText(html.text) editBox:SetWidth(this:GetWidth()*1/2 - 20) end) editBox:SetPoint("BOTTOMLEFT", helpFrame, "BOTTOMLEFT", 16, 16) editBox:SetPoint("TOPRIGHT", helpFrame, "BOTTOM", -10, 55) treeView:SetPoint("TOPLEFT", helpFrame, "TOPLEFT", 12, -35) treeView:SetPoint("RIGHT", helpFrame, "LEFT", 162, 0) treeView:SetPoint("BOTTOM", editBox, "TOP", 0, 15) dropdown:SetPoint("BOTTOMRIGHT", fontString, "TOPRIGHT", -110, 0) local searchBox_text = searchBox:CreateFontString(searchBox:GetName() .. "_Text", "ARTWORK", "GameFontHighlightSmall") searchBox_text:SetPoint("TOPLEFT", helpFrame, "TOPLEFT", 14, -16) searchBox_text:SetText(L["Search:"]) searchBox:SetPoint("LEFT", searchBox_text, "RIGHT", 3, 0) searchBox:SetWidth(120) local function escapeHTML__handler(c) if c == "&" then return "&" elseif c == "<" then return "<" elseif c == ">" then return ">" end end local function escapeHTML(text) return (text:gsub("([&<>])", escapeHTML__handler)) end local function _fix__handler(text) if text:sub(1, 2) == "{{" and text:sub(-2) == "}}" then local x = text:sub(3, -3) x = "[" .. x .. "]" x = DogTag:ColorizeCode(x) -- Find the first opening bracket in the colored string, -- which is the one we just prepended to `x`. local first = string.find(x, "|cff%x%x%x%x%x%x%[") + 11 -- Find the last closing bracket in the colored string, -- which is the one we just appended to `x`. local last = string.find(x, "|cff%x%x%x%x%x%x%]|r[^%]]*$") - 1 local y = x:sub(first, last) return y end return DogTag:ColorizeCode(text:sub(2, -2)) end local function fix__handler(text) return escapeHTML(_fix__handler(text)) end local function fix(text) return text:gsub("(%b{})", fix__handler) end local syntaxHTML = L["SyntaxHTML"] if syntaxHTML == "SyntaxHTML" then syntaxHTML = fix([=[

Syntax

LibDogTag-3.0 works by allowing normal text with interspersed tags wrapped in brackets, e.g. {Hello [Tag] There}. Syntax is in the standard form alpha {[Tag]} bravo where alpha is a literal word, bravo is a literal word and {[Tag]} will be replaced by the associated dynamic text. All tags and modifiers are case-insensitive, but will be corrected to proper casing if the tags are legal.

Modifiers

Modifiers can change how a tag's output looks. For example, the {{:Hide(0)}} modifier will hide the result if it is equal to the number {{0}}, so {[HP:Hide(0)]} will show the current health except when it's equal to {{0}}, at which point it will be blank. You can chain together multiple modifiers as well, e.g. {[MissingHP:Hide(0):Red]} will show the missing health as red and not show it if it's equal to {{0}}. Modifiers are actually syntactic sugar for tags. {[HP:Hide(0)]} is exactly the same as {[Hide(HP, 0)]}. All modifiers work this way and all tags can be used as modifiers if they accept an argument.

Arguments

Tags and modifiers can also take an argument, and can be fed in in a syntax similar to {[Tag(argument)]} or {[Tag:Modifier(argument)]}. You can specify arguments out of order by name Using the syntax {[HP(unit='player')]}. This is exactly equal to {[HP('player')]} and {['player':HP]}.

Literals

Strings require either double or single quotes and are used like {["Hello" ' There']}. Numbers can be typed just as normal numbers, e.g. {[1234 56.78 1e6]}. There are also the literals {{nil}}, {{true}}, and {{false}}, which act just like tags.

Logic Branching (if statements)

* The {{&}} and {{and}} operators function as boolean AND. e.g. {[Alpha and Bravo]} will check if Alpha is non-false, if so, run Bravo.
* The {{||}} and {{or}} operators function as boolean OR. e.g. {[Alpha or Bravo]} will check if Alpha is false, if so, run Bravo, otherwise just show Alpha.
* The {{?}} operator functions as an if statement. It can be used in conjunction with {{!}} to create an if-else statement. e.g. {[IsPlayer ? "Player"]} or {[IsPlayer ? "Player" ! "NPC"]}.
* The {{if}} operator functions as an if statement. It can be used in conjunction with {{else}} to create an if-else statement. e.g. {[if IsPlayer then "Player" end]} or {[if IsPlayer then "Player" else "NPC" end]} or {[if IsPlayer then "Player" elseif IsPet then "Pet" else "NPC"]}.
* The {{not}} and {{~}} operators turn a false value into true and true value into false. e.g. {[not IsPlayer]} or {[~IsPlayer]}

Examples

{[Status || FractionalHP(known=true) || PercentHP]}
Will return one of the following (but only one):

* "|cffffffffDead|r", "|cffffffffOffline|r", "|cffffffffGhost|r", etc -- no further information since the OR indicates that there is already a legitimate return
* "|cffffffff3560/8490|r" or "|cffffffff130/6575|r" (but not "|cffffffff62/100|r" unless the target in fact has {{100}} hit points) -- and not "|cffffffff0/2340|r" or "|cffffffff0/3592|r" because that would mean it is dead and that would have already been taken care of by the first tag in the sequence
* "|cffffffff25|r" or "|cffffffff35|r" or "|cffffffff72|r" (percent health) -- if the unit is not dead, offline, etc, and your addon is uncertain of your target's maximum and current health, it will display percent health.

{[Status || (IsPlayer ? HP(known=true)) || PercentHP:Percent]} will deliver similar returns as to that above, but in a slightly different format which should be fairly apparent already.

But to clarify, the nested {{(IsPlayer ? HP(known=true))}} creates an if statement which means that if {{IsPlayer}} is false, the whole value is taken to be false, and if you've read this far you deserve a cookie. If {{IsPlayer}} is true, the actual returned value of this nested expression is actually the term following the AND -- in this case, {{HP(known=true)}}. So this will show {{HP(known=true)}} if {{IsPlayer}} is found true (that is, if the unit is actually a player).

{[if IsFriend then -MissingHP:Green else HP:Red end]}
Will return one of the following (but only one):

* If the unit is friendly, it will display the amount of health they must be healed to meet their maximum. It will be displayed in green, and with a negative sign in front of it.
* If the unit is an enemy, it will display their current health. As this sequence is written, it will not consider whether it is a valid health value or not. On enemies where the health value is uncertain, it will show a percentage (but without a percent sign), until a more reliable value can be determined. This value will be displayed in red.

Unit specification

Units are typically pre-specified by the addon which uses DogTag, whether {{"player"}}, {{"mouseover"}}, or otherwise. You can override the unit a specific tag or modifier operates on by using the form {[Tag(unit="myunit")]} or {[Tag:Modifier(unit="myunit")]}. e.g. {[HP(unit='player')]} gets the player's health.

List of example units

* player - your character
* target - your target
* targettarget - your target's target
* pet - your pet
* mouseover - the unit you are currently hovering over
* focus - your focus unit
* party1 - the first member of your party
* partypet2 - the pet of the second member of your party
* raid3 - the third member of your raid
* raidpet4 - the pet of the fourth member of your raid

Arithmetic operators

You can use arithmetic operators in your DogTag sequences without issue, they function as expected with proper order-of-operations.

* {{+}} - Addition
* {{-}} - Subtraction
* {{*}} - Multiplication
* {{/}} - Division
* {{%}} - Modulus
* {{^}} - Exponentiation

Comparison operators

You can use comparison operators in your DogTag very similarly to arithmetic operators.

* {{=}} - Equality
* {{~=}} - Inequality
* {{<}} - Less than
* {{>}} - Greater than
* {{<=}} - Less than or equal
* {{>=}} - Greater than or equal

Concatenation

Concatenation (joining two pieces of text together) is very easy, all you have to do is place them next to each other separated by a space, e.g. {['Hello' " There"]} => "|cffffffffHello There|r". For a more true-to-life example, {[HP '/' MaxHP]} => "|cffffffff50/100|r".

]=]) else syntaxHTML = fix(syntaxHTML) end local treeLine = getTreeLine(false, treeView, L["Syntax"], function() return syntaxHTML end) treeLine:Click() local function caseDesensitize__handler(c) return ("[%s%s]"):format(c:lower(), c:upper()) end local function caseDesensitize(searchText) return searchText:gsub("%a", caseDesensitize__handler) end local function escapeSearch(searchText) return searchText:gsub("([%%%[%]%^%$%.%+%*%?%(%)])", "%%%1") end local tagCache = {} local categories = {} local searchLine = getTreeLine(false, treeView, L["Search results"], function() local searchText = searchBox:GetText() searchText = (searchText or ''):trim():gsub("%s%s+", " ") if searchText == '' then return "

" .. L["Type your search into the search box in the upper-left corner of DogTag Help"] .. "

" end local searches = newList((" "):split(searchText)) local t = newList() t[#t+1] = "" t[#t+1] = "" t[#t+1] = "

" t[#t+1] = L["Tags matching %q"]:format(searchText) t[#t+1] = "

" for i, v in ipairs(searches) do searches[i] = caseDesensitize(escapeSearch(v)) end for ns, tagCache_ns in pairs(tagCache) do local firstNamespace = true if categories[ns] then for _, category in ipairs(categories[ns]) do local firstCategory = true for tag, data in pairs(tagCache_ns) do if data.category == category then local good = false for i, v in ipairs(searches) do if data.html:match(v) then good = true break end end if good then if firstNamespace then firstNamespace = false t[#t+1] = "

" t[#t+1] = ns t[#t+1] = "

" end if firstCategory then firstCategory = false t[#t+1] = "

" t[#t+1] = category t[#t+1] = "

" end t[#t+1] = data.html end end end end end end searches = del(searches) t[#t+1] = "" t[#t+1] = "" local s = table.concat(t) t = del(t) return s end) local Tags = DogTag.Tags local namespaces = newList() for namespace in pairs(Tags) do namespaces[#namespaces+1] = namespace end table.sort(namespaces) for i,ns in ipairs(namespaces) do local nsLine = getTreeLine(true, treeView, L[ns], function() return '' end) local tags = {} local categories_ns = {} categories[ns] = categories_ns for tag, tagData in pairs(Tags[ns]) do tags[#tags+1] = tag if tagData.category then categories_ns[tagData.category] = true end end table.sort(tags) local tagCache_ns = {} tagCache[ns] = tagCache_ns for i, tag in ipairs(tags) do local tagData = Tags[ns][tag] if not tagData.noDoc then local tagCache_ns_tag = {} tagCache_ns[tag] = tagCache_ns_tag tagCache_ns_tag.category = tagData.category local u = newList() u[#u+1] = "

" local t = newList() t[#t+1] = "[" if tagData.category ~= L["Operators"] then t[#t+1] = tag local arg = tagData.arg if arg then t[#t+1] = "(" for i = 1, #arg, 3 do if i > 1 then t[#t+1] = ", " end local argName, argTypes, argDefault = arg[i], arg[i+1], arg[i+2] t[#t+1] = argName if argName ~= "..." and argDefault ~= "@req" then t[#t+1] = "=" if argDefault == "@undef" then t[#t+1] = "undef" elseif argDefault == false then if argTypes:match("boolean") then t[#t+1] = "false" else t[#t+1] = "nil" end elseif type(argDefault) == "string" then t[#t+1] = ("%q"):format(argDefault) else t[#t+1] = tostring(argDefault) end end end t[#t+1] = ")" end else local arg = tagData.arg local operator = tag if operator == "unm" then operator = "-" end if #arg == 3 then t[#t+1] = operator end for i = 1, #arg, 3 do local argName, argTypes, argDefault = arg[i], arg[i+1], arg[i+2] if i > 1 then t[#t+1] = " " t[#t+1] = operator t[#t+1] = " " end t[#t+1] = argName end end t[#t+1] = "]" u[#u+1] = escapeHTML(DogTag:ColorizeCode(table.concat(t))) u[#u+1] = "

" t = del(t) u[#u+1] = "

" u[#u+1] = "|r |r |r |r " u[#u+1] = escapeHTML(tagData.doc) if tagData.alias then u[#u+1] = "
" u[#u+1] = "|r |r |r |r " u[#u+1] = L["alias for "] u[#u+1] = escapeHTML(DogTag:ColorizeCode("[" .. tagData.alias .. "]")) end u[#u+1] = "
" local examples = newList((";"):split(tagData.example)) for i, v in ipairs(examples) do local tag, result = v:trim():match("^(.*) => \"(.*)\"$") u[#u+1] = "|r |r |r |r • " u[#u+1] = escapeHTML(DogTag:ColorizeCode(tag)) u[#u+1] = " => \"|cffffffff" u[#u+1] = escapeHTML(result) u[#u+1] = "|r\"" u[#u+1] = "
" end examples = del(examples) u[#u+1] = "
" u[#u+1] = "

" tagCache_ns_tag.html = table.concat(u) u = del(u) end end local tmp = {} for cat in pairs(categories_ns) do tmp[#tmp+1] = cat end categories_ns = tmp categories[ns] = categories_ns tmp = nil table.sort(categories_ns) for i, category in ipairs(categories_ns) do local cachedHTML local catLine = getTreeLine(false, nsLine, category, function() if cachedHTML then return cachedHTML end local t = newList() t[#t+1] = "" t[#t+1] = "" t[#t+1] = "

" t[#t+1] = category t[#t+1] = "

" for tag, data in pairs(tagCache_ns) do if data.category == category then t[#t+1] = data.html end end t[#t+1] = "" t[#t+1] = "" local s = table.concat(t) t = del(t) cachedHTML = s return s end) end end namespaces = del(namespaces) local nextUpdateTime = 0 local function OnUpdate(this) if GetTime() < nextUpdateTime then return end this:SetScript("OnUpdate", nil) searchLine:Click() end local lastText = '' searchBox:SetScript("OnTextChanged", function(this) local text = this:GetText() if text == lastText then return end lastText = text nextUpdateTime = GetTime() + 0.5 searchBox:SetScript("OnUpdate", OnUpdate) end) treeView:Reposition() collectgarbage('collect') function DogTag:OpenHelp() helpFrame:Show() end end _G.SlashCmdList.DOGTAG = function() DogTag:OpenHelp() end _G.SLASH_DOGTAG1 = "/dogtag" _G.SLASH_DOGTAG2 = "/dog" _G.SLASH_DOGTAG3 = "/dt" end