Die Dokumentation für dieses Modul kann unter Modul:TemplUtl/Doku erstellt werden
local TemplUtl = { suite = "TemplUtl", serial = "2017-11-11" }; 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 ) if not TemplUtl.frame then if type( frame ) == "table" then TemplUtl.frame = frame; else TemplUtl.frame = mw.getCurrentFrame(); end end end -- framing() TemplUtl.faculty = function ( analyze, another ) -- Test template arg for boolean -- analyze -- string, boolean, number or nil -- another -- fallback: string, boolean, or nil -- Returns boolean 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 == "1" then r = true; elseif r == "0" then r = false; else r = r:lower(); if r == "y" or r == "yes" or r == "true" then r = true; elseif r == "n" or r == "no" or r == "false" then r = false; else if not TemplUtl.L10N then local lucky; s = string.format( "Module:%s/local", TemplUtl.suite ); lucky, TemplUtl.L10N = pcall( mw.loadData, s ); end if type( TemplUtl.L10N ) == "table" then local entry; if not TemplUtl.lang then -- TODO: page language TemplUtl.lang = mw.language.getContentLanguage():getCode(); end entry = TemplUtl.L10N[ TemplUtl.lang ]; if type( entry ) == "table" then s = entry[ r ]; if type( s ) == "boolean" then r = s; end end else TemplUtl.L10N = true; end if type( r ) ~= "boolean" then if type( another ) ~= "nil" then r = TemplUtl.faculty( another ); else r = true; 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 ); live = ( TemplUtl.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 ) ); 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.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.flat = function ( area ) -- Remove syntax elements that hide effective syntax only -- Precondition: -- area -- string; 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() -- Export local p = { }; function p.faculty( frame ) return TemplUtl.faculty( frame.args[ 1 ], frame.args[ 2 ] ) and "1" 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.isRedirect() return mw.title.getCurrentTitle().isRedirect and "1" or ""; end -- p.isRedirect function p.failsafe() return TemplUtl.serial; end -- p.failsafe() p.TemplUtl = function () return TemplUtl; end -- p.TemplUtl() return p;