# Module:Excerpt

-- Module:Excerpt implements the Excerpt template  
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:Excerpt  
-- Authors: User:Sophivorus, User:Certes, User:Aidan9382 & others  
-- License: CC-BY-SA-3.0  

local Transcluder = require( 'Module:Transcluder' )  

local yesno = require( 'Module:Yesno' )  

local ok, config = pcall( require, 'Module:Excerpt/config' )  
if not ok then config = {} end  

local p = {}  

-- Helper function to get arguments  
local args  
local function getArg( key, default )  
local value = args[ key ]  
if value and mw.text.trim( value ) ~= '' then  
return value  
end  
return default  
end  

-- Helper function to handle errors  
local function getError( message, value )  
if type( message ) == 'string' then  
message = Transcluder.getError( message, value )  
end  
if config.categories and config.categories.errors and mw.title.getCurrentTitle.isContentPage then  
message:node( '' )  
end  
return message  
end  

-- Helper function to get localized messages  
local function getMessage( key )  
local ok, TNT = pcall( require, 'Module:TNT' )  
if not ok then return key end  
return TNT.format( 'I18n/Module:Excerpt.tab', key )  
end  

-- Main entry point for templates  
function p.main  
args = Transcluder.parseArgs  

-- Make sure the requested page exists  
local page = getArg( 1 )  
if not page or page == '}' then return getError( 'no-page' ) end  
local title = mw.title.new(page)  
if not title then return getError( 'invalid-title', page ) end  
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  
local section = getArg( 2, mw.ustring.match( getArg( 1 ), '[^#]+#(.+)' ) )  
local hat = yesno( getArg( 'hat', true ) )  
local edit = yesno( getArg( 'edit', true ) )  
local this = getArg( 'this' )  
local only = getArg( 'only' )  
local files = getArg( 'files', getArg( 'file', ( only == 'file' and 1 ) ) )  
local lists = getArg( 'lists', getArg( 'list', ( only == 'list' and 1 ) ) )  
local tables = getArg( 'tables', getArg( 'table', ( only == 'table' and 1 ) ) )  
local templates = getArg( 'templates', getArg( 'template', ( only == 'template' and 1 ) ) )  
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 hat and not inline then  
if this then  
hat = this  
elseif quote then  
hat = getMessage( 'this' )  
elseif only then  
hat = getMessage( only )  
else  
hat = getMessage( 'section' )  
end  
hat = hat .. ' ' .. getMessage( 'excerpt' ) .. ' '  
if section then  
hat = hat .. '[[:' .. page .. '#' .. mw.uri.anchorEncode( section ) .. '|' .. displaytitle  
.. ' § ' .. mw.ustring.gsub( section, '%[%[([^]|]+)|?[^]]*%]%]', '%1' ) .. ']].' -- remove nested links  
else  
hat = hat .. '' .. displaytitle .. '.'  
end  
if edit then  
hat = hat .. '<span><span>[</span>['  
hat = hat .. title:fullUrl( 'action=edit' ) .. ' ' .. mw.message.new( 'editsection' ):plain  
hat = hat .. ']<span>]</span></span>'  
end  
if config.hat then  
hat = config.hat .. hat .. '}}'  
hat = :preprocess( hat )  
else  
hat = mw.html.create( 'div' ):addClass( 'dablink excerpt-hat' ):wikitext( hat )  
end  
else  
hat = nil  
end  

-- Build the "Read more" link  
if more and not inline then  
more = "**" .. getMessage( 'more' ) .. "**"  
more = mw.html.create( 'div' ):addClass( 'noprint excerpt-more' ):wikitext( more )  
else  
more = nil  
end  

-- Build the options for Module:Transcluder out of the template parameters and the desired defaults  
local options = {  
files = files,  
lists = lists,  
tables = tables,  
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  
if briefDates then  
local startpos = 1 -- skip initial templates  
local s  
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, '' )) 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  

-- If no file was found, try to get one from the infobox  
local fileNamespaces = Transcluder.getNamespaces( 'File' )  
if ( ( only == 'file' or only == 'files' ) or ( not only and ( files ~= '0' or not files ) ) ) and -- caller asked for files  
not Transcluder.matchAny( excerpt, '%[%[', fileNamespaces, ':' ) and -- and there are no files in Transcluder's output  
config.captions -- and we have the config option required to try finding files in templates  
then  
-- We cannot distinguish the infobox from the other templates so we search them all  
local infobox = Transcluder.getTemplates( excerpt );  
infobox = table.concat( infobox )  
local parameters = Transcluder.getParameters( infobox )  
local file, captions, caption  
for _, pair in pairs( config.captions ) do  
file = pair[1]  
file = parameters[file]  
if file and Transcluder.matchAny( file, '^.*%.', { '[Jj][Pp][Ee]?[Gg]', '[Pp][Nn][Gg]', '[Gg][Ii][Ff]', '[Ss][Vv][Gg]' }, '.*' ) then  
file = mw.ustring.match( file, '%[?%[?.-:([^{|]+)%]?%]?' ) or file -- to Example.jpg  
captions = pair[2]  
for _, p in pairs( captions ) do  
if parameters[ p ] then caption = parameters[ p ] break end  
end  
excerpt = '' .. excerpt  
if ( onlyFreeFiles ) then  
excerpt = Transcluder.removeNonFreeFiles( excerpt )  
end  
break  
end  
end  
end  

-- Unlike other elements, templates are filtered here  
-- because we had to search the infoboxes for files  
local trash  
if only and ( only == 'template' or only == 'templates' ) then  
trash, excerpt = Transcluder.getTemplates( excerpt, templates );  
else -- Remove blacklisted templates  
local blacklist = config.blacklist and table.concat( config.blacklist, ',' ) or ''  
if templates then  
if string.sub( templates, 1, 1 ) == '-' then --Unwanted templates. Append to blacklist  
blacklist = templates .. ',' .. blacklist  
else --Wanted templates. Replaces blacklist and acts as whitelist  
blacklist = templates  
end  
else  
blacklist = '-' .. blacklist  
end  
trash, excerpt = Transcluder.getTemplates( excerpt, blacklist );  
end  

-- Remove extra line breaks but leave one before and after so the parser interprets lists, tables, etc. correctly  
excerpt = mw.text.trim( excerpt )  
excerpt = string.gsub( excerpt, '\n\n\n+', '\n\n' )  
excerpt = '\n' .. excerpt .. '\n'  

-- Remove nested categories  
excerpt = :preprocess( excerpt )  
local categories, excerpt = Transcluder.getCategories( excerpt, options.categories )  

-- Add tracking categories  
if config.categories then  
local contentCategory = config.categories.content  
if contentCategory and mw.title.getCurrentTitle.isContentPage then  
excerpt = excerpt .. ''  
end  
local namespaceCategory = config.categories[ mw.title.getCurrentTitle.namespace ]  
if namespaceCategory then  
excerpt = excerpt .. ''  
end  
end  

-- Load the styles  
local styles  
if config.styles then  
styles = :extensionTag( 'templatestyles', '', { src = config.styles } )  
end  

-- Combine and return the elements  
if inline then  
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 backwards compatibility  
function p.lead return p.main end  
function p.excerpt return p.main end  

return p