local WLink = { suite = "WLink",
serial = "2020-07-01",
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, s );
s = false;
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" or "~"
-- or false
-- Postcondition:
-- Returns string -- with queried version, also if problem
-- false -- if appropriate
local last = ( atleast == "~" );
local since = atleast;
local r;
if last or since == "wikidata" then
local item = Failsafe.item;
since = false;
if type( item ) == "number" and item > 0 then
local ent = mw.wikibase.getEntity( string.format( "Q%d",
item ) );
if type( ent ) == "table" then
local vsn = ent:formatPropertyValues( "P348" );
if type( vsn ) == "table" and
type( vsn.value ) == "string" and
vsn.value ~= "" then
if last and vsn.value == Failsafe.serial then
r = false;
else
r = vsn.value;
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;