Модуль:Вложенный список
Данный модуль реализует шаблон {{Вложенный список}}. После перехода на модуль стало возможно заменить старый код шаблона на более доступный и семантичный и начать отслеживать ошибки в коде выводимых страниц. Код вызова модуля: Категории{{Вложенный список}} добавляет ряд штрафных категорий. Для упрощения их исправления модулем добавляются отображаемые при предпросмотре сообщения со ссылками на страницы, из-за которых шаблоном добавляется штрафная категория.
require( 'strict' )
--
-- Implements sublists in {{Вложенный список}}
-- 1. More accessible HTML
-- 2. No more template loops
-- 3. Less parser limit usage and more advanced features like wikitext error tracking
--
-- List to HTML conversion is from https://en.wikipedia.org/wiki/Module:Excerpt_slideshow
--
local getArgs = require( 'Module:Arguments' ).getArgs
local templatePageName = 'Вложенный список'
local modulePageName = 'Вложенный список'
local disambigPageSuffix = ' (значения)'
local editLinkText = 'править'
-- [[:Категория:Википедия:Страницы с ошибками шаблона Вложенный список]]
local errorCat = 'Википедия:Страницы с ошибками шаблона Вложенный список'
-- [[:Категория:Википедия:Страницы с шаблоном Вложенный список без ссылок]]
-- Remove the line to stop tracking this category
local linklessCat = 'Википедия:Страницы с шаблоном Вложенный список без ссылок'
-- [[:Категория:Википедия:Страницы с шаблоном Вложенный список с неверным типом страницы значений]]
-- Remove the line to stop tracking this category
local wrongTypeCat = 'Википедия:Страницы с шаблоном Вложенный список с неверным типом страницы значений'
local goodTypeCategories = {
[ 'Страницы значений:Тёзки' ] = true,
[ 'Страницы значений:Однофамильцы' ] = true,
[ 'Страницы значений:Однофамильцы-тёзки' ] = true,
[ 'Страницы значений:Полные тёзки' ] = true,
}
local errorMsgs = {
noTitle = 'Нет названия страницы.',
invalidTitle = 'Неправильное название страницы <code>%s</code>.',
noContent = 'Ошибка при включении страницы «[[%s]]».',
hasErrors = 'Ошибка в коде вложенного списка «[[%s]]» ([[Шаблон:Вложенный список#Штрафные категории|см. документацию]]).',
noLinks = 'На странице «[[%s]]» нет ссылок на другие страницы.',
notDisambig = 'На странице «[[%s]]» находится статья.',
wrongType = 'Страница «[[%s]]» не является страницей тёзок или однофамильцев. См. [[ВП:Н/ВС]].',
}
-- Adds a replacement to a redirected variant when substed
local substWithRedirects = true
local moduleInvocationCode = '{{#invoke:' .. modulePageName .. '|main|'
local nestedCheckArg = '$$no_checks'
local currentTitle = mw.title.getCurrentTitle()
local p = {}
local function isEmpty( str )
return str == nil or str == ''
end
local function pageNameBase( page )
return ( mw.ustring.gsub( page, '^%s*(.+)%s+%b()%s*$', '%1' ) )
end
local function addWarning( title, message )
if title ~= nil and message == nil then
message = string.format( noContent, title )
end
if title ~= nil and message ~= nil then
message = string.format( message, title )
end
mw.addWarning( string.format( '[[Template:%s|%s]]: %s', templatePageName, templatePageName, message ) )
end
local function templatePattern( str )
return string.format(
"[%s%s]%s",
mw.ustring.upper( mw.ustring.sub( str, 1, 1 ) ),
mw.ustring.lower( mw.ustring.sub( str, 1, 1 ) ),
mw.ustring.gsub( str, '^.', '' )
)
end
local function noGoodCategories( mwTitle )
if mwTitle == nil then
return true
end
local success, data = pcall( function()
return mwTitle.categories
end )
if not success then
return false
end
for _, category in ipairs( data ) do
if goodTypeCategories[ category ] == true then
return false
end
end
return true
end
local function findTwice( str, pattern )
if isEmpty( str ) then
return 0
end
local first, firstPos = mw.ustring.find( str, pattern )
if first == nil then
return 0
end
return mw.ustring.find( str, pattern, firstPos ) ~= nil and 2 or 1
end
local function checkForErrors( sublist )
-- Templates/syntax that should not be there
if
mw.ustring.find( sublist, 'class="error' ) ~= nil
or mw.ustring.find( sublist, 'id="disambig"' ) ~= nil -- {{disambig}}
or mw.ustring.find( sublist, 'class="hatnote' ) ~= nil -- {{hatnote}}
or mw.ustring.find( sublist, ' ambox ambox-' ) ~= nil -- {{ambox}}
or mw.ustring.find( sublist, '__TOC__' ) ~= nil -- __TOC__
or mw.ustring.find( sublist, 'UNIQ--references' ) ~= nil -- {{references}}
then
return true, '❌'
end
-- Incorrect list markup
if
mw.ustring.find( sublist, '^%s*%*' ) ~= 1 -- does not start with a list
or mw.ustring.find( sublist, '^%s*%*%*' ) ~= nil
or mw.ustring.find( sublist, '\n%:%*' ) ~= nil
then
return true, '*'
end
-- Other incorrect markup
if
mw.ustring.find( sublist, '\n;%s*[А-ЯA-Z]' ) ~= nil -- incorrect bold text markup
or mw.ustring.find( sublist, '\n=[^=]' ) ~= nil -- 1st level heading
or mw.ustring.find( sublist, '\n==[^=]' ) ~= nil -- 2nd level heading
or mw.ustring.find( sublist, '\n%-%-%-%-' ) ~= nil -- contains ---- in code
or mw.ustring.find( sublist, '\n<div class="ts%-NL">' ) ~= nil -- {{NL}} after a line break
or mw.ustring.find( sublist, '\n%[%[' ) ~= nil -- [[]] after a line break
then
return true, '🙈'
end
-- links inline
if mw.ustring.find( sublist, '[%[%s]https?:%/%/' ) ~= nil then
return true, '🔗'
end
return false, ' '
end
local function getEditLink( mwTitle )
local span = mw.html.create( 'span' )
:addClass( 'ts-NL-edit mw-editsection-like plainlinks noprint navigation-not-searchable group-user-show' )
:wikitext( string.format(
'<span class="mw-editsection-bracket">[</span>[%s %s]<span class="mw-editsection-bracket">]</span>',
mwTitle:fullUrl( 'action=edit' ),
editLinkText
) )
return tostring( span )
end
local function getIntro( title, text, addendum, mwTitle )
if isEmpty( title ) then
return error( 'getIntro: no title' )
end
text = text and mw.text.trim( text ) or ''
addendum = addendum or ''
-- Remove bracketed suffix automatically from a disambig page name
if isEmpty( text ) and mw.ustring.find( title, disambigPageSuffix, 1, true ) ~= nil then
text = pageNameBase( title )
end
if not isEmpty( text ) then
text = '|' .. text
end
local colon = ':'
if not isEmpty( addendum ) then
colon = ''
addendum = string.format( ' %s:', addendum )
end
local intro = string.format(
'<span class="dabhide">[[%s%s]]%s</span>%s',
title,
text,
colon,
addendum
)
return intro .. getEditLink( mwTitle )
end
local function getError( title, msg, setCategory )
if setCategory == nil then
setCategory = true
end
local errorText = msg
if title ~= nil and title ~= '' then
errorText = string.format( msg, title )
end
addWarning( nil, errorText )
return '<div class="error"><strong>' .. errorText .. '</strong></div>'
.. ( setCategory and string.format( '[[Category:%s]]', errorCat ) or '' )
end
-- Prevent template loop by replacing template calls to the calls to this module
local function replaceSubtemplates( content )
if isEmpty( content ) then
return content
end
local invocation = moduleInvocationCode .. nestedCheckArg .. '=1|'
content = mw.ustring.gsub( content, '{{' .. templatePattern( templatePageName ) .. '/?[23]?|', '{{NL|' )
content = mw.ustring.gsub( content, '{{' .. templatePattern( 'NL' ) .. '/?[23]?%|', invocation )
return content
end
--[[
@param {String} wikitext: Wikitext of just the list (i.e. each line is a list item)
@param {String} symbol: Special character used in the wikitext markup for the list, e.g. '*' or '#'
@param {String} outerTag: Text portion of the tag for each list or sublist, e.g. 'ul' or 'ol'
@param {String} innerTag: Text portion of the tag for each list item, e.g. 'li'
]]
local wikitextToHtmlList = function( wikitext, symbol, outerTag, innerTag )
local listParts = {}
for level, item in mw.ustring.gmatch( '\n' .. wikitext .. '\n', '\n(%' .. symbol .. '+)(.-)%f[\n]' ) do
table.insert( listParts, { level=level, item=item } )
end
table.insert( listParts, { level='', item='' } )
local htmlList = {}
for i, this in ipairs( listParts ) do
local isFirstItem = ( i == 1 )
local isLastItem = ( i == #listParts )
local lastLevel = isFirstItem and '' or listParts[ i - 1 ][ 'level' ]
local tags
if #lastLevel == #this.level then
tags = '</'..innerTag..'><'..innerTag..'>'
elseif #this.level > #lastLevel then
tags = string.rep( '<'..outerTag..'><'..innerTag..'>', #this.level - #lastLevel )
elseif isLastItem then
tags = string.rep( '</'..innerTag..'></'..outerTag..'>', #lastLevel )
else -- ( #this.level < #lastLevel ) and not last item
tags = string.rep( '</'..innerTag..'></'..outerTag..'>', #lastLevel - #this.level ) .. '</'..innerTag..'><'..innerTag..'>'
end
table.insert( htmlList, tags .. this.item )
end
return table.concat( htmlList )
end
--[[
@param {String} wikitext: Wikitext excerpt containg zero or more lists
@param {String} symbol: Special character used in the wikitext markup for the list, e.g. '*' or '#'
@param {String} outerTag: Text portion of the tag for each list or sublist, e.g. 'ul' or 'ol'
@param {String} innerTag: Text portion of the tag for each list item, e.g. 'li'
]]
local gsubWikitextLists = function( wikitext, symbol, outerTag, innerTag )
-- temporarily remove list linebreaks...
wikitext = mw.ustring.gsub( wikitext .. '\n', '\n%' .. symbol, '¿¿¿' .. symbol )
-- ...so we can grab the whole list (and just the list)...
return mw.ustring.gsub(
wikitext,
'¿¿¿%'..symbol..'[^\n]+',
function( listWikitext )
-- ...and then reinstate linebreaks...
listWikitext = mw.ustring.gsub( listWikitext, '¿¿¿%' .. symbol, '\n' .. symbol )
-- ...and finally do the conversion
return wikitextToHtmlList( listWikitext, symbol, outerTag, innerTag )
end
)
end
-- Protects the templates from substitution by substituting them with their own parameters
function p._substing( frame )
local args = getArgs( frame, {
parentOnly = true,
} )
local mTemplateInvocation = require( 'Module:Template invocation' )
local name = mTemplateInvocation.name( frame:getParent():getTitle() )
if substWithRedirects then
name = mw.ustring.gsub( name, 'Вложенный список/?%d?', 'NL' )
name = mw.ustring.gsub( name, 'NL/?%d?', 'NL' )
end
-- Remove redundant parameters
if args[ 2 ] == args[ 1 ] then
args[ 2 ] = nil
end
if not isEmpty( args[ 2 ] ) then
if mw.ustring.find( args[ 1 ], disambigPageSuffix, 1, true ) ~= nil and pageNameBase( args[ 1 ] ) == args[ 2 ] then
args[ 2 ] = nil
end
end
return mTemplateInvocation.invocation( name, args )
end
function p.main( frame )
if mw.isSubsting() then
return p._substing( frame )
end
local args = getArgs( frame )
local title = args[ 1 ]
local linkText = args[ 2 ]
local appendedText = args[ 3 ]
if isEmpty( title ) then
return getError( nil, errorMsgs.noTitle, currentTitle.namespace == 0 )
end
-- frame:expandTemplate is used because mw.title:getContent() does not handle redirects
local mwTitle = mw.title.new( title )
-- Invalid title
if mwTitle == nil then
return getError( title, errorMsgs.invalidTitle )
end
local origMwTitle = mwTitle
if mwTitle.redirectTarget then
mwTitle = mwTitle.redirectTarget
end
-- The page or redirect target is equal to current page
if mw.title.equals( mwTitle, currentTitle ) then
return getIntro( title, linkText, appendedText, origMwTitle )
.. getError( title, errorMsgs.noContent )
end
local content = mwTitle:getContent()
content = replaceSubtemplates( content )
local sublist = frame:preprocess( content )
-- The page returns empty list
if isEmpty( content ) or isEmpty( sublist ) or mw.text.trim( sublist ) == '' then
return getIntro( title, linkText, appendedText, origMwTitle )
.. getError( title, errorMsgs.noContent )
end
-- Reject transclusions that look like articles
if mw.ustring.find( sublist, ' class="infobox' ) ~= nil
or mw.ustring.find( sublist, ' class="navbox' ) ~= nil then
return getIntro( title, linkText, appendedText, origMwTitle )
.. getError( title, errorMsgs.notDisambig )
end
-- Remove 3rd+ level subheadings for easier section insertion (before error checks)
sublist = mw.ustring.gsub( sublist, '\n(===+)(.-)%1\n-', '' )
-- Check sublist for wikitext markup errors
local hasErrors, errorType = checkForErrors( sublist )
-- Replace list markers with HTML list openers
sublist = gsubWikitextLists( '\n' .. sublist, '*', 'ul', 'li' )
sublist = gsubWikitextLists( '\n' .. sublist, '#', 'ol', 'li' )
-- Remove the bold text around links automatically
sublist = mw.ustring.gsub( sublist, "<li>%s*'''%s*%[%[([^%]]+)%]%]%s*'''", '<li>[[%1]]' )
-- Trim and replace double line breaks to avoid breaking the list
sublist = mw.text.trim( sublist )
sublist = mw.ustring.gsub( sublist, '\n\n', '<p>' )
-- Merge adjacent lists
sublist = mw.ustring.gsub( sublist, '</ul>\n-<ul>', '' )
-- Replace remaining
sublist = mw.ustring.gsub( sublist, '\n', '<br>' )
-- Disable nested checks
local notNestedCheck = isEmpty( args[ nestedCheckArg ] )
-- Check if the included page has more than two links
if linklessCat then
-- Two plain wikitext links
local linkCount = findTwice( mw.text.killMarkers( content ), '%*%s*\'*["«„]?\'*%[%[' )
if linkCount < 2 then
-- Two module invocations mean two different links
linkCount = linkCount + findTwice( content, moduleInvocationCode )
end
if linkCount < 2 then
-- [[Модуль:Не переведено]]
linkCount = linkCount
+ findTwice( sublist, ' версия статьи «' )
+ findTwice( sublist, ' title="Элемент статьи «' )
end
if linkCount < 2 then
-- Only add a category on the page itself
if notNestedCheck then
sublist = sublist .. string.format( '[[Category:%s]]', linklessCat )
end
addWarning( title, errorMsgs.noLinks )
end
end
-- Check if the included page has wrong type
if wrongTypeCat and notNestedCheck then
if noGoodCategories( mwTitle ) then
sublist = sublist .. string.format( '[[Category:%s]]', wrongTypeCat )
addWarning( title, errorMsgs.wrongType )
end
end
if hasErrors then
sublist = sublist .. string.format( '[[Category:%s|%s]]', errorCat, errorType )
addWarning( title, errorMsgs.hasErrors )
end
local intro = getIntro( title, linkText, appendedText, mwTitle )
return '<div class="ts-NL">' .. intro .. sublist .. '</div>'
end
return p
|
Index:
pl ar de en es fr it arz nl ja pt ceb sv uk vi war zh ru af ast az bg zh-min-nan bn be ca cs cy da et el eo eu fa gl ko hi hr id he ka la lv lt hu mk ms min no nn ce uz kk ro simple sk sl sr sh fi ta tt th tg azb tr ur zh-yue hy my ace als am an hyw ban bjn map-bms ba be-tarask bcl bpy bar bs br cv nv eml hif fo fy ga gd gu hak ha hsb io ig ilo ia ie os is jv kn ht ku ckb ky mrj lb lij li lmo mai mg ml zh-classical mr xmf mzn cdo mn nap new ne frr oc mhr or as pa pnb ps pms nds crh qu sa sah sco sq scn si sd szl su sw tl shn te bug vec vo wa wuu yi yo diq bat-smg zu lad kbd ang smn ab roa-rup frp arc gn av ay bh bi bo bxr cbk-zam co za dag ary se pdc dv dsb myv ext fur gv gag inh ki glk gan guw xal haw rw kbp pam csb kw km kv koi kg gom ks gcr lo lbe ltg lez nia ln jbo lg mt mi tw mwl mdf mnw nqo fj nah na nds-nl nrm nov om pi pag pap pfl pcd krc kaa ksh rm rue sm sat sc trv stq nso sn cu so srn kab roa-tara tet tpi to chr tum tk tyv udm ug vep fiu-vro vls wo xh zea ty ak bm ch ny ee ff got iu ik kl mad cr pih ami pwn pnt dz rmy rn sg st tn ss ti din chy ts kcg ve
Portal di Ensiklopedia Dunia