Files

189 lines
6.4 KiB
Lua

-- Microchip-chat 0.8.4: clickable web URLs and Ctrl+C copy window.
-- Vanilla WoW 1.12 / Lua 5.0 compatible: no clipboard API, no modern hooks.
-- Only chat *display* text is modified. Existing player/item hyperlinks and
-- all server-bound chat messages remain untouched.
MCURLs = {}
local byURL, byID, nextID = {}, {}, 0
local copyFrame, copyInput
local function isWebAddress(url)
if type(url)~="string" or string.len(url)<4 or string.len(url)>1024 or
string.find(url,"[|%c<>]") then
return false
end
local lower=string.lower(url)
local host
if string.find(lower,"^https?://") then
host=string.gsub(lower,"^https?://", "")
else
host=lower
end
local _,_,candidate=string.find(host,"^([^/%?#:]+)")
host=candidate or ""
if string.sub(host,1,4)=="www." then host=string.sub(host,5) end
-- No scheme-only links, spaces, pipe/control markup, or non-domain words.
if not string.find(host,"^[%w%-%.]+$") then return false end
if string.find(host,"%.%.") or string.find(host,"^%.") then return false end
local _,_,tld=string.find(host,"%.(%a+)$")
if not tld or string.len(tld)<2 or string.len(tld)>24 then return false end
return true
end
-- Surrounding punctuation is not part of the URL. A matched closing bracket
-- within a URL path is retained only if it has an opening counterpart.
local function splitToken(token)
local lead=""
local tail=""
while string.len(token)>0 and string.find(string.sub(token,1,1),'[%(%%%[%{<>"\']') do
lead=lead..string.sub(token,1,1)
token=string.sub(token,2)
end
local changed=true
while changed and string.len(token)>0 do
changed=false
local last=string.sub(token,-1)
if string.find(last,"[%,%.%!%?%;:>\"']") then
tail=last..tail
token=string.sub(token,1,-2)
changed=true
elseif last==")" or last=="]" or last=="}" then
local opener=(last==")" and "(") or (last=="]" and "[") or "{"
local _,opens=string.gsub(token, "%"..opener, "")
local _,closes=string.gsub(token, "%"..last, "")
if closes>opens then
tail=last..tail
token=string.sub(token,1,-2)
changed=true
end
end
end
return lead,token,tail
end
local function linkFor(url)
local id=byURL[url]
if not id then
nextID=nextID+1
id=tostring(nextID)
byURL[url]=id
byID[id]=url
end
return "|cff64bfff|Hmcurl:"..id.."|h"..url.."|h|r"
end
local function transformPlain(plain)
-- Work token-by-token to avoid including ordinary punctuation in the link.
local output=string.gsub(plain,"%S+",function(token)
local lead,url,tail=splitToken(token)
if isWebAddress(url) then
return lead..linkFor(url)..tail
end
return token
end)
return output
end
function MCURLs.Linkify(message)
if type(message)~="string" or message=="" then return message end
-- Don't linkify within an existing WoW hyperlink, including item names
-- containing spaces or URLs. This also preserves class-colored player links.
local output={}
local pos=1
while pos<=string.len(message) do
local start=string.find(message,"|H",pos,true)
if not start then
table.insert(output,transformPlain(string.sub(message,pos)))
break
end
local payloadEnd=string.find(message,"|h",start+2,true)
local labelEnd=payloadEnd and string.find(message,"|h",payloadEnd+2,true)
if not labelEnd then
table.insert(output,transformPlain(string.sub(message,pos)))
break
end
if start>pos then
table.insert(output,transformPlain(string.sub(message,pos,start-1)))
end
table.insert(output,string.sub(message,start,labelEnd+1))
pos=labelEnd+2
end
return table.concat(output)
end
local function makeCopyFrame()
if copyFrame then return end
local frame=CreateFrame("Frame","MicrochipChatURLCopyFrame",UIParent)
frame:SetWidth(560)
frame:SetHeight(118)
frame:SetPoint("CENTER",UIParent,"CENTER",0,120)
frame:SetFrameStrata("DIALOG")
frame:SetMovable(true)
frame:EnableMouse(true)
frame:SetClampedToScreen(true)
frame:SetScript("OnMouseDown",function()
if arg1=="LeftButton" then frame:StartMoving() end
end)
frame:SetScript("OnMouseUp",function()
frame:StopMovingOrSizing()
end)
frame:SetScript("OnHide",function()
frame:StopMovingOrSizing()
end)
local bg=frame:CreateTexture(nil,"BACKGROUND")
bg:SetAllPoints(frame)
bg:SetTexture("Interface\\Tooltips\\UI-Tooltip-Background")
bg:SetVertexColor(0.075,0.11,0.17,0.98)
local title=frame:CreateFontString(nil,"OVERLAY","GameFontNormal")
title:SetPoint("TOPLEFT",frame,"TOPLEFT",17,-13)
title:SetText("MICROCHIP-CHAT / COPY LINK")
local hint=frame:CreateFontString(nil,"OVERLAY","GameFontHighlightSmall")
hint:SetPoint("TOPLEFT",frame,"TOPLEFT",17,-37)
hint:SetText("URL selected - press Ctrl+C to copy, then Esc to close")
local input=CreateFrame("EditBox","MicrochipChatURLCopyInput",frame,"InputBoxTemplate")
input:SetPoint("BOTTOMLEFT",frame,"BOTTOMLEFT",20,17)
input:SetWidth(514)
input:SetHeight(26)
input:SetAutoFocus(false)
input:SetMaxLetters(1024)
input:SetScript("OnEscapePressed",function() frame:Hide() end)
input:SetScript("OnEnterPressed",function() input:HighlightText() end)
local close=CreateFrame("Button",nil,frame)
close:SetPoint("TOPRIGHT",frame,"TOPRIGHT",-9,-8)
close:SetWidth(20)
close:SetHeight(20)
local label=close:CreateFontString(nil,"OVERLAY","GameFontNormal")
label:SetAllPoints(close)
label:SetText("X")
close:SetScript("OnClick",function() frame:Hide() end)
frame:Hide()
copyFrame,copyInput=frame,input
end
function MCURLs.HandleClick(link)
if type(link)~="string" or string.sub(link,1,6)~="mcurl:" then
return false
end
local id=string.sub(link,7)
local url=byID[id]
if not url then return true end
makeCopyFrame()
copyFrame:Show()
copyInput:SetText(url)
copyInput:SetFocus()
copyInput:HighlightText()
return true
end
function MCURLs.GetURLForLink(link)
if type(link)=="string" and string.sub(link,1,6)=="mcurl:" then
return byID[string.sub(link,7)]
end
end