Modul:TemplUtl: Unterschied zwischen den Versionen
Zur Navigation springen
Zur Suche springen
(Setup) |
K (21 Versionen importiert: Doku-Vorlage) |
||
(22 dazwischenliegende Versionen von 6 Benutzern werden nicht angezeigt) | |||
Zeile 1: | Zeile 1: | ||
local TemplUtl = { suite = "TemplUtl", | |||
TemplUtl | serial = "2019-11-20", | ||
item = 52364930 }; | |||
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 ); | |||
if c <= 59 and | |||
( c==35 or c==42 or c==58 or c==59 ) then | |||
if ahead then | |||
r = "\n" .. r; | |||
else | |||
r = mw.text.nowiki( r:sub( 1, 1 ) ) .. r:sub( 2 ); | |||
end | |||
elseif ahead then | |||
local c2 = r:byte( 2, 1 ); | |||
if ( c==123 and c2==124 ) or | |||
( c==124 and c2==45 ) then | |||
r = "\n" .. r; | |||
end | |||
end | |||
return r; | |||
end -- fallible() | |||
Zeile 10: | Zeile 39: | ||
-- accept -- string; trimmed title | -- accept -- string; trimmed title | ||
-- Postcondition: | -- Postcondition: | ||
-- | -- Returns string with pattern | ||
local start = mw.ustring.sub( accept, 1, 1 ); | |||
local r; | local r; | ||
if mw.ustring.match( start, "%a" ) then | if mw.ustring.match( start, "%a" ) then | ||
r = string.format( "[%s%s]%s", | r = string.format( "[%s%s]%s", | ||
Zeile 23: | Zeile 52: | ||
if r:match( " " ) then | if r:match( " " ) then | ||
r = r:gsub( "%", "%%" ) | r = r:gsub( "%", "%%" ) | ||
:gsub( "[^.?+*()$] | :gsub( "[%-^.?+*()$]", "%$1" ) | ||
:gsub( "_", " " ) | :gsub( "_", " " ) | ||
:gsub( "%s+", "[%s_]+" ); | :gsub( "%s+", "[%s_]+" ); | ||
Zeile 32: | Zeile 61: | ||
local TemplUtl = { }; | local framing = function ( frame ) | ||
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.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: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; | |||
else | |||
r = TemplUtl.faculty( another ); | |||
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() | |||
Zeile 39: | Zeile 258: | ||
-- Does this describe an URL beginning? | -- Does this describe an URL beginning? | ||
-- Precondition: | -- Precondition: | ||
-- address -- string; | -- address -- string; what to inspect, URL presumed | ||
-- Postcondition: | -- Postcondition: | ||
-- | -- Returns true, if URL beginning | ||
local start, r = address:match( "^%s*((%a*:?)//)" ); | local start, r = address:match( "^%s*((%a*:?)//)" ); | ||
if start then | if start then | ||
Zeile 71: | Zeile 290: | ||
-- after -- true, if only to search for "}}" | -- after -- true, if only to search for "}}" | ||
-- Postcondition: | -- Postcondition: | ||
-- | -- Returns | ||
-- -- number; byte position in area | -- -- number; byte position in area | ||
-- -- before "|" or "}}", may be at end | -- -- before "|" or "}}", may be at end | ||
Zeile 177: | Zeile 396: | ||
if s and TemplUtl.feasible( s ) then | if s and TemplUtl.feasible( s ) then | ||
s = area .. " "; | s = area .. " "; | ||
s = s:match( "(% | s = s:match( "([^%s|]+)%s", j ); | ||
if s then | if s then | ||
k = s:find( "}}" ); | k = s:find( "}}" ); | ||
Zeile 199: | Zeile 418: | ||
return r, loop; | return r, loop; | ||
end -- TemplUtl.feed() | 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() | |||
Zeile 208: | Zeile 494: | ||
-- ask -- string; parameter name | -- ask -- string; parameter name | ||
-- Postcondition: | -- Postcondition: | ||
-- | -- Returns string with trimmed parameter value, or nil | ||
-- Does not return value if template inside | -- Does not return value if template inside | ||
local r; | local r; | ||
Zeile 237: | Zeile 523: | ||
-- alter -- optional string; lowercase namespace pattern | -- alter -- optional string; lowercase namespace pattern | ||
-- "" for article | -- "" for article | ||
-- no colon (:) | |||
-- Postcondition: | -- Postcondition: | ||
-- | -- Returns ustring position of "{{" in area, or false | ||
-- Requires: | -- Requires: | ||
-- fiatTitleRegExp() | -- fiatTitleRegExp() | ||
local scan = string.format( "{{%s%s%s", | local scan = string.format( "{{%s%s%s", | ||
"([%w_%s:]*)%s*", | "([%w_%s:]*)%s*", | ||
fiatTitleRegExp( access ), | fiatTitleRegExp( access ), | ||
"%s*([|}<]!?)" ); | "%s*([|}<]!?)" ); | ||
local r, space, start, suffix; | |||
if type( at ) == "number" then | if type( at ) == "number" then | ||
r = at; | r = at; | ||
Zeile 297: | Zeile 584: | ||
return r; | return r; | ||
end -- TemplUtl.find() | 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" or "~" | |||
-- or false | |||
-- Postcondition: | |||
-- Returns string -- with queried version, also if problem | |||
-- false -- if appropriate | |||
local last = ( atleast == "~" ) | |||
local since = atleast | |||
local r | |||
if last or since == "wikidata" then | |||
local item = Failsafe.item | |||
since = false | |||
if type( item ) == "number" and item > 0 then | |||
local entity = mw.wikibase.getEntity( string.format( "Q%d", | |||
item ) ) | |||
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 | |||
else | |||
r = vsn.value | |||
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() | |||
Zeile 302: | Zeile 734: | ||
-- Export | -- Export | ||
local p = { }; | 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.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 () | p.TemplUtl = function () | ||
return TemplUtl; | return TemplUtl; | ||
end | end -- p.TemplUtl() | ||
return p; | return p; |
Aktuelle Version vom 3. Juli 2020, 20:19 Uhr
Die Dokumentation für dieses Modul kann unter Modul:TemplUtl/Doku erstellt werden
local TemplUtl = { suite = "TemplUtl",
serial = "2019-11-20",
item = 52364930 };
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 );
if c <= 59 and
( c==35 or c==42 or c==58 or c==59 ) then
if ahead then
r = "\n" .. r;
else
r = mw.text.nowiki( r:sub( 1, 1 ) ) .. r:sub( 2 );
end
elseif ahead then
local c2 = r:byte( 2, 1 );
if ( c==123 and c2==124 ) or
( c==124 and c2==45 ) then
r = "\n" .. r;
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 )
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.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: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;
else
r = TemplUtl.faculty( another );
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" or "~"
-- or false
-- Postcondition:
-- Returns string -- with queried version, also if problem
-- false -- if appropriate
local last = ( atleast == "~" )
local since = atleast
local r
if last or since == "wikidata" then
local item = Failsafe.item
since = false
if type( item ) == "number" and item > 0 then
local entity = mw.wikibase.getEntity( string.format( "Q%d",
item ) )
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
else
r = vsn.value
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.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.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()
return p;