Module:Excerpt: Difference between revisions

Content deleted Content added
Remove Featured Article star more vigorously
Use infobox CSS class if it exists (mainly for dark mode compatibility)
 
(137 intermediate revisions by 10 users not shown)
Line 1:
-- Module:Excerpt implements the Excerpt template
local p = {}
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:Excerpt
local mRedirect = require('Module:Redirect')
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382 & others
-- License: CC-BY-SA-3.0
 
local Transcluder = require( 'Module:Transcluder' )
local errors
-- Return blank text, or an error message if requested
local function err(text)
if errors then error(text, 2) end
return ""
end
 
local yesno = require( 'Module:Yesno' )
-- In text, match pre..list[1]..post or pre..list[2]..post or ...
local function matchany(text, pre, list, post)
local match
for i = 1, #list do
match = mw.ustring.match(text, pre .. list[i] .. post)
if match then return match end
end
return nil
end
 
local ok, config = pcall( require, 'Module:Excerpt/config' )
-- Check image for suitability
if not ok then config = {} end
local function checkimage(image)
local page = matchany(image, "", {"[Ff]ile", "[Ii]mage"}, "%s*:[^|%]]*") -- match File:(name) or Image:(name)
if not page then return false end
 
local p = {}
-- Limit to image types: .gif, .jpg, .jpeg, .png, .svg, .tiff, .xcf (exclude .ogg audio etc.)
if not matchany(page, "%.", {"[Gg][Ii][Ff]", "[Jj][Pp][Ee]?[Gg]", "[Pp][Nn][Gg]", "[Ss][Vv][Gg]", "[Tt][Ii][Ff][Ff]", "[Xx][Cc][Ff]"}, "%s*$") then
return false
end
 
local title = mw.title.new(":" .. page) -- Read description page (for :File:Foo rather than File:Foo)
if not title then return false 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
 
-- Helper function to get arguments
-- Attempt to parse [[File:...]] or [[Image:...]], either anywhere (start=false) or at the start only (start=true)
local args
local function parseimage(text, start)
local startrefunction =getArg( ""key, default )
local value = args[ key ]
if start then startre = "^" end -- a true flag restricts search to start of string
if value and mw.text.trim( value ) ~= '' then
local image = matchany(text, startre .. "%[%[%s*", {"[Ff]ile", "[Ii]mage"}, "%s*:.*") -- [[File: or [[Image: ...
return value
if image then
image = mw.ustring.match(image, "%b[]%s*") -- matching [[...]] to handle wikilinks nested in caption
end
return imagedefault
end
 
-- Helper function to handle errors
-- Parse a caption, which ends at a | (end of parameter) or } (end of infobox) but may contain nested [..] and {..}
local function parsecaptiongetError(caption message, value )
if nottype( captionmessage then) return== nil'string' endthen
message = Transcluder.getError( message, value )
local len = mw.ustring.len(caption)
local pos = 1
while pos <= len do
local linkstart, linkend = mw.ustring.find(caption, "%b[]", pos)
linkstart = linkstart or len + 1 -- avoid comparison with nil when no link
local templatestart, templateend = mw.ustring.find(caption, "%b{}", pos)
templatestart = templatestart or len + 1 -- avoid comparison with nil when no template
local argend = mw.ustring.find(caption, "[|}]", pos) or len + 1
if linkstart < templatestart and linkstart < argend then
pos = linkend + 1 -- skip wikilink
elseif templatestart < argend then
pos = templateend + 1 -- skip template
else -- argument ends before the next wikilink or template
return mw.ustring.sub(caption, 1, argend - 1)
end
end
if config.categories and config.categories.errors and mw.title.getCurrentTitle().isContentPage then
end
message:node( '[[Category:' .. config.categories.errors .. ']]' )
 
-- 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 = matchany(text, "|%s*", {"image", "PD_image", "image_flag", "Ship image", "Cover"}, "%s*=%s*(.*)")
 
if image then -- add in relevant optional parameters: caption, alt text and image size
token = parseimage(image, true) -- look for image=[[File:...]] etc.
if not token then
image = mw.ustring.match(image, "^[^}|]*") -- remove later arguments
token = "[[" -- Add File: unless name already begins File: or Image:
if not matchany(image, "^", {"[Ff]ile", "[Ii]mage"}, "%s*:") then
token = token .. "File:"
end
token = token .. image
local caption = parsecaption(matchany(text, "|%s*", {"[Cc]aption", "Ship caption"}, "%s*=%s*(.*)"))
if caption and mw.ustring.match(caption, "%S") 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 and mw.ustring.match(image_size, "%S") then token = token .. "|" .. image_size end
token = token .. "]]"
end
token = mw.ustring.gsub(token, "\n","") .. "\n"
end
end
return message
 
return token
end
 
-- HelpHelper gsubfunction to removeget unwantedlocalized templatesmessages
local function getMessage( key )
-- If template is unwanted then return "" (gsub will replace by nothing), else return nil (gsub will keep existing string)
local ok, TNT = pcall( require, 'Module:TNT' )
local function striptemplate(t)
if not ok then return key end
local unwanted = {"[Ee]fn", "[Ee]fn%-[lu]a", "[Ee]l[mn]", "[Rr]p?", "[Ss]fn[bmp]", "[Ss]f[bn]", "NoteTag", "#[Tt]ag:%s*[Rr]ef", "[Rr]efn?",
return TNT.format( 'I18n/Module:Excerpt.tab', key )
"[CcDd]n", "[Cc]itation needed", "[Dd]isambiguation needed", "[Ff]eatured article", "[Gg]ood article"}
for _, u in pairs(unwanted) do
if mw.ustring.match(t, "^{{%s*" .. u .. "%s*%f[|}]") then return "" end -- unwanted template: remove
end
return nil -- not an unwanted template: keep
end
 
-- Main entry point for templates
-- Main function returns a string value: text of the lead of a page
local function p.main(pagenames, optionsframe )
args = Transcluder.parseArgs( frame )
errors = options.errors -- set the module level boolean used in local function err
 
-- Make sure the requested page exists
if not pagenames or #pagenames < 1 then return err("No page names given") end
local pagenamepage = getArg( 1 )
if not page or page == '{{{1}}}' then return getError( 'no-page' ) end
local text
local pagecounttitle = #pagenamesmw.title.new(page)
if not title then return getError( 'invalid-title', page ) end
local firstpage = pagenames[1] or "(nil)" -- save for error message, as it the name will be deleted
if title.isRedirect then title = title.redirectTarget end
if not title.exists then return getError( 'page-not-found', page ) end
page = title.prefixedText
 
-- Set variables from the template parameters
-- read the page, or a random one if multiple pages were provided
local section = getArg( 2, mw.ustring.match( getArg( 1 ), '[^#]+#(.+)' ) )
if pagecount > 1 then math.randomseed(os.time()) end
local hat = yesno( getArg( 'hat', true ) )
while not text and pagecount > 0 do
local edit = yesno( getArg( 'edit', true ) )
local pagenum = 1
local this = getArg( 'this' )
if pagecount > 1 then pagenum = math.random(pagecount) end -- pick a random title
local only = getArg( 'only' )
pagename = pagenames[pagenum]
local files = getArg( 'files', getArg( 'file', ( only == 'file' and 1 ) ) )
if pagename and pagename ~= "" then
local lists = getArg( 'lists', getArg( 'list', ( only == 'list' and 1 ) ) )
pagename = mw.ustring.match(pagename, "%[%[%s*(.-)[]|#]") or pagename -- "[[Foo|Bar]]" → "Foo"
local tables = getArg( 'tables', getArg( 'table', ( only == 'table' and 1 ) ) )
pagename = mw.ustring.gsub(pagename, "^%s+", "") -- strip leading ...
local templates = getArg( 'templates', getArg( 'template', ( only == 'template' and 1 ) ) )
pagename = mw.ustring.gsub(pagename, "%s+$", "") -- ...and trailing white space
local paragraphs = getArg( 'paragraphs', getArg( 'paragraph', ( only == 'paragraph' and 1 ) ) )
local references = getArg( 'references' )
local subsections = not yesno( getArg( 'subsections' ) )
local noLinks = not yesno( getArg( 'links', true ) )
local noBold = not yesno( getArg( 'bold' ) )
local onlyFreeFiles = yesno( getArg( 'onlyfreefiles', true ) )
local briefDates = yesno( getArg( 'briefdates', false ) )
local inline = yesno( getArg( 'inline' ) )
local quote = yesno( getArg( 'quote' ) )
local more = yesno( getArg( 'more' ) )
local class = getArg( 'class' )
local displaytitle = getArg( 'displaytitle' ) or page
 
-- Build the hatnote
if pagename and pagename ~= "" then
if hat and not inline then
local title = mw.title.new(pagename) -- Find the lead section of the named page
if this then
if not title then return err("No title for page name " .. pagename) end
hat = this
local redir = mRedirect.getTarget(title)
elseif quote then
if redir then title = mw.title.new(redir) end
hat = getMessage( 'this' )
pagename = redir or pagename
elseif only then
 
texthat = title:getContentgetMessage( only )
endelse
hat = getMessage( 'section' )
end
hat = hat .. ' ' .. getMessage( 'excerpt' ) .. ' '
if not text then table.remove(pagenames, pagenum) end -- this one didn't work; try another
if section then
pagecount = pagecount - 1 -- ensure that we exit the loop after at most #pagenames iterations
hat = hat .. '[[:' .. page .. '#' .. mw.uri.anchorEncode( section ) .. '|' .. displaytitle
.. ' § ' .. mw.ustring.gsub( section, '%[%[([^]|]+)|?[^]]*%]%]', '%1' ) .. ']].' -- remove nested links
else
hat = hat .. '[[:' .. page .. '|' .. displaytitle .. ']].'
end
if edit then
hat = hat .. '<span class="mw-editsection-like plainlinks"><span class="mw-editsection-bracket">[</span>['
hat = hat .. title:fullUrl( 'action=edit' ) .. ' ' .. mw.message.new( 'editsection' ):plain()
hat = hat .. ']<span class="mw-editsection-bracket">]</span></span>'
end
if config.hat then
hat = config.hat .. hat .. '}}'
hat = frame:preprocess( hat )
else
hat = mw.html.create( 'div' ):addClass( 'dablink excerpt-hat' ):wikitext( hat )
end
else
hat = nil
end
if not text then return err("Cannot read a valid page: first name is " .. firstpage) end
 
-- Build the "Read more" link
text = mw.ustring.gsub(text, "<!%-%-.-%-%->","") -- remove HTML comments
if more and not inline then
text = mw.ustring.gsub(text, "%c%s*==.*","") -- remove first ==Heading== and everything after it
more = "'''[[" .. page .. '#' .. ( section or '' ) .. "|" .. getMessage( 'more' ) .. "]]'''"
text = mw.ustring.gsub(text, "<noinclude>.-</noinclude>", "") -- remove noinclude bits
more = mw.html.create( 'div' ):addClass( 'noprint excerpt-more' ):wikitext( more )
text = mw.ustring.gsub(text, "<%s*ref[^>]-/%s*>", "") -- remove refs cited elsewhere
else
text = mw.ustring.gsub(text, "<%s*ref.->.-<%s*/%s*ref%s*>", "") -- remove refs
more = nil
text = mw.ustring.gsub(text, "<%s*imagemap.->.-<%s*/%s*imagemap%s*>", "") -- remove imagemaps
end
text = mw.ustring.gsub(text, "%b{}", striptemplate) -- remove unwanted templates such as references
text = mw.ustring.gsub(text, "\n%s*{{%s*[Tt][Oo][Cc].-}}", "\n") -- remove most common tables of contents
 
-- Build the options for Module:Transcluder out of the template parameters and the desired defaults
local allparas = true -- keep all paragraphs?
iflocal options.paraflags then= {
files = files,
for _, v in pairs(options.paraflags) do
lists = lists,
if v then allparas = false end -- if any para specifically requested, don't keep all
tables = tables,
end
paragraphs = paragraphs,
sections = subsections,
categories = 0,
references = references,
only = only and mw.text.trim( only, 's' ) .. 's',
noLinks = noLinks,
noBold = noBold,
noSelfLinks = true,
noNonFreeFiles = onlyFreeFiles,
noBehaviorSwitches = true,
fixReferences = true,
linkBold = true,
}
 
-- Get the excerpt itself
local title = page .. '#' .. ( section or '' )
local ok, excerpt = pcall( Transcluder.get, title, options )
if not ok then return getError( excerpt ) end
if mw.text.trim( excerpt ) == '' and not only then
if section then return getError( 'section-empty', section ) else return getError( 'lead-empty' ) end
end
 
-- Fix birth and death dates, but only in the first paragraph
local maxfile = 0 -- for efficiency, stop checking images after this many have been found
if options.fileflagsbriefDates then
local startpos = 1 -- skip initial templates
for k, v in pairs(options.fileflags) do
local s
if v and k > maxfile then maxfile = k end -- set maxfile = highest key in fileflags
local e = 0
repeat
startpos = e + 1
s, e = mw.ustring.find( excerpt, "%s*%b{}%s*", startpos )
until not s or s > startpos
s, e = mw.ustring.find( excerpt, "%b()", startpos ) -- get (...), which may be (year–year)
if s and s < startpos + 100 then -- look only near the start
local year1, conjunction, year2 = mw.ustring.match( mw.ustring.sub( excerpt, s, e ), '(%d%d%d+)(.-)(%d%d%d+)' )
if year1 and year2 and (mw.ustring.match( conjunction, '[%-–—]' ) or mw.ustring.match( conjunction, '{{%s*[sS]nd%s*}}' )) then
local y1 = tonumber(year1)
local y2 = tonumber(year2)
if y2 > y1 and y2 < y1 + 125 and y1 <= tonumber( os.date( "%Y" )) then
excerpt = mw.ustring.sub( excerpt, 1, s ) .. year1 .. "–" .. year2 .. mw.ustring.sub( excerpt, e )
end
end
end
end
 
-- aIf basicno parserfile was found, try to trimget one downfrom the leadinfobox
local fileNamespaces = Transcluder.getNamespaces( 'File' )
local leadstart = nil -- have we found some text yet?
if ( ( only == 'file' or only == 'files' ) or ( not only and ( files ~= '0' or not files ) ) ) and -- caller asked for files
local t = "" -- the stripped down output text
not Transcluder.matchAny( excerpt, '%[%[', fileNamespaces, ':' ) and -- and there are no files in Transcluder's output
local files = 0 -- how many images so far
config.captions -- and we have the config option required to try finding files in templates
local paras = 0 -- how many paragraphs so far
then
 
-- We cannot distinguish the infobox from the other templates so we search them all
text = mw.ustring.gsub(text,"^%s*","") -- remove initial white space
local infobox = Transcluder.getTemplates( excerpt );
repeat -- loop around parsing a template, image or paragraph
infobox = table.concat( infobox )
local token = mw.ustring.match(text, "^%b{}%s*") or false -- {{Template}}
local parameters = Transcluder.getParameters( infobox )
if token then -- found a template
local file, captions, caption, cssclasses, cssclass
if leadstart then -- lead has already started, so keep the template within the text
for _, pair in pairs( config.captions ) do
t = t .. token
file = pair[1]
elseif files < maxfile then -- discard template, but if we are still collecting images...
file = parameters[file]
local image = argimage(token) or parseimage(token, false) -- look for embedded [[File:...]], |image=, etc.
if file and Transcluder.matchAny( file, '^.*%.', { '[Jj][Pp][Ee]?[Gg]', '[Pp][Nn][Gg]', '[Gg][Ii][Ff]', '[Ss][Vv][Gg]' }, '.*' ) then
if image and checkimage(image) then -- if image is found and qualifies (not a sound file, non-free, etc.)
file = mw.ustring.match( file, '%[?%[?.-:([^{|]+)%]?%]?' ) or file -- [[File:Example.jpg{{!}}upright=1.5]] to Example.jpg
 
captions = pair[2]
files = files + 1 -- count the file, whether displaying it or not
for _, p in pairs( captions ) do
if options.fileflags and options.fileflags[files] then -- if displaying this image
if parameters[ p ] then caption = parameters[ p ] break end
image = mw.ustring.gsub(image, "|%s*frameless%s*%f[|%]]", "") -- make image a thumbnail, not frameless etc.
end
image = mw.ustring.gsub(image, "|%s*framed?%s*%f[|%]]", "")
if not matchany(image, "|%s*", {"thumb", "thumbnail"}, "%s*%f[|%]]") then
-- Check for CSS classes
image = mw.ustring.gsub(image, "(%]%]%s*)$", "|thumb%1")
-- We opt to use skin-invert-image instead of skin-invert
-- in all other cases, the CSS provided in the infobox is used
if pair[3] then
cssclasses = pair[3]
for _, p in pairs(cssclasses) do
if parameters[p] then
cssclass = ((parameters[p] == 'skin-invert') and 'skin-invert-image' or parameters[p])
break
end
if options.fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. options.fileargs .. "%1") end
t = t .. image
end
end
end
excerpt = '[[File:' .. file ..
else -- the next token in text is not a template
(cssclass and ('|class=' .. cssclass) or '') ..
token = parseimage(text, true)
'|thumb|' .. (caption or '') .. ']]' .. excerpt
if token then -- the next token in text looks like an image
if ( onlyFreeFiles ) then
if files < maxfile and checkimage(token) then -- if more images are wanted and this is a wanted image
excerpt = Transcluder.removeNonFreeFiles( excerpt )
files = files + 1
if options.fileflags and options.fileflags[files] then
local image = token -- copy token for manipulation by adding |right etc. without changing the original
if options.fileargs then image = mw.ustring.gsub(image, "(%]%]%s*)$", "|" .. options.fileargs .. "%1") end
t = t .. image
end
end
break
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 -- position of next paragraph delimiter (or end of text)
local endpos = math.min( -- find position of whichever comes first: [[File:, [[Image: or paragraph delimiter
mw.ustring.find(text, "%[%[%s*[Ff]ile%s*:") or afterend,
mw.ustring.find(text, "%[%[%s*[Ii]mage%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
leadstart = leadstart or mw.ustring.len(t) + 1 -- we got a paragraph, so mark the start of the lead section
paras = paras + 1
if allparas or (options.paraflags and options.paraflags[paras]) then t = t .. token end -- add if this paragraph wanted
end -- of "else got a paragraph"
end -- of "else not a template"
 
if token then text = mw.ustring.sub(text, mw.ustring.len(token)+1) end -- remove parsed token from remaining text
until not text or text == "" or not token or token == "" -- loop until all text parsed
 
text = mw.ustring.gsub(t, "\n+$", "") -- remove trailing line feeds, so "{{Transclude text excerpt|Foo}} more" flows on one line
 
-- replace the bold title or synonym near the start of the article by a wikilink to the article
local lang = mw.language.getContentLanguage()
local pos = mw.ustring.find(text, "'''" .. lang:ucfirst(pagename) .. "'''", 1, true) -- look for "'''Foo''' is..." (uc) or "A '''foo''' is..." (lc)
or mw.ustring.find(text, "'''" .. lang:lcfirst(pagename) .. "'''", 1, true) -- plain search: special characters in pagename represent themselves
if pos then
local len = mw.ustring.len(pagename)
text = mw.ustring.sub(text, 1, pos + 2) .. "[[" .. mw.ustring.sub(text, pos + 3, pos + len + 2) .. "]]" .. mw.ustring.sub(text, pos + len + 3, -1) -- link it
else -- look for anything unlinked in bold, assumed to be a synonym of the title (e.g. a person's birth name)
text = mw.ustring.gsub(text, "(.-''')(.-'*)'''", function(a, b)
if mw.ustring.len(a) < 100 + (leadstart or 0) and not mw.ustring.find(b, "%[") then ---if early in article and not wikilinked
return a .. "[[" .. pagename .. "|" .. b .. "]]'''" -- replace '''Foo''' by '''[[pagename|Foo]]
else
return nil -- instruct gsub to make no change
end
end
end, 1) -- "end" here terminates the anonymous replacement function(a, b) passed to gsub
end
 
-- Unlike other elements, templates are filtered here
if options.more then text = text .. " '''[[" .. pagename .. "|" .. options.more .. "]]'''" end -- wikilink to article for more info
-- because we had to search the infoboxes for files
 
local trash
return text
if only and ( only == 'template' or only == 'templates' ) then
end
trash, excerpt = Transcluder.getTemplates( excerpt, templates );
 
else -- Remove blacklisted templates
-- 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 blacklist = config.blacklist and table.concat( config.blacklist, ',' ) or ''
local function numberflags(str)
if templates then
local ranges = mw.text.split(str, ",") -- parse ranges, e.g. "1,3-5" → {"1","3-5"}
if string.sub( templates, 1, 1 ) == '-' then --Unwanted templates. Append to blacklist
local flags = {}
blacklist = templates .. ',' .. blacklist
for _, r in pairs(ranges) do
else --Wanted templates. Replaces blacklist and acts as whitelist
local min, max = mw.ustring.match(r, "^%s*(%d+)%s*%-%s*(%d+)%s*$") -- "3-5" → min=3 max=5
blacklist = templates
if not max then min, max = mw.ustring.match(r, "^%s*((%d+))%s*$") end -- "1" → min=1 max=1
end
if max then
else
for p = min, max do flags[p] = true end
blacklist = '-' .. blacklist
end
trash, excerpt = Transcluder.getTemplates( excerpt, blacklist );
end
return flags
end
 
-- Remove extra line breaks but leave one before and after so the parser interprets lists, tables, etc. correctly
-- Shared template invocation code for lead and random functions
excerpt = mw.text.trim( excerpt )
local function invoke(frame, articlekey)
excerpt = string.gsub( excerpt, '\n\n\n+', '\n\n' )
-- args = { 1,2,... = page names, paragraphs = list e.g. "1,3-5", files = list, more = text}
excerpt = '\n' .. excerpt .. '\n'
local args = {} -- args[k] = frame.args[k] or frame:getParent().args[k] for all k in either (numeric or not)
 
for k, v in pairs(frame:getParent().args) do args[k] = v end
-- Remove nested categories
for k, v in pairs(frame.args) do args[k] = v end -- args from a Lua call have priority over parent args from template
excerpt = frame:preprocess( excerpt )
local categories, excerpt = Transcluder.getCategories( excerpt, options.categories )
 
-- Add tracking categories
local pagenames = {}
if config.categories then
local articlecount = #args
local contentCategory = config.categories.content
if articlekey then -- 1 for lead template; "selected" for selected template
if contentCategory and mw.title.getCurrentTitle().isContentPage then
articlekey = tonumber(articlekey) or args[articlekey]
excerpt = excerpt .. '[[Category:' .. contentCategory .. ']]'
if tonumber(articlekey) then
-- normalise article number into the range 1..#args
if articlecount < 1 then err("No articles provided") end
articlekey = articlekey % articlecount
if articlekey == 0 then articlekey = articlecount end
end
local namespaceCategory = config.categories[ mw.title.getCurrentTitle().namespace ]
pagenames = { args[articlekey] }
if namespaceCategory then
else
excerpt = excerpt .. '[[Category:' .. namespaceCategory .. ']]'
-- For random, accept any number of page names. If more than one, we'll pick one randomly
if articlecount < 1 then err("No articles provided") end
for i, p in pairs(args) do
if p and type(i) == 'number' then table.insert(pagenames, p) end
end
end
 
-- Load the styles
local options = args -- pick up miscellaneous options: more, errors, fileargs
local styles
options.paraflags = numberflags(args["paragraphs"] or "") -- parse paragraphs, e.g. "1,3-5" → {"1","3-5"}
if config.styles then
options.fileflags = numberflags(args["files"] or "") -- parse file numbers
styles = frame:extensionTag( 'templatestyles', '', { src = config.styles } )
if options.more and options.more == "" then options.more = "Read more..." end -- more= is short for this default text
end
 
-- Combine and return the elements
local text = main(pagenames, options)
if inline then
return frame:preprocess(text)
return mw.text.trim( excerpt )
end
local tag = 'div'
if quote then
tag = 'blockquote'
end
excerpt = mw.html.create( 'div' ):addClass( 'excerpt' ):wikitext( excerpt )
local block = mw.html.create( tag ):addClass( 'excerpt-block' ):addClass( class )
return block:node( styles ):node( hat ):node( excerpt ):node( more )
end
 
-- Entry points for templatebackwards callers using #invoke:compatibility
function p.lead( frame ) return invokep.main( frame, 1) end -- {{Transclude lead article}} reads the first and only article
function p.randomexcerpt( frame ) return invokep.main( frame ) end -- {{Transclude random article}} reads any article (default for invoke with one argument)
function p.selected(frame) return invoke(frame, "selected") end -- {{Transclude selected article}} reads the article whose key is in the selected= parameter
 
return p