Modul:Wikidata

De la Wikipedia, enciclopedia liberă

Acest modul folosește pentru accesarea datelor din structurile de la Wikidata. Desigur, există {{#property:}} cu care se poate accesa o proprietate a itemului curent, dar de cele mai multe ori este nevoie de acțiuni mai complexe.

Convenții

Funcțiile al căror nume începe cu get sunt dedicate formatelor și paginilor, și primesc ca argument un frame (care se transmite automat la apelul din pagini sau formate). Cele al căror nume începe cu find sunt dedicate celorlalte module, și primesc ca argumente direct valorile necesare. De regulă, cele din urmă doar extrag argumentele, apoi le apelează pe cele dintâi cu argumentele extrase.

Funcții exportate

Funcție Argumente Comportament
descriptionIn

frame cuprinzând ca argumente:

  1. codul limbii
  2. id-ul itemului de Wikidata
Extrage descrierea unui item într-o limbă. Limba este specificată de primul argument și poate lipsi, caz în care se folosește limba wikiului; id-ul itemului poate lipsi, caz în care se folosește itemul asociat paginii curente.
labelIn

frame cuprinzând ca argumente:

  1. codul limbii
  2. id-ul itemului de Wikidata
Extrage eticheta unui item într-o limbă. Limba este specificată de primul argument și poate lipsi, caz în care se folosește limba wikiului; id-ul itemului poate lipsi, caz în care se folosește itemul asociat paginii curente.
getEntityId Extrage id-ul entității curente.
findLinkToItem
  • entityId - id-ul entității
  • capitalize - flag care cere scrierea cu inițială majusculă
  • feminine - flag care cere căutarea formei feminine
  • shortestAlias - flag care cere căutarea unei abrevieri
Creează un link către articolul despre o altă entitate decât cea în pagina în care ne aflăm. Se poate cere scrierea etichetei linkului cu inițială majusculă (dacă regulile gramaticale o impun — cazul în care linkul se pune la începutul unui titlu sau al unei fraze); în forma de feminin (de exemplu, pentru meserii, dacă ele sunt referite ca practicate de o femeie); sau cu cea mai scurtă variantă posibilă (dacă se dorește o abreviere). Oricare din ultimele trei flaguri poate lipsi, caz în care ele sunt considerate a avea valoarea false.
findLinkToItemWithLabelFromClaim
  • entityId - id-ul entității
  • capitalize - flag care cere scrierea cu inițială majusculă
  • propertyId - identifică proprietatea folosită pentru extragerea etichetei linkului
Ca și findLinkToItem, doar că generalizează modul de obținere a etichetei: se poate preciza o proprietate a entității de la care se va obține eticheta
findLinkToItemWithLabelFromAnyClaim
  • entityId - id-ul entității
  • capitalize - flag care cere scrierea cu inițială majusculă
  • propertyIdsTable - listă de proprietăți folosite pentru extragerea etichetei linkului
Ca și findLinkToItemWithLabelFromClaim, doar că generalizează și mai mult modul de obținere a etichetei: se poate preciza o listă de proprietăți ale entității care sunt verificate pe rând până se găsește una care are o valoare
printSnak
  • snak - elementul din structura Wikidata
Afișează un „snak” (element polimorf din structura de date de la Wikidata). Este utilă atunci când într-un modul se procesează date preluate în mod direct din structurile de date de la Wikidata. Acest snak poate fi o dată calendaristică, id-ul unui alt item Wikidata, un șir de caractere etc. Funcția ia decizia a ce afișează în funcție de tipul snakului.
outputReferences Creează note de subsol conținând referințele bibliografice asociate unei afirmații. Notele de subsol sunt evitate dacă referința este către un alt proiect Wikipedia. Numele refului se calculează automat pentru a putea fi reutilizat de la o instanță la alta.
getValueList
  • entityId - id-ul entității; dacă este nil, e vorba de entitatea curentă
  • propertyId - id-ul proprietății
Întoarce un table format din toate valorile cu rang de la normal în sus pentru proprietatea și entitatea specificată.
getValueListLimited
  • entityId - id-ul entității; dacă este nil, e vorba de entitatea curentă
  • propertyId - id-ul proprietății
  • limitVal - numărul de itemi de obținut; dacă este negativ sau nil, este considerat nelimitat
Întoarce un table cu cel mult limitVal elemente format din valori cu rang de la normal în sus pentru proprietatea și entitatea specificată și un int ce reprezintă numărul total de valori ce ar fi fost întoarse dacă nu exista limitVal
printTimestampForClaim Întoarce, între paranteze, datele temporale asociate afirmației:
  • simpla specificare a datei dacă e dată prin proprietatea P585 (point in time)
  • „din” urmat de specificarea datei dacă este definită doar proprietatea P580 (start time)
  • „până la” urmat de specificarea datei dacă este definită doar proprietatea P582 (end time)
  • specificarea datelor date de proprietățile P580 și P582 separate de en-dash (–) dacă sunt definite ambele
getTimestampedValueList
  • entityId - id-ul entității; dacă este nil, e vorba de entitatea curentă
  • propertyId - id-ul proprietății
Întoarce un table format din valorile pe care le-ar returna funcția getValueList, fiecare urmată de valoarea pe care ar returna-o, pentru fiecare, funcția printTimestampForClaim apelată cu claimul asociat fiecăreia.
findValueListWithQualifiersInBrackets
  • entityId - id-ul entității; dacă este nil, e vorba de entitatea curentă
  • propertyId - id-ul proprietății
  • best - true/false dacă se dorește sau nu limitarea doar la cele mai bune valori (ignoră valorile cu rang normal dacă există valori cu rang preferat)
  • bracketTemplate - un format pentru datele din paranteză, unde codul fiecărei proprietăți este prefixat de semnul $. De exemplu, formatul $P580-$P582 înseamnă dată de început-dată de sfârșit
Întoarce un table format din valorile pe care le-ar returna funcția getValueList, fiecare urmată de o paranteză conținând calificativele sale de la Wikidata formatate după template-ul specificat ca al patrulea parametru.
getValueListWithSeparator Un frame, având pe prima poziție separatorul, pe a doua id-ul de proprietate și pe a treia id-ul de entitate Returnează o listă ale cărei elemente sunt separate prin separatorul specificat, elementele fiind valorile pe care le-ar returna funcția getValueList apelată cu id-urile de proprietate și entitate specificate.
_getValueListWithSeparator Ca și getValueListWithSeparator, doar că primește direct table-ul de argumente, nu frame-ul Ca și getValueListWithSeparator.
getTimestampedValueListWithSeparator Un frame, având pe prima poziție separatorul, pe a doua id-ul de proprietate și pe a treia id-ul de entitate Returnează o listă ale cărei elemente sunt separate prin separatorul specificat, elementele fiind valorile pe care le-ar returna funcția getTimestampedValueList apelată cu id-urile de proprietate și entitate specificate.
_getTimestampedValueListWithSeparator Ca și getTimestampedValueListWithSeparator, doar că primește direct table-ul de argumente, nu frame-ul Ca și getTimestampedValueListWithSeparator.
getEntityIdsList
  • entityId - id-ul entității; dacă este nil, e vorba de entitatea curentă
  • propertyId - id-ul proprietății
Întoarce un table conținând id-urile de entitate, cu rank cel puțin normal, către care trimite proprietatea specificată a entității specificate.
getBestEntityIdsList
  • entityId - id-ul entității; dacă este nil, e vorba de entitatea curentă
  • propertyId - id-ul proprietății
Întoarce un table conținând id-urile de entitate, cu rank cel mai mare, către care trimite proprietatea specificată a entității specificate.
findClaimsForProperty
  • entityId - id-ul entității; dacă este nil, e vorba de entitatea curentă
  • propertyId - id-ul proprietății
Întoarce un table conținând claimurile pure ce reprezintă afirmațiile asociate proprietății specificate a entității specificate.
findBestClaimsForProperty
  • entityId - id-ul entității; dacă este nil, e vorba de entitatea curentă
  • propertyId - id-ul proprietății
Întoarce un table conținând claimurile pure ce reprezintă afirmațiile cu cel mai bun rang, asociate proprietății specificate a entității specificate.
findSortedClaimsForProperty
  • entityId - id-ul entității; dacă este nil, e vorba de entitatea curentă
  • propertyId - id-ul proprietății
Întoarce un table conținând claimurile pure ce reprezintă afirmațiile asociate proprietății specificate a entității specificate, sortate în funcție de calificatorul start time (P580) asociat lor.
findQualifierValueListForClaim Întoarce un table conținând reprezentarea valorilor asociate calificatorului specificat pentru claimul specificat.
getQualifierValue Un frame conținând în tabelul args, în ordine, id-ul proprietății și id-ul calificatorului Întoarce reprezentarea tuturor calificatorilor cu id-ul specificat pentru proprietatea cu id-ul specificat a entității curente. Calificatoarele sunt separate prin virgulă și spațiu.
getRawValue Un frame având pe prima poziție a tabelului args valoare id-ului proprietății. Întoarce reprezentarea dată de funcția formatPropertyValues a entitătii curente, apelată cu id-ul de proprietate specificat.
getRawQualifierValue Un frame având pe prima poziție a tabelului args valoare id-ului proprietății și pe a doua id-ul unui calificator. Întoarce reprezentarea simplă a valorii (dacă valoarea este id-ul unui alt element, aceasta este labelul acelui element) tuturor calificatoarelor cu id-ul specificat ale id-ului proprietății cu id-ul specificat a entității curente. Reprezentările sunt separate prin virgulă.
findDateValues
  • propertyId - id-ul proprietății
  • entityId - id-ul entității; dacă este nil, e vorba de entitatea curentă
Întoarce un table ale cărui elemente sunt la rândul lor table-uri reprezentând datele cu rangul cel mai bun asociate proprietății cu id-ul specificat al entității cu id-ul specificat. Proprietățile fiecărui element din table-ul ce reprezintă data sunt:
  • day - ziua din lună
  • month - luna din an, întreg între 1 și 12
  • year - anul, poate fi valoare negativă pentru datele dinaintea erei noastre
  • claim - claimul brut asociat datei, pentru eventuale procesări ulterioare
  • precision - precizia, în codificarea Wikidata
  • calendarmodel - modelul calendarului, ca URL de entitate
getDateValue Un frame conținând în tabelul args pe prima poziție id-ul de proprietate, pe a doua id-ul de entitate (nil înseamnă entitatea curentă), pe a treia un format de dată (implicit dmy) și pe a patra un sufix pentru datele dinaintea erei noastre Întoarce o listă a datelor cu rang cel puțin normal asociate id-ului de proprietate și id-ului de entitate specificate, formatate după cum specifică formatul de dată și sufixul, separate prin virgulă și spațiu.
pageId Întoarce id-ul din Wikidata al elementului asociat paginii curente
getPreferredValue Un frame conținând în tabelul args pe prima poziție id-ul de proprietate și pe a doua id-ul de entitate (nil înseamnă entitatea curentă) Întoarce, separate prin virgulă, valorile cu rang preferred pentru entitatea și proprietatea specificate.
ViewSomething Un frame conținând în tabelul args o serie de elemente care împreună constituie o cale de urmat prin modelul de date de la Wikidata Întoarce data existentă pe calea specificată.
Dump Un frame conținând în tabelul args o serie de elemente care împreună constituie o cale de urmat prin modelul de date de la Wikidata Întoarce datele existente pe calea specificată; dacă datele sunt un tabel, el se expandează și se afișează toate datele din el. Funcția este de folosit pentru debug și pentru analiză
findOneValue
  • propertyId - id-ul proprietății
  • entityId - id-ul entității sau însăși entitatea; dacă este nil, e vorba de entitatea curentă
Returnează o singură valoare din cele cu rangul cel mai înalt cu referințele corespunzătoare pentru o proprietate specificată, împreună cu note de subsol care reprezintă referințele bibliografice. Se apelează din alte module.
findOneValueNoRef
  • propertyId - id-ul proprietății
  • entityId - id-ul entității sau însăși entitatea; dacă este nil, e vorba de entitatea curentă
Același lucru ca findOneValue, dar fără referințe bibliografice.
getOneValue Un frame conținând în tabelul args pe prima poziție id-ul de proprietate și pe a doua id-ul de entitate (nil înseamnă entitatea curentă) Returnează o singură valoare din cele cu rangul cel mai înalt cu referințele corespunzătoare pentru o proprietate specificată, împreună cu note de subsol care reprezintă referințele bibliografice.
getOneValueNoRef Un frame conținând în tabelul args pe prima poziție id-ul de proprietate și pe a doua id-ul de entitate (nil înseamnă entitatea curentă) Același lucru ca getOneValue, dar fără referințe bibliografice.
getLatestQualifierDateValueForOneProperty Un frame conținând în tabelul args pe prima poziție id-ul de proprietate și pe a doua id-ul qualifierului Returnează valoarea cea mai recentă a qualifierului specificat pentru proprietea specificată a entității curente
findLatestQualifierDateValueForOneProperty
  • propertyId - id-ul proprietății
  • qualifierId - id-ul qualifierului
Același lucru ca și getLatestQualifierDateValueForOneProperty, doar că de apelat din alte module
getReferenceForOneProperty Un frame conținând în tabelul args pe prima poziție id-ul proprietății Returnează un text ce conține referința pentru o proprietate a entității curente; de apelat din formate
findLabel
  • entityId - id-ul entității
  • lang - codul limbii
Returnează labelul unei entități în limba specificată; dacă nu se găsește, atunci se încearcă în limba wikiului și apoi în limba engleză
getLabel Un frame conținând în tabelul args pe prima poziție id-ul de entitate (nil înseamnă entitatea curentă) și pe a doua codul limbii Ca și findLabel, dar apelabil din formate sau pagini
findBestValues
  • propertyId - id-ul proprietății
  • entity - id-ul entității sau însăși entitatea; dacă este nil, e vorba de entitatea curentă
Returnează o listă a valorilor cu rangul cel mai înalt cu referințele corespunzătoare pentru o proprietate specificată. Apelabil din module.
getBestValuesWithSeparator Un frame conținând în tabelul args pe prima poziție un separator, pe a doua id-ul proprietății și pe a treia id-ul de entitate (nil înseamnă entitatea curentă). Returnează valorile cu rangul cel mai înalt și cu referințele corespunzătoare pentru o proprietate specificată, despărțite prin separatorul specificat
findLanguageText
  • propertyId - id-ul de proprietate
  • langcode - codul limbii
  • entityId - id-ul de entitate
Întoarce labelul unei entități asociate proprietății specificate a entității specificate, în limba specificată; dacă nu se specifică entityId, se folosește entitatea curentă; dacă nu se specifică langcode, se caută întâi în limba wikiului și apoi în limba engleză
getLanguageText Un frame conținând în tabelul args pe prima poziție id-ul de proprietate, pe a doua codul limbii și pe a treia id-ul de entitate Același lucru ca și findLanguageText, doar că apelabilă din formate și pagini
findClaimForTimestamp
  • entityId - id-ul entității; dacă este nil, e vorba de entitatea curentă
  • propertyId - id-ul proprietății
  • timestamp - un obiect de tip dată (cu valori numerice corespunzătoare în câmpurile day, month, year, precision)
  • lang - opțional: dacă tipul de date căutat este multi-language, atunci se caută această limbă; dacă lipsește, se caută româna
Claimul din entitatea specificată, corespunzător proprietății specificate, cu valori ale calificatorilor pentru proprietățile start time și end time care cuprind între ele data specificată de timestamp. Dacă timestamp lipsește, se returnează primul claim întâlnit cu rangul cel mai mare.
findOnlineLinks
  • onlinelinksprops - un table ce conține id-uri de proprietăți care să fie explorate în vederea extragerii de legături externe
  • entityId - id-ul entității
Un table ce conține legăturile web formatate cu tot cu titlu, pentru entitatea specificată
getOnlineLinks Un frame ce conține pe pozițiile numerice valorile proprietăților de căutat, iar pe poziția qid ID-ul entității asociate; dacă nu este precizat, se consideră entitatea asociată paginii curente Ca și findOnlineLinks, doar că lista este concatenată cu fiecare link pe câte o linie.
findTitleOfWork entityId - id-ul entității Titlul unei opere așa cum ar trebui afișat aici. Se caută, în ordine: eticheta în română, titlul în română, titlul în limba originară, eticheta în limba originară, eticheta în engleză
findTitlesOfWorksFromProperty
  • propertyId - id-ul proprietății
  • entityId - id-ul entității
Caută cu ajutorul funcției findTitleOfWork de mai sus titlurile unor opere listate în proprietatea specificată a entității specificate și le returnează ca intrări într-un table; titlurile sunt linkuri trimit spre articolul sau itemul wikidata corespunzător operei;
findBestValuesForLocalLanguage
  • propertyId - id-ul proprietății
  • entityId - id-ul entității
Returnează o listă a valorilor de cel mai mare rang asociate limbii române prin intermediul unui calificator cu proprietatea limba operei sau a denumirii (P407).
getBestValuesForLocalLanguageWithSeparator
  • sep - un separator
  • propertyId - id-ul proprietății
  • entityId - id-ul entității
ca mai sus, doar că apelabilă din format; primește mai întâi un separator și apoi id-urile
getTitleOfWorkFromPropertyWithSeparator Un frame ce conține următoarele argumente:
  • sep - un separator
  • propertyId - id-ul proprietății
  • entityId - id-ul entității
Ca și findTitlesOfWorksFromProperty, dar primește frame, returnează lista serializată cu separatori și cu titlurile italicizate
loadOneValueInChain Un număr teoretic nelimitat de argumente înlănțuite care descriu o cale prin structurile Wikidata. Implicit, punctul de început este elementul Wikidata asociat paginii curente. Dacă un argument este un ID de element Wikidata, atunci acela este stabilit ca punct de început. Dacă este un ID de proprietate, atunci se trece la valoarea proprietății respective pentru elementul curent. Dacă este un ID de proprietate precedat de _ atunci se trece la calificatorul asociat proprietății respective asociate snakului curent. În cele două cazuri din urmă se folosește la fiecare pas doar un singur snak, de rangul cel mai mare. Vezi și {{Date înlănțuite de la Wikidata}}. Forma textuală a snakului final de pe lanț; nil dacă nu există date sau lanțul nu poate fi parcurs până la destinație
getOneValueInChain Ca și mai sus, doar că apelabil cu un frame Ca și mai sus
isA
  • entityId - ID-ul unei entități
  • typeIds - un table care conține o listă de ID-uri de entități
true dacă entitatea cu id-ul entityId are unul din tipurile cu ID-ul din colecția typeIds sau unul din subtipurile directe sau indirecte ale acestuia; false altfel
formatExternalLink Formatează o proprietate a cărei valoare este un link extern
  • pId - ID-ul de proprietate
  • qId - ID-ul de entitate
  • text - textul care să apară pe link în locul valorii proprietății
getFormattedExternalLink Ca și mai sus, apelabilă cu un frame Ca și mai sus, apelabilă cu un frame; pId poate fi dat ca primul parametru fără nume; qId poate fi dat ca al doilea parametru fără nume
findAnniversary
  • propertyId - id-ul proprietății
  • entityId - id-ul entității
  • step - un număr întreg reprezentând numărul de ani la care se consideră o aniversare
Întoarce numărul de ani trecuți de la evenimentul descris în proprietate dacă acesta este multiplu de step, 0 altfel
getAnniversary Ca și mai sus, doar că apelabil cu un frame Ca și mai sus

local getArgs = require('Module:Arguments').getArgs
local SepEntries = require('Module:Separated entries')
local StringUtils = require('Modul:StringUtils')
local DateUtils = require('Modul:DateUtils')
local GregorianDate = require('Modul:GregorianDate')
local Citation = require('Modul:Citation/CS1')
local TableTools = require('Modul:TableTools')
local LangUtils = require('Modul:LangUtils')
local plural = require('Modul:Plural').build_plural 
local Transliteration = require('Modul:Transliteration')
local Set = require('Modul:Set')
local p = {}
local lang = mw.language.getContentLanguage()
local libraryUtil = require('libraryUtil')

local wdReferences = {}

-- internationalisation
local i18n = {
    ["errors"] = {
        ["property-not-found"] = "Proprietatea nu a fost găsită.",
        ["entity-not-found"] = "Itfemul Wikidata nu a fost găsit.",
        ["unknown-claim-type"] = "Tip de afirmație negăsit.",
        ["unknown-snak-type"] = "Tip de Snak negăsit.",
        ["unknown-datavalue-type"] = "Tip de date negăsit.",
        ["unknown-entity-type"] = "Tip de item negăsit.",
        ["qualifier-not-found"] = "Calificator negăsit.",
        ["site-not-found"] = "Proiect Wikimedia negăsit.",
    },
    ["somevalue"] = "O valoare",
    ["novalue"] = "Nicio valoare",
    ["datetime"] =
	{
		-- $1 is a placeholder for the actual number
		[0] = "$1 miliarde de ani",		-- precision: billion years
		[1] = "$100 milioane de ani",	-- precision: hundred million years
		[2] = "$10 milioane de ani",	-- precision: ten million years
		[3] = "$1 milioane de ani",		-- precision: million years
		[4] = "$100.000 de ani",	-- precision: hundred thousand years
		[5] = "$10.000 de ani",		-- precision: ten thousand years
		[6] = "mileniul $1", 	-- precision: millennium
		[7] = "secolul $1",	-- precision: century
		[8] = "deceniul anilor $1",				-- precision: decade
		-- the following use the format of #time parser function
		[9]  = "Y",					-- precision: year, 
		[10] = "F Y",				-- precision: month
		[11] = "j F Y",			-- precision: day
		[12] = 'j F Y, "orele" G',	-- precision: hour
		[13] = "j F Y G:i",		-- precision: minute
		[14] = "j F Y G:i:s",		-- precision: second
		["beforenow"] = "acum $1",	-- how to format negative numbers for precisions 0 to 5
		["afternow"] = "peste $1",		-- how to format positive numbers for precisions 0 to 5
		["bc"] = '$1 "î.e.n."',		-- how print negative years
		["ad"] = "$1"				-- how print positive years
	},
	["monolingualtext"] = '<span lang="%language">%text</span>',
	["warnDump"] = "[[Categorie:Apel de funcție 'Dump' din modulul Wikidata]]"
}

local function propAndEntity(arg1, arg2)
	local propId, entId
	for _,eachArg in ipairs({arg1, arg2}) do
		if mw.ustring.match(eachArg, 'Q%d+') == eachArg then
			entId = eachArg
		elseif mw.ustring.match(eachArg, 'P%d+') == eachArg then
			propId = eachArg
		end
	end
	return propId, entId
end

local function wrapInLangSpan(text, lang)
	if not text then return nil end
	if not lang or lang == 'ro' then return text end
	local langSpan = mw.html.create('span'):attr('lang', lang):wikitext(text)
	return tostring(langSpan)
end

local function isClaimTrue(claim)
	return mw.wikibase.entity.claimRanks['RANK_' .. mw.ustring.upper(claim.rank)] >= mw.wikibase.entity.claimRanks.RANK_NORMAL
end
p.isClaimTrue = isClaimTrue

--- returns true if the claim has a better (or equal) rank than the threshhold specified
--- threshholdRank can be "normal", "preferred" etc.
local function hasBetterRank(claim, threshholdRank)
	return (not threshholdRank
			or mw.wikibase.entity.claimRanks['RANK_' .. mw.ustring.upper(claim.rank)] >= mw.wikibase.entity.claimRanks['RANK_' .. mw.ustring.upper(threshholdRank)])
				and isClaimTrue(claim)
end
p.hasBetterRank = hasBetterRank

function p.descriptionIn(frame)
	local langcode = frame.args[1]
	local id = frame.args[2]
	-- return description of a Wikidata entity in the given language or the default language of this Wikipedia site
	local entity = mw.wikibase.getEntityObject(id)
	if entity then
		return entity.descriptions[langcode or lang.code].value
	end
	return ""
end

function p.getEntityId(frame)
	return mw.wikibase.getEntityIdForCurrentPage()
end

function p.labelIn(frame)
	local langcode = frame.args[1]
	local id = frame.args[2] or mw.wikibase.getEntityIdForCurrentPage()
	-- return label of a Wikidata entity in the given language or the default language of this Wikipedia site
	local entity
	if id then
		return mw.wikibase.getLabelByLang(id, langcode or lang.code)
	end
	return ""
end

local function isValueSnak(snak)
	return snak and snak.snaktype == 'value'
end
p.isValueSnak = isValueSnak

local function hasValueSnak(claim)
	return claim and claim.type == 'statement' and isValueSnak(claim.mainsnak)
end
p.hasValueSnak = hasValueSnak

local function hasRankAtLeastNormal(claim)
	return claim and claim.rank and mw.wikibase.entity.claimRanks['RANK_' .. mw.ustring.upper(claim.rank)] > mw.wikibase.entity.claimRanks.RANK_DEPRECATED
end
p.claimHasRankAtLeastNormal = hasRankAtLeastNormal

local function isWritingSystemLatn(lId)
	local writingSystems = { lId }
	
	local loopIdx = 1
	repeat
		local crtWritingSystemId = table.remove(writingSystems, 1)
		local writingSystemInstanceOfClaims = p.findBestClaimsForProperty(crtWritingSystemId, 'P31')
		if writingSystemInstanceOfClaims then for _,eachScriptInstanceOfClaim in ipairs(writingSystemInstanceOfClaims) do
			if p.hasValueSnak(eachScriptInstanceOfClaim) and eachScriptInstanceOfClaim.mainsnak.datavalue.value.id == 'Q29575627' then
				return true
			end
		end end
		local writingSystemClaims = p.findBestClaimsForProperty(crtWritingSystemId, 'P282')
		if writingSystemClaims then for _,eachWritingSystemClaim in ipairs(writingSystemClaims) do
			if p.hasValueSnak(eachWritingSystemClaim) then
				if eachWritingSystemClaim.mainsnak.datavalue.value.id == 'Q8229' then
					return true
				end
				table.insert(writingSystems, eachWritingSystemClaim.mainsnak.datavalue.value.id)
			end
		end end
		loopIdx = loopIdx + 1
	until #writingSystems == 0 or loopIdx >= 20

	return false	
end

local function findNativeOrEnglishLabel(entityId, capitalize)
	local label
	local labelLang
	if not entityId then
		entityId = mw.wikibase.getEntityIdForCurrentPage()
	end
	if not entityId then return nil end
	local langIds = {}
	for _,eachLangProp in ipairs({'P103', 'P407', 'P37', 'P364'}) do
		for __,eachLangClaim in ipairs(mw.wikibase.getBestStatements(entityId, eachLangProp)) do
			if hasValueSnak(eachLangClaim) then
				table.insert(langIds, eachLangClaim.mainsnak.datavalue.value.id)
			end
		end
	end
	for _,eachCountryProp in ipairs({'P17', 'P27'}) do
		for __,eachCountry in ipairs(mw.wikibase.getBestStatements(entityId, eachCountryProp)) do
			if hasValueSnak(eachCountry) then
				for ___,eachOffLang in ipairs(mw.wikibase.getBestStatements(eachCountry.mainsnak.datavalue.value.id, 'P37')) do
					if hasValueSnak(eachOffLang) then
						table.insert(langIds, eachOffLang.mainsnak.datavalue.value.id)
					end
				end
			end
		end
	end

	table.insert(langIds, 'Q1860')
	langIds = TableTools.mergeSort(langIds, function(l1, l2)
			if l2 and not l1 then return 1 end
			if l1 and not l2 then return -1 end
			if l1 == l2 then return 0 end
			
			local l1IsLatn = isWritingSystemLatn(l1) or Transliteration.isTransliterationSupported(p.loadOneValueInChain({l1, 'P218'}))
			local l2IsLatn = isWritingSystemLatn(l2) or Transliteration.isTransliterationSupported(p.loadOneValueInChain({l2, 'P218'}))
			if l1IsLatn and not l2IsLatn then
				return -1
			elseif not l1IsLatn and l2IsLatn then
				return 1
			else
			    return 0
			end
	end)
	local langsToTry = {}
	for _,eachLangId in ipairs(langIds) do
		for __,eachLangCodeClaim in ipairs(mw.wikibase.getBestStatements(eachLangId, 'P424')) do
			if not labelLang and hasValueSnak(eachLangCodeClaim) then
				table.insert(langsToTry, eachLangCodeClaim.mainsnak.datavalue.value)
			end
		end
	end

	local simplifiedLangsToTry = {}
	for _,eachLangToTry in ipairs(langsToTry) do
		local simplifiedLang = StringUtils._substringBefore({StringUtils._substringBefore({eachLangToTry, '_'}), '-'})
		table.insert(simplifiedLangsToTry, simplifiedLang)
	end
	langsToTry = simplifiedLangsToTry
	for _,eachLang in ipairs(langsToTry) do
		if label == nil then
			label = mw.wikibase.getLabelByLang(entityId, eachLang)
			labelLang = eachLang
		end
		if label then break end
	end
	if label then
		if labelLang and labelLang ~= 'ro' and not Transliteration.isTransliterationSupported(labelLang) then
			label = wrapInLangSpan(label, labelLang)
		end
		if Transliteration.isTransliterationSupported(labelLang) then
			label = wrapInLangSpan(Transliteration.transliterate(label, labelLang), labelLang .. '-Latn')
		end
		if capitalize then
			label = lang:ucfirst(label)
		end
	end
	return label, labelLang
end
p.findNativeOrEnglishLabel = findNativeOrEnglishLabel

p.getNativeOrEnglishLabel = function(frame)
	local args = getArgs(frame)
	local label, lang = findNativeOrEnglishLabel(args[1], args[2])
	if label then return label else return nil end
end

local function computeLinkToItem(entityId, capitalize, callFunction, plainCallFunction)
	local qIfiedEntityId = StringUtils._prependIfMissing({tostring(entityId), 'Q'})
	local sitelink = mw.wikibase.sitelink(qIfiedEntityId)
	local label = nil
	local labelLang = nil
	local object = nil
	local out = ""
	local description = nil
	if plainCallFunction and type(plainCallFunction) == 'function' then
		label, labelLang = plainCallFunction(qIfiedEntityId)
	elseif callFunction and type(callFunction) == 'function' then
		object = mw.wikibase.getEntityObject(qIfiedEntityId)
		label, labelLang = callFunction(object)
	end

	if label == nil or (labelLang and labelLang ~= lang.code) then 
		label = mw.wikibase.getLabelByLang(qIfiedEntityId, 'ro')
		if label then labelLang = 'ro' end
		description = mw.wikibase.getDescription(qIfiedEntityId)
	end

	if not label then
		label, labelLang = findNativeOrEnglishLabel(qIfiedEntityId, capitalize)
	elseif capitalize then
		label = lang:ucfirst(label)
	end

	if not label then
		label = qIfiedEntityId
		labelLang = nil
	end
	if description then description = "(" .. description .. ")" end
	if label == qIfiedEntityId then
		out = "[[Categorie:Articole cu legături către elemente fără etichetă]]"
	elseif labelLang ~= 'ro' then
		out = "[[Categorie:Articole cu legături către elemente fără etichetă în limba română]]"
	end
	
	description = description or ""
	if sitelink then
		return out .. "[[:" .. sitelink .. "|" .. label .. "]]"
	else
		local coextensiveWithPageId = p.loadOneValueInChain({qIfiedEntityId, 'P3403', 'raw'})
		local listPageId = p.loadOneValueInChain({qIfiedEntityId, 'P2354', 'raw'})
		local listPageSitelink = listPageId and mw.wikibase.sitelink(listPageId) or coextensiveWithPageId and mw.wikibase.sitelink(coextensiveWithPageId)
		if listPageSitelink then return "[[:" .. listPageSitelink .. "|" .. label .. "]]" end
		return out .. "[[:d:" .. qIfiedEntityId .. "|" .. label .. "]]<abbr title='Articolul încă nu există în acest wiki'>[*]</abbr>" .. mw.getCurrentFrame():preprocess("{{invizibil|1=[[" .. label .." " .. description .. "|&#8203;]]}}")
	end
end

p.findLinkToItemWithLabel = function(entityId, label)
	local sitelink = mw.wikibase.sitelink(StringUtils._prependIfMissing({tostring(entityId), 'Q'}))
	if sitelink then
		return "[[:" .. sitelink .. "|" .. label .. "]]"
	else
		return computeLinkToItem(entityId, false, nil, function() return label end)
	end
end

p.findLinkToItemWithCallback = function(entityId, capitalize, callBack, plainCallBack)
	return computeLinkToItem(entityId, capitalize, callBack, plainCallBack)
end

p.findLinkToItemWithLabelFromAnyClaim = function(entityId, capitalize, propertyIdsTable)
	local callFunction = nil
	if propertyIdsTable then
		callFunction = function(object)
			local returnValue = nil
			for propIdIdx,propertyId in pairs(propertyIdsTable) do
				if object and object.claims and object.claims[propertyId] and returnValue == nil then
					for eachClaimIdx, eachClaim in pairs(object.claims[propertyId]) do
						if hasValueSnak(eachClaim) and eachClaim.mainsnak.datavalue then
							if eachClaim.mainsnak.datavalue.type == 'monolingualtext' then
								if eachClaim.mainsnak.datavalue.value.language == 'ro' then returnValue = eachClaim.mainsnak.datavalue.value.text end
							elseif eachClaim.mainsnak.datavalue.type == 'wikibase-entityid' then
								returnValue = mw.wikibase.label(StringUtils._prependIfMissing({tostring(eachClaim.mainsnak.datavalue.value['numeric-id']), 'Q'}))
							elseif eachClaim.mainsnak.datavalue.type == 'string' then
								returnValue = eachClaim.mainsnak.datavalue.value
							end
						end
					end
				end
			end
			return returnValue
		end
	end
	return computeLinkToItem(entityId, capitalize, callFunction)
end

p.findLinkToItemWithLabelFromClaim = function(entityId, capitalize, propertyId)
	return p.findLinkToItemWithLabelFromAnyClaim(entityId, capitalize, { propertyId })
end

p.findLinkToItem = function(entityId, capitalize, feminine, shortestAlias)
	if type(entityId) == 'number' then entityId = StringUtils._prependIfMissing({tostring(entityId), 'Q'}) end
	if capitalize == nil then capitalize = false end
	local label
	local returnedLang = nil
	local bestRanks = { ["ro"] = nil, ["en"] = nil }
	
	if shortestAlias then
		local returnedAlias = nil
		local bestShortNameClaims = mw.wikibase.getAllStatements(entityId, 'P1813')
		if bestShortNameClaims then
			local shortNameEn = nil
			for shortNameIdx,shortNameClaim in pairs(bestShortNameClaims) do
				if shortNameClaim.mainsnak.datavalue.value.language == 'en' and hasBetterRank(shortNameClaim, bestRanks["en"]) then 
					shortNameEn = shortNameClaim.mainsnak.datavalue.value.text
					bestRanks["en"] = shortNameClaim.rank
				end
				if shortNameClaim.mainsnak.datavalue.value.language == 'ro' and hasBetterRank(shortNameClaim, bestRanks["ro"]) then
					returnedAlias = shortNameClaim.mainsnak.datavalue.value.text
					bestRanks["ro"] = shortNameClaim.rank
				end
			end
			if returnedAlias then
				returnedLang = 'ro'
			elseif shortNameEn then
				returnedAlias = shortNameEn
				returnedLang = 'en'
			end
			returnedAlias = capitalize and StringUtils._capitalize(returnedAlias) or returnedAlias
		end
		label = wrapInLangSpan(returnedAlias, returnedLang)
	end
	if feminine then
		local feminineForms = mw.wikibase.getBestStatements(entityId, 'P2521')
		if feminineForms then
			for _idx, eachFForm in pairs(feminineForms) do
				if hasValueSnak(eachFForm) and eachFForm.mainsnak.datatype == 'monolingualtext' and eachFForm.mainsnak.datavalue.type == 'monolingualtext' and eachFForm.mainsnak.datavalue.value and eachFForm.mainsnak.datavalue.value.language == 'ro' then
					label = eachFForm.mainsnak.datavalue.value.text
					returnedLang = eachFForm.mainsnak.datavalue.value.language
				end
			end
		end
	end
	return computeLinkToItem(entityId, capitalize, nil, function() return label, returnedLang end)
end

p.getLinkToItem = function(frame)
	local args = getArgs(frame, {frameOnly=true})
	local entityId = args[1]
	return p.findLinkToItem(entityId, false, false, false)
end

local function printRawValue(snak)
	if (isValueSnak(snak)) then
		if snak.datavalue.type == "wikibase-entityid" then
			return snak.datavalue.value.id
		elseif snak.datavalue.type == "time" then
			return snak.datavalue.value.time
		elseif snak.datavalue.type == "monolingualtext" then
			return snak.datavalue.value.text
		elseif snak.datavalue.type == "quantity" then
			return snak.datavalue.value.amount .. ' ' .. snak.datavalue.value.unit
		else
			return snak.datavalue.value
		end
	end
	return ''
end
-- precision: 0 - billion years, 1 - hundred million years, ..., 6 - millennia, 7 - century, 8 - decade, 9 - year, 10 - month, 11 - day, 12 - hour, 13 - minute, 14 - second
local function normalizeDate(date)
	date = mw.text.trim(date, "+")
	-- extract year
	local yearstr = mw.ustring.match(date, "^\-?%d+")
	local year = tonumber(yearstr)
	-- remove leading zeros of year
	return year .. mw.ustring.sub(date, #yearstr + 1), year
end

local function formatDate(indate, precision, timezone)
	precision = precision or 11
	local date, year = normalizeDate(indate)
	if year == 0 and precision <= 9 then return "" end
 
 	-- precision is 10000 years or more
	if precision <= 5 then
		local factor = 10 ^ ((5 - precision) + 4)
		local y2 = math.ceil(math.abs(year) / factor)
		local relative = mw.ustring.gsub(i18n.datetime[precision], "$1", tostring(y2))
		if year < 0 then
			relative = mw.ustring.gsub(i18n.datetime.beforenow, "$1", relative)
		else
			relative = mw.ustring.gsub(i18n.datetime.afternow, "$1", relative)
		end			
		return relative
	end
 
 	-- precision is decades, centuries and millennia
	local era
	if precision == 6 then era = mw.ustring.gsub(i18n.datetime[6], "$1", tostring(math.floor((math.abs(year) - 1) / 1000) + 1)) end
	if era then
		if year < 0 then era = mw.ustring.gsub(mw.ustring.gsub(i18n.datetime.bc, '"', ""), "$1", era)
		elseif year > 0 then era = mw.ustring.gsub(mw.ustring.gsub(i18n.datetime.ad, '"', ""), "$1", era) end
		return era
	end
 
 	if precision >= 7 then
 		return DateUtils.formatDate(DateUtils.parseWikidataDate(indate, precision))
 	end
end

local function snakToString(snak, feminine)
	if (isValueSnak(snak)) then
		if (snak.datavalue.type == "wikibase-entityid") then
			return p.findLinkToItem(snak.datavalue.value["numeric-id"], nil, feminine)
		end
		if (snak.datavalue.type == "time") then
			return formatDate(snak.datavalue.value.time, snak.datavalue.value.precision, snak.datavalue.value.timezone)
		end
		if (snak.datavalue.type == "monolingualtext") then
			return snak.datavalue.value.text
		end
		if (snak.datavalue.type == "quantity") then
			local unit = '1'
			local amount = snak.datavalue.value.amount
			local ret = nil
			local unitQ = mw.ustring.match(snak.datavalue.value.unit, 'Q%d+')
			if unitQ ~= nil then
				unit = p.findLanguageText('P5061', 'ro', unitQ) or p.findLanguageText('P5061', 'ro', unitQ)
				if not unit or mw.ustring.len(unit) == 0 then
					unit = p.findLabel(unitQ, nil)
					if tonumber(amount) > 1 and unit then 
						mw.incrementExpensiveFunctionCount()
						ret = plural(tonumber(amount), unit) 
					end
				end
				unit = unit or '1'
			end
			if tonumber(amount) ~= nil then
				amount = mw.language.new(lang.code):formatNum(tonumber(amount))
			end
			if ret == nil then
		    	ret = amount .. (unit ~= '1' and (mw.text.decode('&nbsp;') .. unit) or '')
			end
			return ret
		end
		return snak.datavalue.value
	end
	return ""
end

p.printSnak = function(snak)
	return snakToString(snak)
end

p.outputReferences = function (claim)
	local refMapping = {}
	local authorityPropertiesMap = {}
	local frame = mw.getCurrentFrame()
	
	refMapping["P1433"] = "publisher"
	refMapping["P123"] = "publisher"
	refMapping["P143"] = "publisher"
	refMapping["P1476"] = "contribution"
	refMapping["P854"] = "url"
	refMapping["P813"] = "accessdate"
	refMapping["P50"] = "author"
	refMapping["P2093"] = "author"
	refMapping["P577"] = "date"
	refMapping["P248"] = "title"
	refMapping["P304"] = "page"
	refMapping["P958"] = "contribution"
	refMapping["P1810"] = "contribution"
	refMapping["P212"] = "isbn"
	
	authorityPropertiesMap['Q20666306'] = 'P268' -- BnF identifier
	authorityPropertiesMap['Q15241312'] = 'P646' -- Freebase identifier
	authorityPropertiesMap['Q36578'] = 'P227' -- GND identifier
	authorityPropertiesMap['Q37312'] = 'P345' -- IMDb identifier
	authorityPropertiesMap['Q278487'] = 'P662' -- PubChem identifier
	authorityPropertiesMap['Q1139587'] = 'P2334' -- Swedish film database identifier
	
	
	local primaryProperties = {}
	primaryProperties["P1433"] = "publisher"
	primaryProperties["P123"] = "publisher"
	primaryProperties["P1476"] = "contribution"
	primaryProperties["P248"] = "title"
	primaryProperties["P854"] = "url"
	primaryProperties["P50"] = "author"
	local out = ""
	local referencesCount = 0
	
	if claim then
		for ref in pairs(claim.references or {}) do
			local citationArguments = {}
			local citationArgumentsFound = false
			local refname = ""
			for snakkey, snakval in pairs(claim.references[ref].snaks) do
				if refMapping[snakkey] ~= nil then
					local snakData = {}
					for snakIndex,snakValElement in ipairs(snakval) do
						local formattedSnakValElement = snakToString(snakValElement)
						if isValueSnak(snakValElement) and snakValElement.datatype == 'time' then
							formattedSnakValElement = DateUtils.formatDate(DateUtils.parseWikidataDate(snakValElement.datavalue.value.time, snakValElement.datavalue.value.precision), false , true)
						elseif isValueSnak(snakValElement) and snakkey == 'P1476' and snakValElement.datatype == 'monolingualtext' and snakValElement.datavalue.value.language ~= 'ro' then
							citationArguments['language'] = snakValElement.datavalue.value.language
						end
						
						table.insert(snakData, formattedSnakValElement)
					end
					citationArguments[refMapping[snakkey]] = table.concat(snakData, ", ", 1, #snakData)
					refname = refname .. '_' .. refMapping[snakkey] .. '_' .. mw.ustring.gsub(mw.ustring.gsub(mw.text.nowiki(citationArguments[refMapping[snakkey]]), '&#%d+;', ''), ' ', '')
					if primaryProperties[snakkey] then
						citationArgumentsFound = true
					end
					
					if snakkey == 'P248' or snakkey == 'P143' then -- "stated in" or "imported from", can be an authority file
						if snakval[1].datavalue and snakval[1].datavalue.type == 'wikibase-entityid' then
							local statedInWbId = snakval[1].datavalue.value.id
							
							-- if Wikimedia project, ignore
							for _idx, eachStatedInInstanceOfClaim in pairs(mw.wikibase.getAllStatements(statedInWbId, 'P31')) do
								if eachStatedInInstanceOfClaim.mainsnak and eachStatedInInstanceOfClaim.mainsnak.datavalue and eachStatedInInstanceOfClaim.mainsnak.datavalue.type == 'wikibase-entityid' and 
								(eachStatedInInstanceOfClaim.mainsnak.datavalue.value['numeric-id'] == 10876391 or eachStatedInInstanceOfClaim.mainsnak.datavalue.value['numeric-id'] == 14827288) then
									citationArgumentsFound = false
									break
								end
							end
							for _idx, eachStatedInInstanceOfClaim in pairs(mw.wikibase.getAllStatements(statedInWbId, 'P1269')) do
								if eachStatedInInstanceOfClaim.mainsnak and eachStatedInInstanceOfClaim.mainsnak.datavalue and eachStatedInInstanceOfClaim.mainsnak.datavalue.type == 'wikibase-entityid' and 
								eachStatedInInstanceOfClaim.mainsnak.datavalue.value['numeric-id'] == 16222597 then
									citationArgumentsFound = false
									break
								end
							end
							-- extract data about authority file
							local authorityWbEntityId = authorityPropertiesMap[StringUtils._prependIfMissing({statedInWbId, 'Q'})]
							if authorityWbEntityId then
								local authorityUrlFormatClaims = mw.wikibase.getBestStatements(authorityWbEntityId, 'P1630')
								if authorityUrlFormatClaims and 0 < #authorityUrlFormatClaims then
								 	local formatterUrl = snakToString(authorityUrlFormatClaims[1].mainsnak)
								 	local crtEntityId = mw.wikibase.getEntityIdForCurrentPage()
								 	if crtEntityId then
								 		local entityAuthIdClaims = mw.wikibase.getBestStatements(crtEntityId, authorityWbEntityId)
								 		if entityAuthIdClaims and 0 < #entityAuthIdClaims and hasValueSnak(entityAuthIdClaims[1]) then
								 			citationArguments['chapterurl'] = mw.ustring.gsub(formatterUrl, '%$1', entityAuthIdClaims[1].mainsnak.datavalue.value)
								 			citationArguments['chapter'] = p.labelIn({args = {'ro'}}) or p.labelIn({args = {'en'}})
							 			end
						 			end
				 				end
	 						elseif citationArgumentsFound == true then
	 							--extract URL from target entity
	 							local statedInUrl = nil
	 							-- the order of the array is important, as we only search the first occurence
	 							local onlinelinksprops = {'P953', 'P854', 'P856', 'P1581', 'P2397','P1065'}
								for _,eachOnlineLinkProp in ipairs(onlinelinksprops) do
									local linkId = p.findOneValueNoRef(eachOnlineLinkProp, statedInWbId)
									
									if linkId then
										local linkFormatter = p.findOneValueNoRef('P1630', eachOnlineLinkProp) or '$1'
										if linkFormatter then
											linkId = mw.ustring.gsub(linkId, '%%', '%%%%')
											statedInUrl = mw.ustring.gsub(linkFormatter, '$1', linkId)
											break
										else
											statedInUrl = linkId
											break
										end
									end
								end
	 							if statedInUrl then
	 								citationArguments[refMapping[snakkey]] = StringUtils._capitalize(p.findTitleOfWork(statedInWbId))
	 								citationArguments['url'] = statedInUrl
	 							else
	 								local props = p.getEntityIdsList(statedInWbId, 'P1687')
	 								if props then for _,eachOnlineLinkProp in ipairs(onlinelinksprops) do
		 								for _,prop in ipairs(props) do
		 									if eachOnlineLinkProp == 'P' .. prop then 
		 										citationArguments[refMapping[snakkey]] = StringUtils._capitalize(p.findTitleOfWork(statedInWbId))
		 										citationArguments['url'] = p.findOneValueNoRef(eachOnlineLinkProp) 
		 									end
		 									break
		 								end
		 							end end
						        end
					        end
			 			end
					end
				end
			end
			if citationArgumentsFound then
				
				--post-process citation arguments:
				--1. if publisher is missing and contribution and title are present, change title to publisher and contribution to title
				if citationArguments['publisher'] == nil and citationArguments['contribution'] and citationArguments['title'] then
					citationArguments['publisher'] = citationArguments['title']
					citationArguments['title'] = citationArguments['contribution']
					citationArguments['contribution'] = nil
				end
				--2. if contribution is present but title is not, change contribution to title
				if citationArguments['contribution'] and citationArguments['title'] == nil then
					citationArguments['title'] = citationArguments['contribution']
					citationArguments['contribution'] = nil
				end
				--3. if title is a wikilink, break it down and assign article link separately
				if citationArguments['title'] then 
					local titlink, titplain = mw.ustring.match(citationArguments['title'], '%[%[(.*)|(.*)%]%]')
					if titlink and titplain then
						if not citationArguments['url'] then citationArguments['titlelink'] = titlink end
						citationArguments['title'] = titplain
					end
				end

				local citationHash = mw.hash.hashValue('md5', refname)
				if referencesCount < 4 then
					if wdReferences[citationHash] then
						out = out .. frame:extensionTag{name="ref", args={name = citationHash}}
					else
						local refText = Citation.citationFromArgs(citationArguments, {CitationClass = 'citation'}, false)
						out = out .. frame:extensionTag("ref", refText, { name = citationHash })
						wdReferences[citationHash] = true
					end
					referencesCount = referencesCount + 1
				end
			end
		end
	end
	return out
end

p.getValueListLimited = function(entityId, propertyId, limitVal, best)
	if limitVal == nil or limitVal <= 0 then limitVal = math.huge end
	entityId = entityId or mw.wikibase.getEntityIdForCurrentPage()
	if propertyId == nil or entityId == nil then return nil,0 end
	local claims = best and mw.wikibase.getBestStatements(entityId, propertyId) or mw.wikibase.getAllStatements(entityId, propertyId)
	if not claims then return nil,0 end
	if #claims == 0 then return {},0 end
	local rez = {}
	local feminine = false
	local genders = mw.wikibase.getBestStatements(entityId, 'P21')
	if genders and #genders > 0 and genders[1] and hasValueSnak(genders[1]) and genders[1].mainsnak.datatype == 'wikibase-item' and genders[1].mainsnak.datavalue.type == 'wikibase-entityid' and genders[1].mainsnak.datavalue.value['numeric-id'] == 6581072 then
		feminine = true
	end
	local count = 0
	for k, v in pairs(claims) do
		if hasValueSnak(v) and hasRankAtLeastNormal(v) then
			count = count+1
			if count <= limitVal then
				table.insert(rez, snakToString(v.mainsnak, feminine) .. p.outputReferences(v))
			end
		end
	end
	return rez, count
end

p.getValueList = function(entityId, propertyId)
	local rez, _ = p.getValueListLimited(entityId, propertyId, nil)
	return rez
end

local function findTimestampInTable(tbl)
	local crtEntry = ''
	local crtProp = nil
	if tbl then
		if tbl['P585'] then
			crtEntry = crtEntry .. snakToString(tbl['P585'][1].mainsnak or tbl['P585'][1])
			crtProp = 'P585'
		elseif tbl['P580'] then
			if tbl['P582'] then
				local starttime = snakToString(tbl['P580'][1].mainsnak or tbl['P580'][1])
				local endtime = snakToString(tbl['P582'][1].mainsnak or tbl['P582'][1])
				if starttime == endtime then
					crtEntry = crtEntry .. starttime
				else
					crtEntry = crtEntry .. starttime .. mw.text.decode('&ndash;', true) .. endtime
				end
				crtProp = 'P582'
			else 
				crtEntry = crtEntry .. 'din ' .. snakToString(tbl['P580'][1].mainsnak or tbl['P580'][1])
				crtProp = 'P580'
			end
		elseif tbl['P582'] then
			local endtime = DateUtils.extractDateFromWikidataSnak(tbl['P582'][1].mainsnak or tbl['P582'][1])
			if endtime then
				crtEntry = crtEntry .. 'până ' .. ((endtime.precision == 9 or endtime.precision == 10) and 'în' or 'la') .. ' ' .. DateUtils.formatDate(endtime)
			end
			crtProp = 'P582'
		end
	end
	return crtEntry, crtProp
end

p.printTimestampForClaim = function(claim)
	if claim and claim.qualifiers then
		local tblTimestamp = findTimestampInTable(claim.qualifiers)
		return tostring(mw.html.create('small'):wikitext(' (' .. tblTimestamp .. ')'))
	end
	return ''
end

p.findTimestamp = function(entityId)
	if type(entityId) == 'number' then entityId = StringUtils._prependIfMissing({tostring(entityId), 'Q'}) end
	if type(entityId) == 'table' then entityId = entityId.id end
	entityId = entityId or mw.wikibase.getEntityIdForCurrentPage()
	
	if entityId then
		local tbl = {}
		for _,propId in ipairs({'P580', 'P582', 'P585'}) do
			local propClaims = mw.wikibase.getAllStatements(entityId, propId)
			if propClaims and #propClaims > 0 then
				tbl[propId] = propClaims
			end
		end
		local tblTstamp, tblProp = findTimestampInTable(tbl)
		return tblTstamp, tblProp
	end
	return ''
end

p.getTimestamp = function(frame)
	return p.findTimestamp(frame and frame.args and frame.args[1] or nil)
end

p.getValueListWithDecoratedQualifiers = function(frame)
	local args = getArgs(frame, {wrappers = {'Format:Listă decorată de la Wikidata'}, frameOnly = false, parentOnly = false})
	local entityId = args['q'] or mw.wikibase.getEntityIdForCurrentPage()
	local propertyId = args['p']
	local best = (args['best'] ~= nil)
	local bracketTemplates = nil
	local limitVal = args['limit'] and tonumber(args['limit']) or -1
	local bracketTemplatesIdces = TableTools.affixNums(args, 'bracketTemplate')
	if bracketTemplatesIdces and #bracketTemplatesIdces > 0 then
		bracketTemplates = {}
		for _, bracketTemplatesIdx in ipairs(bracketTemplatesIdces) do
			table.insert(bracketTemplates, args['bracketTemplate' .. tostring(bracketTemplatesIdx)])
		end
	end
	local pictureSize = args['pictureSize'] or '40px'
	local pictureProp = args['picturePid'] or nil
	local seps = nil
	local sepsIdces = TableTools.affixNums(args, 'sep')
	if sepsIdces and #sepsIdces > 0 then
		seps = {}
		for _, sepsIdx in ipairs(sepsIdces) do
			table.insert(seps, args['sep' .. tostring(sepsIdx)])
		end
	end
	
	local rezList, rezCount = p.findValueListWithDecoratedQualifiers(entityId, propertyId, best, bracketTemplates, seps, pictureProp, pictureSize, limitVal)
	if 0 < limitVal and rezCount > limitVal + 1 then
		local hiddenVals = rezCount - limitVal
		extraSpec = '[[:d:' .. entityId .. '#' .. propertyId .. "|...''încă " .. tostring(hiddenVals) .. "'']]"
		table.insert(rezList, extraSpec)
	end

	return table.concat(rezList or {}, tostring(mw.html.create('br')))
end
p.getValueListWithQualifiersInBrackets = p.getValueListWithDecoratedQualifiers

p.findValueListWithDecoratedQualifiers = function(entityId, propertyId, best, bracketTemplates, seps, pictureProp, pictureSize, limit)
	entityId = entityId or mw.wikibase.getEntityIdForCurrentPage()
	if not limit or type(limit) ~= 'number' then limit = -1 end
	if (type(bracketTemplates) == 'string') then
		bracketTemplates = { bracketTemplates }
	elseif not bracketTemplates then
		bracketTemplates = {}
	end
	seps = seps or {}
	table.insert(seps, 1, '')
	if #seps < #bracketTemplates then for idx=#seps + 1,#bracketTemplates do table.insert(seps, ', ') end end
	if propertyId == nil or entityId == nil then return nil end
	local claims
	
	local qualProps = {}
	for _,bracketTemplate in ipairs(bracketTemplates) do
		mw.ustring.gsub(bracketTemplate, '%$(P%d+)', function(qualProp) 
			table.insert(qualProps, qualProp)
			return nil
		end)
	end
	
	if best then
		claims = mw.wikibase.getBestStatements(entityId, propertyId)
	else
		claims = mw.wikibase.getAllStatements(entityId, propertyId)
	end
	local rez = {}
	local allcount = 0
	if claims and claims[1] then
		local rawclaims = {}
		for k, v in pairs(claims) do
			if hasValueSnak(v) and hasRankAtLeastNormal(v) then
				table.insert(rawclaims, v)
			end
		end
		allcount = #rawclaims
		for k, v in pairs(rawclaims) do
			local crtEntry = snakToString(v.mainsnak)
			if pictureProp then
				local ts
				local picture
				if v.qualifiers and v.qualifiers['P582'] then for _,endTimeQual in ipairs(v.qualifiers['P582']) do
					ts = DateUtils.extractDateFromWikidataSnak(endTimeQual)
				end end
				if ts then
					local pictureClaim = p.findClaimForTimestamp(v.mainsnak.datavalue.value.id, pictureProp, ts)
					if (p.hasValueSnak(pictureClaim)) then
						picture = pictureClaim.mainsnak.datavalue.value
					end
				else
					picture = p.loadOneValueInChain({v.mainsnak.datavalue.value.id, pictureProp, 'raw'})
				end
				if picture and picture ~= '' then
					crtEntry = StringUtils._prependToString({crtEntry, '[[Fișier:' .. picture .. '|' .. pictureSize .. ']]' .. mw.text.decode('&nbsp;')})
				end
			end
			if v.qualifiers and #qualProps > 0 then
				local qualPropValues = {}
				for _,eachQualProp in pairs(qualProps) do
					if v.qualifiers[eachQualProp] then
						local qualPropValuesList = {}
						for __,eachQualSnak in ipairs(v.qualifiers[eachQualProp]) do
							if isValueSnak(eachQualSnak) then
								table.insert(qualPropValuesList, snakToString(eachQualSnak))
							end
						end
						qualPropValues[eachQualProp] = mw.text.listToText(qualPropValuesList, ', ', ' și ')
					else
						qualPropValues[eachQualProp] = ''
					end
				end

				local bracketData = {}
				local usedSeps = {}
				for bracketTemplateIdx,bracketTemplate in ipairs(bracketTemplates) do
					local substedBracketTemplate = mw.ustring.gsub(bracketTemplate, '%$(P%d+)', function(qualProp) 
						return qualPropValues[qualProp]
					end)
					if substedBracketTemplate ~= bracketTemplate and substedBracketTemplate ~= "" then
						bracketData[bracketTemplateIdx] = substedBracketTemplate
						usedSeps[bracketTemplateIdx] = seps[bracketTemplateIdx]
					end
				end
				bracketData = TableTools.compressSparseArray(bracketData)
				usedSeps = TableTools.compressSparseArray(usedSeps)

				local bracketText = ''
				for bracketDatumIdx=1,#bracketData do
					bracketText = bracketText .. (bracketDatumIdx == 1 and '' or usedSeps[bracketDatumIdx])
					bracketText = bracketText .. bracketData[bracketDatumIdx]
				end
				if mw.ustring.len(bracketText) > 0 then
					crtEntry = StringUtils._appendToString({crtEntry, tostring(mw.html.create('small'):wikitext(StringUtils._encloseString({bracketText, '&#32;(', ')'})))})
				end
			end
			
			crtEntry = crtEntry .. p.outputReferences(v)
			if 0 < limit and limit <= #rez then
				break
			end
			table.insert(rez, crtEntry)
		end
	end
	return rez, allcount
end
p.findValueListWithQualifiersInBrackets = p.findValueListWithDecoratedQualifiers

p.getTimestampedValueList = function(entityId, propertyId, best)
	entityId = entityId or mw.wikibase.getEntityIdForCurrentPage()
	if propertyId == nil or entityId == nil then return nil end
	local claims
	if best then
		claims = mw.wikibase.getBestStatements(entityId, propertyId)
	else
		claims = mw.wikibase.getAllStatements(entityId, propertyId)
	end
	local rez = {}
	if claims and claims[1] then
		local rawclaims = {}
		for k, v in pairs(claims) do
			if hasValueSnak(v) then
				table.insert(rawclaims, v)
			end
		end
		for k, v in pairs(rawclaims) do
			local crtEntry = snakToString(v.mainsnak)
			crtEntry = crtEntry .. p.printTimestampForClaim(v)
			crtEntry = crtEntry .. p.outputReferences(v)
			table.insert(rez, crtEntry)
		end
	end
	return rez
end	

p._getValueListWithSeparator = function(...)
	if arg.n == 0 then return nil end
	local funcArgs = type(arg[1]) == 'table' and arg[1] or arg
	local separator = mw.text.trim(funcArgs[1] or ", ")
	local propertyId
	local entityId
	propertyId, entityId = propAndEntity(funcArgs[2], funcArgs[3])
	entityId = entityId or mw.wikibase.getEntityIdForCurrentPage()
	local limit = funcArgs[4] or funcArgs['limit']
	local best = funcArgs[5] or funcArgs['best']
	if not best or best == 'false' or best == 'no' then best = false else best = true end
	local limitVal = 0
	if limit and (limit == mw.ustring.match(limit, '%d+') or type(limit) == 'number') then
		limitVal = tonumber(limit)
	end
	local values, vcount = p.getValueListLimited(entityId, propertyId, limitVal, best)
	if not values then return nil end
	local extraSpec = nil
	if 0 < limitVal and vcount > limitVal + 1 then
		local hiddenVals = vcount - limitVal
		extraSpec = '[[:d:' .. entityId .. '#' .. propertyId .. "|...''încă " .. tostring(hiddenVals) .. "'']]"
		table.insert(values, extraSpec)
	end
	return values and table.concat(values, separator)
end

p.getValueListWithSeparator = function(frame)
	local funcArgs = getArgs(frame, {frameOnly = true})
	return p._getValueListWithSeparator(funcArgs)
end

p.getValue = function(frame)
	local args = getArgs(frame)
	return p._getValueListWithSeparator({', ', args[1], mw.wikibase.getEntityIdForCurrentPage()})
end

p._getTimestampedValueListWithSeparator = function(funcArgs)
	local separator = funcArgs[1] or ", "
	local propertyId = funcArgs[2]
	local entityId = funcArgs[3]
	local best = funcArgs['best'] == 'y' or funcArgs['best'] == 'yes'
	local values = p.getTimestampedValueList(entityId, propertyId, best)
	return values and table.concat(values, separator)
end

p.getTimestampedValueListWithSeparator = function(frame)
	local funcArgs = getArgs(frame, {frameOnly = true})
	return p._getTimestampedValueListWithSeparator(funcArgs)
end

local function extractWikidataIdsFromClaimList(claims)
	local rez = {}
	if claims and #claims > 0 then
		for k, v in pairs(claims) do
			if hasValueSnak(v) and v.mainsnak.datavalue.type == 'wikibase-entityid' then
				table.insert(rez, v.mainsnak.datavalue.value['numeric-id'])
			end
		end
	end
	return rez
end

p.getEntityIdsList = function(entityId, propertyId)
	entityId = entityId or mw.wikibase.getEntityIdForCurrentPage()
	if propertyId == nil or entityId == nil then return nil end
	local claims = nil
	if entityId then
		claims = mw.wikibase.getAllStatements(entityId, propertyId)
	end
	local trueClaims = {}
	for _,claim in ipairs(claims) do
		if hasRankAtLeastNormal(claim) then	table.insert(trueClaims, claim) end
	end
	return extractWikidataIdsFromClaimList(trueClaims)
end

p.getBestEntityIdsList = function(entityId, propertyId)
	local claims = nil
	if propertyId == nil then return nil end
	if type(entityId) == 'table' then
		claims = entityId:getBestStatements(propertyId)
	else
		if type(entityId) == 'number' then entityId = StringUtils._prependIfMissing({tostring(entityId), 'Q'}) end
		if type(propertyId) == 'number' then propertyId = StringUtils._prependIfMissing({tostring(propertyId), 'P'}) end
		entityId = entityId or mw.wikibase.getEntityIdForCurrentPage()
		if propertyId == nil or entityId == nil then return nil end
		claims = mw.wikibase.getBestStatements(entityId, propertyId)
	end
	if not claims then return nil end
	return extractWikidataIdsFromClaimList(claims)
end

local timestampComparator = function(p1, p2)
	if p1 and p2 then
		local q1 = p1.qualifiers
		local q2 = p2.qualifiers
		local d1 = nil
		local d2 = nil
		if q1 and q2 then
			if q1['P580'] and q1['P580'][1] and q1['P580'][1].datavalue then
				d1 = p.extractDateFromClaim(q1['P580'][1])
			end
			if q2['P580'] and q2['P580'][1] and q2['P580'][1].datavalue then
				d2 = p.extractDateFromClaim(q2['P580'][1])
			end
			if d1 and d2 then return DateUtils.compare(d1, d2) > 0
			elseif d1 then return true
			elseif d2 then return false end
		elseif q1 then return true
		elseif q2 then return false
		end
	else
		if p1 then return true end
		if p2 then return false end
	end
	return false
end

p.findBestClaimsForProperty = function(entity, propertyId)
	if propertyId == nil then return nil end
	if entity == nil then entity = mw.wikibase.getEntityIdForCurrentPage() end
	if type(entity) == 'table' then return entity:getBestStatements(propertyId) end
	if type(entity) == 'number' then entity = StringUtils._prependIfMissing({tostring(entity), 'Q'}) end
	if entity == nil then return nil end
	return mw.wikibase.getBestStatements(entity, propertyId)
end

p.findClaimsForProperty = function(entity, propertyId)
	if propertyId == nil then return nil end
	if entity == nil then entity = mw.wikibase.getEntityIdForCurrentPage() end
	if type(entity) == 'table' and entity.claims then return entity.claims[propertyId] end
	if type(entity) == 'number' then entity = StringUtils._prependIfMissing({tostring(entity), 'Q'}) end
	if entity == nil then return nil end
	return mw.wikibase.getAllStatements(entity, propertyId)
end

p.findClaimsForPropertyByType = function(entity, propertyId, typeIds)
	local ret = {}
	if propertyId == nil then return nil end
	if entity == nil then entity = mw.wikibase.getEntityIdForCurrentPage() end
	local claims = {}
	if type(entity) == 'table' and entity.claims then 
		claims = entity.claims[propertyId]
	else
		if type(entity) == 'number' then entity = StringUtils._prependIfMissing({tostring(entity), 'Q'}) end
		if entity == nil then return nil end
		claims = mw.wikibase.getBestStatements(entity, propertyId)
	end

	for k, v in pairs(claims) do
		local snak = v.mainsnak
		local end_date = p.findQualifierValueListForClaim(v, "P582")
		if #end_date == 0 and isValueSnak(snak) and snak.datavalue.type == "wikibase-entityid" then
			value = snak.datavalue.value.id 
			if p.isA(value, typeIds) then
				table.insert(ret, v)
			end
		end
	end
	return ret
end

p.getClaimsForPropertyByType = function(frame)
	local args = getArgs(frame)
	local propertyID, entityID, typeId
	local raw = false
	local ret = {}
	propertyID, entityID = propAndEntity(args[1] or '', args[2] or '')
	typeId = {args[3]}
	if args['raw'] then raw = true end
	local entity = nil
	if entityID ~= "" then entity = mw.wikibase.getEntityObject(entityID) end
	if entity == nil then entity = mw.wikibase.getEntityObject() end
	if entity == nil then return "" end

	local claims = p.findClaimsForPropertyByType(entity, propertyID, typeId)
	if not claims then return "" end
	for _,eachClaim in ipairs(claims) do
		if hasValueSnak(eachClaim) then
			if raw then
				table.insert(ret, printRawValue(eachClaim.mainsnak))
			else
			    table.insert(ret, snakToString(eachClaim.mainsnak))
			end
		end
	end

	return table.concat(ret, ", ")
end

p.findSortedClaimsForProperty = function(entity, propertyId)
	local rawClaims = p.findClaimsForProperty(entity, propertyId)
	if rawClaims == nil then return {} end
	
	table.sort(rawClaims, timestampComparator)
	return rawClaims
end

p.findQualifierValueListForClaim = function(claim, qualifierId)
	if claim == nil or claim.qualifiers == nil or qualifierId == nil then return {} end
	local quals = {}
	local wikidataQualifiers = claim.qualifiers[qualifierId]
	if wikidataQualifiers then
		for qualK, qualV in pairs(wikidataQualifiers) do
			if isValueSnak(qualV) then
				table.insert(quals, snakToString(qualV))
			end
		end
	end
	return quals
end

p.getQualifierValue = function(frame)
	local propertyID = mw.text.trim(frame.args[1] or "")
	local qualifierID = mw.text.trim(frame.args[2] or "")
	local entityID = mw.wikibase.getEntityIdForCurrentPage()
	
	if entityID then 
		local claims = mw.wikibase.getBestStatements(entityID, propertyID)
		local out = {}
		if claims and #claims > 0 then
			for k, v in pairs(claims) do
				if p.isValueSnak(v.mainsnak) then
					if v.qualifiers and v.qualifiers[qualifierID] then
						for k2, v2 in pairs(v.qualifiers[qualifierID]) do
							if p.isValueSnak(v2) then
								table.insert(out, snakToString(v2))
							end
						end
					end
				end
			end
		end
		return table.concat(out, ", ")
	else
		return ""
	end
end

-- This is used to get a value like 'male' (for property p21) which won't be linked and numbers without the thousand separators
p.getRawValue = function(frame)
	local propertyID = mw.text.trim(frame.args[1] or "")
	local input_parm = mw.text.trim(frame.args[2] or "")
	local entity = mw.wikibase.getEntityObject()
	local claims = nil
	if entity and entity.claims then
		claims = entity.claims[propertyID]
	end
	if claims then
		local result = entity:formatPropertyValues(propertyID, mw.wikibase.entity.claimRanks).value
	
		-- if number type: remove thousand separators
		if (hasValueSnak(claims[1]) and claims[1].mainsnak.datavalue.type == "quantity") then
			result = mw.ustring.gsub(result, "(%d),(%d)", "%1%2")
		end
		return result
	else
		return ""
	end
end

p.getRawQualifierValue = function(frame)
	local propertyID = mw.text.trim(frame.args[1] or "")
	local qualifierID = mw.text.trim(frame.args[2] or "")
	local input_parm = mw.text.trim(frame.args[3] or "")
	if input_parm == "FETCH_WIKIDATA" then
		local entity = mw.wikibase.getEntity()
		if entity.claims[propertyID] ~= nil then
			local out = {}
			for k, v in pairs(entity.claims[propertyID]) do
				for k2, v2 in pairs(v.qualifiers[qualifierID]) do
					if isValueSnak(v2) then
						if v2.datavalue.value["numeric-id"] then
							out[#out + 1] = mw.wikibase.label("Q" .. v2.datavalue.value["numeric-id"])
						else
							out[#out + 1] = v2.datavalue.value
						end
					end
				end
			end
			local ret = table.concat(out, ", ")
			return string.upper(string.sub(ret, 1, 1)) .. string.sub(ret, 2)
		else
			return ""
		end
	else
		return input_parm
	end
end

p.extractDateFromClaim = function(claimOrSnak)
	local claim
	if claimOrSnak.mainsnak then claim = claimOrSnak.mainsnak else claim = claimOrSnak end
	if claim.datatype == 'time' and isValueSnak(claim) then
		local d = {}
		local iSOTimeSign = mw.ustring.sub(claim.datavalue.value.time, 1, 1)
		d.precision = tonumber(claim.datavalue.value.precision)
		d.calendarmodel = claim.datavalue.value.calendarmodel
		wdDate = DateUtils.parseWikidataDate(claim.datavalue.value.time, d.precision)
		if wdDate ~= nil then
			d.month = wdDate.month
		    d.day = wdDate.day
		else
			d.month = 1
			d.day = 1
		end
		if d.precision <= 9 then
			d.year = tonumber(mw.ustring.match(claim.datavalue.value.time, "%d+"))
			if iSOTimeSign == '-' then d.year = -d.year end
		elseif wdDate ~= nil then
			d.year = wdDate.year
		else
			d.year = 1
		end
		d.claim = claimOrSnak
		return d
	end
	return nil
end

p.findDateValues = function(propertyId, entityId)
	entityId = entityId or mw.wikibase.getEntityIdForCurrentPage()
	
	if entityId then
		local bestclaims = mw.wikibase.getBestStatements(entityId, propertyId)
		local bestdates = {}
		
		if bestclaims then for k, v in pairs(bestclaims) do
			if hasValueSnak(v) and v.mainsnak.datatype == 'time' then
				local d = p.extractDateFromClaim(v)
				table.insert(bestdates, d)
			elseif hasValueSnak(v) then
				local d = {}
				d.claim = v
				table.insert(bestdates, d)
			end
		end end
		return bestdates
	end
end

-- This is used to get a date value for date_of_birth (p569), etc. which won't be linked -- consolidate by testing if entity.claims[propertyID].mainsnak.datavalue.type is "time"
-- Dates are stored as 28 characters if the year  >99 -- e.g. +00000002014-01-01T00:00:00Z for 2014
-- Dates are stored as 26 characters if the year =<99 -- e.g. +000000050-01-01T00:00:00Z   for 50 CE
p.getDateValue = function(frame)
	local args = getArgs(frame, { frameOnly = true })
	local propertyId = args[1]
	local entityId = args[2] or mw.wikibase.getEntityIdForCurrentPage()
	if entityId then
		local claims = mw.wikibase.getAllStatements(entityId, propertyId)
		local out = {}
		if claims then for _,eachClaim in pairs(claims) do
			if hasValueSnak(eachClaim) then
				local extractedDate = p.extractDateFromClaim(eachClaim)
				table.insert(out, GregorianDate.displayDualDateIfInInterval(extractedDate, true))
			end
		end end
		return table.concat(out, ", ")
	else
		return ""
	end
end

p.getQualifierDateValue = function(frame)
	local propertyID = mw.text.trim(frame.args[1] or "")
	local qualifierID = mw.text.trim(frame.args[2] or "")
	local input_parm = mw.text.trim(frame.args[3] or "")
	local date_format = mw.text.trim(frame.args[4] or "dmy")
	if input_parm == "FETCH_WIKIDATA" then
		local entity = mw.wikibase.getEntity()
		if entity.claims[propertyID] ~= nil then
			local out = {}
			local dt = {}
			for k, v in pairs(entity.claims[propertyID]) do
				for k2, v2 in pairs(v.qualifiers[qualifierID]) do
					if isValueSnak(v2) then
						local d = v2.datavalue.value.time
						if #d > 26 then
							dt.year = string.sub(d, 9, 12)
							dt.month = string.sub(d, 14, 15)
							dt.day = string.sub(d, 17, 18)
						else
							dt.year = string.sub(d, 9, 10)
							dt.month = string.sub(d, 12, 13)
							dt.day = string.sub(d, 15, 16)
						end
						if date_format == "mdy" then
							out[#out + 1] = os.date("%B %e, %Y", os.time(dt))
						elseif date_format == "my" then
							out[#out + 1] = os.date("%B %Y", os.time(dt))
						elseif date_format == "y" then
							out[#out + 1] = os.date("%Y", os.time(dt))
						else
							out[#out + 1] = os.date("%e %B %Y", os.time(dt))
						end
					end
				end
			end
			return table.concat(out, ", ")
		else
			return ""
		end
	else
		return input_parm
	end
end

-- returns the value of the specified qualifier for the specified preferred value of the property of the specified entity (or the current entity)
p.findQualifierForPreferredPropertyValue = function(entityId, propertyId, qualifierId, separator)
	entityId = entityId or mw.wikibase.getEntityIdForCurrentPage()
	if entityId == nil then return nil end
	local claims = mw.wikibase.getAllStatements(entityId, propertyId)
	local out = {}
	if claims and claims[1] then
		for claimK, claimV in pairs(claims) do
			if claimV.rank == 'preferred' then
				local qual = claimV.qualifiers[qualifierId]
				if qual and qual.mainsnak then table.insert(out, snakToString(qual)) end
			end
		end
	end
	return table.concat(out, separator)
end

p.getQualifierForPreferredPropertyValue = function(frame)
	local args = getArgs(frame)
	local entityId = args['entityId'] or args[1]
	local propertyId = args['propertyId'] or args[2]
	local qualifierId = args['qualifierId'] or args[3]
	local separator = args['separator'] or args[4] or ', '
	
	return p.findQualifierForPreferredPropertyValue(entityId, propertyId, qualifierId, separator)
end

-- This is used to get the TA98 (Terminologia Anatomica first edition 1998) values like 'A01.1.00.005' (property P1323)
-- which are then linked to http://www.unifr.ch/ifaa/Public/EntryPage/TA98%20Tree/Entity%20TA98%20EN/01.1.00.005%20Entity%20TA98%20EN.htm
-- uses the newer mw.wikibase calls instead of directly using the snaks
-- formatPropertyValues returns a table with the P1323 values concatenated with ", " so we have to split them out into a table in order to construct the return string
p.getTAValue = function(frame)
	local ent = mw.wikibase.getEntityObject()
	local props = ent:formatPropertyValues('P1323')
	local out = {}
	local t = {}
	for k, v in pairs(props) do
		if k == 'value' then
			t = mw.text.split( v, ", ")
			for k2, v2 in pairs(t) do
				out[#out + 1] = "[http://www.unifr.ch/ifaa/Public/EntryPage/TA98%20Tree/Entity%20TA98%20EN/" .. string.sub(v2, 2) .. "%20Entity%20TA98%20EN.htm " .. v2 .. "]"
			end
		end
	end
	ret = table.concat(out, "<br> ")
	if #ret == 0 then
		ret = "Invalid TA"
	end
	return ret
end

-- returns the page id (Q...) of the current page or nothing of the page is not connected to Wikidata
p.pageId = p.getEntityId

-- the "qualifiers" and "snaks" field have a respective "qualifiers-order" and "snaks-order" field
-- use these as the second parameter and this function instead of the built-in "pairs" function
-- to iterate over all qualifiers and snaks in the intended order.
local function orderedpairs(array, order)
	if not order then return pairs(array) end
 
	-- return iterator function
    local i = 0
    return function()
        i = i + 1
        if order[i] then
            return order[i], array[order[i]]
        end
    end	
end

local function printDatavalueEntity(data, parameter)
	-- data fields: entity-type [string], numeric-id [int, Wikidata id]
	local id = "Q" .. data["numeric-id"]
	if parameter then
		if parameter == "link" then
			return "[[" .. (mw.wikibase.sitelink(id) or (":d:" .. id))  .. "|" ..  (mw.wikibase.label(id) or id)  .. "]]"
		else
			return data[parameter]
		end
	else
		if data["entity-type"] == "item" then return mw.wikibase.label("Q" .. data["numeric-id"]) or id else printError("unknown-entity-type") end
	end
end

local function printDatavalueTime(data, parameter)
	-- data fields: time [ISO 8601 time], timezone [int in minutes], before [int], after [int], precision [int], calendarmodel [wikidata URI]
	--   precision: 0 - billion years, 1 - hundred million years, ..., 6 - millennia, 7 - century, 8 - decade, 9 - year, 10 - month, 11 - day, 12 - hour, 13 - minute, 14 - second
	--   calendarmodel: e.g. http://www.wikidata.org/entity/Q1985727 for the proleptic Gregorian calendar or http://www.wikidata.org/wiki/Q11184 for the Julian calendar]
	if parameter then
		if parameter == "calendarmodel" then data.calendarmodel = mw.ustring.match(data.calendarmodel, "Q%d+") -- extract entity id from the calendar model URI
		elseif parameter == "time" then data.time = normalizeDate(data.time) end
		return data[parameter]
	else
		return formatDate(data.time, data.precision, data.timezone)
	end
end

local function findClaims(entity, property)
	if not property or not entity or not entity.claims then return end
 
	if mw.ustring.match(property, "^P%d+$") then
		-- if the property is given by an id (P..) access the claim list by this id
		return entity.claims[property]
	else
		property = mw.wikibase.resolvePropertyId(property)
		if not property then return end

		return entity.claims[property]
	end
end

local function getSnakValue(snak, parameter)
	-- snaks have three types: "novalue" for null/nil, "somevalue" for not null/not nil, or "value" for actual data
	if snak.snaktype == "novalue" then return i18n["novalue"]
	elseif snak.snaktype == "somevalue" then return i18n["somevalue"]
	elseif snak.snaktype ~= "value" then return nil, printError("unknown-snak-type")
	end
 
	-- call the respective snak parser
	if snak.datavalue.type == "string" then return snak.datavalue.value
	elseif snak.datavalue.type == "globecoordinate" then return printDatavalueCoordinate(snak.datavalue.value, parameter)
	elseif snak.datavalue.type == "quantity" then return printDatavalueQuantity(snak.datavalue.value, parameter)
	elseif snak.datavalue.type == "time" then return printDatavalueTime(snak.datavalue.value, parameter)
	elseif snak.datavalue.type == "wikibase-entityid" then return printDatavalueEntity(snak.datavalue.value, parameter)
	elseif snak.datavalue.type == "monolingualtext" then return printDatavalueMonolingualText(snak.datavalue.value, parameter)
	else return nil, printError("unknown-datavalue-type")
	end
end
 
local function getQualifierSnak(claim, qualifierId)
	-- a "snak" is Wikidata terminology for a typed key/value pair
	-- a claim consists of a main snak holding the main information of this claim,
	-- as well as a list of attribute snaks and a list of references snaks
	if qualifierId then
		-- search the attribute snak with the given qualifier as key
		if claim.qualifiers then
			local qualifier = claim.qualifiers[qualifierId]
			if qualifier then return qualifier[1] end
		end
		return nil, printError("qualifier-not-found")
	else
		-- otherwise return the main snak
		return claim.mainsnak
	end
end
 
local function getValueOfClaim(claim, qualifierId, parameter)
	local error
	local snak
	snak, error = getQualifierSnak(claim, qualifierId)
	if snak then
		return getSnakValue(snak, parameter)
	else
		return nil, error
	end
end

p.claim = function(frame)
	local property = frame.args[1] or ""
	local id = frame.args["id"]	-- "id" must be nil, as access to other Wikidata objects is disabled in Mediawiki configuration
	local qualifierId = frame.args["qualifier"]
	local parameter = frame.args["parameter"]
	local list = frame.args["list"]
	local references = frame.args["references"]
	local showerrors = frame.args["showerrors"]
	local default = frame.args["default"]
	if default then showerrors = nil end
 
	-- get wikidata entity
	local entity = mw.wikibase.getEntityObject(id)
	if not entity then
		if showerrors then return printError("entity-not-found") else return default end
	end
	-- fetch the first claim of satisfying the given property
	local claims = findClaims(entity, property)
	if not claims or not claims[1] then
		if showerrors then return printError("property-not-found") else return default end
	end
 
	-- get initial sort indices
	local sortindices = {}
	for idx in pairs(claims) do
		sortindices[#sortindices + 1] = idx
	end
	-- sort by claim rank
	local comparator = function(a, b)
		local rankmap = { deprecated = 2, normal = 1, preferred = 0 }
		local ranka = rankmap[claims[a].rank or "normal"] ..  string.format("%08d", a)
		local rankb = rankmap[claims[b].rank or "normal"] ..  string.format("%08d", b)
		return ranka < rankb
 	end
	table.sort(sortindices, comparator)
 
	local result
	local error
	if list then
		local value
		-- iterate over all elements and return their value (if existing)
		result = {}
		for idx in pairs(claims) do
			local claim = claims[sortindices[idx]]
			value, error =  getValueOfClaim(claim, qualifierId, parameter)
			if not value and showerrors then value = error end
			if value and references then value = value .. p.outputReferences(claim) end
			result[#result + 1] = value
		end
		result = table.concat(result, list)
	else
		-- return first element	
		local claim = claims[sortindices[1]]
		result, error = getValueOfClaim(claim, qualifierId, parameter)
		if result and references then result = result .. p.outputReferences(claim) end
	end
 
	if result then return result else
		if showerrors then return error else return default end
	end
end

p.getPreferredValue = function(frame)
	local args = getArgs(frame)
	local propertyID = args[1] or args['propertyId'] or args['pid']
	local entityID = args[2] or args['entityId'] or args['qid'] or mw.wikibase.getEntityIdForCurrentPage()
	if entityID == nil or propertyID == nil then return nil end
	local claims = mw.wikibase.getAllStatements(entityID, propertyID)
	if claims then
		-- if wiki-linked value output as link if possible
		if hasValueSnak(claims[1]) then
			local out = {}
			for k, v in pairs(claims) do
				if (v.rank == 'preferred') then
					local snakText = snakToString(v.mainsnak) .. p.outputReferences(v)

					if snakText ~= "" then
						out[#out + 1] = snakText
					end
				end
			end
			return table.concat(out, ", ")
		end
	else
		return ""
	end
	return ""
end


-- look into entity object
p.ViewSomething = function(frame)
	local data = mw.wikibase.getEntityObject()
	if not data then
		return nil
	end

	local f = frame.args[1] and frame or frame:getParent()

	local i = 1
	while true do
		local index = f.args[i]
		if not index then
			return tostring(data)
		end
		
		data = data[index] or data[tonumber(index)]
		if not data then
			return
		end
		
		i = i + 1
	end
end

p.Dump = function(frame)
	local data = mw.wikibase.getEntityObject()
	if not data then
		return i18n.warnDump
	end

	local f = frame.args[1] and frame or frame:getParent()

	local i = 1
	while true do
		local index = f.args[i]
		if not index then
			return "<pre>"..mw.dumpObject(data).."</pre>".. i18n.warnDump
		end

		data = data[index] or data[tonumber(index)]
		if not data then
			return i18n.warnDump
		end

		i = i + 1
	end
end

-- Returnează o singură valoare din cele cu rangul cel mai înalt cu referințele corespunzătoare pentru o proprietate specificată
-- De apelat din alte module
p.findOneValue = function(propertyID, entity)
	local entityId = nil
	if type(entity) == 'table' then entityId = entity.id
	elseif type(entity) == 'number' then entityId = StringUtils._prependIfMissing({tostring(entity), 'Q'})
	elseif type(entity) == 'string' then entityId = entity
	end
		
	entityId = entityId or mw.wikibase.getEntityIdForCurrentPage()
	if not entityId then return nil end
	local claims = mw.wikibase.getBestStatements(entityId, propertyID)
	if claims and 0 < #claims then
		for _,eachClaim in ipairs(claims) do
			if hasValueSnak(eachClaim) then
				return snakToString(eachClaim.mainsnak) .. p.outputReferences(eachClaim)
			end
		end
	end
	return nil
end	

-- Returnează o singură valoare din cele cu rangul cel mai înalt pentru o proprietate specificată
-- De apelat din alte module
p.findOneValueNoRef = function(propertyID, entity)
	local claims = nil
	if type(entity) == 'table' then
		claims = entity:getBestStatements(propertyID)
	else
		if type(entity) == 'number' then entity = 'Q' .. tostring(entity) end
		entity = entity or mw.wikibase.getEntityIdForCurrentPage()
		if not entity then return nil end
		claims = mw.wikibase.getBestStatements(entity, propertyID)
	end
	if claims and 0 < #claims then
		for _,eachClaim in ipairs(claims) do
			if hasValueSnak(eachClaim) then
				return snakToString(eachClaim.mainsnak)
			end
		end
	end
	return nil
end	

-- Returnează o singură valoare din cele cu rangul cel mai înalt pentru o proprietate specificată
-- De apelat din formate
p.getOneValueNoRef = function(frame)
	local propertyID, entityID
	propertyID, entityID = propAndEntity(frame.args[1], frame.args[2])
	local entity = nil
	if entityID ~= "" then entity = mw.wikibase.getEntityObject(entityID) end
	if entity == nil then entity = mw.wikibase.getEntityObject() end

	if entity == nil then return nil end
	return p.findOneValueNoRef(propertyID, entity)
end

-- Returnează o singură valoare din cele cu rangul cel mai înalt și cu referințele corespunzătoare pentru o proprietate specificată
-- De apelat din formate
p.getOneValue = function(frame)
	local args = getArgs(frame)
	local propertyID, entityID
	propertyID, entityID = propAndEntity(args[1], args[2])
	local entity = nil
	if entityID ~= "" then entity = mw.wikibase.getEntityObject(entityID) end
	if entity == nil then entity = mw.wikibase.getEntityObject() end
	
	if entity == nil then return nil end
	return p.findOneValue(propertyID, entity)
end

local function findLatestQualifierValueForOnePropertyOfAnEntity(entity, propertyId, qualifierId)
	if entity == nil then
		entity = mw.wikibase.getEntityObject()
	end
	if entity == nil then return '' end
	local claims = entity:getBestStatements(propertyId)
	if claims then
		if claims[1] then
			if claims[1].qualifiers then
				if claims[1].qualifiers[qualifierId] then
					local allQualifiers = {}
					for qk, qv in pairs(claims[1].qualifiers[qualifierId]) do
						table.insert(allQualifiers, qv)
					end
					local qIndex = 1
					local maxQTime = nil
					while maxQTime == nil and qIndex <= #allQualifiers do
						if allQualifiers[qIndex].datatype == 'time' then
							maxQTime = allQualifiers[qIndex]
						end
						qIndex = qIndex + 1
					end
					if maxQTime ~= nil then
						for i=qIndex,#allQualifiers do
							local msCrtQTime = tonumber(mw.language.new(lang.code):formatDate('U', allQualifiers[i].datavalue.value.time, nil))
							local msMaxQTime = tonumber(mw.language.new(lang.code):formatDate('U', maxQTime.datavalue.value.time, nil))
							if msCrtQTime > msMaxQTime then
								maxQTime = allQualifiers[i]
							end
						end
						if isValueSnak(maxQTime) then
							return formatDate(maxQTime.datavalue.value.time, maxQTime.datavalue.value.precision, maxQTime.datavalue.value.timezone)
						end
					end
					return ""
				else
					return ""
				end
			else
				return ""
			end
		else
			return ""
		end
	else 
		return ""
	end
end

-- Returnează valoarea cea mai recentă a qualifierului specificat pentru proprietea specificată a entității curente
-- De apelat din alte module
p.getLatestQualifierDateValueForOneProperty = function(frame)
 	local propertyId = mw.text.trim(frame.args[1] or "")
	local qualifierId = mw.text.trim(frame.args[2] or "")
	local entity = mw.wikibase.getEntityObject()
	
	return findLatestQualifierValueForOnePropertyOfAnEntity(entity, propertyId, qualifierId)
end

-- Returnează valoarea cea mai recentă a qualifierului specificat pentru proprietea specificată a entității curente
-- De apelat din formate
p.findLatestQualifierDateValueForOneProperty = function(propertyId, qualifierId)
	local entity = mw.wikibase.getEntityObject()
	return entity ~= nil and findLatestQualifierValueForOnePropertyOfAnEntity(entity, propertyId, qualifierId) or ''
end

-- Returnează un text ce conține referința pentru o proprietate; de apelat din formate
p.getReferenceForOneProperty = function(frame)
	local entity = mw.wikibase.getEntityObject()
	local propertyId = mw.text.trim(frame.args[1])
	local claims = entity:getBestStatements(propertyId)

	return p.outputReferences(claims[1])
end

-- Returnează o listă cu obiecte value asociate unei proprietăți a unei entități
-- Creată pentru a obține lista de coordonate a unui oraș în formă pură (listă cu obiectele value asociate)
-- Funcționează pentru: coordonate
-- TODO - de extins la mai multe tipuri de date, de exemplu texte multilingve, date calendaristice, itemuri wikibase
p.findDataValueObjects = function(entityId, propertyId)
	if propertyId == nil then return {} end
	entityId = entityId or mw.wikibase.getEntityIdForCurrentPage()
	if entityId == nil then return {} end
	local ret = {}
	local claims = mw.wikibase.getAllStatements(entityId, propertyId)
	if claims == nil then return ret end
	for claimIdx = 1, #claims do
		local snak = claims[claimIdx].mainsnak
		if isValueSnak(snak) then
			if snak.datatype == 'globe-coordinate' then
				table.insert(ret, snak.datavalue.value)
			end
		end
	end
	
	return ret		
end

p.findRoLabel = function(entityId)
	entityId = entityId or mw.wikibase.getEntityIdForCurrentPage()
	if entityId then
		return mw.wikibase.getLabelByLang(entityId, lang.code)
	end
	return nil
end
p.getRoLabel = function(frame)
	local args = getArgs(frame)
	return p.findRoLabel(args[1])
end

-- Returnează labelul unei entități în limba specificată; dacă nu se găsește, atunci se încearcă în limba wikiului și apoi în limba engleză
p.findLabel = function(entityId, languageCode, default)
	local entity, ret
	if entityId == nil then
		entityId = mw.wikibase.getEntityIdForCurrentPage()
		if not entityId then return default or '' end
	end
	if type(entityId) == 'number' then
		entityId = 'Q' .. tostring(entityId)
	end
	if type(entityId) == 'string' then 
		if languageCode then
			ret = mw.wikibase.getLabelByLang(entityId, languageCode)
		end
		if not ret then
			ret = mw.wikibase.getLabel(entityId)
		end
		return ret or default or mw.wikibase.getLabelByLang(entityId, 'en') or entityId
	end
	if type(entityId) == 'table' then
		local entity = entityId
		if not languageCode or languageCode == '' then languageCode = lang.code end
		ret = entity:getLabel(languageCode) or entity:getLabel(lang.code) or default or entity:getLabel('en')
		return ret
	end
	return default or ''
end

p.getLabel = function(frame)
	local args = getArgs(frame, {frameOnly=true})
	local entityId = args[1]
	local languageCode = args[2] or lang.code
	local default = args['default']
	return p.findLabel(entityId, languageCode, default)
end

local function formatCoordinates(entityId, args)
	entityId = entityId or mw.wikibase.getEntityIdForCurrentPage()
	if entityId == nil then return '' end
	local coordClaims = mw.wikibase.getAllStatements(entityId, 'P625')
	local formattedCoordsList = {}
	if coordClaims and coordClaims[1] then
		for coordIdx=1,#coordClaims do
			if coordClaims[coordIdx].mainsnak.datatype == 'globe-coordinate' and
			coordClaims[coordIdx].mainsnak.datavalue ~= nil then
				local latDir = coordClaims[coordIdx].mainsnak.datavalue.value.latitude < 0 and 'S' or 'N'
				local lonDir = coordClaims[coordIdx].mainsnak.datavalue.value.longitude < 0 and 'V' or 'E'
				local coordArgs = {tostring(math.abs(coordClaims[coordIdx].mainsnak.datavalue.value.latitude)), latDir, tostring(math.abs(coordClaims[coordIdx].mainsnak.datavalue.value.longitude)), lonDir}
				if args['display'] ~= nul then coordArgs['display'] = args['display'] end
				if args['format'] ~= nul then coordArgs['format'] = args['format'] end
				local coordsText = mw.getCurrentFrame():expandTemplate{title='Coord', args=coordArgs}

				table.insert(formattedCoordsList, coordsText)
			end
		end
	end
	return table.concat(formattedCoordsList, ', ')
end

p.getFormattedCoordinates = function(frame)
	local origArgs = getArgs(frame)
	return formatCoordinates(origArgs[1] or origArgs['entityId'], origArgs)
end

-- Returnează o numărul de valori cu cel mai mare rang pentru o proprietate specificată
-- De apelat din alte module
p.countBestValues = function(propertyID, entity)
	if type(entity) == 'table' then
		entity = entity.id
	elseif type(entity) == 'number' then
		entity = 'Q' .. tostring(entity)
	elseif entity == nil then
		entity = mw.wikibase.getEntityIdForCurrentPage()
		if entity == nil then return 0 end
	end
	local claims = mw.wikibase.getBestStatements(entity, propertyID)
	local outList = {}
	if not claims then return 0 end
	return #claims
end	

-- Returnează o singură valoare din cele cu rangul cel mai înalt cu referințele corespunzătoare pentru o proprietate specificată
-- De apelat din alte module
p.findBestValues = function(propertyID, entity)
	if type(entity) == 'table' then
		entity = entity.id
	elseif type(entity) == 'number' then
		entity = 'Q' .. tostring(entity)
	end
	if entity == nil then
		entity = mw.wikibase.getEntityIdForCurrentPage()
		if entity == nil then return {} end
	end
	if not StringUtils._emptyToNil({propertyID}) then return nil end
	local claims = mw.wikibase.getBestStatements(entity, propertyID)
	local outList = {}
	if claims then for claimIdx, actualClaim in pairs(claims) do
		if hasValueSnak(actualClaim) then
			table.insert(outList, snakToString(actualClaim.mainsnak) .. p.outputReferences(actualClaim))
		end
	end end
	return outList
end	

p._getBestValuesWithSeparator = function(entity, propertyID, sep)
	if propertyID == nil then return '' end
	local valueList = p.findBestValues(propertyID, entity)
	return valueList and table.concat(valueList, sep)
end

-- Returnează o listă a valorilor cu rangul cel mai înalt și cu referințele corespunzătoare pentru o proprietate specificată
-- De apelat din formate
p.getBestValuesWithSeparator = function(frame) 
	local sep = frame.args[1]
	if sep == nil or sep == '' then sep = ', ' end
	local propertyID = mw.text.trim(frame.args[2] or "")
	local entityID = mw.text.trim(frame.args[3] or "")
	local entity = nil
	if entityID ~= "" then entity = mw.wikibase.getEntityObject(entityID) end
	if entity == nil then entity = mw.wikibase.getEntityObject() end
	
	return p._getBestValuesWithSeparator(entity, propertyID, sep)
end

p.findLanguageText = function(propertyId, langcode, entityId)
	-- return label of a Wikidata entity in the given language or the default language of this Wikipedia site
	entityId = entityId or mw.wikibase.getEntityIdForCurrentPage()
	if not entityId then return nil end
	local returnValue = nil
	langcode = langcode or 'ro'
	for eachClaimIdx, eachClaim in pairs(mw.wikibase.getAllStatements(entityId, propertyId)) do
		if hasValueSnak(eachClaim) and eachClaim.mainsnak.datavalue then
			if eachClaim.mainsnak.datavalue.type == 'monolingualtext' then
				if eachClaim.mainsnak.datavalue.value.language == langcode then returnValue = eachClaim.mainsnak.datavalue.value.text end
			elseif eachClaim.mainsnak.datavalue.type == 'string' then
				returnValue = eachClaim.mainsnak.datavalue.value
			elseif eachClaim.mainsnak.datavalue.type == 'wikibase-entityid' then
				returnValue = p.findLabel(eachClaim.mainsnak.datavalue.value.id, langcode)
			end
		end
	end
		
	return returnValue
end

p.getLanguageText = function(frame)
	return p.findLanguageText(frame.args[1], frame.args[2] or 'ro', frame.args[3]) or ''
end

p.findBestValuesForLocalLanguage = function(propertyId, entityId)
	entityId = entityId or mw.wikibase.getEntityIdForCurrentPage()
	if not entityId then return nil end
	local bestValues = {}
	local bestRank = nil
	for eachClaimIdx, eachClaim in pairs(mw.wikibase.getAllStatements(entityId, propertyId)) do
		if (hasValueSnak(eachClaim) and eachClaim.qualifiers and eachClaim.qualifiers['P407'] and 0 < #(eachClaim.qualifiers['P407'])) then
			if hasBetterRank(eachClaim, bestRank) then
					for _,eachLangQual in ipairs(eachClaim.qualifiers['P655'] or {}) do
						if isValueSnak(eachLangQual) and eachLangQual.datavalue.value.id == 'Q7913' then
							bestRank = eachLangQual.rank
							if not bestRank or mw.wikibase.entity.claimRanks['RANK_' .. mw.ustring.upper(eachClaim.rank)] > mw.wikibase.entity.claimRanks['RANK_' .. mw.ustring.upper(bestRank)] then
								bestValues = {}
							end
							table.insert(bestValues, snakToString(eachClaim.mainsnak))
						end
					end
			end
		end
	end
	return bestValues
end

p.getBestValuesForLocalLanguageWithSeparator = function(frame)
	return table.concat(p.findBestValuesForLocalLanguage(frame.args[2], frame.args[3]) or {}, frame.args[1])
end

p.findShortestAlias = function(entityId)
	return p.findLinkToItem(entityId, false, false, true)
end

p.findImageAndCaption = function(entityId, props)
	local wikidataImages = nil
	props = props or {'P2716', 'P18', 'P10'}
	-- order is important, we will only retrieve one property
	for _,prop in ipairs(props) do
		wikidataImages = p.findBestClaimsForProperty(entityId, prop)
		-- stop on the first match
		if wikidataImages and #wikidataImages > 0 then break end
	end
	local imageName, imageCaption
	if wikidataImages and #wikidataImages > 0 then
		local wikidataImage = wikidataImages[1]
		if hasValueSnak(wikidataImage) and wikidataImage.mainsnak.datavalue then
			imageName = wikidataImage.mainsnak.datavalue.value
			local roDescr = ''
			if wikidataImage.qualifiers and wikidataImage.qualifiers['P2096'] then
				for _,eachImageDescrLangString in pairs(wikidataImage.qualifiers['P2096']) do
					if isValueSnak(eachImageDescrLangString) and eachImageDescrLangString.datavalue and eachImageDescrLangString.datavalue.value.language == 'ro' then
						roDescr = eachImageDescrLangString.datavalue.value.text or roDescr
					end
				end
			end
			imageCaption = roDescr
		end
	end
	return imageName, imageCaption
end

p.findLinkToWikidataItem = function(itemId, prefix)
	libraryUtil.checkType('findLinkToWikidataItem', 2, prefix, 'string', false)
	local qId = StringUtils._prependIfMissing({itemId, prefix})
	local itemLabel = p.findLabel(qId)
	return '[[:d:' .. (prefix == 'P' and 'Property:' or '') .. qId .. '|' .. (itemLabel or '') .. ' ' .. tostring(mw.html.create('small'):wikitext('(' .. qId .. ')')) .. ']]'
end

p.getLinkToWikidataItem = function(frame)
	local args = getArgs(frame)
	local prefix = args['prefix'] or 'Q'
	if not args[1] then return "''id de " .. (prefix == 'P' and 'proprietate' or 'item') .. " Wikidata nespecificat''" end
	return p.findLinkToWikidataItem(args[1], prefix)
end

p.formatExternalLink = function(pId, qId, text)
	local formatterUrl = p.findOneValueNoRef('P1630', pId) or '$1'
	local urlComponent = text or p.findOneValueNoRef(pId, qId)
	if not urlComponent then return nil end
	local formattedLink = mw.ustring.gsub(formatterUrl, '%$1', text or urlComponent)
	return formattedLink
end

p.getFormattedExternalLink = function(frame)
	local args = getArgs(frame)
	local pId = args['pid'] or args[1]
	local qId = args['qid'] or args[2]
	local text = args['text']
	
	return p.formatExternalLink(pId, qId, text)
end
p.isFemale = function(qId)
	local entityId = qId or mw.wikibase.getEntityIdForCurrentPage()
	if not entityId then return false end
	local genders = mw.wikibase.getBestStatements(entityId, 'P21')
	local feminineIds = { 6581072, 1052281, 43445 }
	
	if genders then for genderIdx,eachGender in ipairs(genders) do
		if hasValueSnak(eachGender) then for _,eachFemId in ipairs(feminineIds) do
			if eachFemId == eachGender.mainsnak.datavalue.value['numeric-id'] then return true end
		end end
	end end
	return false
end

p.findClaimForTimestamp = function(entityId, propertyId, timestamp, lang)
	local propClaims = p.findSortedClaimsForProperty(entityId, propertyId)

	local bestClaim = nil
	if propClaims then for _,eachPropClaim in ipairs(propClaims) do
		if hasValueSnak(eachPropClaim) and isClaimTrue(eachPropClaim) then
			
			if eachPropClaim.mainsnak.datavalue.type ~= 'monolingualtext' or StringUtils._substringBefore({eachPropClaim.mainsnak.datavalue.value.language, '-'}) == StringUtils._substringBefore({lang or 'ro', '-'}) then
				local before = nil
				local after = nil
				if eachPropClaim.qualifiers then
					if eachPropClaim.qualifiers['P580'] and eachPropClaim.qualifiers['P580'][1] and isValueSnak(eachPropClaim.qualifiers['P580'][1]) then
						after = GregorianDate.convertToGregorianIfInInterval(DateUtils.extractDateFromWikidataSnak(eachPropClaim.qualifiers['P580'][1]))
					end
					if eachPropClaim.qualifiers['P582'] and eachPropClaim.qualifiers['P582'][1] and isValueSnak(eachPropClaim.qualifiers['P582'][1]) then
						before = GregorianDate.convertToGregorianIfInInterval(DateUtils.extractDateFromWikidataSnak(eachPropClaim.qualifiers['P582'][1]))
					end
				end

				if timestamp then
					--if before and DateUtils.compare(timestamp, before) > 0 then --the claim list is sorted by before
					--	break
					--else
						if after == nil and before and DateUtils.compare(timestamp, before) < 0 then
							return eachPropClaim
						elseif after and before and DateUtils.compare(timestamp, before) < 0 and DateUtils.compare(timestamp, after) > 0 then 
							return eachPropClaim
						elseif after and before == nil and DateUtils.compare(timestamp, after) > 0 then
							return eachPropClaim
						end
					--end
				end
				if not before then
					if not bestClaim and hasBetterRank(eachPropClaim, 'normal') or (bestClaim and hasBetterRank(eachPropClaim, bestClaim.rank)) then
						bestClaim = eachPropClaim
					end
				end
			end
		end
	end end

	if bestClaim then
		return bestClaim
	else
		return nil
	end
end

p.formatOnlineLink = function(linkProp, entityId)
	local linkId = p.findOneValueNoRef(linkProp, entityId)
	if linkId then
		local linkFormatter = p.findOneValueNoRef('P1630', linkProp) or '$1'
		if linkFormatter then
			linkId = mw.ustring.gsub(linkId, '%%', '%%%%')
			local link = mw.ustring.gsub(linkFormatter, '$1', linkId)
			return link
		end
	end
	return nil
end

p.getFormattedOnlineLink = function(frame)
	local args = getArgs(frame)
	return p.formatOnlineLink(args[1], args[2] or mw.wikibase.getEntityIdForCurrentPage())
end

p.findOnlineLinks = function(onlinelinksprops, entityId, extraProps)
	local onlinelinks = {}
	local onlinelinksprops = onlinelinksprops and #onlinelinksprops > 0 and onlinelinksprops or {'P856', 'P1581', 'P2013', 'P2002', 'P2847', 'P345', 'P3265', 'P3579', 'P3435', 'P2003', 'P2471', 'P2397', 'P953', 'P1065', 'P4173', 'P4431', 'P1968', 'P6634', 'P4264', 'P2572', 'P7085', 'P3789'}
	if extraProps and type(extraProps) == 'table' then onlinelinksprops = Set.valueUnion(onlinelinksprops, extraProps) end
	for _,eachOnlineLinkProp in ipairs(onlinelinksprops) do
		local link = p.formatOnlineLink(eachOnlineLinkProp)
		if link then
			local linkentities = p.getBestEntityIdsList(eachOnlineLinkProp, 'P1629')
			if linkentities and #linkentities > 0 then
				local linklabel = p.findLabel(StringUtils._prependIfMissing({linkentities[1], 'Q'}))
				table.insert(onlinelinks, '[' .. link .. ' ' .. linklabel .. ']')
			end
		end
	end
	return onlinelinks
end

p.getOnlineLinks = function(frame)
	local args = getArgs(frame)
	local _,entityId = propAndEntity(StringUtils._prependIfMissing({args['qid'], 'Q'}))
	local extraPropsStr = args['extras']
	local extraProps = nil
	if  extraPropsStr then extraProps = mw.text.split(extraPropsStr, ',', true) end
	local onlinelinksprops = {}
	local passedProps = TableTools.compressSparseArray(args)
	for __,eachProp in ipairs(passedProps) do
		if mw.ustring.match(eachProp, 'P%d+') == eachProp then
			table.insert(onlinelinksprops, eachProp)
		end
	end
	
	return table.concat(p.findOnlineLinks(onlinelinksprops, entityId, extraProps), tostring(mw.html.create('br')))
end

p.findLanguageIndexedLabelsFromProperty = function(entityId, propertyId)
	local entity = nil
	if entityId then
		entityId = StringUtils._prependIfMissing({tostring(entityId), 'Q'})
	else
		entityId = mw.wikibase.getEntityIdForCurrentPage()
	end
	local titles = {}
	for _,eachTitleClaim in ipairs(mw.wikibase.getAllStatements(entityId, propertyId)) do
		if hasValueSnak(eachTitleClaim) then
			titles[eachTitleClaim.mainsnak.datavalue.value.language] = eachTitleClaim.mainsnak.datavalue.value.text
		end
	end
	return titles
end

p.findTitleOfWork = function(entityId)
	local entity = nil
	if entityId then
		entityId = StringUtils._prependIfMissing({tostring(entityId), 'Q'})
	else
		entityId = mw.wikibase.getEntityIdForCurrentPage()
	end

	--- this comes first just for performance reasons
	if entityId then
		local localLabel, localLang = mw.wikibase.getLabelWithLang(entityId)
		if localLabel and localLang == 'ro' then
			return localLabel
		end
	else
		return nil
	end

	local langs = {}
	for _,eachProperty in ipairs({'P364', 'P407'}) do
		for __,eachLangClaim in ipairs(mw.wikibase.getAllStatements(entityId, eachProperty)) do

			if hasValueSnak(eachLangClaim) then
				local langEntityId = eachLangClaim.mainsnak.datavalue.value.id
				if LangUtils[langEntityId] then
					table.insert(langs, { qid = langEntityId, code = LangUtils[langEntityId] })
				elseif langEntityId then
					for ___,eachLangCodeClaim in ipairs(mw.wikibase.getAllStatements(langEntityId, 'P424')) do
						if hasValueSnak(eachLangCodeClaim) and isClaimTrue(eachLangCodeClaim) then
							local sanitizedLangCode = StringUtils._substringBefore({eachLangCodeClaim.mainsnak.datavalue.value, '-'})
							sanitizedLangCode = StringUtils._substringBefore({sanitizedLangCode, '_'})
							table.insert(langs, { qid = langEntityId, code = sanitizedLangCode} )
						end
					end
				end
			end
		end
	end
	table.insert(langs, {qid = 'Q1860', code = 'en'})

	
	local titles = p.findLanguageIndexedLabelsFromProperty(entityId, 'P1476')
	if titles['ro'] then return titles['ro'] end
	local nonLatinLangs = {}
	for _,eachlanguage in ipairs(langs) do
		local langText = nil
		if isWritingSystemLatn(eachlanguage.qid) then
			langText = wrapInLangSpan(titles[eachlanguage.code] or mw.wikibase.getLabelByLang(entityId, eachlanguage.code), eachlanguage.code)
		elseif Transliteration.isTransliterationSupported(eachlanguage.code) then
			langText = wrapInLangSpan(Transliteration.transliterate(titles[eachlanguage.code] or mw.wikibase.getLabelByLang(entityId, eachlanguage.code), eachlanguage.code), eachlanguage.code)
		else
			table.insert(nonLatinLangs, eachlanguage.code)
		end
		if langText then return langText end
	end
	for _,langCode in ipairs(nonLatinLangs) do
		langText = wrapInLangSpan(titles[langCode] or mw.wikibase.getLabelByLang(entityId, langCode), langCode)
		if langText then return langText end
	end
	return entityId
end

p.findTitlesOfWorksFromProperty = function(propertyId, entityId, limit)
	if limit == nil then limit = 10 end
	if entityId then entityId = StringUtils._prependIfMissing({tostring(entityId), 'Q'})
	else entityId = mw.wikibase.getEntityIdForCurrentPage() end

	if not entityId then return nil end
	
	local ret = {}
	local bestWorksClaims = mw.wikibase.getBestStatements(entityId, propertyId)
	for workIdx = 1,math.min(limit, #bestWorksClaims) do
		local eachWorkClaim = bestWorksClaims[workIdx]
		if hasValueSnak(eachWorkClaim) then
			local workId = eachWorkClaim.mainsnak.datavalue.value.id
			table.insert(ret, computeLinkToItem(workId, true, nil, function() return p.findTitleOfWork(workId) end))
		end
	end
	return ret
end


p.getTitleOfWorkFromPropertyWithSeparator = function(frame)
	local args = getArgs(frame)
	local sep = args[1]
	local propertyId, entityId = propAndEntity(args[2], args[3])
	local limitStr = args['limit']
	local limit = tonumber(limitStr or '10')
	local retListRaw = p.findTitlesOfWorksFromProperty(propertyId, entityId, limit) or {}
	local retList = {}
	for _,eachRetVal in ipairs(retListRaw) do
		table.insert(retList, "''" .. eachRetVal .. "''")
	end
	return table.concat(retList, sep)
end

p.loadOneValueInChain = function(...)
	local chain = arg.n == 0 and {} or type(arg[1]) == 'table' and arg[1] or arg
	local crtChainElementId = mw.wikibase.getEntityIdForCurrentPage()
	local crtChainClaim = nil
	local crtChainSnak = nil
	local coordComponent = nil
	local raw = false
	local label = false
	local ref = false
	for _,eachChainRing in ipairs(chain) do
		if mw.ustring.match(eachChainRing, 'Q%d+') == eachChainRing then
			crtChainElementId = eachChainRing
		elseif mw.ustring.match(eachChainRing, '_P%d+') and crtChainSnak then
			local qualPropertyId = mw.ustring.sub(eachChainRing, 2, mw.ustring.len(eachChainRing))
			if crtChainClaim.qualifiers and crtChainClaim.qualifiers[qualPropertyId] and #(crtChainClaim.qualifiers[qualPropertyId]) > 0 then
				local qualIdx = 1
				local propertyQuals = crtChainClaim.qualifiers[qualPropertyId]
				while qualIdx <= #propertyQuals and not isValueSnak(propertyQuals[qualIdx]) do
					qualIdx = qualIdx + 1
				end
				if qualIdx <= #propertyQuals and isValueSnak(propertyQuals[qualIdx]) then
					local firstValueSnakIdx = qualIdx
					if propertyQuals[qualIdx].datavalue.type == 'monolingualtext' then
						while qualIdx <= #propertyQuals and (not isValueSnak(propertyQuals[qualIdx]) or propertyQuals[qualIdx].datavalue.value.language ~= 'ro') do
							qualIdx = qualIdx + 1
						end
					end
					crtChainSnak = qualIdx <= #propertyQuals and isValueSnak(propertyQuals[qualIdx]) and propertyQuals[qualIdx] or propertyQuals[firstValueSnakIdx]
				else 
					return nil
				end
			else 
				return nil
			end
		elseif mw.ustring.match(eachChainRing, 'P%d+') == eachChainRing then
			if isValueSnak(crtChainSnak) and crtChainSnak.datavalue.type == 'wikibase-entityid' then
				crtChainElementId = crtChainSnak.datavalue.value.id
				crtChainClaim = nil
				crtChainSnak = nil
			end
			if crtChainElementId and not crtChainSnak then
				local propertyClaims = mw.wikibase.getBestStatements(crtChainElementId, eachChainRing)
				if #propertyClaims < 1 then return nil end
				local claimIdx = 1
				
				if propertyClaims[claimIdx].mainsnak.datavalue and propertyClaims[claimIdx].mainsnak.datavalue.type == 'monolingualtext' then
					while claimIdx <= #propertyClaims and (not hasValueSnak(propertyClaims[claimIdx]) or propertyClaims[claimIdx].mainsnak.datavalue.value.language ~= 'ro') do
						claimIdx = claimIdx + 1
					end
				else
					while claimIdx <= #propertyClaims and not hasValueSnak(propertyClaims[claimIdx]) do
						claimIdx = claimIdx + 1
					end
				end
				if claimIdx <= #propertyClaims and hasValueSnak(propertyClaims[claimIdx]) then
					crtChainClaim = propertyClaims[claimIdx]
					crtChainSnak = crtChainClaim.mainsnak
				else 
					return nil
				end
			end
		elseif eachChainRing == 'lat' or eachChainRing == 'long' then
			coordComponent = eachChainRing .. 'itude'
		elseif eachChainRing == 'raw' then
			raw = true
		elseif eachChainRing == 'label' then
			if crtChainSnak then crtChainElementId = crtChainSnak.datavalue.value.id end
			crtChainClaim = nil
			crtChainSnak = nil
			label = true
		elseif eachChainRing == 'ref' then
			ref = true
		end
	end
	if ref and crtChainClaim then
		return p.outputReferences(crtChainClaim)
	end
	if crtChainSnak then
		if coordComponent and crtChainSnak.datavalue.value[coordComponent] then return crtChainSnak.datavalue.value[coordComponent] end
		return raw and printRawValue(crtChainSnak) or snakToString(crtChainSnak)
	end
	if crtChainElementId then 
		if raw then return crtChainElementId
		elseif label then return p.findLabel(crtChainElementId)
		else return p.findLinkToItem(crtChainElementId) end
	end
	return nil
end

p.getOneValueInChain = function(frame)
	local args = getArgs(frame)
	return p.loadOneValueInChain(args)
end

local function isA(entityId, typeIds)
	local entityTypes = p.findClaimsForProperty(entityId, 'P31')
	entityId = type(entityId) == 'number' and ('Q' .. tostring(entityId)) or entityId
	if entityTypes then for _,eachEntityType in ipairs(entityTypes) do
		if isClaimTrue(eachEntityType) and hasValueSnak(eachEntityType) then
			if TableTools.contains(typeIds, eachEntityType.mainsnak.datavalue.value.id) then
				return true
			end
			local recurseSuccess, recursivelyFoundType = pcall(mw.wikibase.getReferencedEntityId, eachEntityType.mainsnak.datavalue.value.id, 'P279', typeIds)
			if recurseSuccess and recursivelyFoundType then
				return true
			end
			if not recurseSuccess then
				--manually recurse one level
				local superTypeClaims = mw.wikibase.getBestStatements(eachEntityType.mainsnak.datavalue.value.id, 'P279')
				if superTypeClaims then for __,eachSuperTypeClaim in ipairs(superTypeClaims) do
					if hasValueSnak(eachSuperTypeClaim) and TableTools.contains(typeIds, eachSuperTypeClaim.mainsnak.datavalue.value.id) then
						return true
					end
				end end
			end
		end
	end end
	return false
end
p.isA = isA

p.findAnniversary = function(propertyId, entityId, step)
	local _date = p.findDateValues(propertyId, entityId)
	if _date and _date[1] then 
		-- no year 0
		if _date[1].year < 0 then _date[1].year = _date[1].year + 1 end
		if (os.date("*t").year - _date[1].year) % step == 0 then
			return (os.date("*t").year - _date[1].year)
		end
	else
		return 0
	end
end

p.getAnniversary = function(frame)
	local args = getArgs(frame)
	return p.findAnniversary(args[1], args[2], args[3])
end

p.findLocationChain = function(entity)
	local q = entity or mw.wikibase.getEntityIdForCurrentPage()
	if not q then return nil end
	local countryQId = p.loadOneValueInChain({q, 'P17', 'raw'})
	local adminUnitQId = p.loadOneValueInChain({q, 'P131', 'raw'})
	if not adminUnitQId or adminUnitQId == countryQId then return p.findLinkToItem(countryQId, true, false, false) end
	return p.findLinkToItem(adminUnitQId, true, false, false) .. ', ' .. p.findLocationChain(adminUnitQId)
end

p.getLocationChain = function(frame)
	local args = getArgs(frame)
	return p.findLocationChain(args.q)
end

p.findAddress = function(qId)
	local q = qId or mw.wikibase.getEntityIdForCurrentPage()
	if not q then return nil,nil end

	p159claims = p.findBestClaimsForProperty(q, 'P159')
	addrs = {}
	if p159claims then for _,eachP159claim in ipairs(p159claims) do
		if hasValueSnak(eachP159claim) then
			local addrParts = { p.printSnak(eachP159claim.mainsnak) }
			if eachP159claim.qualifiers then
				for __,qual in ipairs({'P6375', 'P669', 'P670', 'P281', 'P17'}) do
					if eachP159claim.qualifiers[qual] then for ___,eachP159ClaimQual in ipairs(eachP159claim.qualifiers[qual]) do
						if isValueSnak(eachP159ClaimQual) then
							table.insert(addrParts, p.printSnak(eachP159ClaimQual))
						end
					end end
				end
			end
			table.insert(addrs, table.concat(addrParts, ', '))
		end
	end end
	if #addrs > 0 then
		return table.concat(addrs, tostring(mw.html.create('br'))), 'P159'
	end
	
	p6375 = p.findOneValue('P6375', q)
	if p6375 then return p6375, 'P6375' end
	
	p669claims = p.findBestClaimsForProperty(q, 'P669')
	local addrs = {}
	if p669claims then for _,eachP669Claim in ipairs(p669claims) do
		if hasValueSnak(eachP669Claim) then
			local thisAddr = p.printSnak(eachP669Claim.mainsnak)
			if eachP669Claim.qualifiers and eachP669Claim.qualifiers['P670'] then
				local streetNumbers = {}
				for __,eachP669ClaimP670Qual in ipairs(eachP669Claim.qualifiers['P670']) do
					if isValueSnak(eachP669ClaimP670Qual) then
						table.insert(streetNumbers, eachP669ClaimP670Qual.datavalue.value)
					end
				end
				if #streetNumbers > 0 then
					thisAddr = thisAddr .. ' nr. ' .. table.concat(streetNumbers, ', ')
				end
			end
			table.insert(addrs, thisAddr)
		end
	end end
	if #addrs > 0 then
		table.insert(addrs, 1, p.findLocationChain(q))
		return table.concat(addrs, '; ')
	end

	local p2795text = p.loadOneValueInChain({'P2795'})
	if p2795text then return p2795text end
	return nil
end

p.getAddress = function(frame)
	local args = getArgs(frame)
	local addr, srcProp = p.findAddress(args[1])
	return addr
end

p.hasValueSnak = hasValueSnak

return p