پرش به محتوا

پودمان:ModernInfobox

توضیحات پودمان[ایجاد] [پاکسازی]
local p = {}

local listStyles = {
    flatlist = { patterns = {'^flatlist$', '%sflatlist$', '^flatlist%s', '%sflatlist%s'}, styles = 'Flatlist/styles.css' }
}

local nonNumericParams = {
    'language', 'زبان',
    'above', 'بالا',
    'heading', 'عنوان',
    'subheader', 'زیرعنوان',
    'description', 'توضیح تصویر',
    'header', 'سربرگ',
    'label', 'برچسب',
    'data', 'اطلاعات',
    'icon', 'آیکون',
    'below', 'پایین',
    'footer', 'فوتر',
    'layout', 'طرح‌بندی'
}

local forceEnglishParams = {
    'width', 'عرض',
    'size', 'اندازه تصویر',
    'id', 'شناسه',
    'unit', 'واحد اندازه',
    'table_background_color', 'رنگ پس زمینه جدول',
    'heading_background_color', 'رنگ پس زمینه عنوان',
    'above_background_color', 'رنگ پس زمینه بالا',
    'subheader_background_color', 'رنگ پس زمینه زیرعنوان',
    'picture_background_color', 'رنگ پس زمینه تصویر',
    'description_picture_background_color', 'رنگ پس زمینه توضیح تصویر',
    'below_background_color', 'رنگ پس زمینه پایین',
    'footer_background_color', 'رنگ پس زمینه فوتر',
    'header_background_color', 'رنگ پس زمینه سربرگ', 
    'label_background_color', 'رنگ پس زمینه برچسب',
    'data_background_color', 'رنگ پس زمینه اطلاعات',
    'custom_class', 'کلاس دلخواه',
    'custom_style', 'استایل دلخواه',
    'table_color', 'رنگ جدول',
    'heading_color', 'رنگ عنوان',
    'above_color', 'رنگ بالا',
    'subheader_color', 'رنگ زیرعنوان',
    'picture_color', 'رنگ تصویر',
    'description_color', 'رنگ توضیح تصویر',
    'below_color', 'رنگ پایین',
    'footer_color', 'رنگ فوتر',
    'header_color', 'رنگ سربرگ',
    'label_color', 'رنگ برچسب',
    'data_color', 'رنگ اطلاعات'
}

local globalDynamicParams = {
    header = { names = {'header', 'سربرگ'}, hasSubParams = true, subParams = {'label', 'data', 'icon'} },
    subheader = { names = {'subheader', 'زیرعنوان'}, hasSubParams = false },
    picture = { names = {'picture', 'image', 'تصویر'}, hasSubParams = true, subParams = {'size', 'description'} }
}

local headerDependencies = {
    header = { names = {'header', 'سربرگ'}, subParams = {'label', 'data', 'icon', 'برچسب', 'اطلاعات', 'آیکون'} }
}

local layoutOptions = {
    {'carousel', 'چرخشی'},
    {'2up-2down', 'دوتا-بالا-دوتا-پایین'},
    {'3up-2down', 'سه‌تا-بالا-دوتا-پایین'},
    {'2up-3down', 'دوتا-بالا-سه‌تا-پایین'},
    {'grid', 'شبکه‌ای'},
    {'stacked', 'عمودی'}
}

local function isNonNumericParam(key)
    for _, param in ipairs(nonNumericParams) do
        if mw.ustring.match(key, '^' .. param .. '%d*$') or key == param then
            return true
        end
    end
    return false
end

local function isForceEnglishParam(key)
    for _, param in ipairs(forceEnglishParams) do
        if mw.ustring.match(key, '^' .. param .. '%d*$') or key == param then
            return true
        end
    end
    return false
end

local function convertHexToEnglish(value)
    local numberMap = {
        ['۰'] = '0', ['۱'] = '1', ['۲'] = '2', ['۳'] = '3', ['۴'] = '4',
        ['۵'] = '5', ['۶'] = '6', ['۷'] = '7', ['۸'] = '8', ['۹'] = '9',
        ['٠'] = '0', ['١'] = '1', ['٢'] = '2', ['٣'] = '3', ['٤'] = '4',
        ['٥'] = '5', ['٦'] = '6', ['٧'] = '7', ['٨'] = '8', ['٩'] = '9'
    }
    return mw.ustring.gsub(value, '#([0-9A-Fa-f۰-۹٠-٩]+)', function(hex)
        return '#' .. mw.ustring.gsub(hex, '[۰-۹٠-٩]', function(digit)
            return numberMap[digit] or digit
        end)
    end)
end

local function getArg(args, keys, lang)
    for _, key in ipairs(keys) do
        if args[key] and mw.text.trim(args[key]) ~= '' then
            local value = mw.text.trim(args[key])
            if isForceEnglishParam(key) then
                local num = lang:parseFormattedNumber(value)
                if num then
                    return tostring(num)
                else
                    local numOnly = tonumber(value)
                    if numOnly then
                        return lang:formatNum(numOnly)
                    else
                        return convertHexToEnglish(value)
                    end
                end
            elseif isNonNumericParam(key) then
                return value
            else
                local num = lang:parseFormattedNumber(value)
                if num then
                    return tostring(num)
                else
                    return value
                end
            end
        end
    end
    return nil
end

local function hasSubParams(args, headerNum, lang)
    for i = 1, 50 do
        local label = getArg(args, {'label' .. headerNum .. '_' .. i, 'برچسب' .. headerNum .. '_' .. i}, lang)
        local data = getArg(args, {'data' .. headerNum .. '_' .. i, 'اطلاعات' .. headerNum .. '_' .. i}, lang)
        if label and data then
            return true
        end
    end
    return false
end

local function isValidLayout(layout)
    for _, option in ipairs(layoutOptions) do
        if layout == option[1] or layout == option[2] then
            return true
        end
    end
    return false
end

local function checkListStyle(class)
    for name, list in pairs(listStyles) do
        for _, pattern in ipairs(list.patterns) do
            if class and mw.ustring.match(class, pattern) then
                return name
            end
        end
    end
    return nil
end

local function processRowContent(content, tag)
    if not content or mw.text.trim(content) == '' then return content end
    
    local marker = '<span class="modern-infobox-marker"></span>'
    local processed = mw.ustring.gsub(content, '(</[Tt][Rr]%s*>)((?:%[%[%s*[^%]]*%]%]|%b{})%s*)', '%2%1')
    processed = mw.ustring.gsub(processed, '<[Tt][Rr]', marker .. '<tr')
    processed = mw.ustring.gsub(processed, '</[Tt][Rr]%s*>', '</tr>' .. marker)
    
    if mw.ustring.match(processed, marker) then
        local parts = mw.text.split(processed, marker)
        local result = ''
        for i, part in ipairs(parts) do
            if i == 1 then
                result = result .. part .. '</' .. tag .. '></tr>'
            elseif i == #parts then 
                result = result .. '<tr><' .. tag .. ' colspan="2">' .. part
            elseif mw.text.trim(part) ~= '' then
                result = result .. '<tr><' .. tag .. ' colspan="2">' .. part .. '</' .. tag .. '></tr>'
            end
        end
        return result
    end
    return content
end

local function getDynamicArgNums(args, paramDef)
    local nums = {}
    for k in pairs(args) do
        for _, name in ipairs(paramDef.names) do
            local num = mw.ustring.match(k, '^' .. name .. '([0-9]+)$')
            if num then
                table.insert(nums, tonumber(num))
            end
        end
    end
    table.sort(nums)
    local uniqueNums = {}
    for i, num in ipairs(nums) do
        if i == 1 or num ~= nums[i-1] then
            table.insert(uniqueNums, num)
        end
    end
    return uniqueNums
end

function p.modernInfobox(frame)
    local parentArgs = frame:getParent() and frame:getParent().args or {}
    local frameArgs = frame.args or {}
    local args = {}
    for k, v in pairs(parentArgs) do
        if v and mw.text.trim(v) ~= '' then args[k] = mw.text.trim(v) end
    end
    for k, v in pairs(frameArgs) do
        if v and mw.text.trim(v) ~= '' then args[k] = mw.text.trim(v) end
    end

    if not next(args) then
        return "Error: No arguments received\nRaw arguments: " .. mw.dumpObject(args)
    end

    local langCode = getArg(args, {'language', 'زبان'}, mw.language.getContentLanguage()) or mw.language.getContentLanguage():getCode()
    local lang = mw.language.new(langCode)
    local isRTL = lang:isRTL()

    local templatestyles = frame:extensionTag{
        name = 'templatestyles',
        args = { src = 'Module:ModernInfobox/styles.css', lang = langCode }
    }

    local defaultWidth = '400px'
    local width = getArg(args, {'width', 'عرض'}, lang) or defaultWidth
    local unit = getArg(args, {'unit', 'واحد اندازه'}, lang) or 'px'
    if not mw.ustring.match(width, unit .. '$') then width = width .. unit end

    local infoboxId = getArg(args, {'id', 'شناسه'}, lang) or 'modern-infobox-default'
    local carouselId = 'carousel-' .. infoboxId

    local root = mw.html.create('table')
        :addClass('modern-infobox')
        :addClass(getArg(args, {'custom_class', 'کلاس دلخواه'}, lang))
        :cssText(getArg(args, {'custom_style', 'استایل دلخواه'}, lang))
        :css('width', width)
        :css('direction', isRTL and 'rtl' or 'ltr')
        :css('background', getArg(args, {'table_background_color', 'رنگ پس زمینه جدول'}, lang) and (getArg(args, {'table_background_color', 'رنگ پس زمینه جدول'}, lang) .. ' !important') or nil)
        :css('color', getArg(args, {'table_color', 'رنگ جدول'}, lang) and (getArg(args, {'table_color', 'رنگ جدول'}, lang) .. ' !important') or nil)
        :attr('id', infoboxId)

    local heading = getArg(args, {'heading', 'عنوان'}, lang)
    if heading then
        root:tag('caption')
            :addClass('modern-infobox-heading')
            :cssText(getArg(args, {'custom_style', 'استایل دلخواه'}, lang))
            :css('background', getArg(args, {'heading_background_color', 'رنگ پس زمینه عنوان'}, lang) and (getArg(args, {'heading_background_color', 'رنگ پس زمینه عنوان'}, lang) .. ' !important') or nil)
            :css('color', getArg(args, {'heading_color', 'رنگ عنوان'}, lang) and (getArg(args, {'heading_color', 'رنگ عنوان'}, lang) .. ' !important') or nil)
            :wikitext(heading)
    end

    local tbody = root

    local above = getArg(args, {'above', 'بالا'}, lang)
    if above then
        tbody:tag('tr')
            :tag('th')
                :attr('colspan', '2')
                :addClass('modern-infobox-above')
                :cssText(getArg(args, {'custom_style', 'استایل دلخواه'}, lang))
                :css('background', getArg(args, {'above_background_color', 'رنگ پس زمینه بالا'}, lang) and (getArg(args, {'above_background_color', 'رنگ پس زمینه بالا'}, lang) .. ' !important') or nil)
                :css('color', getArg(args, {'above_color', 'رنگ بالا'}, lang) and (getArg(args, {'above_color', 'رنگ بالا'}, lang) .. ' !important') or nil)
                :wikitext(processRowContent(above, 'th'))
    end

    local subheaderNums = getDynamicArgNums(args, globalDynamicParams.subheader)
    for _, num in ipairs(subheaderNums) do
        local subheader = getArg(args, {'subheader' .. num, 'زیرعنوان' .. num}, lang)
        if subheader then
            tbody:tag('tr')
                :tag('td')
                    :attr('colspan', '2')
                    :addClass('modern-infobox-subheader')
                    :cssText(getArg(args, {'custom_style', 'استایل دلخواه'}, lang))
                    :css('background', getArg(args, {'subheader_background_color', 'رنگ پس زمینه زیرعنوان'}, lang) and (getArg(args, {'subheader_background_color', 'رنگ پس زمینه زیرعنوان'}, lang) .. ' !important') or nil)
                    :css('color', getArg(args, {'subheader_color', 'رنگ زیرعنوان'}, lang) and (getArg(args, {'subheader_color', 'رنگ زیرعنوان'}, lang) .. ' !important') or nil)
                    :wikitext(processRowContent(subheader, 'td'))
        end
    end

    local pictureNums = getDynamicArgNums(args, globalDynamicParams.picture)
    local carouselHtml = ''
    if #pictureNums > 0 then
        local layout = getArg(args, {'layout', 'طرح‌بندی'}, lang) or 'stacked'
        if not isValidLayout(layout) then layout = 'stacked' end
        for _, option in ipairs(layoutOptions) do
            if layout == option[2] then
                layout = option[1]
                break
            end
        end

        -- تعریف اولیه layout کلاس
        local layoutClass = 'layout-' .. layout

        -- بررسی تعداد تصاویر برای چیدمان‌های خاص
        if layout == '2up-2down' and #pictureNums ~= 4 then
            layout = 'stacked'
            layoutClass = 'layout-stacked'
        elseif (layout == '3up-2down' or layout == '2up-3down') and #pictureNums ~= 5 then
            layout = 'stacked'
            layoutClass = 'layout-stacked'
        end

        if layout == 'carousel' then
            local carouselDiv = mw.html.create('div')
                :addClass('modern-infobox-carousel-container')
                :addClass('layout-' .. layout)
                :attr('id', carouselId)
                :css('display', 'none')
                :css('width', width)
                :css('margin', '0 auto')
                :css('background', getArg(args, {'picture_background_color', 'رنگ پس زمینه تصویر'}, lang) and (getArg(args, {'picture_background_color', 'رنگ پس زمینه تصویر'}, lang) .. ' !important') or nil)
                :css('color', getArg(args, {'picture_color', 'رنگ تصویر'}, lang) and (getArg(args, {'picture_color', 'رنگ تصویر'}, lang) .. ' !important') or nil)

            local hasContent = false
            for _, num in ipairs(pictureNums) do
                local picture = getArg(args, {'picture' .. num, 'image' .. num, 'تصویر' .. num}, lang)
                if picture then
                    local pictureDiv = carouselDiv:tag('div')
                        :addClass('modern-infobox-picture')
                    local pictureSize = getArg(args, {'size' .. num, 'اندازه تصویر' .. num}, lang) or '250'
                    if pictureSize ~= 'full' then
                        if tonumber(pictureSize) then pictureSize = pictureSize .. unit else pictureSize = '250' .. unit end
                    else
                        pictureSize = '250' .. unit
                    end
                    local pictureWikitext = string.format('[[File:%s|%s|center]]', picture, pictureSize)
                    pictureDiv:wikitext(pictureWikitext)
                    local description = getArg(args, {'description' .. num, 'توضیح تصویر' .. num}, lang)
                    if description then
                        pictureDiv:tag('div')
                            :addClass('modern-infobox-description')
                            :cssText(getArg(args, {'custom_style', 'استایل دلخواه'}, lang))
                            :css('background', getArg(args, {'description_picture_background_color', 'رنگ پس زمینه توضیح تصویر'}, lang) and (getArg(args, {'description_picture_background_color', 'رنگ پس زمینه توضیح تصویر'}, lang) .. ' !important') or nil)
                            :css('color', getArg(args, {'description_color', 'رنگ توضیح تصویر'}, lang) and (getArg(args, {'description_color', 'رنگ توضیح تصویر'}, lang) .. ' !important') or nil)
                            :wikitext(description)
                    end
                    hasContent = true
                end
            end

            if hasContent then
                carouselHtml = tostring(carouselDiv)

                local placeholderTd = tbody:tag('tr')
                    :tag('td')
                        :attr('colspan', '2')
                        :addClass('modern-infobox-picture-placeholder')
                        :attr('data-carousel-id', carouselId)
                        :css('height', '300px')

                local staticPictures = mw.html.create('div')
                    :addClass('modern-infobox-static-pictures')
                    :css('display', 'block')
                for _, num in ipairs(pictureNums) do
                    local picture = getArg(args, {'picture' .. num, 'image' .. num, 'تصویر' .. num}, lang)
                    if picture then
                        local pictureSize = getArg(args, {'size' .. num, 'اندازه تصویر' .. num}, lang) or '250'
                        if pictureSize ~= 'full' then
                            if tonumber(pictureSize) then pictureSize = pictureSize .. unit else pictureSize = '250' .. unit end
                        else
                            pictureSize = '250' .. unit
                        end
                        local pictureWikitext = string.format('[[File:%s|%s|center]]', picture, pictureSize)
                        staticPictures:tag('div')
                            :addClass('modern-infobox-picture')
                            :wikitext(pictureWikitext)
                    end
                end
                placeholderTd:wikitext(tostring(staticPictures))
            end
        else
            local pictureContainer = tbody:tag('tr')
                :tag('td')
                    :attr('colspan', '2')
                    :addClass('modern-infobox-picture-container')
                    :addClass(layoutClass)
                    :cssText(getArg(args, {'custom_style', 'استایل دلخواه'}, lang))
                    :css('background', getArg(args, {'picture_background_color', 'رنگ پس زمینه تصویر'}, lang) and (getArg(args, {'picture_background_color', 'رنگ پس زمینه تصویر'}, lang) .. ' !important') or nil)
                    :css('color', getArg(args, {'picture_color', 'رنگ تصویر'}, lang) and (getArg(args, {'picture_color', 'رنگ تصویر'}, lang) .. ' !important') or nil)

            for _, num in ipairs(pictureNums) do
                local picture = getArg(args, {'picture' .. num, 'image' .. num, 'تصویر' .. num}, lang)
                if picture then
                    local pictureDiv = pictureContainer:tag('div')
                        :addClass('modern-infobox-picture')
                    local pictureSize = getArg(args, {'size' .. num, 'اندازه تصویر' .. num}, lang) or '250'
                    if pictureSize ~= 'full' then
                        if tonumber(pictureSize) then pictureSize = pictureSize .. unit else pictureSize = '250' .. unit end
                    else
                        pictureSize = '250' .. unit
                    end
                    local pictureWikitext = string.format('[[File:%s|%s|center]]', picture, pictureSize)
                    pictureDiv:wikitext(pictureWikitext)
                    local description = getArg(args, {'description' .. num, 'توضیح تصویر' .. num}, lang)
                    if description then
                        pictureDiv:tag('div')
                            :addClass('modern-infobox-description')
                            :cssText(getArg(args, {'custom_style', 'استایل دلخواه'}, lang))
                            :css('background', getArg(args, {'description_picture_background_color', 'رنگ پس زمینه توضیح تصویر'}, lang) and (getArg(args, {'description_picture_background_color', 'رنگ پس زمینه توضیح تصویر'}, lang) .. ' !important') or nil)
                            :css('color', getArg(args, {'description_color', 'رنگ توضیح تصویر'}, lang) and (getArg(args, {'description_color', 'رنگ توضیح تصویر'}, lang) .. ' !important') or nil)
                            :wikitext(description)
                    end
                end
            end
        end
    end

    local headerNums = getDynamicArgNums(args, globalDynamicParams.header)
    for _, headerNum in ipairs(headerNums) do
        local header = getArg(args, {'header' .. headerNum, 'سربرگ' .. headerNum}, lang)
        if header and hasSubParams(args, headerNum, lang) then
            local isExpanded = getArg(args, {'header' .. headerNum .. '_open', 'باز سربرگ' .. headerNum}, lang)
            isExpanded = (isExpanded == 'yes' or isExpanded == 'بله')
            tbody:tag('tr')
                :tag('th')
                    :attr('colspan', '2')
                    :addClass('modern-infobox-header')
                    :cssText(getArg(args, {'custom_style', 'استایل دلخواه'}, lang))
                    :css('background', getArg(args, {'header_background_color', 'رنگ پس زمینه سربرگ'}, lang) and (getArg(args, {'header_background_color', 'رنگ پس زمینه سربرگ'}, lang) .. ' !important') or nil)
                    :css('color', getArg(args, {'header_color', 'رنگ سربرگ'}, lang) and (getArg(args, {'header_color', 'رنگ سربرگ'}, lang) .. ' !important') or nil)
                    :wikitext(header .. ' <span class="toggle">' .. (isExpanded and '-' or '+') .. '</span>')
            for i = 1, 50 do
                local label = getArg(args, {'label' .. headerNum .. '_' .. i, 'برچسب' .. headerNum .. '_' .. i}, lang)
                local data = getArg(args, {'data' .. headerNum .. '_' .. i, 'اطلاعات' .. headerNum .. '_' .. i}, lang)
                local icon = getArg(args, {'icon' .. headerNum .. '_' .. i, 'آیکون' .. headerNum .. '_' .. i}, lang)
                if label and data then
                    local row = tbody:tag('tr')
                        :addClass('modern-infobox-content')
                        :css('display', isExpanded and '' or 'none')
                        :addClass(getArg(args, {'custom_class', 'کلاس دلخواه'}, lang))
                        :cssText(getArg(args, {'custom_style', 'استایل دلخواه'}, lang))
                    local labelCell = row:tag('th')
                        :attr('scope', 'row')
                        :addClass('modern-infobox-label')
                        :cssText(getArg(args, {'custom_style', 'استایل دلخواه'}, lang))
                        :css('background', getArg(args, {'label_background_color', 'رنگ پس زمینه برچسب'}, lang) and (getArg(args, {'label_background_color', 'رنگ پس زمینه برچسب'}, lang) .. ' !important') or nil)
                        :css('color', getArg(args, {'label_color', 'رنگ برچسب'}, lang) and (getArg(args, {'label_color', 'رنگ برچسب'}, lang) .. ' !important') or nil)
                    if icon then
                        labelCell:wikitext(string.format('[[File:%s|20px]] %s', icon, label))
                    else
                        labelCell:wikitext(label)
                    end
                    row:tag('td')
                        :attr('colspan', '1')
                        :addClass('modern-infobox-data')
                        :cssText(getArg(args, {'custom_style', 'استایل دلخواه'}, lang))
                        :css('background', getArg(args, {'data_background_color', 'رنگ پس زمینه اطلاعات'}, lang) and (getArg(args, {'data_background_color', 'رنگ پس زمینه اطلاعات'}, lang) .. ' !important') or nil)
                        :css('color', getArg(args, {'data_color', 'رنگ اطلاعات'}, lang) and (getArg(args, {'data_color', 'رنگ اطلاعات'}, lang) .. ' !important') or nil)
                        :wikitext(processRowContent(data, 'td'))
                end
            end
        end
    end

    local below = getArg(args, {'below', 'پایین'}, lang)
    if below then
        tbody:tag('tr')
            :tag('td')
                :attr('colspan', '2')
                :addClass('modern-infobox-below')
                :cssText(getArg(args, {'custom_style', 'استایل دلخواه'}, lang))
                :css('background', getArg(args, {'below_background_color', 'رنگ پس زمینه پایین'}, lang) and (getArg(args, {'below_background_color', 'رنگ پس زمینه پایین'}, lang) .. ' !important') or nil)
                :css('color', getArg(args, {'below_color', 'رنگ پایین'}, lang) and (getArg(args, {'below_color', 'رنگ پایین'}, lang) .. ' !important') or nil)
                :wikitext(processRowContent(below, 'td'))
    end

    local footer = getArg(args, {'footer', 'فوتر'}, lang)
    if footer then
        tbody:tag('tr')
            :tag('td')
                :attr('colspan', '2')
                :addClass('modern-infobox-footer')
                :cssText(getArg(args, {'custom_style', 'استایل دلخواه'}, lang))
                :css('background', getArg(args, {'footer_background_color', 'رنگ پس زمینه فوتر'}, lang) and (getArg(args, {'footer_background_color', 'رنگ پس زمینه فوتر'}, lang) .. ' !important') or nil)
                :css('color', getArg(args, {'footer_color', 'رنگ فوتر'}, lang) and (getArg(args, {'footer_color', 'رنگ فوتر'}, lang) .. ' !important') or nil)
                :wikitext(processRowContent(footer, 'td'))
    end

    local listClass = checkListStyle(getArg(args, {'custom_class', 'کلاس دلخواه'}, lang))
    if listClass then
        local currentClasses = getArg(args, {'custom_class', 'کلاس دلخواه'}, lang) or ''
        if not mw.ustring.match(currentClasses, '%s' .. listClass .. '%s') and 
           not mw.ustring.match(currentClasses, '^' .. listClass .. '%s') and 
           not mw.ustring.match(currentClasses, '%s' .. listClass .. '$') and 
           not mw.ustring.match(currentClasses, '^' .. listClass .. '$') then
            root:addClass(listClass)
        end
    end

    return templatestyles .. '\n\n' .. carouselHtml .. '\n\n' .. tostring(root)
end

return p