La documentazione per questo modulo può essere creata in Modulo:Common/man

--modulo contenente funzioni di utilità generale
local p = {}
local item = mw.wikibase.getEntityObject()
local lang = mw.getContentLanguage()

function p.setItem(newItem)
	item = newItem
end

function p.getItem()
	return item
end

--ritorna true se l'item che stiamo usando e' quello collegato alla pagina attuale
function p.isOwnItem()
	return mw.wikibase.getEntityObject().id == item.id
end

function p.getEntityIdForTitle(frame)
	return mw.wikibase.getEntityIdForTitle(frame.args[1])
end

-- *** FUNZIONI DI UTILITA' PER LE TABELLE **

-- numero di elementi in tabella
function p.size(T)
	local count = 0
	for _ in pairs(T) do count = count + 1 end
	return count
end

-- la tabella è vuota?
function p.empty(T)
	return T == nil or next(T) == nil
end

function p.first(T)
	if not p.empty(T) then return T[1] end
end

-- la tabella contiene il dato elemento?
function p.contains(T, el)
    for key, value in pairs(T) do
        if value == el then return true end
    end
    return false
end

function p.set(list)
  local s = {}
  for _, l in ipairs(list) do s[l] = true end
  return s
end

-- stampa la tabella (per i log)
function p.printTable(T, depth)
	depth = depth or 0
	local out = ''
	for k, v in pairs(T) do 
		out = out ..'\n' .. string.rep(' ', depth) .. k .. ' = ' .. p.printElement(v, depth)
	end
	return out
end

-- stampa un elemento qualunque
function p.printElement(el, depth)
	depth = depth or 0
	if (type(el) == "table") then return '{'..p.printTable(el, depth+1)..'}'
	else return tostring(el)
	end
end

--copia profonda di una tabella
function p.deepcopy(orig)
    local orig_type = type(orig)
    local copy
    if orig_type == 'table' then
        copy = {}
        for orig_key, orig_value in next, orig, nil do
            copy[p.deepcopy(orig_key)] = p.deepcopy(orig_value)
        end
        setmetatable(copy, p.deepcopy(getmetatable(orig)))
    else -- number, string, boolean, etc
        copy = orig
    end
    return copy
end

--fonde due tabelle in una terza (aggiungendo alle chiavi un diverso prefisso, se specificato)
function p.mergeTables(T1, T2, prefix1, prefix2)
	local T = {}
	prefix1 = prefix1 or ''
	prefix2 = prefix2 or ''
	for k, v in pairs(T1) do T[prefix1..k] = v	end
	for k, v in pairs(T2) do T[prefix2..k] = v	end
	return T
end

--sottrae dalla T1 gli elementi di T2
function p.subtractTable(T1, T2)
	for j, a in pairs(T2 or {}) do
		for k, v in pairs(T1) do
			if p.equalsIgnoreCase(a, v) then table.remove(T1, k) end
		end
	end
end

--inverte l'ordine degli elementi nella tabella
function p.reverseTable(t)
    local reversedTable = {}
    local itemCount = #t
    for k, v in ipairs(t) do
        reversedTable[itemCount + 1 - k] = v
    end
    return reversedTable
end

function p.sublist(t, i, j)
	local result = {}
	for k, v in ipairs(t) do
		if k >= i and k <= j then
			table.insert(result, v)
		end
	end
	return result
end

-- *** FUNZIONI DI UTILITA' PER LE STRINGHE  **
function p.notEmpty(String)
	return String and mw.text.trim(String) ~= ''
end

function p.startsWith(String,Start)
   return string.sub(String,1, string.len(Start))==Start
end

function p.endsWith(String,End)
   return End=='' or string.sub(String, -string.len(End))==End
end

function p.equalsIgnoreCase(str1, str2)
	return str1 and str2 and str1:lower() == str2:lower()
end

function p.replace()
	frame = mw.getCurrentFrame()
	str = frame.args[1]
	fnd = frame.args[2]
	res = string.gsub(str, fnd:gsub("[%(%)%.%%%+%-%*%?%[%^%$%]]", "%%%1"), "")
	return res
end

--concatena la lista di stringhe fornita
function p.concat(list, separator, lastSeparator)
	separator = separator or ', '
	lastSeparator = lastSeparator or separator
	return mw.text.listToText(list or {}, separator, lastSeparator)
end


-- *** FUNZIONI DI UTILITA' PER WIKISOURCE **

--ritorna il namespace corrente
function p.getNamespace() 
	return mw.title.getCurrentTitle().nsText
end

--true se siamo nel ns 0
function p.isNS0() 
	return mw.title.getCurrentTitle().namespace == 0
end

--true se siamo nel ns Autore
function p.isNSAutore() 
	return p.getNamespace() == 'Autore'
end

--true se siamo in Opera
function p.isNSOpera()
	return p.getNamespace() == 'Opera'
end

--true se siamo in una sottopagina
function p.isSubpage() 
	return mw.title.getCurrentTitle().isSubpage
end

--costruisce una categoria (o un link ad essa se link = true)
function p.category(name, label, link)
	label = label and '|'..label or ''	--default: stringa vuota
	link = (link and ':') or ''			--default: stringa vuota
	return '[['..link..mw.site.namespaces[14].name..':'..name..label..']]'
end

--costruisce un wikilink
function p.link(name, label, site)
	label = label or name
	site = site and ':'..site..':' or ''
	return '[['..site..name..'|'..label..']]'
end

--conta il numero di pagine in una data categoria
function p.pagesInCat(cat)
	return mw.site.stats.pagesInCategory(cat, 'pages')
end

-- richiama il template richiesto
function p.template(title, args)
	--bonifichiamo gli argomenti (se expandTemplate riceve una table, si spacca)
	cleanArgs = {}
	for i, v in pairs(args or {}) do
		if type(v) ~= "table" then cleanArgs[i] = v end
	end
	return mw.getCurrentFrame():expandTemplate{ title = title, args = cleanArgs }
end

--recupera i parametri passati al template che ha invocato questo modulo
function p.getParameters(frame)
	args = {}
	for k, v in pairs(frame:getParent().args) do
		if v and mw.text.trim(v) ~= '' then
			args[k] = mw.text.trim(v)
		end
	end
	return args
end

-- *** FUNZIONI SPECIFICHE PER WIKIDATA **

--ritorna la label per l'item corrente
function p.getLabel()
	if item and item.labels and item.labels.it then
		return  item.labels.it.value
	end
end

--ritorna la lista degli alias per l'item corrente
function p.getAliases()
	local aliasList = {}
	if item and item.aliases and item.aliases.it then
		for k, v in pairs(item.aliases.it) do
			if v and v.value then table.insert(aliasList, v.value) end
		end
	end
	return aliasList
end

--ritorna la label e tutti gli alias dell'item corrente
function p.getLabelAndAliases() 
	local aliases = p.getAliases()
	if item then 
		table.insert(aliases, 1, (item:getLabel())) 
	end
	return aliases
end

--ritorna il valore del claim escludendo i valori sconsigliati
function p.getClaimValue(claim)
	if claim and claim.mainsnak and claim.mainsnak.datavalue and claim.rank ~= 'deprecated' then
		dv = claim.mainsnak.datavalue
		v = dv.value
		if v then
			if dv.type == 'monolingualtext' then
				return v.text
			else
				return v
			end
		end
	end
end

--filtra i claims ritornando solo i "preferred" se ce ne sono, oppure solo i "normal", scartando in ogni caso i "deprecated"
function p.filterClaims(claims)
	local preferred = {}
	local normal = {}
	for index, claim in pairs(claims or {}) do
		if claim.rank == 'preferred' then
			table.insert(preferred, claim)
		elseif claim.rank == 'normal' then
			table.insert(normal, claim)
		end
	end
	if p.empty(preferred) then return normal
	else return preferred end
end

--ordina i claim per data (ascendente/discendente)
function p.sortClaimsByDate(claims, order)
	order = order or 'asc'  --asc/desc
	sortedClaims = {}
	while p.size(claims) > 0 do
		firstIndex = 1
		
		for index, claim in pairs(claims or {}) do
			thisTime = p.getClaimValue(claim)
			firstTime = p.getClaimValue(claims[firstIndex])
			
			if thisTime and thisTime.time and firstTime and firstTime.time then
				thisTimeAC = (thisTime.time:sub(1, 1) == '-')
				firstTimeAC = (thisTime.time:sub(1, 1) == '-')
				
				if order == 'asc' then
					if (not thisTimeAC and not firstTimeAC and thisTime.time < firstTime.time)
					or (thisTimeAC and firstTimeAC and thisTime.time > firstTime.time)
					or (not thisTimeAC and firstTimeAC) then
						firstIndex = index
					end
				else
					if (not thisTimeAC and not firstTimeAC and thisTime.time > firstTime.time)
					or (thisTimeAC and firstTimeAC and thisTime.time < firstTime.time)
					or (thisTimeAC and not firstTimeAC) then
						firstIndex = index
					end
				end
			end
		end
	
		table.insert(sortedClaims, claims[firstIndex])
		table.remove(claims, firstIndex)
	end
	return sortedClaims
end

-- ritorna i claim presenti per la property fornita
function p.getClaimsByProperty(property)
	if item and item.claims and item.claims[property] and #item.claims[property] >= 1 then
		return p.filterClaims(item.claims[property])
	end
end

-- ritorna il primo claim trovato per la property fornita
function p.getSingleClaimByProperty(property)
	return p.first(p.getClaimsByProperty(property))
end

-- ritorna il valore del primo claim trovato per la property fornita
function p.getSingleClaimValueByProperty(property)
	 return p.getClaimValue(p.getSingleClaimByProperty(property))
end

--data una property e l'item id di un elemento, ritorna il claim di quella property che ha come valore quell'elemento
function p.getClaimByPropertyAndValue(property, itemId)
	claims = p.getClaimsByProperty(property) or {}
	for i, claim in pairs(claims) do
		if 'Q'..claim.mainsnak.datavalue.value['numeric-id'] == itemId then
			return claim
		end
	end
end

--recupera il valore di un qualificatore da un claim
function p.getQualifierValueFromClaim(claim, qid)
	if claim and claim.qualifiers then
		qualifiers = claim.qualifiers[qid]
		if qualifiers and #qualifiers >= 1 then
			return qualifiers[1].datavalue.value
		end
	end
end
 
--ritorna l'etichetta di un valore
function p.getLabelFromValue(value)
	if value and value["numeric-id"] then return mw.wikibase.label('Q'..value["numeric-id"]) end
end

--ritorna il sitelink di un valore
function p.getLinkFromValue(value, defaultLink)
	if value and value['numeric-id'] then return mw.wikibase.sitelink('Q'..value['numeric-id']) or defaultLink end
end

--ritorna la lista dei valori della property fornita
function p.getClaimValuesByProperty(property)
	claims = p.getClaimsByProperty(property) or {}
	values = {}
	for k, claim in pairs(claims) do
		value = p.getClaimValue(claim)
		if value then table.insert(values, value) end
	end
	return values
end

--ritorna la lista delle etichette dei valori della property fornita
function p.getLabelsFromPropertyValues(property)
	claims = p.getClaimsByProperty(property) or {}
	labels = {}
	for k, claim in pairs(claims) do
		label = p.getLabelFromValue(p.getClaimValue(claim))
		if label then table.insert(labels, label) end
	end
	return labels
end

--dal valore fornito, recupera sitelink e label e crea un link alla pagina
function p.getWikiLinkFromValue(value, defaultLink)
	local link = p.getLinkFromValue(value, defaultLink)
	local nome = p.getLabelFromValue(value) or link
	if link then return p.link(link, nome) 
	else return nome or '' end
end

--data una lista di item autori, ritorna i link alle loro pagine separati da virgola
function p.getLinksAutori(autori)
	autoriLinkList = {}
	for k, autore in pairs(autori) do
		autoreNome = p.getLabelFromValue(autore)
		if autoreNome ~= nil and autoreNome ~= 'autore anonimo' then 
			autoreNome = lang:ucfirst(autoreNome) 
		else
			autoreNome = "Anonimo"
		end
		autoreLink = p.getLinkFromValue(autore, 'Autore:'..autoreNome)
		table.insert(autoriLinkList, p.link(autoreLink, autoreNome))
	end
	return p.concat(autoriLinkList, ', ')
end

--data una lista di item opere di cui altre opere sono parte, ritorna i link alle loro pagine separati da virgola
function p.getLinksParteOpere(ParteOpere)
	ParteOpereLinkList = {}
	for k, ParteOpera in pairs(ParteOpere) do
		ParteOperaNome = p.getLabelFromValue(ParteOpera)
		ParteOperaLink = p.getLinkFromValue(ParteOpera, 'Opera:'..ParteOperaNome)
		table.insert(ParteOpereLinkList, p.link(ParteOperaLink, ParteOperaNome))
	end
	return p.concat(ParteOpereLinkList, ', ')
end

--data una lista di item, ritorna i link alle loro pagine separati da virgola
function p.getLinks(items, defaultNS, separator)
	local list = {}
	defaultNS = (defaultNS and (defaultNS .. ':')) or ''
	for k, v in pairs(items) do
		local label = p.getLabelFromValue(v)
		local link = p.getLinkFromValue(v, defaultNS..label)
		table.insert(list, p.link(link, label))
	end
	return p.concat(list, separator)
end

--data una lista di item autori, ritorna l'elenco dei loro nomi
function p.getListaAutori(autori)
	autoriList = {}
	for k, autore in pairs(autori) do
		autoreNome = p.getLabelFromValue(autore)
		if autoreNome ~= nil and autoreNome ~= 'autore anonimo' then 
			autoreNome = lang:ucfirst(autoreNome) 
		else
			autoreNome = "Anonimo"
		end
		table.insert(autoriList, autoreNome)
	end
	return autoriList
end

--data una lista di item, ritorna la lista delle loro etichette
function p.getLabelsList(items)
	local list = {}
	for k, v in pairs(items) do
		table.insert(list, p.getLabelFromValue(v))
	end
	return list
end

--true se questo elemento è un'istanza di arg
function p.instanceof(arg)
	claims = p.getClaimsByProperty('P31') or {}
	for index, claim in pairs(claims) do
		local val = p.getClaimValue(claim)
		if val and val['numeric-id'] and arg == val['numeric-id'] then
			return true
		end
	end
	return false
end

-- Restituisce il collegamento corrispondente al codice fornito
function p.sitelink(dbname)
	if item then
		return item:getSitelink(dbname)
	end
end

-- collegamento a Wikipedia in italiano (o nelle lingue di fallback)
function p.wikipedia()
	local linkLang = nil
	local fallbackChain = {
		{'it', 'itwiki'},
		{'en', 'enwiki'},
		{'fr', 'frwiki'},
		{'de', 'dewiki'},
		{'es', 'eswiki'},
		{'pt', 'ptwiki'},
		{'ru', 'ruwiki'},
		{'el', 'elwiki'},
		{'nl', 'nlwiki'},
		{'rm', 'rmwiki'},
		{'ca', 'cawiki'},
		{'eml', 'emlwiki'},
		{'fur', 'furwiki'},
		{'lij', 'lijwiki'},
		{'lld', 'lldwiki'},
		{'lmo', 'lmowiki'},
		{'nap', 'napwiki'},
		{'pms', 'pmswiki'},
		{'roa-tara', 'roa_tarawiki'},
		{'sc', 'scwiki'},
		{'scn', 'scnwiki'},
		{'vec', 'vecwiki'}
	}
	for _, site in pairs(fallbackChain) do
		local sitelink = p.sitelink(site[2])
		if sitelink then
			return site[1], sitelink
		end
	end
end

-- collegamento a Wikiquote in italiano
function p.wikiquote()
	return p.sitelink('itwikiquote')
end

-- collegamento a Wikibooks
function p.wikibooks()
	return p.sitelink('itwikibooks')
end

-- collegamento a Wikinotizie
function p.wikinews()
	return p.sitelink('itwikinews')
end

-- collegamento a Wikizionario
function p.wiktionary()
	return p.sitelink('itwiktionary')
end

-- collegamento a Wikiversità
function p.wikiversity()
	return p.sitelink('itwikiversity')
end

-- collegamento a Wikivoyage
function p.wikivoyage()
	return p.sitelink('itwikivoyage')
end

-- collegamento a Wikispecies
function p.wikispecies()
	return p.sitelink('specieswiki')
end

-- collegamento a Commons (per gallerie)
function p.commons()
	return p.sitelink('commonswiki')
end

-- collegamento a Commons (categorie)
function p.commonscat()
	return p.getSingleClaimValueByProperty('P373')
end
-- *** FUNZIONI VARIE **

--converte il numero dato a numero romano
function p.toRoman(num)
    local t = {
        {1000, "M"},
        {900, "CM"}, {500, "D"}, {400, "CD"}, {100, "C"},
        {90, "XC"}, {50, "L"}, {40, "XL"}, {10, "X"},
        {9, "IX"}, {5, "V"}, {4, "IV"}, {1, "I"} 
    }
    local ret = {}
    for _, v in ipairs(t) do
        local val, letter = unpack(v)
        while num >= val do
            num = num - val
            table.insert(ret, letter)
        end
    end
    return table.concat(ret), num
end

--fa il parsing di una data per ottenere le sue componenti
--formati di input possibili: 
--1° gennaio 2017
--25 dicembre 96 a.C.
--1877/1878
--format: anno, secolo, giornoMese
function p.parseDate(frame)
	date = frame.args[1]
	retFormat = frame.args[2]
	num = frame.args[3]
	return p.parseDateInternal(date, retFormat, num)
end
	
function p.parseDateInternal(date, retFormat, num)
	date = date:gsub(" %- ", "/"):gsub("%-", "/")
	if num then
		index = 1
		for part in date:gmatch("[^/]+") do 
    		if index == tonumber(num) then
    			return p.parseDateInternal(part, retFormat)
    		end
    		index = index + 1
		end
	end
	
	day, month, year = date:match("([%d]+.*) (%w+) (%d+.*)")
	if not day then
		year = date:match("(%d+.*)")
	end
	
	if year then
		yearDigits, yearBC = year:match("(%d+)(.*)")
		century = p.toRoman(math.ceil(yearDigits / 100)) .. ' secolo' .. yearBC
		
		if retFormat == 'anno' then
			return year
		elseif retFormat == 'secolo' then
			return century
		elseif retFormat == 'giornoMese' and day and month then
			return day .. ' ' .. month
		end
	end
	return date
end

--per stripAccents()
local tableAccents = {}
	tableAccents["à"] = "a"
	tableAccents["á"] = "a"
	tableAccents["â"] = "a"
	tableAccents["ã"] = "a"
	tableAccents["ä"] = "a"
	tableAccents["ç"] = "c"
	tableAccents["è"] = "e"
	tableAccents["é"] = "e"
	tableAccents["ê"] = "e"
	tableAccents["ë"] = "e"
	tableAccents["ì"] = "i"
	tableAccents["í"] = "i"
	tableAccents["î"] = "i"
	tableAccents["ï"] = "i"
	tableAccents["ñ"] = "n"
	tableAccents["ò"] = "o"
	tableAccents["ó"] = "o"
	tableAccents["ô"] = "o"
	tableAccents["õ"] = "o"
	tableAccents["ö"] = "o"
	tableAccents["ø"] = "o"
	tableAccents["ù"] = "u"
	tableAccents["ú"] = "u"
	tableAccents["û"] = "u"
	tableAccents["ü"] = "u"
	tableAccents["ý"] = "y"
	tableAccents["ÿ"] = "y"
	tableAccents["À"] = "A"
	tableAccents["Á"] = "A"
	tableAccents["Â"] = "A"
	tableAccents["Ã"] = "A"
	tableAccents["Ä"] = "A"
	tableAccents["Č"] = "C"
	tableAccents["Ç"] = "C"
	tableAccents["Ć"] = "C"
	tableAccents["È"] = "E"
	tableAccents["É"] = "E"
	tableAccents["Ê"] = "E"
	tableAccents["Ë"] = "E"
	tableAccents["Ì"] = "I"
	tableAccents["Í"] = "I"
	tableAccents["Î"] = "I"
	tableAccents["Ï"] = "I"
	tableAccents["Ñ"] = "N"
	tableAccents["Ò"] = "O"
	tableAccents["Ó"] = "O"
	tableAccents["Ô"] = "O"
	tableAccents["Õ"] = "O"
	tableAccents["Ö"] = "O"
	tableAccents["Ø"] = "O"
	tableAccents["Ś"] = "S"
	tableAccents["Ù"] = "U"
	tableAccents["Ú"] = "U"
	tableAccents["Û"] = "U"
	tableAccents["Ü"] = "U"
	tableAccents["Ý"] = "Y"
 
-- Strip accents from a string
function p.stripAccents(str)
	local normalizedString = ""
	for strChar in string.gfind(str, "([%z\1-\127\194-\244][\128-\191]*)") do
		normalizedString = normalizedString .. (tableAccents[strChar] or strChar)
	end
	return normalizedString
end

--dato il PID di una proprietà Wikidata, mostra un link ad essa e l'etichetta associata
function p.propertyDetails(frame)
	pid = frame:getParent().args[1]
	item = mw.wikibase.getEntity(pid)
	label = ''
	if item and item.labels and item.labels.it then
		label = ' ("'..item.labels.it.value..'")'
	end
	return '[[:d:Property:'..pid..'|'..pid..']]'..label
end

-- rimuovi eventuali parentesi dal titolo
function p.stripTitolo2(titolo)
	titolo = titolo:gsub(".*/", ""):gsub("_", " ")
		-- mantieni la parentesi se seguita da una lettera minuscola
		:gsub("%((%l)", "_%1")
		:gsub(" %(.*%)", "")
		:gsub("_", "(")
	return titolo
end
function p.stripTitolo(frame)
	return p.stripTitolo2(frame:getParent().args[1])
end

return p