<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en-GB">
	<id>https://the-democratika.com/wiki/index.php?action=history&amp;feed=atom&amp;title=Module%3AURLutil</id>
	<title>Module:URLutil - Revision history</title>
	<link rel="self" type="application/atom+xml" href="https://the-democratika.com/wiki/index.php?action=history&amp;feed=atom&amp;title=Module%3AURLutil"/>
	<link rel="alternate" type="text/html" href="https://the-democratika.com/wiki/index.php?title=Module:URLutil&amp;action=history"/>
	<updated>2026-04-05T01:50:47Z</updated>
	<subtitle>Revision history for this page on the wiki</subtitle>
	<generator>MediaWiki 1.43.0</generator>
	<entry>
		<id>https://the-democratika.com/wiki/index.php?title=Module:URLutil&amp;diff=9618&amp;oldid=prev</id>
		<title>&gt;ExE Boss: Copied from de:Modul:URLutil</title>
		<link rel="alternate" type="text/html" href="https://the-democratika.com/wiki/index.php?title=Module:URLutil&amp;diff=9618&amp;oldid=prev"/>
		<updated>2022-12-09T17:12:27Z</updated>

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