Wikipédi bewwiki https://bew.wikipedia.org/wiki/Bal%C3%A9-bal%C3%A9 MediaWiki 1.47.0-wmf.22 first-letter Wasilah Istimèwa Kongko Pemaké Kongko pemaké Wikipédi Kongko Wikipédi Gepokan Kongko gepokan MediaWiki Kongko MediaWiki Sablonan Kongko sablonan Pertulungan Kongko pertulungan Bangsaan Kongko bangsaan TimedText TimedText talk Modul Pembicaraan Modul Pegelaran Kongko Pegelaran The Simpsons 0 3164 35201 35196 2026-10-05T02:12:30Z Stephan1000000 900 811 35201 wikitext text/x-wiki {{Italic title}} {{Infobox television | image = The Simpsons yellow logo.svg | genre = {{Plainlist| * [[Animated sitcom]] * [[Satire]] }} | creator = [[Matt Groening]] | based_on = {{Based on|[[The Simpsons shorts|''The Simpsons'' shorts]]|Matt Groening}} | developer = {{Plainlist| * [[James L. Brooks]] * Matt Groening * [[Sam Simon]] }} | showrunner = {{Plainlist| * James L. Brooks (seasons 1-2) * Matt Groening (seasons 1-2) * Sam Simon (seasons 1-2) * [[Al Jean]] (seasons 3-4; 13-32) * [[Mike Reiss]] (seasons 3-4) * [[David Mirkin]] (seasons 5-6) * [[Bill Oakley]] (seasons 7-8) * [[Josh Weinstein]] (seasons 7-8) * [[Mike Scully]] (seasons 9-12) * [[Matt Selman]] (season 33-present) }} | voices = {{Plainlist| * [[Dan Castellaneta]] * [[Julie Kavner]] * [[Nancy Cartwright]] * [[Yeardley Smith]] * [[Harry Shearer]] * [[Hank Azaria]] * ([[List of The Simpsons cast members|Complete list]]) }} | theme_music_composer = [[Danny Elfman]] | open_theme = "[[The Simpsons Theme|''The Simpsons'' Theme]]" | end_theme = "''The Simpsons'' Theme" | composer = [[Richard Gibbs]] (1989–1990)<br />[[Alf Clausen]] (1990–2017)<br />[[Bleeding Fingers Music]] (2017–present) | country = United States | language = English | num_seasons = 38 | num_episodes = 811<!-- as of 04.10.2026 --><!-- self-transclusion of this number removed, because it broke [[Portal:The Simpsons]] --> | list_episodes = List of The Simpsons episodes | executive_producer = {{Collapsible list | expand = | title = List | liststyle = | hlist = | bullets = yes | <!-- 1 = --> James L. Brooks | <!-- 2 = --> Matt Groening | <!-- 3 = --> Al Jean (1992–1993; 1995–present) | <!-- 4 = --> Matt Selman (2005–present) | <!-- 5 = --> [[John Frink]] (2009–present) | <!-- 6 = --> Sam Simon (1989–1993) | <!-- 7 = --> Mike Reiss (1992–1993; 1995–1998) | <!-- 8 = --> David Mirkin (1993–1995) | <!-- 9 = --> Bill Oakley (1995–1997) | <!-- 10 = --> Josh Weinstein (1995–1997) | <!-- 11 = --> Mike Scully (1997–2001) | <!-- 12 = --> [[David X. Cohen]] (1998–1999) | <!-- 13 = --> [[George Meyer]] (1999–2001) | <!-- 14 = --> [[Carolyn Omine]] (2005–2006) | <!-- 15 = --> [[Tim Long]] (2005–2009) | <!-- 16 = --> [[Ian Maxtone-Graham]] (2005–2012) }} | producer = {{Plainlist| * [[Bonita Pietila]] * [[Richard Raynis]] * [[Richard Sakai]] * Denise Sirkot }} | editor = {{Plainlist| * Don Barrozo * Michael Bridge }} | runtime = 21–24 minutes | company = {{Plainlist| * [[Gracie Films]] * [[20th Television]](seasons 1–32) * [[20th Television Animation]] (season 33–present) }} | network = [[Fox Broadcasting Company|Fox]] | first_aired = {{Start date|1989|12|17}} | last_aired = present | related = <!-- Per infobox instructions, this field is for "remakes, spin-offs, adaptations for different audiences, etc.", not for crossovers. --> }} '''''The Simpsons''''' entu rèrodan [[pèlem cèmèn]] [[bodoran]] [[Amrik Serèkat]] nyang dibikin ama [[Matt Groening]]. Ni acara pertama-tama ditayangin jadi punggelan acara ''[[Tracey Ullman Show]]'' tahon 1987. == Catetan == 5her08fcir0r2xam0viu42gp2k0ek2a Modul:Excerpt 828 5088 35203 34541 2026-10-05T11:43:52Z Sophivorus 558 Update from [[d:Special:GoToLinkedPage/enwiki/Q52428273|master]] using [[mw:Synchronizer| #Synchronizer]] 35203 Scribunto text/plain -- 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 parser = require( 'Module:WikitextParser' ) local yesno = require( 'Module:Yesno' ) local ok, config = pcall( require, 'Module:Excerpt/config' ) if not ok then config = {} end local Excerpt = {} -- Main entry point for templates function Excerpt.main( frame ) -- Make sure the requested page exists and get the wikitext local page = Excerpt.getArg( 1 ) if not page or page == '{{{1}}}' then return Excerpt.getError( 'no-page' ) end local title = mw.title.new( page ) if not title then return Excerpt.getError( 'invalid-title', page ) end local fragment = title.fragment -- save for later if title.isRedirect then title = title.redirectTarget if fragment == "" then fragment = title.fragment -- page merge potential end end if not title.exists then return Excerpt.getError( 'page-not-found', page ) end page = title.prefixedText local wikitext = title:getContent() -- Get the template params and process them local params = { hat = yesno( Excerpt.getArg( 'hat', true ) ), this = Excerpt.getArg( 'this' ), only = Excerpt.getArg( 'only' ), files = Excerpt.getArg( 'files', Excerpt.getArg( 'file' ) ), lists = Excerpt.getArg( 'lists', Excerpt.getArg( 'list' ) ), tables = Excerpt.getArg( 'tables', Excerpt.getArg( 'table' ) ), templates = Excerpt.getArg( 'templates', Excerpt.getArg( 'template' ) ), galleries = Excerpt.getArg( 'galleries', Excerpt.getArg( 'gallery' ) ), paragraphs = Excerpt.getArg( 'paragraphs', Excerpt.getArg( 'paragraph' ) ), references = yesno( Excerpt.getArg( 'references', true ) ), subsections = yesno( Excerpt.getArg( 'subsections', false ) ), links = yesno( Excerpt.getArg( 'links', true ) ), bold = yesno( Excerpt.getArg( 'bold', false ) ), briefDates = yesno( Excerpt.getArg( 'briefdates', false ) ), inline = yesno( Excerpt.getArg( 'inline' ) ), quote = yesno( Excerpt.getArg( 'quote' ) ), more = yesno( Excerpt.getArg( 'more' ) ), class = Excerpt.getArg( 'class' ), track = yesno( Excerpt.getArg( 'track', true ) ), displayTitle = Excerpt.getArg( 'displaytitle', page ), } -- Make sure the requested section exists and get the excerpt local excerpt local section = Excerpt.getArg( 2, fragment ) section = mw.text.trim( section ) if section == '' then section = nil end if section then excerpt = parser.getSectionTag( wikitext, section ) if not excerpt then if params.subsections then excerpt = parser.getSection( wikitext, section ) else local sections = parser.getSections( wikitext ) excerpt = sections[ section ] end end if not excerpt then return Excerpt.getError( 'section-not-found', section ) end if excerpt == '' then return Excerpt.getError( 'section-empty', section ) end else excerpt = parser.getLead( wikitext ) if excerpt == '' then return Excerpt.getError( 'lead-empty' ) end end -- Remove noinclude bits excerpt = excerpt:gsub( '<[Nn][Oo][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>.-</[Nn][Oo][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>', '' ) -- Filter various elements from the excerpt excerpt = Excerpt.filterFiles( excerpt, params.files ) excerpt = Excerpt.filterLists( excerpt, params.lists ) excerpt = Excerpt.filterTables( excerpt, params.tables ) excerpt = Excerpt.filterGalleries( excerpt, params.galleries ) excerpt = Excerpt.filterParagraphs( excerpt, params.paragraphs ) -- If no file is found, try to get one from the infobox if ( params.only == 'file' or params.only == 'files' or params.only == 'filename' or not params.only and ( not params.files or params.files ~= '0' ) ) -- caller asked for files and not section -- and we're in the lead section and config.captions -- and we have the config option required to try finding files in infoboxes and #parser.getFiles( excerpt ) == 0 -- and there're no files in the excerpt then excerpt = Excerpt.addInfoboxFile( excerpt ) end -- Filter the templates by appending the templates blacklist to the templates filter if config.blacklist then local blacklist = table.concat( config.blacklist, ',' ) if params.templates then if string.sub( params.templates, 1, 1 ) == '-' then params.templates = params.templates .. ',' .. blacklist end else params.templates = '-' .. blacklist end end excerpt = Excerpt.filterTemplates( excerpt, params.templates ) -- Leave only the requested elements if params.only == 'file' or params.only == 'files' then local files = parser.getFiles( excerpt ) excerpt = params.only == 'file' and files[1] or table.concat( files, '\n\n' ) end if params.only == 'list' or params.only == 'lists' then local lists = parser.getLists( excerpt ) excerpt = params.only == 'list' and lists[1] or table.concat( lists, '\n\n' ) end if params.only == 'table' or params.only == 'tables' then local tables = parser.getTables( excerpt ) excerpt = params.only == 'table' and tables[1] or table.concat( tables, '\n\n' ) end if params.only == 'gallery' or params.only == 'galleries' then local galleries = parser.getGalleries( excerpt ) excerpt = params.only == 'gallery' and galleries[1] or table.concat( galleries, '\n\n' ) end if params.only == 'paragraph' or params.only == 'paragraphs' then local paragraphs = parser.getParagraphs( excerpt ) excerpt = params.only == 'paragraph' and paragraphs[1] or table.concat( paragraphs, '\n\n' ) end if params.only == 'template' or params.only == 'templates' then local templates = parser.getTemplates( excerpt ) excerpt = params.only == 'template' and templates[1] or table.concat( templates, '\n\n' ) end if params.only == 'filename' then local files = parser.getFiles( excerpt ) local file = files[1] excerpt = file and parser.getFileName( file ) or 'Noimage.png' params.inline = true end -- @todo Make more robust and move downwards if params.briefDates then excerpt = Excerpt.fixDates( excerpt ) end -- Remove unwanted elements excerpt = Excerpt.removeComments( excerpt ) excerpt = Excerpt.removeSelfLinks( excerpt ) excerpt = Excerpt.removeNonFreeFiles( excerpt ) excerpt = Excerpt.removeBehaviorSwitches( excerpt ) -- Fix or remove the references if params.references then excerpt = Excerpt.fixReferences( excerpt, page, wikitext ) else excerpt = Excerpt.removeReferences( excerpt ) end -- Remove wikilinks if not params.links then excerpt = Excerpt.removeLinks( excerpt ) end -- Link the bold text near the start of most leads and then remove it if not section then excerpt = Excerpt.linkBold( excerpt, page ) end if not params.bold then excerpt = Excerpt.removeBold( excerpt ) end -- Remove extra line breaks but leave one before and after so the parser interprets lists, tables, etc. correctly excerpt = excerpt:gsub( '\n\n\n+', '\n\n' ) excerpt = mw.text.trim( excerpt ) excerpt = '\n' .. excerpt .. '\n' -- Remove nested categories excerpt = frame:preprocess( excerpt ) excerpt = Excerpt.removeCategories( excerpt ) -- Add tracking categories if params.track and config.categories then excerpt = Excerpt.addTrackingCategories( excerpt ) end -- Build the final output if params.inline then return mw.text.trim( excerpt ) end local tag = params.quote and 'blockquote' or 'div' local block = mw.html.create( tag ):addClass( 'excerpt-block' ):addClass( params.class ) if config.styles then local styles = frame:extensionTag( 'templatestyles', '', { src = config.styles } ) block:node( styles ) end if params.hat then local hat = Excerpt.getHat( page, section, params ) block:node( hat ) end excerpt = mw.html.create( 'div' ):addClass( 'excerpt' ):wikitext( excerpt ) block:node( excerpt ) if params.more then local more = Excerpt.getReadMore( page, section ) block:node( more ) end return block end -- Filter the files in the given wikitext against the given filter function Excerpt.filterFiles( wikitext, filter ) if not filter then return wikitext end local filters, isBlacklist = Excerpt.parseFilter( filter ) local files = parser.getFiles( wikitext ) for index, file in pairs( files ) do local name = parser.getFileName( file ) if isBlacklist and ( Excerpt.matchFilter( index, filters ) or Excerpt.matchFilter( name, filters ) ) or not isBlacklist and ( not Excerpt.matchFilter( index, filters ) and not Excerpt.matchFilter( name, filters ) ) then wikitext = Excerpt.removeString( wikitext, file ) end end return wikitext end -- Filter the lists in the given wikitext against the given filter function Excerpt.filterLists( wikitext, filter ) if not filter then return wikitext end local filters, isBlacklist = Excerpt.parseFilter( filter ) local lists = parser.getLists( wikitext ) for index, list in pairs( lists ) do if isBlacklist and Excerpt.matchFilter( index, filters ) or not isBlacklist and not Excerpt.matchFilter( index, filters ) then wikitext = Excerpt.removeString( wikitext, list ) end end return wikitext end -- Filter the tables in the given wikitext against the given filter function Excerpt.filterTables( wikitext, filter ) if not filter then return wikitext end local filters, isBlacklist = Excerpt.parseFilter( filter ) local tables = parser.getTables( wikitext ) for index, tableWikitext in pairs( tables ) do local id = parser.getTableAttribute( tableWikitext, 'id' ) if isBlacklist and ( Excerpt.matchFilter( index, filters ) or Excerpt.matchFilter( id, filters ) ) or not isBlacklist and ( not Excerpt.matchFilter( index, filters ) and not Excerpt.matchFilter( id, filters ) ) then wikitext = Excerpt.removeString( wikitext, tableWikitext ) end end return wikitext end -- Filter the galleries in the given wikitext against the given filter function Excerpt.filterGalleries( wikitext, filter ) if not filter then return wikitext end local filters, isBlacklist = Excerpt.parseFilter( filter ) local galleries = parser.getGalleries( wikitext ) for index, gallery in pairs( galleries ) do if isBlacklist and Excerpt.matchFilter( index, filters ) or not isBlacklist and not Excerpt.matchFilter( index, filters ) then wikitext = Excerpt.removeString( wikitext, gallery ) end end return wikitext end -- Filter the paragraphs in the given wikitext against the given filter function Excerpt.filterParagraphs( wikitext, filter ) if not filter then return wikitext end local filters, isBlacklist = Excerpt.parseFilter( filter ) local paragraphs = parser.getParagraphs( wikitext ) for index, paragraph in pairs( paragraphs ) do if isBlacklist and Excerpt.matchFilter( index, filters ) or not isBlacklist and not Excerpt.matchFilter( index, filters ) then wikitext = Excerpt.removeString( wikitext, paragraph ) end end return wikitext end -- Filter the templates in the given wikitext against the given filter function Excerpt.filterTemplates( wikitext, filter ) if not filter then return wikitext end local filters, isBlacklist = Excerpt.parseFilter( filter ) local templates = parser.getTemplates( wikitext ) for index, template in pairs( templates ) do local name = parser.getTemplateName( template ) if isBlacklist and ( Excerpt.matchFilter( index, filters ) or Excerpt.matchFilter( name, filters ) ) or not isBlacklist and ( not Excerpt.matchFilter( index, filters ) and not Excerpt.matchFilter( name, filters ) ) then wikitext = Excerpt.removeString( wikitext, template ) end end return wikitext end function Excerpt.addInfoboxFile( excerpt ) -- We cannot distinguish the infobox from the other templates, so we search them all local templates = parser.getTemplates( excerpt ) for _, template in pairs( templates ) do local parameters = parser.getTemplateParameters( template ) local file, captions, caption, cssClasses, cssClass for _, pair in pairs( config.captions ) do file = pair[1] file = parameters[file] if file and Excerpt.matchAny( file, '^.*%.', { '[Jj][Pp][Ee]?[Gg]', '[Pp][Nn][Gg]', '[Gg][Ii][Ff]', '[Ss][Vv][Gg]' }, '.*' ) then file = string.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 -- Check for CSS classes -- 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 end end local class = cssClass and ( '|class=' .. cssClass ) or '' return '[[File:' .. file .. class .. '|thumb|' .. ( caption or '' ) .. ']]\n' .. excerpt end end end return excerpt end function Excerpt.removeNonFreeFiles( wikitext ) local files = parser.getFiles( wikitext ) for _, file in pairs( files ) do local fileName = 'File:' .. parser.getFileName( file ) local fileTitle = mw.title.new( fileName ) if fileTitle then local fileDescription = fileTitle:getContent() if not fileDescription or fileDescription == '' then local frame = mw.getCurrentFrame() fileDescription = frame:preprocess( '{{' .. fileName .. '}}' ) -- try Commons end if fileDescription and string.match( fileDescription, '[Nn]on%-free' ) then wikitext = Excerpt.removeString( wikitext, file ) end end end return wikitext end function Excerpt.getHat( page, section, params ) local hat -- Build the text if params.this then hat = params.this elseif params.quote then hat = Excerpt.getMessage( 'this' ) elseif params.only then hat = Excerpt.getMessage( params.only ) else hat = Excerpt.getMessage( 'section' ) end hat = hat .. ' ' .. Excerpt.getMessage( 'excerpt' ) -- Build the link if section then hat = hat .. ' [[:' .. page .. '#' .. mw.uri.anchorEncode( section ) .. '|' .. params.displayTitle .. ' § ' .. section:gsub( '%[%[([^]|]+)|?[^]]*%]%]', '%1' ) .. ']].' -- remove nested links else hat = hat .. ' [[:' .. page .. '|' .. params.displayTitle .. ']].' end -- Build the edit link local title = mw.title.new( page ) local editUrl = title:fullUrl( 'action=edit' ) hat = hat .. '<span class="mw-editsection-like plainlinks"><span class="mw-editsection-bracket">[</span>[' hat = hat .. editUrl .. ' ' .. mw.message.new( 'editsection' ):plain() hat = hat .. ']<span class="mw-editsection-bracket">]</span></span>' if config.hat then local frame = mw.getCurrentFrame() hat = config.hat .. hat .. '}}' hat = frame:preprocess( hat ) else hat = mw.html.create( 'div' ):addClass( 'dablink excerpt-hat' ):wikitext( hat ) end return hat end function Excerpt.getReadMore( page, section ) local link = "'''[[" .. page if section then link = link .. '#' .. section end local text = Excerpt.getMessage( 'more' ) link = link .. '|' .. text .. "]]'''" link = mw.html.create( 'div' ):addClass( 'noprint excerpt-more' ):wikitext( link ) return link end -- Fix birth and death dates, but only in the first paragraph -- @todo Use parser.getParagraphs() to get the first paragraph function Excerpt.fixDates( excerpt ) local start local s local e = 0 repeat start = e + 1 s, e = mw.ustring.find( excerpt, '%s*%b{}%s*', start ) until not s or s > start s, e = mw.ustring.find( excerpt, '%b()', start ) -- get (...), which may be (year–year) if s and s < start + 100 then -- look only near the start local excerptStart = mw.ustring.sub( excerpt, s, e ) local year1, conjunction, year2 = string.match( excerptStart, '(%d%d%d+)(.-)(%d%d%d+)' ) if year1 and year2 and ( string.match( conjunction, '[%-–—]' ) or string.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 return excerpt end -- Replace the first call to each reference defined outside of the excerpt 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"> function Excerpt.fixReferences( excerpt, page, wikitext ) local references = parser.getReferences( excerpt ) local fixed = {} for _, reference in pairs( references ) do local name = parser.getTagAttribute( reference, 'name' ) if not fixed[ name ] then -- fix each reference only once local content = parser.getTagContent( reference ) if not content then -- reference is self-closing local full = parser.getReference( excerpt, name ) if not full then -- the reference is not defined in the excerpt full = parser.getReference( wikitext, name ) if full then excerpt = excerpt:gsub( Excerpt.escapeString( reference ), Excerpt.escapeString( full ), 1 ) end table.insert( fixed, name ) end end end end -- Prepend the page title to the reference names to prevent conflicts with other references in the transcluding page excerpt = excerpt:gsub( '< *[Rr][Ee][Ff][^>]*name *= *["\']?([^"\'>/]+)["\']?[^>/]*(/?) *>', '<ref name="' .. page:gsub( '"', '' ) .. ' %1"%2>' ) -- Remove reference groups because they don't apply to the transcluding page excerpt = excerpt:gsub( '< *[Rr][Ee][Ff] *group *= *["\']?[^"\'>/]+["\'] *>', '<ref>' ) return excerpt end function Excerpt.removeReferences( excerpt ) local references = parser.getReferences( excerpt ) for _, reference in pairs( references ) do excerpt = Excerpt.removeString( excerpt, reference ) end return excerpt end function Excerpt.removeCategories( excerpt ) local categories = parser.getCategories( excerpt ) for _, category in pairs( categories ) do excerpt = Excerpt.removeString( excerpt, category ) end return excerpt end function Excerpt.removeBehaviorSwitches( excerpt ) return excerpt:gsub( '__[A-Z]+__', '' ) end function Excerpt.removeComments( excerpt ) return excerpt:gsub( '<!%-%-.-%-%->', '' ) end function Excerpt.removeBold( excerpt ) return excerpt:gsub( "'''", '' ) end function Excerpt.removeLinks( excerpt ) local links = parser.getLinks( excerpt ) for _, link in pairs( links ) do excerpt = Excerpt.removeString( excerpt, link ) end return excerpt end -- @todo Use parser.getLinks function Excerpt.removeSelfLinks( excerpt ) local lang = mw.language.getContentLanguage() local page = Excerpt.escapeString( mw.title.getCurrentTitle().prefixedText ) local ucpage = lang:ucfirst( page ) local lcpage = lang:lcfirst( page ) excerpt = excerpt :gsub( '%[%[(' .. ucpage .. ')%]%]', '%1' ) :gsub( '%[%[(' .. lcpage .. ')%]%]', '%1' ) :gsub( '%[%[' .. ucpage .. '|([^]]+)%]%]', '%1' ) :gsub( '%[%[' .. lcpage .. '|([^]]+)%]%]', '%1' ) return excerpt end -- Replace the bold title or synonym near the start of the page by a link to the page function Excerpt.linkBold( excerpt, page ) local lang = mw.language.getContentLanguage() local position = mw.ustring.find( excerpt, "'''" .. lang:ucfirst( page ) .. "'''", 1, true ) -- look for "'''Foo''' is..." (uc) or "A '''foo''' is..." (lc) or mw.ustring.find( excerpt, "'''" .. lang:lcfirst( page ) .. "'''", 1, true ) -- plain search: special characters in page represent themselves if position then local length = mw.ustring.len( page ) excerpt = mw.ustring.sub( excerpt, 1, position + 2 ) .. '[[' .. mw.ustring.sub( excerpt, position + 3, position + length + 2 ) .. ']]' .. mw.ustring.sub( excerpt, 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) excerpt = mw.ustring.gsub( excerpt, "'''(.-'*)'''", function ( text ) if not string.find( text, '%[' ) and not string.find( text, '%{' ) then -- if not wikilinked or some weird template return "'''[[" .. page .. '|' .. text .. "]]'''" -- replace '''Foo''' by '''[[page|Foo]]''' else return nil -- instruct gsub to make no change end end, 1 ) -- terminates the anonymous replacement function passed to gsub end return excerpt end function Excerpt.addTrackingCategories( excerpt ) local currentTitle = mw.title.getCurrentTitle() local addedCategories = false local contentCategory = config.categories.content if contentCategory and currentTitle.isContentPage then addedCategories = true excerpt = excerpt .. '[[Category:' .. contentCategory .. ']]' end local namespaceCategory = config.categories[ currentTitle.namespace ] if namespaceCategory then addedCategories = true excerpt = excerpt .. '[[Category:' .. namespaceCategory .. ']]' end if addedCategories then excerpt = excerpt .. '\n' end return excerpt end -- Helper method to match from a list of regular expressions -- Like so: match pre..list[1]..post or pre..list[2]..post or ... function Excerpt.matchAny( text, pre, list, post, init ) for i = 1, #list do local match = { mw.ustring.match( text, pre .. list[ i ] .. post, init ) } if match[1] then return unpack( match ) end end return nil end -- Helper function to get arguments -- args from Lua calls have priority over parent args from template function Excerpt.getArg( key, default ) local frame = mw.getCurrentFrame() for k, value in pairs( frame:getParent().args ) do if k == key and mw.text.trim( value ) ~= '' then return value end end for k, value in pairs( frame.args ) do if k == key and mw.text.trim( value ) ~= '' then return value end end return default end -- Helper method to get an error message -- This method also categorizes the current page in one of the configured error categories function Excerpt.getError( key, value ) local message = Excerpt.getMessage( 'error-' .. key, value ) local markup = mw.html.create( 'div' ):addClass( 'error' ):wikitext( message ) if config.categories and config.categories.errors and mw.title.getCurrentTitle().isContentPage then markup:node( '[[Category:' .. config.categories.errors .. ']]' ) end return markup end -- Helper method to get a localized message -- This method uses Module:TNT to get localized messages from https://commons.wikimedia.org/wiki/Data:I18n/Module:Excerpt.tab -- If Module:TNT is not available or the localized message does not exist, the key is returned instead function Excerpt.getMessage( key, value ) local ok2, TNT = pcall( require, 'Module:TNT' ) if not ok2 then return key end local ok3, message = pcall( TNT.format, 'I18n/Module:Excerpt.tab', key, value ) if not ok3 then return key end return message end -- Helper method to escape a string for use in regexes function Excerpt.escapeString( str ) return str:gsub( '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' ) end -- Helper method to remove a string from a text -- @param text Text from where to remove the string -- @param str String to remove -- @return The given text with the string removed function Excerpt.removeString( text, str ) local pattern = Excerpt.escapeString( str ) if #pattern > 9999 then -- strings longer than 10000 bytes can't be put into regexes pattern = Excerpt.escapeString( mw.ustring.sub( str, 1, 999 ) ) .. '.-' .. Excerpt.escapeString( mw.ustring.sub( str, -999 ) ) end return text:gsub( pattern, '' ) end -- Helper method to convert a comma-separated list of numbers or min-max ranges into a list of booleans -- @param filter Required. Comma-separated list of numbers or min-max ranges, for example '1,3-5' -- @return Map from integers to booleans, for example {1=true,2=false,3=true,4=true,5=true} -- @return Boolean indicating whether the filters should be treated as a blacklist or not -- @note Merging this into matchFilter is possible, but way too inefficient function Excerpt.parseFilter( filter ) local filters = {} local isBlacklist = false if string.sub( filter, 1, 1 ) == '-' then isBlacklist = true filter = string.sub( filter, 2 ) end local values = mw.text.split( filter, ',' ) -- split values: '1,3-5' to {'1','3-5'} for _, value in pairs( values ) do value = mw.text.trim( value ) local min, max = mw.ustring.match( value, '^(%d+)%s*[-–—]%s*(%d+)$' ) -- '3-5' to min=3 max=5 if not max then min, max = string.match( value, '^((%d+))$' ) end -- '1' to min=1 max=1 if max then for i = min, max do filters[ i ] = true end else filters[ value ] = true -- if we reach this point, the string had the form 'a,b,c' rather than '1,2,3' end end filter = { cache = {}, terms = filters } return filter, isBlacklist end -- Helper function to see if a value matches any of the given filters function Excerpt.matchFilter( value, filter ) if value == nil then return false elseif type(value) == "number" then return filter.terms[value] else local cached = filter.cache[value] if cached ~= nil then return cached end local lang = mw.language.getContentLanguage() local lcvalue = lang:lcfirst(value) local ucvalue = lang:ucfirst(value) for term in pairs( filter.terms ) do if value == tostring(term) or type(term) == "string" and ( lcvalue == term or ucvalue == term or mw.ustring.match( value, term ) ) then filter.cache[value] = true return true end end filter.cache[value] = false end end return Excerpt ti7siuqx917e9fizdf8nk9scneu1psk Modul:WikitextParser 828 7836 35202 33618 2026-10-05T11:37:11Z Sophivorus 558 Update from [[d:Special:GoToLinkedPage/enwiki/Q137784397|master]] using [[mw:Synchronizer| #Synchronizer]] 35202 Scribunto text/plain -- Module:WikitextParser is a general-purpose wikitext parser -- Documentation and master version: https://en.wikipedia.org/wiki/Module:WikitextParser -- License: GPL-3.0 (https://www.gnu.org/licenses/gpl-3.0.html) -- Authors: User:Sophivorus, User:Certes, User:Aidan9382, et al. local str_find, str_sub, str_match = string.find, string.sub, string.match local str_gsub, str_gmatch, str_rep = string.gsub, string.gmatch, string.rep local str_lower = string.lower local table_insert, table_concat = table.insert, table.concat -- Private helper method to escape a string for use in regexes local function escapeString( str ) return str_gsub( str, '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0' ) end -- Fast gsplit (a la mw.text.gsplit) for fixed ASCII separators local function str_gsplit( str, sep ) if sep == '' or str_find( sep, '[\128-\255]' ) then error( 'Separator must be ASCII and cannot be empty' ) end local i, str1 = 1, str .. sep return function() local s, e = str_find( str1, sep, i, true ) if not s then return s end local t = i i = e + 1 return str_sub( str1, t, s - 1 ) end end -- Fast dedicated two-way split (a la mw.text.split) for fixed ASCII separators local function str_split2( str, sep ) if sep == '' or str_find( sep, '[\128-\255]' ) then error( 'Separator must be ASCII and cannot be empty' ) end local s, e = str_find( str, sep, 1, true ) if not s then return str end return str_sub( str, 1, s - 1 ), str_sub( str, e + 1 ) end -- Fast whitespace trim (a la mw.text.trim) local function str_trim( str ) return str_match( str, '^%s*(.-)%s*$' ) end local parser = {} -- Get the lead section from the given wikitext -- The lead section is any content before the first section title. -- @param wikitext Required. Wikitext to parse. -- @return Wikitext of the lead section. May be empty if the lead section is empty. function parser.getLead( wikitext ) wikitext = '\n' .. wikitext wikitext = str_gsub( wikitext, '\n==.*', '' ) wikitext = str_trim( wikitext ) return wikitext end -- Get the sections from the given wikitext -- This method doesn't get the lead section, use getLead for that -- @param wikitext Required. Wikitext to parse. -- @return Map from section title to section content function parser.getSections( wikitext ) local sections = {} wikitext = '\n' .. wikitext .. '\n==' for title in str_gmatch( wikitext, '\n==+ *([^=]-) *==+' ) do local section = str_match( wikitext, '\n==+ *' .. escapeString( title ) .. ' *==+(.-)\n==' ) section = str_trim( section ) sections[ title ] = section end return sections end -- Get a section from the given wikitext (including any subsections) -- If the given section title appears more than once, only the section of the first instance will be returned -- @param wikitext Required. Wikitext to parse. -- @param title Required. Title of the section -- @return Wikitext of the section, or nil if it isn't found. May be empty if the section is empty or contains only subsections. function parser.getSection( wikitext, title ) title = str_trim( title ) title = escapeString( title ) wikitext = '\n' .. wikitext .. '\n' local level level, wikitext = str_match( wikitext, '\n(==+) *' .. title .. ' *==.-\n(.*)' ) if wikitext then local nextSection = '\n==' .. str_rep( '=?', #level - 2 ) .. '[^=].*' wikitext = str_gsub( wikitext, nextSection, '' ) -- remove later sections at this level or higher wikitext = str_trim( wikitext ) return wikitext end end -- Get the content of a <section> tag from the given wikitext. -- We can't use getTags because unlike all other tags, both opening and closing <section> tags are self-closing. -- @param wikitext Required. Wikitext to parse. -- @param name Required. Name of the <section> tag -- @return Content of the <section> tag, or nil if it isn't found. May be empty if the section tag is empty. function parser.getSectionTag( wikitext, name ) name = str_trim( name ) name = escapeString( name ) local sections = {} for section in str_gmatch( wikitext, '< *section +begin *= *["\']? *' .. name .. ' *["\']? */>(.-)< *section +end= *["\']? *'.. name ..' *["\']? */>' ) do table_insert( sections, section ) end if #sections > 0 then return table_concat( sections ) end end -- Get the lists from the given wikitext. -- @param wikitext Required. Wikitext to parse. -- @return Sequence of lists. function parser.getLists( wikitext ) local lists = {} wikitext = '\n' .. wikitext .. '\n\n' for list in str_gmatch( wikitext, '\n([*#].-)\n[^*#]' ) do table_insert( lists, list ) end return lists end -- Get the paragraphs from the given wikitext. -- @param wikitext Required. Wikitext to parse. -- @return Sequence of paragraphs. function parser.getParagraphs( wikitext ) local paragraphs = {} -- Remove non-paragraphs wikitext = '\n' .. wikitext .. '\n' -- add newlines to simplfy patterns wikitext = str_gsub( wikitext, '%f[^\n]<!%-%-.-%-%-> *%f[\n]', '' ) -- remove comments wikitext = str_gsub( wikitext, '%f[^\n]%[%b[]%] *%f[\n]', '' ) -- remove files and categories wikitext = str_gsub( wikitext, '<gallery.-</gallery>', '\n\n' ) -- remove galleries wikitext = str_gsub( wikitext, '%f[^\n]%b{} *%f[\n]', '' ) -- remove tables and block templates wikitext = str_gsub( wikitext, '%f[^\n]%b{} *%b{} *%f[\n]', '' ) -- remove neighboring tables and block templates wikitext = str_gsub( wikitext, '%f[^\n]%b{} *<!%-%-.-%-%-> *%b{} *%f[\n]', '' ) -- remove neighboring tables and block templates with a comment among them wikitext = str_gsub( wikitext, '%f[^\n][*#].-%f[\n]', '' ) -- remove lists wikitext = str_gsub( wikitext, '%f[^\n]==+[^=]+==+ *%f[\n]', '' ) -- remove section titles wikitext = str_trim( wikitext ) for paragraph in str_gsplit( wikitext, "\n\n" ) do paragraph = str_trim( paragraph ) if paragraph ~= "" then -- Filter out 3+ consecutive newlines table_insert( paragraphs, paragraph ) end end return paragraphs end -- Get the templates from the given wikitext. -- @param wikitext Required. Wikitext to parse. -- @return Sequence of templates. function parser.getTemplates( wikitext ) local templates = {} for template in str_gmatch( wikitext, '{%b{}}' ) do if str_sub( template, 1, 3 ) ~= '{{#' then -- skip parser functions like #if table_insert( templates, template ) end end return templates end -- Get the requested template from the given wikitext. -- If the template appears more than once, only the first instance will be returned -- @param wikitext Required. Wikitext to parse. -- @param name Name of the template to get -- @return Wikitext of the template, or nil if it wasn't found function parser.getTemplate( wikitext, name ) local templates = parser.getTemplates( wikitext ) local lang = mw.language.getContentLanguage() for _, template in ipairs( templates ) do local templateName = parser.getTemplateName( template ) if lang:ucfirst( templateName ) == lang:ucfirst( name ) then return template end end end -- Get name of the template from the given template wikitext. -- @param templateWikitext Required. Wikitext of the template to parse. -- @return Name of the template -- @todo Strip "Template:" namespace? function parser.getTemplateName( templateWikitext ) return str_match( templateWikitext, '^{{ *([^}|\n]+)' ) end -- Get the parameters from the given template wikitext. -- @param templateWikitext Required. Wikitext of the template to parse. -- @return Map from parameter names to parameter values, NOT IN THE ORIGINAL ORDER. -- @return Order in which the parameters were parsed. function parser.getTemplateParameters( templateWikitext ) local parameters = {} local paramOrder = {} local params = str_match( templateWikitext, '{{[^|}]-|(.*)}}' ) if params then -- Temporarily replace pipes in subtemplates and links to avoid chaos for subtemplate in str_gmatch( params, '{%b{}}' ) do params = str_gsub( params, escapeString( subtemplate ), str_gsub( subtemplate, '.', { ['%']='%%', ['|']="@@:@@", ['=']='@@_@@' } ), 1 ) end for link in str_gmatch( params, '%[%b[]%]' ) do params = str_gsub( params, escapeString( link ), str_gsub( link, '.', { ['%']='%%', ['|']='@@:@@', ['=']='@@_@@' } ), 1 ) end local count = 0 local name, value for param in str_gsplit( params, '|' ) do name, value = str_split2( param, '=' ) if value then local number = tonumber( name ) if number then --XXX: What about keys like: 01, .1, 2e0, etc. name = number end value = str_trim( value ) else value = name count = count + 1 name = count end value = str_gsub( value, '@@_@@', '=' ) value = str_gsub( value, '@@:@@', '|' ) parameters[ name ] = value table_insert( paramOrder, name ) end end return parameters, paramOrder end -- Get the tags from the given wikitext. -- @param wikitext Required. Wikitext to parse. -- @return Sequence of tags. function parser.getTags( wikitext ) local tags = {} local tag, tagName, tagEnd -- Don't match closing tags like </div>, comments like <!--foo-->, comparisons like 1<2 or things like <3 for tagStart, tagOpen in str_gmatch( wikitext, '()(<[^/!%d].->)' ) do tagName = parser.getTagName( tagOpen ) -- If we're in a self-closing tag, like <ref name="foo" />, <references/>, <br/>, <br>, <hr>, etc. if str_match( tagOpen, '<.-/>' ) or tagName == 'br' or tagName == 'hr' then tag = tagOpen -- If we're in a tag that may contain others like it, like <div> or <span> elseif tagName == 'div' or tagName == 'span' then local position = tagStart + #tagOpen - 1 local depth = 1 while depth > 0 do tagEnd = str_match( wikitext, '</ ?' .. tagName .. ' ?>()', position ) if tagEnd then tagEnd = tagEnd - 1 else break -- unclosed tag end position = str_match( wikitext, '()< ?' .. tagName .. '[ >]', position + 1 ) if not position then position = tagEnd + 1 end if position > tagEnd then depth = depth - 1 else depth = depth + 1 end end tag = str_sub( wikitext, tagStart, tagEnd ) -- Else we're probably in tag that shouldn't contain others like it, like <math> or <strong> else tagEnd = str_match( wikitext, '</ ?' .. tagName .. ' ?>()', tagStart ) if tagEnd then tag = str_sub( wikitext, tagStart, tagEnd - 1 ) -- If no end tag is found, assume we matched something that wasn't a tag, like <no. 1> else tag = nil end end table_insert( tags, tag ) end return tags end -- Get the name of the tag in the given wikitext -- @param tag Required. Tag to parse. -- @return Name of the tag or nil if not found function parser.getTagName( tagWikitext ) local tagName = str_match( tagWikitext, '^< *(.-)[ />]' ) if tagName then tagName = str_lower( tagName ) end return tagName end -- Get the value of an attribute in the given tag. -- @param tagWikitext Required. Wikitext of the tag to parse. -- @param attribute Required. Name of the attribute. -- @return Value of the attribute or nil if not found function parser.getTagAttribute( tagWikitext, attribute ) local _, value = str_match( tagWikitext, '^<[^/>]*' .. attribute .. ' *= *(["\']?)([^/>]-)%1[ />]' ) return value end -- Get the content of the given tag. -- @param tagWikitext Required. Wikitext of the tag to parse. -- @return Content of the tag. May be empty if the tag is empty. Will be nil if the tag is self-closing. -- @todo May fail with nested tags function parser.getTagContent( tagWikitext ) return str_match( tagWikitext, '^<.->.-</.->' ) end -- Get the <gallery> tags from the given wikitext. -- @param wikitext Required. Wikitext to parse. -- @return Sequence of gallery tags. function parser.getGalleries( wikitext ) local galleries = {} local tags = parser.getTags( wikitext ) for _, tag in ipairs( tags ) do local tagName = parser.getTagName( tag ) if tagName == 'gallery' then table_insert( galleries, tag ) end end return galleries end -- Get the <ref> tags from the given wikitext. -- @param wikitext Required. Wikitext to parse. -- @return Sequence of ref tags. function parser.getReferences( wikitext ) local references = {} local tags = parser.getTags( wikitext ) for _, tag in ipairs( tags ) do local tagName = parser.getTagName( tag ) if tagName == 'ref' then table_insert( references, tag ) end end return references end -- Get the reference with the given name from the given wikitext. -- @param wikitext Required. Wikitext to parse. -- @param referenceName Required. Name of the reference. -- @return Wikitext of the reference function parser.getReference( wikitext, referenceName ) local references = parser.getReferences( wikitext ) for _, reference in ipairs( references ) do local content = parser.getTagContent( reference ) local name = parser.getTagAttribute( reference, 'name' ) if content and name == referenceName then return reference end end end -- Get the tables from the given wikitext. -- @param wikitext Required. Wikitext to parse. -- @return Sequence of tables. function parser.getTables( wikitext ) local tables = {} wikitext = '\n' .. wikitext for t in str_gmatch( wikitext, '\n%b{}' ) do if str_sub( t, 1, 3 ) == '\n{|' then t = str_trim( t ) -- exclude the leading newline table_insert( tables, t ) end end return tables end -- Get the id from the given table wikitext -- @param tableWikitext Required. Wikitext of the table to parse. -- @param attribute Required. Name of the attribute. -- @return Value of the attribute or nil if not found function parser.getTableAttribute( tableWikitext, attribute ) local _, value = str_match( tableWikitext, '^{|[^\n]*' .. attribute .. ' *= *(["\']?)([^\n]-)%1[^\n]*\n' ) if not value or value == '' then value = str_match( tableWikitext, '^{|[^\n]*' .. attribute .. ' *= *([^\n ]+)[^\n]*\n' ) end return value end -- Get a table by id from the given wikitext -- @param wikitext Required. Wikitext to parse. -- @param id Required. Id of the table -- @return Wikitext of the table or nil if not found function parser.getTable( wikitext, id ) local tables = parser.getTables( wikitext ) for _, t in ipairs( tables ) do if id == parser.getTableAttribute( t, 'id' ) then return t end end end -- Get the data from the given table wikitext -- @param tableWikitext Required. Wikitext of the table to parse. -- @return Table data -- @todo Test and make more robust function parser.getTableData( tableWikitext ) local tableData = {} tableWikitext = str_trim( tableWikitext ); tableWikitext = str_gsub( tableWikitext, '^{|.-\n', '' ) -- remove the header tableWikitext = str_gsub( tableWikitext, '\n|}$', '' ) -- remove the footer tableWikitext = str_gsub( tableWikitext, '^|%+.-\n', '' ) -- remove any caption tableWikitext = str_gsub( tableWikitext, '|%-.-\n', '|-\n' ) -- remove any row attributes tableWikitext = str_gsub( tableWikitext, '^|%-\n', '' ) -- remove any leading empty row tableWikitext = str_gsub( tableWikitext, '\n|%-$', '' ) -- remove any trailing empty row for rowWikitext in str_gsplit( tableWikitext, '|-' ) do local rowData = {} rowWikitext = str_gsub( rowWikitext, '||', '\n|' ) rowWikitext = str_gsub( rowWikitext, '!!', '\n|' ) rowWikitext = str_gsub( rowWikitext, '\n!', '\n|' ) rowWikitext = str_gsub( rowWikitext, '^!', '\n|' ) rowWikitext = str_gsub( rowWikitext, '^\n|', '' ) for cellWikitext in str_gsplit( rowWikitext, '\n|' ) do cellWikitext = str_trim( cellWikitext ) table_insert( rowData, cellWikitext ) end table_insert( tableData, rowData ) end return tableData end -- Get the internal links from the given wikitext (includes category and file links). -- @param wikitext Required. Wikitext to parse. -- @return Sequence of internal links. function parser.getLinks( wikitext ) local links = {} for link in str_gmatch( wikitext, '%[%b[]%]' ) do table_insert( links, link ) end return links end -- Get the file links from the given wikitext. -- @param wikitext Required. Wikitext to parse. -- @return Sequence of file links. function parser.getFiles( wikitext ) local files = {} local links = parser.getLinks( wikitext ) for _, link in ipairs( links ) do local namespace = str_match( link, '^%[%[ *(.-) *:' ) if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'File' then table_insert( files, link ) end end return files end -- Get name of the file from the given file wikitext. -- @param fileWikitext Required. Wikitext of the file to parse. -- @return Name of the file function parser.getFileName( fileWikitext ) return str_match( fileWikitext, '^%[%[ *.- *: *(.-) *[]|]' ) end -- Get the category links from the given wikitext. -- @param wikitext Required. Wikitext to parse. -- @return Sequence of category links. function parser.getCategories( wikitext ) local categories = {} local links = parser.getLinks( wikitext ) for _, link in ipairs( links ) do local namespace = str_match( link, '^%[%[ -(.-) -:' ) if namespace and mw.site.namespaces[ namespace ] and mw.site.namespaces[ namespace ].canonicalName == 'Category' then table_insert( categories, link ) end end return categories end -- Get the external links from the given wikitext. -- @param wikitext Required. Wikitext to parse. -- @return Sequence of external links. function parser.getExternalLinks( wikitext ) local links = {} for link in str_gmatch( wikitext, '%b[]' ) do if str_match( link, '^%[//' ) or str_match( link, '^%[https?://' ) then table_insert( links, link ) end end return links end return parser 3r3fpf7d7jt2ttpx3ny4bt85muaz5lj