Module:WLink explained

local WLink = ;--[=[ ansiPercent formatURL getArticleBase getBaseTitle getEscapedTitle getExtension getFile getFragment getLanguage getNamespace getPlain getProject getTarget getTargetPage getTitle getWeblink isBracketedLink isBracketedURL isCategorization isExternalLink isInterlanguage isInterwiki isMedia isTitledLink isValidLink isWikilink wikilink failsafe ]=]

-- local globalslocal URLutil = false;

local utilURL = function -- Attach URLutil library module -- Postcondition: -- Returns table, with URLutil library -- Throws error, if not available if not URLutil then local lucky, util = pcall(require, "Module:URLutil"); if lucky then if type(util)

"table" then URLutil = util.URLutil; end util = "library URLutil invalid"; end if type(URLutil) ~= "table" then error(util, 0); end end return URLutil;end -- utilURL

local contentExtlink = function (attempt) -- Retrieve span of external link between brackets -- Precondition: -- attempt -- string, with presumable link -- the first char is expected to be "[" -- Postcondition: -- Returns string, number, number -- string including whitespace -- number with index of relevant "[" -- number with index after relevant "]" -- false if nothing found local r1 = false; local r2 = false; local r3 = attempt:find("]", 2, true); if r3 then local s = attempt:sub(2, r3 - 1); local i = s:find("[", 1, true); if i then r1 = s:sub(i + 1); r2 = i; else r1 = s; r2 = 1; end else r3 = false; end return r1, r2, r3; end -- contentExtlink local contentWikilink = function (attempt) -- Retrieve span of wikilink between brackets -- Precondition: -- attempt -- string, with presumable link -- the first two chars are expected to be "[[" -- Postcondition: -- Returns string, number, number -- string including whitespace -- number with index of relevant "[[" -- number with index after relevant "]]" -- false if nothing found local r1 = false; local r2 = false; local r3 = attempt:find("]]", 3, true); if r3 then local s = attempt:sub(3, r3 - 1); local i = s:find("", 1, true); if i then r1 = s:sub(1, i - 1); r2 = s:sub(i + 1); else r1 = s; end r1 = mw.text.trim(r1); if r1

"" then r1 = false; else r1 = r1:gsub("_", " ") :gsub(" ", " ") :gsub(" ", " ") :gsub(" ", " ") :gsub(" ", " ") :gsub(" +", " "); r1 = mw.text.decode(r1); end end return r1, r2;end -- extractWikilink

local prefix = function (ask, ahead) -- Interprete prefix of language or project type -- Precondition: -- ask -- string, with presumable prefix -- ahead -- true, if first segment -- Postcondition: -- Returns string,string or nil -- first string one of "lead", "lang", "project" -- second string is formatted value -- type is one of "lead", "lang", "project" -- nil if nothing found local r1, r2; local prefixes = ; local s = mw.text.trim(ask); if s

"" then if ahead then r1 = "lead"; r2 = true; end else local p; s = s:lower; p = prefixes[s ]; if p

true then r1 = "project"; r2 = s; elseif p then r1 = "project"; r2 = p; elseif p

false then r1 = "lang"; r2 = s; elseif s:match("^%l%l%l?$") and mw.language.isSupportedLanguage(s) then r1 = "lang"; r2 = s; end end return r1, r2;end -- prefix

local target = function (attempt, lonely) -- Retrieve first target (wikilink or URL), or entire string -- Precondition: -- attempt -- string, with presumable link somewhere -- lonely -- remove fragment, if true -- Postcondition: -- Returns string, number -- string, with detected link target, or entire -- number, with number of brackets, if found, or 2 local r1, r2 = WLink.getTarget(attempt); if not r1 then r1 = mw.text.trim(attempt); r2 = 2; end if lonely then local i = r1:find("#", 1, true); if i

1 then r1 = ""; elseif i then r1 = r1:sub(1, i - 1); end end return r1, r2;end -- target

function WLink.ansiPercent(attempt, alter) -- Convert string by ANSI encoding rather than UTF-8 encoding -- Precondition: -- attempt -- string, with presumable ANSI characters -- alter -- string or nil, to use for spaces instead of %20 -- Postcondition: -- Returns string, encoded local k, s; local r = attempt; if alter then r = r:gsub(" ", alter); end for i = mw.ustring.len(r), 1, -1 do k = mw.ustring.codepoint(r, i, i); if k <= 32 or k > 126 then if k > 255 then s = mw.ustring.sub(r, i, i); if k > 2047 then s = string.format("%%%2X%%%2X%%%2X", s:byte(1, 1), s:byte(2, 2), s:byte(3, 3)); else s = string.format("%%%2X%%%2X", s:byte(1, 1), s:byte(2, 2)); end else s = string.format("%%%2X", k); end r = string.format("%s%s%s", mw.ustring.sub(r, 1, i - 1), s, mw.ustring.sub(r, i + 1)); end end -- for --i r = mw.ustring.gsub(r, '^%*', '%%2A') return r;end -- WLink.ansiPercent

function WLink.formatURL(adjust) -- Create bracketed link, if not yet -- Precondition: -- adjust -- string, with URL or domain/path or bracketed link -- Postcondition: -- Returns string, with bracketed link -- false on invalid format local r; if type(adjust)

"string" then if WLink.isBracketedLink(adjust) then r = adjust; else local url = mw.text.trim(adjust); local host; utilURL; host = URLutil.getHost(adjust); if not host then url = "http://" .. adjust; host = URLutil.getHost(url); end if host then local path = URLutil.getRelativePath(url); local show; if path

"/" then if not url:match("/$") then url = url .. "/"; end show = host; else local i = path:find("#"); if i then path = path:sub(1, i - 1); end show = host .. path; end r = string.format("[%s %s]", url, show); else r = adjust; end end else r = false; end return r;end -- WLink.formatURL

function WLink.getArticleBase(attempt) -- Retrieve generic article title, no fragment nor brackets -- Precondition: -- attempt -- string, with wikilink or page title -- current page title, if missing -- Postcondition: -- Returns string, with identified lemma, or all -- false on invalid format local r; if attempt then local m; r, m = target(attempt, true); if m ~= 2 then r = false; end else r = mw.title.getCurrentTitle.text; end if r then local sub = r:match("^(.*%S) *%(.+%)$"); if sub then r = sub; end end return r;end -- WLink.getArticleBase

function WLink.getBaseTitle(attempt) -- Retrieve last segment in subpage, no fragment -- Precondition: -- attempt -- string, with wikilink or page title -- Postcondition: -- Returns string, with identified segment, or all local r; local s, m = target(attempt, true); if m

2 then local sub = s:match("/([^/]+)$"); if sub then r = sub; else r = s; end else r = false; end return r;end -- WLink.getBaseTitle

function WLink.getEscapedTitle(attempt) -- Retrieve escaped link title -- Precondition: -- attempt -- string, with presumable link title -- Postcondition: -- Returns string, with suitable link title local s = mw.text.trim(attempt); return s:gsub("\n", " ") :gsub("%[", "&#91;") :gsub("%]", "]") :gsub("|", "|");end -- WLink.getEscapedTitle

function WLink.getExtension(attempt) -- Retrieve media extension -- Precondition: -- attempt -- string, with wikilink (media link) or page title -- if URL, PDF may be detected -- Postcondition: -- Returns string, with detected downcased media type -- false if no extension found local r = false; local s, m = target(attempt); if m

2 then s = s:match("%.(%a+)$"); if s then r = s:lower; end elseif s:upper:match("[%./](PDF)%W?") then r = "pdf"; end return r;end -- WLink.getExtension

function WLink.getFile(attempt) -- Retrieve media page identifier -- Precondition: -- attempt -- string, with wikilink (media link) or page title -- Postcondition: -- Returns string, with detected file title -- no namespace nor project -- false if no file found local r = false; local s, m = target(attempt); if m

2 then local slow = ":" .. s:lower; local find = function (a) local seek = string.format(":%s:.+%%.%%a+$", a:lower); local join = slow:find(seek); local ret; if join then ret = s:sub(join + #a + 1); end return ret; end; r = find("file"); if not r then local trsl = mw.site.namespaces[6]; r = find(trsl.name); if not r then trsl = trsl.aliases; for k, v in pairs(trsl) do r = find(v); if r then break; -- for k, v end end -- for k, v end end end return r;end -- WLink.getFile

function WLink.getFragment(attempt) -- Retrieve fragment -- Precondition: -- attempt -- string, with presumable fragment -- Postcondition: -- Returns string, with detected fragment -- false if no address found local r = false; local s, m = target(attempt); if s then local i = s:find("#", 1, true); if i then if i > 1 then s = s:sub(i - 1); i = 2; end if s:find("&#", 1, true) then s = mw.text.decode(s); i = s:find("#", 1, true); if not i then s = ""; i = 0; end end s = s:sub(i + 1); r = mw.text.trim(s); if r

"" then r = false; elseif m

2 then r = r:gsub("%.(%x%x)", "%%%1") :gsub("_", " "); r = mw.uri.decode(r, "PATH"); end end end return r;end -- WLink.getFragment

function WLink.getLanguage(attempt) -- Retrieve language project identifier -- Precondition: -- attempt -- string, with wikilink or page title -- Postcondition: -- Returns string, with detected downcased language identifier -- false if no project language found local r = false; local s, m = WLink.getTarget(attempt); if m

2 then local w = WLink.wikilink(s); if w and w.lang then r = w.lang; end end return r;end -- WLink.getLanguage

function WLink.getNamespace(attempt) -- Retrieve namespace number -- Precondition: -- attempt -- string, with wikilink or page title -- Postcondition: -- Returns number, of detected namespace -- false if no namespace found local r = false; local s, m = WLink.getTarget(attempt); if m

2 then local w = WLink.wikilink(s); if w and not w.lang and not w.project and w.ns then r = w.ns; end end return r;end -- WLink.getNamespace

function WLink.getPlain(attempt) -- Retrieve text with all links replaced by link titles -- Precondition: -- attempt -- string, with wikitext -- Postcondition: -- Returns string, with modified wikitext without links local r = attempt; local i = 1; local j, k, n, lean, s, shift, space, suffix; while (true) do j = r:find("[", i, true); if j then suffix = r:sub(j); i = j + 1; lean = (r:byte(i, i) == 91); if lean then s, k, n = contentWikilink(suffix); else s, k, n = contentExtlink(suffix); end if s then if k > 1 then n = n - k; i = j + k; j = i - 1; suffix = r:sub(j); end if lean then s, shift = extractWikilink(suffix); if s then space = s:match("^([^:]+):"); if space then space = mw.site.namespaces[space ]; if space then space = space.id; end end if space

6 or space

14 then shift = ""; elseif not shift then shift = s; end else s = ""; shift = ""; end else s, shift = extractExtlink(suffix); if not s then s = ""; end if not shift then shift = ""; end i = i - 1; end if j > 1 then s = r:sub(1, j - 1); else s = ""; end r = string.format("%s%s%s", s, shift, r:sub(n + i)); i = i + #shift; else break; -- while true end else break; -- while true end end -- while true return r;end -- WLink.getPlain

function WLink.getProject(attempt) -- Retrieve wikifarm project identifier -- Precondition: -- attempt -- string, with wikilink or page title -- Postcondition: -- Returns string, with detected downcased project identifier -- false if no project identifier found local r = false; local s, m = WLink.getTarget(attempt); if m

2 then local w = WLink.wikilink(s); if w and w.project then r = w.project; end end return r;end -- WLink.getProject

function WLink.getTarget(attempt) -- Retrieve first target (wikilink or URL) -- Precondition: -- attempt -- string, with presumable link somewhere -- Postcondition: -- Returns string, number -- string, with first detected link target -- number, with number of brackets, if found -- false if nothing found local r1 = false; local r2 = false; local i = attempt:find("[", 1, true); if i then local m; r1 = attempt:sub(i); if r1:byte(2, 2) == 91 then m = 2; r1 = extractWikilink(r1); else m = 1; r1 = extractExtlink(r1); end if r1 then r2 = m; end else r1 = attempt:match("%A?([hf]t?tps?://%S+)%s?"); if r1 then if utilURL.isResourceURL(r1) then r2 = 0; else r1 = false; end else r1 = false; end end return r1, r2;end -- WLink.getTarget

function WLink.getTargetPage(attempt) -- Retrieve first target page (page name or URL of page) -- Precondition: -- attempt -- string, with presumable link somewhere -- Postcondition: -- Returns string, with first detected linked page -- false if nothing found local r1, r2 = WLink.getTarget(attempt); if r1 then local i = r1:find("#", 1, true); if i then if i

1 then r1 = false; else r1 = mw.text.trim(r1:sub(1, i - 1)); end end end return r1, r2;end -- WLink.getTargetPage

function WLink.getTitle(attempt) -- Retrieve first link title (wikilink or URL), or wikilink target -- Precondition: -- attempt -- string, with presumable link somewhere -- Postcondition: -- Returns string, with first detected link target -- false if nothing found local r = false; local i = attempt:find("[", 1, true); if i then local s1, s2; r = attempt:sub(i); if r:byte(2, 2) == 91 then s1, s2 = extractWikilink(r); if s2 then r = s2; else r = s1; end else s1, r = extractExtlink(r); end end return r; end -- WLink.getTitle function WLink.getWeblink(attempt, anURLutil) -- Retrieve bracketed link from resource URL -- Precondition: -- attempt -- string, with URL, or something different -- anURLutil -- library module object, or nil -- Postcondition: -- Returns string, with first detected link target -- false if nothing found local second = ".ac.co.go.gv.or."; local r; if type(anURLutil) == "table" then URLutil = anURLutil; else utilURL; end if URLutil.isResourceURL(attempt) then local site = URLutil.getAuthority(attempt); local show; if #attempt == #site then site = site .. "/"; end show = URLutil.getTop3domain("//" .. site); if show then local scan = "[%./](%a+)(%.%l%l%.)(%a+)$"; local search = "." .. show; local s1, s2, s3 = search:match(scan); if s2 then if not second:find(s2, 1, true) then show = string.format("%s.%s", s2, s3); end else show = false; end end if not show then show = URLutil.getTop2domain("//" .. site); if not show then show = URLutil.getHost("//" .. site); end end r = string.format("[%s %s]", attempt, show); else r = attempt; end return r;end -- WLink.getWeblink

function WLink.isBracketedLink(attempt) -- Does attempt match a bracketed link? -- Precondition: -- attempt -- string, with presumable link somewhere -- Postcondition: -- Returns boolean local r = false; local i = attempt:find("[", 1, true); if i then local s = attempt:sub(i); if s:byte(2, 2) == 91 then s = extractWikilink(s); else s = extractExtlink(s); end if s then r = true; end end return r; end -- WLink.isBracketedLink function WLink.isBracketedURL(attempt) -- Does attempt match a bracketed URL? -- Precondition: -- attempt -- string, with presumable link somewhere -- Postcondition: -- Returns boolean local s, r = WLink.getTarget(attempt); return (r == 1); end -- WLink.isBracketedURL function WLink.isCategorization(attempt) -- Does attempt match a categorization? -- Precondition: -- attempt -- string, with presumable link somewhere -- Postcondition: -- Returns boolean local r = false; local s, m = WLink.getTarget(attempt); if m == 2 then local w = WLink.wikilink(s); if w and w.ns == 14 and not (w.lead or w.lang or w.project) and w.title ~= "" then r = true; end end return r; end -- WLink.isCategorization function WLink.isExternalLink(attempt) -- Does attempt match an external link? -- Precondition: -- attempt -- string, with presumable link somewhere -- Postcondition: -- Returns boolean local s, r = WLink.getTarget(attempt); if r then r = (r < 2); end return r; end -- WLink.isExternalLink function WLink.isInterlanguage(attempt) -- Does attempt match an interlanguage link? -- Precondition: -- attempt -- string, with presumable link somewhere -- Postcondition: -- Returns boolean local r = false; local s, m = WLink.getTarget(attempt); if m == 2 then local w = WLink.wikilink(s); if w and w.lang and not w.project and not w.lead and w.title ~= "" then r = true; end end return r; end -- WLink.isInterlanguage function WLink.isInterwiki(attempt) -- Does attempt match an interwiki link within wikifarm? -- Precondition: -- attempt -- string, with presumable link somewhere -- Postcondition: -- Returns boolean local r = false; local s, m = WLink.getTarget(attempt); if m == 2 then local w = WLink.wikilink(s); if w and (w.lang or w.project) and w.title ~= "" then r = true; end end return r; end -- WLink.isInterwiki function WLink.isMedia(attempt) -- Does attempt match a media translusion? -- Precondition: -- attempt -- string, with presumable link somewhere -- Postcondition: -- Returns boolean local r = false; local s, m = WLink.getTarget(attempt); if m == 2 then local w = WLink.wikilink(s); if w and w.ns == 6 and not (w.lead or w.lang or w.project) and w.title ~= "" and WLink.getExtension(w.title) then r = true; end end return r; end -- WLink.isMedia function WLink.isTitledLink(attempt) -- Does attempt match a titled link? -- Precondition: -- attempt -- string, with presumable link somewhere -- Postcondition: -- Returns boolean local r = false; local i = attempt:find("[", 1, true); if i then local c, n; local s = attempt:sub(i); if s:byte(2, 2) == 91 then n = s:find("%]%]", 5); c = "|"; else n = s:find("%]", 8); c = "%s%S"; end if n then local m = s:find(c, 2); if m and m + 1 < n and WLink.getTarget(attempt) then r = true; end end end return r;end -- WLink.isTitledLink

function WLink.isValidLink(attempt) -- Does attempt match a link? -- Precondition: -- attempt -- string, with presumable link somewhere -- Postcondition: -- Returns boolean local s, r = WLink.getTarget(attempt); if r then r = true; end return r;end -- WLink.isValidLink

function WLink.isWikilink(attempt) -- Does attempt match a wikilink? -- Precondition: -- attempt -- string, with presumable link somewhere -- Postcondition: -- Returns boolean local s, m = WLink.getTarget(attempt); return (m

2);end -- WLink.isWikilink

function WLink.wikilink(attempt) -- Retrieve wikilink components -- Precondition: -- attempt -- string, with presumable link -- expected to be enclosed in "" "" -- else wikilink -- Postcondition: -- Returns table or false -- table of assignments with -- type is one of "lead", -- "project", "lang", -- "ns", "space", "title" -- false if nothing found local s = contentWikilink(attempt); local got, n, r; if not s then s = attempt; end i = s:find("|", 1, true); if i then s = s:sub(1, i - 1); end got = mw.text.split(s, ":"); n = table.maxn(got); if n

1 then r = ; else local j, k, o, v; r = ; if n > 4 then k = 4; else k = n - 1; end j = k; for i = 1, j do s = mw.text.trim(got[i ]); if s ~= "" then o = mw.site.namespaces[mw.text.trim(got[ i ]) ]; if o then r.ns = o.id; r.space = o.name; k = i + 1; j = i - 1; break; -- for i end end end -- for i for i = 1, j do o, v = prefix(got[i ], (i

1)); if o then if r[o ] then k = i; break; -- for i else r[o ] = v; end else k = i; break; -- for i end end -- for i for i = k, n do r.title = r.title .. got[i ]; if i < n then r.title = r.title .. ":"; end end -- for i end if r.lead and (r.project or not r.title or (not r.lang and r.ns ~= 6 and r.ns ~= 14)) then r.lead = false; end return r;end -- WLink.wikilink

function WLink.failsafe(assert) -- Retrieve versioning and check for compliance -- Precondition: -- assert -- string, with required version, or false -- Postcondition: -- Returns string with appropriate version, or false local r; if assert and assert > WLink.serial then r = false; else r = WLink.serial; end return rend -- WLink.failsafe

local function Template(frame, action, leave, lone) -- Run actual code from template transclusion -- Precondition: -- frame -- object -- action -- string, with function name -- leave -- true: keep whitespace around -- lone -- true: permit call without parameters -- Postcondition: -- Return string; might be error message local lucky = true; local s = false; local r = false; local space; for k, v in pairs(frame.args) do if k

1 then if leave then s = v; else s = mw.text.trim(v); end elseif action

"ansiPercent" and k

"space" then if v ~= "" then space = v; end elseif k ~= "template" then lucky = false; if r then r = r .. "|"; else r = "Unknown parameter: "; end r = string.format("%s%s=", r, k); end end -- for k, v if lucky then if s or lone then lucky, r = pcall(WLink[action ], s, space); else r = "Parameter missing"; lucky = false; end end if lucky then if type(r)

"boolean" then if r then r = "1"; else r = ""; end end else r = string.format("

%s", r); end return r;end -- Template

-- Exportlocal p = ;

p.ansiPercent = function (frame) return Template(frame, "ansiPercent");endp.formatURL = function (frame) return Template(frame, "formatURL");endp.getArticleBase = function (frame) return Template(frame, "getArticleBase", false, true);endp.getBaseTitle = function (frame) return Template(frame, "getBaseTitle");endp.getEscapedTitle = function (frame) return Template(frame, "getEscapedTitle");endp.getExtension = function (frame) return Template(frame, "getExtension");endp.getFile = function (frame) return Template(frame, "getFile");endp.getFragment = function (frame) return Template(frame, "getFragment");endp.getInterwiki = function (frame) return Template(frame, "getInterwiki");endp.getLanguage = function (frame) return Template(frame, "getLanguage");endp.getNamespace = function (frame) return tostring(Template(frame, "getNamespace"));endp.getPlain = function (frame) return Template(frame, "getPlain");endp.getProject = function (frame) return Template(frame, "getProject");endp.getTarget = function (frame) return Template(frame, "getTarget");endp.getTargetPage = function (frame) return Template(frame, "getTargetPage");endp.getTitle = function (frame) return Template(frame, "getTitle");endp.getWeblink = function (frame) return Template(frame, "getWeblink");endp.isBracketedLink = function (frame) return Template(frame, "isBracketedLink");endp.isBracketedURL = function (frame) return Template(frame, "isBracketedURL");endp.isCategorization = function (frame) return Template(frame, "isCategorization");endp.isExternalLink = function (frame) return Template(frame, "isExternalLink");endp.isInterlanguage = function (frame) return Template(frame, "isInterlanguage");endp.isInterwiki = function (frame) return Template(frame, "isInterwiki");endp.isMedia = function (frame) return Template(frame, "isMedia");endp.isTitledLink = function (frame) return Template(frame, "isTitledLink");endp.isValidLink = function (frame) return Template(frame, "isValidLink");endp.isWeblink = function (frame) return Template(frame, "isWeblink");endp.isWikilink = function (frame) return Template(frame, "isWikilink");endp.failsafe = function (frame) local since = frame.args[1 ]; if since then since = mw.text.trim(since); if since

"" then since = false; end end return WLink.failsafe(since) or "";endp.WLink = function return WLink;end

return p;