Modul:TemplUtl

aus Wikipedia, der freien Enzyklopädie
Zur Navigation springen Zur Suche springen

Diese Seite enthält Code in der Programmiersprache Lua. Einbindungszahl Cirrus

Dies ist die (produktive) Mutterversion eines global benutzten Lua-Moduls.
Wenn die serial-Information nicht übereinstimmt, müsste eine Kopie hiervon in das lokale Wiki geschrieben werden.
Versionsbezeichnung auf WikiData: 2022年05月16日

 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( "&gt;&gt;&gt;" );
 sign = string.format( "[[#%s|%s]]", sign, tostring( btn ) );
 top:wikitext( sign, "&#160;", 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( "&#123;&#123;%s&#125;&#125;", 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;
Abgerufen von „https://de.wikipedia.org/w/index.php?title=Modul:TemplUtl&oldid=222953765"