-- 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
(
'[[Category:'
..
config
.
categories
.
errors
..
']]'
)
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
(
frame
)
args
=
Transcluder
.
parseArgs
(
frame
)
-- Make sure the requested page exists
local
page
=
getArg
(
1
)
if
not
page
or
page
==
'{{{1}}}'
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
..
'[[:'
..
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
-- Build the "Read more" link
if
more
and
not
inline
then
more
=
"'''[["
..
page
..
'#'
..
(
section
or
''
)
..
"|"
..
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
,
'{{%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
-- 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
-- [[File:Example.jpg{{!}}upright=1.5]] to Example.jpg
captions
=
pair
[
2
]
for
_
,
p
in
pairs
(
captions
)
do
if
parameters
[
p
]
then
caption
=
parameters
[
p
]
break
end
end
excerpt
=
'[[File:'
..
file
..
'|thumb|'
..
(
caption
or
''
)
..
']]'
..
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
=
frame
:
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
..
'[[Category:'
..
contentCategory
..
']]'
end
local
namespaceCategory
=
config
.
categories
[
mw
.
title
.
getCurrentTitle
().
namespace
]
if
namespaceCategory
then
excerpt
=
excerpt
..
'[[Category:'
..
namespaceCategory
..
']]'
end
end
-- Load the styles
local
styles
if
config
.
styles
then
styles
=
frame
:
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
(
frame
)
return
p
.
main
(
frame
)
end
function
p
.
excerpt
(
frame
)
return
p
.
main
(
frame
)
end
return
p