Modul:Databox

Från Wikipedia
Hoppa till navigering Hoppa till sök

Dokumentationen för denna modul kan skapas på Modul:Databox/dok

-- Versions: (Besides minor adjustments to the property_blacklist and layout)
-- 2020-05-25 Hide "is instance of" -> "human"
-- 2020-05-25 Category:Databox that shows qid code
-- 2020-05-24 Years linked to articles. Decades, centuries and millennia formated in local language.
-- 2020-05-13 P155 (follows) and P156 (followed by) merged into one "Chronology" list, also showing current object.
--            Parameter "list_separator" for replacing comma in lists.  
-- 2020-05-10 Parameter "era" for choosing if year "BCE" (or similar in local language) should be replaced by "BC", empty string or other.
--            Upper-case initial letter of P31 (instance of).
-- 2020-05-05 P31 hidden if too long list. Administrative wiki category if too long list.
-- 2020-05-02 More than two parent/child levels in bulleted list.
-- 2020-05-01 Property short names based on P1813. Image legend/caption. 
-- 2020-04-28 Properties linked to articles (based on Property:P1629 of the property). 
--            Datatype "url" not shown (except for official web site). Input parameter "levels". 
-- 2020-04-27 Two higher and two lower levels of child and parent items shown as bulleted list for some properties
-- 2020-04-21 Parameters "width", "height", "zoom" and "list_length". Soft hyphens auto-inserted in long property names.
-- 2020-04-18 Monolingual text strings hidden. Property name hidden if no good value. First image shown if several images. 
-- 2020-04-16 Description shown (only in local language). First letter of label upper-case.
-- 2020-04-13 Values of datatype "quantity" shown. Pen hidden in printout.
-- 2020-04-13 Mapframe code copied from the 2019-03-04 af.wikipedia.org version.
-- 2020-04-07 Imported from the 2019-04-26 fr.wikipedia.org version 

-- Blocked properties:
local property_blacklist = {
    'P360', --is a list of
    'P4224', --category contains
    'P935', -- Commons gallery
    'P1472', -- Commons Creator page
    'P1612', -- Commons Institution page
    'P373', -- Commons category
    'P3722', -- Commons maps category
    'P7561', --  for the interior of the item
    'P1151', -- topic's main Wikimedia portal
    'P1424', -- topic's main template
    'P910', -- topic's main category
    'P1200', -- bodies of water basin category
    'P1792', -- category of associated people
    'P1464', -- category for people born here
    'P1465', -- category for people who died here
    'P1791', -- category of people buried here
    'P1740', -- category for films shot at this location
    'P2033', -- Category for pictures taken with camera
    'P2517', -- category for recipients of this award
    'P4195', -- category for employees of the organization
    'P1754', -- category related to list
    'P301', -- category's main topic
    'P971', -- category combines topics
    'P3876', -- category for alumni of educational institution
    'P1753', -- list related to category
    'P7867', -- category for maps
    'P1921', -- Wikidata RDF URI format
    'P3921', -- Wikidata SPARQL query equivalent
    'P1204', -- Wikimedia portal's main topic
    'P1423', -- template's main topic
    'P1709', -- equivalent class
    'P3950', -- narrower external class
    'P2888', -- exact match
    'P1382', -- coincident with
    'P2670', -- has parts of the class
    'P3113', -- does not have part
    'P2737', -- union of
    'P2738', -- disjoint union of
    'P2445', -- metasubclass of
    'P1963', -- properties for this type
    'P3176', -- uses property
    'P1889', -- different from
    'P460', -- said to be the same as
    'P2959', -- permanent duplicated item
    'P2860', -- cites
    'P5125', -- wikimedia outline
    'P5008', -- on focus list of Wikimedia project
    'P7084', -- related category
    'P1687', -- Wikidata main property for this item 
    'P2559', -- Wikidata usage instructions
    'P5692', -- Wikidata dummy value
    'P1343', -- described by source
    'P972',  -- catalogue
    'P1282', -- OSM tag or key
    'P553',  -- web site account
    'P968',  -- email
    'P2572', -- hashtag
    'P3761', -- IPv4 range
    'P4839', -- Wolfram Language entity code
    'P6104', -- Maintained by Wikiproject
    'P5996', -- Category for films in this language
    'P2354', -- list article (seldom available in local language)
    'P6365', -- member category
    'P528', -- catalog code
    'P667', -- ICPC 2 ID
    'P944', -- Code of nomenclature
    'P1438', -- Jewish Encyclopedia ID (Russian)e
    'P1402', -- Foundational Model of Anatomy ID
    'P1461', -- Patientplus ID
    'P1692', -- ICD-9-CM code
    'P1748', -- NCI Thesaurus ID
    'P1193', -- prevalence (often different value in different countries)
    'P2176', -- drug used for treatment (we avoid medical advise)
    'P2293', -- genetic association
    'P1814', -- Japanese name in kana
    'P747', -- editions
    'P1433', -- published in
    'P4969', -- derivative work
    'P217', -- inventory number
    'P2540', -- Aarne–Thompson–Uther Tale Type Index
    'P1036', -- DDC
    'P1149', -- LCC
    'P1150', -- RVK
    'P1190', -- UDC
    'P1987', -- MCN code
    'P2263', -- ISOCat id
    'P2283', -- Uses 
    'P443',  -- Pronounciation. Media file. (Should be shown if in local language.)
    'P2184', -- History of subject. (Should be shown if article in local language)
    'P989',  -- spoken text. (Should be shown if in local language)
    'P1793', -- Format som regex
    'P4354', -- Sökurlformat
    'P5869', -- model item
    'P859',  -- sponsor
    'P7973',  -- quantity symbol (LaTeX)
    'P6216',  -- copyright status
    'P1830'  -- owner of (seldom useful)
}

-- Exceptions to the datatype blocking:
local property_whitelist = { 
	'P856', -- official website
	'P3896',-- geoshape
	'P345'  -- IMDB id
}

-- Properties with higher level items:
local properties_with_parents = { 
	'P131', -- located in the administrative territorial entity
	'P144', -- based on
	'P155', -- follows
	'P171', -- parent taxon
	'P276', -- location
	'P279', -- subclass of
	'P361', -- part of
	'P706', -- located on terrain feature
	'P749', -- parent organization
	'P1647' -- subproperty of
}

local properties_with_children = { 
-- Properties with lower level items:
	'P150', -- contains administrative territorial entity
	'P156', -- followed by
	'P355', -- subsidiary
	'P527', -- has part 
	'P1012', -- contains
	'P4330' -- contains
}

local function buildInteractiveMap(width, point, item_id, zoom)
--Utility function to build maps
    local geojson = {
        {
            type = 'Feature',
            geometry = {
                type = "Point",
                coordinates = { point.longitude, point.latitude }
            },
            properties = {
                title = point.text or '',
                ['marker-symbol'] = point.marker or 'marker',
                ['marker-color'] =  point.markercolor or "#224422",
            }
        }
    }
    local args = {
        ['height'] = width,
        ['width'] = width,
        ['frameless'] = 'frameless',
        ['align'] = 'center',
        ['latitude'] = point.latitude,
        ['longitude'] = point.longitude,
        ['zoom'] = zoom,
        ['lang'] = 'en' -- (Should first show wiki language, but currently only shows English, and local language of map if English name is missing. )
	}
    return mw.getCurrentFrame():extensionTag('mapframe', mw.text.jsonEncode(geojson), args)
end

function Set(list) -- values to booleans with keys
	local set = {}
	for _, l in pairs(list) do 
		set[l] = true
	end
	return set
end

function  listCase(str)
	-- Capitalizes first visible character of list produced by formatStatements() 
	-- Example: <span><span>[[link|first item]]</span>, <span>second item</span></span>
	--     -->  <span><span>[[link|First item]]</span>, <span>second item</span></span>
	return str
		:gsub('^<span><span>%[%[(.-)|(.-)%]%]</span>',
			function(a,b) 
				return '<span><span>[[' .. a .. '|' 
				.. b:gsub('^%l', string.upper) 
				.. ']]</span>' 
			end)
		:gsub('^<span><span>(%l)', 
			function(a) 
				return '<span><span>' ..string.upper(a) 
			end)
end

function listSeparate(str, list_separator)
	-- Replaces comma in list produced by formatStatements() 
	-- Example: list_separator = ';<br>'  
	--   str  = <span><span>[[link|first item]]</span>, <span>second item</span></span>
    --     ->   <span><span>[[link|first item]]</span>;<br> <span>second item</span></span>
	if list_separator ~= ',' then
		return str
			:gsub('</span>,', '</span>'..list_separator)
	else 
		return str
	end
end

function hyphenate(str, lang)
	-- Inserts soft hyphens in long words, typically before each consonant that is followed by a vowel (lower-case letters)
	-- Should work good enough for most languages. Language specific exceptions may be added.
	local nonHyphenatedLanguages = Set{ 'ar', 'he', 'zh', 'ja', 'ko', 'vi', 'fa', 'ps' }
	if nonHyphenatedLanguages[lang] then -- Not languages without alphabetic writing system or with few vowels 
		return str
	end
	result = ''
	for word in str:gmatch("%S+") do 
		if #word < 10 then
			result = result .. ' ' .. word
		else
			result = result .. ' ' 
			.. word:sub(1,3) -- Not too early in word
			.. word:sub(4)
			:gsub("([bcdfghjklmnpqrstvwxzđçčĉñŋĝĥĵŝšŧžßÐðþğşśćńŁżźбвгжийклмнпрст]"
				.. "[aouåeiyäöæøáéíóúýàèâêëüãŭœāēīōūəąęóадеёзоу])",
				"&shy;%1") -- Insert soft-hyphens before each consonant that is followed by vowel
			:gsub("&shy;([\128-\193])", "%1" ) -- Revert split of two-byte UTF-8 character
			:gsub("-(%a?%a?%a?%a?)&shy;", "-%1"):gsub("&shy;(%a?%a?%a?%a?)-", "%1-") -- Not too near a hard hyphen
			
			-- Some Swedish exceptions for wikidata properties, probably relevant to similar languages:
			:gsub("&shy;x", "x&shy;") -- Example: tids-komp-lex-i-tet   
			:gsub("sc&shy;h", "&shy;sch")
			:gsub("ss&shy;j", "s&shy;sj")
			:gsub("n&shy;g", "ng&shy;")
			:gsub("&shy;sche[&shy;]*f", "s&shy;chef") -- Example: Rege-ring-sche-fens -> Rege-rings-chefens
			:gsub("nist&shy;ra", "nis&shy;tra") -- Example: admi-nist-ra-tion -> admi-nis-tra-tion
			:gsub("&shy;ror&shy;ga[&shy;]*n", "r&shy;organ") -- Example: dotte-ror-ga-ni-sa-tion -> dotter-organi-sa-tion
			:gsub("&shy;rob[&shy;]*jek", "r&shy;objek") -- Example: dot-te-rob-jekt -> dot-ter-objekt
			:gsub("s&shy;ta&shy;tus", "&shy;status") -- Example: skydds-status
			:gsub("k&shy;las[&shy;]*s", "&shy;klass") -- Example: deci-malk-las-si-fi-ka-tion -> deci-mal-klas-si-fi-ka-tion
			:gsub("&shy;nom&shy;rå&shy;de", "n&shy;område") -- Example: vatten-område
			:gsub("ra&shy;lort", "ral&shy;ort") -- Example: central-ort
			:gsub("s&shy;kydd", "&shy;skydd") -- Example: Kul-turs-kydd -> Kul-tur-skydd
			:gsub("k&shy;ri&shy;te&shy;ri", "&shy;kri&shy;te&shy;ri")-- Example: Värld-sarvsk-ri-te-rium -> Värld-sarvs-kri-te-rium
			:gsub("guasp&shy;he", "gua&shy;sphe") -- Example: lingua-sphere
			:gsub("gars&shy;kap", "gar&shy;skap") -- Example: medbor-gars-kap -> medbor-gar-skap
		end
	end
	return result:sub(2,-1)
	-- :gsub("&shy;", "-") -- Show soft hyphens as hard hyphens. Only for sandboxed test purposes.
end

function year(str, lang, replaceTime)
	-- Postprocesses years (datatype time) in local language

	-- Incorrect formating of the first decade.
	if lang == 'sv'	then
		str = str
			:gsub("<span>0</span>", "<span>00-talet</span>")
			:gsub("<span>0 BCE</span>", "<span>00-talet f.v.t.</span>")
	end
	-- Replace BCE with BC (or corresponding in local language) depending on era template parameter:
	if replaceTime then
		for p,r in pairs(replaceTime) do
			str = str:gsub(p, r)
		end
	end
	
	-- Link years to articles:
	str = str
		:gsub("([%s>])(%d?%d?%d?%d)</span>", "%1[[%2]]</span>") -- April 1852 -> April [[1852]]
		:gsub("([%s>])(%d?%d?%d?%d) ([%a%.]*)</span>", "%1[[%2 %3]]</span>") -- April 20 BCE -> April [[20 BCE]]

	-- Format decades, centuries and millennias correctly in local language, and link to articles:  
	if lang=='sv' then
		str = str
        	:gsub("(%d?1%d)%.? år([h|t])(%a+)det?", "%1:e år%2%3det") -- 12. årtusende -> 12:e årtusendet
			:gsub("(%d?%d?[1-2])%.? år([h|t])(%a+)det?", "%1:a år%2%3det") -- 2. århundrade  -> 2:a århundradet
        	:gsub("(%d?%d?[0,3-9])%.? år([h|t])(%a+)det?", "%1:e år%2%3det") -- 13 århundrandet f.Kr. -> 13:e århundradet f.Kr 
			:gsub("<span>(%d?%d?%d?)00-talet</span>", "<span>[[%100-talet (decennium)]]</span>") -- 1900-talet -> [[1900-talet (årtionde)]]
			:gsub("<span>(%d?%d?%d?)00-talet ([%a%.]*)</span>", "<span>[[%100-talet	(decennium) %2]]</span>") -- 100-talet f.Kr. -> [[100-talet f.Kr. (decennium)]]
			:gsub("(%d?%d?%d):[a|e] århundradet", 
				function(a) 
					return tonumber(a)-1 .. '00-talet' 
				end) -- 21:a århundradet -> 2000-talet
			:gsub("<span>(%d+)-talet</span>", "<span>[[%1-talet]]</span>") -- 2000-talet -> [[2000-talet]]
			:gsub("<span>(%d+)-talet ([%a%.]*)</span>", "<span>[[%1-talet %2]]</span>") -- 000-talet f.Kr. -> [[000-talet f.Kr.]]	
			:gsub("(%d?%d?%d):[a|e] årtusendet", 
				function(a) 
					return tonumber(a)-1 .. '000-talet' 
				end) -- 2:a århundradet -> 2000-talet
			:gsub("<span>(%d+)-talet</span>", "<span>[[%1-talet (millennium)]]</span>") -- 2000-talet -> [[2000-talet (millennium)]]
			:gsub("<span>(%d+)-talet ([%a%.]*)</span>", "<span>[[%1-talet %2 (millennium)]]</span>") -- 0000-talet f.Kr. -> [[0000-talet f.Kr. (millennium)]]	
	end	
	return str
end

local p = {}

function p.databox(frame)
    local args = frame:getParent().args
    local itemId = nil
    if args.item then
        itemId = args.item
    end
    local item = mw.wikibase.getEntity(itemId) 
    if item == nil then
        mw.addWarning("Wikidata item not found")
        return ""
    end
    
    local width = '260' -- default max width of template, image and map, and height of map 
    if args.width then
        width = args.width
    end
    local height = '240' -- default max height of image. hidden if <= 0.
    if args.height then
        height = args.height
    end
    local zoom = 12 -- default map zoom level. hidden if <0.
    if args.zoom then
        zoom = tonumber(args.zoom)
    end
    local list_length = 8 -- default max no of values in lists
    if args.list_length then
    	list_length = tonumber(args.list_length)
	end
	local list_separator = ',' -- default no replacement of comma in lists
    if args.list_separator then
        list_separator = args.list_separator
    end
	local levels = 3 -- default max no of child and parent levels
    if args.levels then
    	levels = tonumber(args.levels)
	end

    local langObject = mw.language.getContentLanguage()
    local lang = langObject:getCode()
	local edit_message = mw.message.new('vector-view-edit'):plain()
	
	-- Date formating
	local bceDict = {} -- Dictionary: Before current era (BCE) in different languages
	bceDict['en'] = 'BCE'
	bceDict['sv'] = 'f.v.t.'
	local bcDict = {} -- Dictionary: Before Christ (BC) in different languages
	bcDict['en'] = 'BC'	
	bcDict['sv'] = 'f.Kr.'
	local bc = bcDict[lang] or 'BC'
	local bce = bceDict[lang] or 'BCE'
	local era = bc -- default era 
    if args.era then
    	if args.era == 'BC' then
    		era = bc -- replace 'BCE' by 'BC' in content language
    	elseif args.era == 'BCE' then
    		era = bce -- replace 'BC' by 'BCE' in content language
    	else
    		era = args.era -- replace 'BCE' and 'BC' by arbitrary argument value, for example empty string
    	end
    end
    local replaceTime = {} -- global variable
	replaceTime[' BCE'] = ' '..era
	-- replaceTime[' BC'] = ' '..era -- (If this line is included, we should check that BC is not followed by E)
	replaceTime[' '..bce] = ' '..era
	replaceTime[' '..bc] = ' '..era

	local noValueDict = {} -- Dictionary: No value
	noValueDict['en'] = 'no value'
	noValueDict['sv'] = 'inget värde'
	
	local wikicategory = ''
    
    local databoxRoot = mw.html.create('div')
    	:addClass('infobox')
        :css({
            float = 'right',
            clear = 'right',
            border = '1px solid #aaa',
            ['background-color'] = '#f9f9f9',
            ['width'] = width .. 'px',
            padding = '0 0.4em',
            margin = '0 0 0.4em 0.4em',
        })

    --Title
    databoxRoot:tag('div')
        :css({
            ['text-align'] = 'center',
            ['background-color'] = 'LightGrey',
            padding = '0em 0.4',
            margin = '0em 0',
            ['font-size'] = '120%',
            ['font-weight'] = 'bold',
        })
		:wikitext( langObject:ucfirst(item:getLabel()) or langObject:ucfirst(mw.title.getCurrentTitle().text ))
		mw.log(' === Label: ' .. item:getLabel() .. '===')

    --Description
    local descr, descrLang = item:getDescriptionWithLang()
    if descrLang == lang then -- Do not show any fallback language
    	databoxRoot:tag('div')
        :css({
            ['text-align'] = 'center',
            ['vertical-align'] = 'text-top',
            ['font-size'] = '90%',
            ['line-height'] = '140%',
            padding = '0.2em 0.4',
            margin = '0.0em 0.4',
            ['padding-bottom'] = '0.5em',
        })
        :wikitext(langObject:ucfirst(descr):sub(1,-1))
        :wikitext('<sup class="noprint Inline-Template">&nbsp;[[File:Blue_pencil.svg|' 
        	.. edit_message .. '|8px|baseline|class=noviewer|link=https://www.wikidata.org/wiki/' 
        	.. item.id .. ']]</sup>')
    end
    
    --Show first good image with legend/caption in content language, or first good image
    if tonumber(height) > 0 then
	    local images = item:getBestStatements('P18') -- p18 is 'image'
	    if #images >= 1 then
	    	local image = images[1]
	    	for _, i in pairs(images) do
 				if i.qualifiers and i.qualifiers.P2096 then -- P2096 is 'caption'
 					for _, c in pairs(i.qualifiers.P2096) do 
 						if c.snaktype == 'value' and c.datavalue.value.language == lang then
        					caption = c
				 			image = i
							break
 						end
 					end 
 				end
 				if caption then
 					break
 				end
 			end 
 			databoxRoot
	   		:tag('div')
	   		:css({
	    		['text-align'] = 'center',
	    		padding = '0.0em 0.4',
	    	})
	    	:wikitext('[[File:' .. image.mainsnak.datavalue.value .. '|frameless|' 
	       		.. width .. 'x' .. height .. 'px]]')	    	
	       	if caption then
	 			databoxRoot
		   		:tag('div')
	   			:css({
	    			['text-align'] = 'center',
	    			['font-size'] = '90%',
	            	['line-height'] = '140%',
	            	['padding-bottom'] = '0.5em',
	    	})
				:wikitext(caption.datavalue.value.text)
			end
		end
	end
	
    --Table:
    local dataTable = databoxRoot
        :tag('table')
        :css({
            ['text-align'] = 'left',
            ['font-size'] = '90%',
            ['line-height'] = '140%',
            ['hyphens'] = 'auto', -- works only in some browsers and languages
            ['word-break'] = 'break-word',
            ['width'] = '100%',
            ['table-layout'] = 'fixed',
            ['padding-bottom'] = '0.5em',
        })
    
    --Instance of:
    local dataValues 
    local statements = item:getBestStatements('P31')
    if #statements > list_length then -- Hide too long list
		if lang == 'sv' then
			wikicategory = wikicategory .. '[[Kategori:Databox med dold lång lista]]'
		end
    elseif #statements >= 1 then
		dataValues=item:formatStatements('P31').value
		if lang == 'sv' then
			if dataValues:match("<span>%[%[Människa|människa%]%]</span>") then -- Remove 'is instance of' -> 'human' TODO: Same for other languages
				if #statements == 1 then
					dataValues = ''
				else
					dataValues = dataValues:gsub("<span>%[%[Människa|människa%]%]</span>,?%s?", "")
				end
			end
		end
		
		if #dataValues > 0 then
			dataValues=listCase(dataValues)
				
	    	dataTable:tag('caption')
	             :css({
	             	['background-color'] = 'LightGrey',
	             	['font-weight'] = 'bold',
	             	['margin-top'] = '0.4em',
	             	margin = '0.5em 0',
	            	padding = '1em 1',
	             })
				:wikitext(dataValues)    		 
				:wikitext('<sup class="noprint Inline-Template">&nbsp;[[File:Blue_pencil.svg|' 
	                   	.. edit_message .. '|8px|baseline|class=noviewer|link=https://www.wikidata.org/wiki/' 
	                   	.. item.id .. '#P31' .. ']]</sup>')
	
			if #statements >= math.max(list_length-3,3) and lang == 'sv' then -- warning of long list but not too long list
				wikicategory = wikicategory .. '[[Kategori:Databox med lång lista]]'
			end
		end
	end
	
    local properties = mw.wikibase.orderProperties(item:getProperties())
    local property_blacklist_hash = Set(property_blacklist)
    property_blacklist_hash['P18'] = true --Showed separately
    property_blacklist_hash['P31'] = true --Showed separately
    local property_whitelist_hash = Set(property_whitelist)
	local properties_with_parents_hash = Set(properties_with_parents)
	local properties_with_children_hash = Set(properties_with_children)
    local countryid = ' '
    pcall(function () 
    	countryid = item.claims['P17'][1].mainsnak.datavalue.value.id 
    end)

    for _, property in pairs(properties) do
        local datatype = item.claims[property][1].mainsnak.datatype
        local statements = item:getBestStatements(property)
        if ( (datatype ~= 'external-id' 
        			and datatype ~= 'commonsMedia'
        			and datatype ~= 'monolingualtext' 
        			and datatype ~= 'url')
        		or property_whitelist_hash[property] )
        	and not property_blacklist_hash[property] 
        	and 1 <= #statements then
        	
        	if #statements > list_length then
    			if lang == 'sv' then
			       	wikicategory = wikicategory .. '[[kategori:Databox med dold lång lista]]' 
			    end	
			else
	            local propertyValue = item:formatStatements(property)
	            propertyValue.label = langObject:ucfirst(hyphenate(propertyValue.label, lang)) -- left table cell content 
	            	            local propertyEntity = mw.wikibase.getEntity(property) -- Time consuming
	            if propertyEntity then
	            	
	            	-- Replace property name by short name if only one in content language:
	            	if propertyEntity['claims']['P1813'] then
	            		local shortNames = propertyEntity['claims']['P1813'] -- 'P1813' = short name.
						shortname = ''
						for _, s in pairs(shortNames) do 
	 						if s.mainsnak.datavalue.value.language == lang then -- (Should check that only one value is in the content lang)
			            		if #shortname > 0 then
			            			shortname = ''
			            			break -- Several shortnames in the local language
			            		end
			            		shortname = s.mainsnak.datavalue.value.text
					        end
	 					end 
				        if #shortname > 0 then
		            		propertyValue.label = langObject:ucfirst(hyphenate(shortname, lang))  
				        end
					end
	
	            	-- Link row label (property name) to related article in content language:
	            	local propertySubjects = propertyEntity:getBestStatements('P1629') -- 'P1629 = subject item of this property'
	            	if #propertySubjects == 1 then
			            local subjectItemQid = propertyEntity['claims']['P1629'][1].mainsnak.datavalue.value.id
			            articleSitelink = mw.wikibase.getSitelink(subjectItemQid)
			            if articleSitelink ~= nil then -- Property subject item has local article
			            	propertyValue.label = '[[' .. articleSitelink .. '|' .. propertyValue.label .. ']]'  
			            end
			            
	            	end
	        	
	        	end
	        
	            local dataValues -- right table cell content
	            if #statements == 1 
	            		and levels >= 2 
	            		and (properties_with_parents_hash[property] 
	            			or properties_with_children_hash[property])
	            		then 
	            	dataValues = propertyValue.value
					if merged_chronology and property == 'P156' then -- If 'P155' (follows) already shown as bulleted list, P156 (followed by) should be part of same list.
	            		propertyValue.label = '' -- Hide 'Followed by' in left column
	            	end
	            	if property == 'P155' or property == 'P156' then -- follows or followed by
	            		dataValues = '• ' .. dataValues
	            	end					
            	
            		local level = {}
					
					-- Show parent/child item if any:
		            level[1] = {}
		            level[1].value = item['claims'][property][1].mainsnak.datavalue.value -- (Can give non-best statement?)
		            level[1].item = mw.wikibase.getEntity(level[1].value.id) -- Time consuming
		            level[1].statements = mw.wikibase.getBestStatements(level[1].item.id, property) 
					
					if #level[1].statements == 1 and level[1].statements[1].mainsnak.datavalue then
						level[1].qid = level[1].statements[1].mainsnak.datavalue.value.id
						if level[1].qid ~= countryid then -- do not repeat country as administrative belonging or place 
								
							level[1].propertyValue = level[1].item:formatStatements(property)
				        	if level[1].propertyValue then
				        		
							-- Show multi-level list as bulleted list:
				        		if not (property == 'P155' or property == 'P156') then
					        		dataValues = '• ' .. dataValues
								end							
								level[1].qid = level[1].statements[1].mainsnak.datavalue.value.id
					        	if properties_with_children_hash[property] then -- next lower level / child item: put in end of the list. 
					        		if property == 'P156' then -- follows
					        			dataValues = dataValues 
					        			.. '<br/>• ' .. level[1].propertyValue.value 
					        		else -- indent
					        			dataValues = dataValues 
					        			.. '<br/>' .. '&nbsp;• ' .. level[1].propertyValue.value
					        		end
					        		
					        	else -- next higher level / parent item: put first in list
					        		if property == 'P155' then -- followed by
					        			dataValues = '• ' .. level[1].propertyValue.value
					        			.. '<br/>' .. dataValues 
					        		else -- indent
					        			dataValues = '• ' .. level[1].propertyValue.value
					        			.. '<br/>&nbsp;' .. dataValues 
					        		end
					        	end
		            	
				            	local l = 2 -- level counter
						    	while l<levels and level[l-1].item.claims[property][1].mainsnak.datavalue do 
							    	level[l]={}
							    	level[l].value = level[l-1].item.claims[property][1].mainsnak.datavalue.value -- (Best statement?)
						            level[l].item = mw.wikibase.getEntity(level[l].value.id) -- Time consuming
						            level[l].statements = mw.wikibase.getBestStatements(level[l].item.id, property)  
									 
									if #level[l].statements > 0 and level[l].statements[1].mainsnak.datavalue then 
										level[l].qid = level[l].statements[1].mainsnak.datavalue.value.id
								    	if #level[l].statements == 1 
								    			and level[l].qid ~= countryid -- do not repeat country as administrative belonging or place 
								    			then
								    		level[l].propertyValue = level[l].item:formatStatements(property) -- (Best statement?)
								    		if properties_with_children_hash[property] then -- next lower level / child item 
					        					if property == 'P156' then -- follows
					        						dataValues = dataValues .. '<br/>'
								        			.. '• ' .. level[l].propertyValue.value
					        					else -- indent
					        						dataValues = dataValues .. '<br/>'
								        			.. string.rep('&nbsp;', l) .. '• ' .. level[l].propertyValue.value
								        		end
								        	else -- next higher level / parent item
					        					if property == 'P155' then -- followed by
								        			dataValues = '• ' .. level[l].propertyValue.value
								        			.. '<br/>' .. dataValues
					        					else -- indent
								        			dataValues = '• ' .. level[l].propertyValue.value
								        			.. '<br/>&nbsp;' .. dataValues:gsub('<br/>', '<br/>&nbsp;')
								    			end
								    		end
					            			l = l+1
						            	else
					            			break
								    	end
					            	else
					            		break
					            	end
								end -- while l
						        if lang == 'sv' then
						        	wikicategory = wikicategory .. '[[kategori:Databox med ' .. l .. ' nivåer]]' 
						        end
				        	end
				        	if property == 'P155' then -- P155 (follows) was a bulleted list
								followed_by = mw.wikibase.getBestStatements(level[1].item.id, 'P156')
								if followed_by and #followed_by == 1 then -- P156 (followed by) may also be a bulleted list
									
									-- Show merged chronology list, including this wikidata object
									dataValues = dataValues
									.. "<p>• \'\'\'" .. item:getLabel() .. "\'\'\'"
									if lang == 'sv' then
										propertyValue.label = 'Kronologi'
										merged_chronology = true
									end
									if lang == 'en' then
										propertyValue.label = 'Chronology'
										merged_chronology = true
									end
								end
							end
						end
			    	end
	            else -- not a multi-level list
	            	if datatype == 'url' then  -- only show first url
	            		if #statements[1].mainsnak.datavalue.value>40 then -- replace long url by "link"
		            		if lang == 'sv' then
			            		dataValues = frame:preprocess('[' .. statements[1].mainsnak.datavalue.value .. ' länk]')
			            	else
			            		dataValues = frame:preprocess('[' .. statements[1].mainsnak.datavalue.value .. ' link]')
							end
						else -- hide "https://" or "http:// and / in the end"
							dataValues = frame:preprocess('[' .. statements[1].mainsnak.datavalue.value 
								.. ' ' .. statements[1].mainsnak.datavalue.value:gsub('https?://', ''):gsub('/$', '') .. ']')
						end
	            	elseif datatype == 'geo-shape' then -- only show first geo-shape
	            		if lang == 'sv' then
		            		dataValues = frame:preprocess('[https://commons.wikimedia.org/wiki/' 
		            			.. statements[1].mainsnak.datavalue.value:gsub(' ', '_') .. ' kartlänk]')
		            	else
		            		dataValues = frame:preprocess('[https://commons.wikimedia.org/wiki/' 
		            			.. statements[1].mainsnak.datavalue.value:gsub(' ', '_') .. ' map link]')
						end
	            	else 
	            		dataValues = frame:preprocess(propertyValue.value)
	            		if #statements > 1 then
	            			dataValues = listSeparate(dataValues, list_separator)
	            		end
		        		-- if datatype == 'time' and replaceTime then
	            		-- 	for p,r in pairs(replaceTime) do
		            	-- 		dataValues = dataValues:gsub(p, r) -- Replace BCE with BC depending on era parameter
		            	-- 	end
		        		-- end
            			if datatype == 'time' then
            				dataValues = year(dataValues, lang, replaceTime)
            			end
	        			if #statements >= math.max(list_length-3,3) and lang == 'sv' then --Warning on long but not hidden list
							wikicategory = wikicategory .. '[[kategori:Databox med lång lista]]'
						end
					end
	    		end
	    		-- Replace "no value" with a dash:
	    		if noValueDict[lang] then
	    			dataValues = dataValues:gsub('<span>' .. noValueDict[lang] .. '</span>', '<span>–</span>')	
	    		end
	        	-- Render table row:
	            dataTable:tag('tr')
	                :tag('th')
	            	:css({
	       	            ['vertical-align'] = 'text-top',
		            })
	                    :attr('scope', 'row')
	                    :attr('colspan', '1')
	                    :wikitext(propertyValue.label):done()
	                :tag('td')
	                :css({
	       	            ['vertical-align'] = 'text-top',
		             })
	                    :attr('colspan', '2')
	                    :wikitext(dataValues)
	                    :wikitext('<sup class="noprint Inline-Template">&nbsp;[[File:Blue_pencil.svg|' 
	                    	.. edit_message .. '|8px|baseline|class=noviewer|link=https://www.wikidata.org/wiki/' 
	                    	.. item.id .. '#' .. property .. ']]</sup>')
        		
        	end -- if #statements
        end -- if datatype
    end -- for property
    
    --Map
    if zoom >= 0 then
	    local coordinates_statements = item:getBestStatements('P625')
	    if #coordinates_statements == 1 
	    		and coordinates_statements[1].mainsnak.datavalue 
	    		and coordinates_statements[1].mainsnak.datavalue.value.globe == 'http://www.wikidata.org/entity/Q2' then
	        databoxRoot:wikitext(buildInteractiveMap(width, coordinates_statements[1].mainsnak.datavalue.value, item.id, zoom))
	        if lang == 'sv' then
				wikicategory = wikicategory .. '[[Kategori:Sidor med kartor skapade med Databox]]'
			end
	    end
    end
    
    if mw.title.getCurrentTitle().namespace == 0 then -- Only in main namespace 
		if tostring(databoxRoot):match('<span>Q%d+</span>') then
	        if lang == 'sv' then
				wikicategory = wikicategory .. '[[Kategori:Databox som visar qid-kod]]'
			elseif lang == 'en' then
				wikicategory = wikicategory .. '[[Category:Databox that shows qid code]]'
			end
		end
		if #wikicategory then
    		databoxRoot:wikitext(wikicategory)
		end
  	end
	return tostring(databoxRoot)
end

return p