Módulo:WLink

A documentação para este módulo pode ser criada na página Módulo:WLink/doc

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( "%[", "&#91;" )
												:gsub( "%]", "&#93;" )
												:gsub( "|",  "&#124;" );
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 = "&#93;";
				elseif mw.text.unstripNoWiki( attempt ) ~= attempt then
								r = "&lt;nowiki&gt;";
				elseif attempt:find( "\n", 1, true ) then
								r = "&#92;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 = "&lt;ref&gt;";
								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( "&lt;%s&gt;", 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;