Mô đun:Thông tin khu dân cư

番版𠓨𣅶14:32、𣈜19𣎃8𢆥2015𧵑Mxn (討論 | 㨂𢵰) (Xóa chuỗi thử nghiệm)
(恪) ←番版𫇰 | 番版㵋一 () | 番版㵋→ ()

Documentation for this module may be created at Module:Mô đun:Thông tin khu dân cư/doc

local p = {}
local lang = mw.getContentLanguage()

local leader_fmt = {
	[=[<tr class="mergedrow">
	<th>&nbsp;-&nbsp;%s
	<td>%s %s
</tr>]=],
}

local event_fmt = {
	[=[<tr class="mergedrow">
	<th>%s
	<td>%s %s
</tr>]=],
}

local pop_fmt = {
	[=[<tr class="mergedtoprow">
	<td>'''Dân số''' %s
	<td>%s %s
</tr>]=],
	"%s %s %s",
}

local coord_fmt = {
	[=[<tr class="mergedbottomrow">
	<th colspan="2" style="text-align: center; font-size: smaller; padding-bottom: 0.7em;">Tọa độ: <span>%s</span> %s</th>
</tr>]=],
}

local map_fmt = {
	[=[<tr class="mergedrow">
        <td colspan="2" align="center">
%s
        </td>
    </tr>]=],
    "<center>%s</center>",
}

local date_fmts_by_precision = {
	[6] = function (os_time)
		local year = os.date("*t", os_time).year
		local century
		if year > 0 then
			century = math.floor((year - 1) / 1000 + 1)
		else
			century = math.ceil((year + 1) / 1000 - 1)
		end
		return mw.ustring.format("Thiên niên kỷ %i", century)
	end,
	[7] = function (os_time)
		local year = os.date("*t", os_time).year
		local century
		if year > 0 then
			century = year / 100 + 1
		else
			century = year / 100 - 1
		end
		return mw.ustring.format("Thế kỷ %i", century)
	end,
	[8] = '"Thập niên" Y',
	[9] = "Y",
	[10] = '"Tháng" n "năm" Y',
	[11] = 'j "tháng" n "năm" Y',
	[12] = 'j "tháng" n "năm" Y "lúc" G "giờ"',
	[13] = 'j "tháng" n "năm" Y "lúc" G:i',
	[14] = 'j "tháng" n "năm" Y "lúc" G:i:s',
}

function map(domain, func)
	local range = {}
	for i, item in ipairs(domain) do
		table.insert(func(item))
	end
	return range
end

function all_pass(items, test)
	for i, item in ipairs(items) do
		if not test(item) then return false end
	end
	return true
end

function any_pass(items, test)
	for i, item in ipairs(items) do
		if test(item) then return true end
	end
	return false
end

function has(items, item)
	for i, it in ipairs(items) do
		if it == item then return true end
	end
	return false
end

---Returns the maximum item in the given list, as defined by a function that
-- returns true if and only if the first parameter is less than the second.
function max(items, comp)
	local item = items[1]
	if #items > 1 then
		for i = 2, #items do
			if comp(item, items[i]) then
				item = items[i]
			end
		end
	end
	return item
end

---Creates a time object compatible with os.time() given an ISO 8601 date
-- string. See [[mw:Wikibase/DataModel#Dates and times]].
function create_time(iso_time)
	local t = {}
	t.year = string.sub(iso_time, 1, 12)
	t.month = string.sub(iso_time, 14, 15)
	t.day = string.sub(iso_time, 17, 18)
	t.hour = string.sub(iso_time, 20, 21)
	t.min = string.sub(iso_time, 23, 24)
	t.sec = string.sub(iso_time, 26, 27)
	return os.time(t)
end
--p.create_time = create_time														-- debug

---Returns whether the given qualifier is in the past.
function is_past(qualifier)
	local iso_time = qualifier and qualifier.datavalue
		and qualifier.datavalue.value and qualifier.datavalue.value.time
	return os.difftime(os.time(), create_time(iso_time)) > 0
end

---Returns whether the given qualifier is in the future.
function is_future(qualifier)
	local iso_time = qualifier and qualifier.datavalue
		and qualifier.datavalue.value and qualifier.datavalue.value.time
	return os.difftime(create_time(iso_time), os.time()) > 0
end

---Returns claims that are currently in effect or are not time-qualified.
function claim_is_current(claim)
	local start_dates = claim.qualifiers and claim.qualifiers.P580
	local end_dates = claim.qualifiers and claim.qualifiers.P582
	return (not start_dates or #start_dates < 1 or all_pass(start_dates, is_past))
		and (not end_dates or #end_dates < 1 or all_pass(end_dates, is_future))
end

function qualifier_entities(claim, prop_id)
	local qualifiers = claim.qualifiers and claim.qualifiers["P" .. prop_id]
	if not qualifiers then return nil end
	
	local qualifier_ids = {}
	for i, qualifier in ipairs(qualifiers) do
		local qualifier_id = qualifier.datavalue.value["numeric-id"]
		if qualifier_id then
			table.insert(qualifier_ids, qualifier_id)
		end
	end
	return qualifier_ids
end

function entity_link(entity_id, ucfirst)
	local title = mw.wikibase.sitelink("Q" .. entity_id)
	if ucfirst and title then title = lang:ucfirst(title) end
	local label = mw.wikibase.label("Q" .. entity_id)
	if ucfirst and label then label = lang:ucfirst(label) end
	if title then return mw.ustring.format("[[%s|%s]]", title, label or title) end
	return mw.ustring.format("[[d:Q%s|%s]]", entity_id,
		label or "Q" .. entity_id)
end

function p.leaders(frame)
	if frame.args.override and #frame.args.override > 1 then return frame.args.override end
	
	local entity = mw.wikibase.getEntityObject()
	if not entity then return nil end
	
	local leaders = entity.claims.P6
	if not leaders then return nil end
	
	local version = tonumber(frame.args.version) or #leader_fmt
	local edit_button = frame:expandTemplate{
		title = "Sửa dữ liệu",
		args = {
			p = 6,
		},
	}
	
	local rows = {}
	for i, leader in ipairs(leaders) do
		local leader_id = leader.mainsnak.datavalue.value["numeric-id"]
		if leader_id and claim_is_current(leader) then
			local leader_name = entity_link(leader_id)
			local leader_title_ids = qualifier_entities(leader, 39)
			if leader_title_ids and #leader_title_ids > 0 then
				for i, leader_title_id in ipairs(leader_title_ids) do
					local row = mw.ustring.format(leader_fmt[version],
						entity_link(leader_title_id, true), leader_name,
						edit_button)
					table.insert(rows, row)
				end
			else
				local row = mw.ustring.format(leader_fmt[version],
					"Viên chức", leader_name, edit_button)
				table.insert(rows, row)
			end
		end
	end
	return table.concat(rows, "\n")
end

---Returns the os.time() compatible object represented by the given claim.
function time_from_claim(claim)
	local iso_time = claim and claim.mainsnak.datavalue
		and claim.mainsnak.datavalue.value and claim.mainsnak.datavalue.value.time
	return create_time(iso_time)
end

function time_str_from_claim(claim)
	local os_time = time_from_claim(claim)
	local precision = claim and claim.mainsnak.datavalue
		and claim.mainsnak.datavalue.value and claim.mainsnak.datavalue.value.precision
	if not os_time and precision then return end
	
	local fmt = date_fmts_by_precision[precision]
	if not fmt then return end
	if type(fmt) == "function" then return fmt(os_time) end
	
	local fmt_date = lang:formatDate(fmt, claim.mainsnak.datavalue.value.time:sub(9))
	if os.date("*t", os_time).year < 1 then
		fmt_date = fmt_date .. " TCN"
	end
	return fmt_date
end

function p.events(frame)
	if frame.args.override and #frame.args.override > 1 then return frame.args.override end
	
	local entity = mw.wikibase.getEntityObject()
	if not entity then return nil end
	
	local events = entity.claims.P571
	if not events then return nil end
	table.sort(events, function (a, b)
		return os.difftime(time_from_claim(b), time_from_claim(a)) > 0
	end)
	
	local version = tonumber(frame.args.version) or #event_fmt
	local edit_button = frame:expandTemplate{
		title = "Sửa dữ liệu",
		args = {
			p = 571,
		},
	}
	
	local rows = {}
	for i, event in ipairs(events) do
		local event_str = time_str_from_claim(event)
		if event_str then
			local event_title_ids = qualifier_entities(event, 132)
			if event_title_ids and #event_title_ids > 0 then
				for i, event_title_id in ipairs(event_title_ids) do
					local row = mw.ustring.format(event_fmt[version],
						entity_link(event_title_id, true), event_str,
						edit_button)
					table.insert(rows, row)
				end
			else
				local row = mw.ustring.format(event_fmt[version],
					"Thành lập", event_str, edit_button)
				table.insert(rows, row)
			end
		end
	end
	return table.concat(rows, "\n")
end

---Returns an os.time() compatible object representing the time by which the
-- given claim is qualified.
function time_of_claim(claim)
	local qualifiers = claim.qualifiers and claim.qualifiers.P585
	local iso_time = qualifiers and qualifiers[1] and qualifiers[1].datavalue
		and qualifiers[1].datavalue.value and qualifiers[1].datavalue.value.time
	return (iso_time and create_time(iso_time)) or os.time()
end

function latest_population_claim()
	local entity = mw.wikibase.getEntityObject()
	if not entity then return nil end
	
	local pops = entity.claims.P1082
	if not pops then return nil end
	local latest_pop = max(pops, function (a, b)
		return os.difftime(time_of_claim(b), time_of_claim(a)) > 0
	end)
	return latest_pop
end

function number_from_claim(claim)
	return claim and claim.mainsnak.datavalue.value
		and tonumber(claim.mainsnak.datavalue.value.amount)
end

function claim_is_estimate(claim)
	local method_ids = qualifier_entities(claim, 459)
	return method_ids and has(method_ids, 791801)
end

function p.population(frame)
	if frame.args.override and #frame.args.override > 1 then return frame.args.override end
	
	local latest_pop = latest_population_claim()
	if not latest_pop then return end
	
	local pop = number_from_claim(latest_pop)
	if not pop or frame.args.plain then return pop end
	
	local version = tonumber(frame.args.version) or #pop_fmt
	local edit_button = frame:expandTemplate{
		title = "Sửa dữ liệu",
		args = {
			p = 1082,
		},
	}
	
	local os_time = time_of_claim(latest_pop)
	local is_estimate = claim_is_estimate(latest_pop)
	local fmt_date
	if os_time then
		local year = os.date("*t", os_time).year
		fmt_date = string.format("%i", year)
		if year < 1 then
			fmt_date = fmt_date:sub(2) .. " TCN"
		end
		if fmt_date then
			if is_estimate then fmt_date = "ước lượng " .. fmt_date end
			fmt_date = mw.ustring.format("(%s)", fmt_date)
		end
	end
	if is_estimate and not fmt_date then
		fmt_date = "(ước lượng)"
	end
	
	local pop_str = lang:formatNum(pop)
	if version == 2 then
		return mw.ustring.format(pop_fmt[version], pop_str, fmt_date, edit_button)
	end
	return mw.ustring.format(pop_fmt[version], fmt_date, pop_str, edit_button)
end

function p.coordinates(frame)
	if frame.args.override and #frame.args.override > 1 then return frame.args.override end
	
	local entity = mw.wikibase.getEntityObject()
	if not entity then return nil end
	
	local coords = entity.claims.P625
	if not coords or #coords < 1 then return nil end
	local coord = coords[1] and coords[1].mainsnak.datavalue
		and coords[1].mainsnak.datavalue.value
	if not coord then return nil end
	
	local version = tonumber(frame.args.version) or #pop_fmt
	local edit_button = frame:expandTemplate{
		title = "Sửa dữ liệu",
		args = {
			p = 625,
		},
	}
	
	if not frame.args.type or frame.args.type == "city" then
		local pop = number_from_claim(latest_population_claim())
		if pop then
			frame.args.type = string.format("city(%i)", pop + 1)
		else
			frame.args.type = "city"
		end
	end
	
	-- [[mw:Wikibase/DataModel#Geographic locations]], [[:en:Template:Coord#globe:G]]
	if not frame.args.globe and coord.globe then
		local globes_by_entity = {
			[2] = "earth",
			-- Maybe someday?
			[308] = "mercury", [313] = "venus", [405] = "moon", [111] = "mars",
			[7547] = "phobos", [7548] = "deimos", [596] = "ceres",
			[3030] = "vesta", [3169] = "ganymede", [3134] = "callisto",
			[3123] = "io", [3143] = "europa", [15034] = "mimas",
			[3303] = "enceladus", [15047] = "tethys", [15040] = "dione",
			[15050] = "rhea", [2565] = "titan", [15037] = "hyperion",
			[15037] = "iapetus", [17975] = "phoebe", [3352] = "miranda",
			[3343] = "ariel", [3338] = "umbriel", [3322] = "titania",
			[3332] = "oberon", [3359] = "triton", [339] = "pluto",
		}
		local globe_entity = tonumber(string.match(coord.globe, "https?://www.wikidata.org/entity/Q(%d+)"))
		frame.args.globe = globes_by_entity[globe_entity]
	end
	
	local valid_params = {"type", "scale", "dim", "region", "globe", "source"}
	local params = {}
	for i, param in ipairs(valid_params) do
		if frame.args[param] and #frame.args[param] > 0 then
			table.insert(params, param .. ":" .. frame.args[param])
		end
	end
	
	local coord_str = frame:expandTemplate{
		title = "Tọa độ",
		args = {
			coord.latitude,
			coord.longitude,
			table.concat(params, "_"),
			display = (frame.args.display and "inline,title") or "inline",
			format = (frame.args.format and #frame.args.format > 0 and frame.args.format) or "dms",
			name = frame.args.name,
			notes = frame.args.notes,
		},
	}
	
	return mw.ustring.format(coord_fmt[version], coord_str, edit_button)
end

function p.rawCoordinates(frame)
	local entity = mw.wikibase.getEntityObject()
	if not entity then return nil end
	
	local coords = entity.claims.P625
	if not coords or #coords < 1 then return nil end
	local coord = coords[1] and coords[1].mainsnak.datavalue
		and coords[1].mainsnak.datavalue.value
	if not coord then return nil end
	
	return coord[frame.args[1]]
end

function p.map(frame)
	if frame.args.override and #frame.args.override > 1 then return frame.args.override end
	
	local entity = mw.wikibase.getEntityObject()
	if not entity then return nil end
	
	local coords = entity.claims.P625
	if not coords or #coords < 1 then return nil end
	local coord = coords[1] and coords[1].mainsnak.datavalue
		and coords[1].mainsnak.datavalue.value
	if not coord then return nil end
	
	local version = tonumber(frame.args.version) or #pop_fmt
	local map_str = frame:expandTemplate{
		title = "Bản đồ định vị",
		args = {
			frame.args[1],
			label = frame.args.label,
			lat = coord.latitude,
			long = coord.longitude,
			float = frame.args.float or "none",
			caption = (frame.args.caption and
				mw.ustring.format("<center>%s</center>", frame.args.caption)),
			border = frame.args.border or "none",
			position = frame.args.position or "right",
			width = frame.args.width or 250,
		},
	}
	
	return mw.ustring.format(map_fmt[version], map_str)
end

local math_module = require( "Module:Math" )
local precision = math_module._precision
local infobox_module = require('Module:Infobox')
local image_module = require('Module:InfoboxImage')
local locationmap_module = require('Module:Location map')

local function isnotempty(s)
	return s and s:match( '^%s*(.-)%s*$' ) ~= ''
end

local function firstnonempty(s)
	for i=1,#s do
		if (s[i] and s[i]:match( '^%s*(.-)%s*$' ) ~= '') then
			return s[i]
		end
	end
	return ''
end

local function yesno(s, yes, no, ukn, blank)
	-- this function implements yesno
	local vals = {['yes'] = 1, ['y'] = 1, ['1'] = 1, ['no'] = 0, ['n'] = 0, ['0'] = 0}
	if( s and s:match( '^%s*(.-)%s*$' ) ~= '') then
		local outval = vals(s:lower())
		if( outval == nil ) then
			return ukn
		else
			return (outval == 1) and yes or no
		end
	else
		return blank
	end
end

local function rnd(num, digits)
	-- This function implements {{rnd}}
	return math_module._precision_format(tostring(num), tostring(digits))
end

local function order_of_magnitude(num)
	-- This function partially implements {{Order of magnitude}}
    if( num ) then
		num = math.abs(num)
		if( num == 0 ) then
			return 0
		else
			return math.floor( math.log10(num) )
		end
	else
		return 0
	end
end

local function page_exists( title )
    -- This function implements #ifexist
    local noError, titleObject = pcall(mw.title.new, title)
    if not noError then
        return false
    else
        if titleObject then
            return titleObject.exists
        else
            return false
        end
    end
end

local function link(pagelink, linktext, name)
    -- This function implements {{Infobox settlement/link}}
    pagelink = pagelink or ''
    linktext = linktext or ''
    name     = name or ''
    
    if( pagelink ~= '' ) then
        -- use "[[pagelink|linktext]]"
        return string.format('[[%s|%s]]', pagelink, linktext)
    else
        -- try "[[linktext of PAGENAME|linktext]]"
        pagelink = string.format('%s of %s',linktext, mw.title.getCurrentTitle().text)
        if( page_exists( pagelink ) ) then
            return string.format('[[%s|%s]]', pagelink, linktext)
        elseif( name ~= '' ) then
            -- try "[[linktext of name|linktext]]"
            pagelink = string.format('%s of %s', linktext, name)
            if( page_exists(pagelink) ) then
               return string.format('[[%s|%s]]', pagelink, linktext)
            end
        end
    end

    return linktext
end

local function columns(cell1, cell2, cell3, cell4)
    --- This function implements {{Infobox settlement/columns}} with no cell0
    local function makecell( c1 )
        if isnotempty(c1) then
			local root = mw.html.create('td')
			root:attr('align', 'center')
				:css('vertical-align', 'middle')
				:wikitext(c1)
            return tostring(root)
        end
        return ''
    end
    
    local function makerow( c1, c2 )
        local root = mw.html.create('')
        if isnotempty(c1) then
            if isnotempty(c2) then
            	root:tag('td')
					:attr('align', 'center')
					:css('vertical-align', 'middle')
					:wikitext(c1)
				root:tag('td')
					:attr('align', 'center')
					:css('vertical-align', 'middle')
					:wikitext(c2)
            else
            	root:tag('td')
					:attr('colspan', '2')
					:attr('align', 'center')
					:css('vertical-align', 'middle')
					:wikitext(c1)
            end
        elseif isnotempty(c2) then
			root:tag('td')
				:attr('colspan', '2')
				:attr('align', 'center')
				:css('vertical-align', 'middle')
				:wikitext(c2)
        end
        return tostring(root)
    end
    
    local count = 0
    count = count + (isnotempty(cell1) and 1 or 0)
    count = count + (isnotempty(cell2) and 1 or 0)
    count = count + (isnotempty(cell3) and 1 or 0)
    count = count + (isnotempty(cell4) and 1 or 0)

    if(count > 0) then
		local root = mw.html.create('table')
		root:css('width', '100%')
			:css('background', 'transparent')
		if(count > 2) then
			root:tag('tr')
					:wikitext(makerow(cell1, cell2))
			root:tag('tr')
					:wikitext(makerow(cell3, cell4))
		else
			root:tag('tr')
				:wikitext(makecell(cell1))
				:wikitext(makecell(cell2))
				:wikitext(makecell(cell3))
				:wikitext(makecell(cell4))
		end
		return tostring(root)
	else
		return ''
	end
end

local function columns2(cell0, cell1, cell2, cell3, cell4, cell5)
    -- This function implements {{Infobox settlement/columns}} with cell0
    local function makerow( c1 )
		if isnotempty(c1) then
			local root = mw.html.create('tr')
			root:tag('td')
				:attr('align', 'center')
				:css('style', 'vertical-align:middle')
				:wikitext(c1)
			return tostring(root)
		else
			return ''
		end
	end
    
	local count = 0
	count = count + (isnotempty(cell1) and 1 or 0)
	count = count + (isnotempty(cell2) and 1 or 0)
	count = count + (isnotempty(cell3) and 1 or 0)
	count = count + (isnotempty(cell4) and 1 or 0)
	count = count + (isnotempty(cell5) and 1 or 0)

	if(count > 0) then
		local root = mw.html.create('table')
		root:css('width', '100%')
			:css('background', 'transparent')
		local row = root:tag('tr')
		row
			:tag('td')
				:tag('table')
					:css('width', '100%')
					:css('background', 'transparent')
					:wikitext(makerow(cell1))
					:wikitext(makerow(cell2))
					:wikitext(makerow(cell3))
					:wikitext(makerow(cell4))
					:wikitext(makerow(cell5))
        if isnotempty(cell0) then
			row
				:tag('td')
					:attr('align', 'center')
					:css('vertical-align', 'top')
					:wikitext(cell0)
		end
		return tostring(root)
	else
		return cell0
	end
end

local function translitlangbox(transtype, transinfo)
	local args = {}
	args['child'] = 'yes'
	local count = 0
	for i=0,6 do
		if(isnotempty(transinfo[i]) and isnotempty(transtype[i])) then
			args['rowclass' .. tostring(i+1)] = isnotempty(transtype[i+1]) and 'mergedrow' or 'mergedbottomrow'
			args['label' .. tostring(i+1)] = '&nbsp;•&nbsp;' .. transtype[i]
			args['data' .. tostring(i+1)] = transinfo[i]
			count = count + 1
		end
	end
	return ( count > 0 ) and args or nil
end

local function unitpref(pref, region, unit_type)
    -- This function implements {{Infobox settlement/pref}}
    local pref_impus = { ['imperial'] = 1, ['english'] = 1, ['uk'] = 1, ['us'] = 1, 
        ['u.s.'] = 1, ['standard'] = 1, ['us customary'] = 1, ['u.s. customary'] = 1}
    local pr = (pref and pref:lower()) or ''
    local r = region or ''
    local u = unit_type or ''

    pr = mw.ustring.gsub(pr, '^%s*([a-z].*[a-z\.])%s*$','%1')

    if( pref_impus[pr] ) then
        return 'impus'
    end
    if( mw.ustring.match( r, 'United States' ) ) then
        return 'impus'
    end
    if( mw.ustring.match( r, 'United Kingdom' ) ) then
        return 'impus'
    end
    if( (u .. '_' .. pr ) == 'area_dunam' ) then
        return 'dunam'
    end
    
    return 'metric'
end

local function areadisp(frame, pref, name, mag, ha, km2, sqmi, acre, dunam, percent, link)
    -- This function implements {{Infobox settlement/areadisp}}
    local function formatnum(num)
        return frame:callParserFunction{ name = 'formatnum', args = num }
    end
    
    local metv, metu = '', ''
    local impv, impu = '', ''
    local dunv, dunu = '', ''
    local lstr1, lstr2 = '', ''
    local rndv = 0
    
    pref = pref or ''
    name = name or ''
    mag = mag or ''
    ha = ha or ''
    km2 = km2 or ''
    sqmi = sqmi or ''
    acre = acre or ''
    dunam = dunam or ''
    percent = percent or ''
    link = link or ''
    
    if ( ha ~= '' ) then
        metv = formatnum( ha )
        metu = 'ha'
    elseif ( km2 ~= '' ) then
        metv = formatnum( km2 )
        metu = 'km<sup>2</sup>'
    elseif ( dunam ~= '' ) then
        if (tonumber(dunam) < 1E3) then
            -- convert dunams to hectares
            metv = dunam/10
            metu = 'ha'
            rndv = precision(dunam)+1
            metv = rnd(metv,rndv)
        else
            -- convert dunams to square kilometers
            metv = dunam/1000
            metu = 'km<sup>2</sup>'
            rndv = precision(dunam)+3
            metv = rnd(metv,rndv)
        end
    elseif( acre ~= '' ) then 
        -- convert acres to hectares
        metv = acre*0.4046856422
        metu = 'ha'
        rndv = math.max(precision(acre),-1*order_of_magnitude(metv))
        metv = rnd(metv,rndv)
    elseif( sqmi ~= '' ) then
        -- convert sqmi to km2
        metv = sqmi*2.589988110336
        metu = 'km<sup>2</sup>'
        rndv = math.max(precision(sqmi)-1,-1*order_of_magnitude(metv))
        metv = rnd(metv,rndv)
    end

    if ( acre ~= '' ) then
        impv = formatnum( acre )
        impu = 'acre'
    elseif ( sqmi ~= '' ) then
        impv = formatnum( sqmi )
        impu = 'sq&nbsp;mi'
    elseif (ha ~= '' ) then
        -- convert hectares to acres
        impv = ha/0.4046856422
        impu = 'acre'
        rndv = precision(ha)
        impv = rnd(impv,rndv)
    elseif (km2 ~= '' ) then
        -- convert square kilometres to square miles
        impv = km2/2.589988110336
        impu = 'sq&nbsp;mi'
        rndv = math.max(precision(km2),-1*order_of_magnitude(impv))
        impv = rnd(impv,rndv)
    elseif (dunam ~= '' ) then
        if (tonumber(dunam) < 2589) then
            -- convert dunams to acres
            impv = dunam/4.046856422
            impu = 'acre'
            rndv = math.max(precision(dunam),-1*order_of_magnitude(impv))
            impv = rnd(impv,rndv)
        else
            -- convert dunams to square miles
            impv = dunam/2589.988110336
            impu = 'sq&nbsp;mi'
            rndv = math.max(precision(dunam)+3,-1*order_of_magnitude(impv))
            impv = rnd(impv,rndv)
        end
    end

    if( mw.ustring.match(pref:lower(), '^%s*dunam%s*$') and (dunam == '') ) then
        if( km2 ~= '' ) then
            -- convert square kilometres to dunams
            dunv = km2*1000
            rndv = precision(km2)-3
            dunv = rnd(dunv,rndv)
        elseif( ha ~= '' ) then
            -- convert hectares to dunams
            dunv = ha*10
            rndv = precision(ha)-1
            dunv = rnd(dunv,rndv)
        elseif( sqmi ~= '' ) then
            -- convert square miles to dunams
            dunv = sqmi*2589.988110336
            rndv = math.max(precision(sqmi)-4,-1*order_of_magnitude(dunv))
            dunv = rnd(dunv,rndv)
        elseif( acre ~= '' ) then
            -- convert acres to dunams
            dunv = acre*4.046856422
            rndv = math.max(precision(acre)-1,-1*order_of_magnitude(dunv))
            dunv = rnd(dunv,rndv)
        end
    else
        dunv = formatnum( dunam )
    end
    if( link ~= '' ) then
        dunu = '[[dunum]]'
    else
        dunu = 'dunam'
    end
    
    if( (impu == 'acre') and (tonumber(impv) ~= 1) ) then
        impu = impu .. 's'
    end    
    if( tonumber(dunv) ~= 1 ) then
        dunu = dunu .. 's'
    end
    
    if( metv ~= '' and impv ~= '' ) then
        pref = unitpref(pref, name, 'area')
        if( percent ~= '' ) then
            percent = ' &nbsp;' .. percent .. '%'
        end
        
        if( mag ~= '' ) then
            if( metu == 'ha' ) then
                lstr1 = tostring( order_of_magnitude( metv * 1E4 ) ) 
            else
                lstr1 = tostring( order_of_magnitude( metv * 1E6 ) )
            end
            lstr1 = '[[1_E+' .. lstr1 .. '_m²|'
            lstr2 = ']]'
         end
   
         if( pref == 'impus' ) then
             return string.format('%s&nbsp;%s (%s%s&nbsp;%s%s)%s', 
                 impv, impu, lstr1, metv, metu, lstr2, percent)
         elseif ( pref == 'dunam' ) then
             return string.format('%s&nbsp;%s (%s%s&nbsp;%s%s&nbsp;or&nbsp;%s&nbsp;%s)%s', 
                 dunv, dunu, lstr1, metv, metu, lstr2, impv, impu, percent)
         else
             return string.format('%s%s&nbsp;%s%s (%s&nbsp;%s)%s', 
                 lstr1, metv, metu, lstr2, impv, impu, percent)
         end
     end
end

local function densdisp(frame, pref, name, perkm2, persqmi, pop, ha, km2, sqmi, acre, dunam)
    -- This function implements {{Infobox settlement/densdisp}}
    local function numorzero( num )
        num = num or ''
        num = tonumber(frame:callParserFunction{ name = 'formatnum', args = {num, 'R'}})
        if( num == nil ) then
            return 0
        else
            return num
        end
    end

    local function formatnum(num)
        return frame:callParserFunction{ name = 'formatnum', args = num }
    end
    
    local function formatnumR(num)
        return frame:callParserFunction{ name = 'formatnum', args = {num, 'R'} }
    end

    local metv, metu = '', 'km<sup>2</sup>'
    local impv, impu = '', 'sq&nbsp;mi'
    local rndv = 0
    local perkm2num = tonumber(formatnumR(perkm2 or ''))
    local persqminum = tonumber(formatnumR(persqmi or ''))
    local popnum = tonumber(formatnumR(pop or ''))

    pref = pref or ''
    name = name or ''
    perkm2 = perkm2 or ''
    persqmi = persqmi or ''
    pop = pop or ''
    ha = numorzero(ha)
    km2 = numorzero(km2)
    acre = numorzero(acre)
    sqmi = numorzero(sqmi)
    dunam = numorzero(dunam)
    
    if( (perkm2num == nil) and (persqminum == nil) ) then
       if( mw.ustring.match(perkm2:lower(), '^%s*auto%s*$') or mw.ustring.match(persqmi:lower(), '^%s*auto%s*$') ) then
           if( popnum ~= nil ) then
               if( km2 > 0 ) then
                   metv = popnum/km2
                   rndv = 1 - order_of_magnitude(metv)
                   metv = rnd(metv,rndv)
               elseif( ha > 0 ) then
                   metv = 100*popnum/ha
                   rndv = 1 - order_of_magnitude(metv)
                   metv = rnd(metv,rndv)
               elseif( dunam > 0 ) then
                   metv = 1000*popnum/dunam
                   rndv = 1 - order_of_magnitude(metv)
                   metv = rnd(metv,rndv)
               elseif( acre > 0 ) then
                   metv = (popnum/acre)/0.004046856422
                   rndv = 1 - order_of_magnitude(metv)
                   metv = rnd(metv,rndv)
               elseif( sqmi > 0 ) then
                   metv = (popnum/sqmi)/2.589988110336
                   rndv = 1 - order_of_magnitude(metv)
                   metv = rnd(metv,rndv)
               end
               if( sqmi > 0 ) then
                   impv = popnum/sqmi
                   rndv = 1 - order_of_magnitude(impv)
                   impv = rnd(impv,rndv)
               elseif( acre > 0 ) then
                   impv = 640*popnum/acre
                     rndv = 1 - order_of_magnitude(impv)
                   impv = rnd(impv,rndv)
               elseif( km2 > 0 ) then
                   impv = 2.589988110336*popnum/km2
                   rndv = 1 - order_of_magnitude(impv)
                   impv = rnd(impv,rndv)
               elseif( ha > 0 ) then
                   impv = 258.9988110336*popnum/ha
                   rndv = 1 - order_of_magnitude(impv)
                   impv = rnd(impv,rndv)
               elseif( dunam > 0 ) then
                   impv = 2589.988110336*popnum/dunam
                   rndv = 1 - order_of_magnitude(impv)
                   impv = rnd(impv,rndv)
               end
           end
       end
   elseif( perkm2num ~= nil ) then
       if( persqminum ~= nil ) then
           metv = formatnum( perkm2 )
           impv = formatnum( persqmi )
       else
           metv = formatnum( perkm2 )
           impv = perkm2num*2.589988110336
           rndv = math.max(precision(perkm2num)-1,-1*order_of_magnitude(impv)) 
           impv = rnd(impv,rndv)
       end
    elseif( persqminum ~= nil ) then
        metv = persqminum/2.589988110336
        rndv = math.max(precision(persqminum),-1*order_of_magnitude(metv))
        metv = rnd(metv,rndv)
        impv = formatnum( persqmi )
    end

    if( metv ~= '' and impv ~= '') then
      pref = unitpref(pref, name, 'area')
      if( pref == 'impus' ) then
         return string.format('%s/%s (%s/%s)', impv, impu, metv, metu)
      else
         return string.format('%s/%s (%s/%s)', metv, metu, impv, impu)
      end
    else
      return ''
    end  
end

local function lengthdisp(frame, pref, name, km, m, mi, ft)
    -- This function implements {{Infobox settlement/lengthdisp}}
    local function formatnum(num)
        return frame:callParserFunction{ name = 'formatnum', args = num }
    end
    
    local metv, metu = '', ''
    local impv, impu = '', ''
    pref = pref or ''
    name = name or ''
    m = m or ''
    km = km or ''
    ft = ft or ''
    mi = mi or ''
    
    if ( km ~= '' ) then
        metv = formatnum( km )
        metu = 'km'
    elseif ( m ~= '' ) then
        metv = formatnum( m )
        metu = 'm'
    elseif ( mi ~= '' ) then
        metv = mi*1.609344
        metu = 'km'
        rndv = precision(mi)
        metv = rnd(metv,rndv)
    elseif ( ft ~= '' ) then
        metv = ft*0.3048
        metu = 'm'
        rndv = math.max(precision(ft),-1*order_of_magnitude(metv))
        metv = rnd(metv,rndv)
    end
    
    if ( mi ~= '' ) then
        impv = formatnum( mi )
        impu = 'mi'
    elseif ( ft ~= '' ) then
        impv = formatnum( ft )
        impu = 'ft'
    elseif ( km ~= '' ) then
        impv = km/1.609344
        impu = 'mi'
        rndv = math.max(precision(km),-1*order_of_magnitude(impv))
        impv = rnd(impv,rndv)
    elseif ( m ~= '' ) then
        impv = m/0.3048
        impu = 'ft'
        rndv = precision(m)
        impv = rnd(impv,rndv)
    end
    
    if( impv ~= '' and metv ~= '' ) then
        pref = unitpref(pref, name, 'length')
        if( pref == 'impus' ) then
           return string.format('%s&nbsp;%s (%s&nbsp;%s)', impv, impu, metv, metu)
        else
           return string.format('%s&nbsp;%s (%s&nbsp;%s)', metv, metu, impv, impu)
        end
    else
        return ''
    end    
end

function p.areadisp(frame)
    local args = frame.args
    return areadisp(frame, args['pref'], args['name'], args['mag'], 
        args['ha'], args['km2'], args['sqmi'], args['acre'], args['dunam'], args['percent'], args['link'])
end

function p.densdisp(frame)
    local args = frame.args
    return densdisp(frame, args['pref'], args['name'], 
        args['/km2'], args['/sqmi'], args['pop'], args['ha'], args['km2'], args['sqmi'], args['acre'], args['dunam'])
end

function p.lengthdisp(frame)
    local args = frame.args
    return lengthdisp(frame, args['pref'], args['name'], args['km'], args['m'], args['mi'], args['ft'])
end

function p.link(frame)
    local args = frame.args
    return link(args['link'], args['type'], args['name'])
end

function p.columns(frame)
    local args = frame.args
    if( args[0] and args[0] ~= '' ) then
      return columns2(args[0], args[1], args[2], args[3], args[4], args[5])
    else
      return columns(args[1], args[2], args[3], args[4])
    end
end

function p.infobox(frame)
    local args = {}
    local oargs = frame:getParent().args
    local pname = firstnonempty({oargs['name'], oargs['official_name'], mw.title.text})
    local narrowmap = isnotempty(oargs['pushpin_map_narrow']) and isnotempty(oargs['pushpin_map'])
    args['bodyclass'] = 'geography vcard'
    args['bodystyle'] = 'width:23em'
    args['headerstyle'] = 'text-align:left'
    args['abovestyle'] = 'font-size:1.25em; white-space:nowrap'
    -- build the names, type, and transliterations subbox
    local sargs = {}
    local scount = 0
    sargs['child'] = 'yes'
    if( (isnotempty(oargs['name']) or isnotempty(oargs['official_name']) ) and
    	(isnotempty(oargs['settlement_type']) or isnotempty(oargs['type']) ) ) then
	    sargs['subheaderstyle'] = 'background-color:#cddeff; font-weight:bold;'
    	sargs['subheader'] = '<span class="category">'
    		.. firstnonempty(oargs['settlement_type'], oargs['type']) .. '</span>'
    	scount = scount + 1
    end
    if( isnotempty(oargs['name']) and isnotempty(oargs['official_name']) ) then
    	sargs['rowclass1'] = 'mergedtoprow'
    	sargs['header1'] = oargs['official_name']
    	scount = scount + 1
    end
    if( isnotempty(oargs['translit_lang1']) ) then
    	local targs = translitlangbox(
    		{oargs['translit_lang1_type'],
    		 oargs['translit_lang1_type1'],
    		 oargs['translit_lang1_type2'],
    		 oargs['translit_lang1_type3'],
    		 oargs['translit_lang1_type4'],
    		 oargs['translit_lang1_type5'],
    		 oargs['translit_lang1_type6']},
    		{oargs['translit_lang1_info'],
    		 oargs['translit_lang1_info1'],
    		 oargs['translit_lang1_info2'],
    		 oargs['translit_lang1_info3'],
    		 oargs['translit_lang1_info4'],
    		 oargs['translit_lang1_info5'],
    		 oargs['translit_lang1_info6']})
    	if( targs ) then
    		sargs['rowclass2'] = 'mergedtoprow'
    		sargs['header2'] = oargs['translit_lang1'] .. '&nbsp;transcription(s)' .. infobox_module._infobox(targs)
	    	scount = scount + 1
    	end
    end
    if( isnotempty(oargs['translit_lang2']) ) then
    	local targs = translitlangbox(
    		{oargs['translit_lang2_type'],
    		 oargs['translit_lang2_type1'],
    		 oargs['translit_lang2_type2'],
    		 oargs['translit_lang2_type3'],
    		 oargs['translit_lang2_type4'],
    		 oargs['translit_lang2_type5'],
    		 oargs['translit_lang2_type6']},
    		{oargs['translit_lang2_info'],
    		 oargs['translit_lang2_info1'],
    		 oargs['translit_lang2_info2'],
    		 oargs['translit_lang2_info3'],
    		 oargs['translit_lang2_info4'],
    		 oargs['translit_lang2_info5'],
    		 oargs['translit_lang2_info6']})
    	if( targs ) then
    		sargs['rowclass3'] = 'mergedtoprow'
    		sargs['header3'] = oargs['translit_lang2'] .. '&nbsp;transcription(s)' .. infobox_module._infobox(targs)
	    	scount = scount + 1
    	end
	end
    -- End of names, type, and transliterations

    args['above'] =  '<span class="fn org">' .. pname .. '</span>'
    if(isnotempty(oargs['native_name'])) then
    	args['above'] = args['above'] .. '<br /><span class="nickname"'
    		.. isnotempty(oargs['native_name_lang']) and ' lang="' .. oargs['native_name_lang'] .. '">'
    		.. oargs['native_name'] .. '</span>'
    end
    if(isnotempty(oargs['other_name'])) then
    	args['above'] = args['above'] .. '<br /><span class="nickname" style="font-size:78%">'
    		.. oargs['other_name'] .. '</span>'
    end
   	if( scount > 0 ) then
	    args['above'] = args['above'] .. infobox_module._infobox(sargs)
	end
	-- Skyline image
	if( isnotempty(oargs['image_skyline']) ) then
		args['imagestyle'] = 'padding:0.7em 0.8em'
		args['image'] = image_module._InfoboxImage(
			{['image'] = oargs['image_skyline'], 
			 ['size'] = oargs['imagesize'], 
			 ['sizedefault'] = '250px', 
			 ['alt'] = oargs['image_alt'],
			 ['title'] = oargs['image_caption'] or ('Hình nền trời của ' .. pname)
			 })
		if( isnotempty(oargs['image_caption']) ) then
			args['image'] = args['image'] .. '<br /><small>' .. oargs['image_caption'] .. '</small>'
		end
	end
	-- Other image
	if( isnotempty(oargs['image']) ) then
		args['image2'] = oargs['image']
	end
	-- Primary map
	local image_map = nil
	if( isnotempty(oargs['image_map']) ) then
		local msize = narrowmap and '100px' or '250px'
		
		image_map = image_module._InfoboxImage({
			['image'] = oargs['image_map'],
			['size'] = oargs['mapsize'],
			['sizedefault'] = msize,
			['alt'] = oargs['map_alt'],
			['title'] = oargs['map_caption'] or 'Vị trí của ' .. pname,
			})
		if( isnotempty(oargs['map_caption'])) then
			image_map = image_map .. '<br /><small>' .. oargs['map_caption'] .. '</small>'
		end
	end
	-- Primary pushpin map
	local pushpin_map = nil
	if( isnotempty(oargs['pushpin_map']) and isnotempty(oargs['latd']) and isnotempty(oargs['longd'])) then
		local plabel
		if(oargs['pushpin_label_position'] and oargs['pushpin_label_position']:lower() == 'none') then
			plabel = ''
		else if( isnotempty(oargs['pushpin_label']) ) then
			plabel = oargs['pushpin_label']
		else
			plabel = pname
		end
		local pwidth = ''
		if(narrowmap) then
			pwidth = firstnonempty(oargs['pushpin_mapsize'], '150')
		end
		local lat, lat_deg, lat_min, lat_sec
		if(isnotempty(oargs['latm']) or isnotempty(oargs['latNS'])) then
			lat = ''
			lat_deg = oargs['latd'] or ''
			lat_min = oargs['latm'] or ''
			lat_sec = oargs['lats'] or ''
		else
			lat = oargs['latd'] or ''
		end
		local long, lon_deg, lon_min, lon_sec
		if(isnotempty(oargs['longm']) or isnotempty(oargs['longEW'])) then
			long = ''
			lon_deg = oargs['longd'] or ''
			lon_min = oargs['longm'] or ''
			lon_sec = oargs['longs'] or ''
		else
			long = oargs['longd'] or ''
		end
		local caption = firstnonempty({oargs['pushpin_map_caption'], oargs['map_caption']})
		pushpin_map = '<center>' .. locationmap_module._main({
			['1'] = oargs['pushpin_map'],
			['border'] = 'none',
			['alt'] = oargs['pushpin_map_alt'],
			['caption'] = '',
			['float'] = 'none',
			['width'] = pwidth,
			['default_width'] = '250',
			['relief'] = oargs['pushpin_relief'],
			['AlternativeMap'] = oargs['pushpin_image'],
			['label'] = plabel,
			['lat'] = lat, ['long'] = long,
			['lat_deg'] = lat_deg, ['lat_min'] = lat_min, ['lat_sec'] = lat_sec,
			['lat_dir'] = oargs['latNS'] or '',
			['lon_deg'] = lon_deg, ['lon_min'] = lon_min, ['lon_sec'] = lon_sec,
			['lon_dir'] = oargs['lonEW'] or '',
			['marksize'] = '6',
			['position'] = oargs['pushpin_label_position'] or ''
			})
		if( caption ) then
			pushpin_map = pushpin_map .. '<small>' .. caption .. '</small>'
		end
			pushpin_map = pushpin_map .. '</center>'
		end
	end
	-- Flag, Seal, Shield and Coat of arms
	if( isnotempty(oargs['image_flag']) or isnotempty(oargs['image_seal']) 
		or isnotempty(oargs['image_shield']) or isnotempty(oargs['image_blank_emblem'])
		or narrowmap) then
		args['rowclass1'] = 'mergedtoprow'
		args['class1'] = 'maptable'
		local isize = (isnotempty(oargs['pushpin_map_narrow']) and isnotempty(oargs['pushpin_map'])) and '85px' or '100px'
		local targs = {}
		if( isnotempty(oargs['image_flag']) ) then
			local iborder = yesno(oargs['flag_border'], 'yes', '', 'yes', 'yes')
			targs[1] = image_module._InfoboxImage({
				['image'] = oargs['image_flag'],
				['size'] = oargs['flag_size'],
				['sizedefault'] = isize,
				['border'] = iborder,
				['alt'] = oargs['flag_alt'],
				['title'] = 'Hiệu kỳ của ' .. pname,
				}) .. '<br /><small>\'\'\'' .. link(oargs['flag_link'], 'Flag', oargs['official_name']) .. '\'\'\'</small>'
		end
		if( isnotempty(oargs['image_seal']) ) then
			targs[2] = image_module._InfoboxImage({
				['image'] = oargs['image_seal'],
				['size'] = oargs['seal_size'],
				['sizedefault'] = isize,
				['alt'] = oargs['seal_alt'],
				['title'] = 'Con dấu chính thức của ' .. pname,
				}) .. '<br /><small>\'\'\'' .. link(oargs['seal_link'], firstnonempty(oargs['seal_type'], 'Seal'), oargs['official_name']) .. '\'\'\'</small>'
		end
		if( isnotempty(oargs['image_shield']) ) then
			targs[3] = image_module._InfoboxImage({
				['image'] = oargs['image_shield'],
				['size'] = oargs['shield_size'],
				['sizedefault'] = isize,
				['alt'] = oargs['shield_alt'],
				['title'] = 'Huy hiệu của ' .. pname,
				}) .. '<br /><small>\'\'\'' .. link(oargs['shield_link'], 'Coat of arms', oargs['official_name']) .. '\'\'\'</small>'
		end
		if( isnotempty(oargs['image_blank_emblem']) ) then
			targs[4] = image_module._InfoboxImage({
				['image'] = oargs['image_blank_emblem'],
				['size'] = oargs['blank_emblem_size'],
				['sizedefault'] = isize,
				['alt'] = oargs['blank_emblem_alt'],
				['title'] = 'Biểu trưng chính thức của ' .. pname,
				}) .. '<br /><small>\'\'\'' .. link(oargs['blank_emblem_link'], firstnonempty(oargs['blank_emblem_type'], 'Logo'), oargs['official_name']) .. '\'\'\'</small>'
		end
		targs[5] = image_map
		if( narrowmap and pushpin_map) then
			args['data1'] = columns2(pushpin_map, targs[1], targs[2], targs[3], targs[4], targs[5])
		else
			args['data1'] = columns(targs[1], targs[2], targs[3], targs[4])
		end
	end
	-- Nickname
	if( isnotempty(oargs['nickname']) ) then
		args['rowclass2'] = 'mergedrow'
		args['data2'] = 'Tên hiệu: <span class="nickname">' .. oargs['nickname'] .. '</span>'
	end
	-- Motto
	if( isnotempty(oargs['motto']) ) then
		args['rowclass3'] = 'mergedrow'
		args['data3'] = 'Khẩu hiệu: ' .. oargs['motto']
	end
	-- Anthem
	if( isnotempty(oargs['anthem']) ) then
		args['rowclass4'] = 'mergedrow'
		args['data4'] = 'Nhạc hiệu: ' .. oargs['anthem']
	end
	-- Map
	if( narrowmap  and image_map ) then
		args['rowclass5'] = mergedrow
		args['data5'] = image_map
	end
	if( isnotempty(oargs['image_map1']) ) then
		args['rowclass6'] = mergedrow
		args['data6'] = image_module._InfoboxImage({
			['image'] = oargs['image_map1'],
			['size'] = oargs['mapsize1'],
			['sizedefault'] = '250px',
			['alt'] = oargs['map_alt1'],
			['title'] = oargs['map_caption1'] or 'Vị trí của ' .. pname,
			})
		if( isnotempty(oargs['map_caption1'])) then
			image_map = image_map .. '<br /><small>' .. oargs['map_caption1'] .. '</small>'
		end
	end
	-- Dot map
	if( isnotempty(oargs['image_dot_map']) ) then
		args['rowclass7'] = 'mergedrow'
		args['data7'] = '<center>Bản đồ vị trí không hỗ trợ!</center>'
	end
	
    -- Pushpin map
	if( narrowmap == nil and pushpin_map) then
		args['rowclass8'] = 'mergedtoprow'
		args['data8'] = pushpin_map
	end

    return infobox_module._infobox(args)
 
end

local function _superdivisions(entity, divisions, i)
	if not divisions[i] then divisions[i] = {} end
	if divisions[i][entity.id:sub(2)] then return end
	divisions[i][entity.id:sub(2)] = true
	local claims = entity.claims.P131
	for j, claim in ipairs(claims or {}) do
		local superentity = mw.wikibase.getEntity("Q" .. claim.mainsnak.datavalue.value["numeric-id"])
		_superdivisions(superentity, divisions, i + 1)
	end
end

function p.subdivisions(frame)
	local entity = mw.wikibase.getEntityObject()
	if not entity then return nil end
	
	local edit_button = frame:expandTemplate{
		title = "Sửa dữ liệu",
		args = {
			p = 131,
		},
	}
	
	local divisions = {}
	_superdivisions(entity, divisions, 1)
	local rows = {}
	for i = #divisions, 2, -1 do
		local row = {}
		for id, _ in pairs(divisions[i]) do
			table.insert(row, entity_link(id))
		end
		table.insert(rows, "* " .. table.concat(row, ", "))
	end
	
	return frame:expandTemplate{
		title = "Liệt kê",
		args = {
			table.concat(rows, "\n"),
		},
	} .. " " .. edit_button
end
 
return p