Module:ExcerptHeaderLevel
local p = {}
local parser = require('Module:WikitextParser')
local function getArg(args, key, fallback)
local v = args[key]
if v == nil or v == '' then
return fallback
end
return v
end
local function tonumberOrNil(v)
local n = tonumber(v)
if n then
return n
end
return nil
end
local function tonumberOrZero(v)
local n = tonumber(v)
if n then
return n
end
return 0
end
local function clamp(n, low, high)
if n < low then
return low
elseif n > high then
return high
end
return n
end
local function trim(s)
return mw.text.trim(tostring(s or ''))
end
local function isYes(v)
v = trim(v):lower()
return v == '1' or v == 'yes' or v == 'y' or v == 'true'
end
local function parseHeadingLine(line)
local eq1, title, eq2 = tostring(line):match('^(=+)%s*(.-)%s*(=+)%s*$')
if eq1 and eq2 and eq1 == eq2 then
return {
level = #eq1,
title = title
}
end
return nil
end
local function isMarkerOnlyLine(line)
return tostring(line):match('^%s*=+%s*$') ~= nil
end
local function rebuildHeading(title, level)
title = trim(title):gsub('[\r\n]+', ' ')
level = clamp(level, 2, 6)
local eq = string.rep('=', level)
return eq .. ' ' .. title .. ' ' .. eq
end
local function adjustHeading(line, shift)
local h = parseHeadingLine(line)
if not h then
return line
end
return rebuildHeading(h.title, h.level + shift)
end
local function adjustHeadingRelativeToTop(line, originalTopLevel, desiredTopLevel)
local h = parseHeadingLine(line)
if not h then
return line
end
local relativeDepth = h.level - originalTopLevel
return rebuildHeading(h.title, desiredTopLevel + relativeDepth)
end
local function makeTopHeading(title, level)
return rebuildHeading(title, level)
end
local function splitLines(text)
local lines = {}
text = tostring(text or '')
for line in (text .. '\n'):gmatch('(.-)\n') do
table.insert(lines, line)
end
return lines
end
local function stripLeadingGarbage(lines, sectionTitle)
local normalizedTitle = trim(sectionTitle)
local removedSomething = true
while removedSomething and #lines > 0 do
removedSomething = false
while #lines > 0 and trim(lines[1]) == '' do
table.remove(lines, 1)
removedSomething = true
end
if #lines == 0 then
return
end
local first = trim(lines[1])
local second = trim(lines[2] or '')
local h = parseHeadingLine(first)
if h and trim(h.title) == normalizedTitle then
table.remove(lines, 1)
removedSomething = true
elseif isMarkerOnlyLine(first) then
table.remove(lines, 1)
removedSomething = true
if #lines > 0 and trim(lines[1]) == normalizedTitle then
table.remove(lines, 1)
end
elseif first == normalizedTitle then
table.remove(lines, 1)
removedSomething = true
elseif second == normalizedTitle and isMarkerOnlyLine(first) then
table.remove(lines, 1)
table.remove(lines, 1)
removedSomething = true
end
end
end
local function stripTrailingBlankLines(lines)
while #lines > 0 and trim(lines[#lines]) == '' do
table.remove(lines, #lines)
end
end
local function makeExcerptHatnote(page, section)
local displaySection = section:gsub('%[%[([^]|]+)|?[^]]*%]%]', '%1')
local link = '[[:' .. page .. '#' .. mw.uri.anchorEncode(section) .. '|' .. page .. ' § ' .. displaySection .. ']]'
local title = mw.title.new(page)
local editUrl = title and title:fullUrl('action=edit') or nil
local hat = 'These paragraphs are an excerpt from ' .. link .. '.'
if editUrl then
hat = hat
.. '<span class="mw-editsection-like plainlinks"><span class="mw-editsection-bracket">[</span>['
.. tostring(editUrl) .. ' ' .. mw.message.new('editsection'):plain()
.. ']<span class="mw-editsection-bracket">]</span></span>'
end
return tostring(
mw.html.create('div')
:addClass('dablink')
:addClass('excerpt-hat')
:wikitext(hat)
)
end
local function escapeString(str)
return tostring(str):gsub('[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0')
end
local function removeString(text, str)
local pattern = escapeString(str)
if #pattern > 9999 then
pattern = escapeString(mw.ustring.sub(str, 1, 999)) .. '.-' .. escapeString(mw.ustring.sub(str, -999))
end
return text:gsub(pattern, '')
end
local function 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, ',')
for _, value in pairs(values) do
value = mw.text.trim(value)
local min, max = mw.ustring.match(value, '^(%d+)%s*[-–—]%s*(%d+)$')
if not max then
min, max = string.match(value, '^((%d+))$')
end
if max then
for i = min, max do
filters[i] = true
end
else
filters[value] = true
end
end
return { cache = {}, terms = filters }, isBlacklist
end
local function 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
return false
end
end
local function filterTemplates(wikitext, filter)
if not filter or filter == '' then
return wikitext
end
local filters, isBlacklist = parseFilter(filter)
local templates = parser.getTemplates(wikitext)
for index, template in pairs(templates) do
local name = parser.getTemplateName(template)
if
(isBlacklist and (matchFilter(index, filters) or matchFilter(name, filters)))
or
(not isBlacklist and (not matchFilter(index, filters) and not matchFilter(name, filters)))
then
wikitext = removeString(wikitext, template)
end
end
return wikitext
end
local function getRawSectionWikitext(page, section, includeSubsections)
local title = mw.title.new(page)
if not title then
return nil, 'invalid-title'
end
if title.isRedirect then
title = title.redirectTarget
end
if not title or not title.exists then
return nil, 'page-not-found'
end
local wikitext = title:getContent()
if not wikitext or wikitext == '' then
return nil, 'page-empty'
end
local excerpt = parser.getSectionTag(wikitext, section)
if not excerpt then
if includeSubsections then
excerpt = parser.getSection(wikitext, section)
else
local sections = parser.getSections(wikitext)
excerpt = sections[section]
end
end
if not excerpt or excerpt == '' then
return nil, 'section-not-found'
end
return excerpt
end
function p.main(frame)
local args = frame:getParent().args
local page = trim(getArg(args, 1, getArg(args, 'page', nil)))
local section = trim(getArg(args, 2, getArg(args, 'section', nil)))
if page == '' or section == '' then
return '<strong class="error">Template:ExcerptHeaderLevel requires both a page and a section heading.</strong>'
end
local includeTopHeader = isYes(getArg(args, 'include-top-header', 'yes'))
local showExcerptNoticeDefault = includeTopHeader and 'yes' or 'no'
local showExcerptNotice = isYes(getArg(args, 'show-excerpt-notice', showExcerptNoticeDefault))
local includeSubsections = isYes(getArg(args, 'subsections', 'yes'))
local templateFilter = getArg(args, 'templates', nil)
local topHeaderLevel = tonumberOrNil(getArg(args, 'top-header-level', nil))
local shift
if topHeaderLevel then
topHeaderLevel = clamp(topHeaderLevel, 2, 6)
else
shift = tonumberOrZero(getArg(args, 'shift', 0))
local higher = tonumberOrZero(getArg(args, 'higher', 0))
local lower = tonumberOrZero(getArg(args, 'lower', 0))
shift = shift - higher + lower
end
local raw, err = getRawSectionWikitext(page, section, includeSubsections)
if not raw then
return '<strong class="error">ExcerptHeaderLevel error: ' .. tostring(err) .. '.</strong>'
end
if templateFilter and templateFilter ~= '' then
raw = filterTemplates(raw, templateFilter)
end
raw = raw:gsub('<[Nn][Oo][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>.-</[Nn][Oo][Ii][Nn][Cc][Ll][Uu][Dd][Ee]>', '')
raw = raw:gsub('\n\n\n+', '\n\n')
raw = mw.text.trim(raw)
raw = '\n' .. raw .. '\n'
raw = frame:preprocess(raw)
local lines = splitLines(raw)
stripLeadingGarbage(lines, section)
local out = {}
if showExcerptNotice then
table.insert(out, makeExcerptHatnote(page, section))
end
if topHeaderLevel then
if includeTopHeader then
table.insert(out, makeTopHeading(section, topHeaderLevel))
end
for _, line in ipairs(lines) do
table.insert(out, adjustHeadingRelativeToTop(line, 2, topHeaderLevel + 1))
end
else
if includeTopHeader then
table.insert(out, makeTopHeading(section, clamp(2 + shift, 2, 6)))
end
for _, line in ipairs(lines) do
table.insert(out, adjustHeading(line, shift))
end
end
stripTrailingBlankLines(out)
return table.concat(out, '\n')
end
return p
Content Disclaimer
Informasi ini disarikan dari Wikipedia dan disajikan kembali untuk tujuan edukasi. Konten tersedia di bawah lisensi CC BY-SA 3.0. Kami tidak bertanggung jawab atas ketidakakuratan data yang bersumber dari kontribusi publik tersebut.
- The information displayed on this website is sourced in part or in whole from Wikipedia and has been adapted for the purpose of restating it. We strive to provide accurate and relevant information, however:
- There is no guarantee of absolute accuracy. Wikipedia is an open, collaborative project that can be edited by anyone, so information is subject to change.
- It is not intended to constitute professional advice. The content displayed is for informational and educational purposes only. For important decisions (e.g., medical, legal, or financial), please consult a professional.
- Content copyright. Wikipedia is licensed under the Creative Commons Attribution-ShareAlike License (CC BY-SA). This means that content may be reused with appropriate attribution and shared under a similar license.
- Responsible use. Any risk arising from the use of information from this website is entirely the responsibility of the user.