imported>Mabschaaf (Update gem. Spezial:Diff/169981461/169985081) |
imported>Crazy1880 (Aktualisiert auf Anfrage) |
||
Zeile 1: | Zeile 1: | ||
local WLink = { suite | local WLink = { suite = "WLink", | ||
serial = " | serial = "2022-05-09", | ||
item = 19363224, | |||
globals = { URLutil = 10859193 } }; | |||
--[=[ | --[=[ | ||
ansiPercent() | ansiPercent() | ||
Zeile 11: | Zeile 14: | ||
getFragment() | getFragment() | ||
getLanguage() | getLanguage() | ||
getLinktextProblem() | |||
getNamespace() | getNamespace() | ||
getNamespaced() | |||
getPlain() | getPlain() | ||
getProject() | getProject() | ||
getTarget() | getTarget() | ||
getTalkPage() | |||
getTargetPage() | getTargetPage() | ||
getTitle() | getTitle() | ||
getWeblink() | getWeblink() | ||
getWikilink() | |||
isBracketedLink() | isBracketedLink() | ||
isBracketedURL() | isBracketedURL() | ||
Zeile 25: | Zeile 32: | ||
isInterwiki() | isInterwiki() | ||
isMedia() | isMedia() | ||
isTalkPage() | |||
isTitledLink() | isTitledLink() | ||
isValidLink() | isValidLink() | ||
isValidLinktext() | |||
isWikilink() | isWikilink() | ||
pageLink() | |||
pageTarget() | |||
wikilink() | wikilink() | ||
failsafe() | failsafe() | ||
Zeile 35: | Zeile 46: | ||
-- local globals | -- local globals | ||
local URLutil = false; | local URLutil = false; | ||
local Failsafe = WLink; | |||
local GlobalMod = WLink; | |||
local htmlInline = { b = true, | |||
bdi = true, | |||
bdo = true, | |||
big = true, | |||
code = true, | |||
em = true, | |||
i = true, | |||
kbd = true, | |||
s = true, | |||
samp = true, | |||
small = true, | |||
span = true, | |||
strong = true, | |||
style = true, | |||
sub = true, | |||
sup = true, | |||
tt = true, | |||
var = true }; | |||
local foreignModule = function ( access, advanced, append, alt, alert ) | |||
-- Fetch global module | |||
-- Precondition: | |||
-- access -- string, with name of base module | |||
-- advanced -- true, for require(); else mw.loadData() | |||
-- append -- string, with subpage part, if any; or false | |||
-- alt -- number, of wikidata item of root; or false | |||
-- alert -- true, for throwing error on data problem | |||
-- Postcondition: | |||
-- Returns whatever, probably table | |||
-- 2020-01-01 | |||
local storage = access; | |||
local finer = function () | |||
if append then | |||
storage = string.format( "%s/%s", | |||
storage, | |||
append ); | |||
end | |||
end | |||
local fun, lucky, r, suited; | |||
if advanced then | |||
fun = require; | |||
else | |||
fun = mw.loadData; | |||
end | |||
GlobalMod.globalModules = GlobalMod.globalModules or { }; | |||
suited = GlobalMod.globalModules[ access ]; | |||
if not suited then | |||
finer(); | |||
lucky, r = pcall( fun, "Module:" .. storage ); | |||
end | |||
if not lucky then | |||
if not suited and | |||
type( alt ) == "number" and | |||
alt > 0 then | |||
suited = string.format( "Q%d", alt ); | |||
suited = mw.wikibase.getSitelink( suited ); | |||
GlobalMod.globalModules[ access ] = suited or true; | |||
end | |||
if type( suited ) == "string" then | |||
storage = suited; | |||
finer(); | |||
lucky, r = pcall( fun, storage ); | |||
end | |||
if not lucky and alert then | |||
error( "Missing or invalid page: " .. storage ); | |||
end | |||
end | |||
return r; | |||
end -- foreignModule() | |||
Zeile 45: | Zeile 132: | ||
-- Throws error, if not available | -- Throws error, if not available | ||
if not URLutil then | if not URLutil then | ||
local | local util = foreignModule( "URLutil", | ||
true, | |||
false, | |||
WLink.globals.URLutil ); | |||
if type( util ) == "table" then | |||
URLutil = util.URLutil(); | |||
else | |||
util = "library URLutil invalid"; | util = "library URLutil invalid"; | ||
end | end | ||
Zeile 58: | Zeile 147: | ||
return URLutil; | return URLutil; | ||
end -- utilURL() | end -- utilURL() | ||
local cleanWikilink = function ( access ) | |||
-- Refine wikilink spacing and decode | |||
-- Precondition: | |||
-- access -- string, with presumable link | |||
-- Postcondition: | |||
-- Returns string, with pretty target | |||
local r; | |||
if not WLink.lrm then | |||
WLink.lrm = mw.ustring.char( 0x200E ); | |||
WLink.rlm = mw.ustring.char( 0x200F ); | |||
end | |||
r = access:gsub( "_", " " ) | |||
:gsub( " ", " " ) | |||
:gsub( " ", " " ) | |||
:gsub( " ", " " ) | |||
:gsub( " ", " " ) | |||
:gsub( "‎", "" ) | |||
:gsub( "‏", "" ) | |||
:gsub( WLink.lrm, "" ) | |||
:gsub( WLink.rlm, "" ) | |||
:gsub( "%s+", " " ); | |||
r = mw.text.decode( r ); | |||
return r; | |||
end -- cleanWikilink() | |||
Zeile 188: | Zeile 304: | ||
r1 = false; | r1 = false; | ||
else | else | ||
r1 = | r1 = cleanWikilink( r1 ); | ||
end | end | ||
end | end | ||
Zeile 202: | Zeile 312: | ||
local prefix = function ( ask | local farming = function ( already ) | ||
-- Retrieve wikifarm project information | |||
-- Precondition: | |||
-- already -- table, with wikilink components | |||
-- Postcondition: | |||
-- Returns table, with wikilink components extended | |||
local r = already; | |||
if not r.project then | |||
local codes = { mediawiki = "mw", | |||
wikibooks = "b", | |||
wikidata = "d", | |||
wikinews = "n", | |||
wikipedia = "w", | |||
wikiquote = "q", | |||
wikisource = "s", | |||
wikiversity = "v", | |||
wikivoyage = "voy", | |||
wiktionary = "wikt" }; | |||
local server = mw.site.server:gsub( "([/.])m%.", | |||
"%1" ) | |||
:gsub( "%.beta%.wmflabs%.org$", | |||
".org" ); | |||
local site = server:match( "[/.](%l+)%.org$" ); | |||
r.project = codes[ site ]; | |||
if r.project then | |||
if not r.lang and | |||
r.project ~= "mw" and r.project ~= "d" then | |||
r.lang = server:match( "//(%l+)%." ); | |||
end | |||
else | |||
site = server:match( "//(%l+)%.wikimedia%.org$" ); | |||
if site == "commons" or site == "meta" then | |||
r.project = site; | |||
end | |||
end | |||
end | |||
if r.project and r.ns and | |||
( r.project == "commons" or | |||
r.project == "d" or | |||
r.project == "meta" or | |||
r.project == "mw" ) then | |||
r.language = true; | |||
end | |||
return r; | |||
end -- farming() | |||
local prefix = function ( ask ) | |||
-- Interprete prefix of language or project type | -- Interprete prefix of language or project type | ||
-- Precondition: | -- Precondition: | ||
-- ask -- string, with presumable prefix | -- ask -- string, with presumable prefix | ||
-- Postcondition: | -- Postcondition: | ||
-- Returns string,string or nil | -- Returns string,string or nil | ||
Zeile 241: | Zeile 398: | ||
}; | }; | ||
local s = mw.text.trim( ask ); | local s = mw.text.trim( ask ); | ||
if s | if s ~= "" then | ||
local p; | local p; | ||
s = s:lower(); | s = s:lower(); | ||
Zeile 325: | Zeile 477: | ||
end | end | ||
else | else | ||
s = string.format( "%%% | s = string.format( "%%%02X", k ); | ||
end | end | ||
r = string.format( "%s%s%s", | r = string.format( "%s%s%s", | ||
Zeile 338: | Zeile 490: | ||
function WLink.formatURL( adjust ) | function WLink.formatURL( adjust, assure ) | ||
-- Create bracketed link, if not yet | -- Create bracketed link, if not yet | ||
-- Precondition: | -- Precondition: | ||
-- adjust -- string, with URL or domain/path or bracketed link | -- adjust -- string, with URL or domain/path or bracketed link | ||
-- assure -- boolean, true for secure HTTP | |||
-- Postcondition: | -- Postcondition: | ||
-- Returns string, with bracketed link | -- Returns string, with bracketed link | ||
Zeile 355: | Zeile 508: | ||
host = URLutil.getHost( adjust ); | host = URLutil.getHost( adjust ); | ||
if not host then | if not host then | ||
url | url = "://" .. adjust; | ||
if assure then | |||
url = "s" .. url; | |||
end | |||
url = "http" .. url; | |||
host = URLutil.getHost( url ); | host = URLutil.getHost( url ); | ||
end | end | ||
Zeile 444: | Zeile 601: | ||
-- Postcondition: | -- Postcondition: | ||
-- Returns string, with suitable link title | -- Returns string, with suitable link title | ||
return | local s = mw.text.trim( attempt ); | ||
return s:gsub( "\n", " " ) | |||
:gsub( "%[", "[" ) | |||
:gsub( "%]", "]" ) | |||
:gsub( "|", "|" ); | |||
end -- WLink.getEscapedTitle() | end -- WLink.getEscapedTitle() | ||
Zeile 499: | Zeile 657: | ||
r = find( "file" ); | r = find( "file" ); | ||
if not r then | if not r then | ||
local trsl = mw.site.namespaces[6]; | local trsl = mw.site.namespaces[ 6 ]; | ||
r = find( trsl.name ); | r = find( trsl.name ); | ||
if not r then | if not r then | ||
Zeile 574: | Zeile 732: | ||
return r; | return r; | ||
end -- WLink.getLanguage() | end -- WLink.getLanguage() | ||
function WLink.getLinktextProblem( attempt ) | |||
-- Which problem has this presumable link text? | |||
-- Precondition: | |||
-- attempt -- string, with presumable linktext | |||
-- Postcondition: | |||
-- Returns string, with error message, or false | |||
local r; | |||
if attempt:find( "]", 1, true ) then | |||
r = "]"; | |||
elseif mw.text.unstripNoWiki( attempt ) ~= attempt then | |||
r = "<nowiki>"; | |||
elseif attempt:find( "\n", 1, true ) then | |||
r = "\n"; | |||
elseif mw.text.unstrip( attempt ) ~= attempt then | |||
if not WLink.stripREF then | |||
WLink.stripREF = string.format( "%c%c%c%c%s%c%c%c%c", | |||
127, 39, 34, 96, | |||
"UNIQ%-+ref%-%x+%-QINU", | |||
96, 34, 39, 127 ); | |||
end | |||
if mw.ustring.find( attempt, WLink.stripREF ) then | |||
r = "<ref>"; | |||
end | |||
end | |||
if not r then | |||
local i = attempt:find( "<", 1, true ); | |||
if i then | |||
local s = mw.ustring.lower( attempt:sub( i ) ); | |||
local sign = true; | |||
local skip; | |||
while sign and not r do | |||
skip, sign = s:match( "^([^<]*< *)(%l[%l%d]*)[ /]*.*>" ); | |||
if sign then | |||
if htmlInline[ sign ] then | |||
i = skip:len() + sign:len() + 1; | |||
s = s:sub( i ); | |||
else | |||
r = string.format( "<%s>", sign ); | |||
end | |||
end | |||
end -- while sign and not r | |||
end | |||
if not r then | |||
local s = attempt .. " "; | |||
if s:find( "ISBN ", 1, true ) then | |||
r = s:match( "(ISBN %d[%-%d]+[%dxX])%W" ); | |||
end | |||
if not r then | |||
if s:find( "PMID ", 1, true ) then | |||
r = s:match( "(PMID [1-9]%d*)%W" ); | |||
end | |||
if not r then | |||
if s:find( "RFC ", 1, true ) then | |||
r = s:match( "(RFC [1-9]%d?%d?%d?)%W" ); | |||
end | |||
end | |||
end | |||
end | |||
end | |||
return r or false; | |||
end -- WLink.getLinktextProblem() | |||
Zeile 594: | Zeile 816: | ||
return r; | return r; | ||
end -- WLink.getNamespace() | end -- WLink.getNamespace() | ||
function WLink.getNamespaced( area, attempt ) | |||
-- Retrieve page in namespace | |||
-- Precondition: | |||
-- area -- string or number, with some namespace spec | |||
-- attempt -- string, with wikilink or page title or page name | |||
-- Postcondition: | |||
-- Returns page prefixed by namespace, | |||
-- false if invalid | |||
local r = false; | |||
local s = type( area ); | |||
local room; | |||
if s == "string" then | |||
room = mw.site.namespaces[ tonumber( area ) or area ]; | |||
elseif s == "number" then | |||
room = mw.site.namespaces[ area ]; | |||
end | |||
if room then | |||
local m; | |||
s, m = WLink.getTarget( attempt ); | |||
if not s then | |||
s = attempt; | |||
elseif m ~= 2 then | |||
s = false; | |||
end | |||
if s then | |||
local w = WLink.wikilink( s ); | |||
if w and not w.lang and not w.project and | |||
( not w.ns or w.ns == room.id ) then | |||
r = string.format( "%s:%s", | |||
room.name, w.title ); | |||
end | |||
end | |||
end | |||
return r; | |||
end -- WLink.getNamespaced() | |||
Zeile 620: | Zeile 880: | ||
if k > 1 then | if k > 1 then | ||
n = n - k; | n = n - k; | ||
i = j + k; | i = j + k + 1; | ||
j = i - 1; | j = i - 1; | ||
suffix = r:sub( j ); | suffix = r:sub( j ); | ||
Zeile 668: | Zeile 928: | ||
break; -- while true | break; -- while true | ||
end | end | ||
end -- while true | end -- while true | ||
return r; | return r; | ||
end -- WLink.getPlain() | end -- WLink.getPlain() | ||
Zeile 691: | Zeile 951: | ||
return r; | return r; | ||
end -- WLink.getProject() | end -- WLink.getProject() | ||
function WLink.getTalkPage( attempt ) | |||
-- Retrieve talk page name for attempt, or that page name itself | |||
-- Precondition: | |||
-- attempt -- string, with presumable link somewhere | |||
-- Postcondition: | |||
-- Returns string or false | |||
local r = false; | |||
local s, m = WLink.getTarget( attempt ); | |||
if m ~= 2 and attempt then | |||
s = mw.text.trim( attempt ); | |||
end | |||
if s and s ~= "" then | |||
local w = mw.title.new( s ); | |||
if w then | |||
w = w.talkPageTitle; | |||
if w then | |||
r = w.prefixedText; | |||
end | |||
end | |||
end | |||
return r; | |||
end -- WLink.getTalkPage() | |||
Zeile 837: | Zeile 1.122: | ||
return r; | return r; | ||
end -- WLink.getWeblink() | end -- WLink.getWeblink() | ||
function WLink.getWikilink( attempt, appear ) | |||
-- Retrieve bracketed link from text | |||
-- Precondition: | |||
-- attempt -- string, with current target, or plain | |||
-- appear -- string, with link title, or nil | |||
-- Postcondition: | |||
-- Returns string, with first detected link target | |||
-- false if nothing found | |||
local r = WLink.pageTarget( attempt ); | |||
if r then | |||
if appear then | |||
local show = WLink.getEscapedTitle( appear ); | |||
if show ~= r and show ~= "" then | |||
r = string.format( "%s|%s", r, show ); | |||
end | |||
end | |||
r = string.format( "[[%s]]", r ); | |||
end | |||
return r; | |||
end -- WLink.getWikilink() | |||
Zeile 970: | Zeile 1.278: | ||
return r; | return r; | ||
end -- WLink.isMedia() | end -- WLink.isMedia() | ||
function WLink.isTalkPage( attempt ) | |||
-- Does attempt describe a talk page? | |||
-- Precondition: | |||
-- attempt -- string, with presumable link somewhere | |||
-- Postcondition: | |||
-- Returns boolean | |||
local r = false; | |||
local s, m = WLink.getTarget( attempt ); | |||
if m ~= 2 and attempt then | |||
s = mw.text.trim( attempt ); | |||
end | |||
if s and s ~= "" then | |||
local w = mw.title.new( s ); | |||
if w then | |||
r = w.isTalkPage; | |||
end | |||
end | |||
return r; | |||
end -- WLink.isTalkPage() | |||
Zeile 1.009: | Zeile 1.339: | ||
-- Postcondition: | -- Postcondition: | ||
-- Returns boolean | -- Returns boolean | ||
local | local u, r = WLink.getTarget( attempt ); | ||
if r then | if r then | ||
r = true; | if r < 2 then | ||
if u:find( "''", 1, true ) then | |||
r = false; | |||
else | |||
r = true; | |||
end | |||
else | |||
r = true; | |||
end | |||
end | end | ||
return r; | return r; | ||
end -- WLink.isValidLink() | end -- WLink.isValidLink() | ||
function WLink.isValidLinktext( attempt, allow ) | |||
-- Is attempt a plain inline text? | |||
-- Precondition: | |||
-- attempt -- string, with presumable linktext | |||
-- allow -- boolean or nil, if multiline permitted | |||
-- Postcondition: | |||
-- Returns boolean | |||
local s; | |||
if allow and s:find( "\n", 1, true ) then | |||
s = attempt:gsub( "\n", " " ); | |||
else | |||
s = attempt; | |||
end | |||
return not WLink.getLinktextProblem( s ); | |||
end -- WLink.isValidLinktext() | |||
Zeile 1.027: | Zeile 1.383: | ||
return ( m == 2 ); | return ( m == 2 ); | ||
end -- WLink.isWikilink() | end -- WLink.isWikilink() | ||
function WLink.pageLink( attempt, appear, assure ) | |||
-- Create safely standardized wikilink target of a page | |||
-- attempt -- string, with presumable link | |||
-- appear -- string or true or nil, with link title | |||
-- assure -- string or nil, shield against wiki template syntax | |||
-- "URL" or "WIKI" | |||
-- Postcondition: | |||
-- Returns string with link target | |||
local r = WLink.pageTarget( attempt, assure ); | |||
if appear then | |||
local show; | |||
if type( appear ) == "string" then | |||
show = appear; | |||
else | |||
show = attempt; | |||
end | |||
r = string.format( "%s|%s", r, show ); | |||
end | |||
return r; | |||
end -- WLink.pageLink() | |||
function WLink.pageTarget( attempt, assure ) | |||
-- Create standardized wikilink target of a page | |||
-- Precondition: | |||
-- attempt -- string, with presumable link | |||
-- expected to be enclosed in "[[" "]]" | |||
-- else wikilink | |||
-- table, of assignments with { type, value } | |||
-- type is one of "lead", | |||
-- "project", "lang", | |||
-- "ns", "space", "title" | |||
-- assure -- string or nil, shield against wiki template syntax | |||
-- "URL" or "WIKI" | |||
-- Postcondition: | |||
-- Returns string with link target | |||
local p = type( attempt ); | |||
local s = assure; | |||
local r; | |||
if p == "string" then | |||
p = WLink.wikilink( attempt ); | |||
elseif p == "table" then | |||
p = attempt; | |||
else | |||
p = false; | |||
end | |||
if p then | |||
local site = p.project; | |||
local slang = p.lang; | |||
local lead; | |||
if p.title:sub( 1, 1 ) == "#" then | |||
p.title = mw.title.getCurrentTitle().text .. p.title; | |||
end | |||
if p.ns then | |||
if not slang then | |||
p = farming( p ); | |||
end | |||
if p.lang and | |||
p.lang ~= mw.language.getContentLanguage():getCode() then | |||
p.language = true; | |||
end | |||
if p.language then | |||
p.space = mw.site.namespaces[ p.ns ].canonicalName; | |||
end | |||
lead = ( p.ns == 6 or p.ns == 14 ); | |||
end | |||
if slang then | |||
lead = true; | |||
end | |||
if s == "WIKI" then | |||
if not site and | |||
( lead or | |||
( not p.space and | |||
p.title and p.title:match( "^[*;]" ) ) ) then | |||
p = farming( p ); | |||
site = p.project; | |||
slang = p.lang; | |||
end | |||
s = false; | |||
end | |||
if site then | |||
r = site .. ":"; | |||
elseif lead then | |||
r = ":"; | |||
else | |||
r = ""; | |||
end | |||
if slang then | |||
r = string.format( "%s%s:", r, slang ); | |||
end | |||
if p.space then | |||
r = string.format( "%s%s:", r, p.space ); | |||
end | |||
if p.title then | |||
r = r .. p.title; | |||
end | |||
if r == "" then | |||
r = false; | |||
end | |||
end | |||
if not r then | |||
p = { lang = mw.language.getContentLanguage():getCode() }; | |||
if s == "WIKI" then | |||
r = WLink.pageTarget( p, "WIKI" ); | |||
else | |||
r = string.format( ":%s:", p.lang ); | |||
end | |||
end | |||
if s == "URL" and r:match( "^[*#;:]" ) then | |||
r = mw.uri.encode( r:sub( 1, 1 ) ) .. r:sub( 2 ); | |||
end | |||
return r; | |||
end -- WLink.pageTarget() | |||
Zeile 1.038: | Zeile 1.511: | ||
-- Postcondition: | -- Postcondition: | ||
-- Returns table or false | -- Returns table or false | ||
-- table of assignments with { type, value} | -- table of assignments with { type, value } | ||
-- type is one of "lead", | -- type is one of "lead", | ||
-- "project", "lang", | -- "project", "lang", | ||
-- "ns", "space", "title" | -- "ns", "space", "title" | ||
-- false if nothing found | -- false if nothing found | ||
local s = contentWikilink( attempt ); | local s = contentWikilink( attempt or "" ); | ||
local got, n, r; | local got, i, n, r; | ||
if not s then | if not s then | ||
s = attempt; | s = attempt; | ||
end | |||
if s:find( "%", 1, true ) then | |||
s = mw.uri.decode( s, "PATH" ); | |||
end | end | ||
i = s:find( "|", 1, true ); | i = s:find( "|", 1, true ); | ||
Zeile 1.059: | Zeile 1.535: | ||
local j, k, o, v; | local j, k, o, v; | ||
r = { title = "" }; | r = { title = "" }; | ||
while ( got[ 1 ] == "" ) do | |||
r.lead = true; | |||
table.remove( got, 1 ); | |||
n = n - 1; | |||
end -- while got[ 1 ] == "" | |||
if n > 4 then | if n > 4 then | ||
k = 4; | k = 4; | ||
elseif n > 1 then | |||
k = n - 1; | |||
else | else | ||
k = | k = 1; | ||
end | end | ||
j = k; | j = k; | ||
Zeile 1.068: | Zeile 1.551: | ||
s = mw.text.trim( got[ i ] ); | s = mw.text.trim( got[ i ] ); | ||
if s ~= "" then | if s ~= "" then | ||
o = mw.site.namespaces[ | o = mw.site.namespaces[ s ]; | ||
if o then | if o then | ||
r.ns = o.id; | r.ns = o.id; | ||
Zeile 1.079: | Zeile 1.562: | ||
end -- for i | end -- for i | ||
for i = 1, j do | for i = 1, j do | ||
o, v = prefix( got[ i ] | o, v = prefix( got[ i ] ); | ||
if o then | if o then | ||
if r[ o ] then | if r[ o ] then | ||
Zeile 1.085: | Zeile 1.568: | ||
break; -- for i | break; -- for i | ||
else | else | ||
if i >= k then | |||
k = i + 1; | |||
end | |||
r[ o ] = v; | r[ o ] = v; | ||
end | end | ||
else | else | ||
if i == 1 and r.ns then | |||
r.ns = false; | |||
r.space = false; | |||
end | |||
k = i; | k = i; | ||
break; -- for i | break; -- for i | ||
end | end | ||
end -- for i | end -- for i | ||
for i = k, n do | if k > 0 then | ||
for i = k, n do | |||
r.title = r.title .. got[ i ]; | |||
if i < n then | |||
r.title = r.title .. ":"; | |||
end | |||
end -- for i | |||
end | |||
end | end | ||
r.title = cleanWikilink( r.title ); | |||
if r.lead and | if r.lead and | ||
( r.project | ( r.project or | ||
( not r.lang and r.ns ~= 6 and r.ns ~= 14 ) ) then | ( not r.lang and r.ns ~= 6 and r.ns ~= 14 ) ) then | ||
r.lead = false; | r.lead = false; | ||
Zeile 1.109: | Zeile 1.602: | ||
Failsafe.failsafe = function ( atleast ) | |||
-- Retrieve versioning and check for compliance | -- Retrieve versioning and check for compliance | ||
-- Precondition: | -- Precondition: | ||
-- | -- atleast -- string, with required version | ||
-- or wikidata|item|~|@ or false | |||
-- Postcondition: | -- Postcondition: | ||
-- Returns string with | -- Returns string -- with queried version/item, also if problem | ||
local r | -- false -- if appropriate | ||
if | -- 2020-08-17 | ||
local since = atleast | |||
local last = ( since == "~" ) | |||
r = | local linked = ( since == "@" ) | ||
local link = ( since == "item" ) | |||
local r | |||
if last or link or linked or since == "wikidata" then | |||
local item = Failsafe.item | |||
since = false | |||
if type( item ) == "number" and item > 0 then | |||
local suited = string.format( "Q%d", item ) | |||
if link then | |||
r = suited | |||
else | |||
local entity = mw.wikibase.getEntity( suited ) | |||
if type( entity ) == "table" then | |||
local seek = Failsafe.serialProperty or "P348" | |||
local vsn = entity:formatPropertyValues( seek ) | |||
if type( vsn ) == "table" and | |||
type( vsn.value ) == "string" and | |||
vsn.value ~= "" then | |||
if last and vsn.value == Failsafe.serial then | |||
r = false | |||
elseif linked then | |||
if mw.title.getCurrentTitle().prefixedText | |||
== mw.wikibase.getSitelink( suited ) then | |||
r = false | |||
else | |||
r = suited | |||
end | |||
else | |||
r = vsn.value | |||
end | |||
end | |||
end | |||
end | |||
end | |||
end | |||
if type( r ) == "nil" then | |||
if not since or since <= Failsafe.serial then | |||
r = Failsafe.serial | |||
else | |||
r = false | |||
end | |||
end | end | ||
return r | return r | ||
end -- | end -- Failsafe.failsafe() | ||
Zeile 1.138: | Zeile 1.672: | ||
local s = false; | local s = false; | ||
local r = false; | local r = false; | ||
local space; | local safe, space; | ||
for k, v in pairs( frame.args ) do | for k, v in pairs( frame.args ) do | ||
if k == 1 then | if k == 1 then | ||
Zeile 1.146: | Zeile 1.680: | ||
s = mw.text.trim( v ); | s = mw.text.trim( v ); | ||
end | end | ||
elseif action == " | elseif ( k == 2 and | ||
( action == "getNamespaced" or | |||
action == "getWikilink" or | |||
action == "pageLink" ) ) or | |||
( k == "space" and action == "ansiPercent" ) then | |||
v = mw.text.trim( v ); | |||
if v ~= "" then | if v ~= "" then | ||
space = v; | space = v; | ||
end | end | ||
elseif k == "safe" and action == "pageLink" then | |||
v = mw.text.trim( v ); | |||
if v ~= "" then | |||
safe = v; | |||
end | |||
elseif k == "lines" and action == "isValidLinktext" then | |||
space = ( k == "1" ); | |||
elseif k ~= "template" then | elseif k ~= "template" then | ||
lucky = false; | lucky = false; | ||
Zeile 1.162: | Zeile 1.708: | ||
if lucky then | if lucky then | ||
if s or lone then | if s or lone then | ||
lucky, r = pcall( WLink[ action ], s, space ); | lucky, r = pcall( WLink[ action ], s, space, safe ); | ||
else | else | ||
r = "Parameter missing"; | r = "Parameter missing"; | ||
Zeile 1.177: | Zeile 1.723: | ||
end | end | ||
else | else | ||
local e = mw.html.create( "span" ); | |||
r = tostring( e:addClass( "error" ) | |||
:wikitext( r ) ); | |||
end | end | ||
return r; | return r; | ||
Zeile 1.216: | Zeile 1.764: | ||
p.getLanguage = function ( frame ) | p.getLanguage = function ( frame ) | ||
return Template( frame, "getLanguage" ); | return Template( frame, "getLanguage" ); | ||
end | |||
p.getLinktextProblem = function ( frame ) | |||
return Template( frame, "getLinktextProblem" ); | |||
end | end | ||
p.getNamespace = function ( frame ) | p.getNamespace = function ( frame ) | ||
return tostring( Template( frame, "getNamespace" ) ); | return tostring( Template( frame, "getNamespace" ) ); | ||
end | |||
p.getNamespaced = function ( frame ) | |||
return tostring( Template( frame, "getNamespaced" ) ); | |||
end | end | ||
p.getPlain = function ( frame ) | p.getPlain = function ( frame ) | ||
Zeile 1.225: | Zeile 1.779: | ||
p.getProject = function ( frame ) | p.getProject = function ( frame ) | ||
return Template( frame, "getProject" ); | return Template( frame, "getProject" ); | ||
end | |||
p.getTalkPage = function ( frame ) | |||
return Template( frame, "getTalkPage" ); | |||
end | end | ||
p.getTarget = function ( frame ) | p.getTarget = function ( frame ) | ||
Zeile 1.237: | Zeile 1.794: | ||
p.getWeblink = function ( frame ) | p.getWeblink = function ( frame ) | ||
return Template( frame, "getWeblink" ); | return Template( frame, "getWeblink" ); | ||
end | |||
p.getWikilink = function ( frame ) | |||
return Template( frame, "getWikilink" ); | |||
end | end | ||
p.isBracketedLink = function ( frame ) | p.isBracketedLink = function ( frame ) | ||
Zeile 1.258: | Zeile 1.818: | ||
p.isMedia = function ( frame ) | p.isMedia = function ( frame ) | ||
return Template( frame, "isMedia" ); | return Template( frame, "isMedia" ); | ||
end | |||
p.isTalkPage = function ( frame ) | |||
return Template( frame, "isTalkPage" ); | |||
end | end | ||
p.isTitledLink = function ( frame ) | p.isTitledLink = function ( frame ) | ||
Zeile 1.264: | Zeile 1.827: | ||
p.isValidLink = function ( frame ) | p.isValidLink = function ( frame ) | ||
return Template( frame, "isValidLink" ); | return Template( frame, "isValidLink" ); | ||
end | |||
p.isValidLinktext = function ( frame ) | |||
return Template( frame, "isValidLinktext" ); | |||
end | end | ||
p.isWeblink = function ( frame ) | p.isWeblink = function ( frame ) | ||
Zeile 1.270: | Zeile 1.836: | ||
p.isWikilink = function ( frame ) | p.isWikilink = function ( frame ) | ||
return Template( frame, "isWikilink" ); | return Template( frame, "isWikilink" ); | ||
end | |||
p.pageLink = function ( frame ) | |||
return Template( frame, "pageLink" ); | |||
end | end | ||
p.failsafe = function ( frame ) | p.failsafe = function ( frame ) | ||
Zeile 1.285: | Zeile 1.854: | ||
end | end | ||
end | end | ||
return | return Failsafe.failsafe( since ) or ""; | ||
end | end -- p.failsafe | ||
p.WLink = function () | p.WLink = function () | ||
return WLink; | return WLink; |
Die Dokumentation für dieses Modul kann unter Modul:WLink/Doku erstellt werden
local WLink = { suite = "WLink", serial = "2022-05-09", item = 19363224, globals = { URLutil = 10859193 } }; --[=[ ansiPercent() formatURL() getArticleBase() getBaseTitle() getEscapedTitle() getExtension() getFile() getFragment() getLanguage() getLinktextProblem() getNamespace() getNamespaced() getPlain() getProject() getTarget() getTalkPage() getTargetPage() getTitle() getWeblink() getWikilink() isBracketedLink() isBracketedURL() isCategorization() isExternalLink() isInterlanguage() isInterwiki() isMedia() isTalkPage() isTitledLink() isValidLink() isValidLinktext() isWikilink() pageLink() pageTarget() wikilink() failsafe() ]=] -- local globals local URLutil = false; local Failsafe = WLink; local GlobalMod = WLink; local htmlInline = { b = true, bdi = true, bdo = true, big = true, code = true, em = true, i = true, kbd = true, s = true, samp = true, small = true, span = true, strong = true, style = true, sub = true, sup = true, tt = true, var = true }; local foreignModule = function ( access, advanced, append, alt, alert ) -- Fetch global module -- Precondition: -- access -- string, with name of base module -- advanced -- true, for require(); else mw.loadData() -- append -- string, with subpage part, if any; or false -- alt -- number, of wikidata item of root; or false -- alert -- true, for throwing error on data problem -- Postcondition: -- Returns whatever, probably table -- 2020-01-01 local storage = access; local finer = function () if append then storage = string.format( "%s/%s", storage, append ); end end local fun, lucky, r, suited; if advanced then fun = require; else fun = mw.loadData; end GlobalMod.globalModules = GlobalMod.globalModules or { }; suited = GlobalMod.globalModules[ access ]; if not suited then finer(); lucky, r = pcall( fun, "Module:" .. storage ); end if not lucky then if not suited and type( alt ) == "number" and alt > 0 then suited = string.format( "Q%d", alt ); suited = mw.wikibase.getSitelink( suited ); GlobalMod.globalModules[ access ] = suited or true; end if type( suited ) == "string" then storage = suited; finer(); lucky, r = pcall( fun, storage ); end if not lucky and alert then error( "Missing or invalid page: " .. storage ); end end return r; end -- foreignModule() local utilURL = function () -- Attach URLutil library module -- Postcondition: -- Returns table, with URLutil library -- Throws error, if not available if not URLutil then local util = foreignModule( "URLutil", true, false, WLink.globals.URLutil ); if type( util ) == "table" then URLutil = util.URLutil(); else util = "library URLutil invalid"; end if type( URLutil ) ~= "table" then error( util, 0 ); end end return URLutil; end -- utilURL() local cleanWikilink = function ( access ) -- Refine wikilink spacing and decode -- Precondition: -- access -- string, with presumable link -- Postcondition: -- Returns string, with pretty target local r; if not WLink.lrm then WLink.lrm = mw.ustring.char( 0x200E ); WLink.rlm = mw.ustring.char( 0x200F ); end r = access:gsub( "_", " " ) :gsub( " ", " " ) :gsub( " ", " " ) :gsub( " ", " " ) :gsub( " ", " " ) :gsub( "‎", "" ) :gsub( "‏", "" ) :gsub( WLink.lrm, "" ) :gsub( WLink.rlm, "" ) :gsub( "%s+", " " ); r = mw.text.decode( r ); return r; end -- cleanWikilink() 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( i + 2 ); r2 = i; else r1 = s; r2 = 1; end end return r1, r2, r3; end -- contentWikilink() local extractExtlink = function ( attempt ) -- Retrieve external link -- Precondition: -- attempt -- string, with presumable link -- the first char is expected to be "[" -- Postcondition: -- Returns string, string -- first with target and title -- second result false if not titled -- false if nothing found local r1 = false; local r2 = false; local s = contentExtlink( attempt ); if s then local i = s:find( "%s", 1 ); if i then r1 = s:sub( 1, i - 1 ); r2 = mw.text.trim( s:sub( i + 1 ) ); if r2 == "" then r2 = false; end else r1 = s; end if r1 then r1 = mw.text.trim( r1 ); if r1 == "" or not utilURL().isResourceURL( r1 ) then r1 = false; end end if not r1 then r2 = false; end end return r1, r2; end -- extractExtlink() local extractWikilink = function ( attempt ) -- Retrieve wikilink -- Precondition: -- attempt -- string, with presumable link -- the first two chars are expected to be "[[" -- Postcondition: -- Returns string, string -- first with target -- second result title, or false if not piped -- false if nothing found local r1 = false; local r2 = false; local s = contentWikilink( attempt ); if s then 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 = cleanWikilink( r1 ); end end return r1, r2; end -- extractWikilink() local farming = function ( already ) -- Retrieve wikifarm project information -- Precondition: -- already -- table, with wikilink components -- Postcondition: -- Returns table, with wikilink components extended local r = already; if not r.project then local codes = { mediawiki = "mw", wikibooks = "b", wikidata = "d", wikinews = "n", wikipedia = "w", wikiquote = "q", wikisource = "s", wikiversity = "v", wikivoyage = "voy", wiktionary = "wikt" }; local server = mw.site.server:gsub( "([/.])m%.", "%1" ) :gsub( "%.beta%.wmflabs%.org$", ".org" ); local site = server:match( "[/.](%l+)%.org$" ); r.project = codes[ site ]; if r.project then if not r.lang and r.project ~= "mw" and r.project ~= "d" then r.lang = server:match( "//(%l+)%." ); end else site = server:match( "//(%l+)%.wikimedia%.org$" ); if site == "commons" or site == "meta" then r.project = site; end end end if r.project and r.ns and ( r.project == "commons" or r.project == "d" or r.project == "meta" or r.project == "mw" ) then r.language = true; end return r; end -- farming() local prefix = function ( ask ) -- Interprete prefix of language or project type -- Precondition: -- ask -- string, with presumable prefix -- 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 = { b = true, c = "commons", d = true, commons = true, m = "meta", mediawiki = "mw", mw = true, meta = true, n = true, q = true, s = true, simple = false, v = true, voy = true, w = true, wikibooks = "b", wikidata = "d", wikinews = "n", wikipedia = "w", wikiquote = "q", wikisource = "s", wikiversity = "v", wikivoyage = "voy", wikt = true, wiktionary = "wikt" }; local s = mw.text.trim( ask ); if s ~= "" then 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( "%%%02X", 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 return r; end -- WLink.ansiPercent() function WLink.formatURL( adjust, assure ) -- Create bracketed link, if not yet -- Precondition: -- adjust -- string, with URL or domain/path or bracketed link -- assure -- boolean, true for secure HTTP -- 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 = "://" .. adjust; if assure then url = "s" .. url; end url = "http" .. url; 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( "%[", "[" ) :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.getLinktextProblem( attempt ) -- Which problem has this presumable link text? -- Precondition: -- attempt -- string, with presumable linktext -- Postcondition: -- Returns string, with error message, or false local r; if attempt:find( "]", 1, true ) then r = "]"; elseif mw.text.unstripNoWiki( attempt ) ~= attempt then r = "<nowiki>"; elseif attempt:find( "\n", 1, true ) then r = "\n"; elseif mw.text.unstrip( attempt ) ~= attempt then if not WLink.stripREF then WLink.stripREF = string.format( "%c%c%c%c%s%c%c%c%c", 127, 39, 34, 96, "UNIQ%-+ref%-%x+%-QINU", 96, 34, 39, 127 ); end if mw.ustring.find( attempt, WLink.stripREF ) then r = "<ref>"; end end if not r then local i = attempt:find( "<", 1, true ); if i then local s = mw.ustring.lower( attempt:sub( i ) ); local sign = true; local skip; while sign and not r do skip, sign = s:match( "^([^<]*< *)(%l[%l%d]*)[ /]*.*>" ); if sign then if htmlInline[ sign ] then i = skip:len() + sign:len() + 1; s = s:sub( i ); else r = string.format( "<%s>", sign ); end end end -- while sign and not r end if not r then local s = attempt .. " "; if s:find( "ISBN ", 1, true ) then r = s:match( "(ISBN %d[%-%d]+[%dxX])%W" ); end if not r then if s:find( "PMID ", 1, true ) then r = s:match( "(PMID [1-9]%d*)%W" ); end if not r then if s:find( "RFC ", 1, true ) then r = s:match( "(RFC [1-9]%d?%d?%d?)%W" ); end end end end end return r or false; end -- WLink.getLinktextProblem() 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.getNamespaced( area, attempt ) -- Retrieve page in namespace -- Precondition: -- area -- string or number, with some namespace spec -- attempt -- string, with wikilink or page title or page name -- Postcondition: -- Returns page prefixed by namespace, -- false if invalid local r = false; local s = type( area ); local room; if s == "string" then room = mw.site.namespaces[ tonumber( area ) or area ]; elseif s == "number" then room = mw.site.namespaces[ area ]; end if room then local m; s, m = WLink.getTarget( attempt ); if not s then s = attempt; elseif m ~= 2 then s = false; end if s then local w = WLink.wikilink( s ); if w and not w.lang and not w.project and ( not w.ns or w.ns == room.id ) then r = string.format( "%s:%s", room.name, w.title ); end end end return r; end -- WLink.getNamespaced() 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, span, 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 + 1; 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 span, shift = extractExtlink( suffix ); if span then if not shift then shift = ""; end else shift = string.format( "[%s]", s ); 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.getTalkPage( attempt ) -- Retrieve talk page name for attempt, or that page name itself -- Precondition: -- attempt -- string, with presumable link somewhere -- Postcondition: -- Returns string or false local r = false; local s, m = WLink.getTarget( attempt ); if m ~= 2 and attempt then s = mw.text.trim( attempt ); end if s and s ~= "" then local w = mw.title.new( s ); if w then w = w.talkPageTitle; if w then r = w.prefixedText; end end end return r; end -- WLink.getTalkPage() 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 service = attempt; local show; if #attempt == #site then site = site .. "/"; end show = URLutil.getTop3domain( "//" .. site ); if show then local scan = "[%./](%a[%a%%%-]*%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:sub( 2 ), 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 if not service:match( "^[a-z:]*//.+/" ) then service = service .. "/"; end r = string.format( "[%s %s]", service, show ); else r = attempt; end return r; end -- WLink.getWeblink() function WLink.getWikilink( attempt, appear ) -- Retrieve bracketed link from text -- Precondition: -- attempt -- string, with current target, or plain -- appear -- string, with link title, or nil -- Postcondition: -- Returns string, with first detected link target -- false if nothing found local r = WLink.pageTarget( attempt ); if r then if appear then local show = WLink.getEscapedTitle( appear ); if show ~= r and show ~= "" then r = string.format( "%s|%s", r, show ); end end r = string.format( "[[%s]]", r ); end return r; end -- WLink.getWikilink() 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.isTalkPage( attempt ) -- Does attempt describe a talk page? -- Precondition: -- attempt -- string, with presumable link somewhere -- Postcondition: -- Returns boolean local r = false; local s, m = WLink.getTarget( attempt ); if m ~= 2 and attempt then s = mw.text.trim( attempt ); end if s and s ~= "" then local w = mw.title.new( s ); if w then r = w.isTalkPage; end end return r; end -- WLink.isTalkPage() 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 u, r = WLink.getTarget( attempt ); if r then if r < 2 then if u:find( "''", 1, true ) then r = false; else r = true; end else r = true; end end return r; end -- WLink.isValidLink() function WLink.isValidLinktext( attempt, allow ) -- Is attempt a plain inline text? -- Precondition: -- attempt -- string, with presumable linktext -- allow -- boolean or nil, if multiline permitted -- Postcondition: -- Returns boolean local s; if allow and s:find( "\n", 1, true ) then s = attempt:gsub( "\n", " " ); else s = attempt; end return not WLink.getLinktextProblem( s ); end -- WLink.isValidLinktext() 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.pageLink( attempt, appear, assure ) -- Create safely standardized wikilink target of a page -- attempt -- string, with presumable link -- appear -- string or true or nil, with link title -- assure -- string or nil, shield against wiki template syntax -- "URL" or "WIKI" -- Postcondition: -- Returns string with link target local r = WLink.pageTarget( attempt, assure ); if appear then local show; if type( appear ) == "string" then show = appear; else show = attempt; end r = string.format( "%s|%s", r, show ); end return r; end -- WLink.pageLink() function WLink.pageTarget( attempt, assure ) -- Create standardized wikilink target of a page -- Precondition: -- attempt -- string, with presumable link -- expected to be enclosed in "[[" "]]" -- else wikilink -- table, of assignments with { type, value } -- type is one of "lead", -- "project", "lang", -- "ns", "space", "title" -- assure -- string or nil, shield against wiki template syntax -- "URL" or "WIKI" -- Postcondition: -- Returns string with link target local p = type( attempt ); local s = assure; local r; if p == "string" then p = WLink.wikilink( attempt ); elseif p == "table" then p = attempt; else p = false; end if p then local site = p.project; local slang = p.lang; local lead; if p.title:sub( 1, 1 ) == "#" then p.title = mw.title.getCurrentTitle().text .. p.title; end if p.ns then if not slang then p = farming( p ); end if p.lang and p.lang ~= mw.language.getContentLanguage():getCode() then p.language = true; end if p.language then p.space = mw.site.namespaces[ p.ns ].canonicalName; end lead = ( p.ns == 6 or p.ns == 14 ); end if slang then lead = true; end if s == "WIKI" then if not site and ( lead or ( not p.space and p.title and p.title:match( "^[*;]" ) ) ) then p = farming( p ); site = p.project; slang = p.lang; end s = false; end if site then r = site .. ":"; elseif lead then r = ":"; else r = ""; end if slang then r = string.format( "%s%s:", r, slang ); end if p.space then r = string.format( "%s%s:", r, p.space ); end if p.title then r = r .. p.title; end if r == "" then r = false; end end if not r then p = { lang = mw.language.getContentLanguage():getCode() }; if s == "WIKI" then r = WLink.pageTarget( p, "WIKI" ); else r = string.format( ":%s:", p.lang ); end end if s == "URL" and r:match( "^[*#;:]" ) then r = mw.uri.encode( r:sub( 1, 1 ) ) .. r:sub( 2 ); end return r; end -- WLink.pageTarget() 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, value } -- type is one of "lead", -- "project", "lang", -- "ns", "space", "title" -- false if nothing found local s = contentWikilink( attempt or "" ); local got, i, n, r; if not s then s = attempt; end if s:find( "%", 1, true ) then s = mw.uri.decode( s, "PATH" ); 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 = { title = mw.text.trim( s ) }; else local j, k, o, v; r = { title = "" }; while ( got[ 1 ] == "" ) do r.lead = true; table.remove( got, 1 ); n = n - 1; end -- while got[ 1 ] == "" if n > 4 then k = 4; elseif n > 1 then k = n - 1; else k = 1; end j = k; for i = 1, j do s = mw.text.trim( got[ i ] ); if s ~= "" then o = mw.site.namespaces[ s ]; 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 ] ); if o then if r[ o ] then k = i; break; -- for i else if i >= k then k = i + 1; end r[ o ] = v; end else if i == 1 and r.ns then r.ns = false; r.space = false; end k = i; break; -- for i end end -- for i if k > 0 then for i = k, n do r.title = r.title .. got[ i ]; if i < n then r.title = r.title .. ":"; end end -- for i end end r.title = cleanWikilink( r.title ); if r.lead and ( r.project or ( not r.lang and r.ns ~= 6 and r.ns ~= 14 ) ) then r.lead = false; end return r; end -- WLink.wikilink() Failsafe.failsafe = function ( atleast ) -- Retrieve versioning and check for compliance -- Precondition: -- atleast -- string, with required version -- or wikidata|item|~|@ or false -- Postcondition: -- Returns string -- with queried version/item, also if problem -- false -- if appropriate -- 2020-08-17 local since = atleast local last = ( since == "~" ) local linked = ( since == "@" ) local link = ( since == "item" ) local r if last or link or linked or since == "wikidata" then local item = Failsafe.item since = false if type( item ) == "number" and item > 0 then local suited = string.format( "Q%d", item ) if link then r = suited else local entity = mw.wikibase.getEntity( suited ) if type( entity ) == "table" then local seek = Failsafe.serialProperty or "P348" local vsn = entity:formatPropertyValues( seek ) if type( vsn ) == "table" and type( vsn.value ) == "string" and vsn.value ~= "" then if last and vsn.value == Failsafe.serial then r = false elseif linked then if mw.title.getCurrentTitle().prefixedText == mw.wikibase.getSitelink( suited ) then r = false else r = suited end else r = vsn.value end end end end end end if type( r ) == "nil" then if not since or since <= Failsafe.serial then r = Failsafe.serial else r = false end end return r end -- Failsafe.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 safe, 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 ( k == 2 and ( action == "getNamespaced" or action == "getWikilink" or action == "pageLink" ) ) or ( k == "space" and action == "ansiPercent" ) then v = mw.text.trim( v ); if v ~= "" then space = v; end elseif k == "safe" and action == "pageLink" then v = mw.text.trim( v ); if v ~= "" then safe = v; end elseif k == "lines" and action == "isValidLinktext" then space = ( k == "1" ); 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, safe ); 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 local e = mw.html.create( "span" ); r = tostring( e:addClass( "error" ) :wikitext( r ) ); end return r; end -- Template() -- Export local p = { }; p.ansiPercent = function ( frame ) return Template( frame, "ansiPercent" ); end p.formatURL = function ( frame ) return Template( frame, "formatURL" ); end p.getArticleBase = function ( frame ) return Template( frame, "getArticleBase", false, true ); end p.getBaseTitle = function ( frame ) return Template( frame, "getBaseTitle" ); end p.getEscapedTitle = function ( frame ) return Template( frame, "getEscapedTitle" ); end p.getExtension = function ( frame ) return Template( frame, "getExtension" ); end p.getFile = function ( frame ) return Template( frame, "getFile" ); end p.getFragment = function ( frame ) return Template( frame, "getFragment" ); end p.getInterwiki = function ( frame ) return Template( frame, "getInterwiki" ); end p.getLanguage = function ( frame ) return Template( frame, "getLanguage" ); end p.getLinktextProblem = function ( frame ) return Template( frame, "getLinktextProblem" ); end p.getNamespace = function ( frame ) return tostring( Template( frame, "getNamespace" ) ); end p.getNamespaced = function ( frame ) return tostring( Template( frame, "getNamespaced" ) ); end p.getPlain = function ( frame ) return Template( frame, "getPlain" ); end p.getProject = function ( frame ) return Template( frame, "getProject" ); end p.getTalkPage = function ( frame ) return Template( frame, "getTalkPage" ); end p.getTarget = function ( frame ) return Template( frame, "getTarget" ); end p.getTargetPage = function ( frame ) return Template( frame, "getTargetPage" ); end p.getTitle = function ( frame ) return Template( frame, "getTitle" ); end p.getWeblink = function ( frame ) return Template( frame, "getWeblink" ); end p.getWikilink = function ( frame ) return Template( frame, "getWikilink" ); end p.isBracketedLink = function ( frame ) return Template( frame, "isBracketedLink" ); end p.isBracketedURL = function ( frame ) return Template( frame, "isBracketedURL" ); end p.isCategorization = function ( frame ) return Template( frame, "isCategorization" ); end p.isExternalLink = function ( frame ) return Template( frame, "isExternalLink" ); end p.isInterlanguage = function ( frame ) return Template( frame, "isInterlanguage" ); end p.isInterwiki = function ( frame ) return Template( frame, "isInterwiki" ); end p.isMedia = function ( frame ) return Template( frame, "isMedia" ); end p.isTalkPage = function ( frame ) return Template( frame, "isTalkPage" ); end p.isTitledLink = function ( frame ) return Template( frame, "isTitledLink" ); end p.isValidLink = function ( frame ) return Template( frame, "isValidLink" ); end p.isValidLinktext = function ( frame ) return Template( frame, "isValidLinktext" ); end p.isWeblink = function ( frame ) return Template( frame, "isWeblink" ); end p.isWikilink = function ( frame ) return Template( frame, "isWikilink" ); end p.pageLink = function ( frame ) return Template( frame, "pageLink" ); end p.failsafe = function ( frame ) local s = type( frame ); local since; if s == "table" then since = frame.args[ 1 ]; elseif s == "string" then since = frame; end if since then since = mw.text.trim( since ); if since == "" then since = false; end end return Failsafe.failsafe( since ) or ""; end -- p.failsafe p.WLink = function () return WLink; end return p;