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.
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( ">>>" ); 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;