Module:Excerpt/portals
Appearance
From Wikipedia, the free encyclopedia
This module is rated as beta. It is considered ready for widespread use, but as it is still relatively new, it should be applied with some caution to ensure results are as expected.
Page extended-confirmed-protected This module is currently under extended confirmed protection.
Extended confirmed protection prevents edits from all unregistered editors and registered users with fewer than 30 days tenure and 500 edits. The policy on community use specifies that extended confirmed protection can be applied to combat disruption, if semi-protection has proven to be ineffective. Extended confirmed protection may also be applied to enforce arbitration sanctions. Please discuss any changes on the talk page; you may submit an edit request to ask for uncontroversial changes supported by consensus.
Extended confirmed protection prevents edits from all unregistered editors and registered users with fewer than 30 days tenure and 500 edits. The policy on community use specifies that extended confirmed protection can be applied to combat disruption, if semi-protection has proven to be ineffective. Extended confirmed protection may also be applied to enforce arbitration sanctions. Please discuss any changes on the talk page; you may submit an edit request to ask for uncontroversial changes supported by consensus.
Warning This Lua module is used on approximately 4,800 pages and changes may be widely noticed. Test changes in the module's /sandbox or /testcases subpages, or in your own module sandbox. Consider discussing changes on the talk page before implementing them.
This module depends on the following other modules:
Usage
{{#invoke:Excerpt/portals|lead}}- Transcludes the lead of an article as an excerpt. Main documentation: {{Transclude lead excerpt/doc }}
{{#invoke:Excerpt/portals|linked}}- Transcludes as an excerpt the lead of an article selected randomly from wikilinks on a page. Main documentation: {{Transclude linked excerpt/doc }}
{{#invoke:Excerpt/portals|listitem}}- Transcludes as an excerpt the lead of an article selected randomly from list items on a page. Main documentation: {{Transclude list item excerpt/doc }}
{{#invoke:Excerpt/portals|random}}- Transcludes as an excerpt the lead of an article selected randomly from the parameters. Main documentation: {{Transclude random excerpt/doc }}
{{#invoke:Excerpt/portals|selected}}- Transcludes the lead of a selected article as an excerpt. Main documentation: {{Transclude selected excerpt/doc }}
Note
Before saving a change to this module, please preview with:
- Template:Transclude lead excerpt/testcases
- Template:Transclude linked excerpt/testcases
- Template:Transclude list item excerpt/testcases
- Template:Transclude random excerpt/testcases
- Template:Transclude selected excerpt/testcases
See also
The above documentation is transcluded from Module:Excerpt/portals/doc. (edit | history)
Editors can experiment in this module's sandbox (edit | diff) and testcases (create) pages.
Subpages of this module.
Editors can experiment in this module's sandbox (edit | diff) and testcases (create) pages.
Subpages of this module.
-- ATTENTION ! -- This version of Excerpt is designed specifically for the portal namespace and its associated templates -- Prefer Module:Excerpt whenever possible -- Name of the category to track content pages with errors localerrorCategory="Articles with broken excerpts" -- Error messages localerrorMessages={ prefix="Excerpt error: ", noPage="No page given", pageNotFound="Page '%s' not found", leadEmpty="Lead section is empty", sectionEmpty="Section '%s' is empty", sectionNotFound="Section '%s' not found", fragmentEmpty="Fragment '%s' is empty", fragmentNotFound="Fragment '%s' not found" } -- Regular expressions to match all aliases of the file namespace localfileNamespaces={ "[Ff]ile", "[Ii]mage" } -- Regular expressions to match all image parameters localimageParams={ {"thumb","thumbnail","frame","framed","frameless"}, {"right","left","center","centre","none"}, {"baseline","middle","sub","super","text-top","text-bottom","top","bottom"} } -- Regular expressions to match all infobox parameters for image captions localcaptionParams={ "[^=|]*[Cc]aption[^=|]*", "[^=|]*[Ll]egend[^=|]*" } -- List of file types that are allowed to be transcluded localfileTypes={"[Gg][Ii][Ff]","[Jj][Pp][Ee]?[Gg]","[Pp][Nn][Gg]","[Ss][Vv][Gg]","[Tt][Ii][Ff][Ff]","[Xx][Cc][Ff]"} -- Regular expressions to match all inline templates that are undesirable in excerpts localunwantedInlineTemplates={ "[Ee]fn","[Ee]fn%-[lu][arg]","[Ee]fn [%a ]-","[Ee]l[mn]","[Rr]p?","[Ss]fn[bmp]","[Ss]f[bn]","[Nn]ote[Tt]ag","#[Tt]ag:%s*[Rr]ef","[Rr]efn?","[Ff]amily[ _]name[ _]footnote", "[CcDd]n","[Cc]itation[%- _]needed","[Dd]isambiguation needed","[Ff]eatured article","[Gg]ood article", "[Dd]ISPLAYTITLE","[Ss]hort[ _]+description","[Cc]itation","[Cc]ite[%- _]+[%w_%s]-","[Cc]oor[%w_%s]-", "[Uu]?n?[Rr]eliable source[%?%w_%s]-","[Rr]s%??","[Vv]c","[Vv]erify credibility","[Bb]y[ _]*[Ww]ho[m]*%??","[Ww]ikisource[ -_]*multi","[Ii]nflation[ _/-]*[Ff]n", "[Bb]iblesource", "[Dd]ecadebox", "[Ee]vents by year for decade", -- aliases for Clarification needed "[Cc]f[ny]","[Cc]larification[ _]+inline","[Cc]larification[%- _]*needed","[Cc]larification","[Cc]larify%-inline","[Cc]larify%-?me", "[Cc]larify[ _]+inline","[Cc]larify","[Cc]LARIFY","[Cc]onfusing%-inline","[Cc]onfusing%-short","[Ee]xplainme","[Hh]uh[ _]*%??","[Ww]hat%?", "[Ii]nline[ _]+[Uu]nclear","[Ii]n[ _]+what[ _]+sense","[Oo]bscure","[Pp]lease[ _]+clarify","[Uu]nclear[ _]+inline","[Ww]hat's[ _]+this%?", "[Gg]eoQuelle","[Nn]eed[s]+[%- _]+[Ii][Pp][Aa]","[Ii]PA needed", -- aliases for Clarification needed lead "[Cc]itation needed %(?lea?de?%)?","[Cc]nl","[Ff]act %(?lea?de?%)?","[Ll]ead citation needed","[Nn]ot in body","[Nn]ot verified in body", -- Primary source etc. "[Pp]s[ci]","[Nn]psn","[Nn]on%-primary[ _]+source[ _]+needed","[Ss]elf%-published[%w_%s]-","[Uu]ser%-generated[%w_%s]-", "[Pp]rimary source[%w_%s]-","[Ss]econdary source[%w_%s]-","[Tt]ertiary source[%w_%s]-","[Tt]hird%-party[%w_%s]-", -- aliases for Disambiguation (page) and similar "[Bb]egriffsklärung","[Dd][Aa][Bb]","[Dd]big","[%w_%s]-%f[%w][Dd]isam[%w_%s]-","[Hh][Nn][Dd][Ii][Ss]", -- aliases for Failed verification "[Bb]adref","[Ff]aile?[ds] ?[rv][%w_%s]-","[Ff][Vv]","[Nn][Ii]?[Cc][Gg]","[Nn]ot ?in ?[crs][%w_%s]-","[Nn]ot specifically in source", "[Vv]erification[%- _]failed", -- aliases for When "[Aa]s[ _]+of[ _]+when%??","[Aa]s[ _%-]+of%?","[Cc]larify date","[Dd]ate[ _]*needed","[Nn]eeds?[ _]+date","[Rr]ecently","[Ss]ince[ _]+when%??", "[Ww]HEN","[Ww]hen%??", -- aliases for Update "[Nn]ot[ _]*up[ _]*to[ _]*date","[Oo]u?[Tt][Dd]","[Oo]ut[%- _]*o?f?[%- _]*dated?","[Uu]pdate","[Uu]pdate[ _]+sect","[Uu]pdate[ _]+Watch", -- aliases for Pronunciation needed "[Pp]ronunciation%??[%- _]*n?e?e?d?e?d?","[Pp]ronounce","[Rr]equested[%- _]*pronunciation","[Rr]e?q?pron","[Nn]eeds[%- _]*pronunciation", -- Chart, including Chart/start etc. "[Cc]hart","[Cc]hart/[%w_%s]-", -- Cref and others "[Cc]ref2?","[Cc]note", -- Explain and others "[Ee]xplain","[Ff]urther[ ]*explanation[ ]*needed","[Ee]laboration[ ]*needed","[Ee]xplanation[ ]*needed", -- TOC templates "[Cc][Oo][Mm][Pp][Aa][Cc][Tt][ _]*[Tt][Oo][Cc][8]*[5]*","[Tt][Oo][Cc]","09[Aa][Zz]","[Tt][Oo][Cc][ ]*[Cc][Oo][Mm][Pp][Aa][Cc][Tt]","[Tt][Oo][Cc][ ]*[Ss][Mm][Aa][Ll][Ll]","[Cc][Oo][Mm][Pp][Aa][Cc][Tt][ _]*[Aa][Ll][Pp][Hh][Aa][Bb][Ee][Tt][Ii][Cc][ _]*[Tt][Oo][Cc]", "DEFAULTSORT:.-", "[Oo]ne[ _]+source", "[Cc]ontains[ _]+special[ _]+characters", "[Ii]nfobox[ _]+[Cc]hinese" } -- Regular expressions to match all block templates that are desirable in excerpts localwantedBlockTemplates={ "[Bb]asketball[ _]roster[ _]header", "[Cc]abinet[ _]table[^|}]*", "[Cc]hart[^|}]*", "[Cc]lear", "[Cc]ol[%- es][^|}]*",-- all abbreviated column templates without excessively matching ({{col-2}}, {{colend}}, etc.) "[Cc]olumn[^|}]*",-- all other column templates "COVID-19[ _]pandemic[ _]data[^|}]*", "[Cc]ycling[ _]squad[^|}]*", "[Dd]ynamic[ _]list", "[Ee]lection[ _]box[^|}]*", "[Gg]allery", "[Gg]raph[^|}]*", "[Hh]idden", "[Hh]istorical[ _]populations", "[Ll]egend[ _]inline", "[Pp]lainlist", "[Pp]layer[^|}]*", "[Ss]eries[ _]overview", "[Ss]ide[ _]box", "[Ss]witcher", "[Tt]ree[ _]chart[^|}]*", "[Tt]elevision[ _]ratings[ _]graph" } -- Regular expressions to match non-free file templates localnonFreeFileTemplates={ "[Nn]on%-free", } localTranscluder=require("Module:Transcluder") localescapeString=require("Module:String")._escapePattern localyesno=require('Module:Yesno') localp={} -- Helper function to test for truthy and falsy values localfunctionis(value) ifnotvalueorvalue==""orvalue=="0"orvalue=="false"orvalue=="no"then returnfalse end returntrue end -- Error handling function -- Throws a Lua error or returns an empty string if error reporting is disabled localerrors=true-- show errors by default localfunctionluaError(message,value) ifnotis(errors)thenreturn''end-- error reporting is disabled message=errorMessages[message]ormessageor'' message=mw.ustring.format(message,value) error(message,2) end -- Error handling function -- Returns a wiki friendly error or an empty string if error reporting is disabled localfunctionwikiError(message,value) ifnotis(errors)thenreturn''end-- error reporting is disabled message=errorMessages[message]ormessageor'' message=mw.ustring.format(message,value) message=errorMessages.prefix..message ifmw.title.getCurrentTitle().isContentPagethen localerrorCategory=mw.title.new(errorCategory,'Category') iferrorCategorythenmessage=message..'[['..errorCategory.prefixedText..']]'end end message=mw.html.create('div'):addClass('error'):wikitext(message) returnmessage end -- Helper function to match from a list regular expressions -- Like so: match pre..list[1]..post or pre..list[2]..post or ... localfunctionmatchAny(text,pre,list,post,init) localmatch={} fori=1,#listdo match={mw.ustring.match(text,pre..list[i]..post,init)} ifmatch[1]thenreturnunpack(match)end end returnnil end -- Helper function to convert imagemaps into standard images localfunctionconvertImageMap(imagemap) localimage=matchAny(imagemap,"[>\n]%s*",fileNamespaces,"[^\n]*") ifimagethen return"<!--imagemap-->[["..mw.ustring.gsub(image,"[>\n]%s*","",1).."]]" else return""-- remove entire block if image can't be extracted end end -- Helper function to convert a comma-separated list of numbers or min-max ranges into a list of booleans -- For example: "1,3-5" to {1=true,2=false,3=true,4=true,5=true} localfunctionnumberFlags(str) ifnotstrthenreturn{}end localflags={} localranges=mw.text.split(str,",")-- parse ranges: "1,3-5" to {"1","3-5"} for_,rinpairs(ranges)do localmin,max=mw.ustring.match(r,"^%s*(%d+)%s*[-–—]%s*(%d+)%s*$")-- "3-5" to min=3 max=5 ifnotmaxthenmin,max=mw.ustring.match(r,"^%s*((%d+))%s*$")end-- "1" to min=1 max=1 ifmaxthen forp=min,maxdoflags[p]=trueend end end returnflags end -- Helper function to convert template arguments into an array of arguments fit for get() localfunctionparseArgs(frame) localargs={} forkey,valueinpairs(frame:getParent().args)doargs[key]=valueend forkey,valueinpairs(frame.args)doargs[key]=valueend-- args from a Lua call have priority over parent args from template args.paraflags=numberFlags(args["paragraphs"]or"")-- parse paragraphs: "1,3-5" to {"1","3-5"} args.fileflags=numberFlags(args["files"]or"")-- parse file numbers returnargs end -- simulate {{Airreg}} without the footnote, given "N|485US|," or similar localfunctionairreg(p) locals=mw.text.split(p,"%s*|%s*") ifs[1]~="N"ands[1]~="HL"ands[1]~="JA"thens[1]=s[1].."-"end returntable.concat(s,"") end -- Helper function to remove unwanted templates and pseudo-templates such as #tag:ref and DEFAULTSORT localfunctionstripTemplate(t) -- If template is unwanted then return "" (gsub will replace by nothing), else return nil (gsub will keep existing string) ifmatchAny(t,"^{{%s*",unwantedInlineTemplates,"%s*%f[|}]")thenreturn""end -- If template is wanted but produces an unwanted reference then return the string with |Note=, |ref or |shortref removed localnoRef=mw.ustring.gsub(t,"|%s*Note%s*=.-%f[|}]","") noRef=mw.ustring.gsub(noRef,"|%s*ref%s*%f[|}]","") noRef=mw.ustring.gsub(noRef,"|%s*shortref%s*%f[|}]","") -- If a wanted template has unwanted nested templates, purge them too noRef=mw.ustring.sub(noRef,1,2)..mw.ustring.gsub(mw.ustring.sub(noRef,3),"%b{}",stripTemplate) -- Hide pipes in wikilinks for the next bit of processing noRef=mw.ustring.gsub(noRef,"%[(%b[])%]",function(s) return"["..mw.ustring.gsub(s,"|","27円_PIPE_27円").."]" end) -- Replace {{audio}} by its text parameter: {{Audio|Foo.ogg|Bar}} → Bar noRef=mw.ustring.gsub(noRef,"^{{%s*[Aa]udio.-|.-|(.-)%f[|}].*","%1") -- Replace {{Nihongo foot}} by its text parameter: {{Nihongo foot|English|英語|eigo}} → English noRef=mw.ustring.gsub(noRef,"^{{%s*[Nn]ihongo[ _]+foot%s*|(.-)%f[|}].*","%1") -- Replace {{Airreg}} by its text parameter: {{Airreg|N|485US|,}} → N485US, noRef=mw.ustring.gsub(noRef,"^{{%s*[Aa]irreg%s*|%s*(.-)}}",airreg) -- Bring back the hidden wikilink pipes noRef=mw.ustring.gsub(noRef,"27円_PIPE_27円","|") ifnoRef~=tthenreturnnoRefend returnnil-- not an unwanted template: keep end -- Get a page's content, following redirects -- Also returns the page name, or the target page name if a redirect was followed, or false if no page found -- For file pages, returns the content of the file description page localfunctiongetContent(page) localtitle=mw.title.new(page) ifnottitlethenreturnfalse,falseend localtarget=title.redirectTarget iftargetthentitle=targetend returntitle:getContent(),title.prefixedText end -- Get the tables only localfunctiongetTables(text,options) localtables={} forcandidateinmw.ustring.gmatch(text,"%b{}")do ifmw.ustring.sub(candidate,1,2)=='{|'then table.insert(tables,candidate) end end returntable.concat(tables,'\n') end -- Get the lists only localfunctiongetLists(text,options) locallists={} forlistinmw.ustring.gmatch(text,"\n[*#][^\n]+")do table.insert(lists,list) end returntable.concat(lists,'\n') end -- Check image for suitability localfunctioncheckImage(image) iftype(image)=="table"then --Infobox image. Pass in a quick string equivilant of the image, since we should still check it for things like non-free files returncheckImage("[[File:"..image.file.."]]") end localpage=matchAny(image,"",fileNamespaces,"%s*:[^|%]]*")-- match File:(name) or Image:(name) ifnotpagethenreturnfalseend -- Limit to image types: .gif, .jpg, .jpeg, .png, .svg, .tiff, .xcf (exclude .ogg, audio, etc.) ifnotmatchAny(page,"%.",fileTypes,"%s*$")thenreturnfalseend -- Check the local wiki localfileDescription,fileTitle=getContent(page)-- get file description and title after following any redirect ifnotfileTitleorfileTitle==""thenreturnfalseend-- the image doesn't exist -- Check Commons ifnotfileDescriptionorfileDescription==""then localframe=mw.getCurrentFrame() fileDescription=frame:preprocess("{{"..fileTitle.."}}") end -- Filter non-free images ifnotfileDescriptionorfileDescription==""ormatchAny(fileDescription,"",nonFreeFileTemplates,"")then returnfalse end returntrue end -- Attempt to parse [[File:...]] or [[Image:...]], either anywhere (start=false) or at the start only (start=true) localfunctionparseImage(text,start) localstartre="" ifstartthenstartre="^"end-- a true flag restricts search to start of string localimage=matchAny(text,startre.."%[%[%s*",fileNamespaces,"%s*:.*")-- [[File: or [[Image: ... ifimagethen image=mw.ustring.match(image,"%b[]%s*")-- matching [[...]] to handle wikilinks nested in caption end returnimage end -- Returns the file name and the arg data of the file if it exists localfunctionextractFileData(str,notmultiline) localreg="^%[?%[?%a-:([^{|]+)(.-)%]?%]?$" localname,args,_=mw.ustring.match(str,reg) ifnamethen returnname,args else returnstr,""--Default fallback end end --Modifies an image's parameters, automatically fixing related parameters in the process localfunctionmodifyImage(image,fileArgs) iftype(image)=="table"then --Pass in a dummy string version and use that to handle modification localnewversion=modifyImage("[[File:"..image.file..string.gsub(image.args,"{{!}}","|").."]]",fileArgs) --Since we know the format is strictly controlled, we can do a lazy sub grab for the args image.args=string.sub(newversion,8+#image.file,-3) returnimage end iffileArgsthen for_,filearginpairs(mw.text.split(fileArgs,"|"))do-- handle fileArgs=left|border etc. localfa=mw.ustring.gsub(filearg,"=.*","")-- "upright=0.75" → "upright" localgroup={fa}-- group of "border" is ["border"]... for_,ginpairs(imageParams)do for_,ainpairs(g)do iffa==athengroup=gend-- ...but group of "left" is ["right", "left", "center", "centre", "none"] end end for_,ainpairs(group)do image=mw.ustring.gsub(image,"|%s*"..a.."%f[%A]%s*=[^|%]]*","")-- remove "|upright=0.75" etc. image=mw.ustring.gsub(image,"|%s*"..a.."%s*([|%]])","%1")-- replace "|left|" by "|" etc. end image=mw.ustring.gsub(image,"([|%]])","|"..filearg.."%1",1)-- replace "|" by "|left|" etc. end end image=mw.ustring.gsub(image,"(|%s*%d*x?%d+%s*px%s*.-)|%s*%d*x?%d+%s*px%s*([|%]])","%1%2")-- double px args returnimage end -- Turns a template's file table into a [[File:...]] string localfunctionformatTemplateImage(image,allowFancy) --Certain positional elements may need to apply to the containing infobox, and not the file itself, so we should check that here ifis(image.caption)andallowFancythen--Will be displayed like an infobox localalignment= (string.find(image.args,"|left")and"left") or(string.find(image.args,"|center")orstring.find(image.args,"|centre"))and"center" or"right" modifyImage(image,"none")--Remove all positioning elements from the image modifyImage(image,"frameless") localargs=image.args args=string.gsub(args,"|thumb","")--Don't allow using |thumb in this mode returnmw.text.unstrip(mw.getCurrentFrame():expandTemplate({ title="Image frame", args={ content="[[File:"..image.file..args.."]]",caption='<div class="center">'..image.caption.."</div>", align=alignment,["max-width"]=300,mode="scrollable" } })).."\n" else localcaptionText=(is(image.caption)and"|"..image.caption)or"" return"[[File:"..image.file..captionText..image.args.."]]\n" end end -- Attempts to construct a [[File:...]] block from {{infobox ... |image= ...}} or other templates localfunctiongetTemplateImages(text) localhasNamedArgs=mw.ustring.find(text,"|")andmw.ustring.find(text,"=") ifnothasNamedArgsthenreturnnilend-- filter out any template that obviously doesn't contain an image -- ensure image map is captured, while removing anything beyond it text=mw.ustring.gsub(text,'<!%-%-imagemap%-%->(%[%b[]%])[^|]+','|imagemap=%1') -- filter through parameters for image related ones localimages={} localparameters,_,parameterOrder=Transcluder.getParameters(text) --Search all template parameters for file-like objects localpositionalImages={} localposition=1 for_,keyinipairs(parameterOrder)do position=position+1--Cant rely on ipairs due to potentially weird manipulation later localvalue=parameters[key] ifis(value)then--Ensure its not empty ifstring.sub(value,1,2)=="{{"andstring.sub(value,-2,-1)=="}}"then--Template in a template --Extract files from the template and insert files if any appear localinternalImages=getTemplateImages(value)or{} localinitialPosition=position forindex,imageinipairs(internalImages)do positionalImages[initialPosition+index]=image--Still positional, technically position=position+1--Advance our own counter to avoid overlap end else ifmatchAny(key,"",captionParams,"%s*")then --Caption-like parameter name, try to associate it with an image localscanPosition=position whilescanPosition>0do scanPosition=scanPosition-1 localimage=positionalImages[scanPosition] ifimageandimage.caption==""then image.caption=mw.getCurrentFrame():preprocess(value)--Assign caption to most recently defined image break end end elseifmatchAny(value,"%.",fileTypes,"%s*$")then --File-like value, assume its an image localfilename,fileargs=extractFileData(value) positionalImages[position]={file=filename,caption="",args=fileargs} elseifmw.ustring.match(key,"[Ii][Mm][Aa][Gg][Ee]")ormw.ustring.match(key,"[Pp][Hh][Oo][Tt][Oo]")ormw.ustring.match(key,"[Ss][Yy][Mm][Bb][Oo][Ll]")then --File-like parameter name, assume its an image after some scrutinization localkeyLower=string.lower(key) ifstring.find(keyLower,"caption") orstring.find(keyLower,"size")orstring.find(keyLower,"width") orstring.find(keyLower,"upright") orstring.find(keyLower,"alt")then--Argument is defining image settings, not an image --Do nothing for now --TODO: we really should extract some of this for later use else localfilename,fileargs=extractFileData(value) positionalImages[position]={file=filename,caption="",args=fileargs} end end end--End of "Is template in template" check end--End of "is(value)" check end --Append entries from positionalImages into the main images table fori=1,positiondo localvalue=positionalImages[i] ifvaluethen table.insert(images,value) end end returnimages end -- a basic parser to trim down extracted wikitext -- @param text : Wikitext to be processed -- @param options : A table of options... -- options.paraflags : Which number paragraphs to keep, as either a string (e.g. '1,3-5') or a table (e.g. {1=true,2=false,3=true,4=true,5=true}. If not present, all paragraphs will be kept. -- options.fileflags : table of which files to keep, as either a string (e.g. '1,3-5') or a table (e.g. {1=true,2=false,3=true,4=true,5=true} -- options.fileargs : args for the [[File:]] syntax, such as 'left' -- options.filesOnly : only return the files and not the prose localfunctionparse(text,options) localallParagraphs=true-- keep all paragraphs? ifoptions.paraflagsthen iftype(options.paraflags)~="table"thenoptions.paraflags=numberFlags(options.paraflags)end for_,vinpairs(options.paraflags)do ifvthenallParagraphs=falseend-- if any para specifically requested, don't keep all end end ifis(options.filesOnly)then allParagraphs=false options.paraflags={} end localmaxfile=0-- for efficiency, stop checking images after this many have been found ifoptions.fileflagsthen iftype(options.fileflags)~="table"thenoptions.fileflags=numberFlags(options.fileflags)end fork,vinpairs(options.fileflags)do ifvandk>maxfilethenmaxfile=kend-- set maxfile = highest key in fileflags end end localfileArgs=options.fileargsandmw.text.trim(options.fileargs) iffileArgs==''thenfileArgs=nilend localdoFancyFiles=yesno(options.doFancyFiles) ifdoFancyFiles==nilthendoFancyFiles=trueend localleadStart=nil-- have we found some text yet? localt=""-- the stripped down output text localfileText=""-- output text with concatenated [[File:Foo|...]]\n entries localfiles=0-- how many images so far localparas=0-- how many paragraphs so far localstartLine=true-- at the start of a line (no non-spaces found since last \n)? text=mw.ustring.gsub(text,"^%s*","")-- remove initial white space -- Add named files localf=options.files iffandmw.ustring.match(f,"[^%d%s%-,]")then-- filename rather than number list f=mw.ustring.gsub(f,"^%s*File%s*:%s*","",1) f=mw.ustring.gsub(f,"^%s*Image%s*:%s*","",1) f="[[File:"..f.."]]" f=modifyImage(f,"thumb") f=modifyImage(f,fileArgs) ifcheckImage(f)thenfileText=fileText..f.."\n"end end repeat-- loop around parsing a template, image or paragraph localtoken=mw.ustring.match(text,"^%b{}%s*")orfalse-- {{Template}} or {| Table |} ifnotleadStartandnottokenthentoken=mw.ustring.match(text,"^%b<>%s*%b{}%s*")end-- allow <tag>{{template}} before lead has started localline=mw.ustring.match(text,"[^\n]*") iftokenandlineandmw.ustring.len(token)<mw.ustring.len(line)then-- template is followed by text (but it may just be other templates) line=mw.ustring.gsub(line,"%b{}","")-- remove all templates from this line line=mw.ustring.gsub(line,"%b<>","")-- remove all HTML tags from this line -- if anything is left, other than an incomplete further template or an image, keep the template: it counts as part of the line ifmw.ustring.find(line,"%S")andnotmatchAny(line,"^%s*",{"{{","%[%[%s*[Ff]ile:","%[%[%s*[Ii]mage:"},"")then token=nil end end iftokenthen-- found a template which is not the prefix to a line of text ifis(options.keepTables)andmw.ustring.sub(token,1,2)=='{|'then t=t..token-- keep tables elseifmw.ustring.sub(token,1,3)=='{{#'then t=t..token-- keep parser functions elseifleadStartthen-- lead has already started, so keep the template within the text, unless it's a whole line (navbox etc.) ifnotis(options.filesOnly)andnotstartLinethent=t..tokenend elseifmatchAny(token,"^{{%s*",wantedBlockTemplates,"%s*%f[|}]")then t=t..token-- keep wanted block templates elseiffiles<maxfilethen-- Check it for images if we need those, and then discard it localimages=getTemplateImages(token)or{} for_,imageinipairs(images)do iffiles<maxfileandcheckImage(image)then-- if image is found and qualifies (not a sound file, not non-free, etc.) files=files+1-- count the file, whether displaying it or not ifoptions.fileflagsandoptions.fileflags[files]then-- if displaying this image image=modifyImage(image,"thumb") image=modifyImage(image,fileArgs) fileText=fileText..formatTemplateImage(image,doFancyFiles) end end end end else-- the next token in text is not a template token=parseImage(text,true) iftokenthen-- the next token in text looks like an image iffiles<maxfileandcheckImage(token)then-- if more images are wanted and this is a wanted image files=files+1 ifoptions.fileflagsandoptions.fileflags[files]then localimage=token-- copy token for manipulation by adding |right etc. without changing the original image=modifyImage(image,fileArgs) fileText=fileText..image end end else-- got a paragraph, which ends at a file, image, blank line or end of text localafterEnd=mw.ustring.len(text)+1 localblankPosition=mw.ustring.find(text,"\n%s*\n")orafterEnd-- position of next paragraph delimiter (or end of text) localendPosition=math.min(-- find position of whichever comes first: [[File:, [[Image: or paragraph delimiter mw.ustring.find(text,"%[%[%s*[Ff]ile%s*:")orafterEnd, mw.ustring.find(text,"%[%[%s*[Ii]mage%s*:")orafterEnd, blankPosition) token=mw.ustring.sub(text,1,endPosition-1) ifblankPosition<afterEndandblankPosition==endPositionthen-- paragraph ends with a blank line token=token..mw.ustring.match(text,"\n%s*\n",blankPosition) end localisHatnote=not(leadStart)andmw.ustring.sub(token,1,1)==':' ifnotisHatnotethen leadStart=leadStartormw.ustring.len(t)+1-- we got a paragraph, so mark the start of the lead section paras=paras+1 ifallParagraphsor(options.paraflagsandoptions.paraflags[paras])thent=t..tokenend-- add if this paragraph wanted end end-- of "else got a paragraph" end-- of "else not a template" iftokenthentext=mw.ustring.sub(text,mw.ustring.len(token)+1)end-- remove parsed token from remaining text startLine=mw.ustring.find(token,"\n%s*$")-- will the next token be the first non-space on a line? untilnottextortext==""ornottokenortoken==""-- 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 returnfileText..text end localfunctioncleanupText(text,options) text=mw.ustring.gsub(text,"<!%-%-.-%-%->","")-- remove HTML comments text=mw.ustring.gsub(text,"<[Nn][Oo][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>.-</[Nn][Oo][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>","")-- remove noinclude bits ifnotis(options.ignoreOnlyincludes)andmw.ustring.find(text,"[Oo][Nn][Ll][Yy][Ii][Nn][Cc][Ll][Uu][Dd][Ee]")then-- avoid expensive search if possible text=mw.ustring.gsub(text,"</[Oo][Nn][Ll][Yy][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>.-<[Oo][Nn][Ll][Yy][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>","")-- remove text between onlyinclude sections text=mw.ustring.gsub(text,"^.-<[Oo][Nn][Ll][Yy][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>","")-- remove text before first onlyinclude section text=mw.ustring.gsub(text,"</[Oo][Nn][Ll][Yy][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>.*","")-- remove text after last onlyinclude section end ifnotis(options.keepSubsections)then text=mw.ustring.gsub(text,"\n==.*","")-- remove first ==Heading== and everything after it text=mw.ustring.gsub(text,"^==.*","")-- ...even if the lead is empty end ifnotis(options.keepRefs)then text=mw.ustring.gsub(text,"<%s*[Rr][Ee][Ff][^>]-/%s*>","")-- remove refs cited elsewhere text=mw.ustring.gsub(text,"<%s*[Rr][Ee][Ff].->.-<%s*/%s*[Rr][Ee][Ff]%s*>","")-- remove refs text=mw.ustring.gsub(text,"{%b{}}",stripTemplate)-- remove unwanted templates such as references end text=mw.ustring.gsub(text,"<%s*[Ss][Cc][Oo][Rr][Ee].->.-<%s*/%s*[Ss][Cc][Oo][Rr][Ee]%s*>","")-- remove musical scores text=mw.ustring.gsub(text,"<%s*[Ii][Mm][Aa][Gg][Ee][Mm][Aa][Pp].->.-<%s*/%s*[Ii][Mm][Aa][Gg][Ee][Mm][Aa][Pp]%s*>",convertImageMap)-- convert imagemaps into standard images text=mw.ustring.gsub(text,"%s*{{%s*[Tt][Oo][Cc].-}}","")-- remove most common tables of contents text=mw.ustring.gsub(text,"%s*__[A-Z]*TOC__","")-- remove TOC behavior switches text=mw.ustring.gsub(text,"\n%s*{{%s*[Pp]p%-.-}}","\n")-- remove protection templates text=mw.ustring.gsub(text,"%s*{{[^{|}]*[Ss]idebar%s*}}","")-- remove most sidebars text=mw.ustring.gsub(text,"%s*{{[^{|}]*%-[Ss]tub%s*}}","")-- remove most stub templates text=mw.ustring.gsub(text,"%s*%[%[%s*:?[Cc]ategory:.-%]%]","")-- remove categories text=mw.ustring.gsub(text,"^:[^\n]+\n","")-- remove DIY hatnote indented with a colon returntext end -- Parse a ==Section== from a page localfunctiongetSection(text,section,mainOnly) localescapedSection=mw.ustring.gsub(mw.uri.decode(section),"([%^%$%(%)%%%.%[%]%*%+%-%?])","%%%1")-- %26 → & etc, then ^ → %^ etc. locallevel,content=mw.ustring.match(text.."\n","\n(==+)%s*"..escapedSection.."%s*==.-\n(.*)") ifnotcontentthenreturnluaError("sectionNotFound",section)end localnextSection ifmainOnlythen nextSection="\n==.*"-- Main part of section terminates at any level of header else nextSection="\n=="..mw.ustring.rep("=?",#level-2).."[^=].*"-- "===" → "\n===?[^=].*", matching "==" or "===" but not "====" end content=mw.ustring.gsub(content,nextSection,"")-- remove later sections with headings at this level or higher ifmw.ustring.match(content,"^%s*$")thenreturnluaError("sectionEmpty",section)end returncontent end -- Parse a <section begin="Name of the fragment"> -- @todo Implement custom parsing of fragments rather than relying on #lst localfunctiongetFragment(page,fragment) localframe=mw.getCurrentFrame() localtext=frame:callParserFunction('#lst',page,fragment) ifmw.ustring.match(text,"^%s*$")thenreturnluaError("fragmentEmpty",fragment)end returntext end -- Remove unmatched <tag> or </tag> tags localfunctionfixTags(text,tag) localstartCount=0 foriinmw.ustring.gmatch(text,"<%s*"..tag.."%f[^%w_].->")dostartCount=startCount+1end localendCount=0 foriinmw.ustring.gmatch(text,"</"..tag.."%s*>")doendCount=endCount+1end ifstartCount>endCountthen-- more <tag> than </tag>: remove the last few <tag>s locali=0 text=mw.ustring.gsub(text,"<%s*"..tag.."%f[^%w_].->",function(t) i=i+1 ifi>endCountthenreturn""elsereturnnilend end)-- "end" here terminates the anonymous replacement function(t) passed to gsub elseifendCount>startCountthen-- more </tag> than <tag>: remove the first few </tag>s text=mw.ustring.gsub(text,"</"..tag.."%s*>","",endCount-startCount) end returntext end localfunctionfixTemplates(text) repeat-- hide matched {{template}}s including nested templates localt=text text=mw.ustring.gsub(text,"{(%b{})}","27円{27円%127円}27円")-- {{sometemplate}} → E{E{sometemplate}E}E where E represents escape text=mw.ustring.gsub(text,"(< *math[^>]*>[^<]-)}}(.-< */math *>)","%1}27円}27円%2")-- <math>\{sqrt\{hat{x}}</math> → <math>\{sqrt\{hat{x}E}E</math> untiltext==t text=text.gsub(text,"([{}])%1[^27円].*","")-- remove unmatched {{, }} and everything thereafter, avoiding }E}E etc. text=text.gsub(text,"([{}])%1$","")-- remove unmatched {{, }} at end of text text=mw.ustring.gsub(text,"27円","")-- unhide matched pairs: E{E{ → {{, etc. returntext end localfunctionfixTables(text) repeat-- hide matched {|tables|}s localt=text forpotentialTableinstring.gmatch(text,"\n%b{}")do ifstring.sub(potentialTable,1,3)=="\n{|"then localinnerContent=mw.ustring.sub(potentialTable,3,-2) text=mw.ustring.gsub(text,escapeString(potentialTable),"\n27円{27円"..mw.ustring.gsub(innerContent,"%%","%%%%").."27円}27円") -- {|sometable|} → E{E|sometable|E}E where E represents escape end end untiltext==t text=mw.ustring.gsub(text,"\n{|.*","")-- remove unmatched {| and everything after it text=mw.ustring.gsub(text,"27円","")-- unhide matched pairs: E{E| → {|, etc. returntext end localfunctionfixLinks(text) repeat-- hide matched [[wikilink]]s including nested links like [[File:Example.jpg|Some [[nested]] link.]] localt=text text=mw.ustring.gsub(text,"%[(%b[])%]","27円[27円%127円]27円") untiltext==t text=text.gsub(text,"([%[%]])%1[^27円].*","")-- remove unmatched [[ or ]] and everything thereafter, avoiding ]E]E etc. text=text.gsub(text,"([%[%]])%1$","")-- remove unmatched [[ or ]] at end of text text=mw.ustring.gsub(text,"27円","")-- unhide matched pairs: ]E]E → ]], etc. returntext end -- Replace the first call to each reference defined outside of the text for the full reference, to prevent undefined references -- Then prefix the page title to the reference names to prevent conflicts -- that is, replace <ref name="Foo"> for <ref name="Title of the article Foo"> -- and also <ref name="Foo" /> for <ref name="Title of the article Foo" /> -- also remove reference groups: <ref name="Foo" group="Bar"> for <ref name="Title of the article Foo"> -- and <ref group="Bar"> for <ref> -- @todo The current regex may fail in cases with both kinds of quotes, like <ref name="Darwin's book"> localfunctionfixRefs(text,page,full) ifnotfullthenfull=getContent(page)end localrefNames={} localrefName localrefBody localposition=1 whileposition<mw.ustring.len(text)do refName,position=mw.ustring.match(text,"<%s*[Rr][Ee][Ff][^>]*name%s*=%s*[\"']?([^\"'>]+)[\"']?[^>]*/%s*>()",position) ifrefNamethen refName=mw.text.trim(refName) ifnotrefNames[refName]then-- make sure we process each ref name only once table.insert(refNames,refName) refName=mw.ustring.gsub(refName,"[%^%$%(%)%.%[%]%*%+%-%?%%]","%%%0")-- escape special characters refBody=mw.ustring.match(text,"<%s*[Rr][Ee][Ff][^>]*name%s*=%s*[\"']?%s*"..refName.."%s*[\"']?[^>/]*>.-<%s*/%s*[Rr][Ee][Ff]%s*>") ifnotrefBodythen-- the ref body is not in the excerpt refBody=mw.ustring.match(full,"<%s*[Rr][Ee][Ff][^>]*name%s*=%s*[\"']?%s*"..refName.."%s*[\"']?[^/>]*>.-<%s*/%s*[Rr][Ee][Ff]%s*>") ifrefBodythen-- the ref body was found elsewhere text=mw.ustring.gsub(text,"<%s*[Rr][Ee][Ff][^>]*name%s*=%s*[\"']?%s*"..refName.."%s*[\"']?[^>]*/?%s*>",refBody,1) end end end else position=mw.ustring.len(text) end end text=mw.ustring.gsub(text,"<%s*[Rr][Ee][Ff][^>]*name%s*=%s*[\"']?([^\"'>/]+)[\"']?[^>/]*(/?)%s*>",'<ref name="'..page..' %1" %2>') text=mw.ustring.gsub(text,"<%s*[Rr][Ee][Ff][^>]*group%s*=%s*[\"']?[^\"'>/]+[\"']%s*>",'<ref>') returntext end -- Replace the bold title or synonym near the start of the article by a wikilink to the article functionlinkBold(text,page) locallang=mw.language.getContentLanguage() localposition=mw.ustring.find(text,"'''"..lang:ucfirst(page).."'''",1,true)-- look for "'''Foo''' is..." (uc) or "A '''foo''' is..." (lc) ormw.ustring.find(text,"'''"..lang:lcfirst(page).."'''",1,true)-- plain search: special characters in page represent themselves ifpositionthen locallength=mw.ustring.len(page) text=mw.ustring.sub(text,1,position+2).."[["..mw.ustring.sub(text,position+3,position+length+2).."]]"..mw.ustring.sub(text,position+length+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) ifnotmw.ustring.find(b,"%[")then-- if not wikilinked return"'''[["..page.."|"..b.."]]'''"-- replace '''Foo''' by '''[[page|Foo]]''' else returnnil-- instruct gsub to make no change end end,1)-- "end" here terminates the anonymous replacement function(a, b) passed to gsub end returntext end -- Main function for modules localfunctionget(page,options) ifoptions.errorsthenerrors=options.errorsend ifnotpageorpage==""thenreturnluaError("noPage")end localtext page,section=mw.ustring.match(page,"([^#]+)#?([^#]*)") text,page=getContent(page) ifnotpagethenreturnluaError("noPage")end ifnottextthenreturnluaError("pageNotFound",page)end localfull=text-- save the full text for later ifis(options.fragment)then text=getFragment(page,options.fragment) end ifis(section)then text=getSection(text,section) end -- Strip text of all undersirables text=cleanupText(text,options) text=parse(text,options) -- Replace the bold title or synonym near the start of the article by a wikilink to the article text=linkBold(text,page) -- Remove '''bold text''' if requested ifis(options.nobold)thentext=mw.ustring.gsub(text,"'''","")end -- Keep only tables if requested ifis(options.tablesOnly)thentext=getTables(text)end -- Keep only lists if requested ifis(options.listsOnly)thentext=getLists(text)end -- Seek and destroy unterminated templates, tables, links and tags text=fixTemplates(text) text=fixTables(text) text=fixLinks(text) text=fixTags(text,"div") -- Fix broken references ifis(options.keepRefs)thentext=fixRefs(text,page,full)end -- Trim trailing newlines to avoid appending text weirdly text=mw.text.trim(text) -- Add (Full article...) link ifoptions.moreLinkTextthen text=text.." ('''[["..page.."|"..options.moreLinkText.."]]''')" end returntext end -- Main invocation function for templates localfunctionmain(frame) localargs=parseArgs(frame) localpage=args[1] localok,text=pcall(get,page,args) ifnotokthen text=errorMessages.prefix..text iferrorCategoryanderrorCategory~=''andmw.title.getCurrentTitle().isContentPagethen text=text..'[['..errorCategory..']]' end returnmw.html.create('div'):addClass('error'):wikitext(text) end returnframe:preprocess(text) end localfunctiongetMoreLinkText(more) localdefaultText="Full article..."-- default text, same as in [[Template:TFAFULL]] ifnotmoreormore==''then-- nil/empty => use default returndefaultText end ifnotyesno(more,true)then-- falsy values => suppress the link returnnil end returnmore end -- Shared invocation function used by templates meant for portals localfunctionportal(frame,template) localargs=parseArgs(frame) errors=args['errors']orfalse-- disable error reporting unless requested -- There should be at least one argument except with selected=Foo and Foo=Somepage if#args<1andnot(template=="selected"andargs[template]andargs[args[template]])then returnwikiError("noPage") end -- Figure out the page to excerpt localpage localcandidates={} iftemplate=="lead"then page=args[1] page=mw.text.trim(page) ifnotpageorpage==""thenreturnwikiError("noPage")end candidates={page} elseiftemplate=="selected"then localkey=args[template] localcount=#args iftonumber(key)then-- normalise article number into the range 1..#args key=key%count ifkey==0thenkey=countend end page=args[key] page=mw.text.trim(page) ifnotpageorpage==""thenreturnwikiError("noPage")end candidates={page} elseiftemplate=="linked"ortemplate=="listitem"then localsource=args[1] localtext,source=getContent(source) ifnotsourcethen returnwikiError("noPage") elseifnottextthen returnwikiError("noPage") end localsection=args.section ifsectionthen-- check relevant section only text=getSection(text,section) ifnottextthenreturnwikiError("sectionNotFound",section)end end -- Replace annotated links with real links text=mw.ustring.gsub(text,"{{%s*[Aa]nnotated[ _]link%s*|%s*(.-)%s*}}","[[%1]]") iftemplate=="linked"then forcandidateinmw.ustring.gmatch(text,"%[%[%s*([^%]|\n]*)")dotable.insert(candidates,candidate)end else-- listitem: first wikilink on a line beginning *, :#, etc. except in "See also" or later section text=mw.ustring.gsub(text,"\n== *See also.*","") forcandidateinmw.ustring.gmatch(text,"\n:*[%*#][^\n]-%[%[%s*([^%]|\n]*)")dotable.insert(candidates,candidate)end end elseiftemplate=="random"then forkey,valueinpairs(args)do ifvalueandtype(key)=="number"then table.insert(candidates,mw.text.trim(value)) end end end -- Build an options array for the Excerpt module out of the arguments and the desired defaults localoptions={ errors=args['errors']orfalse, fileargs=args['fileargs'], fileflags=numberFlags(args['files']), paraflags=numberFlags(args['paragraphs']), moreLinkText=getMoreLinkText(args['more']), keepSubsections=args['keepSubsections'], keepRefs=args['keepRefs'], nobold=args['nobold'], doFancyFiles=args['fancyfiles'] } -- Select a random candidate and make sure its valid localtext localcandidateCount=#candidates ifcandidateCount>0then localcandidateKey=1 localcandidateString localcandidateArgs ifcandidateCount>1thenmath.randomseed(os.time())end while(nottextortext=="")andcandidateCount>0do ifcandidateCount>1thencandidateKey=math.random(candidateCount)end-- pick a random candidate candidateString=candidates[candidateKey] ifcandidateStringandcandidateString~=""then -- We have page or [[page]] or [[page|text]], possibly followed by |opt1|opt2... page,candidateArgs=mw.ustring.match(candidateString,"^%s*(%[%b[]%])%s*|?(.*)") ifpageandpage~=""then page=mw.ustring.match(page,"%[%[([^|%]]*)")-- turn [[page|text]] into page, discarding text else-- we have page or page|opt... page,candidateArgs=mw.ustring.match(candidateString,"%s*([^|]*[^|%s])%s*|?(.*)") end -- candidate arguments (even if value is "") have priority over global arguments ifcandidateArgsandcandidateArgs~=""then for_,tinpairs(mw.text.split(candidateArgs,"|"))do localk,v=mw.ustring.match(t,"%s*([^=]-)%s*=(.-)%s*$") ifk=='files'thenoptions.fileflags=numberFlags(v) elseifk=='paragraphs'thenoptions.paraflags=numberFlags(v) elseifk=='more'thenargs.more=v elseoptions[k]=vend end end ifpageandpage~=""then localsection=mw.ustring.match(page,"[^#]+#([^#]+)")-- save the section text,page=getContent(page)-- make sure the page exists ifpageandpage~=""andtextandtext~=""then ifargs.nostubsthen localisStub=mw.ustring.find(text,"%s*{{[^{|}]*%-[Ss]tub%s*}}") ifisStubthentext=nilend end ifsectionandsection~=""then page=page..'#'..section-- restore the section end text=get(page,options) end end end table.remove(candidates,candidateKey)-- candidate processed candidateCount=candidateCount-1-- ensure that we exit the loop after all candidates are done end end ifnottextortext==""thenreturnwikiError("No valid pages found")end -- Store all candidates before processing localallCandidates={page}-- Include current page for_,candidateinpairs(candidates)do table.insert(allCandidates,candidate) end ifargs.showallthen localseparator=args.showall ifseparator==""thenseparator="{{clear}}{{hr}}"end -- Add list at the top for showall if list parameter is not present ifnotargs.listthen locallistText="{{collapse top|title={{resize|85%|List of articles in rotation}}|bg=var(--background-color-base, #fff)|fc=inherit}}{{hlist" for_,candidateinpairs(allCandidates)do ifmw.ustring.match(candidate,"%S")then listText=listText.."|[["..mw.text.trim(candidate).."]]" end end listText=listText.."}}\n{{collapse bottom}}\n" text=listText..text end for_,candidateinpairs(candidates)do localt=get(candidate,options) ift~=""then text=text..separator..t end end end -- Add a collapsed list of pages which might appear ifargs.listthen locallist=args.list iflist==""thenlist="Other articles"end text=text.."{{collapse top|title={{resize|85%|"..list.."}}|bg=var(--background-color-base, #fff)|fc=inherit}}{{hlist" for_,candidateinpairs(candidates)do ifmw.ustring.match(candidate,"%S")thentext=text.."|[["..mw.text.trim(candidate).."]]"end end text=text.."}}\n{{collapse bottom}}" end returnframe:preprocess(text) end -- Old invocation function used by {{Excerpt}} localfunctionexcerpt(frame) localargs=parseArgs(frame) -- Make sure the requested page exists localpage=args[1]orargs.articleorargs.sourceorargs.page ifnotpagethenreturnwikiError("noPage")end localtitle=mw.title.new(page) ifnottitlethenreturnwikiError("noPage")end iftitle.isRedirectthentitle=title.redirectTargetend ifnottitle.existsthenreturnwikiError("pageNotFound",page)end page=title.prefixedText -- Define some useful variables localsection=args[2]orargs.sectionormw.ustring.match(args[1],"[^#]+#([^#]+)") localtag=args.tagor'div' -- Define the HTML elements localblock=mw.html.create(tag):addClass('excerpt-block') ifis(args.indicator)thenblock:addClass('excerpt-indicator')end localstyle=frame:extensionTag{name='templatestyles',args={src='Excerpt/styles.css'}} localhatnote ifnotargs.nohatthen ifargs.thisthen hatnote=args.this elseifargs.indicatorthen hatnote='This is' elseifargs.only=='file'then hatnote='This file is' elseifargs.only=='file'then hatnote='These files are' elseifargs.only=='list'then hatnote='This list is' elseifargs.only=='lists'then hatnote='These lists are' elseifargs.only=='table'then hatnote='This table is' elseifargs.only=='tables'then hatnote='These tables are' else hatnote='This section is' end hatnote=hatnote..' an excerpt from ' ifsectionthen hatnote=hatnote..'[['..page..'#'..section..'|'..page..' § '..section..']]' else hatnote=hatnote..'[['..page..']]' end hatnote=hatnote.."''"..'<span class="mw-editsection-like plainlinks"><span class="mw-editsection-bracket">[</span>[' hatnote=hatnote..title:fullUrl('action=edit')..' edit' hatnote=hatnote..']<span class="mw-editsection-bracket">]</span></span>'.."''" hatnote=require('Module:Hatnote')._hatnote(hatnote,{selfref=true})orwikiError('Error generating hatnote') end -- Build the module options out of the template arguments and the desired defaults localoptions={ fileflags=numberFlags(args['files']or1), paraflags=numberFlags(args['paragraphs']), filesOnly=is(args['only']=='file'orargs['only']=='files'), listsOnly=is(args['only']=='list'orargs['only']=='lists'), tablesOnly=is(args['only']=='table'orargs['only']=='tables'), keepTables=is(args['tables']ortrue), keepRefs=is(args['references']ortrue), keepSubsections=is(args['subsections']), nobold=notis(args['bold']), fragment=args['fragment'] } -- Get the excerpt itself ifsectionthenpage=page..'#'..sectionend localok,excerpt=pcall(e.get,page,options) ifnotokthenreturnwikiError(excerpt)end excerpt="\n"..excerpt-- line break is necessary to prevent broken tables and lists ifmw.title.getCurrentTitle().isContentPagethenexcerpt=excerpt..'[[Category:Articles with excerpts]]'end excerpt=frame:preprocess(excerpt) excerpt=mw.html.create(tag):addClass('excerpt'):wikitext(excerpt) -- Combine and return the elements returnblock:node(style):node(hatnote):node(excerpt) end -- Entry points for templates functionp.main(frame)returnmain(frame)end functionp.lead(frame)returnportal(frame,"lead")end-- {{Transclude lead excerpt}} reads a randomly selected article linked from the given page functionp.linked(frame)returnportal(frame,"linked")end-- {{Transclude linked excerpt}} reads a randomly selected article linked from the given page functionp.listitem(frame)returnportal(frame,"listitem")end-- {{Transclude list item excerpt}} reads a randomly selected article listed on the given page functionp.random(frame)returnportal(frame,"random")end-- {{Transclude random excerpt}} reads any article (default for invoke with one argument) functionp.selected(frame)returnportal(frame,"selected")end-- {{Transclude selected excerpt}} reads the article whose key is in the selected= parameter functionp.excerpt(frame)returnexcerpt(frame)end-- {{Excerpt}} transcludes part of an article into another article -- Entry points for other Lua modules functionp.get(page,options)returnget(page,options)end functionp.getContent(page)returngetContent(page)end functionp.getSection(text,section)returngetSection(text,section)end functionp.getTables(text,options)returngetTables(text,options)end functionp.getLists(text,options)returngetLists(text,options)end functionp.parse(text,options)returnparse(text,options)end functionp.parseImage(text,start)returnparseImage(text,start)end functionp.parseArgs(frame)returnparseArgs(frame)end functionp.getTemplateImages(text)returngetTemplateImages(text)end functionp.checkImage(image)returncheckImage(image)end functionp.cleanupText(text,options)returncleanupText(text,options)end functionp.numberFlags(str)returnnumberFlags(str)end functionp.getMoreLinkText(more)returngetMoreLinkText(more)end returnp