Module:Annotated link
Jump to navigation
Jump to search
Documentation for this module may be created at Module:Annotated link/doc
local function pipedLink(name, display) return '[[:'..name..'|'..display..']]' end local function isEmpty(value) return value == nil or value == '' end local function notEmpty(value) return not isEmpty(value) end -- Unescape functionality grabbed from https://stackoverflow.com/a/14899740/1832568 local function unescape(str) str = string.gsub(str, '&#(%d+);', string.char) str = string.gsub(str, '&#x(%d+);', function(d) return string.char(tonumber(d, 16)) end) return str end local function hashDelimitedList(list_string) return mw.text.gsplit(unescape(list_string), '%s*#%s*') end local function alarmingMessage(message) return '<span style="color:#d33">[[Module:Annotated link]] '..message..'.</span>'.. '[[Category:Pages displaying alarming messages about Module:Annotated link]]' end local function optionallyVisibleCategory(class, category) return '<span style="display:none" class="'..class..'">'..category.. '</span>[[Category:'..category..' via Module:Annotated link]]' end local function handleFirstLetterCase(short_description, case) return mw.ustring.gsub(short_description, '^([^%d])', function(first_char) if case == 'upper' then return mw.ustring.upper(first_char) end return mw.ustring.lower(first_char) end ) end local mLang = require('Module:Lang') local function langify(args) local lang = args.lang local text = args.text if isEmpty(lang) or lang == 'en' then return text end return mLang._lang { lang, text, italic = args.italic, nocat = args.nocat, size = args.size, cat = args.cat, rtl = args.rtl } end local function formatResult(result, dash, description, prefix_parentheses) if notEmpty(description) then if prefix_parentheses then local startIdx = description:find("%(") if startIdx then local beforeParens = description:sub(1, startIdx - 2) local insideParens = description:sub(startIdx, -1) return result..' '..insideParens..dash..' '..beforeParens end end return result..dash..' '..description end return result end local function annotatedLink(args) local name = args.name if isEmpty(name) then return alarmingMessage('requires a page name (including namespace)') end -- In order to handle an attempt to annotate a template link -- already formatted with the likes of {{tl|<template name>}}; -- unescape name to make sense of braces in lua patern matching. name = unescape(name) if name:match('^{%b{}}$') then -- The possibility to extract useful data exists here: e.g. {{tl*|Template}}. return alarmingMessage( 'requires only a page name (including namespace) without markup. '.. 'If an attempt is being made to annotate a link to a template, '.. 'provide only the template name with namespace e.g. "Template:Example"') end -- If a literal link was provided as name; -- extract the content and apply it to name and display as appropriate. local wikilink = mw.ustring.match(name, '^%[%[%s*:*%s*(.-)%s*%]%]$') if wikilink then local link_name, link_display = unpack(mw.text.split(wikilink, '%s*|%s*')) if link_name then name = link_name end if link_display and isEmpty(args.display) then args.display = link_display end end -- Prepare to concatenate. local result local is_template = name:match('^Template:(.+)$') local template_link = args.template_link if is_template and template_link ~= 'no' then result = '{{'..pipedLink(name, is_template)..'}}' if template_link == 'code' then result = '<code>'..result..'</code>' end else local display = args.display if isEmpty(display) then display = name end result = langify({ lang = args.link_lang, text = pipedLink(name, display), italic = args.link_lang_italic, nocat = args.link_lang_nocat, size = args.link_lang_size, cat = args.link_lang_cat, rtl = args.link_lang_rtl }) if notEmpty(args.quote) then result = '"'..result..'"' end local abbr = args.abbr if notEmpty(abbr) then result = result..' (<abbr' local abbr_title = args.abbr_title if notEmpty(abbr_title) then result = result..' title="'..abbr_title..'"' end result = result..'>'..abbr..'</abbr>)' end end if isEmpty(result) then return alarmingMessage('could not create a link for "'..name..'"') end local aka = args.aka if notEmpty(aka) then result = result..', also known as '..langify({ lang = args.aka_lang, text = aka, italic = args.aka_lang_italic, nocat = args.aka_lang_nocat, size = args.aka_lang_size, cat = args.aka_lang_cat, rtl = args.aka_lang_rtl }) end local wedge = args.wedge if notEmpty(wedge) then result = result..', '..langify({ lang = args.wedge_lang, text = wedge, italic = args.wedge_lang_italic, nocat = args.wedge_lang_nocat, size = args.wedge_lang_size, cat = args.wedge_lang_cat, rtl = args.wedge_lang_rtl }) end -- Exclude wikidata fallback for any specified list of link titles, -- unless explicity instructed that it's okay. local not_wikidata_for_links_starting_with = args.not_wikidata_for_links_starting_with if isEmpty(args.wikidata) and notEmpty(not_wikidata_for_links_starting_with) then for only_explicit in hashDelimitedList(not_wikidata_for_links_starting_with) do if name:match('^'..only_explicit) then args.only = 'explicit' break end end end -- Get the short description from Module:GetShortDescription. local short_description = require('Module:GetShortDescription').main({ none_is_valid = args.none_is_valid, none_is_nil = args.none_is_nil, lang_italic = args.desc_lang_italic, lang_nocat = args.desc_lang_nocat, lang_size = args.desc_lang_size, lang_cat = args.desc_lang_cat, lang_rtl = args.desc_lang_rtl, lang_no = args.desc_lang_no, prefer = args.prefer, only = args.only, name = name }) local dash = args.dash if isEmpty(dash) then dash = ' –' end local fallback = args.fallback if isEmpty(short_description) or short_description.redlink then return formatResult(result, dash, fallback, args.prefix_parentheses) end if short_description.alarm then return short_description.alarm end local maintenance = '' if short_description.redirected then maintenance = optionallyVisibleCategory( 'category-annotation-with-redirected-description', 'Pages displaying short descriptions of redirect targets') end local fellback if short_description.wikidata then if short_description.fellback then fellback = true maintenance = maintenance..optionallyVisibleCategory( 'category-wikidata-fallback-annotation', 'Pages displaying wikidata descriptions as a fallback') end short_description = short_description.wikidata -- Filter against likely rubbish wikidata descriptions. local not_wikidata_descriptions_including = args.not_wikidata_descriptions_including if notEmpty(not_wikidata_descriptions_including) then -- Case insentive matching. local lower_case_short_description = short_description:lower() for exclusion in hashDelimitedList(not_wikidata_descriptions_including:lower()) do if lower_case_short_description:match(exclusion) then short_description = '' break end end end if isEmpty(short_description) then return formatResult(result, dash, fallback, args.prefix_parentheses) end else short_description = short_description.explicit end local lower_case_name = name:lower() if notEmpty(short_description) and not short_description:match(' ') then -- Filter against likely rubbish single word descriptions. local lower_case_short_description = short_description:lower() local not_single_word = args.not_single_word if notEmpty(not_single_word) then -- Case insentive matching. for single_word in hashDelimitedList(not_single_word:lower()) do if single_word == lower_case_short_description then short_description = '' break end end end if isEmpty(short_description) or lower_case_name:match(lower_case_short_description) then return formatResult(result, dash, fallback, args.prefix_parentheses) end if isEmpty(args.space_cat) then maintenance = maintenance..optionallyVisibleCategory( 'category-spaceless-annotation', 'Pages displaying short descriptions with no spaces') end end if lower_case_name == short_description:lower() then if fellback then return formatResult(result, dash, fallback, args.prefix_parentheses) end maintenance = maintenance..optionallyVisibleCategory( 'category-annotation-matches-name', 'Pages displaying short descriptions matching their page name') end -- Short descriptions on en Wikipedia should be formatted with an uppercase first letter, but -- the typical application of this module will require the first character to be lowercase, but -- some descriptions may start with proper names and should start with an uppercase letter even if used in an annotaion. -- By default; this module will not affect the first letter case of descriptions retrieved by Module:GetShortDescription, but -- the first letter case may be transformed explicitly if required. local desc_first_letter_case = args.desc_first_letter_case if desc_first_letter_case == 'upper' or desc_first_letter_case == 'lower' then short_description = handleFirstLetterCase(short_description, desc_first_letter_case) end return formatResult(result, dash, (short_description or fallback)..maintenance, args.prefix_parentheses) end local p = {} function p.main(frame) local args = require('Module:Arguments' ).getArgs(frame) if isEmpty(args) then return alarmingMessage('could not getArgs') -- This really would be alarming. end return annotatedLink(args) end return p