Modulo:Transcluder: differenze tra le versioni
Contenuto cancellato Contenuto aggiunto
←Nuova pagina: local p = {} -- Helper function to test for truthy and falsy values local function truthy(value) if not value or value == '' or value == 0 or value == '0' or value ==... |
Update from master using #Synchronizer |
||
(9 versioni intermedie di 3 utenti non mostrate) | |||
Riga 1:
-- Module:Transcluder is a general-purpose transclusion engine
-- Documentation and master version: https://en.wikipedia.org/wiki/Module:Transcluder
-- Authors: User:Sophivorus, User:Certes & others
-- License: CC-BY-SA-3.0
local p = {}
-- Helper function to test for truthy and falsy values
-- @todo Somehow internationalize it
local function truthy(value)
if not value or value == '' or value == 0 or value == '0' or value == 'false' or value == 'no' or value == 'non' then
return false
end
Line 9 ⟶ 15:
end
-- Helper function to match from a list of regular expressions
-- Like so: match pre..list[1]..post or pre..list[2]..post or ...
local function matchAny(text, pre, list, post, init)
Line 20 ⟶ 26:
end
-- Like matchAny but for Category/File links with less overhead
local function matchAnyLink(text, list)
local match
for _, v in ipairs(list) do
match = string.match(text, '%[%[%s*' .. v .. '%s*:.*%]%]')
if match then break end
end
return match
end
-- Helper function to escape a string for use in regexes
local function escapeString(str)
return
end
Line 30 ⟶ 47:
pattern = escapeString(mw.ustring.sub(str, 1, 999)) .. '.-' .. escapeString(mw.ustring.sub(str, -999))
end
return
end
Line 36 ⟶ 53:
-- @param flags 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
local function parseFlags(value)
local flags = {}
Line 45 ⟶ 62:
if type(value) == 'number' then
if value < 0 then
value =
blacklist = true
end
Line 51 ⟶ 68:
elseif type(value) == 'string' then
if
blacklist = true
value =
end
local ranges = mw.text.split(value, ',') -- split ranges: '1,3-5' to {'1','3-5'}
for _, range in pairs(ranges) do
range = mw.text.trim(range)
local min, max = mw.ustring.match(range, '^(%d+)%s*
if not max then
if max then
for
else
flags[range] = true -- if we reach this point, the string had the form 'a,b,c' rather than '1,2,3'
Line 67 ⟶ 84:
end
-- List has the form { [1] = false, [2] = true, ['c'] = false }
-- Convert it to { [1] = true, [2] = true, ['c'] = true }
-- But if ANY value is set to false, treat the list as a blacklist
elseif type(value) == 'table' then
for i, v in pairs(value) do
if v == false then blacklist = true end
Line 83 ⟶ 99:
-- Helper function to see if a value matches any of the given flags
local function matchFlag(value, flags)
if
value = tostring(value)
local lang = mw.language.getContentLanguage()
local lcvalue = lang:lcfirst(value)
local ucvalue = lang:ucfirst(value)
for flag in pairs(flags) do
if
or
or ucvalue == flag
or ( not tonumber(flag) and mw.ustring.match(value, flag) ) then
return true
end
Line 125 ⟶ 145:
-- @return Local name of the namespace and all aliases, for example {'File','Image','Archivo','Imagen'}
local function getNamespaces(name)
local namespaces = mw.clone(mw.site.namespaces[name].aliases) -- Clone because https://en.wikipedia.org/w/index.php?diff=1056921358
table.insert(namespaces, mw.site.namespaces[name].name)
table.insert(namespaces, mw.site.namespaces[name].canonicalName)
Line 135 ⟶ 155:
-- For file pages, returns the content of the file description page
local function getText(page, noFollow)
page = mw.text.decode(page)
local title = mw.title.new(page)
if not title then return false, false end
Line 145 ⟶ 166:
-- Remove <noinclude> tags
text =
-- Keep <onlyinclude> tags
if
text = text
:gsub('</onlyinclude>.*', '') -- remove text after last onlyinclude section
end
Line 157 ⟶ 179:
end
-- Get the requested files
-- @param text Required. Wikitext to parse.
-- @param flags Range of files to return, for example 2 or '1,3-5'. Omit to return all files.
-- @return Sequence of strings containing the wikitext of the requested files.
-- @return Original wikitext minus
local function getFiles(text, flags)
local files = {}
Line 168 ⟶ 190:
local name
local count = 0
for file in
if
name =
count = count + 1
if not blacklist and ( not flags or flags[count] or matchFlag(name, flags) )
Line 184 ⟶ 206:
end
-- Get the requested tables
-- @param text Required. Wikitext to parse.
-- @param flags Range of tables to return, for example 2 or '1,3-5'. Omit to return all tables.
-- @return Sequence of strings containing the wikitext of the requested tables.
-- @return Original wikitext minus
local function getTables(text, flags)
local tables = {}
Line 194 ⟶ 216:
local id
local count = 0
for t in
if
id =
count = count + 1
if not blacklist and ( not flags or flags[count] or flags[id] )
Line 209 ⟶ 231:
end
-- Get the requested templates
-- @param text Required. Wikitext to parse.
-- @param flags Range of templates to return, for example 2 or '1,3-5'. Omit to return all templates.
-- @return Sequence of strings containing the wikitext of the requested templates.
-- @return Original wikitext minus
local function getTemplates(text, flags)
local templates = {}
Line 219 ⟶ 241:
local name
local count = 0
for template in
if
name = mw.text.trim(
count = count + 1
if not blacklist and ( not flags or flags[count] or matchFlag(name, flags) ) or blacklist and flags and not flags[count] and not matchFlag(name, flags) then
table.insert(templates, template)
else
text = removeString(text, template)
end
end
end
Line 234 ⟶ 258:
end
-- Get the requested template parameters
-- @param text Required. Wikitext to parse.
-- @param flags Range of parameters to return, for example 2 or '1,3-5'. Omit to return all parameters.
-- @return Map from parameter name to value, NOT IN THE ORIGINAL ORDER
-- @return Original wikitext minus
-- @return Order in which the parameters were parsed.
local function getParameters(text, flags)
local parameters, parameterOrder = {}, {}
local flags, blacklist = parseFlags(flags)
local params, count, parts, key, value
for template in
params =
if params then
count = 0
-- Temporarily replace pipes in subtemplates
for subtemplate in
params =
end
for link in
params =
end
for parameter in mw.text.gsplit(params, '|') do
parts = mw.text.split(parameter, '=')
key = mw.text.trim(parts[1])
value = key
count = count + 1
key = count
else
value = mw.text.trim(
end
value =
if not blacklist and ( not flags or matchFlag(key, flags) )
or blacklist and flags and not matchFlag(key, flags) then
table.insert(parameterOrder, key)
parameters[key] = value
else
Line 275 ⟶ 300:
end
end
return parameters, text, parameterOrder
end
-- Get the requested lists
-- @param text Required. Wikitext to parse.
-- @param flags Range of lists to return, for example 2 or '1,3-5'. Omit to return all lists.
-- @return Sequence of strings containing the wikitext of the requested lists.
-- @return Original wikitext minus
local function getLists(text, flags)
local lists = {}
local flags, blacklist = parseFlags(flags)
local count = 0
for list in
count = count + 1
if not blacklist and ( not flags or flags[count] )
Line 299 ⟶ 324:
end
-- Get the requested paragraphs
-- @param text Required. Wikitext to parse.
-- @param flags Range of paragraphs to return, for example 2 or '1,3-5'. Omit to return all paragraphs.
-- @return Sequence of strings containing the wikitext of the requested paragraphs.
-- @return Original wikitext minus
local function getParagraphs(text, flags)
local paragraphs = {}
Line 313 ⟶ 338:
elements, temp = getLists(temp, 0) -- remove lists
elements, temp = getFiles(temp, 0) -- remove files
temp = mw.
:gsub('\n%b{} *\n', '\n%0\n') -- add spacing between tables and block templates ))
-- Assume that anything remaining is a paragraph
local count = 0
for paragraph in mw.text.gsplit(temp, '\n\n+') do
if mw.text.trim(paragraph) ~= '' then
count = count + 1
Line 336 ⟶ 361:
end
-- Get the requested categories
-- @param text Required. Wikitext to parse.
-- @param flags Range of categories to return, for example 2 or '1,3-5'. Omit to return all categories.
-- @return Sequence of strings containing the wikitext of the requested categories.
-- @return Original wikitext minus
local function getCategories(text, flags)
local categories = {}
Line 347 ⟶ 372:
local name
local count = 0
for category in
if
name =
count = count + 1
if not blacklist and ( not flags or flags[count] or matchFlag(name, flags) )
Line 362 ⟶ 387:
end
-- Get the requested references
-- @param text Required. Wikitext to parse.
-- @param flags Range of references to return, for example 2 or '1,3-5'. Omit to return all references.
-- @return Sequence of strings containing the wikitext of the requested references.
-- @return Original wikitext minus
local function getReferences(text, flags)
local references = {}
-- Remove all references, including citations, when 0 references are requested
-- This is kind of hacky but currently necessary because the rest of the code
-- doesn't remove citations like <ref name="Foo" /> if Foo is defined elsewhere
if flags and not truthy(flags) then
text = string.gsub(text, '<%s*[Rr][Ee][Ff][^>/]*>.-<%s*/%s*[Rr][Ee][Ff]%s*>', '')
text = string.gsub(text, '<%s*[Rr][Ee][Ff][^>/]*/%s*>', '')
return references, text
end
local flags, blacklist = parseFlags(flags)
local name
local count = 0
for reference in
name =
count = count + 1
if not blacklist and ( not flags or flags[count] or
or blacklist and flags and not flags[count] and not
table.insert(references, reference)
else
text = removeString(text, reference)
if name then
for citation in
text = removeString(text, citation)
end
Line 390 ⟶ 425:
end
-- Get the lead section
-- @param text Required. Wikitext to parse.
-- @return Wikitext of the lead section.
local function getLead(text)
text =
text = mw.text.trim(text)
if not text then return throwError('lead-empty') end
Line 400 ⟶ 435:
end
-- Get the
-- @param text Required. Wikitext to parse.
-- @param flags Range of sections to return, for example 2 or '1,3-5'. Omit to return all
-- @return Sequence of strings containing the wikitext of the requested sections.
-- @return Original wikitext minus
local function getSections(text, flags)
local sections = {}
Line 410 ⟶ 445:
local count = 0
local prefix, section, suffix
for title in
count = count + 1
prefix, section, suffix =
if not blacklist and ( not flags or flags[count] or matchFlag(title, flags) )
or blacklist and flags and not flags[count] and not matchFlag(title, flags) then
sections[title] = section
else
text =
text =
end
end
Line 424 ⟶ 459:
end
-- Get the requested section
-- @param text Required. Wikitext to parse.
-- @param section Required. Title of the section to get (in wikitext), for example 'History' or 'History of [[Athens]]'.
Line 432 ⟶ 467:
local escapedSection = escapeString(section)
-- First check if the section title matches a <section> tag
if
text = mw.text.trim((text
:gsub('<%s*[Ss]ection%s+end=%s*["\']?%s*'.. escapedSection ..'%s*["\']?%s*/>.*', '') -- remove text after last section tag
))
if text == '' then return throwError('section-tag-empty', section) end
return text
end
local level, text =
if not text then return throwError('section-not-found', section) end
local nextSection = '\n==' ..
text =
text = mw.text.trim(text)
if text == '' then return throwError('section-empty', section) end
Line 473 ⟶ 509:
refBody = mw.ustring.match(full, '<%s*[Rr][Ee][Ff][^>]*name%s*=%s*["\']?%s*' .. refName .. '%s*["\']?[^/>]*>.-<%s*/%s*[Rr][Ee][Ff]%s*>')
if refBody then -- the ref body was found elsewhere
text = mw.ustring.gsub(text, '<%s*[Rr][Ee][Ff][^>]*name%s*=%s*["\']?%s*' .. refName .. '%s*["\']?[^>]*/?%s*>', mw.ustring.gsub(refBody, '%%', '%%%%'), 1)
end
end
Line 481 ⟶ 517:
end
end
page = string.gsub(page, '"', '') -- remove any quotation marks from the page title
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]%s*group%s*=%s*["\']?[^"\'>/]+["\']%s*>', '<ref>')
Line 487 ⟶ 524:
-- Replace the bold title or synonym near the start of the page by a link to the page
local function linkBold(text, page)
local lang = mw.language.getContentLanguage()
local position = mw.ustring.find(text, "'''" .. lang:ucfirst(page) .. "'''", 1, true) -- look for "'''Foo''' is..." (uc) or "A '''foo''' is..." (lc)
Line 501 ⟶ 538:
return nil -- instruct gsub to make no change
end
end
return text
Line 514 ⟶ 551:
local fileDescription
local frame = mw.getCurrentFrame()
for file in
if
fileName = 'File:' ..
fileDescription, fileName = getText(fileName)
if fileName then
Line 522 ⟶ 559:
fileDescription = frame:preprocess('{{' .. fileName .. '}}') -- try Commons
end
if fileDescription and
text = removeString(text, file)
end
Line 532 ⟶ 569:
-- Remove any self links
local function removeSelfLinks(text)
local lang = mw.language.getContentLanguage()
local page = escapeString(
text = text
:gsub('%[%[(' .. lcpage .. ')%]%]', '%1')
:gsub('%[%[' .. ucpage .. '|([^]]+)%]%]', '%1')
:gsub('%[%[' .. lcpage .. '|([^]]+)%]%]', '%1')
return text
end
-- Remove all wikilinks
local function removeLinks(text)
text = text
:gsub('%[%[[^%]|]+|([^]]+)%]%]', '%1')
:gsub('%[%[([^]]+)%]%]', '%1')
:gsub('%[[^ ]+ ([^]]+)%]', '%1')
:gsub('%[([^]]+)%]', '%1')
return text
end
-- Remove HTML comments
local function removeComments(text)
text = string.gsub(text, '<!%-%-.-%-%->', '')
return text
end
-- Remove behavior switches, such as __NOTOC__
local function removeBehaviorSwitches(text)
text = string.gsub(text, '__[A-Z]+__', '')
return text
end
-- Remove bold text
local function removeBold(text)
text = string.gsub(text, "'''", '')
return text
end
Line 546 ⟶ 614:
if not options then options = {} end
-- Make sure the page exists
if not page then return throwError('no-page') end
page = mw.text.trim(page)
page = mw.text.decode(page)
if page == '' then return throwError('no-page') end
local page, hash, section =
local text,
if not
page = temp
if not text then return throwError('page-not-found', page) end
local full = text -- save the full text for fixReferences below
Line 563 ⟶ 634:
-- Keep only the requested elements
local elements
if options.only then
if options.only == 'sections' then elements = getSections(text, options.sections) end
if options.only == 'lists' then elements = getLists(text, options.lists) end
if options.only == 'files' then elements = getFiles(text, options.files) end
if options.only == 'tables' then elements = getTables(text, options.tables) end
if options.only == 'templates' then elements = getTemplates(text, options.templates) end
if options.only == 'parameters' then elements = getParameters(text, options.parameters) end
if options.only == 'paragraphs' then elements = getParagraphs(text, options.paragraphs) end
if options.only == 'categories' then elements = getCategories(text, options.categories) end
if options.only == 'references' then elements = getReferences(text, options.references) end
text = ''
if elements then
for key, element in pairs(elements) do
end
end
end
-- Filter the requested elements
if options.sections and options.only ~= 'sections' then elements, text = getSections(text, options.sections) end
if options.lists and options.only ~= 'lists' then elements, text = getLists(text, options.lists) end
if options.files and options.only ~= 'files' then elements, text = getFiles(text, options.files) end
if options.tables and options.only ~= 'tables' then elements, text = getTables(text, options.tables) end
if options.templates and options.only ~= 'templates' then elements, text = getTemplates(text, options.templates) end
if options.parameters and options.only ~= 'parameters' then elements, text = getParameters(text, options.parameters) end
if options.paragraphs and options.only ~= 'paragraphs' then elements, text = getParagraphs(text, options.paragraphs) end
if options.categories and options.only ~= 'categories' then elements, text = getCategories(text, options.categories) end
if options.references and options.only ~= 'references' then elements, text = getReferences(text, options.references) end
-- Misc options
if truthy(options.fixReferences) then text = fixReferences(text, page, full) end
if truthy(options.linkBold) and not truthy(section) then text = linkBold(text, page) end
if truthy(options.
if truthy(options.noLinks) then text = removeLinks(text) end
if truthy(options.noSelfLinks) then text = removeSelfLinks(text) end
if truthy(options.
if truthy(options.noBehaviorSwitches) then text =
if truthy(options.noComments) then text =
-- Remove multiple newlines left over from removing elements
text =
text = mw.text.trim(text)
Line 628 ⟶ 687:
local ok, text = pcall(get, page, args)
if not ok then return getError(text) end
local raw = args['raw']
if raw then return text end
return frame:preprocess(text)
end
Line 656 ⟶ 717:
function p.matchFlag(value, flags) return matchFlag(value, flags) end
function p.getNamespaces(name) return getNamespaces(name) end
function p.removeBold(text) return removeBold(text) end
function p.removeLinks(text) return removeLinks(text) end
function p.removeSelfLinks(text) return removeSelfLinks(text) end
function p.removeNonFreeFiles(text) return removeNonFreeFiles(text) end
function p.removeBehaviorSwitches(text) return removeBehaviorSwitches(text) end
function p.removeComments(text) return removeComments(text) end
return p
|