پرش به محتوا

پودمان:AlborzInfoBox: تفاوت میان نسخه‌ها

از ویکی البرز
جزبدون خلاصۀ ویرایش
جزبدون خلاصۀ ویرایش
 
(۶ نسخهٔ میانیِ ایجادشده توسط همین کاربر نشان داده نشد)
خط ۱: خط ۱:
local p = {}
local p = {}
local data = mw.loadData('Module:AlborzInfoBox/Data/Geography')


-------------------------------------------------------
------------------------------------------------------------------------
-- NORMALIZE (improved for Persian)
-- DATA
-------------------------------------------------------
------------------------------------------------------------------------
local function norm(s)
 
    if not s then return nil end
local data = mw.loadData("Module:AlborzInfoBox/Data/Geography")
    s = mw.text.trim(s)
 
    s = mw.ustring.gsub(s, "[\u200C\u200D]", "")
------------------------------------------------------------------------
    s = mw.ustring.lower(s) -- برای اطمینان بیشتر
-- NORMALIZE
    return mw.text.trim(s)
------------------------------------------------------------------------
 
local function norm(value)
 
if value == nil then
return nil
end
 
value = tostring(value)
 
value = mw.text.trim(value)
 
-- حذف نیم‌فاصله و کاراکترهای نامرئی
value = mw.ustring.gsub(value, "[\u200C\u200D\uFEFF]", "")
 
-- یکسان‌سازی فاصله‌ها
value = mw.ustring.gsub(value, "%s+", " ")
 
-- حروف عربی → فارسی
value = mw.ustring.gsub(value, "ي", "ی")
value = mw.ustring.gsub(value, "ى", "ی")
value = mw.ustring.gsub(value, "ك", "ک")
 
return mw.text.trim(value)
 
end
end


-------------------------------------------------------
 
-- ARG HANDLING
------------------------------------------------------------------------
-------------------------------------------------------
-- ARGUMENT HANDLING
------------------------------------------------------------------------
 
local function collectArgs(frame)
local function collectArgs(frame)
    local args = {}
 
    local function push(tbl)
local args = {}
        if not tbl then return end
 
        for k, v in pairs(tbl) do
local function push(source)
            if v and v ~= "" then
 
                args[norm(k)] = mw.text.trim(v)
if not source then
            end
return
        end
end
    end
 
    push(frame.args)
for key, value in pairs(source) do
    push(frame:getParent() and frame:getParent().args)
 
    return args
if value ~= nil then
 
local normalizedKey = norm(key)
local normalizedValue = mw.text.trim(tostring(value))
 
if normalizedKey ~= "" and normalizedValue ~= "" then
args[normalizedKey] = normalizedValue
end
 
end
 
    end
 
end
 
-- آرگومان‌های مستقیم invoke
push(frame.args)
 
-- آرگومان‌های الگوی مادر
local parent = frame:getParent()
 
if parent then
push(parent.args)
end
 
return args
 
end
end


-------------------------------------------------------
 
-- GET FIELD VALUE
------------------------------------------------------------------------
-------------------------------------------------------
-- FIELD VALUE
------------------------------------------------------------------------
 
local function getValue(args, field)
local function getValue(args, field)
    if not field then return nil end
 
    local names = {}
if not field then
    if field.arg then table.insert(names, field.arg) end
return nil
    if field.args then
end
        for _, v in ipairs(field.args) do
 
            table.insert(names, v)
local names = {}
        end
 
    end
-- نام اصلی
    for _, n in ipairs(names) do
if field.arg then
        local v = args[norm(n)]
table.insert(names, field.arg)
        if v and v ~= "" then
end
            return v
 
        end
-- نام‌های جایگزین
    end
if field.args then
    return field.default
 
for _, name in ipairs(field.args) do
table.insert(names, name)
end
 
end
 
-- جست‌وجوی مقدار
for _, name in ipairs(names) do
 
local key = norm(name)
local value = args[key]
 
if value and value ~= "" then
return value
end
 
end
 
-- مقدار پیش‌فرض
if field.default ~= nil then
return field.default
end
 
return nil
 
end
end


-------------------------------------------------------
 
-- ROW RENDERER
------------------------------------------------------------------------
-------------------------------------------------------
-- SAFE WIKITEXT
------------------------------------------------------------------------
 
local function cleanText(value)
 
if value == nil then
return nil
end
 
value = tostring(value)
 
value = mw.text.trim(value)
 
if value == "" then
return nil
end
 
return value
 
end
 
 
------------------------------------------------------------------------
-- ADD ROW
------------------------------------------------------------------------
 
local function addRow(root, label, value, raw)
local function addRow(root, label, value, raw)
    if not value or value == "" then return end
 
    local tr = root:tag("tr")
value = cleanText(value)
    tr:tag("th"):wikitext(label)
 
    local td = tr:tag("td")
if not value then
    if raw then
return
        td:wikitext(value)
end
    else
 
        td:wikitext(mw.text.encode(value))
local tr = root:tag("tr")
    end
 
-- Label
local th = tr:tag("th")
 
th
:attr("scope", "row")
:addClass("infobox-label")
:wikitext(label)
 
-- Value
local td = tr:tag("td")
 
td:addClass("infobox-value")
 
if raw then
td:wikitext(value)
else
td:wikitext(mw.text.encode(value))
end
 
end
end


-------------------------------------------------------
 
-- BADGE (بهبود یافته)
------------------------------------------------------------------------
-------------------------------------------------------
-- TYPE BADGE
------------------------------------------------------------------------
 
local badgeMap = {
local badgeMap = {
    city = "شهر",
 
    village = "روستا",
city         = "شهر",
    county = "شهرستان",
village       = "روستا",
    district = "بخش",
county       = "شهرستان",
    ruraldistrict = "دهستان",
district     = "بخش",
     mountain = "کوه",
ruraldistrict = "دهستان",
    cave = "غار",
 
    river = "رودخانه",
neighborhood  = "محله",
     waterfall = "آبشار",
urbanarea     = "منطقه شهری",
    park = "پارک",
 
    historic = "بنای تاریخی"
mountain     = "کوه",
cave         = "غار",
river         = "رودخانه",
waterfall     = "آبشار",
park         = "پارک",
historic     = "بنای تاریخی"
 
}
}


local function addBadge(root, t)
 
    local label = badgeMap[t] or "مکان"
local function addBadge(root, typeKey)
    root:tag("tr")
 
        :tag("td")
local label = badgeMap[typeKey]
            :attr("colspan", "2")
 
            :css({
-- اگر نوع ناشناخته باشد
                ["text-align"] = "center",
if not label then
                ["padding"] = "6px 0 8px 0"
label = "مکان"
            })
end
            :tag("span")
 
                :css({
local tr = root:tag("tr")
                    display = "inline-block",
 
                    background = "#e8f0f8",
local td = tr:tag("td")
                    color = "#1e4b7a",
 
                    ["font-size"] = "87%",
td
                    ["font-weight"] = "bold",
:attr("colspan", "2")
                    padding = "3px 12px",
:addClass("infobox-badge-row")
                    ["border-radius"] = "5px",
 
                    border = "1px solid #b0c8e0"
local span = td:tag("span")
                })
 
                :wikitext(label)
span
:addClass("infobox-badge")
:wikitext(label)
 
end
end


-------------------------------------------------------
 
-- IMAGE, CAPTION, TITLE (بدون تغییر)
------------------------------------------------------------------------
-------------------------------------------------------
-- IMAGE
local function isSimpleFilename(s)
------------------------------------------------------------------------
    if not s or s == "" then return false end
 
    s = mw.text.trim(s)
local function isSimpleFilename(value)
    return not (
 
        s:find("{", 1, true) or
if not value or value == "" then
        s:find("<", 1, true) or
return false
        s:find("|", 1, true) or
end
        s:find("[[", 1, true)
 
    )
value = mw.text.trim(value)
 
-- اگر خود مقدار شامل ویکی‌متن یا HTML باشد
if value:find("{", 1, true)
or value:find("<", 1, true)
or value:find("|", 1, true)
or value:find("[[", 1, true)
then
return false
end
 
return true
 
end
end
------------------------------------------------------------------------
-- IMAGE RENDERER
------------------------------------------------------------------------


local function addImage(root, args)
local function addImage(root, args)
    local content = args[norm("تصویر")] or args[norm("پرونده")] or args[norm("image")]
 
    if not content or content == "" then return end
local content =
    content = mw.text.trim(content)
args[norm("تصویر")]
    if isSimpleFilename(content) then
or args[norm("پرونده")]
        local filename = mw.ustring.gsub(content, "^[Ff]ile:", "")
or args[norm("image")]
        filename = mw.ustring.gsub(filename, "^پرونده:", "")
 
        root:tag("tr")
if not content or content == "" then
            :tag("td")
return
                :attr("colspan", "2")
end
                :css("text-align", "center")
 
                :css("padding", "4px 0 6px 0")
content = mw.text.trim(content)
                :wikitext("[[File:" .. filename .. "|250px]]")
 
    else
local tr = root:tag("tr")
        root:tag("tr")
 
            :tag("td")
local td = tr:tag("td")
                :attr("colspan", "2")
 
                :css("text-align", "center")
td
                :css("padding", "4px 0 6px 0")
:attr("colspan", "2")
                :wikitext(content)
:addClass("infobox-image")
    end
 
if isSimpleFilename(content) then
 
local filename = content
 
filename = mw.ustring.gsub(
filename,
"^[Ff]ile:",
""
)
 
filename = mw.ustring.gsub(
filename,
"^پرونده:",
""
)
 
td:wikitext(
"[[File:" .. filename .. "|250px]]"
)
 
else
 
td:wikitext(content)
 
end
 
end
end
------------------------------------------------------------------------
-- IMAGE CAPTION
------------------------------------------------------------------------


local function addCaption(root, args)
local function addCaption(root, args)
    local cap = args[norm("توضیح تصویر")] or args[norm("عنوان تصویر")] or args[norm("زیرنویس")]
 
    if not cap or cap == "" then return end
local caption =
    root:tag("tr")
args[norm("توضیح تصویر")]
        :tag("td")
or args[norm("عنوان تصویر")]
            :attr("colspan", "2")
or args[norm("زیرنویس")]
            :css({ ["text-align"] = "center", ["font-size"] = "90%", padding = "3px 0 8px 0" })
or args[norm("برچسب تصویر")]
            :wikitext(mw.text.encode(cap))
 
if not caption or caption == "" then
return
end
 
local tr = root:tag("tr")
 
local td = tr:tag("td")
 
td
:attr("colspan", "2")
:addClass("infobox-caption")
:wikitext(mw.text.encode(caption))
 
end
 
 
------------------------------------------------------------------------
-- TITLE
------------------------------------------------------------------------
 
local function getTitle(args)
 
return
args[norm("نام‌رسمی")]
or args[norm("نام رسمی")]
or args[norm("نام")]
or mw.title.getCurrentTitle().text
 
end
end


local function addTitle(root, args)
local function addTitle(root, args)
    local title = args[norm("نام")] or args[norm("نام رسمی")] or mw.title.getCurrentTitle().text
 
    root:tag("tr")
local title = getTitle(args)
        :tag("th")
 
            :attr("colspan", "2")
if not title or title == "" then
            :css({ ["text-align"] = "center", ["font-size"] = "118%", padding = "8px 4px" })
return
            :wikitext(mw.text.encode(title))
end
 
local tr = root:tag("tr")
 
local th = tr:tag("th")
 
th
:attr("colspan", "2")
:addClass("infobox-title")
:wikitext(mw.text.encode(title))
 
end
end


-------------------------------------------------------
 
------------------------------------------------------------------------
-- TABLE
-- TABLE
-------------------------------------------------------
------------------------------------------------------------------------
 
local function newTable(typeKey)
local function newTable(typeKey)
    return mw.html.create("table")
 
        :addClass("infobox")
local root = mw.html.create("table")
        :addClass("alborz-infobox")
 
        :addClass("infobox-type-" .. typeKey)
root
:addClass("infobox")
:addClass("alborz-infobox")
:addClass("infobox-geography")
:addClass("infobox-type-" .. typeKey)
:attr("role", "presentation")
 
return root
 
end
end


-------------------------------------------------------
 
-- TYPE NORMALIZATION (بهبود یافته)
------------------------------------------------------------------------
-------------------------------------------------------
-- TYPE ALIASES
------------------------------------------------------------------------
 
local typeAliases = {
local typeAliases = {
    ["شهرستان"] = "county",
 
    ["شهر"] = "city",
["شهرستان"]       = "county",
    ["روستا"] = "village",
["county"]        = "county",
    ["بخش"] = "district",
 
    ["دهستان"] = "ruraldistrict",
["شهر"]           = "city",
     ["کوه"] = "mountain",
["city"]            = "city",
    ["غار"] = "cave",
 
    ["رودخانه"] = "river",
["روستا"]         = "village",
    ["آبشار"] = "waterfall",
["village"]        = "village",
    ["پارک"] = "park",
 
    ["بنای تاریخی"] = "historic"
["بخش"]           = "district",
["district"]      = "district",
 
["دهستان"]         = "ruraldistrict",
["ruraldistrict"]  = "ruraldistrict",
 
["محله"]          = "neighborhood",
["neighborhood"]  = "neighborhood",
 
["منطقه شهری"]    = "urbanarea",
["منطقه‌شهری"]     = "urbanarea",
["منطقه شهری کرج"] = "urbanarea",
["منطقه"]          = "urbanarea",
["urbanarea"]      = "urbanarea",
["urban area"]    = "urbanarea",
 
["کوه"]           = "mountain",
["mountain"]      = "mountain",
 
["غار"]           = "cave",
["cave"]          = "cave",
 
["رودخانه"]       = "river",
["river"]          = "river",
 
["آبشار"]         = "waterfall",
["waterfall"]      = "waterfall",
 
["پارک"]           = "park",
["park"]          = "park",
 
["بنای تاریخی"]   = "historic",
["بنای‌تاریخی"]    = "historic",
["historic"]      = "historic"
 
}
}


local function resolveType(t)
 
    if not t then return "city" end
------------------------------------------------------------------------
    t = norm(t)
-- TYPE RESOLUTION
    return typeAliases[t] or t
------------------------------------------------------------------------
 
local function resolveType(value)
 
-- اگر نوع مشخص نشده باشد
if not value or value == "" then
return "city"
end
 
value = norm(value)
 
-- ابتدا از aliasهای ماژول استفاده می‌کنیم
local resolved = typeAliases[value]
 
if resolved then
return resolved
end
 
-- سپس از typeMap موجود در Data
if data.typeMap then
 
resolved = data.typeMap[value]
 
if resolved then
return resolved
end
 
end
 
-- در نهایت خود مقدار
return value
 
end
end


-------------------------------------------------------
 
------------------------------------------------------------------------
-- LAYOUT
------------------------------------------------------------------------
 
local function getLayout(typeKey)
 
if not data or not data.layouts then
return {}
end
 
return
data.layouts[typeKey]
or data.layouts.city
or {}
 
end
 
 
------------------------------------------------------------------------
-- RENDER ROWS
-- RENDER ROWS
-------------------------------------------------------
------------------------------------------------------------------------
 
local function renderRows(root, args, layout)
local function renderRows(root, args, layout)
    for _, key in ipairs(layout) do
 
        local field = data.fields[key]
if not layout then
        if field then
return
            local value = getValue(args, field)
end
            if value and value ~= "" then
 
                addRow(root, field.label, value, field.raw)
for _, key in ipairs(layout) do
            end
 
        end
local field = data.fields[key]
    end
 
if field then
 
local value = getValue(args, field)
 
if value and value ~= "" then
 
addRow(
root,
field.label,
value,
field.raw
)
 
end
 
end
 
end
 
end
end


-------------------------------------------------------
 
------------------------------------------------------------------------
-- MAIN RENDER
-- MAIN RENDER
-------------------------------------------------------
------------------------------------------------------------------------
 
local function render(args)
local function render(args)
    if not data or not data.fields then
 
        return "Infobox data not found"
if not data then
    end
return "Infobox data not found"
   
end
    local typeKey = resolveType(args["نوع"])
 
    local internalType = (data.typeMap and data.typeMap[typeKey]) or typeKey
if not data.fields then
   
return "Infobox fields not found"
    local layout = (data.layouts and data.layouts[internalType]) or (data.layouts and data.layouts.city) or {}
end
   
 
    local root = newTable(internalType)
----------------------------------------------------------------
    addTitle(root, args)
-- Resolve type
    addBadge(root, internalType)
----------------------------------------------------------------
    addImage(root, args)
 
    addCaption(root, args)
local requestedType =
    renderRows(root, args, layout)
args[norm("نوع")]
   
or args[norm("type")]
    return tostring(root)
 
local typeKey =
resolveType(requestedType)
 
----------------------------------------------------------------
-- Data type map
----------------------------------------------------------------
 
local internalType = typeKey
 
if data.typeMap and data.typeMap[typeKey] then
internalType = data.typeMap[typeKey]
end
 
----------------------------------------------------------------
-- اطمینان از وجود layout
----------------------------------------------------------------
 
if not data.layouts[internalType] then
 
-- اگر نوع ناشناخته بود، شهر را پیش‌فرض نکنیم
-- بلکه از نوع مشخص‌شده استفاده کنیم.
-- در صورت نبود layout، از layout شهر استفاده می‌شود.
internalType = typeKey
 
end
 
----------------------------------------------------------------
-- Layout
----------------------------------------------------------------
 
local layout =
getLayout(internalType)
 
----------------------------------------------------------------
-- Table
----------------------------------------------------------------
 
local root =
newTable(internalType)
 
----------------------------------------------------------------
-- Title
----------------------------------------------------------------
 
addTitle(root, args)
 
----------------------------------------------------------------
-- Type badge
----------------------------------------------------------------
 
addBadge(root, internalType)
 
----------------------------------------------------------------
-- Image
----------------------------------------------------------------
 
addImage(root, args)
 
----------------------------------------------------------------
-- Caption
----------------------------------------------------------------
 
addCaption(root, args)
 
----------------------------------------------------------------
-- Rows
----------------------------------------------------------------
 
renderRows(
root,
args,
layout
)
 
return tostring(root)
 
end
end


-------------------------------------------------------
 
-- EXPORT
------------------------------------------------------------------------
-------------------------------------------------------
-- PUBLIC ENTRY POINT
------------------------------------------------------------------------
 
function p.main(frame)
function p.main(frame)
    return render(collectArgs(frame))
 
local args =
collectArgs(frame)
 
return render(args)
 
end
end
------------------------------------------------------------------------
-- RETURN
------------------------------------------------------------------------


return p
return p

نسخهٔ کنونی تا ‏۷ اوت ۲۰۲۶، ساعت ۲۲:۵۶

توضیحات این پودمان می‌تواند در پودمان:AlborzInfoBox/توضیحات قرار گیرد.

local p = {}

------------------------------------------------------------------------
-- DATA
------------------------------------------------------------------------

local data = mw.loadData("Module:AlborzInfoBox/Data/Geography")

------------------------------------------------------------------------
-- NORMALIZE
------------------------------------------------------------------------

local function norm(value)

	if value == nil then
		return nil
	end

	value = tostring(value)

	value = mw.text.trim(value)

	-- حذف نیم‌فاصله و کاراکترهای نامرئی
	value = mw.ustring.gsub(value, "[\u200C\u200D\uFEFF]", "")

	-- یکسان‌سازی فاصله‌ها
	value = mw.ustring.gsub(value, "%s+", " ")

	-- حروف عربی → فارسی
	value = mw.ustring.gsub(value, "ي", "ی")
	value = mw.ustring.gsub(value, "ى", "ی")
	value = mw.ustring.gsub(value, "ك", "ک")

	return mw.text.trim(value)

end


------------------------------------------------------------------------
-- ARGUMENT HANDLING
------------------------------------------------------------------------

local function collectArgs(frame)

	local args = {}

	local function push(source)

		if not source then
			return
		end

		for key, value in pairs(source) do

			if value ~= nil then

				local normalizedKey = norm(key)
				local normalizedValue = mw.text.trim(tostring(value))

				if normalizedKey ~= "" and normalizedValue ~= "" then
					args[normalizedKey] = normalizedValue
				end

			end

	    	end

	end

	-- آرگومان‌های مستقیم invoke
	push(frame.args)

	-- آرگومان‌های الگوی مادر
	local parent = frame:getParent()

	if parent then
		push(parent.args)
	end

	return args

end


------------------------------------------------------------------------
-- FIELD VALUE
------------------------------------------------------------------------

local function getValue(args, field)

	if not field then
		return nil
	end

	local names = {}

	-- نام اصلی
	if field.arg then
		table.insert(names, field.arg)
	end

	-- نام‌های جایگزین
	if field.args then

		for _, name in ipairs(field.args) do
			table.insert(names, name)
		end

	end

	-- جست‌وجوی مقدار
	for _, name in ipairs(names) do

		local key = norm(name)
		local value = args[key]

		if value and value ~= "" then
			return value
		end

	end

	-- مقدار پیش‌فرض
	if field.default ~= nil then
		return field.default
	end

	return nil

end


------------------------------------------------------------------------
-- SAFE WIKITEXT
------------------------------------------------------------------------

local function cleanText(value)

	if value == nil then
		return nil
	end

	value = tostring(value)

	value = mw.text.trim(value)

	if value == "" then
		return nil
	end

	return value

end


------------------------------------------------------------------------
-- ADD ROW
------------------------------------------------------------------------

local function addRow(root, label, value, raw)

	value = cleanText(value)

	if not value then
		return
	end

	local tr = root:tag("tr")

	-- Label
	local th = tr:tag("th")

	th
		:attr("scope", "row")
		:addClass("infobox-label")
		:wikitext(label)

	-- Value
	local td = tr:tag("td")

	td:addClass("infobox-value")

	if raw then
		td:wikitext(value)
	else
		td:wikitext(mw.text.encode(value))
	end

end


------------------------------------------------------------------------
-- TYPE BADGE
------------------------------------------------------------------------

local badgeMap = {

	city          = "شهر",
	village       = "روستا",
	county        = "شهرستان",
	district      = "بخش",
	ruraldistrict = "دهستان",

	neighborhood  = "محله",
	urbanarea     = "منطقه شهری",

	mountain      = "کوه",
	cave          = "غار",
	river         = "رودخانه",
	waterfall     = "آبشار",
	park          = "پارک",
	historic      = "بنای تاریخی"

}


local function addBadge(root, typeKey)

	local label = badgeMap[typeKey]

	-- اگر نوع ناشناخته باشد
	if not label then
		label = "مکان"
	end

	local tr = root:tag("tr")

	local td = tr:tag("td")

	td
		:attr("colspan", "2")
		:addClass("infobox-badge-row")

	local span = td:tag("span")

	span
		:addClass("infobox-badge")
		:wikitext(label)

end


------------------------------------------------------------------------
-- IMAGE
------------------------------------------------------------------------

local function isSimpleFilename(value)

	if not value or value == "" then
		return false
	end

	value = mw.text.trim(value)

	-- اگر خود مقدار شامل ویکی‌متن یا HTML باشد
	if value:find("{", 1, true)
		or value:find("<", 1, true)
		or value:find("|", 1, true)
		or value:find("[[", 1, true)
	then
		return false
	end

	return true

end


------------------------------------------------------------------------
-- IMAGE RENDERER
------------------------------------------------------------------------

local function addImage(root, args)

	local content =
		args[norm("تصویر")]
		or args[norm("پرونده")]
		or args[norm("image")]

	if not content or content == "" then
		return
	end

	content = mw.text.trim(content)

	local tr = root:tag("tr")

	local td = tr:tag("td")

	td
		:attr("colspan", "2")
		:addClass("infobox-image")

	if isSimpleFilename(content) then

		local filename = content

		filename = mw.ustring.gsub(
			filename,
			"^[Ff]ile:",
			""
		)

		filename = mw.ustring.gsub(
			filename,
			"^پرونده:",
			""
		)

		td:wikitext(
			"[[File:" .. filename .. "|250px]]"
		)

	else

		td:wikitext(content)

	end

end


------------------------------------------------------------------------
-- IMAGE CAPTION
------------------------------------------------------------------------

local function addCaption(root, args)

	local caption =
		args[norm("توضیح تصویر")]
		or args[norm("عنوان تصویر")]
		or args[norm("زیرنویس")]
		or args[norm("برچسب تصویر")]

	if not caption or caption == "" then
		return
	end

	local tr = root:tag("tr")

	local td = tr:tag("td")

	td
		:attr("colspan", "2")
		:addClass("infobox-caption")
		:wikitext(mw.text.encode(caption))

end


------------------------------------------------------------------------
-- TITLE
------------------------------------------------------------------------

local function getTitle(args)

	return
		args[norm("نام‌رسمی")]
		or args[norm("نام رسمی")]
		or args[norm("نام")]
		or mw.title.getCurrentTitle().text

end


local function addTitle(root, args)

	local title = getTitle(args)

	if not title or title == "" then
		return
	end

	local tr = root:tag("tr")

	local th = tr:tag("th")

	th
		:attr("colspan", "2")
		:addClass("infobox-title")
		:wikitext(mw.text.encode(title))

end


------------------------------------------------------------------------
-- TABLE
------------------------------------------------------------------------

local function newTable(typeKey)

	local root = mw.html.create("table")

	root
		:addClass("infobox")
		:addClass("alborz-infobox")
		:addClass("infobox-geography")
		:addClass("infobox-type-" .. typeKey)
		:attr("role", "presentation")

	return root

end


------------------------------------------------------------------------
-- TYPE ALIASES
------------------------------------------------------------------------

local typeAliases = {

	["شهرستان"]       = "county",
	["county"]         = "county",

	["شهر"]            = "city",
	["city"]            = "city",

	["روستا"]          = "village",
	["village"]        = "village",

	["بخش"]            = "district",
	["district"]       = "district",

	["دهستان"]         = "ruraldistrict",
	["ruraldistrict"]  = "ruraldistrict",

	["محله"]           = "neighborhood",
	["neighborhood"]   = "neighborhood",

	["منطقه شهری"]     = "urbanarea",
	["منطقه‌شهری"]     = "urbanarea",
	["منطقه شهری کرج"] = "urbanarea",
	["منطقه"]          = "urbanarea",
	["urbanarea"]      = "urbanarea",
	["urban area"]     = "urbanarea",

	["کوه"]            = "mountain",
	["mountain"]       = "mountain",

	["غار"]            = "cave",
	["cave"]           = "cave",

	["رودخانه"]        = "river",
	["river"]          = "river",

	["آبشار"]          = "waterfall",
	["waterfall"]      = "waterfall",

	["پارک"]           = "park",
	["park"]           = "park",

	["بنای تاریخی"]    = "historic",
	["بنای‌تاریخی"]    = "historic",
	["historic"]       = "historic"

}


------------------------------------------------------------------------
-- TYPE RESOLUTION
------------------------------------------------------------------------

local function resolveType(value)

	-- اگر نوع مشخص نشده باشد
	if not value or value == "" then
		return "city"
	end

	value = norm(value)

	-- ابتدا از aliasهای ماژول استفاده می‌کنیم
	local resolved = typeAliases[value]

	if resolved then
		return resolved
	end

	-- سپس از typeMap موجود در Data
	if data.typeMap then

		resolved = data.typeMap[value]

		if resolved then
			return resolved
		end

	end

	-- در نهایت خود مقدار
	return value

end


------------------------------------------------------------------------
-- LAYOUT
------------------------------------------------------------------------

local function getLayout(typeKey)

	if not data or not data.layouts then
		return {}
	end

	return
		data.layouts[typeKey]
		or data.layouts.city
		or {}

end


------------------------------------------------------------------------
-- RENDER ROWS
------------------------------------------------------------------------

local function renderRows(root, args, layout)

	if not layout then
		return
	end

	for _, key in ipairs(layout) do

		local field = data.fields[key]

		if field then

			local value = getValue(args, field)

			if value and value ~= "" then

				addRow(
					root,
					field.label,
					value,
					field.raw
				)

			end

		end

	end

end


------------------------------------------------------------------------
-- MAIN RENDER
------------------------------------------------------------------------

local function render(args)

	if not data then
		return "Infobox data not found"
	end

	if not data.fields then
		return "Infobox fields not found"
	end

	----------------------------------------------------------------
	-- Resolve type
	----------------------------------------------------------------

	local requestedType =
		args[norm("نوع")]
		or args[norm("type")]

	local typeKey =
		resolveType(requestedType)

	----------------------------------------------------------------
	-- Data type map
	----------------------------------------------------------------

	local internalType = typeKey

	if data.typeMap and data.typeMap[typeKey] then
		internalType = data.typeMap[typeKey]
	end

	----------------------------------------------------------------
	-- اطمینان از وجود layout
	----------------------------------------------------------------

	if not data.layouts[internalType] then

		-- اگر نوع ناشناخته بود، شهر را پیش‌فرض نکنیم
		-- بلکه از نوع مشخص‌شده استفاده کنیم.
		-- در صورت نبود layout، از layout شهر استفاده می‌شود.
		internalType = typeKey

	end

	----------------------------------------------------------------
	-- Layout
	----------------------------------------------------------------

	local layout =
		getLayout(internalType)

	----------------------------------------------------------------
	-- Table
	----------------------------------------------------------------

	local root =
		newTable(internalType)

	----------------------------------------------------------------
	-- Title
	----------------------------------------------------------------

	addTitle(root, args)

	----------------------------------------------------------------
	-- Type badge
	----------------------------------------------------------------

	addBadge(root, internalType)

	----------------------------------------------------------------
	-- Image
	----------------------------------------------------------------

	addImage(root, args)

	----------------------------------------------------------------
	-- Caption
	----------------------------------------------------------------

	addCaption(root, args)

	----------------------------------------------------------------
	-- Rows
	----------------------------------------------------------------

	renderRows(
		root,
		args,
		layout
	)

	return tostring(root)

end


------------------------------------------------------------------------
-- PUBLIC ENTRY POINT
------------------------------------------------------------------------

function p.main(frame)

	local args =
		collectArgs(frame)

	return render(args)

end


------------------------------------------------------------------------
-- RETURN
------------------------------------------------------------------------

return p