PhotosLocation

From Wikipedia, the free encyclopedia

--script that retrieves basic data stored in Wikidata, for the datamodel, see https://www.mediawiki.org/wiki/Extension:Wikibase_Client/Lua



local wd = {}



-- creation of a subobject to store comparison funtions, used for sorting claims

-- to be able to build more complex sorts like topological sorts



wd.compare = {}



local modules = { }

local modulesNames = {

	reference = 'Module:Wikidata/Références',

	i18n = 'Module:Wikidata/I18n',

	globes = 'Module:Wikidata/Globes',

	langhierarchy = 'Module:Wikidata/Hiérarchie des langues',

	langcodes = 'Module:Dictionnaire Wikidata/Codes langue', -- big, infrequently useda

	invertedlangcodes = 'Module:Dictionnaire Wikidata/Codes langue/inversé',



	linguistic = 'Module:Linguistique',

	datemodule = 'Module:Date',

	formatDate = 'Module:Date complexe',

	formatNum = 'Module:Conversion',

	langmodule = 'Module:Langue',

	cite = 'Module:Biblio',

	weblink = 'Module:Weblink'

}



local function loadModule( t, key )

	if modulesNameskey then

		local m = require( modulesNameskey )

		tkey = m

		return m

	end

end

setmetatable( modules, { __index = loadModule } )



local datequalifiers = {'P585', 'P571', 'P580', 'P582', 'P1319', 'P1326'}



-- === I18n ===

local defaultlang = mw.getContentLanguage():getCode()



function wd.translate(str, rep1, rep2)

	str = modules.i18nstr or str

	if rep1 and (type (rep1) == 'string') then

		str = str:gsub('$1', rep1)

	end

	if rep2 and (type (rep2) == 'string')then

		str = str:gsub('$2', rep2)

	end

	return str

end



local function addCat(cat, sortkey)

	if sortkey then

		return  '[[Category:' .. cat .. '|' .. sortkey .. ']]'

	end

	return '[[Category:' .. cat  .. ']]'

end



local function formatError( key , category, debug)

    if debug then

        return error(modules.i18nkey or key)

    end

    if category then

        return addCat(category, key)

    else

        return addCat('cat-unsorted-issue', key)

    end

end



-- 



function wd.isSpecial(snak)

	return (snak.snaktype ~= 'value')

end



function wd.getId(snak)

	if (snak.snaktype == 'value') then

		return 'Q' .. snak.datavalue.value'numeric-id'

	end

end



function wd.getNumericId(snak)

	if (snak.snaktype == 'value') then

		return snak.datavalue.value'numeric-id'

	end

end



function wd.getMainId(claim)

	return wd.getId(claim.mainsnak)

end



function wd.entityId(entity)

	if type(entity) == 'string' then

		return entity

	elseif type(entity) == 'table' then

		return entity.id

	end

end



function wd.getEntityIdForCurrentPage()

	return mw.wikibase.getEntityIdForCurrentPage()

end



-- function that returns true if the "qid" parameter is the qid 

-- of the item that is linked to the calling page

function wd.isPageOfQId(qid) 

	local self_id = mw.wikibase.getEntityIdForCurrentPage() 

	return self_id ~= nil and qid == self_id 

end



function wd.getEntity( val ) 

	if type(val) == 'table' then

		return val

	end

	if val == '-' then

		return nil

	end

	if val == '' then

		val = nil

	end

	return mw.wikibase.getEntity(val)

end



function wd.splitStr(val) -- transforme en table les chaînes venant du Wikitexte qui utilisent des virgules de séparation

	if type(val) == 'string' then

		val = mw.text.split(val, ",")

	end

	return val

end



function wd.isHere(searchset, val)

	for i, j in pairs(searchset) do

		if val == j then

			return true

		end

	end

	return false

end





local function wikidataLink(entity)

	local name =':d:'

	

	if type(entity) == 'string' then

		if entity:match("P[0-9+]") then

			entity = "Property:" .. entity

		end

		return name .. entity

	elseif type(entity) == 'table' then

		if entity"type" == "property" then

			name = ":d:Property:"

		end

		return name .. entity.id

	elseif type(entity) == nil then

		return formatError('entity-not-found')

	end

end



function wd.siteLink(entity, project, lang)

	-- returns 3 values: a sitelink (with the relevant prefix) a project name and a language

	lang = lang or defaultlang

	if (type(project) ~= 'string') then

		project = 'wiki'

	end

	project = project:lower()

	if project == 'wikipedia' then

		project = 'wiki'

	end

	if type(entity) == 'string' and (project == 'wiki') and ( (not lang or lang == defaultlang) ) then -- évite de charger l'élément entier

		return  mw.wikibase.sitelink(entity), 'wiki', defaultlang

	end

	if project == 'wikidata' then

		return wikidataLink(entity), 'wikidata'

	end

	local projects = {

		-- nom = {préfixe sur Wikidata, préfix pour les liens sur Wikipédia, ajouter préfixe de langue}

		wiki = {'wiki', nil, true}, -- wikipedia

		commons = {'commonswiki', 'commons', false},

		commonswiki = {'commonswiki', 'commons', false},

		wikiquote = {'wikiquote', 'q', true},

		wikivoyage = {'wikivoyage', 'voy', true},

		wikibooks = {'wikibooks', 'b', true},

		wikinews = {'wikinews', 'n', true},

		wikiversity = {'wikiversity', 'v', true},

		wikisource = {'wikisource', 's', true},

		wiktionary = {'wiktionary', 'wikt', true},

		specieswiki = {'specieswiki', 'species', false},

		metawiki = {'metawiki', 'm', false},

		incubator = {'incubator', 'incubator', false},

		outreach = {'outreach', 'outreach', false},

		mediawiki = {'mediawiki', 'mw', false}

	}



	local entityid = entity.id or entity



	local projectdata = projectsproject:lower()]

	if not projectdata then -- defaultlink might be in the form "dewiki" rather than "project: 'wiki', lang: 'de' "

		for k, v in pairs(projects) do

			if project:match( k .. '$' ) 

				and mw.language.isKnownLanguageTag(project:sub(1, #project-#k))

			then

				lang = project:sub(1, #project-#k)

				project = project:sub(#lang + 1, #project)

				projectdata = projectsproject

				break

			end

		end

		if not mw.language.isKnownLanguageTag(lang) then

			return --formatError('invalid-project-code', projet or 'nil')

		end

	end

	if not projectdata then

		return -- formatError('invalid-project-code', projet or 'nil')

	end

	

	local linkcode = projectdata1

	local prefix = projectdata2

	local multiversion = projectdata3

	if multiversion then

		linkcode = lang .. linkcode

	end

	local link = mw.wikibase.getSitelink(entityid, linkcode)

	if not link then

		return nil

	end

	

	if prefix then

		link = prefix .. ':' .. link

	end

	if multiversion then

		link = ':' .. lang .. ':' .. link

	end

	return link, project, lang

end



-- add new values to a list, avoiding duplicates

function wd.addNewValues(olditems, newitems, maxnum, stopval)

	if not newitems then

		return olditems

	end

	for _, qid in pairs(newitems) do

		if stopval and (qid == stopval) then

			table.insert(olditems, qid)

			return olditems

		end

		if maxnum and (#olditems >= maxnum) then

			return olditems

		end

		if not wd.isHere(olditems, qid) then

			table.insert(olditems, qid)

		end

	end

	return olditems

end



--=== FILTER CLAIMS ACCORDING TO VARIOUS CRITERIA : FUNCTION GETCLAIMS et alii ===



local function notSpecial(claim)

	local type

	

	if claim.mainsnak ~= nil then

		type = claim.mainsnak.snaktype

	else

		-- condition respectée quand showonlyqualifier est un paramètre renseigné

		-- dans ce cas, claim n'est pas une déclaration entière, mais UNE snak qualifiée du main snak

		type = claim.snaktype

	end

	

	return type == 'value'

end



local function hasTargetValue(claim, targets) -- retourne true si la valeur est dans la liste des target, ou si c'est une valeur spéciale filtrée séparément par excludespecial

	local id = wd.getMainId(claim)

	local targets = wd.splitStr(targets)

	return wd.isHere(targets, id) or wd.isSpecial(claim.mainsnak)

end



local function excludeValues(claim, values) -- true si la valeur n'est pas dans la liste, ou si c'est une valeur spéciale (filtrée à part par excludespecial)

	return wd.isSpecial(claim.mainsnak) or not ( hasTargetValue(claim, values) )

end



local function bestRanked(claims)

	if not claims then

		return nil

	end

	local preferred, normal = {}, {}

	for i, j in pairs(claims) do

		if j.rank == 'preferred' then

			table.insert(preferred, j)

		elseif j.rank == 'normal' then

			table.insert(normal, j)

		end

	end

	if #preferred > 0 then

		return preferred

	else

		return normal

	end

end



local function withRank(claims, target)

	if target == 'best' then

		return bestRanked(claims)

	end

	local newclaims = {}

	for pos, claim in pairs(claims)  do

		if target == 'valid' then

			if claim.rank ~= 'deprecated' then

				table.insert(newclaims, claim)

			end

		elseif claim.rank == target then

			table.insert(newclaims, claim)

		end

	end

	return newclaims

end



function wd.hasQualifier(claim, acceptedqualifs, acceptedvals, excludequalifiervalues)

	local claimqualifs = claim.qualifiers

	

	if (not claimqualifs) then

		return false

	end



	acceptedqualifs = wd.splitStr(acceptedqualifs)

	acceptedvals = wd.splitStr( acceptedvals)





	local function ok(qualif) -- vérification pour un qualificatif individuel

		if not claimqualifsqualif then

			return false

		end

		if not (acceptedvals) then  -- si aucune valeur spécifique n'est demandée, OK

			return true

		end

		for i, wanted in pairs(acceptedvals) do

			for j, actual in pairs(claimqualifsqualif]) do

				if wd.getId(actual) == wanted then

					return true

				end

			end

		end

	end



	for i, qualif in pairs(acceptedqualifs) do

		if ok(qualif) then

			return true

		end

	end

	return false

end



function wd.hasQualifierNumber(claim, acceptedqualifs, acceptedvals, excludequalifiervalues)

	local claimqualifs = claim.qualifiers

	

	if (not claimqualifs) then

		return false

	end



	acceptedqualifs = wd.splitStr(acceptedqualifs)

	acceptedvals = wd.splitStr( acceptedvals)





	local function ok(qualif) -- vérification pour un qualificatif individuel

		if not claimqualifsqualif then

			return false

		end

		if not (acceptedvals) then  -- si aucune valeur spécifique n'est demandée, OK

			return true

		end

		for i, wanted in pairs(acceptedvals) do

			for j, actual in pairs(claimqualifsqualif]) do

				if mw.wikibase.renderSnak(actual) == wanted then

					return true

				end

			end

		end

	end



	for i, qualif in pairs(acceptedqualifs) do

		if ok(qualif) then

			return true

		end

	end

	return false

end



local function hasSource(claim, targetsource, sourceproperty)

	sourceproperty = sourceproperty or 'P248'

	if targetsource == "-" then

		return true

	end

	if (not claim.references) then return

		false

	end

	local candidates = claim.references1].snakssourceproperty -- les snaks utilisant la propriété demandée

	if (not candidates) then

		return false

	end

	if (targetsource == "any") then -- si n'importe quelle valeur est acceptée tant qu'elle utilise en ref la propriété demandée

		return true

	end

	targetsource = wd.splitStr(targetsource)

	for _, source in pairs(candidates) do

		local s = wd.getId(source)

		for i, target in pairs(targetsource) do

			if s == target then return true end

		end

	end

	return false

end



local function excludeQualifier(claim, qualifier, qualifiervalues)

	return not wd.hasQualifier(claim, qualifier, qualifiervalues)

end



function wd.hasDate(claim)

	if not claim then

		return false --error() ?

	end

	if wd.getDateFromQualif(claim, 'P585') or wd.getDateFromQualif(claim, 'P580') or wd.getDateFromQualif(claim, 'P582') then

		return true

	end

	return false

end



local function hasLink(claim, site, lang)

	if (claim.mainsnak.snaktype ~= 'value') then -- ne pas supprimer les valeurs spéciales, il y a une fonction dédiée pour ça

		return true

	end

	local id = wd.getMainId(claim)

	local link = wd.siteLink(id, site, lang)

	if link then

		return true

	end

end



local function isInLanguage(claim, lang) -- ne fonctionne que pour les monolingualtext / étendre aux autres types en utilisant les qualifiers ?

	if type(lang) == 'table' then -- si c'est une table de language séparées par des virgules, on les accepte toutes

		for i, l in pairs(lang) do

			local v = isInLanguage(claim, l)

			if v then

				return true

			end

		end

	end

	if type(lang) ~= ('string') then

		return --?

	end



	if (lang == '-') then

		return true

	end

	if (lang == 'locallang') then

		lang =  mw.getContentLanguage():getCode()

	end

	

	-- pour les monolingual text

	local snak = claim.mainsnak or claim

	if snak.snaktype == 'value' and snak.datavalue.type == 'monolingualtext' then

		if snak.datavalue.value.language == lang then

			return true

		end

		return false

	end



	-- pour les autres types de données : recherche dans les qualificatifs

	if (lang == 'fr') then

		lang = 'Q150'

	elseif (lang == 'en') then

		lang = 'Q1860'

	else

		lang = invertedlangcodeslang

	end

	if claim.qualifiers and claim.qualifiers.P407 then

		if wd.hasQualifier(claim, {'P407'}, {lang}) then

			return true

		else

			return false

		end

	end



	return true -- si on ne ne sait pas la langue, on condière que c'est bon

end



local function firstVals(claims, numval) -- retourn les numval premières valeurs de la table claims

    local numval = tonumber(numval) or 0 -- raise a error if numval is not a positive integer ?

    if not claims then

    	return nil

    end

    while (#claims > numval) do

    	table.remove(claims)

    end

    return claims

end



local function lastVals(claims, numval2) -- retourn les valeurs de la table claims à partir de numval2

    local numval2 = tonumber(numval2) or 0 -- raise a error if numval is not a positive integer ?

    if not claims then

    	return nil

    end

    for i=1,numval2 do

    	table.remove(claims, 1)

    end

    return claims

end



-- retourne les valeurs de la table claims à partir de removedupesdate,

-- sans les dates en doublons avec conversion entre les calendrier julien et grégorien,

-- ou uniquement en catégorisant si le paramètre removedupesdate est égale à 'cat'

local function removeDupesDate(claims, removedupesdate)

	if not claims or #claims < 2 then

		return claims, ''

	end

	local cat = ''

	local newClaims = {}

	local newIsos = {}



	local function findIndex(searchset, val) -- similaire à wd.isHere mais retourne l'index de la valeur trouvée

		for i, j in pairs(searchset) do

			if val == j then

				return i

			end

		end

		return -1

	end



	for _, claim in ipairs( claims ) do

		local snack = claim.mainsnak or claim

		if (snack.snaktype == 'value') and (snack.datatype == 'time') and snack.datavalue.value.precision >= 11 then -- s'il s'agit d'un time et que la précision est au moins l'année

			local iso = snack.datavalue.value.time

			_, _, iso = string.find(iso, "(+%d+-%d+-%d+T)")

			local deleteIfDuplicate = false

			if snack.datavalue.value.calendarmodel == 'http://www.wikidata.org/entity/Q1985727' then -- si la date est grégorienne

				if modules.formatDate.before('+1582', iso) then -- si avant 1582 on calcule la date julienne

					_, _, y, m, d = string.find(iso, "+(%d+)-(%d+)-(%d+)T")

					y, m , d = modules.datemodule.gregorianToJulian(y, m , d)

					if m < 10 then m = '0' .. m end

					if d < 10 then d = '0' .. d end

					iso = '+' .. y .. '-' .. m .. '-' .. d .. 'T'

					deleteIfDuplicate = true

				end

				local index = findIndex(newIsos, iso)

				if index >= 0 then -- si la date est déjà présente

					cat = cat .. '[[Catégorie:Article avec des dates identiques venant de wikidata dans le code de l\'infobox]]'

					if removedupesdate == "cat" then -- ne faire que catégoriser

						table.insert(newIsos, iso)

						table.insert(newClaims, claim)

					elseif not deleteIfDuplicate then -- supprimer l'autre date si la date courante n'a pas été convertie

						newClaimsindex = claim

					end -- sinon supprimer la date courante

				else -- pas de doublon

					table.insert(newIsos, iso)

					table.insert(newClaims, claim)

				end

			elseif snack.datavalue.value.calendarmodel == 'http://www.wikidata.org/entity/Q1985786' then -- si date julienne

				if not modules.formatDate.before('+1582', iso) then -- si après 1582 on calcule la date grégorienne

					_, _, y, m, d = string.find(iso, "+(%d+)-(%d+)-(%d+)T")

					y, m , d = modules.datemodule.julianToGregorian(y, m , d)

					if m < 10 then m = '0' .. m end

					if d < 10 then d = '0' .. d end

					iso = '+' .. y .. '-' .. m .. '-' .. d .. 'T'

					deleteIfDuplicate = true

				end

				local index = findIndex(newIsos, iso)

				if index >= 0 then -- si date déjà présente

					cat = cat .. '[[Catégorie:Article avec des dates identiques venant de wikidata dans le code de l\'infobox]]'

					if removedupesdate == "cat" then -- ne faire que catégoriser

						table.insert(newIsos, iso)

						table.insert(newClaims, claim)

					elseif not deleteIfDuplicate then -- supprimer l'autre date si la date courante n'a pas été convertie

						newClaimsindex = claim

					end -- sinon supprimer la date courante

				else -- pas de doublon

					table.insert(newIsos, iso)

					table.insert(newClaims, claim)

				end

			else -- autre calendrier

				table.insert(newIsos, iso)

				table.insert(newClaims, claim)

			end

		else -- précision insuffisante

			table.insert(newIsos, iso)

			table.insert(newClaims, claim)

		end

	end

	return newClaims, cat

end



local function timeFromQualifs(claim, qualifs)

	local claimqualifs = claim.qualifiers

	if not claimqualifs then

		return nil

	end

	for i, qualif in pairs(qualifs or timequalifiers) do

		local vals = claimqualifsqualif

		if vals and (vals1].snaktype == 'value') then

			return vals1].datavalue.value.time, vals1].datavalue.value.precision

		end

	end

end



local function atDate(claim, mydate) 

	if mydate == "today" then

		mydate = os.date("!%Y-%m-%dT%TZ")

	end

	-- determines required precision depending on the atdate format

	local d = mw.text.split(mydate, "-")



	local myprecision

	if d3 then

		myprecision =  11 -- day

	elseif d2 then

		myprecision = 10 -- month

	else

		myprecision = 9 -- year

 	end



	-- with point in time

	local d, storedprecision = timeFromQualifs(claim, {'P585'})

	if d then

		return modules.formatDate.equal(mydate, d, math.min(myprecision, storedprecision))

	end

	-- with start or end date -- TODO: precision 

	local mindate = timeFromQualifs(claim, {'P580'}) 

	local maxdate = timeFromQualifs(claim, {'P582'})

	if modules.formatDate.before(mydate, mindate) and  modules.formatDate.before(maxdate, mydate) then

		return true

	end

	return false

end



local function check(claim, condition)

	if type(condition) == 'function' then -- cas standard

		return condition(claim)

	end

	return formatError('invalid type', 'function', type(condition)) 

end



local function minPrecision(claim, minprecision)

	local snack

	if claim.qualifiers then -- si une date est donnée en qualificatif, c'est elle qu'on utilise de préférence au mainsnak

		for i, j in ipairs(datequalifiers) do

			if claim.qualifiersj then

				snack = claim.qualifiersj][1 

				break

			end

		end

	end

	if not snack then

		snack = claim.mainsnak or claim

	end

	if (snack.snaktype == 'value') and (snack.datatype == 'time') and (snack.datavalue.value.precision < minprecision) then

		return false

	end

	return true

end



function wd.sortClaims(claims, sorttype)

	if not claims then

		return nil

	end

	if sorttype == 'chronological' then

		return wd.chronoSort(claims)

	elseif sorttype == 'inverted' then

		return wd.chronoSort(claims, true)

	elseif sorttype == 'order' then

		return wd.chronoSort(claims)

	elseif sorttype == 'ascending' then

		return wd.quantitySort(claims)

	elseif sorttype == 'descending' then

		return wd.quantitySort(claims, true)

	elseif type(sorttype) == 'function' then

		table.sort(claims, sorttype)

		return claims

	elseif type(sorttype) == 'string' and sorttype:sub(1, 1) == 'P' then

		return wd.numericPropertySort(claims, sorttype)

	end

	return claims

end



function wd.filterClaims(claims, args) --retire de la tables de claims celles qui sont éliminés par un des filters de la table des filters



	local function filter(condition, filterfunction, funargs)

		if not argscondition then

			return

		end

		for i = #claims, 1, -1 do

			if not( filterfunction(claimsi], argsfunargs1]], argsfunargs2]], argsfunargs3]]) ) then

				table.remove(claims, i)

			end

		end

	end

	filter('isinlang', isInLanguage, {'isinlang'} )

	filter('excludespecial', notSpecial, {} )

	filter('condition', check, {'condition'} )

	if claims1 and claims1].mainsnak then

		filter('targetvalue', hasTargetValue, {'targetvalue'} )

		filter('atdate', atDate, {'atdate'} )

		filter('qualifier', wd.hasQualifier, {'qualifier', 'qualifiervalue'} )

		filter('qualifiernumber', wd.hasQualifierNumber, {'qualifiernumber', 'qualifiernumbervalue'} )

		filter('excludequalifier', excludeQualifier, {'excludequalifier', 'excludequalifiervalue'} )

		filter('withsource', hasSource, {'withsource', 'sourceproperty'} )

		filter('withdate', wd.hasDate, {} )

		filter('excludevalues', excludeValues, {'excludevalues'})

		filter('withlink', hasLink, {'withlink', 'linklang'} )

		filter('minprecision', minPrecision, {'minprecision'} )

		claims = withRank(claims, args.rank or 'best')

	end

	if #claims == 0 then

		return nil

	end

	if args.sorttype then

		claims = wd.sortClaims(claims, args.sorttype)

	end

	if args.numval2 then

		claims = lastVals(claims, args.numval2)

	end

	if args.numval then

		claims = firstVals(claims, args.numval)

	end

	return claims



end



function wd.loadEntity(entity, cache)

	if type(entity) ~= 'table' then

		if cache then

			if not cacheentity then 

				cacheentity = mw.wikibase.getEntity(entity)

				mw.log("cached")

     		end

			return cacheentity

        else

			if entity == '' or (entity == '-') then

				entity = nil

			end

        	return mw.wikibase.getEntity(entity)

        end

    else 

    	return entity

    end

end





function wd.getClaims( args ) -- returns a table of the claims matching some conditions given in args

	if args.claims then -- if claims have already been set, return them

		return args.claims

	end

	local properties = args.property

	if type(properties) == 'string' then

		properties = wd.splitStr(string.upper(args.property))

	end

	if not properties then

		return formatError( 'property-param-not-provided' )

	end



	--Get entity

	local entity = args.entity

	if type(entity) == 'string' then

		if entity == '' then

			entity = nil

		end

	elseif type(entity) == 'table' then

		entity = entity.id

	end

        if (not entity) then

            entity = mw.wikibase.getEntityIdForCurrentPage()

        end

	if (not entity) or (entity == '-') then

		return nil

	end

	local claims = {}



	if #properties == 1 then

		claims = mw.wikibase.getAllStatements(entity, properties1]) -- do not use mw.wikibase.getBestStatements at this stage, as it may remove the best ranked values that match other criteria in the query

	else

		for i, prop in ipairs(properties) do

			local newclaims = mw.wikibase.getAllStatements(entity, prop) 

			if newclaims and #newclaims > 0 then

				for j, claim in ipairs(newclaims) do

					table.insert(claims, claim)

				end

			end

		end

	end





	if (not claims) or (#claims == 0) then

		return nil

	end

	return wd.filterClaims(claims, args)

end



--=== ENTITY FORMATTING ===



function wd.getLabel(entity, lang1, lang2)

	

	if (not entity) then

		return nil -- ou option de gestion des erreurs ?

	end

	entity = entity.id or ( type(entity) == "string" and entity)

	if not(type(entity) == 'string') then return nil end

	

	lang1 = lang1 or defaultlang

	

	local str, lang --str : texte rendu, lang : langue de celui-ci

	if lang1 == defaultlang then -- le plus économique

		str, lang = mw.wikibase.getLabelWithLang(entity) -- le libellé peut être en français ou en anglais

	else

		str = mw.wikibase.getLabelByLang(entity, lang1)

		if str then lang = lang1 end

	end

	if str and (lang == lang1) then --pas de catégorie "à traduire" si on a obtenu un texte dans la langue désirée (normalement fr)

		return str

	end

	if lang2 then -- langue secondaire, avec catégorie "à traduire"

		str2 = mw.wikibase.getLabelByLang(entity, lang2)

		if str2 then

			lang = lang2

			str = str2

		end

	end

	if not str then --si ni lang1, ni lang2 ni l'anglais ne sont présents,  parcours de la hiérarchie des langues

		for _, trylang in ipairs(modules.langhierarchy.codes) do

			str = mw.wikibase.getLabelByLang(entity, trylang)

			if str then

				lang = trylang

				break

			end

		end

	end

		

	if str then

		local translationCat = modules.i18n'to translate'

		translationCat = translationCat .. (modules.langhierarchy.cattextlang or '')

		translationCat = addCat(translationCat)

		return str, translationCat

	end

end



function wd.formatEntity( entity, params )



	if (not entity) then

		return nil --formatError('entity-not-found')

	end

	local id = entity

	if type(id) == 'table' then

		id = id.id

	end



	params = params or {}

	local lang = params.lang or defaultlang

	local speciallabels = params.speciallabels

	local displayformat = params.displayformat

	local labelformat = params.labelformat

	local labelformat2 = params.labelformat2

	local defaultlabel = params.defaultlabel or id

	local linktype = params.link

	local defaultlink = params.defaultlink

	local defaultlinkquery = params.defaultlinkquery



	if speciallabels and speciallabelsid then --speciallabels override the standard label + link combination

		return speciallabelsid

	end

	if params.displayformat == 'raw' then

		return id

	end



	local link, label, translationCat

	

	if type(labelformat) == 'function' then -- sert à des cas particuliers

		label, translationCat = labelformat(entity)

	end

	

	if not label then label, translationCat = wd.getLabel(entity, lang, params.wikidatalang) end



	translationCat = translationCat or "" -- sera toujours ajoutée au résultat mais sera vide si la catégorie de maintenance n'est pas nécessaire

	

	if type(labelformat2) == 'function' then -- sert à des cas particuliers

		label = labelformat2(label)

	end

	

	-- détermination du fait qu'on soit ou non en train de rendre l'élément sur la page de son article

	local rendering_entity_on_its_page = wd.isPageOfQId(id)

	

	if not label then

		if (defaultlabel == '-') then 

			return nil

		end

		link = wd.siteLink(id, 'wikidata')

		return '[[' .. link .. '|' .. id .. ']]' .. translationCat

-- si pas de libellé, on met un lien vers Wikidata pour qu'on comprenne à quoi ça fait référence

	end



	if (linktype == '-') or rendering_entity_on_its_page then

		return label .. translationCat

	end



	local link = wd.siteLink(entity, linktype, lang)



	-- defaultlinkquery will try to link to another page on this Wiki

	if (not link) and defaultlinkquery then

		if type(defaultlinkquery) == 'string' then

				defaultlinkquery = {property = defaultlinkquery}

		end

		defaultlinkquery.excludespecial = true

		defaultlinkquery.entity = entity

		local claims = wd.getClaims(defaultlinkquery)

		if claims then

			for i, j in pairs(claims) do

				local id = wd.getMainId(j)

				link = wd.siteLink(id, linktype, lang)

				if link then

					break

				end

			end

		end	

	end



	if link then

		if mw.ustring.sub(link, 1, 9) == "Category:" or mw.ustring.sub(link, 1, 10) == "Catégorie:" then

			link = ":" .. link			--lier vers une catégorie au lieu de catégoriser

		end

		return '[[' .. link .. '|' .. label .. ']]' .. translationCat

	end



	-- if not link, you can use defaultlink: a sidelink to another Wikimedia project

	if (not defaultlink) then

		defaultlink = {'enwiki'}

	end

	if defaultlink and (defaultlink ~= '-') then

		local linktype

		local sidelink, site, langcode

	

		if type(defaultlink) == 'string' then

			defaultlink = {defaultlink}

		end

		for i, j in ipairs(defaultlink) do

			sidelink, site, langcode = wd.siteLink(entity, j, lang)

			if sidelink then

				break

			end

		end

		if not sidelink then

			sidelink, site = wd.siteLink(entity, 'wikidata')

		end

		

		local icon, class, title = site, nil, nil -- le texte affiché du lien

		if site == 'wiki' then

			icon, class, title = langcode, "indicateur-langue", wd.translate('see-another-language', mw.language.fetchLanguageName(langcode, defaultlang))	

		elseif site == 'wikidata' then

			icon, class, title = 'd',  "indicateur-langue", wd.translate('see-wikidata')		

		else

			title = wd.translate('see-another-project', site)

		end

		local val = '[[' .. sidelink .. '|' .. '<span class = "' .. (class or '').. '" title = "' .. (title or '') .. '">' .. icon .. '</span>]]'

		return label .. ' <small>(' .. val .. ')</small>' .. translationCat

	end 

	return label .. translationCat

end





function wd.addTrackingCat(prop, cat) -- doit parfois être appelé par d'autres modules

	if type(prop) == 'table' then

		prop = prop1 -- devrait logiquement toutes les ajouter

	end

	if not prop and not cat then

		return formatError("property-param-not-provided")

	end

	if not cat then

		cat = wd.translate('trackingcat', prop or 'P??')

	end

	return addCat(cat )

end



local function unknownValue(snak, label)

	local str = label



	if type(str) == "function" then

		str = str(snak)

	end



	if (not str) then

		if snak.datatype == 'time' then

			str = wd.translate('sometime')

		else

			str = wd.translate('somevalue')

		end

	end



	if type(str) ~= "string" then

		return formatError(snak.datatype)

	end

	return str

end



local function noValue(displayformat)

	if not displayformat then

		return wd.translate('novalue')

	end

	if type(displayformat) == 'string' then

		return displayformat

	end

	return formatError()

end



local function getLangCode(entityid)

	return modules.langcodestonumber(entityid:sub(2))]

end



local function showLang(statement, maxLang) -- retourne le code langue entre paranthèse avant la valeur (par exemple pour les biblios et liens externes)

	local mainsnak = statement.mainsnak

	if mainsnak.snaktype ~= 'value' then

		return nil

	end

	local langlist = {}

	if mainsnak.datavalue.type == 'monolingualtext' then

		langlist = {mainsnak.datavalue.value.language}

	elseif (not statement.qualifiers) or (not statement.qualifiers.P407) then

		return

	else

		for i, j in pairs( statement.qualifiers.P407 ) do

			if  j.snaktype == 'value' then

				local langentity = wd.getId(j)

				local langcode =  getLangCode(langentity)

				table.insert(langlist, langcode)

			end

		end

	end

	if (#langlist > 1) or (#langlist == 1 and langlist1 ~= defaultlang) then -- si c'est en français, pas besoin de le dire

		langlist.maxLang = maxLang

		return modules.langmodule.indicationMultilingue(langlist)

	end

end





-- === DATE HANDLING ===



function wd.addStandardQualifs(str, statement)

	if (not statement) or (not statement.qualifiers) then

		return str

	end

	if not str then

		return error()-- what's that ?

	end



	if statement.qualifiers.P1480 then

		for i, j in pairs(statement.qualifiers.P1480) do

			local v = wd.getId(j)

			if (v == "Q21818619") then

				str = wd.translate('approximate-place', str)

			elseif (v == "Q18122778") or (v == "Q18912752") then

				str = wd.translate('uncertain-information', str)

			elseif (v == "Q5727902") then

				if (statement.mainsnak.datatype == 'time') then

					str = modules.formatDate.fuzzydate(str)

				else

					str = wd.translate('approximate-value', str)

				end

			end			

		end

	end

	return str

end



local function rangeObject(begin, ending, params)

	--[[

		objet comportant un timestamp pour le classement chronologique et deux dateobject (begin et ending)

	]]-- 

	local timestamp

	if begin then

		timestamp = begin.timestamp

	else

		timestamp = ending.timestamp

	end

	return {begin = begin, ending = ending, timestamp = timestamp, type = 'rangeobject'}

end



local function dateObject(orig, params)

	--[[ transforme un snak en un nouvel objet utilisable par Module:Date complexe

		{type = 'dateobject', timestamp = str, era = '+' ou '-', year = number, month = number, day = number, calendar = calendar}

	]]-- 

	if not params then

		params = {}

	end

	

	local newobj = modules.formatDate.splitDate(orig.time, orig.calendarmodel)

	

	newobj.precision = params.precision or orig.precision

	newobj.type = 'dateobject'

	return newobj

end



local function objectToText(obj, params)

	if obj.type == 'dateobject' then

		return modules.formatDate.simplestring(obj, params)

	elseif obj.type == 'rangeobject' then

		return modules.formatDate.daterange(obj.begin, obj.ending, params)

	end

end



function wd.getDateFromQualif(statement, qualif)

	if (not statement) or (not statement.qualifiers) or not (statement.qualifiersqualif]) then

		return nil

	end

	local v = statement.qualifiersqualif][1

	if v.snaktype ~= 'value' then -- que faire dans ce cas ?

		return nil

	end

	return dateObject(v.datavalue.value)

end



function wd.getDate(statement)

	local period = wd.getDateFromQualif(statement, 'P585') -- retourne un dateobject

	if period then

		return period

	end

	local begin, ending = wd.getDateFromQualif(statement, 'P580'),  wd.getDateFromQualif(statement, 'P582')

	if begin or ending then

		return rangeObject(begin, ending) -- retourne un rangeobject fait de deux dateobject

	end

	return nil

end



function wd.getFormattedDate(statement, params)

	if not statement then

		return nil

	end

	local str





	--cherche la date avec les qualifs P580/P582

	local datetable = wd.getDate(statement)

	if datetable then

		str = objectToText(datetable, params)

	end

	

	-- puis limite intérieur / supérieur

	if not str then

		local start, ending = wd.getDateFromQualif(statement, 'P1319'), wd.getDateFromQualif(statement, 'P1326')

		str = modules.formatDate.between(start, ending, params)

	end



	 -- sinon, le mainsnak, pour les données de type time

	if (not str) and (statement.mainsnak.datatype == 'time') then

		local mainsnak = statement.mainsnak

		if (mainsnak.snaktype == 'value') or (mainsnak.snaktype == 'somevalue') then

			str = wd.formatSnak(mainsnak, params)

		end

	end



	if str and params and (params.addstandardqualifs ~= '-') then

		str = wd.addStandardQualifs(str, statement)

	end

	return str

end



wd.compare.by_quantity = function(c1, c2)

	local v1 = wd.getDataValue(c1.mainsnak)

	local v2 = wd.getDataValue(c2.mainsnak) 

	if not (v1 and v2) then

			return true

	end

	return v1 < v2

end



--[[ tri chronologique générique : 

             retourne une fonction de tri de liste de déclaration

             en fonction d’une fonction qui calcule la clé de tri

             et d’une fonction qui compare les clés de tri

   

   paramètres nommés: (appel type wikidata.compare.chrono_key_sort{sortKey="nom clé"})

        sortKey (optionnel)   : chaine, le nom de la clé utilisée pour un tri 

                                (pour éviter de rentrer en collision avec "dateSortKey" 

                                utilisé par chronoSort au besoin)

        snak_key_get_function : fonction qui calcule la valeur de la clé à partir d’un snak ou d’une déclaration,

        (obligatoire)           le résultat n’est calculé qu’une fois et est stocké en cache dans claim[sortKey]

        key_compare_function  : fonction de comparaison des clés calculées par snak_key_get_function

        (optionnel)

--]]    



function wd.chrono_key_sort(arg)

	

	local snak_key_get_function = arg.snak_key_get_function

	local sortKey = arg.sortKey or "dateSortKey"

	local key_compare_function = arg.key_compare_function or 

									function(c1, c2) return c1 < c2 end

	return function(claims)

		for _, claim in ipairs( claims ) do

			if not claimsortKey then

				local key = snak_key_get_function(claim)

				if key then

					claimsortKey = wd.compare.get_claim_date(key)

				else

					claimsortKey = 0

				end

			end

		end

		table.sort(

			claims,

			function(c1, c2)

				return key_compare_function(c1sortKey], c2sortKey])

			end

		)

		return claims

	end

end





function wd.quantitySort(claims, inverted)

	local function sort(c1, c2)

		local v1 = wd.getDataValue(c1.mainsnak)

		local v2 = wd.getDataValue(c2.mainsnak) 

		if not (v1 and v2) then

				return true

		end

		if inverted then

			return v2 < v1

		end

		return v1 < v2

	end

	table.sort(claims, sort )

	return claims

end



function wd.compare.get_claim_date(claim)

	local snack = claim.mainsnak or claim

	local iso

	if (snack.snaktype == 'value') and (snack.datatype == 'time') then

		iso = snack.datavalue.value.time

	else

		iso = timeFromQualifs(claim, datequalifiers) or '0'

	end

	-- transformation en nombre (indication de la base car gsub retourne deux valeurs)

	return tonumber( iso:gsub( '(%d)%D', '%1' ), 10 )

end



function wd.compare.chronoCompare(c1, c2)

	return wd.compare.get_claim_date(c1) < wd.compare.get_claim_date(c2)

end



-- fonction pour renverser l’ordre d’une autre fonction

function wd.compare.rev(comp_criteria)

	return function(c1, c2)

		-- attention les tris en lua attendent des fonctions de comparaison strictement inférieur, on doit

		-- vérifier la non égalité quand on inverse l’ordre d’un critère, d’ou "and comp_criteria(c2,c1)"

		return not(comp_criteria(c1,c2)) and comp_criteria(c2,c1)

	end

end



-- Fonction qui trie des Claims de type time selon l'ordre chronologique

-- Une clé de tri nomée « dateSortKey » est ajouté à chaque claim.

-- Si des clés de tri de ce nom existent déjà, elles sont utilisées sans modification.



function wd.chronoSort( claims, inverted )

	for _, claim in ipairs( claims ) do

		if not claim.dateSortKey then

			claim.dateSortKey = wd.compare.get_claim_date(claim)

		end

	end

	table.sort( 

		claims,

		function ( c1, c2 )

			if inverted then

				return c2.dateSortKey < c1.dateSortKey

			end

			return c1.dateSortKey < c2.dateSortKey

		end

	)

	return claims

end



local function get_numeric_claim_value(claim, propertySort)

	local val

	local claimqualifs = claim.qualifiers

	if claimqualifs then

		local vals = claimqualifspropertySort

		if vals and vals1].snaktype == 'value' then

			val = vals1].datavalue.value

		end

	end

	return tonumber(val or 0)

end



function wd.compare.numeric(propertySort)

	return function(c1, c2)

		return get_numeric_claim_value(c1, propertySort) < get_numeric_claim_value(c2, propertySort)

	end

end



-- Fonction qui trie des Claims de type value selon l'ordre de la propriété fournit

-- Une clé de tri nomée « dateSortKey » est ajouté à chaque claim.

-- Si des clés de tri de ce nom existent déjà, elles sont utilisées sans modification.



function wd.numericPropertySort( claims, propertySort )

	for _, claim in ipairs( claims ) do

		if not claim.dateSortKey then

			local val = get_numeric_claim_value(claim, propertySort)

			claim.dateSortKey = tonumber(val or 0)

		end

	end

	table.sort(

		claims,

		function ( c1, c2 )

			return c1.dateSortKey < c2.dateSortKey

		end

	)

	return claims

end



--[[

test possible en console pour la fonction précédente : 

= p.formatStatements{entity = "Q375946", property = 'P50', sorttype = 'P1545', linkback = "true"}

--]]



-- ===================

function wd.getReferences(statement)

	local refdata = statement.references

	if not refdata then

		return nil

	end



	local refs = {}

	local hashes = {}

	for i, ref in pairs(refdata) do

		local s

		local function hasValue(prop) -- checks that the prop is here with valid value

			if ref.snaksprop and ref.snaksprop][1].snaktype == 'value' then

				return true

			end

			return false

		end		



		if ref.snaks.P248 then -- cas lorsque P248 (affirmé dans) est utilisé 

			for j, source in pairs(ref.snaks.P248) do

				if source.snaktype == 'value' then

					local page, accessdate, quotation

					if hasValue('P304') then -- page 

						page = wd.formatSnak(ref.snaks.P3041])

					end

					if hasValue('P813') then -- date de consultation

						accessdate = wd.formatSnak(ref.snaks.P8131])

					end

					if hasValue('P1683') then -- citation

						quotation = wd.formatSnak(ref.snaks.P16831])

					end

					local sourceId = wd.getId(source)

					s = modules.reference.citeitem(sourceId, {['page' = page, 'accessdate' = accessdate, 'citation' = quotation})

					table.insert(refs, s)

					table.insert(hashes, ref.hash .. sourceId)

				end

			end

	

		elseif hasValue('P8091') or hasValue('P854') then -- cas lorsque P8091 (Archival Resource Key) ou P854 (URL de la référence)est utilisé

			local arkKey, url, title, author, publisher, accessdate, publishdate, publishlang, quotation, description



			if hasValue('P8091') then

				arkKey = wd.formatSnak(ref.snaks.P80911], {text = "-"})

				url = 'https://n2t.net/' .. arkKey

				if hasValue('P1476') then

					title = wd.formatSnak(ref.snaks.P14761])

				else

					title = arkKey

				end

			elseif hasValue('P854') then

				url = wd.formatSnak(ref.snaks.P8541], {text = "-"})

				if hasValue('P1476') then

					title = wd.formatSnak(ref.snaks.P14761])

				else

					title = mw.ustring.gsub(url, '^[Hh][Tt][Tt][Pp]([Ss]?):(/?)([^/])', 'http%1://%3')

				end

			end



			--todo : handle multiple values for author, etc.

			if hasValue('P1810') then -- sous le nom

				description = 'sous le nom ' .. wd.formatSnak(ref.snaks.P18101])

			end

			if hasValue('P813') then -- date de consultation

				accessdate = wd.formatSnak(ref.snaks.P8131])

			end

			if hasValue('P50') then  -- author (item type)

				author = wd.formatSnak(ref.snaks.P501])

			elseif hasValue('P2093') then -- author (string type)

				author = wd.formatSnak(ref.snaks.P20931])

			end

			if hasValue('P123') then -- éditeur

				publisher = wd.formatSnak(ref.snaks.P1231])

			end

			if hasValue('P1683') then -- citation

				quotation = wd.formatSnak(ref.snaks.P16831])

			end

			if hasValue('P577') then -- date de publication

				publishdate = wd.formatSnak(ref.snaks.P5771])

			end 

			if hasValue('P407') then -- langue de l'œuvre

				local id = wd.getId(ref.snaks.P4071])

				publishlang = getLangCode(id)

			end

			s = modules.cite.lienWeb{titre = title, url = url, auteur = author, editeur = publisher, langue = publishlang, 'en ligne le' = publishdate, 'consulté le' = accessdate, 'citation' = quotation, 'description' = description}

			table.insert(hashes, ref.hash)

			table.insert(refs, s)



		elseif ref.snaks.P854 and ref.snaks.P8541].snaktype == 'value' then

			s = wd.formatSnak(ref.snaks.P8541], {text = "-"})

			table.insert(hashes, ref.snaks.P8541].hash)

			table.insert(refs, s)

		end

	end

	if #refs > 0 then

		if #hashes == #refs then

			return refs, hashes

		end

		return refs

	end

end



function wd.sourceStr(sources, hashes)

	if not sources or (#sources == 0) then

		return nil

	end

	local useHashes = hashes and #hashes == #sources

	for i, j in ipairs(sources) do

		local refArgs = {name = 'ref', content = j}

		if useHashes and hashesi ~= '-' then

			refArgs.args = {name = 'wikidata-' .. hashesi]}

		end

		sourcesi = mw.getCurrentFrame():extensionTag(refArgs)

	end

	return table.concat(sources, '<sup class="reference cite_virgule">,</sup>')

end



function wd.getDataValue(snak, params)

	if not params then

		params = {}

	end

	local speciallabels = params.speciallabels -- parfois on a besoin de faire une liste d'éléments pour lequel le libellé doit être changé, pas très pratique d'utiliser une fonction pour ça



	if snak.snaktype ~= 'value' then

		return nil

	end



	local datatype = snak.datatype

	local value = snak.datavalue.value

	

	local displayformat = params.displayformat

	if type(displayformat) == 'function' then

		return displayformat(snak, params)

	end



	if datatype == 'wikibase-item' then

		return wd.formatEntity(wd.getId(snak), params)

	end



	if datatype == 'url' then

		if params.displayformat == 'raw' then

			return value

		else

			return modules.weblink.makelink(value, params.text)

		end

	end



	if datatype == 'math' then

		return mw.getCurrentFrame():extensionTag( "math", value)

	end



	if datatype == 'tabular-data' then

		return mw.ustring.sub(value, 6, 100)  -- returns the name of the file, without the "Data:" prefix

	end



	if (datatype == 'string') or (datatype == 'external-id') or (datatype == 'commonsMedia') then -- toutes les données de type string sauf "math"

		if params.urlpattern then

			local urlpattern = params.urlpattern

			if type(urlpattern) == 'function' then

				urlpattern = urlpattern(value)

			end

			-- encodage de l'identifiant qui se retrouve dans le path de l'URL, à l'exception des slashes parfois rencontrés, qui sont des séparateurs à ne pas encoder

			local encodedValue = mw.uri.encode(value, 'PATH'):gsub('%%2F', '/')

			-- les parenthèses autour du encodedValue:gsub() sont nécessaires, sinon sa 2e valeur de retour est aussi passée en argument au mw.ustring.gsub() parent

			local url = mw.ustring.gsub(urlpattern, '$1', (encodedValue:gsub('%%', '%%%%')))

			value = '[' .. url .. ' ' .. (params.text or value) .. ']'

		end

		return value

	end

	

	if datatype == 'time' then -- format example: +00000001809-02-12T00:00:00Z

		if displayformat == 'raw' then

			return value.time

		else

			local dateobject = dateObject(value, {precision = params.precision})

			return objectToText(dateobject, params)

		end

	end



	if datatype == 'globe-coordinate' then

		-- retourne une table avec clés latitude, longitude, précision et globe à formater par un autre module (à changer ?)

		if displayformat == 'latitude' then

			return value.latitude

		elseif displayformat == 'longitude' then

			return value.longitude

		else

			local coordvalue = mw.clone( value )

			coordvalue.globe = modules.globesvalue.globe -- transforme l'ID du globe en nom anglais utilisable par geohack

			return coordvalue -- note : les coordonnées Wikidata peuvent être utilisée depuis Module:Coordinates. Faut-il aussi autoriser à appeler Module:Coordiantes ici ?

		end

	end



	if datatype == 'quantity' then -- todo : gérer les paramètres précision

		local amount, unit = value.amount, value.unit



		if unit then

			unit = unit:match('Q%d+')

		end

		

		if not unit then

			unit = 'dimensionless'

		end

		

		local raw	

		if displayformat == "raw" then

			raw = true

		end

		return modules.formatNum.displayvalue(amount, unit,

			{targetunit = params.targetunit, raw = raw, rounding = params.rounding, showunit = params.showunit or 'short', showlink = params.showlink}

		)

	end

	if datatype == 'monolingualtext' then

		if value.language == defaultlang then

			return value.text

		else

			return modules.langmodule.langue({value.language, value.text, nocat=true})

		end

	end	

	return formatError('unknown-datavalue-type' )



end



function wd.stringTable(args) -- like getClaims, but get a list of string rather than a list of snaks, for easier manipulation

	local claims = args.claims

	local cat = ''



	if not claims then

		claims = wd.getClaims(args)

	end

	if not claims or claims == {} then

		return {}, {}, cat

	end

	if args.removedupesdate and (args.removedupesdate ~= '-') then

		claims, cat = removeDupesDate(claims, args.removedupesdate)

	end

	local props = {} -- liste des propriétés associété à chaque string pour catégorisation et linkback

	for i, j in pairs(claims) do

		claimsi = wd.formatStatement(j, args)

		table.insert(props, j.mainsnak.property)

	end

	if args.removedupes and (args.removedupes ~= '-') then

		claims = wd.addNewValues({}, claims) -- devrait aussi supprimer de props celles qui ne sont pas utilisées

	end

	return claims, props, cat

end



function wd.getQualifiers(statement, qualifs, params)

	if not statement.qualifiers then

		return nil

	end

	local vals = {}

	if type(qualifs) == 'string' then

		qualifs = wd.splitStr(qualifs)

	end

	for i, j in pairs(qualifs) do

		if statement.qualifiersj then

			for k, l in pairs(statement.qualifiersj]) do

				table.insert(vals, l)

			end

		end

	end

	if #vals == 0 then

		return nil

	end

	return vals

end



function wd.getFormattedQualifiers(statement, qualifs, params)

	if not params then params = {} end

	local qualiftable = wd.getQualifiers(statement, qualifs)

	if not qualiftable then

		return nil

	end

	qualiftable = wd.filterClaims(qualiftable, params) or {}

	for i, j in pairs(qualiftable) do

		qualiftablei = wd.formatSnak(j, params)

	end

	return modules.linguistic.conj(qualiftable, params.conjtype)

end



function wd.showQualifiers(str, statement, args)

	local qualifs =  args.showqualifiers

	if not qualifs then

		return str -- or error ?

	end

	if type(qualifs) == 'string' then

			qualifs = wd.splitStr(qualifs)

	end

	local qualifargs = args.qualifargs or {}

	-- formatage des qualificatifs = args commençant par "qualif", ou à défaut, les mêmes que pour la valeur principale

	qualifargs.displayformat = args.qualifdisplayformat or args.displayformat

	qualifargs.labelformat = args.qualiflabelformat or args.labelformat

	qualifargs.labelformat2 = args.qualiflabelformat2 or args.labelformat2

	qualifargs.link = args.qualiflink or args.link

	qualifargs.linktopic = args.qualiflinktopic or args.linktopic

	qualifargs.conjtype = args.qualifconjtype

	qualifargs.precision = args.qualifprecision

	qualifargs.targetunit = args.qualiftargetunit

	qualifargs.defaultlink = args.qualifdefaultlink or args.defaultlink

	qualifargs.defaultlinkquery = args.qualifdefaultlinkquery or args.defaultlinkquery

	

	local formattedqualifs

	if args.qualifformat and type (args.qualifformat) == 'function' then

		formattedqualifs = args.qualifformat(statement, qualifs, qualifargs)

	else		

		formattedqualifs = wd.getFormattedQualifiers(statement, qualifs, qualifargs)

	end

	if formattedqualifs and formattedqualifs ~= "" then

		str = str .. " (" .. formattedqualifs .. ")"

	end

	return str

end





function wd.formatSnak( snak, params )

	if not params then params = {} end -- pour faciliter l'appel depuis d'autres modules

	if snak.snaktype == 'somevalue' then

		return unknownValue(snak, params.unknownlabel)

	elseif snak.snaktype == 'novalue' then

		return noValue(params.novaluelabel)

	elseif snak.snaktype == 'value' then

		return wd.getDataValue( snak, params)

	else

		return formatError( 'unknown-snak-type' )

	end

end



function wd.formatStatement( statement, args ) -- FONCTION A REORGANISER (pas très lisible)

	if not args then

		args = {}

	end

	if not statement.type or statement.type ~= 'statement' then

		return formatError( 'unknown-claim-type' )

	end

	local prop = statement.mainsnak.property



	local str



	-- special displayformat f

	if args.statementformat and (type(args.statementformat) == 'function') then

		str = args.statementformat(statement, args)

	elseif (statement.mainsnak.datatype == 'time') and (statement.mainsnak.dateformat ~= '-') then

		if args.displayformat == 'raw' and statement.mainsnak.snaktype == 'value' then

			str = statement.mainsnak.datavalue.value.time

		else

			str = wd.getFormattedDate(statement, args)

		end

	elseif args.showonlyqualifier and (args.showonlyqualifier ~= '') then

		str = wd.getFormattedQualifiers(statement, args.showonlyqualifier, args)

		if not str then

			return nil

		end

		if args.addstandardqualifs ~= '-' then

			str = wd.addStandardQualifs(str, statement)

		end

	else

		str = wd.formatSnak( statement.mainsnak, args )

		if (args.addstandardqualifs ~= '-') and (args.displayformat ~= 'raw') then

			str = wd.addStandardQualifs(str, statement)

		end

	end



	-- ajouts divers

	if args.showlang == true then

		local indicateur = showLang(statement, args.maxLang)

		if indicateur then

			str = indicateur .. '&nbsp;' .. str	

		end

	end

	if args.showqualifiers then

		str = wd.showQualifiers(str, statement, args)

	end



	if args.showdate then -- when "showdate and chronosort are both set, date retrieval is performed twice

		local period = wd.getFormattedDate(statement, args, "-") -- 3 arguments indicate the we should not use additional qualifiers, alrady added by wd.formatStatement

		if period then

			str = str .. " <small>(" .. period .. ")</small>"

		end

	end



	if args.showsource and args.showsource ~= '-' then

		local sources, hashes = wd.getReferences(statement)

		if sources then

			local source = wd.sourceStr(sources, hashes)

			if source then

				str = str .. source

			end

		end

	end



	return str

end



function wd.addLinkBack(str, id, property)

	if not id or id == '' then

		id = wd.getEntityIdForCurrentPage()

	end

	if not id then

		return str

	end

	if type(property) == 'table' then

		property = property1

	end

	

	id = wd.entityId(id)



	local class = ''

	if property then

		class = 'wd_' .. string.lower(property)

	end

	local icon = '[[File:Blue pencil.svg|%s|10px|baseline|class=noviewer|link=%s]]'

	local title = wd.translate('see-wikidata-value')

	local url = mw.uri.fullUrl('d:' .. id, 'uselang=fr')

	url.fragment = property -- ajoute une #ancre si paramètre "property" défini

	url = tostring(url)

	local v = mw.html.create('span')

		:addClass(class)

		:wikitext(str)

		:tag('span')

			:addClass('noprint wikidata-linkback')

			:wikitext(icon:format(title, url))

		:allDone()

	return tostring(v)

end



function wd.addRefAnchor(str, id)

--[[

	Insère une ancre pour une référence générée à partir d'un élément wd.

	L'id Wikidata sert d'identifiant à l'ancre, à utiliser dans les modèles type "harvsp"

--]]

	return tostring(

		mw.html.create('span')

			:attr('id', id)

			:attr('class', "ouvrage")

			:wikitext(str)

	)

end



--=== FUNCTIONS USING AN ENTITY AS ARGUMENT ===

local function formatStatementsGrouped(args, type)

	-- regroupe les affirmations ayant la même valeur en mainsnak, mais des qualificatifs différents

	-- (seulement pour les propriétés de type élément)



	local claims = wd.getClaims(args)

	if not claims then

		return nil

	end

	local groupedClaims = {}



	-- regroupe les affirmations par valeur de mainsnak

	local function addClaim(claim)

		local id = wd.getMainId(claim)

		for i, j in pairs(groupedClaims) do 

			if (j.id == id) then

				table.insert(groupedClaimsi].claims, claim)

				return

			end

		end

		table.insert(groupedClaims, {id = id, claims = {claim}})

	end

	for i, claim in pairs(claims) do	

		addClaim(claim)

	end



	local stringTable = {}



	-- instructions ad hoc pour les paramètres concernant la mise en forme d'une déclaration individuelle

	local funs = {

		{param = "showqualifiers", fun = function(str, claims)

			local qualifs = {}

			for i, claim in pairs(claims) do

				local news = wd.getFormattedQualifiers(claim, args.showqualifiers, args)

				if news then

					table.insert(qualifs, news)

				end

			end

			local qualifstr = modules.linguistic.conj(qualifs, wd.translate("qualif-separator"))

			if qualifstr and qualifstr ~= "" then

				str = str .. " (" .. qualifstr .. ")"

			end

			return str

			end

		},

		{param = "showdate", fun = function(str, claims)

			-- toutes les dates sont regroupées à l'intérieur des mêmes parenthèses ex "médaille d'or (1922, 1924)"

			local dates = {}

			for i, statement in pairs(claims) do

				local s = wd.getFormattedDate(statement, args, true)

				if statement then table.insert(dates, s) end

			end

			local datestr = modules.linguistic.conj(dates)

			if datestr and datestr ~= "" then

				str = str .. " <small>(" .. datestr .. ")</small>"

			end

			return str

			end

		},

		{param = "showsource", fun = function(str, claims)

			-- les sources sont toutes affichées au même endroit, à la fin

			-- si deux affirmations ont la même source, on ne l'affiche qu'une fois

			local sources = {}

			local hashes = {}

		

			local function dupeRef(old, new)

				for i, j in pairs(old) do

					if j == new then

						return true

					end

				end

			end

			for i, claim in pairs(claims) do

				local refs, refHashes = wd.getReferences(claim)

				if refs then

					for i, j in pairs(refs) do

						if not dupeRef(sources, j) then

							table.insert(sources, j)

							local hash = (refHashes and refHashesi]) or '-'

							table.insert(hashes, hash)

						end

					end

				end

			end

			return str .. (wd.sourceStr(sources, hashes) or "")

			end

		}

	}



	for i, group in pairs(groupedClaims) do -- bricolage pour utiliser les arguments de formatStatements

		local str = wd.formatEntity(group.id, args)

		if not str then

			str = '???' -- pour éviter erreur Lua si formatEntity a retourné nil

		end

		for i, fun in pairs(funs) do

			if argsfun.param then

				str = fun.fun(str, group.claims, args)

			end

		end

		table.insert(stringTable, str)

	end

				

	args.valuetable = stringTable

	return wd.formatStatements(args)

end





function wd.formatStatements( args )--Format statement and concat them cleanly



	if args.value == '-' then

		return nil

	end



	-- If a value is already set: use it, except if it's the special value {{WD}} (use wikidata)

	if args.value and args.value ~= '' then

		local valueexpl = wd.translate("activate-query")

		if args.value ~= valueexpl then

			return args.value

		end

	-- There is no value set, and args.expl disables wikidata on empty values

	elseif args.expl then

		return nil

	end



	if args.grouped and args.grouped ~= '' then

		args.grouped = false

		return formatStatementsGrouped(args)

	end

	local valuetable = args.valuetable -- dans le cas où les valeurs sont déjà formtées

	local props -- les prorpriétés réellement utilisées (dans certainse cas, ce ne sont pas toutes celles de ags.property

	local cat = ''

	if not valuetable then -- cas le plus courant

		valuetable, props, cat = wd.stringTable(args)

	end



	local str = modules.linguistic.conj(valuetable, args.conjtype)

	if not str then

		return args.default

	end

	if not props then

		props = wd.splitStr(args.property)[1

	end

	if args.ucfirst ~= '-' then

		str = modules.linguistic.ucfirst(str)

	end



	if args.addcat and (args.addcat ~= '-') then

		str = str .. wd.addTrackingCat(props) .. cat

	end

	if args.linkback and (args.linkback ~= '-') then

		str = wd.addLinkBack(str, args.entity, props)

	end

	if args.returnnumberofvalues then

		return str, #valuetable

	end

	return str

end



function wd.formatAndCat(args)

	if not args then

		return nil

	end

	args.linkback = args.linkback or true

	args.addcat = true

	if args.value then -- do not ignore linkback and addcat, as formatStatements do

		if args.value == '-' then

			return nil

		end

		local val = args.value .. wd.addTrackingCat(args.property)

		val = wd.addLinkBack(val, args.entity, args.property)

		return val

	end 

	return wd.formatStatements( args )

end



function wd.getTheDate(args)

	local claims = wd.getClaims(args)

	if not claims then

		return nil

	end

	local formattedvalues = {}

	for i, j in pairs(claims) do

		local v = wd.getFormattedDate(j, args)

		if v then

			table.insert(formattedvalues, v )

		end

	end

	local val = modules.linguistic.conj(formattedvalues)

	if not val then

		return nil

	end

	if args.addcat == true then

		val = val .. wd.addTrackingCat(args.property)

	end

	val = wd.addLinkBack(val, args.entity, args.property)

	return val

end





function wd.keyDate (event, item, params)

	params = params or {}

	params.entity = item

	if type(event) == 'table' then

		for i, j in pairs(event) do

			params.targetvalue = nil -- réinitialisation barbare des paramètres modifiés

			local s = wd.keyDate(j, item, params)

			if s then

				return s

			end

		end

	elseif type(event) ~= 'string' then

		 return formatError('invalid-datatype', type(event), 'string')

	elseif string.sub(event, 1, 1) == 'Q' then -- on demande un élément utilisé dans P:P793 (événement clé)

		params.property = 'P793'

		params.targetvalue = event

		params.addcat = params.addcat or true

		return wd.getTheDate(params)

	elseif string.sub(event, 1, 1) == 'P'  then -- on demande une propriété

		params.property = event

		return wd.formatAndCat(params)

	else

		return formatError('invalid-entity-id', event)

	end

end



function wd.mainDate(entity)	

	-- essaye P580/P582

	local args = {entity = entity, addcat = true}

	

	args.property = 'P580'

	local startpoint = wd.formatStatements(args)

	args.property = 'P582'

	local endpoint = wd.formatStatements(args)



	local str

	if (startpoint or endpoint) then

		str = modules.formatDate.daterange(startpoint, endpoint, params)

		str = wd.addLinkBack(str, entity, 'P582')

		return str

	end



	-- défaut : P585

	args.property = {'P585', 'P571'}

	args.linkback = true

	return wd.formatStatements(args)

end



-- === FUNCTIONS FOR TRANSITIVE PROPERTIES ===



function wd.getIds(item, query)

	query.excludespecial = true

	query.displayformat = 'raw'

	query.entity = item

	query.addstandardqualifs = '-'

	return wd.stringTable(query)

end





-- recursively adds a list of qid to an existing list, based on the results of a query

function wd.addVals(list, query, maxdepth, maxnodes, stopval)

	maxdepth = tonumber(maxdepth) or 10

	maxnodes = tonumber(maxnodes) or 100

	if (maxdepth < 0) then

		return list

	end

	if stopval and wd.isHere(list, stopval) then

		return list

	end

	local origsize = #list

	for i = 1, origsize do

		-- tried a  "checkpos" param instead of starting to 1 each time, but no impact on performance

		local candidates = wd.getIds(listi], query)

		list = wd.addNewValues(list, candidates, maxnodes, stopval)

		if list#list == stopval then

			return list

		end

		if #list >= maxnodes then

			return list

		end

	end

	

	if (#list == origsize) then

		return list

	end

	return wd.addVals(list, query, maxdepth - 1, maxnodes, stopval, origsize + 1)

end



-- returns a list of items transitively matching a query (orig item is not included in the list)



function wd.transitiveVals(item, query, maxdepth, maxnodes, stopval)

	maxdepth = tonumber(maxdepth) or 5

	if type(query) == "string" then

		query = {property = query}

	end



	-- récupération des valeurs

	local vals = wd.getIds(item, query)

	if not vals then

		return nil

	end

	local v = wd.addVals(vals, query, maxdepth - 1, maxnodes, stopval) 

	if not v then

		return nil

	end



	-- réarrangement des valeurs

	if query.valorder == "inverted" then

		local a = {}

		for i = #v, 1, -1 do

			a#a+1 = vi

		end

		v = a

	end



	return v

end



-- returns true if an item is the value of a query, transitively

function wd.inTransitiveVals(searchedval, sourceval, query, maxdepth, maxnodes )

	local vals = wd.transitiveVals(sourceval, query, maxdepth, maxnodes, searchedval )

	if (not vals) then 

		return false

	end

	for _, val in ipairs(vals) do

		if (val == searchedval) then

			return true

		end

	end

	return false

end



-- returns true if an item is a superclass of another, based on P279

function wd.isSubclass(class, item, maxdepth)

	local query = {property = 'P279'}

	if class == item then -- item is a subclass of itself iff it is a class

		if wd.getIds(item, query) then

			return true

		end

		return false

	end

	return wd.inTransitiveVals(class, item, query, maxdepth )

end



-- returns true if one of the best ranked P31 values of an item is the target or a subclass of the target

-- rank = 'valid' would seem to make sense, but it would need to check for date qualifiers as some P31 values have begin or end date

function wd.isInstance(targetclass, item, maxdepth)

	maxdepth = maxdepth or 10

	local directclasses = wd.transitiveVals(item, {property = 'P31'}, 1)

	if not directclasses then

		return false

	end 

	for i, class in pairs(directclasses) do

		if wd.isSubclass(targetclass, class, maxdepth - 1) then

			return true

		end

	end

	return false

end



-- return the first value in a transitive query that belongs to a particular class. For instance find a value of P131 that is a province of Canada

function wd.findVal(sourceitem, targetclass, query, recursion, instancedepth)

	if type(query) == "string" then

		query = {property = query}

	end

	local candidates = wd.getIds(sourceitem, query)

	if candidates then

		for i, j in pairs(candidates) do

			if wd.isInstance(targetclass, j,  instancedepth) then

				return j

			end

		end

		if not recursion then

			recursion = 3

		else

			recursion = recursion - 1

		end

		if recursion < 0 then

			return nil

		end

		for i, candidate in pairs(candidates) do

			return wd.findVal(candidate, targetclass, query, recursion, instancedepth)

		end

	end

end





-- === VARIA ===

function wd.getDescription(entity, lang)

	lang = lang or defaultlang



	local description

	if lang == defaultlang then

		return  mw.wikibase.description(qid)

	end

	if not entity.descriptions then

		return wd.translate('no description')

	end

	local descriptions = entity.descriptions

	if not descriptions then

		return nil

	end

	if descriptionslang then

		return descriptionsdelang].value

	end

	return entity.id

end



function wd.Dump(entity)

	entity = wd.getEntity(entity)

	if not entity then

		return formatError("entity-param-not-provided")

	end

	return "<pre>"..mw.dumpObject(entity).."</pre>"

end



function wd.frameFun(frame)

	local args = frame.args

	local funname = args1

	table.remove(args, 1)

	return wdfunname](args)

end





return wd
From Wikipedia, the free encyclopedia

--script that retrieves basic data stored in Wikidata, for the datamodel, see https://www.mediawiki.org/wiki/Extension:Wikibase_Client/Lua



local wd = {}



-- creation of a subobject to store comparison funtions, used for sorting claims

-- to be able to build more complex sorts like topological sorts



wd.compare = {}



local modules = { }

local modulesNames = {

	reference = 'Module:Wikidata/Références',

	i18n = 'Module:Wikidata/I18n',

	globes = 'Module:Wikidata/Globes',

	langhierarchy = 'Module:Wikidata/Hiérarchie des langues',

	langcodes = 'Module:Dictionnaire Wikidata/Codes langue', -- big, infrequently useda

	invertedlangcodes = 'Module:Dictionnaire Wikidata/Codes langue/inversé',



	linguistic = 'Module:Linguistique',

	datemodule = 'Module:Date',

	formatDate = 'Module:Date complexe',

	formatNum = 'Module:Conversion',

	langmodule = 'Module:Langue',

	cite = 'Module:Biblio',

	weblink = 'Module:Weblink'

}



local function loadModule( t, key )

	if modulesNameskey then

		local m = require( modulesNameskey )

		tkey = m

		return m

	end

end

setmetatable( modules, { __index = loadModule } )



local datequalifiers = {'P585', 'P571', 'P580', 'P582', 'P1319', 'P1326'}



-- === I18n ===

local defaultlang = mw.getContentLanguage():getCode()



function wd.translate(str, rep1, rep2)

	str = modules.i18nstr or str

	if rep1 and (type (rep1) == 'string') then

		str = str:gsub('$1', rep1)

	end

	if rep2 and (type (rep2) == 'string')then

		str = str:gsub('$2', rep2)

	end

	return str

end



local function addCat(cat, sortkey)

	if sortkey then

		return  '[[Category:' .. cat .. '|' .. sortkey .. ']]'

	end

	return '[[Category:' .. cat  .. ']]'

end



local function formatError( key , category, debug)

    if debug then

        return error(modules.i18nkey or key)

    end

    if category then

        return addCat(category, key)

    else

        return addCat('cat-unsorted-issue', key)

    end

end



-- 



function wd.isSpecial(snak)

	return (snak.snaktype ~= 'value')

end



function wd.getId(snak)

	if (snak.snaktype == 'value') then

		return 'Q' .. snak.datavalue.value'numeric-id'

	end

end



function wd.getNumericId(snak)

	if (snak.snaktype == 'value') then

		return snak.datavalue.value'numeric-id'

	end

end



function wd.getMainId(claim)

	return wd.getId(claim.mainsnak)

end



function wd.entityId(entity)

	if type(entity) == 'string' then

		return entity

	elseif type(entity) == 'table' then

		return entity.id

	end

end



function wd.getEntityIdForCurrentPage()

	return mw.wikibase.getEntityIdForCurrentPage()

end



-- function that returns true if the "qid" parameter is the qid 

-- of the item that is linked to the calling page

function wd.isPageOfQId(qid) 

	local self_id = mw.wikibase.getEntityIdForCurrentPage() 

	return self_id ~= nil and qid == self_id 

end



function wd.getEntity( val ) 

	if type(val) == 'table' then

		return val

	end

	if val == '-' then

		return nil

	end

	if val == '' then

		val = nil

	end

	return mw.wikibase.getEntity(val)

end



function wd.splitStr(val) -- transforme en table les chaînes venant du Wikitexte qui utilisent des virgules de séparation

	if type(val) == 'string' then

		val = mw.text.split(val, ",")

	end

	return val

end



function wd.isHere(searchset, val)

	for i, j in pairs(searchset) do

		if val == j then

			return true

		end

	end

	return false

end





local function wikidataLink(entity)

	local name =':d:'

	

	if type(entity) == 'string' then

		if entity:match("P[0-9+]") then

			entity = "Property:" .. entity

		end

		return name .. entity

	elseif type(entity) == 'table' then

		if entity"type" == "property" then

			name = ":d:Property:"

		end

		return name .. entity.id

	elseif type(entity) == nil then

		return formatError('entity-not-found')

	end

end



function wd.siteLink(entity, project, lang)

	-- returns 3 values: a sitelink (with the relevant prefix) a project name and a language

	lang = lang or defaultlang

	if (type(project) ~= 'string') then

		project = 'wiki'

	end

	project = project:lower()

	if project == 'wikipedia' then

		project = 'wiki'

	end

	if type(entity) == 'string' and (project == 'wiki') and ( (not lang or lang == defaultlang) ) then -- évite de charger l'élément entier

		return  mw.wikibase.sitelink(entity), 'wiki', defaultlang

	end

	if project == 'wikidata' then

		return wikidataLink(entity), 'wikidata'

	end

	local projects = {

		-- nom = {préfixe sur Wikidata, préfix pour les liens sur Wikipédia, ajouter préfixe de langue}

		wiki = {'wiki', nil, true}, -- wikipedia

		commons = {'commonswiki', 'commons', false},

		commonswiki = {'commonswiki', 'commons', false},

		wikiquote = {'wikiquote', 'q', true},

		wikivoyage = {'wikivoyage', 'voy', true},

		wikibooks = {'wikibooks', 'b', true},

		wikinews = {'wikinews', 'n', true},

		wikiversity = {'wikiversity', 'v', true},

		wikisource = {'wikisource', 's', true},

		wiktionary = {'wiktionary', 'wikt', true},

		specieswiki = {'specieswiki', 'species', false},

		metawiki = {'metawiki', 'm', false},

		incubator = {'incubator', 'incubator', false},

		outreach = {'outreach', 'outreach', false},

		mediawiki = {'mediawiki', 'mw', false}

	}



	local entityid = entity.id or entity



	local projectdata = projectsproject:lower()]

	if not projectdata then -- defaultlink might be in the form "dewiki" rather than "project: 'wiki', lang: 'de' "

		for k, v in pairs(projects) do

			if project:match( k .. '$' ) 

				and mw.language.isKnownLanguageTag(project:sub(1, #project-#k))

			then

				lang = project:sub(1, #project-#k)

				project = project:sub(#lang + 1, #project)

				projectdata = projectsproject

				break

			end

		end

		if not mw.language.isKnownLanguageTag(lang) then

			return --formatError('invalid-project-code', projet or 'nil')

		end

	end

	if not projectdata then

		return -- formatError('invalid-project-code', projet or 'nil')

	end

	

	local linkcode = projectdata1

	local prefix = projectdata2

	local multiversion = projectdata3

	if multiversion then

		linkcode = lang .. linkcode

	end

	local link = mw.wikibase.getSitelink(entityid, linkcode)

	if not link then

		return nil

	end

	

	if prefix then

		link = prefix .. ':' .. link

	end

	if multiversion then

		link = ':' .. lang .. ':' .. link

	end

	return link, project, lang

end



-- add new values to a list, avoiding duplicates

function wd.addNewValues(olditems, newitems, maxnum, stopval)

	if not newitems then

		return olditems

	end

	for _, qid in pairs(newitems) do

		if stopval and (qid == stopval) then

			table.insert(olditems, qid)

			return olditems

		end

		if maxnum and (#olditems >= maxnum) then

			return olditems

		end

		if not wd.isHere(olditems, qid) then

			table.insert(olditems, qid)

		end

	end

	return olditems

end



--=== FILTER CLAIMS ACCORDING TO VARIOUS CRITERIA : FUNCTION GETCLAIMS et alii ===



local function notSpecial(claim)

	local type

	

	if claim.mainsnak ~= nil then

		type = claim.mainsnak.snaktype

	else

		-- condition respectée quand showonlyqualifier est un paramètre renseigné

		-- dans ce cas, claim n'est pas une déclaration entière, mais UNE snak qualifiée du main snak

		type = claim.snaktype

	end

	

	return type == 'value'

end



local function hasTargetValue(claim, targets) -- retourne true si la valeur est dans la liste des target, ou si c'est une valeur spéciale filtrée séparément par excludespecial

	local id = wd.getMainId(claim)

	local targets = wd.splitStr(targets)

	return wd.isHere(targets, id) or wd.isSpecial(claim.mainsnak)

end



local function excludeValues(claim, values) -- true si la valeur n'est pas dans la liste, ou si c'est une valeur spéciale (filtrée à part par excludespecial)

	return wd.isSpecial(claim.mainsnak) or not ( hasTargetValue(claim, values) )

end



local function bestRanked(claims)

	if not claims then

		return nil

	end

	local preferred, normal = {}, {}

	for i, j in pairs(claims) do

		if j.rank == 'preferred' then

			table.insert(preferred, j)

		elseif j.rank == 'normal' then

			table.insert(normal, j)

		end

	end

	if #preferred > 0 then

		return preferred

	else

		return normal

	end

end



local function withRank(claims, target)

	if target == 'best' then

		return bestRanked(claims)

	end

	local newclaims = {}

	for pos, claim in pairs(claims)  do

		if target == 'valid' then

			if claim.rank ~= 'deprecated' then

				table.insert(newclaims, claim)

			end

		elseif claim.rank == target then

			table.insert(newclaims, claim)

		end

	end

	return newclaims

end



function wd.hasQualifier(claim, acceptedqualifs, acceptedvals, excludequalifiervalues)

	local claimqualifs = claim.qualifiers

	

	if (not claimqualifs) then

		return false

	end



	acceptedqualifs = wd.splitStr(acceptedqualifs)

	acceptedvals = wd.splitStr( acceptedvals)





	local function ok(qualif) -- vérification pour un qualificatif individuel

		if not claimqualifsqualif then

			return false

		end

		if not (acceptedvals) then  -- si aucune valeur spécifique n'est demandée, OK

			return true

		end

		for i, wanted in pairs(acceptedvals) do

			for j, actual in pairs(claimqualifsqualif]) do

				if wd.getId(actual) == wanted then

					return true

				end

			end

		end

	end



	for i, qualif in pairs(acceptedqualifs) do

		if ok(qualif) then

			return true

		end

	end

	return false

end



function wd.hasQualifierNumber(claim, acceptedqualifs, acceptedvals, excludequalifiervalues)

	local claimqualifs = claim.qualifiers

	

	if (not claimqualifs) then

		return false

	end



	acceptedqualifs = wd.splitStr(acceptedqualifs)

	acceptedvals = wd.splitStr( acceptedvals)





	local function ok(qualif) -- vérification pour un qualificatif individuel

		if not claimqualifsqualif then

			return false

		end

		if not (acceptedvals) then  -- si aucune valeur spécifique n'est demandée, OK

			return true

		end

		for i, wanted in pairs(acceptedvals) do

			for j, actual in pairs(claimqualifsqualif]) do

				if mw.wikibase.renderSnak(actual) == wanted then

					return true

				end

			end

		end

	end



	for i, qualif in pairs(acceptedqualifs) do

		if ok(qualif) then

			return true

		end

	end

	return false

end



local function hasSource(claim, targetsource, sourceproperty)

	sourceproperty = sourceproperty or 'P248'

	if targetsource == "-" then

		return true

	end

	if (not claim.references) then return

		false

	end

	local candidates = claim.references1].snakssourceproperty -- les snaks utilisant la propriété demandée

	if (not candidates) then

		return false

	end

	if (targetsource == "any") then -- si n'importe quelle valeur est acceptée tant qu'elle utilise en ref la propriété demandée

		return true

	end

	targetsource = wd.splitStr(targetsource)

	for _, source in pairs(candidates) do

		local s = wd.getId(source)

		for i, target in pairs(targetsource) do

			if s == target then return true end

		end

	end

	return false

end



local function excludeQualifier(claim, qualifier, qualifiervalues)

	return not wd.hasQualifier(claim, qualifier, qualifiervalues)

end



function wd.hasDate(claim)

	if not claim then

		return false --error() ?

	end

	if wd.getDateFromQualif(claim, 'P585') or wd.getDateFromQualif(claim, 'P580') or wd.getDateFromQualif(claim, 'P582') then

		return true

	end

	return false

end



local function hasLink(claim, site, lang)

	if (claim.mainsnak.snaktype ~= 'value') then -- ne pas supprimer les valeurs spéciales, il y a une fonction dédiée pour ça

		return true

	end

	local id = wd.getMainId(claim)

	local link = wd.siteLink(id, site, lang)

	if link then

		return true

	end

end



local function isInLanguage(claim, lang) -- ne fonctionne que pour les monolingualtext / étendre aux autres types en utilisant les qualifiers ?

	if type(lang) == 'table' then -- si c'est une table de language séparées par des virgules, on les accepte toutes

		for i, l in pairs(lang) do

			local v = isInLanguage(claim, l)

			if v then

				return true

			end

		end

	end

	if type(lang) ~= ('string') then

		return --?

	end



	if (lang == '-') then

		return true

	end

	if (lang == 'locallang') then

		lang =  mw.getContentLanguage():getCode()

	end

	

	-- pour les monolingual text

	local snak = claim.mainsnak or claim

	if snak.snaktype == 'value' and snak.datavalue.type == 'monolingualtext' then

		if snak.datavalue.value.language == lang then

			return true

		end

		return false

	end



	-- pour les autres types de données : recherche dans les qualificatifs

	if (lang == 'fr') then

		lang = 'Q150'

	elseif (lang == 'en') then

		lang = 'Q1860'

	else

		lang = invertedlangcodeslang

	end

	if claim.qualifiers and claim.qualifiers.P407 then

		if wd.hasQualifier(claim, {'P407'}, {lang}) then

			return true

		else

			return false

		end

	end



	return true -- si on ne ne sait pas la langue, on condière que c'est bon

end



local function firstVals(claims, numval) -- retourn les numval premières valeurs de la table claims

    local numval = tonumber(numval) or 0 -- raise a error if numval is not a positive integer ?

    if not claims then

    	return nil

    end

    while (#claims > numval) do

    	table.remove(claims)

    end

    return claims

end



local function lastVals(claims, numval2) -- retourn les valeurs de la table claims à partir de numval2

    local numval2 = tonumber(numval2) or 0 -- raise a error if numval is not a positive integer ?

    if not claims then

    	return nil

    end

    for i=1,numval2 do

    	table.remove(claims, 1)

    end

    return claims

end



-- retourne les valeurs de la table claims à partir de removedupesdate,

-- sans les dates en doublons avec conversion entre les calendrier julien et grégorien,

-- ou uniquement en catégorisant si le paramètre removedupesdate est égale à 'cat'

local function removeDupesDate(claims, removedupesdate)

	if not claims or #claims < 2 then

		return claims, ''

	end

	local cat = ''

	local newClaims = {}

	local newIsos = {}



	local function findIndex(searchset, val) -- similaire à wd.isHere mais retourne l'index de la valeur trouvée

		for i, j in pairs(searchset) do

			if val == j then

				return i

			end

		end

		return -1

	end



	for _, claim in ipairs( claims ) do

		local snack = claim.mainsnak or claim

		if (snack.snaktype == 'value') and (snack.datatype == 'time') and snack.datavalue.value.precision >= 11 then -- s'il s'agit d'un time et que la précision est au moins l'année

			local iso = snack.datavalue.value.time

			_, _, iso = string.find(iso, "(+%d+-%d+-%d+T)")

			local deleteIfDuplicate = false

			if snack.datavalue.value.calendarmodel == 'http://www.wikidata.org/entity/Q1985727' then -- si la date est grégorienne

				if modules.formatDate.before('+1582', iso) then -- si avant 1582 on calcule la date julienne

					_, _, y, m, d = string.find(iso, "+(%d+)-(%d+)-(%d+)T")

					y, m , d = modules.datemodule.gregorianToJulian(y, m , d)

					if m < 10 then m = '0' .. m end

					if d < 10 then d = '0' .. d end

					iso = '+' .. y .. '-' .. m .. '-' .. d .. 'T'

					deleteIfDuplicate = true

				end

				local index = findIndex(newIsos, iso)

				if index >= 0 then -- si la date est déjà présente

					cat = cat .. '[[Catégorie:Article avec des dates identiques venant de wikidata dans le code de l\'infobox]]'

					if removedupesdate == "cat" then -- ne faire que catégoriser

						table.insert(newIsos, iso)

						table.insert(newClaims, claim)

					elseif not deleteIfDuplicate then -- supprimer l'autre date si la date courante n'a pas été convertie

						newClaimsindex = claim

					end -- sinon supprimer la date courante

				else -- pas de doublon

					table.insert(newIsos, iso)

					table.insert(newClaims, claim)

				end

			elseif snack.datavalue.value.calendarmodel == 'http://www.wikidata.org/entity/Q1985786' then -- si date julienne

				if not modules.formatDate.before('+1582', iso) then -- si après 1582 on calcule la date grégorienne

					_, _, y, m, d = string.find(iso, "+(%d+)-(%d+)-(%d+)T")

					y, m , d = modules.datemodule.julianToGregorian(y, m , d)

					if m < 10 then m = '0' .. m end

					if d < 10 then d = '0' .. d end

					iso = '+' .. y .. '-' .. m .. '-' .. d .. 'T'

					deleteIfDuplicate = true

				end

				local index = findIndex(newIsos, iso)

				if index >= 0 then -- si date déjà présente

					cat = cat .. '[[Catégorie:Article avec des dates identiques venant de wikidata dans le code de l\'infobox]]'

					if removedupesdate == "cat" then -- ne faire que catégoriser

						table.insert(newIsos, iso)

						table.insert(newClaims, claim)

					elseif not deleteIfDuplicate then -- supprimer l'autre date si la date courante n'a pas été convertie

						newClaimsindex = claim

					end -- sinon supprimer la date courante

				else -- pas de doublon

					table.insert(newIsos, iso)

					table.insert(newClaims, claim)

				end

			else -- autre calendrier

				table.insert(newIsos, iso)

				table.insert(newClaims, claim)

			end

		else -- précision insuffisante

			table.insert(newIsos, iso)

			table.insert(newClaims, claim)

		end

	end

	return newClaims, cat

end



local function timeFromQualifs(claim, qualifs)

	local claimqualifs = claim.qualifiers

	if not claimqualifs then

		return nil

	end

	for i, qualif in pairs(qualifs or timequalifiers) do

		local vals = claimqualifsqualif

		if vals and (vals1].snaktype == 'value') then

			return vals1].datavalue.value.time, vals1].datavalue.value.precision

		end

	end

end



local function atDate(claim, mydate) 

	if mydate == "today" then

		mydate = os.date("!%Y-%m-%dT%TZ")

	end

	-- determines required precision depending on the atdate format

	local d = mw.text.split(mydate, "-")



	local myprecision

	if d3 then

		myprecision =  11 -- day

	elseif d2 then

		myprecision = 10 -- month

	else

		myprecision = 9 -- year

 	end



	-- with point in time

	local d, storedprecision = timeFromQualifs(claim, {'P585'})

	if d then

		return modules.formatDate.equal(mydate, d, math.min(myprecision, storedprecision))

	end

	-- with start or end date -- TODO: precision 

	local mindate = timeFromQualifs(claim, {'P580'}) 

	local maxdate = timeFromQualifs(claim, {'P582'})

	if modules.formatDate.before(mydate, mindate) and  modules.formatDate.before(maxdate, mydate) then

		return true

	end

	return false

end



local function check(claim, condition)

	if type(condition) == 'function' then -- cas standard

		return condition(claim)

	end

	return formatError('invalid type', 'function', type(condition)) 

end



local function minPrecision(claim, minprecision)

	local snack

	if claim.qualifiers then -- si une date est donnée en qualificatif, c'est elle qu'on utilise de préférence au mainsnak

		for i, j in ipairs(datequalifiers) do

			if claim.qualifiersj then

				snack = claim.qualifiersj][1 

				break

			end

		end

	end

	if not snack then

		snack = claim.mainsnak or claim

	end

	if (snack.snaktype == 'value') and (snack.datatype == 'time') and (snack.datavalue.value.precision < minprecision) then

		return false

	end

	return true

end



function wd.sortClaims(claims, sorttype)

	if not claims then

		return nil

	end

	if sorttype == 'chronological' then

		return wd.chronoSort(claims)

	elseif sorttype == 'inverted' then

		return wd.chronoSort(claims, true)

	elseif sorttype == 'order' then

		return wd.chronoSort(claims)

	elseif sorttype == 'ascending' then

		return wd.quantitySort(claims)

	elseif sorttype == 'descending' then

		return wd.quantitySort(claims, true)

	elseif type(sorttype) == 'function' then

		table.sort(claims, sorttype)

		return claims

	elseif type(sorttype) == 'string' and sorttype:sub(1, 1) == 'P' then

		return wd.numericPropertySort(claims, sorttype)

	end

	return claims

end



function wd.filterClaims(claims, args) --retire de la tables de claims celles qui sont éliminés par un des filters de la table des filters



	local function filter(condition, filterfunction, funargs)

		if not argscondition then

			return

		end

		for i = #claims, 1, -1 do

			if not( filterfunction(claimsi], argsfunargs1]], argsfunargs2]], argsfunargs3]]) ) then

				table.remove(claims, i)

			end

		end

	end

	filter('isinlang', isInLanguage, {'isinlang'} )

	filter('excludespecial', notSpecial, {} )

	filter('condition', check, {'condition'} )

	if claims1 and claims1].mainsnak then

		filter('targetvalue', hasTargetValue, {'targetvalue'} )

		filter('atdate', atDate, {'atdate'} )

		filter('qualifier', wd.hasQualifier, {'qualifier', 'qualifiervalue'} )

		filter('qualifiernumber', wd.hasQualifierNumber, {'qualifiernumber', 'qualifiernumbervalue'} )

		filter('excludequalifier', excludeQualifier, {'excludequalifier', 'excludequalifiervalue'} )

		filter('withsource', hasSource, {'withsource', 'sourceproperty'} )

		filter('withdate', wd.hasDate, {} )

		filter('excludevalues', excludeValues, {'excludevalues'})

		filter('withlink', hasLink, {'withlink', 'linklang'} )

		filter('minprecision', minPrecision, {'minprecision'} )

		claims = withRank(claims, args.rank or 'best')

	end

	if #claims == 0 then

		return nil

	end

	if args.sorttype then

		claims = wd.sortClaims(claims, args.sorttype)

	end

	if args.numval2 then

		claims = lastVals(claims, args.numval2)

	end

	if args.numval then

		claims = firstVals(claims, args.numval)

	end

	return claims



end



function wd.loadEntity(entity, cache)

	if type(entity) ~= 'table' then

		if cache then

			if not cacheentity then 

				cacheentity = mw.wikibase.getEntity(entity)

				mw.log("cached")

     		end

			return cacheentity

        else

			if entity == '' or (entity == '-') then

				entity = nil

			end

        	return mw.wikibase.getEntity(entity)

        end

    else 

    	return entity

    end

end





function wd.getClaims( args ) -- returns a table of the claims matching some conditions given in args

	if args.claims then -- if claims have already been set, return them

		return args.claims

	end

	local properties = args.property

	if type(properties) == 'string' then

		properties = wd.splitStr(string.upper(args.property))

	end

	if not properties then

		return formatError( 'property-param-not-provided' )

	end



	--Get entity

	local entity = args.entity

	if type(entity) == 'string' then

		if entity == '' then

			entity = nil

		end

	elseif type(entity) == 'table' then

		entity = entity.id

	end

        if (not entity) then

            entity = mw.wikibase.getEntityIdForCurrentPage()

        end

	if (not entity) or (entity == '-') then

		return nil

	end

	local claims = {}



	if #properties == 1 then

		claims = mw.wikibase.getAllStatements(entity, properties1]) -- do not use mw.wikibase.getBestStatements at this stage, as it may remove the best ranked values that match other criteria in the query

	else

		for i, prop in ipairs(properties) do

			local newclaims = mw.wikibase.getAllStatements(entity, prop) 

			if newclaims and #newclaims > 0 then

				for j, claim in ipairs(newclaims) do

					table.insert(claims, claim)

				end

			end

		end

	end





	if (not claims) or (#claims == 0) then

		return nil

	end

	return wd.filterClaims(claims, args)

end



--=== ENTITY FORMATTING ===



function wd.getLabel(entity, lang1, lang2)

	

	if (not entity) then

		return nil -- ou option de gestion des erreurs ?

	end

	entity = entity.id or ( type(entity) == "string" and entity)

	if not(type(entity) == 'string') then return nil end

	

	lang1 = lang1 or defaultlang

	

	local str, lang --str : texte rendu, lang : langue de celui-ci

	if lang1 == defaultlang then -- le plus économique

		str, lang = mw.wikibase.getLabelWithLang(entity) -- le libellé peut être en français ou en anglais

	else

		str = mw.wikibase.getLabelByLang(entity, lang1)

		if str then lang = lang1 end

	end

	if str and (lang == lang1) then --pas de catégorie "à traduire" si on a obtenu un texte dans la langue désirée (normalement fr)

		return str

	end

	if lang2 then -- langue secondaire, avec catégorie "à traduire"

		str2 = mw.wikibase.getLabelByLang(entity, lang2)

		if str2 then

			lang = lang2

			str = str2

		end

	end

	if not str then --si ni lang1, ni lang2 ni l'anglais ne sont présents,  parcours de la hiérarchie des langues

		for _, trylang in ipairs(modules.langhierarchy.codes) do

			str = mw.wikibase.getLabelByLang(entity, trylang)

			if str then

				lang = trylang

				break

			end

		end

	end

		

	if str then

		local translationCat = modules.i18n'to translate'

		translationCat = translationCat .. (modules.langhierarchy.cattextlang or '')

		translationCat = addCat(translationCat)

		return str, translationCat

	end

end



function wd.formatEntity( entity, params )



	if (not entity) then

		return nil --formatError('entity-not-found')

	end

	local id = entity

	if type(id) == 'table' then

		id = id.id

	end



	params = params or {}

	local lang = params.lang or defaultlang

	local speciallabels = params.speciallabels

	local displayformat = params.displayformat

	local labelformat = params.labelformat

	local labelformat2 = params.labelformat2

	local defaultlabel = params.defaultlabel or id

	local linktype = params.link

	local defaultlink = params.defaultlink

	local defaultlinkquery = params.defaultlinkquery



	if speciallabels and speciallabelsid then --speciallabels override the standard label + link combination

		return speciallabelsid

	end

	if params.displayformat == 'raw' then

		return id

	end



	local link, label, translationCat

	

	if type(labelformat) == 'function' then -- sert à des cas particuliers

		label, translationCat = labelformat(entity)

	end

	

	if not label then label, translationCat = wd.getLabel(entity, lang, params.wikidatalang) end



	translationCat = translationCat or "" -- sera toujours ajoutée au résultat mais sera vide si la catégorie de maintenance n'est pas nécessaire

	

	if type(labelformat2) == 'function' then -- sert à des cas particuliers

		label = labelformat2(label)

	end

	

	-- détermination du fait qu'on soit ou non en train de rendre l'élément sur la page de son article

	local rendering_entity_on_its_page = wd.isPageOfQId(id)

	

	if not label then

		if (defaultlabel == '-') then 

			return nil

		end

		link = wd.siteLink(id, 'wikidata')

		return '[[' .. link .. '|' .. id .. ']]' .. translationCat

-- si pas de libellé, on met un lien vers Wikidata pour qu'on comprenne à quoi ça fait référence

	end



	if (linktype == '-') or rendering_entity_on_its_page then

		return label .. translationCat

	end



	local link = wd.siteLink(entity, linktype, lang)



	-- defaultlinkquery will try to link to another page on this Wiki

	if (not link) and defaultlinkquery then

		if type(defaultlinkquery) == 'string' then

				defaultlinkquery = {property = defaultlinkquery}

		end

		defaultlinkquery.excludespecial = true

		defaultlinkquery.entity = entity

		local claims = wd.getClaims(defaultlinkquery)

		if claims then

			for i, j in pairs(claims) do

				local id = wd.getMainId(j)

				link = wd.siteLink(id, linktype, lang)

				if link then

					break

				end

			end

		end	

	end



	if link then

		if mw.ustring.sub(link, 1, 9) == "Category:" or mw.ustring.sub(link, 1, 10) == "Catégorie:" then

			link = ":" .. link			--lier vers une catégorie au lieu de catégoriser

		end

		return '[[' .. link .. '|' .. label .. ']]' .. translationCat

	end



	-- if not link, you can use defaultlink: a sidelink to another Wikimedia project

	if (not defaultlink) then

		defaultlink = {'enwiki'}

	end

	if defaultlink and (defaultlink ~= '-') then

		local linktype

		local sidelink, site, langcode

	

		if type(defaultlink) == 'string' then

			defaultlink = {defaultlink}

		end

		for i, j in ipairs(defaultlink) do

			sidelink, site, langcode = wd.siteLink(entity, j, lang)

			if sidelink then

				break

			end

		end

		if not sidelink then

			sidelink, site = wd.siteLink(entity, 'wikidata')

		end

		

		local icon, class, title = site, nil, nil -- le texte affiché du lien

		if site == 'wiki' then

			icon, class, title = langcode, "indicateur-langue", wd.translate('see-another-language', mw.language.fetchLanguageName(langcode, defaultlang))	

		elseif site == 'wikidata' then

			icon, class, title = 'd',  "indicateur-langue", wd.translate('see-wikidata')		

		else

			title = wd.translate('see-another-project', site)

		end

		local val = '[[' .. sidelink .. '|' .. '<span class = "' .. (class or '').. '" title = "' .. (title or '') .. '">' .. icon .. '</span>]]'

		return label .. ' <small>(' .. val .. ')</small>' .. translationCat

	end 

	return label .. translationCat

end





function wd.addTrackingCat(prop, cat) -- doit parfois être appelé par d'autres modules

	if type(prop) == 'table' then

		prop = prop1 -- devrait logiquement toutes les ajouter

	end

	if not prop and not cat then

		return formatError("property-param-not-provided")

	end

	if not cat then

		cat = wd.translate('trackingcat', prop or 'P??')

	end

	return addCat(cat )

end



local function unknownValue(snak, label)

	local str = label



	if type(str) == "function" then

		str = str(snak)

	end



	if (not str) then

		if snak.datatype == 'time' then

			str = wd.translate('sometime')

		else

			str = wd.translate('somevalue')

		end

	end



	if type(str) ~= "string" then

		return formatError(snak.datatype)

	end

	return str

end



local function noValue(displayformat)

	if not displayformat then

		return wd.translate('novalue')

	end

	if type(displayformat) == 'string' then

		return displayformat

	end

	return formatError()

end



local function getLangCode(entityid)

	return modules.langcodestonumber(entityid:sub(2))]

end



local function showLang(statement, maxLang) -- retourne le code langue entre paranthèse avant la valeur (par exemple pour les biblios et liens externes)

	local mainsnak = statement.mainsnak

	if mainsnak.snaktype ~= 'value' then

		return nil

	end

	local langlist = {}

	if mainsnak.datavalue.type == 'monolingualtext' then

		langlist = {mainsnak.datavalue.value.language}

	elseif (not statement.qualifiers) or (not statement.qualifiers.P407) then

		return

	else

		for i, j in pairs( statement.qualifiers.P407 ) do

			if  j.snaktype == 'value' then

				local langentity = wd.getId(j)

				local langcode =  getLangCode(langentity)

				table.insert(langlist, langcode)

			end

		end

	end

	if (#langlist > 1) or (#langlist == 1 and langlist1 ~= defaultlang) then -- si c'est en français, pas besoin de le dire

		langlist.maxLang = maxLang

		return modules.langmodule.indicationMultilingue(langlist)

	end

end





-- === DATE HANDLING ===



function wd.addStandardQualifs(str, statement)

	if (not statement) or (not statement.qualifiers) then

		return str

	end

	if not str then

		return error()-- what's that ?

	end



	if statement.qualifiers.P1480 then

		for i, j in pairs(statement.qualifiers.P1480) do

			local v = wd.getId(j)

			if (v == "Q21818619") then

				str = wd.translate('approximate-place', str)

			elseif (v == "Q18122778") or (v == "Q18912752") then

				str = wd.translate('uncertain-information', str)

			elseif (v == "Q5727902") then

				if (statement.mainsnak.datatype == 'time') then

					str = modules.formatDate.fuzzydate(str)

				else

					str = wd.translate('approximate-value', str)

				end

			end			

		end

	end

	return str

end



local function rangeObject(begin, ending, params)

	--[[

		objet comportant un timestamp pour le classement chronologique et deux dateobject (begin et ending)

	]]-- 

	local timestamp

	if begin then

		timestamp = begin.timestamp

	else

		timestamp = ending.timestamp

	end

	return {begin = begin, ending = ending, timestamp = timestamp, type = 'rangeobject'}

end



local function dateObject(orig, params)

	--[[ transforme un snak en un nouvel objet utilisable par Module:Date complexe

		{type = 'dateobject', timestamp = str, era = '+' ou '-', year = number, month = number, day = number, calendar = calendar}

	]]-- 

	if not params then

		params = {}

	end

	

	local newobj = modules.formatDate.splitDate(orig.time, orig.calendarmodel)

	

	newobj.precision = params.precision or orig.precision

	newobj.type = 'dateobject'

	return newobj

end



local function objectToText(obj, params)

	if obj.type == 'dateobject' then

		return modules.formatDate.simplestring(obj, params)

	elseif obj.type == 'rangeobject' then

		return modules.formatDate.daterange(obj.begin, obj.ending, params)

	end

end



function wd.getDateFromQualif(statement, qualif)

	if (not statement) or (not statement.qualifiers) or not (statement.qualifiersqualif]) then

		return nil

	end

	local v = statement.qualifiersqualif][1

	if v.snaktype ~= 'value' then -- que faire dans ce cas ?

		return nil

	end

	return dateObject(v.datavalue.value)

end



function wd.getDate(statement)

	local period = wd.getDateFromQualif(statement, 'P585') -- retourne un dateobject

	if period then

		return period

	end

	local begin, ending = wd.getDateFromQualif(statement, 'P580'),  wd.getDateFromQualif(statement, 'P582')

	if begin or ending then

		return rangeObject(begin, ending) -- retourne un rangeobject fait de deux dateobject

	end

	return nil

end



function wd.getFormattedDate(statement, params)

	if not statement then

		return nil

	end

	local str





	--cherche la date avec les qualifs P580/P582

	local datetable = wd.getDate(statement)

	if datetable then

		str = objectToText(datetable, params)

	end

	

	-- puis limite intérieur / supérieur

	if not str then

		local start, ending = wd.getDateFromQualif(statement, 'P1319'), wd.getDateFromQualif(statement, 'P1326')

		str = modules.formatDate.between(start, ending, params)

	end



	 -- sinon, le mainsnak, pour les données de type time

	if (not str) and (statement.mainsnak.datatype == 'time') then

		local mainsnak = statement.mainsnak

		if (mainsnak.snaktype == 'value') or (mainsnak.snaktype == 'somevalue') then

			str = wd.formatSnak(mainsnak, params)

		end

	end



	if str and params and (params.addstandardqualifs ~= '-') then

		str = wd.addStandardQualifs(str, statement)

	end

	return str

end



wd.compare.by_quantity = function(c1, c2)

	local v1 = wd.getDataValue(c1.mainsnak)

	local v2 = wd.getDataValue(c2.mainsnak) 

	if not (v1 and v2) then

			return true

	end

	return v1 < v2

end



--[[ tri chronologique générique : 

             retourne une fonction de tri de liste de déclaration

             en fonction d’une fonction qui calcule la clé de tri

             et d’une fonction qui compare les clés de tri

   

   paramètres nommés: (appel type wikidata.compare.chrono_key_sort{sortKey="nom clé"})

        sortKey (optionnel)   : chaine, le nom de la clé utilisée pour un tri 

                                (pour éviter de rentrer en collision avec "dateSortKey" 

                                utilisé par chronoSort au besoin)

        snak_key_get_function : fonction qui calcule la valeur de la clé à partir d’un snak ou d’une déclaration,

        (obligatoire)           le résultat n’est calculé qu’une fois et est stocké en cache dans claim[sortKey]

        key_compare_function  : fonction de comparaison des clés calculées par snak_key_get_function

        (optionnel)

--]]    



function wd.chrono_key_sort(arg)

	

	local snak_key_get_function = arg.snak_key_get_function

	local sortKey = arg.sortKey or "dateSortKey"

	local key_compare_function = arg.key_compare_function or 

									function(c1, c2) return c1 < c2 end

	return function(claims)

		for _, claim in ipairs( claims ) do

			if not claimsortKey then

				local key = snak_key_get_function(claim)

				if key then

					claimsortKey = wd.compare.get_claim_date(key)

				else

					claimsortKey = 0

				end

			end

		end

		table.sort(

			claims,

			function(c1, c2)

				return key_compare_function(c1sortKey], c2sortKey])

			end

		)

		return claims

	end

end





function wd.quantitySort(claims, inverted)

	local function sort(c1, c2)

		local v1 = wd.getDataValue(c1.mainsnak)

		local v2 = wd.getDataValue(c2.mainsnak) 

		if not (v1 and v2) then

				return true

		end

		if inverted then

			return v2 < v1

		end

		return v1 < v2

	end

	table.sort(claims, sort )

	return claims

end



function wd.compare.get_claim_date(claim)

	local snack = claim.mainsnak or claim

	local iso

	if (snack.snaktype == 'value') and (snack.datatype == 'time') then

		iso = snack.datavalue.value.time

	else

		iso = timeFromQualifs(claim, datequalifiers) or '0'

	end

	-- transformation en nombre (indication de la base car gsub retourne deux valeurs)

	return tonumber( iso:gsub( '(%d)%D', '%1' ), 10 )

end



function wd.compare.chronoCompare(c1, c2)

	return wd.compare.get_claim_date(c1) < wd.compare.get_claim_date(c2)

end



-- fonction pour renverser l’ordre d’une autre fonction

function wd.compare.rev(comp_criteria)

	return function(c1, c2)

		-- attention les tris en lua attendent des fonctions de comparaison strictement inférieur, on doit

		-- vérifier la non égalité quand on inverse l’ordre d’un critère, d’ou "and comp_criteria(c2,c1)"

		return not(comp_criteria(c1,c2)) and comp_criteria(c2,c1)

	end

end



-- Fonction qui trie des Claims de type time selon l'ordre chronologique

-- Une clé de tri nomée « dateSortKey » est ajouté à chaque claim.

-- Si des clés de tri de ce nom existent déjà, elles sont utilisées sans modification.



function wd.chronoSort( claims, inverted )

	for _, claim in ipairs( claims ) do

		if not claim.dateSortKey then

			claim.dateSortKey = wd.compare.get_claim_date(claim)

		end

	end

	table.sort( 

		claims,

		function ( c1, c2 )

			if inverted then

				return c2.dateSortKey < c1.dateSortKey

			end

			return c1.dateSortKey < c2.dateSortKey

		end

	)

	return claims

end



local function get_numeric_claim_value(claim, propertySort)

	local val

	local claimqualifs = claim.qualifiers

	if claimqualifs then

		local vals = claimqualifspropertySort

		if vals and vals1].snaktype == 'value' then

			val = vals1].datavalue.value

		end

	end

	return tonumber(val or 0)

end



function wd.compare.numeric(propertySort)

	return function(c1, c2)

		return get_numeric_claim_value(c1, propertySort) < get_numeric_claim_value(c2, propertySort)

	end

end



-- Fonction qui trie des Claims de type value selon l'ordre de la propriété fournit

-- Une clé de tri nomée « dateSortKey » est ajouté à chaque claim.

-- Si des clés de tri de ce nom existent déjà, elles sont utilisées sans modification.



function wd.numericPropertySort( claims, propertySort )

	for _, claim in ipairs( claims ) do

		if not claim.dateSortKey then

			local val = get_numeric_claim_value(claim, propertySort)

			claim.dateSortKey = tonumber(val or 0)

		end

	end

	table.sort(

		claims,

		function ( c1, c2 )

			return c1.dateSortKey < c2.dateSortKey

		end

	)

	return claims

end



--[[

test possible en console pour la fonction précédente : 

= p.formatStatements{entity = "Q375946", property = 'P50', sorttype = 'P1545', linkback = "true"}

--]]



-- ===================

function wd.getReferences(statement)

	local refdata = statement.references

	if not refdata then

		return nil

	end



	local refs = {}

	local hashes = {}

	for i, ref in pairs(refdata) do

		local s

		local function hasValue(prop) -- checks that the prop is here with valid value

			if ref.snaksprop and ref.snaksprop][1].snaktype == 'value' then

				return true

			end

			return false

		end		



		if ref.snaks.P248 then -- cas lorsque P248 (affirmé dans) est utilisé 

			for j, source in pairs(ref.snaks.P248) do

				if source.snaktype == 'value' then

					local page, accessdate, quotation

					if hasValue('P304') then -- page 

						page = wd.formatSnak(ref.snaks.P3041])

					end

					if hasValue('P813') then -- date de consultation

						accessdate = wd.formatSnak(ref.snaks.P8131])

					end

					if hasValue('P1683') then -- citation

						quotation = wd.formatSnak(ref.snaks.P16831])

					end

					local sourceId = wd.getId(source)

					s = modules.reference.citeitem(sourceId, {['page' = page, 'accessdate' = accessdate, 'citation' = quotation})

					table.insert(refs, s)

					table.insert(hashes, ref.hash .. sourceId)

				end

			end

	

		elseif hasValue('P8091') or hasValue('P854') then -- cas lorsque P8091 (Archival Resource Key) ou P854 (URL de la référence)est utilisé

			local arkKey, url, title, author, publisher, accessdate, publishdate, publishlang, quotation, description



			if hasValue('P8091') then

				arkKey = wd.formatSnak(ref.snaks.P80911], {text = "-"})

				url = 'https://n2t.net/' .. arkKey

				if hasValue('P1476') then

					title = wd.formatSnak(ref.snaks.P14761])

				else

					title = arkKey

				end

			elseif hasValue('P854') then

				url = wd.formatSnak(ref.snaks.P8541], {text = "-"})

				if hasValue('P1476') then

					title = wd.formatSnak(ref.snaks.P14761])

				else

					title = mw.ustring.gsub(url, '^[Hh][Tt][Tt][Pp]([Ss]?):(/?)([^/])', 'http%1://%3')

				end

			end



			--todo : handle multiple values for author, etc.

			if hasValue('P1810') then -- sous le nom

				description = 'sous le nom ' .. wd.formatSnak(ref.snaks.P18101])

			end

			if hasValue('P813') then -- date de consultation

				accessdate = wd.formatSnak(ref.snaks.P8131])

			end

			if hasValue('P50') then  -- author (item type)

				author = wd.formatSnak(ref.snaks.P501])

			elseif hasValue('P2093') then -- author (string type)

				author = wd.formatSnak(ref.snaks.P20931])

			end

			if hasValue('P123') then -- éditeur

				publisher = wd.formatSnak(ref.snaks.P1231])

			end

			if hasValue('P1683') then -- citation

				quotation = wd.formatSnak(ref.snaks.P16831])

			end

			if hasValue('P577') then -- date de publication

				publishdate = wd.formatSnak(ref.snaks.P5771])

			end 

			if hasValue('P407') then -- langue de l'œuvre

				local id = wd.getId(ref.snaks.P4071])

				publishlang = getLangCode(id)

			end

			s = modules.cite.lienWeb{titre = title, url = url, auteur = author, editeur = publisher, langue = publishlang, 'en ligne le' = publishdate, 'consulté le' = accessdate, 'citation' = quotation, 'description' = description}

			table.insert(hashes, ref.hash)

			table.insert(refs, s)



		elseif ref.snaks.P854 and ref.snaks.P8541].snaktype == 'value' then

			s = wd.formatSnak(ref.snaks.P8541], {text = "-"})

			table.insert(hashes, ref.snaks.P8541].hash)

			table.insert(refs, s)

		end

	end

	if #refs > 0 then

		if #hashes == #refs then

			return refs, hashes

		end

		return refs

	end

end



function wd.sourceStr(sources, hashes)

	if not sources or (#sources == 0) then

		return nil

	end

	local useHashes = hashes and #hashes == #sources

	for i, j in ipairs(sources) do

		local refArgs = {name = 'ref', content = j}

		if useHashes and hashesi ~= '-' then

			refArgs.args = {name = 'wikidata-' .. hashesi]}

		end

		sourcesi = mw.getCurrentFrame():extensionTag(refArgs)

	end

	return table.concat(sources, '<sup class="reference cite_virgule">,</sup>')

end



function wd.getDataValue(snak, params)

	if not params then

		params = {}

	end

	local speciallabels = params.speciallabels -- parfois on a besoin de faire une liste d'éléments pour lequel le libellé doit être changé, pas très pratique d'utiliser une fonction pour ça



	if snak.snaktype ~= 'value' then

		return nil

	end



	local datatype = snak.datatype

	local value = snak.datavalue.value

	

	local displayformat = params.displayformat

	if type(displayformat) == 'function' then

		return displayformat(snak, params)

	end



	if datatype == 'wikibase-item' then

		return wd.formatEntity(wd.getId(snak), params)

	end



	if datatype == 'url' then

		if params.displayformat == 'raw' then

			return value

		else

			return modules.weblink.makelink(value, params.text)

		end

	end



	if datatype == 'math' then

		return mw.getCurrentFrame():extensionTag( "math", value)

	end



	if datatype == 'tabular-data' then

		return mw.ustring.sub(value, 6, 100)  -- returns the name of the file, without the "Data:" prefix

	end



	if (datatype == 'string') or (datatype == 'external-id') or (datatype == 'commonsMedia') then -- toutes les données de type string sauf "math"

		if params.urlpattern then

			local urlpattern = params.urlpattern

			if type(urlpattern) == 'function' then

				urlpattern = urlpattern(value)

			end

			-- encodage de l'identifiant qui se retrouve dans le path de l'URL, à l'exception des slashes parfois rencontrés, qui sont des séparateurs à ne pas encoder

			local encodedValue = mw.uri.encode(value, 'PATH'):gsub('%%2F', '/')

			-- les parenthèses autour du encodedValue:gsub() sont nécessaires, sinon sa 2e valeur de retour est aussi passée en argument au mw.ustring.gsub() parent

			local url = mw.ustring.gsub(urlpattern, '$1', (encodedValue:gsub('%%', '%%%%')))

			value = '[' .. url .. ' ' .. (params.text or value) .. ']'

		end

		return value

	end

	

	if datatype == 'time' then -- format example: +00000001809-02-12T00:00:00Z

		if displayformat == 'raw' then

			return value.time

		else

			local dateobject = dateObject(value, {precision = params.precision})

			return objectToText(dateobject, params)

		end

	end



	if datatype == 'globe-coordinate' then

		-- retourne une table avec clés latitude, longitude, précision et globe à formater par un autre module (à changer ?)

		if displayformat == 'latitude' then

			return value.latitude

		elseif displayformat == 'longitude' then

			return value.longitude

		else

			local coordvalue = mw.clone( value )

			coordvalue.globe = modules.globesvalue.globe -- transforme l'ID du globe en nom anglais utilisable par geohack

			return coordvalue -- note : les coordonnées Wikidata peuvent être utilisée depuis Module:Coordinates. Faut-il aussi autoriser à appeler Module:Coordiantes ici ?

		end

	end



	if datatype == 'quantity' then -- todo : gérer les paramètres précision

		local amount, unit = value.amount, value.unit



		if unit then

			unit = unit:match('Q%d+')

		end

		

		if not unit then

			unit = 'dimensionless'

		end

		

		local raw	

		if displayformat == "raw" then

			raw = true

		end

		return modules.formatNum.displayvalue(amount, unit,

			{targetunit = params.targetunit, raw = raw, rounding = params.rounding, showunit = params.showunit or 'short', showlink = params.showlink}

		)

	end

	if datatype == 'monolingualtext' then

		if value.language == defaultlang then

			return value.text

		else

			return modules.langmodule.langue({value.language, value.text, nocat=true})

		end

	end	

	return formatError('unknown-datavalue-type' )



end



function wd.stringTable(args) -- like getClaims, but get a list of string rather than a list of snaks, for easier manipulation

	local claims = args.claims

	local cat = ''



	if not claims then

		claims = wd.getClaims(args)

	end

	if not claims or claims == {} then

		return {}, {}, cat

	end

	if args.removedupesdate and (args.removedupesdate ~= '-') then

		claims, cat = removeDupesDate(claims, args.removedupesdate)

	end

	local props = {} -- liste des propriétés associété à chaque string pour catégorisation et linkback

	for i, j in pairs(claims) do

		claimsi = wd.formatStatement(j, args)

		table.insert(props, j.mainsnak.property)

	end

	if args.removedupes and (args.removedupes ~= '-') then

		claims = wd.addNewValues({}, claims) -- devrait aussi supprimer de props celles qui ne sont pas utilisées

	end

	return claims, props, cat

end



function wd.getQualifiers(statement, qualifs, params)

	if not statement.qualifiers then

		return nil

	end

	local vals = {}

	if type(qualifs) == 'string' then

		qualifs = wd.splitStr(qualifs)

	end

	for i, j in pairs(qualifs) do

		if statement.qualifiersj then

			for k, l in pairs(statement.qualifiersj]) do

				table.insert(vals, l)

			end

		end

	end

	if #vals == 0 then

		return nil

	end

	return vals

end



function wd.getFormattedQualifiers(statement, qualifs, params)

	if not params then params = {} end

	local qualiftable = wd.getQualifiers(statement, qualifs)

	if not qualiftable then

		return nil

	end

	qualiftable = wd.filterClaims(qualiftable, params) or {}

	for i, j in pairs(qualiftable) do

		qualiftablei = wd.formatSnak(j, params)

	end

	return modules.linguistic.conj(qualiftable, params.conjtype)

end



function wd.showQualifiers(str, statement, args)

	local qualifs =  args.showqualifiers

	if not qualifs then

		return str -- or error ?

	end

	if type(qualifs) == 'string' then

			qualifs = wd.splitStr(qualifs)

	end

	local qualifargs = args.qualifargs or {}

	-- formatage des qualificatifs = args commençant par "qualif", ou à défaut, les mêmes que pour la valeur principale

	qualifargs.displayformat = args.qualifdisplayformat or args.displayformat

	qualifargs.labelformat = args.qualiflabelformat or args.labelformat

	qualifargs.labelformat2 = args.qualiflabelformat2 or args.labelformat2

	qualifargs.link = args.qualiflink or args.link

	qualifargs.linktopic = args.qualiflinktopic or args.linktopic

	qualifargs.conjtype = args.qualifconjtype

	qualifargs.precision = args.qualifprecision

	qualifargs.targetunit = args.qualiftargetunit

	qualifargs.defaultlink = args.qualifdefaultlink or args.defaultlink

	qualifargs.defaultlinkquery = args.qualifdefaultlinkquery or args.defaultlinkquery

	

	local formattedqualifs

	if args.qualifformat and type (args.qualifformat) == 'function' then

		formattedqualifs = args.qualifformat(statement, qualifs, qualifargs)

	else		

		formattedqualifs = wd.getFormattedQualifiers(statement, qualifs, qualifargs)

	end

	if formattedqualifs and formattedqualifs ~= "" then

		str = str .. " (" .. formattedqualifs .. ")"

	end

	return str

end





function wd.formatSnak( snak, params )

	if not params then params = {} end -- pour faciliter l'appel depuis d'autres modules

	if snak.snaktype == 'somevalue' then

		return unknownValue(snak, params.unknownlabel)

	elseif snak.snaktype == 'novalue' then

		return noValue(params.novaluelabel)

	elseif snak.snaktype == 'value' then

		return wd.getDataValue( snak, params)

	else

		return formatError( 'unknown-snak-type' )

	end

end



function wd.formatStatement( statement, args ) -- FONCTION A REORGANISER (pas très lisible)

	if not args then

		args = {}

	end

	if not statement.type or statement.type ~= 'statement' then

		return formatError( 'unknown-claim-type' )

	end

	local prop = statement.mainsnak.property



	local str



	-- special displayformat f

	if args.statementformat and (type(args.statementformat) == 'function') then

		str = args.statementformat(statement, args)

	elseif (statement.mainsnak.datatype == 'time') and (statement.mainsnak.dateformat ~= '-') then

		if args.displayformat == 'raw' and statement.mainsnak.snaktype == 'value' then

			str = statement.mainsnak.datavalue.value.time

		else

			str = wd.getFormattedDate(statement, args)

		end

	elseif args.showonlyqualifier and (args.showonlyqualifier ~= '') then

		str = wd.getFormattedQualifiers(statement, args.showonlyqualifier, args)

		if not str then

			return nil

		end

		if args.addstandardqualifs ~= '-' then

			str = wd.addStandardQualifs(str, statement)

		end

	else

		str = wd.formatSnak( statement.mainsnak, args )

		if (args.addstandardqualifs ~= '-') and (args.displayformat ~= 'raw') then

			str = wd.addStandardQualifs(str, statement)

		end

	end



	-- ajouts divers

	if args.showlang == true then

		local indicateur = showLang(statement, args.maxLang)

		if indicateur then

			str = indicateur .. '&nbsp;' .. str	

		end

	end

	if args.showqualifiers then

		str = wd.showQualifiers(str, statement, args)

	end



	if args.showdate then -- when "showdate and chronosort are both set, date retrieval is performed twice

		local period = wd.getFormattedDate(statement, args, "-") -- 3 arguments indicate the we should not use additional qualifiers, alrady added by wd.formatStatement

		if period then

			str = str .. " <small>(" .. period .. ")</small>"

		end

	end



	if args.showsource and args.showsource ~= '-' then

		local sources, hashes = wd.getReferences(statement)

		if sources then

			local source = wd.sourceStr(sources, hashes)

			if source then

				str = str .. source

			end

		end

	end



	return str

end



function wd.addLinkBack(str, id, property)

	if not id or id == '' then

		id = wd.getEntityIdForCurrentPage()

	end

	if not id then

		return str

	end

	if type(property) == 'table' then

		property = property1

	end

	

	id = wd.entityId(id)



	local class = ''

	if property then

		class = 'wd_' .. string.lower(property)

	end

	local icon = '[[File:Blue pencil.svg|%s|10px|baseline|class=noviewer|link=%s]]'

	local title = wd.translate('see-wikidata-value')

	local url = mw.uri.fullUrl('d:' .. id, 'uselang=fr')

	url.fragment = property -- ajoute une #ancre si paramètre "property" défini

	url = tostring(url)

	local v = mw.html.create('span')

		:addClass(class)

		:wikitext(str)

		:tag('span')

			:addClass('noprint wikidata-linkback')

			:wikitext(icon:format(title, url))

		:allDone()

	return tostring(v)

end



function wd.addRefAnchor(str, id)

--[[

	Insère une ancre pour une référence générée à partir d'un élément wd.

	L'id Wikidata sert d'identifiant à l'ancre, à utiliser dans les modèles type "harvsp"

--]]

	return tostring(

		mw.html.create('span')

			:attr('id', id)

			:attr('class', "ouvrage")

			:wikitext(str)

	)

end



--=== FUNCTIONS USING AN ENTITY AS ARGUMENT ===

local function formatStatementsGrouped(args, type)

	-- regroupe les affirmations ayant la même valeur en mainsnak, mais des qualificatifs différents

	-- (seulement pour les propriétés de type élément)



	local claims = wd.getClaims(args)

	if not claims then

		return nil

	end

	local groupedClaims = {}



	-- regroupe les affirmations par valeur de mainsnak

	local function addClaim(claim)

		local id = wd.getMainId(claim)

		for i, j in pairs(groupedClaims) do 

			if (j.id == id) then

				table.insert(groupedClaimsi].claims, claim)

				return

			end

		end

		table.insert(groupedClaims, {id = id, claims = {claim}})

	end

	for i, claim in pairs(claims) do	

		addClaim(claim)

	end



	local stringTable = {}



	-- instructions ad hoc pour les paramètres concernant la mise en forme d'une déclaration individuelle

	local funs = {

		{param = "showqualifiers", fun = function(str, claims)

			local qualifs = {}

			for i, claim in pairs(claims) do

				local news = wd.getFormattedQualifiers(claim, args.showqualifiers, args)

				if news then

					table.insert(qualifs, news)

				end

			end

			local qualifstr = modules.linguistic.conj(qualifs, wd.translate("qualif-separator"))

			if qualifstr and qualifstr ~= "" then

				str = str .. " (" .. qualifstr .. ")"

			end

			return str

			end

		},

		{param = "showdate", fun = function(str, claims)

			-- toutes les dates sont regroupées à l'intérieur des mêmes parenthèses ex "médaille d'or (1922, 1924)"

			local dates = {}

			for i, statement in pairs(claims) do

				local s = wd.getFormattedDate(statement, args, true)

				if statement then table.insert(dates, s) end

			end

			local datestr = modules.linguistic.conj(dates)

			if datestr and datestr ~= "" then

				str = str .. " <small>(" .. datestr .. ")</small>"

			end

			return str

			end

		},

		{param = "showsource", fun = function(str, claims)

			-- les sources sont toutes affichées au même endroit, à la fin

			-- si deux affirmations ont la même source, on ne l'affiche qu'une fois

			local sources = {}

			local hashes = {}

		

			local function dupeRef(old, new)

				for i, j in pairs(old) do

					if j == new then

						return true

					end

				end

			end

			for i, claim in pairs(claims) do

				local refs, refHashes = wd.getReferences(claim)

				if refs then

					for i, j in pairs(refs) do

						if not dupeRef(sources, j) then

							table.insert(sources, j)

							local hash = (refHashes and refHashesi]) or '-'

							table.insert(hashes, hash)

						end

					end

				end

			end

			return str .. (wd.sourceStr(sources, hashes) or "")

			end

		}

	}



	for i, group in pairs(groupedClaims) do -- bricolage pour utiliser les arguments de formatStatements

		local str = wd.formatEntity(group.id, args)

		if not str then

			str = '???' -- pour éviter erreur Lua si formatEntity a retourné nil

		end

		for i, fun in pairs(funs) do

			if argsfun.param then

				str = fun.fun(str, group.claims, args)

			end

		end

		table.insert(stringTable, str)

	end

				

	args.valuetable = stringTable

	return wd.formatStatements(args)

end





function wd.formatStatements( args )--Format statement and concat them cleanly



	if args.value == '-' then

		return nil

	end



	-- If a value is already set: use it, except if it's the special value {{WD}} (use wikidata)

	if args.value and args.value ~= '' then

		local valueexpl = wd.translate("activate-query")

		if args.value ~= valueexpl then

			return args.value

		end

	-- There is no value set, and args.expl disables wikidata on empty values

	elseif args.expl then

		return nil

	end



	if args.grouped and args.grouped ~= '' then

		args.grouped = false

		return formatStatementsGrouped(args)

	end

	local valuetable = args.valuetable -- dans le cas où les valeurs sont déjà formtées

	local props -- les prorpriétés réellement utilisées (dans certainse cas, ce ne sont pas toutes celles de ags.property

	local cat = ''

	if not valuetable then -- cas le plus courant

		valuetable, props, cat = wd.stringTable(args)

	end



	local str = modules.linguistic.conj(valuetable, args.conjtype)

	if not str then

		return args.default

	end

	if not props then

		props = wd.splitStr(args.property)[1

	end

	if args.ucfirst ~= '-' then

		str = modules.linguistic.ucfirst(str)

	end



	if args.addcat and (args.addcat ~= '-') then

		str = str .. wd.addTrackingCat(props) .. cat

	end

	if args.linkback and (args.linkback ~= '-') then

		str = wd.addLinkBack(str, args.entity, props)

	end

	if args.returnnumberofvalues then

		return str, #valuetable

	end

	return str

end



function wd.formatAndCat(args)

	if not args then

		return nil

	end

	args.linkback = args.linkback or true

	args.addcat = true

	if args.value then -- do not ignore linkback and addcat, as formatStatements do

		if args.value == '-' then

			return nil

		end

		local val = args.value .. wd.addTrackingCat(args.property)

		val = wd.addLinkBack(val, args.entity, args.property)

		return val

	end 

	return wd.formatStatements( args )

end



function wd.getTheDate(args)

	local claims = wd.getClaims(args)

	if not claims then

		return nil

	end

	local formattedvalues = {}

	for i, j in pairs(claims) do

		local v = wd.getFormattedDate(j, args)

		if v then

			table.insert(formattedvalues, v )

		end

	end

	local val = modules.linguistic.conj(formattedvalues)

	if not val then

		return nil

	end

	if args.addcat == true then

		val = val .. wd.addTrackingCat(args.property)

	end

	val = wd.addLinkBack(val, args.entity, args.property)

	return val

end





function wd.keyDate (event, item, params)

	params = params or {}

	params.entity = item

	if type(event) == 'table' then

		for i, j in pairs(event) do

			params.targetvalue = nil -- réinitialisation barbare des paramètres modifiés

			local s = wd.keyDate(j, item, params)

			if s then

				return s

			end

		end

	elseif type(event) ~= 'string' then

		 return formatError('invalid-datatype', type(event), 'string')

	elseif string.sub(event, 1, 1) == 'Q' then -- on demande un élément utilisé dans P:P793 (événement clé)

		params.property = 'P793'

		params.targetvalue = event

		params.addcat = params.addcat or true

		return wd.getTheDate(params)

	elseif string.sub(event, 1, 1) == 'P'  then -- on demande une propriété

		params.property = event

		return wd.formatAndCat(params)

	else

		return formatError('invalid-entity-id', event)

	end

end



function wd.mainDate(entity)	

	-- essaye P580/P582

	local args = {entity = entity, addcat = true}

	

	args.property = 'P580'

	local startpoint = wd.formatStatements(args)

	args.property = 'P582'

	local endpoint = wd.formatStatements(args)



	local str

	if (startpoint or endpoint) then

		str = modules.formatDate.daterange(startpoint, endpoint, params)

		str = wd.addLinkBack(str, entity, 'P582')

		return str

	end



	-- défaut : P585

	args.property = {'P585', 'P571'}

	args.linkback = true

	return wd.formatStatements(args)

end



-- === FUNCTIONS FOR TRANSITIVE PROPERTIES ===



function wd.getIds(item, query)

	query.excludespecial = true

	query.displayformat = 'raw'

	query.entity = item

	query.addstandardqualifs = '-'

	return wd.stringTable(query)

end





-- recursively adds a list of qid to an existing list, based on the results of a query

function wd.addVals(list, query, maxdepth, maxnodes, stopval)

	maxdepth = tonumber(maxdepth) or 10

	maxnodes = tonumber(maxnodes) or 100

	if (maxdepth < 0) then

		return list

	end

	if stopval and wd.isHere(list, stopval) then

		return list

	end

	local origsize = #list

	for i = 1, origsize do

		-- tried a  "checkpos" param instead of starting to 1 each time, but no impact on performance

		local candidates = wd.getIds(listi], query)

		list = wd.addNewValues(list, candidates, maxnodes, stopval)

		if list#list == stopval then

			return list

		end

		if #list >= maxnodes then

			return list

		end

	end

	

	if (#list == origsize) then

		return list

	end

	return wd.addVals(list, query, maxdepth - 1, maxnodes, stopval, origsize + 1)

end



-- returns a list of items transitively matching a query (orig item is not included in the list)



function wd.transitiveVals(item, query, maxdepth, maxnodes, stopval)

	maxdepth = tonumber(maxdepth) or 5

	if type(query) == "string" then

		query = {property = query}

	end



	-- récupération des valeurs

	local vals = wd.getIds(item, query)

	if not vals then

		return nil

	end

	local v = wd.addVals(vals, query, maxdepth - 1, maxnodes, stopval) 

	if not v then

		return nil

	end



	-- réarrangement des valeurs

	if query.valorder == "inverted" then

		local a = {}

		for i = #v, 1, -1 do

			a#a+1 = vi

		end

		v = a

	end



	return v

end



-- returns true if an item is the value of a query, transitively

function wd.inTransitiveVals(searchedval, sourceval, query, maxdepth, maxnodes )

	local vals = wd.transitiveVals(sourceval, query, maxdepth, maxnodes, searchedval )

	if (not vals) then 

		return false

	end

	for _, val in ipairs(vals) do

		if (val == searchedval) then

			return true

		end

	end

	return false

end



-- returns true if an item is a superclass of another, based on P279

function wd.isSubclass(class, item, maxdepth)

	local query = {property = 'P279'}

	if class == item then -- item is a subclass of itself iff it is a class

		if wd.getIds(item, query) then

			return true

		end

		return false

	end

	return wd.inTransitiveVals(class, item, query, maxdepth )

end



-- returns true if one of the best ranked P31 values of an item is the target or a subclass of the target

-- rank = 'valid' would seem to make sense, but it would need to check for date qualifiers as some P31 values have begin or end date

function wd.isInstance(targetclass, item, maxdepth)

	maxdepth = maxdepth or 10

	local directclasses = wd.transitiveVals(item, {property = 'P31'}, 1)

	if not directclasses then

		return false

	end 

	for i, class in pairs(directclasses) do

		if wd.isSubclass(targetclass, class, maxdepth - 1) then

			return true

		end

	end

	return false

end



-- return the first value in a transitive query that belongs to a particular class. For instance find a value of P131 that is a province of Canada

function wd.findVal(sourceitem, targetclass, query, recursion, instancedepth)

	if type(query) == "string" then

		query = {property = query}

	end

	local candidates = wd.getIds(sourceitem, query)

	if candidates then

		for i, j in pairs(candidates) do

			if wd.isInstance(targetclass, j,  instancedepth) then

				return j

			end

		end

		if not recursion then

			recursion = 3

		else

			recursion = recursion - 1

		end

		if recursion < 0 then

			return nil

		end

		for i, candidate in pairs(candidates) do

			return wd.findVal(candidate, targetclass, query, recursion, instancedepth)

		end

	end

end





-- === VARIA ===

function wd.getDescription(entity, lang)

	lang = lang or defaultlang



	local description

	if lang == defaultlang then

		return  mw.wikibase.description(qid)

	end

	if not entity.descriptions then

		return wd.translate('no description')

	end

	local descriptions = entity.descriptions

	if not descriptions then

		return nil

	end

	if descriptionslang then

		return descriptionsdelang].value

	end

	return entity.id

end



function wd.Dump(entity)

	entity = wd.getEntity(entity)

	if not entity then

		return formatError("entity-param-not-provided")

	end

	return "<pre>"..mw.dumpObject(entity).."</pre>"

end



function wd.frameFun(frame)

	local args = frame.args

	local funname = args1

	table.remove(args, 1)

	return wdfunname](args)

end





return wd

Videos

Youtube | Vimeo | Bing

Websites

Google | Yahoo | Bing

Encyclopedia

Google | Yahoo | Bing

Facebook