پرش به محتوا

پودمان:تقویم

توضیحات پودمان[نمایش] [ویرایش] [تاریخچه] [پاکسازی]
شنبه
یکشنبه
دوشنبه
سه‌شنبه
چهارشنبه
پنجشنبه
جمعه
۱
۲
۳
۴
۵
۶
۷
۸ رمضان
۸
۹
۱۰
۱۱
۱۲
۱۳
۱۴
۱۵
۱۶
۱۷
۱۸
۱۹
۲۰
۲۱
۲۲
۲۳
۲۴
۲۵
۲۶
۲۷
۲۸
۲۹
ملی شدن نفت
شنبه
یکشنبه
دوشنبه
سه‌شنبه
چهارشنبه
پنجشنبه
جمعه
۱
۲
۳
۴
۵
۶
۷
۸ رمضان
۸
۹
۱۰
۱۱
۱۲
۱۳
۱۴
۱۵
۱۶
۱۷
۱۸
۱۹
۲۰
۲۱
۲۲
۲۳
۲۴
۲۵
۲۶
۲۷
۲۸
۲۹
ملی شدن نفت
شنبه
یکشنبه
دوشنبه
سه‌شنبه
چهارشنبه
پنجشنبه
جمعه
۱
۲
۳
۴
۵
۶
۷
۸
۹
۱۰
۱۱
۱۲
بازگشت امام
۱۳
۱۴
۱۵
۱۶
۱۷
۱۸
۱۹
۲۰
۲۱
۲۲
پیروزی انقلاب
۲۳
۲۴
۲۵
۲۶
۲۷
۲۸
۲۹
۳۰

-- =====================================================
-- پودمان تقویم شمسی
-- نسخه ۲۸: هوشمندسازی تابع تبدیل اعداد
-- =====================================================
local p = {}

-- -------------------------
-- داده‌های ثابت
-- -------------------------
local farsi_digits = { '۰','۱','۲','۳','۴','۵','۶','۷','۸','۹' }
local month_names = {
    'فروردین','اردیبهشت','خرداد','تیر','مرداد','شهریور',
    'مهر','آبان','آذر','دی','بهمن','اسفند'
}
local week_days = {
    'شنبه','یکشنبه','دوشنبه','سه‌شنبه','چهارشنبه','پنجشنبه','جمعه'
}

-- -------------------------
-- توابع کمکی
-- -------------------------

-- تابع toFarsiDigits (هوشمند شده)
local function toFarsiDigits(num)
    if not num then return '' end
    local s = tostring(num)
    
    -- بررسی اینکه آیا رشته از قبل فارسی است یا نه
    if s:match('[۰-۹]') then
        return s -- اگر فارسی بود، دست نزن
    end
    
    -- اگر انگلیسی بود، تبدیل کن
    return s:gsub('%d', function(d)
        return farsi_digits[tonumber(d) + 1]
    end)
end

-- بقیه توابع بدون تغییر...
local function normalizeArgs(val)
    if not val then return nil end
    val = tostring(val)
    val = val:gsub('[\226\128\142\226\128\143]', '')
    val = val:gsub('۰','0'):gsub('۱','1'):gsub('۲','2'):gsub('۳','3'):gsub('۴','4'):gsub('۵','5'):gsub('۶','6'):gsub('۷','7'):gsub('۸','8'):gsub('۹','9')
    local n = val:match('^%s*(%d+)%s*$')
    return n and tonumber(n) or nil
end

local function load_events_data()
    local success, data = pcall(mw.loadData, 'Module:تقویم/data') -- مسیر پودمان داده
    if success then return data else return {} end
end

local function clean_wiki_content(content)
    if not content then return '' end
    content = content:gsub('<noinclude>.-</noinclude>', '')
    content = content:match('^%s*(.-)%s*$')
    return content
end

local function get_wiki_page_content(page_title)
    local title = mw.title.new(page_title)
    if not title or not title.exists then return nil end
    local content = title:getContent()
    if not content then return nil end
    local clean_text = clean_wiki_content(content)
    if clean_text == '' then return nil end
    local is_long = false
    if #clean_text > 800 then
        clean_text = clean_text:sub(1, 800) .. '...'
        is_long = true
    end
    return { title = page_title, text = clean_text, is_long = is_long }
end

local function get_corrected_hijri(frame)
    local final_day = frame:callParserFunction('#time', 'xmj', 'now -1 day')
    local final_month = frame:callParserFunction('#time', 'xmF', 'now -1 day')
    
    -- خروجی #time همیشه عدد انگلیسی است، اما برای اطمینان...
    -- در نسخه جدید، toFarsiDigits خودش مدیریت می‌کند
    return final_day, final_month
end

local function prev_month(y, m) return (m == 1) and y - 1 or y, (m == 1) and 12 or m - 1 end
local function next_month(y, m) return (m == 12) and y + 1 or y, (m == 12) and 1 or m + 1 end

local function gregorian_to_jalali(gy, gm, gd)
    local gdm = {0,31,59,90,120,151,181,212,243,273,304,334}
    local jy = (gy <= 1600) and 0 or 979
    gy = gy - ((gy <= 1600) and 621 or 1600)
    local gy2 = (gm > 2) and (gy + 1) or gy
    local days = 365*gy + math.floor((gy2+3)/4) - math.floor((gy2+99)/100) + math.floor((gy2+399)/400) - 80 + gd + gdm[gm]
    jy = jy + 33*math.floor(days/12053)
    days = days % 12053
    jy = jy + 4*math.floor(days/1461)
    days = days % 1461
    jy = jy + math.floor((days-1)/365)
    if days > 365 then days = (days-1)%365 end
    local jm = (days < 186) and 1 + math.floor(days/31) or 7 + math.floor((days - 186) / 30)
    local jd = 1 + ((days < 186) and (days % 31) or ((days - 186) % 30))
    return jy, jm, jd
end

local function is_persian_leap(y)
    local r = (y - 474) % 33
    return r==1 or r==5 or r==9 or r==13 or r==17 or r==22 or r==26 or r==30
end

local function days_in_month(y, m)
    if m <= 6 then return 31 end
    if m <= 11 then return 30 end
    return is_persian_leap(y) and 30 or 29
end

local function persian_weekday(y, m, d)
    local total = 0
    for yy = 1400, y-1 do total = total + (is_persian_leap(yy) and 366 or 365) end
    for mm = 1, m-1 do total = total + days_in_month(y, mm) end
    total = total + (d-1)
    return (1 + total) % 7
end

function p.main(frame)
    local args = frame.args
    local parent = frame:getParent()
    if parent then for k,v in pairs(parent.args) do if not args[k] then args[k] = v end end end

    local today = os.date('*t')
    local ty, tm, td = gregorian_to_jalali(today.year, today.month, today.day)

    local year  = normalizeArgs(args['سال'])  or ty
    local month = normalizeArgs(args['ماه']) or tm
    local month_name = args['نام_ماه'] or month_names[month]

    local py, pm = prev_month(year, month)
    local ny, nm = next_month(year, month)
    local prev_month_name = month_names[pm]
    local next_month_name = month_names[nm]
    local year_fa = toFarsiDigits(year)

    local h_day, h_month = get_corrected_hijri(frame)
    local hijri_full = ''
    if h_day and h_day ~= '' then
        hijri_full = toFarsiDigits(h_day) .. ' ' .. h_month
    end
    
    local events = load_events_data() 
    local start = persian_weekday(year, month, 1)
    local dim   = days_in_month(year, month)

    local out = {}
    table.insert(out, '<div class="calendar-root">')

    -- Header
    table.insert(out, '<div class="calendar-header plainlinks">')
    local prev_link = '[[' .. '۱ ' .. prev_month_name .. '|‹ ' .. prev_month_name .. ']]'
    table.insert(out, '<span class="nav-btn prev">' .. prev_link .. '</span>')
    local title_html = '[[' .. month_name .. ' (ماه)|' .. month_name .. ']] ' .. 
                       '[[' .. 'سال ' .. year_fa .. ' هجری شمسی|' .. year_fa .. ']]'
    table.insert(out, '<div class="calendar-title">' .. title_html .. '</div>')
    local next_link = '[[' .. '۱ ' .. next_month_name .. '|' .. next_month_name .. ' ›]]'
    table.insert(out, '<span class="nav-btn next">' .. next_link .. '</span>')
    table.insert(out, '</div>')

    -- Weekdays
    table.insert(out, '<div class="calendar-weekdays">')
    for i=1,7 do table.insert(out, '<div class="weekday">' .. week_days[i] .. '</div>') end
    table.insert(out, '</div>')

    -- Days Grid
    table.insert(out, '<div class="calendar-grid">')
    for i=1,start do table.insert(out, '<div class="day empty"></div>') end

    for d=1,dim do
        local cls = 'day'
        local content_extra = ''
        
        if persian_weekday(year, month, d) == 6 then cls = cls .. ' friday' end
        
        if year==ty and month==tm and d==td then
            cls = cls .. ' today'
            if hijri_full ~= '' then
                content_extra = content_extra .. '<div class="day-sub">' .. hijri_full .. '</div>'
            end
        end

        local event_key = month .. '-' .. d
        local event_val = events[event_key] or events[tostring(event_key)]
        
        if event_val then
            cls = cls .. ' has-event'
            content_extra = content_extra .. '<div class="day-event">' .. event_val .. '</div>'
        end

        table.insert(out, '<div class="'..cls..'"><span class="day-main">'..toFarsiDigits(d)..'</span>'..content_extra..'</div>')
    end
    table.insert(out, '</div>')

    -- Footer
    if year == ty and month == tm then
        table.insert(out, '<div class="calendar-footer-wrapper">')

        -- Solar Events
        local solar_page_title = toFarsiDigits(td) .. ' ' .. month_names[tm]
        local solar_data = get_wiki_page_content(solar_page_title)
        if solar_data then
            table.insert(out, '<div class="footer-box solar-events">')
            table.insert(out, '<div class="footer-header">📅 رویدادهای امروز (' .. solar_data.title .. ')</div>')
            table.insert(out, '<div class="footer-content">' .. frame:preprocess(solar_data.text) .. '</div>')
            if solar_data.is_long then
                table.insert(out, '<div class="footer-more">[[' .. solar_data.title .. '|مشاهده کامل رویداد]]</div>')
            end
            table.insert(out, '</div>')
        end

        -- Hijri Events
        if h_day and h_day ~= '' then
            local hijri_page_title = toFarsiDigits(h_day) .. ' ' .. h_month
            local hijri_data = get_wiki_page_content(hijri_page_title)
            if hijri_data then
                table.insert(out, '<div class="footer-box hijri-events">')
                table.insert(out, '<div class="footer-header">🌙 رویدادهای قمری (' .. hijri_data.title .. ')</div>')
                table.insert(out, '<div class="footer-content">' .. frame:preprocess(hijri_data.text) .. '</div>')
                if hijri_data.is_long then
                    table.insert(out, '<div class="footer-more">[[' .. hijri_data.title .. '|مشاهده کامل رویداد]]</div>')
                end
                table.insert(out, '</div>')
            end
        end
        table.insert(out, '</div>')
    end

    table.insert(out, '</div>')
    table.insert(out, frame:extensionTag{ name='templatestyles', args={ src='پودمان:تقویم/styles.css' } })
    return table.concat(out)
end

return p