Permanently protected module

Module:Tag

From OpenStreetMap Wiki
Jump to navigation Jump to search

local p = {}
local getArgs = require('Module:Arguments').getArgs
local languages = require("Module:Languages")
local currentTitle = mw.title.getCurrentTitle()
local defaultLangCode = languages.languageFromTitle(currentTitle)

local function makeInvokeFunc(funcName)
	return function (frame)
		local args = getArgs(frame, {
			trim = true,
			removeBlanks = false,
		})
		return p[funcName](args)
	end
end

function pageLink(pageName, label, langCode)
	local title = mw.title.new(pageName)
	local validPageName
	if langCode and #langCode > 0 and title then
		validPageName = languages.translationPageName(langCode, title)
	elseif defaultLangCode ~= "en" and pageName ~= currentTitle.fullText and title then
		local translatedPageName = languages.translationPageName(defaultLangCode, title)
		if mw.title.new(translatedPageName).exists then
			validPageName = translatedPageName
		else
			validPageName = pageName
		end
	elseif title then
		validPageName = pageName
	end
	
	if validPageName then
		return "[[" .. validPageName .. "|" .. label .. "]]"
	else
		return label
	end
end

function valueLink(key, value, langCode)
	local pageName = "Tag:" .. key .. "=" .. value
	return pageLink(pageName, tostring(mw.html.create("bdi"):wikitext(value)), langCode)
end

p.value = makeInvokeFunc("_value")
function p._value(args)
	local langCode = args.kl or args.lang
	local key = args.key or args[1]
	
	local pageName
	if args.link == "value" or args.link == "tag" or (args[2] and #args[2] > 0) then
		if key == "type" then
			pageName = "Relation:" .. (args.value or args[2])
		else
			pageName = mw.ustring.format("Tag:%s=%s", key, args.value or args[2])
		end
	elseif args.link == "key#" or (args[3] and #args[3] > 0) then
		pageName = mw.ustring.format("Key:%s#%s-%s", key, key, args.value or args[3])
	elseif args.link ~= "none" and key then
		pageName = "Key:" .. key
	end
	
	local label = args.value or args[4] or args[3] or args[2] or mw.text.nowiki("*")
	label = mw.html.create("bdi"):wikitext(label)
	
	if pageName then
		return tostring(pageLink(pageName, tostring(label), langCode))
	else
		return tostring(label)
	end
end

function tag(args, includesValue)
	local components = {}
	local keyComponents = {}
	local keyComponentsToLink = {}
	
	-- Key
	local key = args[1]
	table.insert(keyComponents, key)
	table.insert(keyComponentsToLink, key)
	
	-- Give |subkey= precedence over |subkey1=.
	if args.subkey then
		table.insert(keyComponents, args.subkey)
	end
	
	-- Collect subkeys.
	local subkeyIndex = 2
	while args["subkey" .. subkeyIndex] do
		local subkey = args["subkey" .. subkeyIndex]
		table.insert(keyComponents, subkey)
		subkeyIndex = subkeyIndex + 1
	end
	
	-- Combine |subkey*= with |key=, but not for linking purposes.
	keyComponents = {
		table.concat(keyComponents, mw.text.nowiki(":")),
	}
	
	-- Collect subkeys to be linked separately.
	local subkeyIndex = 1
	while args[string.rep(":", subkeyIndex)] do
		local subkey = args[string.rep(":", subkeyIndex)]
		table.insert(keyComponents, subkey)
		table.insert(keyComponentsToLink, subkey)
		subkeyIndex = subkeyIndex + 1
	end
	
	-- Link the key and any subkeys.
	local linkedKeyComponents = {}
	for i, key in ipairs(keyComponentsToLink) do
		local langCode = args["kl" .. string.rep(":", i - 1)] or args.lang
		table.insert(linkedKeyComponents, pageLink("Key:" .. key, keyComponents[i], langCode))
	end
	table.insert(components, table.concat(linkedKeyComponents, ":"))
	
	components = {
		tostring(mw.html.create("bdi")
			:css("white-space", "nowrap")
			:wikitext(table.concat(components)))
	}
	if not includesValue then
		return table.concat(components)
	end
	
	table.insert(components, "=")
	
	-- Values
	local lastKeyComponent = keyComponents[#keyComponents]
	if args[2] and #args[2] > 0 then
		local values = {}
		if args[2] then
			table.insert(values, args[2])
		end
		if args[";"] or args.subval then
			table.insert(values, args[";"] or args.subval)
		end
		local subvalueIndex = 2
		while args[string.rep(";", subvalueIndex)] or args["subval" .. subvalueIndex] do
			local otherValue = args[string.rep(";", subvalueIndex)] or args["subval" .. subvalueIndex]
			if #otherValue > 0 then
				table.insert(values, otherValue)
			end
			subvalueIndex = subvalueIndex + 1
		end
		local linkedValues = {}
		for i, value in ipairs(values) do
			local langCode = args[i > 1 and ("vl" .. i) or "vl"] or args.lang
			table.insert(linkedValues, valueLink(lastKeyComponent, value, langCode))
		end
		table.insert(components, table.concat(linkedValues, ";"))
	elseif args[3] and #args[3] > 0 then
		local lastBaseKeyComponent = keyComponentsToLink[#keyComponentsToLink]
		local lastSubkey = lastKeyComponent:match("%w+$")
		local value = args[3]
		
		-- A wiki page title cannot contain a square bracket, so this is likely already a wikilink or external link.
		local isLiteralLink = (value:sub(1, 1)) == "["
		
		local pageName
		local url
		if not isLiteralLink then
			if lastSubkey == "wikipedia" or lastBaseKeyComponent == "wikipedia" then
				if lastSubkey ~= "wikipedia" then
					value = lastSubkey .. ":" .. value
				end
				pageName = "w:" .. value
			elseif lastSubkey == "wikidata" or lastBaseKeyComponent == "wikidata" then
				pageName = "d:" .. value
			elseif lastSubkey == "wikimedia_commons" or lastBaseKeyComponent == "wikimedia_commons" then
				pageName = "Commons:" .. value
			end
			
			if lastSubkey == "url" or lastBaseKeyComponent == "url" or lastSubkey == "website" or lastBaseKeyComponent == "website" then
				url = value
			end
		end
		
		if url then
			local label = mw.html.create("bdi")
				:css("white-space", "normal")
				:wikitext(mw.text.nowiki(url))
			table.insert(components, "[" .. url .. " " .. tostring(label) .. "]")
		elseif pageName then
			local label = mw.html.create("bdi"):wikitext(args[3])
			table.insert(components, pageLink(pageName, tostring(label), ""))
		else
			local label = mw.html.create("bdi"):wikitext(args[3])
			table.insert(components, tostring(label))
		end
	else
		table.insert(components, mw.text.nowiki("*"))
	end
	
	return table.concat(components)
end

p.key = makeInvokeFunc("_key")
function p._key(args)
	return tag(args, false)
end

p.tag = makeInvokeFunc("_tag")
function p._tag(args)
	return tag(args, true)
end

function p.keyComponents(key)
	if #key == 0 then
		return {}
	end
	
	local rawComponents = mw.text.split(key, ":", true)
	local resolvedComponents = {}
	local mostSpecificTitle
	local mostSpecificEntityId
	local mostSpecificDescription
	local seenCoreComponent = false
	for i, component in ipairs(rawComponents) do
		local base = table.concat(resolvedComponents, ":")
		if #base > 0 then
			base = base .. ":"
		end
		local key = mw.ustring.format("%s%s", base, component)
		local title
		local entityId
		
		-- First check if this component is a prefix.
		-- TODO: Require the prefix to precede any non-prefixes.
		local pageName = mw.ustring.format("Key:%s:*", key)
		title = mw.title.new(pageName)
		entityId = mw.wikibase.getEntityIdForTitle(pageName)
		
		-- How about a suffix?
		-- TODO: Require the suffix to follow any non-suffixes.
		if not entityId and not (title and title.exists) then
			local pageName = mw.ustring.format("Key:*:%s", key)
			title = mw.title.new(pageName)
			entityId = mw.wikibase.getEntityIdForTitle(pageName)
		end
		
		if not entityId and not (title and title.exists) then
			local pageName = mw.ustring.format("Key:%s", key)
			title = mw.title.new(pageName)
			entityId = mw.wikibase.getEntityIdForTitle(pageName)
		end
		
		if entityId or title.exists then
			local description = mw.wikibase.getDescription(entityId)
			if (description or not mw.ustring.find(key, ":")) and
					-- Avoid deprecated keys, which are less likely to be key components.
					(not entityId or #mw.wikibase.getBestStatements(entityId, "P17") == 0) then
				table.insert(resolvedComponents, component)
				mostSpecificTitle = title
				mostSpecificEntityId = entityId
				mostSpecificDescription = description
			else
				break
			end
		else
			break
		end
	end
	
	if #resolvedComponents == 0 then
		local component = rawComponents[1]
		if mw.language.isKnownLanguageTag(component) then
			table.insert(resolvedComponents, component)
			mostSpecificDescription = mw.ustring.format("[[w:ISO 639:%s|%s]]",
				component,
				mw.language.fetchLanguageName(component, defaultLangCode))
		end
	end
	
	local subkey = table.concat(rawComponents, ":", #resolvedComponents + 1)
	if #resolvedComponents == 0 then
		local component = {
			name = subkey,
		}
		return { component }
	end
	
	local superkey = {
		name = table.concat(resolvedComponents, ":"),
		title = mostSpecificTitle,
		entityId = mostSpecificEntityId,
		description = mostSpecificDescription,
	}
	resolvedComponents = p.keyComponents(subkey)
	table.insert(resolvedComponents, 1, superkey)
	return resolvedComponents
end

function p.keyComponentList(frame)
	-- Get arguments from the calling frame, falling back to its calling frame
	local args = frame.args[1] and frame.args or frame:getParent().args
	
	local key = args[1]
	local components = p.keyComponents(key)
	if #components < 2 then
		return ""
	end
	
	local listItems = {}
	for i, component in ipairs(components) do
		local tag
		if i == 1 then
			tag = frame:expandTemplate {
				title = "Tag",
				args = { component["name"] },
			}
		elseif component["title"] or component["entityId"] then
			tag = frame:expandTemplate {
				title = "Tag",
				args = {
					"*",
					[":"] = component["name"]
				},
			}
		else
			tag = frame:expandTemplate {
				title = "Value",
				args = {
					mw.ustring.format("&#x2a;&#x3a;%s=*", component["name"]),
				},
			}
		end
		local description = component.description
		local edit = component.entityId and frame:expandTemplate {
			title = "Edit",
			args = {
				"Item:" .. component.entityId,
			},
		} or ""
		if description then
			table.insert(listItems, mw.ustring.format("* %s: %s %s", tag, description, edit))
		else
			table.insert(listItems, mw.ustring.format("* %s %s", tag, edit))
		end
	end
	
	if args.intro and #args.intro > 0 then
		table.insert(listItems, 1, args.intro)
	end
	
	return table.concat(listItems, "\n")
end

return p