Modul:TemplUtl
Erscheinungsbild
Dokumentation für das Modul TemplUtl[Ansicht] [Bearbeiten] [Versionsgeschichte] [ ]
Dieses Modul wurde am 20. Oktober 2021 von Modul:TemplUtl der deutschen Wikipedia importiert. Statt Änderungen hier auf Wikivoyage vorzunehmen, sollte eine neuer Import vorgezogen werden, falls im originalen Wiki neue Funktionen hinzugekommen sind. Stimme dich dazu bitte mit der Community in der Vorlagenwerkstatt ab. |
Das Modul ist auf dem aktuellen Stand. |
Dieses Modul ist getestet und für den projektweiten Gebrauch geeignet. Es kann in Vorlagen benutzt und auf Hilfeseiten erläutert werden. Entwicklungen an dem Modul sollten auf TemplUtl/Test und die Anwendung auf der Spielwiese getestet werden, da wiederholte Trial-and-Error-Edits die Resourcen stark belasten können. |
Funktion
Hinweise
- Die obige Dokumentation wurde aus der Seite Modul:TemplUtl/Doku eingefügt. (bearbeiten | Versionsgeschichte) Die Kategorien für dieses Modul sollten in der Dokumentation eingetragen werden. Die Interwiki-Links sollten auf Wikidata eingepflegt werden.
- Liste der Unterseiten
local TemplUtl = { suite = "TemplUtl",
serial = "2022-05-16",
item = 52364930 };
--[=[
Utilities to support template programming.
]=]
local Failsafe = TemplUtl;
local fallible = function ( adjust, ahead )
-- Check for leading character disturbing syntax
-- Precondition:
-- adjust -- string; trimmed wikitext
-- ahead -- true, if leading syntax shall start on new line
-- Postcondition:
-- Returns string, modified if necessary
local r = adjust;
local c = r:byte( 1, 1 );
local lead;
if c <= 59 and
( c==35 or c==42 or c==58 or c==59 ) then
lead = true;
elseif c == 123 or c == 124 then
local c2 = r:byte( 2, 1 );
if c == 123 and c2 == 124 then
lead = true;
elseif ahead and c == 124 and
( c2 == 43 or c2 == 45 or c2 == 125 ) then
lead = true;
end
end
if lead then
if ahead then
r = "\n" .. r;
else
r = mw.text.nowiki( r:sub( 1, 1 ) ) .. r:sub( 2 );
end
end
return r;
end -- fallible()
local fiatTitleRegExp = function ( accept )
-- Create pattern to detect page name
-- Precondition:
-- accept -- string; trimmed title
-- Postcondition:
-- Returns string with pattern
local start = mw.ustring.sub( accept, 1, 1 );
local r;
if mw.ustring.match( start, "%a" ) then
r = string.format( "[%s%s]%s",
mw.ustring.lower( start ),
mw.ustring.upper( start ),
mw.ustring.sub( accept, 2 ) );
else
r = accept;
end
if r:match( " " ) then
r = r:gsub( "%", "%%" )
:gsub( "[%-^.?+*()$]", "%$1" )
:gsub( "_", " " )
:gsub( "%s+", "[%s_]+" );
end
return r;
end -- fiatTitleRegExp()
local framing = function ( frame )
-- Ensure availability of frame object
-- Precondition:
-- frame -- object; #invoke environment, or false
-- Postcondition:
-- Return frame object
if not TemplUtl.frame then
if type( frame ) == "table" then
TemplUtl.frame = frame;
else
TemplUtl.frame = mw.getCurrentFrame();
end
end
return TemplUtl.frame;
end -- framing()
TemplUtl.facets = function ( ask, adjust )
local r = ask;
if adjust == "%" and r:find( "%%%x%x" ) then
r = mw.uri.decode( r, "PATH" );
elseif r:find( "&", 1, true ) then
r = mw.text.decode( r );
end
r = mw.ustring.gsub( r, "[%s%p%c]+", " " );
r = mw.text.trim( r );
return r;
end -- TemplUtl.facets()
TemplUtl.faculty = function ( analyze, another )
-- Test template arg for boolean
-- analyze -- string, boolean, number or nil
-- another -- fallback: string, boolean, or nil
-- "-" to test for explicit vocabulary choice
-- Returns boolean, or "-"
local s = type( analyze );
local r;
if s == "string" then
r = mw.text.trim( analyze );
if r == "" then
r = TemplUtl.faculty( another, nil );
elseif r:find( "1", 1, true ) and
r:match( "^[0%-]*1[01%-]*$" ) then
r = true;
elseif r:match( "^[0%-]+$" ) then
r = false;
else
r = r:lower();
if r == "y" or
r == "yes" or
r == "true" or
r == "on" then
r = true;
elseif r == "n" or
r == "no" or
r == "false" or
r == "off" then
r = false;
else
if not TemplUtl.boolang then
-- TODO: page language
local l, d = pcall( mw.ext.data.get, "i18n/01.tab" );
if type( d ) == "table" and
type( d.data ) == "table" then
local f = function ( at )
local e = d.data[ at ];
l = e[ 1 ];
s = e[ 2 ];
if type( l ) == "boolean" and
type( s ) == "string" then
s = mw.text.split( s, "|" );
for i = 1, #s do
TemplUtl.boolang[ s[ i ] ] = l;
end -- for i
end
end
TemplUtl.boolang = { };
f( 1 );
f( 2 );
else
TemplUtl.boolang = true;
end
end
if type( TemplUtl.boolang ) == "table" then
s = TemplUtl.boolang[ r ];
if type( s ) == "boolean" then
r = s;
end
end
if type( r ) ~= "boolean" then
s = type( another );
if s == "nil" then
r = true;
elseif s == "boolean" then
r = another;
elseif s == "string" then
s = mw.text.trim( another );
if s == "-" then
r = "-";
elseif s == "" then
r = true;
else
r = TemplUtl.faculty( s );
end
end
end
end
end
elseif s == "boolean" then
r = analyze;
elseif s == "number" then
r = ( analyze ~= 0 );
else
r = false;
end
return r;
end -- TemplUtl.faculty()
TemplUtl.failure = function ( alert, always, addClass, frame )
-- Format error message, mostly hidden
-- alert -- string: message
-- always -- boolean, or nil: do not hide
-- addClass -- string, or nil: add classes to element
-- frame -- object, or nil
-- Returns string
local err = mw.html.create( "span" )
:addClass( "error" )
:wikitext( alert );
local live = ( framing( frame ):preprocess( "{{REVISIONID}}" )
== "" );
if type( addClass ) == "string" then
err:addClass( addClass )
end
if live then
local max = 1000000000;
local id = math.floor( os.clock() * max );
local sign = string.format( "error_%d", id );
local btn = mw.html.create( "span" );
local top = mw.html.create( "div" );
err:attr( "id", sign );
-- TODO: LTR
btn:css( { ["background"] = "#FFFF00",
["border"] = "#FF0000 3px solid",
["font-weight"] = "bold",
["padding"] = "2px",
["text-decoration"] = "none" } )
:wikitext( ">>>" );
sign = string.format( "[[#%s|%s]]", sign, tostring( btn ) );
top:wikitext( sign, " ", alert );
mw.addWarning( tostring( top:attr( "role", "alert" ) ) );
elseif not always then
err:css( { ["display"] = "none" } );
-- err:css( { ["display"] = "inline-block",
-- ["line-height"] = "0",
-- ["max-height"] = "0",
-- ["max-width"] = "0",
-- ["visibility"] = "hidden" } );
end
return tostring( err );
end -- TemplUtl.failure()
TemplUtl.fake = function ( access )
-- Simulation of template transclusion
-- Precondition:
-- access -- string; page name (template)
if type( access ) == "string" then
local s = mw.text.trim( access );
if s ~= "" then
local t = mw.title.new( s, 10 );
if not mw.title.equals( mw.title.getCurrentTitle(), t ) and
t.exists then
t:getContent();
end
end
end
end -- TemplUtl.fake()
TemplUtl.fakes = function ( array, frame, ahead, answer )
-- Simulation of template transclusions
-- Precondition:
-- array -- table, with template title strings
-- frame -- object, or nil
-- ahead -- string, or nil, with common prefix
-- answer -- true, or nil, for list creation
-- Postcondition:
-- Returns string, if answer requested
local e = framing( frame );
local f = function ( a )
e:expandTemplate{ title = a };
end
local s = ahead or "";
local r;
for k, v in pairs( array ) do
if type( k ) == "number" and
type( v ) == "string" then
v = s .. mw.text.trim( v );
pcall( f, v );
if answer then
if r then
r = r .. "\n";
else
r = "";
end
r = string.format( "%s* [[Template:%s|%s]]", r, v, v );
end
end
end -- for k, v
return r;
end -- TemplUtl.fakes()
TemplUtl.feasible = function ( address )
-- Does this describe an URL beginning?
-- Precondition:
-- address -- string; what to inspect, URL presumed
-- Postcondition:
-- Returns true, if URL beginning
local start, r = address:match( "^%s*((%a*:?)//)" );
if start then
if r == "" then
r = true;
elseif r:sub( -1, -1 ) == ":" then
local schemes = ":ftp:ftps:http:https:";
r = ":" .. r:lower();
if schemes:find( r, 1, true ) then
r = true;
else
r = false;
end
else
r = false;
end
end
return r;
end -- TemplUtl.feasible()
TemplUtl.feed = function ( area, ahead, at, after )
-- Detect next free "|" or "}}"
-- Precondition:
-- area -- string; template transclusion
-- ahead -- string; opening element, or false
-- at -- number; byte position in area where to start
-- after -- true, if only to search for "}}"
-- Postcondition:
-- Returns
-- -- number; byte position in area
-- -- before "|" or "}}", may be at end
-- -- to continue search; ahead has been closed
-- -- true, if to be continued at number
local j = at;
local loop = true;
local c, k, r, s, seek;
if after then
seek = "[{}<]";
else
seek = "[%[%]|{}<:]";
end
while loop do
j = area:find( seek, j );
if j then
c = area:byte( j, j );
if c == 123 then -- {
k = j + 1;
if area:byte( k, k ) == 123 then
k = k + 1;
if area:byte( k, k ) == 123 then
j, loop = TemplUtl.feed( area, "{{{", k, after );
else
k = k - 1;
j, loop = TemplUtl.feed( area, "{{", k, after );
end
if not loop then
r = j;
end
end
elseif c == 125 then -- }
k = j + 1;
if area:byte( k, k ) == 125 then
if ahead == "{{" then
r = k;
break; -- while loop;
elseif ahead == "{{{" then
k = k + 1;
if area:byte( k, k ) == 125 then
r = k;
break; -- while loop;
end
elseif not ahead then
r = j - 1;
loop = false;
end
end
elseif c == 60 then -- <
k = j + 3;
if area:sub( j, k ) == "<!--" then
k = area:find( "-->", k );
if k then
j = k + 2;
end
else
local skip;
s = area:sub( j + 1 ):lower();
skip = s:match( "^%s*nowiki%s*>" );
if skip then
local n = skip:len();
n, k = s:find( "<%s*/%s*nowiki%s*>", n );
if k then
j = j + k;
else
loop = false;
end
end
end
elseif c == 124 then -- |
if not r then
r = j - 1;
end
if not ahead then
loop = false;
end
elseif c == 91 then -- [
k = j + 1;
if area:byte( k, k ) == 91 then
k = k + 1;
j, loop = TemplUtl.feed( area, "[[", k, after );
elseif TemplUtl.feasible( area:sub( k ) ) then
k = k + 3;
j, loop = TemplUtl.feed( area, "[", k, after );
end
if not loop then
r = j;
end
elseif c == 93 then -- ]
if ahead == "[" then
r = j;
break; -- while loop
elseif ahead == "[[" then
k = j + 1;
if area:byte( k, k ) == 93 then
r = k;
break; -- while loop
end
end
elseif c == 58 then -- :
s = area:sub( j + 1, j + 2 );
if s == "//" then
s = " " .. area:sub( 1, j + 2 );
s = s:match( "%s(%a+://)$" );
if s and TemplUtl.feasible( s ) then
s = area .. " ";
s = s:match( "([^%s|]+)%s", j );
if s then
k = s:find( "}}" );
if k then
j = j + k + 1;
else
j = j + s:len();
end
end
end
end
end
j = j + 1;
else
loop = false;
end
end -- while loop
if not r then
r = area:len();
end
return r, loop;
end -- TemplUtl.feed()
TemplUtl.feeder = function ( area, at )
-- Retrieve all parameters
-- Precondition:
-- area -- string; template transclusion
-- at -- optional number; byte position in area of "{{"
-- Postcondition:
-- Returns
-- -- table
-- [0] -- template, page, parser function name
-- [1] -- unnamed parameter
-- ["name"] -- named parameter
-- -- string; error message, if any, else nil
local n = 0;
local j, k, p, r, r2, s, v;
if type( at ) == "number" then
j = at + 2;
else
j = 3;
end
while true do
k = TemplUtl.feed( area, false, j );
s = area:sub( j, k );
s = s:gsub( "<!--.*-->", "" );
if n == 0 then
r = { [ 0 ] = s };
n = 1;
else
p, v = s:match( "^([^=]*)=(.*)$" );
if p then
if p:match( "^%s*%d+%s*$" ) then
p = tonumber( p );
else
p = mw.text.trim( p );
end
v = mw.text.trim( v );
else
p = n;
v = s;
n = n + 1;
end
if r[ p ] then
if r2 then
r2 = r2 .. " * ";
else
r2 = "";
end
r2 = string.format( "%s%s '%s'",
r2,
"duplicated parameter",
tostring( p ) );
end
r[ p ] = v;
end
s = area:sub( k + 1, k + 2 );
if s == "}}" then
break; -- while true
elseif s == "" then
r2 = "template not closed";
break; -- while true
end
j = k + 2;
end -- while true
return r, r2;
end -- TemplUtl.feeder()
TemplUtl.fetch = function ( area, ask )
-- Find assignment of a named template parameter
-- Precondition:
-- area -- string; template transclusion
-- ask -- string; parameter name
-- Postcondition:
-- Returns string with trimmed parameter value, or nil
-- Does not return value if template inside
local r;
local scan = string.format( "%s%s%s",
"|%s*", ask, "%s*=(.+)$" );
r = mw.ustring.match( area, scan );
if r then
local j = TemplUtl.feed( r, false, 1 );
r = r:sub( 1, j );
if r then
r = mw.text.trim( r );
if r == "" then
r = nil;
end
end
end
return r;
end -- TemplUtl.fetch()
TemplUtl.find = function ( area, access, at, alter )
-- Find next occurrence of a template
-- Precondition:
-- area -- string; where to search
-- access -- string; trimmed (template) title
-- at -- optional number; ustring position in area, if not 1
-- alter -- optional string; lowercase namespace pattern
-- "" for article
-- no colon (:)
-- Postcondition:
-- Returns ustring position of "{{" in area, or false
-- Requires:
-- fiatTitleRegExp()
local scan = string.format( "{{%s%s%s",
"([%w_%s:]*)%s*",
fiatTitleRegExp( access ),
"%s*([|}<]!?)" );
local r, space, start, suffix;
if type( at ) == "number" then
r = at;
else
r = 1;
end
while true do
r = mw.ustring.find( area, scan, r );
if r then
start, suffix = mw.ustring.match( area, scan, r );
if start then
start = mw.text.trim( start );
if start == "" then
break; -- while true
elseif alter then
if not space then
space = string.format( "^:?%s:$", alter );
end
start = mw.ustring.lower( start );
if mw.ustring.match( start, space ) then
break; -- while true
end
else
start = start:match( "^:?(.+):$" );
if start then
start = mw.ustring.lower( start );
if start == "template" then
break; -- while true
else
if not space then
space = mw.site.namespaces[ 10 ].name;
space = mw.ustring.lower( space );
end
start = start:gsub( "_", " " )
:gsub( "%s+", " " );
if start == space then
break; -- while true
end
end
end
end
else
break; -- while true
end
r = r + 2;
else
r = false;
break; -- while true
end
end -- while true
return r;
end -- TemplUtl.find()
-- finder()
-- 1 page name
-- 2 template title / page name
-- 3 4 5 6
-- more like 2
TemplUtl.firstbreak = function ( adjust )
-- Precede leading character with newline if specific syntax
-- Precondition:
-- adjust -- string; trimmed wikitext
-- Postcondition:
-- Returns string, modified if necessary
return fallible( adjust, true );
end -- TemplUtl.firstbreak()
TemplUtl.flat = function ( area )
-- Remove syntax elements that hide effective syntax only
-- Precondition:
-- area -- string; unparsed wikitext to be reduced
-- Postcondition:
-- Returns cleared wikitext
local delimiters = { { "<%s*NOWIKI%s*>", "<%s*/%s*NOWIKI%s*>" },
{ "<!--", "-->", true },
{ "<%s*PRE%s*>", "<%s*/%s*PRE%s*>" },
{ "<%s*SYNTAXHIGHLIGHT[^<>]*>",
"<%s*/%s*SYNTAXHIGHLIGHT%s*>" } };
local i = 1;
local r = area;
local k, m, n;
if not TemplUtl.Delimiters then
local c, sD, sP;
TemplUtl.Delimiters = { };
for j = 1, #delimiters do
table.insert( TemplUtl.Delimiters, { } );
for ji = 1, 2 do
sD = delimiters[ j ][ ji ];
sP = "";
for js = 1, #sD, 1 do
c = sD:byte( js, js );
if c >= 65 and c <= 90 then
sP = string.format( "%s[%c%c]",
sP, c, c + 32 );
else
sP = sP .. string.char( c );
end
end -- for js
table.insert( TemplUtl.Delimiters[ j ], sP );
end -- for ji
end -- for j
end
while ( true ) do
k = false;
for j = 1, #delimiters do
m = r:find( TemplUtl.Delimiters[ j ][ 1 ],
i,
TemplUtl.Delimiters[ j ][ 3 ] );
if m and ( not k or m < k ) then
k = m;
n = j;
end
end -- for j
if k then
local s
if k > 1 then
i = k - 1;
s = r:sub( 1, i );
else
s = "";
end
j, m = r:find( TemplUtl.Delimiters[ n ][ 2 ],
k + 1,
TemplUtl.Delimiters[ n ][ 3 ] );
if m then
r = s .. r:sub( m + 1 );
else
r = s;
break; -- while true
end
else
break; -- while true
end
end -- while true
return r;
end -- TemplUtl.flat()
TemplUtl.nowiki1 = function ( adjust )
-- HTML-escape leading character if disturbing syntax
-- Precondition:
-- adjust -- string; trimmed wikitext
-- Postcondition:
-- Returns string, modified if necessary
return fallible( adjust, false );
end -- TemplUtl.nowiki1()
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()
-- Export
local p = { };
function p.facets( frame )
return TemplUtl.facets( frame.args[ 1 ] or "",
frame.args.decode );
end -- p.facets
function p.faculty( frame )
local r = TemplUtl.faculty( frame.args[ 1 ],
frame.args[ 2 ] );
if r ~= "-" then
r = r and "1";
end
return r or "";
end -- p.faculty
function p.failure( frame )
local scream = mw.text.trim( frame.args[ 1 ] or "" );
local loud = frame.args[ 2 ];
local select = frame.args.class;
if scream == "" then
scream = "?????????";
end
if loud then
loud = TemplUtl.faculty( loud, nil );
end
return TemplUtl.failure( scream, loud, select, frame );
end -- p.failure
function p.fake( frame )
TemplUtl.fake( frame.args[ 1 ] or "", frame );
return "";
end -- p.fake
function p.fakes( frame )
local list = ( frame.args.list == "1" );
local r = TemplUtl.fakes( frame.args,
frame,
frame.args.prefix,
list );
return r or "";
end -- p.fakes
function p.firstbreak( frame )
local r = ( frame.args[ 1 ] );
if r then
r = mw.text.trim( r );
if r ~= "" then
r = TemplUtl.firstbreak( r );
end
end
return r or "";
end -- p.firstbreak
function p.from( frame )
local r = frame:getParent():getTitle();
if r then
r = string.format( "{{%s}}", r );
end
return r or "";
end -- p.from
function p.isRedirect()
return mw.title.getCurrentTitle().isRedirect and "1" or "";
end -- p.isRedirect
function p.nowiki1( frame )
local r = ( frame.args[ 1 ] );
if r then
r = mw.text.trim( r );
if r ~= "" then
r = TemplUtl.nowiki1( r );
end
end
return r or "";
end -- p.nowiki1
p.failsafe = function ( frame )
-- Versioning interface
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.TemplUtl = function ()
return TemplUtl;
end -- p.TemplUtl()
setmetatable( p, { __call = function ( func, ... )
setmetatable( p, nil );
return Failsafe;
end } );
return p;