Vorlage:Shared Template Warning
local p = {}
local mRedirect = require('Module:Redirect')
local errors
-- Return blank text, or an error message if requested
local function err(text)
if errors then error(text, 2) end
return ""
end
-- Check image for suitablity
local function checkimage(image)
local page = mw.ustring.match(image, "([Ff]ile%s*:[^|%]]*)") -- File:(name) ...
or mw.ustring.match(image, "([Ii]mage%s*:[^|%]]*)") -- or Image:(name) ...
if not page then return nil end
local title = mw.title.new(":" .. page) -- Read description page (for :File:Foo rather than File:Foo)
if not title then return nil end
local redir = mRedirect.getTarget(title)
if redir then title = mw.title.new(redir) end
local frame = mw.getCurrentFrame()
local desc = frame:preprocess("{{" .. title.prefixedText .. "}}")
return desc and desc ~= "" and not mw.ustring.match(desc, "[Nn]on%-free") -- hide non-free image
end
-- Attempt to parse [[File:...]] or [[Image:...]], either anywhere or at the start
local function parseimage(text, start)
local startre = ""
if start then startre = "^" end -- a true flag restricts search to start of string
local image = mw.ustring.match(text, startre .. "%[%[%s*[Ff]ile%s*:.*") -- [[File: ...
or mw.ustring.match(text, startre .. "%[%[%s*[Ii]mage%s*:.*") -- or [[Image: ...
if image then
image = mw.ustring.match(image, "%b[]%s*") -- match [[...]] to handle nesting
end
return image
end
-- Attempt to construct a [[File:...]] block from {{infobox ... |image= ...}}
local function argimage(text)
local token = nil
if mw.ustring.match(text, "%{%{%s*[Ii]nfobox") then
local image = mw.ustring.match(text, "|%s*image%s*=%s*([^%}|]*)") -- parse image= argument...
or mw.ustring.match(text, "|%s*Cover%s*=%s*([^%}|]-)") -- or Cover= from Infobox album
if image then -- add in relevant parameters: caption, alt text and image size
token = "[[File:" .. image
local caption = mw.ustring.match(text, "|%s*[Cc]aption%s*=%s*([^%}|]*)")
if caption then token = token .. "|" .. caption end
local alt = mw.ustring.match(text, "|%s*alt%s*=%s*([^%}|]*)")
if alt then token = token .. "|alt=" .. alt end
local image_size = mw.ustring.match(text, "|%s*image_size%s*=%s*([^%}|]*)")
if image_size then token = token .. "|" .. image_size end
token = mw.ustring.gsub(token, "\n","") .. "]]\n"
end
end
return token
end
-- Entry point for Lua callers
-- Returns a string value: text of the lead of a page
function p._lead(pagenames, options)
errors = options.errors
if not pagenames or #pagenames < 1 then return err("No page names given") end
local pagename
local text
local pagecount = #pagenames
local firstpage = pagenames[1] or "(nil)" -- save for error message, as it the name will be deleted
-- read the page, or a random one if multiple pages were provided
if pagecount > 1 then math.randomseed(os.time()) end
while not text and pagecount > 0 do
local pagenum = 1
if pagecount > 1 then pagenum = math.random(pagecount) end -- pick a random title
pagename = pagenames[pagenum]
if pagename and pagename ~= "" then
pagename = mw.ustring.match(pagename, "%[%[%s*(.-)[]|#]") or pagename -- "[[Foo|Bar]]" → "Foo"
pagename = mw.ustring.match(pagename, "%S.*%S") -- strip leading and trailing white space
if pagename and pagename ~= "" then
local title = mw.title.new(pagename) -- Find the lead section of the named page
if not title then return err("No title for page name " .. pagename) end
local redir = mRedirect.getTarget(title)
if redir then title = mw.title.new(redir) end
text = title:getContent()
end
end
if not text then table.remove(pagenames, pagenum) end -- this one didn't work; try another
pagecount = pagecount - 1 -- ensure that we exit the loop eventually
end
if not text then return err("Cannot read a valid page: first name is " .. firstpage) end
text = mw.ustring.gsub(text, "<!%-%-.-%-%->","") -- remove HTML comments
text = mw.ustring.gsub(text, "%c%s*==.*","") -- remove first heading and everything after it
text = mw.ustring.gsub(text, "<noinclude>.-</noinclude>", "") -- remove noinclude bits
text = mw.ustring.gsub(text, "<%s*ref[^>]-/%s*>", "") -- remove refs cited elsewhere
text = mw.ustring.gsub(text, "<%s*ref.->.-<%s*/%s*ref%s*>", "") -- remove refs
text = mw.ustring.gsub(text, "<%s*imagemap.->.-<%s*/%s*imagemap%s*>", "") -- remove imagemaps
for _, t in pairs {"[Ee]fn", "[Ee]fn-la", "[Ee]l[mn]", "[Rr]p?", "[Ss]fn[bp]", "[Ss]f[bn]", "NoteTag", "#[Tt]ag:%s*[Rr]ef",
"[CcDd]n", "Citation needed", "Disambiguation needed"} do
text = mw.ustring.gsub(text, "{{%s*" .. t .. "%s*|.-}}", "") -- remove ref and footnote templates
end
text = mw.ustring.gsub(text, "\n%s*{{%s*[Tt][Oo][Cc].-}}", "\n") -- remove most common tables of contents
local allparas = true -- keep all paragraphs?
if options.paraflags then
for _, v in pairs(options.paraflags) do
if v then allparas = false end -- if any para specifically requested, don't keep all
end
end
local maxfile = 0 -- for efficiency, stop checking images after this many have been found
if options.fileflags then
for k, v in pairs(options.fileflags) do
if v and k > maxfile then maxfile = k end
end
end
-- a basic parser to trim down the lead
local inlead = false -- have we found some text yet?
local t = "" -- the stripped down output text
local files = 0 -- how many [[Image: or [[File: so far
local paras = 0 -- how many paragraphs so far
text = mw.ustring.gsub(text,"^%s*","") -- remove initial white space
repeat -- loop around parsing a template, image or paragraph
local token = mw.ustring.match(text, "^%b{}%s*") or false -- {{Template}}
if token then
if inlead then -- keep comments and templates only within text body
t = t .. token
elseif files < maxfile then -- look for [[File:... embedded in an infobox etc. in the preamble
local image = parseimage(token, false) or argimage(token)
if image and checkimage(image) then -- keep comments and templates only within text body
files = files + 1
if options.fileflags and options.fileflags[files] then
image = mw.ustring.gsub(image, "|%s*frameless%s*%f[|%]]", "") -- make image a thumbnail, not frameless etc.
image = mw.ustring.gsub(image, "|%s*framed?%s*%f[|%]]", "")
if not mw.ustring.match(image, "|%s*thumb%s*%f[|%]]")
and not mw.ustring.match(image, "|%s*thumbnail%s*%f[|%]]") then
image = mw.ustring.gsub(image, "(%]%]%s*)$", "|thumb%1")
end
if options.fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. options.fileargs .. "%1") end
t = t .. image
end
end
end
else
token = parseimage(text, true)
if token then
if files < maxfile and checkimage(token) then
files = files + 1
if options.fileflags and options.fileflags[files] then
local image = token
if options.fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. options.fileargs .. "%1") end
t = t .. image
end
end
else -- got a paragraph, which ends at a file, image, blank line or end of text
local afterend = mw.ustring.len(text) + 1
local blankpos = mw.ustring.find(text, "\n%s*\n") or afterend
local endpos = math.min(
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
blankpos)
token = mw.ustring.sub(text, 1, endpos-1)
if blankpos < afterend and blankpos == endpos then -- paragraph ends with a blank line
token = token .. mw.ustring.match(text, "\n%s*\n", blankpos)
end
inlead = true
paras = paras + 1
if allparas or (options.paraflags and options.paraflags[paras]) then t = t .. token end
end
end
if token then text = mw.ustring.sub(text, mw.ustring.len(token)+1) end
until not text or text == "" or not token or token == ""
text = mw.ustring.gsub(t, "\n+$", "") -- remove trailing line feeds, so "{{Transclude text excerpt|Foo}} more" flows on one line
if options.more then text = text .. " '''[[" .. pagename .. "|" .. options.more .. "]]'''" end
return text
end
-- Convert a comma-separated list of numbers or min-max ranges into a list of booleans, e.g. "1,3-5" → {1=true,2=false,3=true,4=true,5=true}
local function numberflags(str)
local ranges = mw.text.split(str, ",") -- parse ranges, e.g. "1,3-5" → {"1","3-5"}
local flags = {}
for _, r in pairs(ranges) do
local min, max = mw.ustring.match(r, "^%s*(%d+)%s*%-%s*(%d+)%s*$") -- "3-5" → min=3 max=5
if not max then min, max = mw.ustring.match(r, "^%s*((%d+))%s*$") end -- "1" → min=1 max=1
if max then
for p = min, max do flags[p] = true end
end
end
return flags
end
-- Shared template invocation code for lead and random functions
local function leadrandom(frame, israndom)
-- args = { 1,2,... = page names, paragraphs = list e.g. "1,3-5", files = list, more = text}
local args = frame.args -- from calling module
local pargs = frame:getParent().args -- from template
local pagenames = { args[1] or pargs[1] } -- For lead, ignore all but the first unnamed argument
if israndom then
-- For random, accept any number of page names. If more than one, we'll pick one randomly
for i, p in pairs(args) do
if p and type(i) == 'number' and i > 1 then table.insert(pagenames, p) end
end
for i, p in pairs(pargs) do
if p and type(i) == 'number' and i > 1 and not args[i] then table.insert(pagenames, p) end
end
end
local options = {}
options.paraflags = numberflags(args["paragraphs"] or pargs["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
options.fileflags = numberflags(args["files"] or pargs["files"] or "") -- parse file numbers
options.fileargs = args["fileargs"] or pargs["fileargs"]
options.more = args["more"] or pargs["more"]
if options.more and options.more == "" then options.more = "weiterlesen..." end -- more= is short for this default text
options.errors = args["errors"] or pargs["errors"]
local text = p._lead(pagenames, options)
return frame:preprocess(text)
end
-- Entry points for template callers using #invoke:
function p.lead(frame) return leadrandom(frame, false) end
function p.random(frame) return leadrandom(frame, true) end
return p