Módulo:URLutil

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

local URLutil = { suite  = "URLutil",
																		serial = "2022-04-05",
																		item   = 10859193 }
--[=[
Utilities for URL etc. on www.
* decode()
* encode()
* getAuthority()
* getFragment()
* getHost()
* getLocation()
* getNormalized()
* getPath()
* getPort()
* getQuery()
* getQueryTable()
* getRelativePath()
* getScheme()
* getSortkey()
* getTLD()
* getTop2domain()
* getTop3domain()
* isAuthority()
* isDomain()
* isDomainExample()
* isDomainInt()
* isHost()
* isHostPathResource()
* isIP()
* isIPlocal()
* isIPv4()
* isIPv6()
* isMailAddress()
* isMailLink()
* isProtocolDialog()
* isProtocolWiki()
* isResourceURL()
* isSuspiciousURL()
* isUnescapedURL()
* isWebURL()
* wikiEscapeURL()
* failsafe()
Only [[dotted decimal]] notation for IPv4 expected.
Does not support dotted hexadecimal, dotted octal, or single-number formats.
IPv6 URL (bracketed) not yet implemented; might need Wikintax escaping anyway.
]=]
local Failsafe  = URLutil
local decodeComponentProtect = { F = "\"#%<>[\]^`{|}",
																																	P = "\"#%<>[\]^`{|}/?",
																																	Q = "\"#%<>[\]^`{|}&=+;,",
																																	X = "\"#%<>[\]^`{|}&=+;,/?" }
local decodeComponentEscape = function ( averse, adapt )
				return  adapt == 20  or  adapt == 127  or
												decodeComponentProtect[ averse ]:find( string.char( adapt ),
																																																			1,
																																																			true )
end -- decodeComponentEscape()
local decodeComponentML = function ( ask )
				local i = 1
				local j, n, s
				while ( i ) do
								i = ask:find( "&#[xX]%x%x+;", i )
								if i then
												j = ask:find( ";",  i + 3,  true )
												s = ask:sub( i + 2,  j - 1 ):upper()
												n = s:byte( 1, 1 )
												if n == 88 then
																n = tonumber( s:sub( 2 ),  16 )
												elseif s:match( "^%d+$" ) then
																n = tonumber( s )
												else
																n = false
												end
												if n then
																if n >= 128 then
																				s = string.format( "&#%d;", n )
																elseif decodeComponentEscape( "X", n ) then
																				s = string.format( "%%%02X", n )
																else
																				s = string.format( "%c", n )
																end
																j = j + 1
																if i == 1 then
																				ask = s .. ask:sub( j )
																else
																				ask = string.format( "%s%s%s",
																																									ask:sub( 1,  i - 1 ),
																																									s,
																																									ask:sub( j ) )
																end
												end
												i = i + 1
								end
				end -- while i
				return ask
end -- decodeComponentML()
local decodeComponentPercent = function ( ask, averse )
				local i = 1
				local j, k, m, n
				while ( i ) do
								i = ask:find( "%%[2-7]%x", i )
								if i then
												j = i + 1
												k = j + 1
												n = ask:byte( k, k )
												k = k + 1
												m = ( n > 96 )
												if m then
																n = n - 32
																m = n
												end
												if n > 57 then
																n = n - 55
												else
																n = n - 48
												end
												n = ( ask:byte( j, j ) - 48 )  *  16   +   n
												if n == 39  and
															ask:sub( i + 3,  i + 5 ) == "%27" then
															j = i + 6
															while ( ask:sub( j,  j + 2 )  ==  "%27" ) do
																		j = j + 3
															end -- while "%27"
												elseif decodeComponentEscape( averse, n ) then
																if m then
																				ask = string.format( "%s%c%s",
																																									ask:sub( 1, j ),
																																									m,
																																									ask:sub( k ) )
																end
												elseif i == 1 then
																ask = string.format( "%c%s",  n,  ask:sub( k ) )
												else
																ask = string.format( "%s%c%s",
																																					ask:sub( 1,  i - 1 ),
																																					n,
																																					ask:sub( k ) )
												end
												i = j
								end
				end -- while i
				return ask
end -- decodeComponentPercent()
local getTopDomain = function ( url, mode )
				local r = URLutil.getHost( url )
				if r then
								local pattern = "[%w%%%-]+%.%a[%w%-]*%a)$"
								if mode == 3 then
												pattern = "[%w%%%-]+%." .. pattern
								end
								r = mw.ustring.match( "." .. r,  "%.(" .. pattern )
								if not r then
												r = false
								end
				else
								r = false
				end
				return r
end -- getTopDomain()
local getHash = function ( url )
				local r = url:find( "#", 1, true )
				if r then
								local i = url:find( "&#", 1, true )
								if i then
												local s
												while ( i ) do
																s = url:sub( i + 2 )
																if s:match( "^%d+;" ) or s:match( "^x%x+;" ) then
																				r = url:find( "#",  i + 4,  true )
																				if r then
																								i = url:find( "&#",  i + 4,  true )
																				else
																								i = false
																				end
																else
																				r = i + 1
																				i = false
																end
												end -- while i
								end
				end
				return r
end -- getHash()
URLutil.decode = function ( url, enctype )
				local r, s
				if type( enctype ) == "string" then
								s = mw.text.trim( enctype )
								if s == "" then
												s = false
								else
												s = s:upper()
								end
				end
				r = mw.text.encode( mw.uri.decode( url, s ) )
				if r:find( "[%[|%]]" ) then
								local k
								r, k = r:gsub( "%[", "&#91;" )
																:gsub( "|", "&#124;" )
																:gsub( "%]", "&#93;" )
				end
				return r
end -- URLutil.decode()
URLutil.encode = function ( url, enctype )
				local k, r, s
				if type( enctype ) == "string" then
								s = mw.text.trim( enctype )
								if s == "" then
												s = false
								else
												s = s:upper()
								end
				end
				r = mw.uri.encode( url, s )
				k = r:byte( 1, 1 )
				if -- k == 35  or      -- #
										k == 42  or      -- *
										k == 58  or      -- :
										k == 59 then     -- ;
								r = string.format( "%%%X%s", k, r:sub( 2 ) )
				end
				if r:find( "[%[|%]]" ) then
								r, k = r:gsub( "%[", "%5B" )
																:gsub( "|",  "%7C" )
																:gsub( "%]", "%5D" )
				end
				return r
end -- URLutil.encode()
URLutil.getAuthority = function ( url )
				local r
				if type( url ) == "string" then
								local colon, host, port
								local pattern = "^%s*%w*:?//([%w%.%%_-]+)(:?)([%d]*)/"
								local s = mw.text.decode( url )
								local i = s:find( "#", 6, true )
								if i then
												s = s:sub( 1,  i - 1 )  ..  "/"
								else
												s = s .. "/"
								end
								host, colon, port = mw.ustring.match( s, pattern )
								if URLutil.isHost( host ) then
												host = mw.ustring.lower( host )
												if colon == ":" then
																if port:find( "^[1-9]" ) then
																				r = ( host .. ":" .. port )
																end
												elseif #port == 0 then
																r = host
												end
								end
				else
								r = false
				end
				return r
end -- URLutil.getAuthority()
URLutil.getFragment = function ( url, decode )
				local r
				if type( url ) == "string" then
								local i = getHash( url )
								if i then
												r = mw.text.trim( url:sub( i ) ):sub( 2 )
												if type( decode ) == "string" then
																local encoding = mw.text.trim( decode )
																local launch
																if encoding == "%" then
																				launch = true
																elseif encoding == "WIKI" then
																				r = r:gsub( "%.(%x%x)", "%%%1" )
																									:gsub( "_", " " )
																				launch = true
																end
																if launch then
																				r = mw.uri.decode( r, "PATH" )
																end
												end
								else
												r = false
								end
				else
								r = nil
				end
				return r
end -- URLutil.getFragment()
URLutil.getHost = function ( url )
				local r = URLutil.getAuthority( url )
				if r then
								r = mw.ustring.match( r, "^([%w%.%%_%-]+):?[%d]*$" )
				end
				return r
end -- URLutil.getHost()
URLutil.getLocation = function ( url )
				local r
				if type( url ) == "string" then
								r = mw.text.trim( url )
								if r == "" then
												r = false
								else
												local i
												i = getHash( r )
												if i then
																if i == 1 then
																				r = false
																else
																				r = r:sub( 1,  i - 1 )
																end
												end
								end
				else
								r = nil
				end
				return r
end -- URLutil.getLocation()
URLutil.getNormalized = function ( url )
				local r
				if type( url ) == "string" then
								r = mw.text.trim( url )
								if r == "" then
												r = false
								else
												r = decodeComponentML( r )
								end
				else
								r = false
				end
				if r then
								local k = r:find( "//", 1, true )
								if k then
												local j = r:find( "/",  k + 2,  true )
												local sF, sP, sQ
												if r:find( "%%[2-7]%x" ) then
																local i = getHash( r )
																if i then
																				sF = r:sub( i + 1 )
																				r  = r:sub( 1,  i - 1 )
																				if sF == "" then
																								sF = false
																				else
																								sF = decodeComponentPercent( sF, "F" )
																				end
																end
																i = r:find( "?", 1, true )
																if i then
																				sQ = r:sub( i )
																				r  = r:sub( 1,  i - 1 )
																				sQ = decodeComponentPercent( sQ, "Q" )
																end
																if j then
																				if #r > j then
																								sP = r:sub( j + 1 )
																								sP = decodeComponentPercent( sP, "P" )
																				end
																				r = r:sub( 1,  j - 1 )
																end
												elseif j then
																local n = #r
																if r:byte( n, n ) == 35 then    -- '#'
																				n = n - 1
																				r = r:sub( 1, n )
																end
																if n > j then
																				sP = r:sub( j + 1 )
																end
																r = r:sub( 1,  j - 1 )
												end
												r = mw.ustring.lower( r ) .. "/"
												if sP then
																r = r .. sP
												end
												if sQ then
																r = r .. sQ
												end
												if sF then
																r = string.format( "%s#%s", r, sF )
												end
								end
								r = r:gsub( " ",  "%%20" )
													:gsub( "%[", "%%5B" )
													:gsub( "|",  "%%7C" )
													:gsub( "%]", "%%5D" )
													:gsub( "%<", "%%3C" )
													:gsub( "%>", "%%3E" )
				end
				return r
end -- URLutil.getNormalized()
URLutil.getPath = function ( url )
				local r = URLutil.getRelativePath( url )
				if r then
								local s = r:match( "^([^%?]*)%?" )
								if s then
												r = s
								end
								s = r:match( "^([^#]*)#" )
								if s then
												r = s
								end
				end
				return r
end -- URLutil.getPath()
URLutil.getPort = function ( url )
				local r = URLutil.getAuthority( url )
				if r then
								r = r:match( ":([1-9][0-9]*)$" )
								if r then
												r = tonumber( r )
								else
												r = false
								end
				end
				return r
end -- URLutil.getPort()
URLutil.getQuery = function ( url, key, separator )
				local r = URLutil.getLocation( url )
				if r then
								r = r:match( "^[^%?]*%?(.+)$" )
								if r then
												if type( key ) == "string" then
																local single = mw.text.trim( key )
																local sep = "&"
																local s, scan
																if type( separator ) == "string" then
																				s = mw.text.trim( separator )
																				if s:match( "^[&;,/]$" ) then
																								sep = s
																				end
																end
																s = string.format( "%s%s%s", sep, r, sep )
																scan = string.format( "%s%s=([^%s]*)%s",
																																						sep, key, sep, sep )
																r = s:match( scan )
												end
								end
								if not r then
												r = false
								end
				end
				return r
end -- URLutil.getQuery()
URLutil.getQueryTable = function ( url, separator )
				local r = URLutil.getQuery( url )
				if r then
								local sep = "&"
								local n, pairs, s, set
								if type( separator ) == "string" then
												s = mw.text.trim( separator )
												if s:match( "^[&;,/]$" ) then
																sep = s
												end
								end
								pairs = mw.text.split( r, sep, true )
								n = #pairs
								r = { }
								for i = 1, n do
												s = pairs[ i ]
												if s:find( "=", 2, true ) then
																s, set = s:match( "^([^=]+)=(.*)$" )
																if s then
																				r[ s ] = set
																end
												else
																r[ s ] = false
												end
								end -- for i
				end
				return r
end -- URLutil.getQueryTable()
URLutil.getRelativePath = function ( url )
				local r
				if type( url ) == "string" then
								local s = url:match( "^%s*[a-zA-Z]*://(.*)$" )
								if s then
												s = s:match( "[^/]+(/.*)$" )
								else
												local x
												x, s = url:match( "^%s*(/?)(/.*)$" )
												if x == "/" then
																s = s:match( "/[^/]+(/.*)$" )
												end
								end
								if s then
												r = mw.text.trim( s )
								elseif URLutil.isResourceURL( url ) then
												r = "/"
								else
												r = false
								end
				else
								r = nil
				end
				return r
end -- URLutil.getRelativePath()
URLutil.getScheme = function ( url )
				local r
				if type( url ) == "string" then
								local pattern = "^%s*([a-zA-Z]*)(:?)(//)"
								local prot, colon, slashes = url:match( pattern )
								r = false
								if slashes == "//" then
												if colon == ":" then
																if #prot > 2 then
																				r = prot:lower() .. "://"
																end
												elseif #prot == 0 then
																r = "//"
												end
								end
				else
								r = nil
				end
				return r
end -- URLutil.getScheme()
URLutil.getSortkey = function ( url )
				local r = url
				if type( url ) == "string" then
								local i = url:find( "//" )
								if i then
												local scheme
												if i == 0 then
																scheme = ""
												else
																scheme = url:match( "^%s*([a-zA-Z]*)://" )
												end
												if scheme then
																local s = url:sub( i + 2 )
																local comps, site, m, suffix
																scheme = scheme:lower()
																i      = s:find( "/" )
																if i  and  i > 1 then
																				suffix = s:sub( i + 1 )            -- mw.uri.encode()
																				s      = s:sub( 1,  i - 1 )
																				suffix = suffix:gsub( "#", " " )
																else
																				suffix = ""
																end
																site, m = s:match( "^(.+)(:%d+)$" )
																if not m then
																				site = s
																				m    = 0
																end
																comps = mw.text.split( site:lower(), ".", true )
																r = "///"
																for i = #comps, 2, -1 do
																				r =  string.format( "%s%s.", r, comps[ i ] )
																end -- for --i
																r = string.format( "%s%s %d %s: %s",
																																			r, comps[ 1 ], m, scheme, suffix )
												end
								end
				end
				return r
end -- URLutil.getSortkey()
URLutil.getTLD = function ( url )
				local r = URLutil.getHost( url )
				if r then
								r = mw.ustring.match( r, "%w+%.(%a[%w%-]*%a)$" )
								if not r then
												r = false
								end
				end
				return r
end -- URLutil.getTLD()
URLutil.getTop2domain = function ( url )
				return getTopDomain( url, 2 )
end -- URLutil.getTop2domain()
URLutil.getTop3domain = function ( url )
				return getTopDomain( url, 3 )
end -- URLutil.getTop3domain()
URLutil.isAuthority = function ( s )
				local r
				if type( s ) == "string" then
								local pattern = "^%s*([%w%.%%_-]+)(:?)(%d*)%s*$"
								local host, colon, port = mw.ustring.match( s, pattern )
								if colon == ":" then
												port = port:match( "^[1-9][0-9]*$" )
												if type( port ) ~= "string" then
																r = false
												end
								elseif port ~= "" then
												r = false
								end
								r = URLutil.isHost( host )
				else
								r = nil
				end
				return r
end -- URLutil.isAuthority()
URLutil.isDomain = function ( s )
				local r
				if type( s ) == "string" then
								local scan = "^%s*([%w%.%%_-]*%w)%.(%a[%w-]*%a)%s*$"
								local scope
								s, scope = mw.ustring.match( s, scan )
								if type( s ) == "string" then
												if mw.ustring.find( s, "^%w" ) then
																if mw.ustring.find( s, "..", 1, true ) then
																				r = false
																else
																				r = true
																end
												end
								end
				else
								r = nil
				end
				return r
end -- URLutil.isDomain()
URLutil.isDomainExample = function ( url )
				-- RFC 2606: example.com example.net example.org example.edu
				local r = getTopDomain( url, 2 )
				if r then
								local s = r:lower():match( "^example%.([a-z][a-z][a-z])$" )
								if s then
												r = ( s == "com" or
																		s == "edu" or
																		s == "net" or
																		s == "org" )
								else
												r = false
								end
				end
				return r
end -- URLutil.isDomainExample()
URLutil.isDomainInt = function ( url )
				-- Internationalized Domain Name (Punycode)
				local r = URLutil.getHost( url )
				if r then
								if r:match( "^[!-~]+$" ) then
												local s = "." .. r
												if s:find( ".xn--", 1, true ) then
																r = true
												else
																r = false
												end
								else
												r = true
								end
				end
				return r
end -- URLutil.isDomainInt()
URLutil.isHost = function ( s )
				return URLutil.isDomain( s ) or URLutil.isIP( s )
end -- URLutil.isHost()
URLutil.isHostPathResource = function ( s )
				local r = URLutil.isResourceURL( s )
				if not r  and s then
								r = URLutil.isResourceURL( "//" .. mw.text.trim( s ) )
				end
				return r
end -- URLutil.isHostPathResource()
URLutil.isIP = function ( s )
				return URLutil.isIPv4( s ) and 4 or URLutil.isIPv6( s ) and 6
end -- URLutil.isIP()
URLutil.isIPlocal = function ( s )
				-- IPv4 according to RFC 1918, RFC 1122; even any 0.0.0.0 (RFC 5735)
				local r = false
				local num = s:match( "^ *([01][0-9]*)%." )
				if num then
								num = tonumber( num )
								if num == 0 then
												r = s:match( "^ *0+%.[0-9]+%.[0-9]+%.[0-9]+ *$" )
								elseif num == 10  or  num == 127 then
												-- loopback; private/local host: 127.0.0.1
												r = URLutil.isIPv4( s )
								elseif num == 169 then
												-- 169.254.*.*
								elseif num == 172 then
												-- 172.(16...31).*.*
												num = s:match( "^ *0*172%.([0-9]+)%." )
												if num then
																num = tonumber( num )
																if num >= 16  and  num <= 31 then
																				r = URLutil.isIPv4( s )
																end
												end
								elseif beg == 192 then
												-- 192.168.*.*
												num = s:match( "^ *0*192%.([0-9]+)%." )
												if num then
																num = tonumber( num )
																if num == 168 then
																				r = URLutil.isIPv4( s )
																end
												end
								end
				end
				if r then
								r = true
				end
				return r
end -- URLutil.isIPlocal()
URLutil.isIPv4 = function ( s )
				local function legal( n )
														return ( tonumber( n ) < 256 )
										end
				local r = false
				if type( s ) == "string" then
								local p1, p2, p3, p4 = s:match( "^%s*([1-9][0-9]?[0-9]?)%.([12]?[0-9]?[0-9])%.([12]?[0-9]?[0-9])%.([12]?[0-9]?[0-9])%s*$" )
								if p1 and p2 and p3 and p4 then
												r = legal( p1 ) and legal( p2 ) and legal( p3 ) and legal( p4 )
								end
				end
				return r
end -- URLutil.isIPv4()
URLutil.isIPv6 = function ( s )
				local dcolon, groups
				if type( s ) ~= "string"
								or s:len() == 0
								or s:find( "[^:%x]" ) -- only colon and hex digits are legal chars
								or s:find( "^:[^:]" ) -- can begin or end with :: but not with single :
								or s:find( "[^:]:$" )
								or s:find( ":::" )
				then
								return false
				end
				s = mw.text.trim( s )
				s, dcolon = s:gsub( "::", ":" )
				if dcolon > 1 then
								return false
				end -- at most one ::
				s = s:gsub( "^:?", ":" ) -- prepend : if needed, upper
				s, groups = s:gsub( ":%x%x?%x?%x?", "" ) -- remove valid groups, and count them
				return ( ( dcolon == 1 and groups < 8 ) or
													( dcolon == 0 and groups == 8 ) )
								and ( s:len() == 0 or ( dcolon == 1 and s == ":" ) ) -- might be one dangling : if original ended with ::
end -- URLutil.isIPv6()
URLutil.isMailAddress = function ( s )
				if type( s ) == "string" then
								s = mw.ustring.match( s, "^%s*[%w%.%%_-]+@([%w%.%%-]+)%s*$" )
								return URLutil.isDomain( s )
				end
				return false
end -- URLutil.isMailAddress()
URLutil.isMailLink = function ( s )
				if type( s ) == "string" then
								local addr
								s, addr = mw.ustring.match( s, "^%s*([Mm][Aa][Ii][Ll][Tt][Oo]):(%S[%w%.%%_-]*@[%w%.%%-]+)%s*$" )
								if type( s ) == "string" then
												if s:lower() == "mailto" then
																return URLutil.isMailAddress( addr )
												end
								end
				end
				return false
end -- URLutil.isMailLink()
local function isProtocolAccepted( prot, supplied )
				if type( prot ) == "string" then
								local scheme, colon, slashes = mw.ustring.match( prot, "^%s*([a-zA-Z]*)(:?)(/?/?)%s*$" )
								if slashes ~= "/" then
												if scheme == "" then
																if colon ~= ":" and slashes == "//" then
																				return true
																end
													elseif colon == ":" or slashes == "" then
																local s = supplied:match( " " .. scheme:lower() .. " " )
																if type( s ) == "string" then
																				return true
																end
												end
								end
				end
				return false
end -- isProtocolAccepted()
URLutil.isProtocolDialog = function ( prot )
				return isProtocolAccepted( prot, " mailto irc ircs ssh telnet " )
end -- URLutil.isProtocolDialog()
URLutil.isProtocolWiki = function ( prot )
				return isProtocolAccepted( prot,
																															" ftp ftps git http https nntp sftp svn worldwind " )
end -- URLutil.isProtocolWiki()
URLutil.isResourceURL = function ( url )
				local scheme = URLutil.getScheme( url )
				if scheme then
								local s = " // http:// https:// ftp:// sftp:// "
								s = s:find( string.format( " %s ", scheme ) )
								if s then
												if URLutil.getAuthority( url ) then
																if not url:match( "%S%s+%S" ) then
																				local s1, s2 = url:match( "^([^#]+)(#.*)$" )
																				if s2 then
																								if url:match( "^%s*[a-zA-Z]*:?//(.+)/" ) then
																												return true
																								end
																				else
																								return true
																				end
																end
												end
								end
				end
				return false
end -- URLutil.isResourceURL()
URLutil.isSuspiciousURL = function ( url )
				if URLutil.isResourceURL( url ) then
								local s = URLutil.getAuthority( url )
								local pat = "[%[|%]" ..
																				mw.ustring.char( 34,
																																					8201, 45, 8207,
																																					8234, 45, 8239,
																																					8288 )
																				.. "]"
								if s:find( "@" )
											or url:find( "''" )
											or url:find( pat )
											or url:find( "[%.,]$" ) then
												return true
								end
								-- TODO  zero width character ??
								return false
				end
				return true
end -- URLutil.isSuspiciousURL()
URLutil.isUnescapedURL = function ( url, trailing )
				if type( trailing ) ~= "string" then
								if URLutil.isWebURL( url ) then
												if url:match( "[%[|%]]" ) then
																return true
												end
								end
				end
				return false
end -- URLutil.isUnescapedURL()
URLutil.isWebURL = function ( url )
				if URLutil.getScheme( url ) and URLutil.getAuthority( url ) then
								if not url:find( "%S%s+%S" )  and
											not url:find( "''", 1, true ) then
												return true
								end
				end
				return false
end -- URLutil.isWebURL()
URLutil.wikiEscapeURL = function ( url )
				if url:find( "[%[|%]]" ) then
								local n
								url, n = url:gsub( "%[", "&#91;" )
																				:gsub( "|", "&#124;" )
																				:gsub( "%]", "&#93;" )
				end
				return url
end -- URLutil.wikiEscapeURL()
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, amount )
				-- Run actual code from template transclusion
				-- Precondition:
				--     frame   -- object
				--     action  -- string, with function name
				--     amount  -- number, of args if > 1
				-- Postcondition:
				--     Return string or not
				local n = amount or 1
				local v = { }
				local r, s
				for i = 1, n do
								s = frame.args[ i ]
								if s then
													s = mw.text.trim( s )
													if s ~= "" then
																	v[ i ] = s
													end
									end
				end -- for i
				if v[ 1 ] then
									r = URLutil[ action ](  v[ 1 ], v[ 2 ], v[ 3 ] )
				end
				return r
end -- Template()
local p = {}
function p.decode( frame )
				return Template( frame, "decode", 2 ) or ""
end
function p.encode( frame )
				return Template( frame, "encode", 2 ) or ""
end
function p.getAuthority( frame )
				return Template( frame, "getAuthority" ) or ""
end
function p.getFragment( frame )
				local r = Template( frame, "getFragment", 2 )
				if r then
								r = "#" .. r
				else
								r = ""
				end
				return r
end
function p.getHost( frame )
				return Template( frame, "getHost" ) or ""
end
function p.getLocation( frame )
				return Template( frame, "getLocation" ) or ""
end
function p.getNormalized( frame )
				return Template( frame, "getNormalized" ) or ""
end
function p.getPath( frame )
				return Template( frame, "getPath" ) or ""
end
function p.getPort( frame )
				return Template( frame, "getPort" ) or ""
end
function p.getQuery( frame )
				local r = Template( frame, "getQuery", 3 )
				if r then
								local key = frame.args[ 2 ]
								if key then
												key = mw.text.trim( key )
												if key == "" then
																key = nil
												end
								end
								if not key then
												r = "?" .. r
								end
				else
								r = ""
				end
				return r
end
function p.getRelativePath( frame )
				return Template( frame, "getRelativePath" ) or ""
end
function p.getScheme( frame )
				return Template( frame, "getScheme" ) or ""
end
function p.getSortkey( frame )
				return Template( frame, "getSortkey" ) or ""
end
function p.getTLD( frame )
				return Template( frame, "getTLD" ) or ""
end
function p.getTop2domain( frame )
				return Template( frame, "getTop2domain" ) or ""
end
function p.getTop3domain( frame )
				return Template( frame, "getTop3domain" ) or ""
end
function p.isAuthority( frame )
				return Template( frame, "isAuthority" ) and "1" or ""
end
function p.isDomain( frame )
				return Template( frame, "isDomain" ) and "1" or ""
end
function p.isDomainExample( frame )
				return Template( frame, "isDomainExample" ) and "1" or ""
end
function p.isDomainInt( frame )
				return Template( frame, "isDomainInt" ) and "1" or ""
end
function p.isHost( frame )
				return Template( frame, "isHost" ) and "1" or ""
end
function p.isHostPathResource( frame )
				return Template( frame, "isHostPathResource" ) and "1" or ""
end
function p.isIP( frame )
				return Template( frame, "isIP" ) or ""
end
function p.isIPlocal( frame )
				return Template( frame, "isIPlocal" ) and "1" or ""
end
function p.isIPv4( frame )
				return Template( frame, "isIPv4" ) and "1" or ""
end
function p.isIPv6( frame )
				return Template( frame, "isIPv6" ) and "1" or ""
end
function p.isMailAddress( frame )
				return Template( frame, "isMailAddress" ) and "1" or ""
end
function p.isMailLink( frame )
				return Template( frame, "isMailLink" ) and "1" or ""
end
function p.isProtocolDialog( frame )
				return Template( frame, "isProtocolDialog" ) and "1" or ""
end
function p.isProtocolWiki( frame )
				return Template( frame, "isProtocolWiki" ) and "1" or ""
end
function p.isResourceURL( frame )
				return Template( frame, "isResourceURL" ) and "1" or ""
end
function p.isSuspiciousURL( frame )
				return Template( frame, "isSuspiciousURL" ) and "1" or ""
end
function p.isUnescapedURL( frame )
				return Template( frame, "isUnescapedURL", 2 ) and "1" or ""
end
function p.isWebURL( frame )
				return Template( frame, "isWebURL" ) and "1" or ""
end
function p.wikiEscapeURL( frame )
				return Template( frame, "wikiEscapeURL" )
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
function p.URLutil()
				return URLutil
end
return p