Module:FormatSegmentRow

From Bodhicitta
Revision as of 17:59, 24 April 2026 by Jeremi (talk | contribs) (VSCode edit)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)

Documentation for this module may be created at Module:FormatSegmentRow/doc

local p = {}

-- TransMemID prefixes that indicate a non-English SegmentTranslation
local TRANSMEM_LANG = {
    ['004-Tsadra-BCA-Root-Padmakara-French'] = 'french',
    ['021-Tsadra-BCA-Padmakara-Spanish']     = 'spanish',
}

-- Build language table, routing SegmentTranslation to the correct language
-- based on TransMemID (defaults to english)
local function buildLanguages(transMemLang)
    local englishParams  = (transMemLang == 'english') and {'SegmentTranslation'} or {}
    local frenchParams   = (transMemLang == 'french')  and {'SegmentTranslation', 'FrenchTranslation', 'French'} or {'FrenchTranslation', 'French'}
    local spanishParams  = (transMemLang == 'spanish') and {'SegmentTranslation', 'Spanish'} or {'Spanish'}
    return {
        { id = 'tibetan',        params = {'SegmentSource'},        classPrefix = 'tibetan-version',        textClass = 'text-160',    langType = 'source' },
        { id = 'tibetan-tsadra', params = {'SegmentSourceTsadra'},  classPrefix = 'tibetan-tsadra-version', textClass = 'text-160',    langType = 'source' },
        { id = 'english',        params = englishParams,            classPrefix = 'english-version',        textClass = 'text-article', langType = 'translation' },
        { id = 'wylie',          params = {'SourceTranslit'},       classPrefix = 'wylie-version',          textClass = 'text-article', langType = 'translation' },
        { id = 'wylie-tsadra',   params = {'SegmentTranslitTsadra'}, classPrefix = 'wylie-tsadra-version',  textClass = 'text-article', langType = 'translation' },
        { id = 'french',         params = frenchParams,             classPrefix = 'french-version',         textClass = 'text-article', langType = 'translation' },
        { id = 'spanish',        params = spanishParams,            classPrefix = 'spanish-version',        textClass = 'text-article', langType = 'translation' },
        { id = 'devanagari',     params = {'SanskritSource'},       classPrefix = 'devanagari-version',     textClass = 'text-160',    langType = 'source' },
        { id = 'sanskrit',       params = {'SanskritTranslit'},     classPrefix = 'sanskrit-version',       textClass = 'text-article', langType = 'translation' },
    }
end

-- Priority order for default visible language per side, given available params
local leftPriority  = { 'tibetan', 'tibetan-tsadra', 'devanagari', 'wylie', 'wylie-tsadra', 'sanskrit' }
local rightPriority = { 'english', 'french', 'spanish', 'wylie', 'wylie-tsadra' }

-- Get first non-empty value from a list of param names
local function getParamValue(args, paramNames)
    for _, name in ipairs(paramNames) do
        local val = args[name]
        if val and mw.text.trim(val) ~= '' then
            return mw.text.trim(val)
        end
    end
    return nil
end

-- Resolve default language for a side: first priority-list entry with content in args
local function resolveDefault(args, priorityList, languages)
    local paramsByLang = {}
    for _, lang in ipairs(languages) do
        paramsByLang[lang.id] = lang.params
    end
    for _, id in ipairs(priorityList) do
        local params = paramsByLang[id]
        if params and #params > 0 and getParamValue(args, params) then
            return id
        end
    end
    return priorityList[1]
end

-- Convert semicolon-separated page names into wiki links
local function wikiLinkList(str)
    local links = {}
    for page in mw.text.gsplit(str, '%s*;%s*') do
        if page ~= '' then
            table.insert(links, '[[' .. page .. ']]')
        end
    end
    return table.concat(links, ', ')
end

-- Wrap content with poem extension tag (for translation-side verse formats)
local function wrapPoem(frame, content)
    return frame:extensionTag('poem', '<p class="m-0">' .. content .. '</p>')
end

-- Apply SegmentFormat styling to content
-- isSource: true for source-type languages, false for translation-type
local function formatContent(content, format, isSource, frame, sabcheNum, quoteSource)
    if not content then return nil end

    if format == 'Root' then
        if isSource then
            return '<div class="pl-4" style="color: #8c1409;"><p>' .. content .. '</p></div>'
        else
            return '<div class="p-bl-fix pl-4 my-2" style="color: #8c1409;"><p>' .. content .. '</p></div>'
        end

    elseif format == 'Title' then
        if isSource then
            return '<span class="text-130" style="color: #467083;">' .. content .. '</span>'
        else
            return '<div class="h3 font-serif-alt" style="color: #467083; white-space: break-spaces;">' .. content .. '</div>'
        end

    elseif format == 'Commentary' or format == 'Commentary2' or format == 'Colophon' then
        return content

    elseif format == 'Quote' then
        local noteSuffix = ''
        if quoteSource and quoteSource ~= '' then
            noteSuffix = frame:expandTemplate{ title = 'note', args = { 'source', wikiLinkList(quoteSource) } }
        end
        if isSource then
            return '<div class="pl-4" style="color: #897435;">' .. content .. noteSuffix .. '</div>'
        else
            return '<div class="pl-4" style="color: #897435;">' .. wrapPoem(frame, content .. noteSuffix) .. '</div>'
        end

    elseif format == 'DoubleQuote' or format == 'Verse inside a quote' then
        local noteSuffix = ''
        if quoteSource and quoteSource ~= '' then
            noteSuffix = frame:expandTemplate{ title = 'note', args = { 'source', wikiLinkList(quoteSource) } }
        end
        if isSource then
            return '<div class="pl-5" style="color: #897435;">' .. content .. noteSuffix .. '</div>'
        else
            return '<div class="pl-5" style="color: #897435;">' .. wrapPoem(frame, content .. noteSuffix) .. '</div>'
        end

    elseif format == 'Sabche' then
        if isSource then
            return '<span style="color: #467083;">' .. content .. '</span>'
        else
            local sabche = '<div class="p-bl-fix"><span style="color: #467083;">' .. content .. '</span>'
            if sabcheNum then
                sabche = sabche .. ' <span class="float-right">' .. sabcheNum .. '</span>'
            end
            return sabche .. '</div>'
        end

    elseif format == 'SabcheHeading' then
        if isSource then
            return '<div class="mt-3" style="color: #467083;">' .. content .. '</div>'
        else
            local heading = '<div class="h5 mt-3 font-serif-alt" style="color: #467083;">' .. content
            if sabcheNum then
                heading = heading .. ' <span class="float-right">' .. sabcheNum .. '</span>'
            end
            return heading .. '</div>'
        end

    elseif format == 'SabcheSubHeading' then
        if isSource then
            return '<div class="mt-2" style="color: #467083;">' .. content .. '</div>'
        else
            local subheading = '<div class="h6 mt-2 font-serif-alt" style="color: #467083;">' .. content
            if sabcheNum then
                subheading = subheading .. ' <span class="float-right">' .. sabcheNum .. '</span>'
            end
            return subheading .. '</div>'
        end

    elseif format == 'Section Title1' then
        if isSource then
            return content
        else
            return '<div class="h3 font-serif-alt" style="color: #467083; white-space: break-spaces;">' .. content .. '</div>'
        end

    elseif format == 'Section Title2' then
        if isSource then
            return '<span class="text-120" style="color: #467083;">' .. content .. '</span>'
        else
            return '<div class="h4 font-serif-alt" style="color: #467083; white-space: break-spaces;">' .. content .. '</div>'
        end

    elseif format == 'Indented Verse' then
        if isSource then
            return '<div class="pl-4">' .. content .. '</div>'
        else
            return '<div class="pl-4">' .. wrapPoem(frame, content) .. '</div>'
        end

    elseif format == 'Root non-verse' then
        return '<span style="color: #8c1409;">' .. content .. '</span>'

    else
        -- Default: no special formatting
        return content
    end
end

-- Render all language divs for one side (left or right)
local function renderSide(args, side, format, frame, defaultLang, languages)
    local sabcheNum = getParamValue(args, {'SourceSabcheNumber'})
    local quoteSource = getParamValue(args, {'QuoteSource'})
    local parts = {}

    for _, lang in ipairs(languages) do
        if #lang.params > 0 then
            local content = getParamValue(args, lang.params)
            if content then
                local isSource = (lang.langType == 'source')
                local formatted = formatContent(content, format, isSource, frame, sabcheNum, quoteSource)

                if formatted then
                    local hidden = lang.id ~= defaultLang and ' hidden' or ''
                    local classes = 'toggleable ' .. lang.classPrefix .. '-' .. side .. ' ' .. lang.textClass .. hidden
                    table.insert(parts, '<div class="' .. classes .. '">' .. formatted .. '</div>')
                end
            end
        end
    end

    return table.concat(parts)
end

function p.main(frame)
    local args = frame:getParent().args
    local format = getParamValue(args, {'SegmentFormat'}) or 'Commentary'

    -- Determine which language SegmentTranslation holds based on TransMemID
    local transMemId = getParamValue(args, {'TransMemID'}) or ''
    local transMemLang = TRANSMEM_LANG[transMemId] or 'english'

    local languages = buildLanguages(transMemLang)

    local defaultLeft  = resolveDefault(args, leftPriority, languages)
    local defaultRight = resolveDefault(args, rightPriority, languages)

    -- Fetch SingleLanguage and SingleColumn from the current page via #show.
    -- mw.smw.ask cannot reliably query the current page's own properties during
    -- the same parse (SMW hasn't committed them yet), so we use frame:callParserFunction
    -- with #show instead, which reads already-stored SMW values correctly.
    local pageTitle  = mw.title.getCurrentTitle().prefixedText
    local singleLang = mw.text.trim(frame:callParserFunction('#show', pageTitle, '?SingleLanguage'))
    if singleLang == '' then singleLang = nil else singleLang = singleLang:lower() end
    local scRaw      = mw.text.trim(frame:callParserFunction('#show', pageTitle, '?SingleColumn')):lower()
    local singleColumn = (scRaw == 'true' or scRaw == '1' or scRaw == 't')
    local svnRaw     = mw.text.trim(frame:callParserFunction('#show', pageTitle, '?ShowVerseNumbers')):lower()
    local showVerseNumbers = (svnRaw == 'true' or svnRaw == '1' or svnRaw == 't')

    local verseNum = getParamValue(args, {'RootTextChapVerseNum', 'RootTextVerseNum'})
    local showVerseCol = showVerseNumbers and verseNum ~= nil
    local verseCol = ''
    if showVerseCol then
        verseCol = '<div class="col-1 text-right font-serif-alt text-160 pt-1 mb-3">' .. verseNum .. '</div>'
    end

    -- Determine which side the single language lives on
    local singleIsLeft  = singleLang and (function()
        for _, id in ipairs(leftPriority) do if id == singleLang then return true end end
    end)()
    local singleIsRight = singleLang and (function()
        for _, id in ipairs(rightPriority) do if id == singleLang then return true end end
    end)()

    -- Filter languages to only the requested one when in single-language mode
    local function filterLanguages(langs, targetId)
        local filtered = {}
        for _, lang in ipairs(langs) do
            if lang.id == targetId then
                table.insert(filtered, lang)
            end
        end
        return filtered
    end

    local output = ''

    local mainClass  = showVerseCol and 'col-11' or 'col-12'
    local halfClass  = showVerseCol and 'col-md-5' or 'col-md-6'

    if singleColumn then
        -- Single-column mode: all available languages in one column, left-side classes.
        -- The language switcher (RootVerseToggles) still works since the togglable divs
        -- use the same tibetan-version-left / wylie-version-left etc. class names.
        local content = renderSide(args, 'left', format, frame, defaultLeft, languages)
        output = verseCol .. '<div class="' .. mainClass .. ' font-serif line14 p-bl-fix mb-3">' .. content .. '</div>'
    elseif singleLang then
        -- Single-language mode: render only the matching column at full width
        if singleIsLeft then
            local filteredLangs = filterLanguages(languages, singleLang)
            local content = renderSide(args, 'left', format, frame, singleLang, filteredLangs)
            output = verseCol .. '<div class="' .. mainClass .. ' font-serif line14 p-bl-fix mb-3">' .. content .. '</div>'
        elseif singleIsRight then
            local filteredLangs = filterLanguages(languages, singleLang)
            local content = renderSide(args, 'right', format, frame, singleLang, filteredLangs)
            output = verseCol .. '<div class="' .. mainClass .. ' line14 p-bl-fix mb-3">' .. content .. '</div>'
        else
            -- Unknown language id: fall back to normal two-column layout
            local left = renderSide(args, 'left', format, frame, defaultLeft, languages)
            local right = renderSide(args, 'right', format, frame, defaultRight, languages)
            output = verseCol .. '<div class="' .. halfClass .. ' font-serif line14 p-bl-fix mb-3">' .. left .. '</div>'
                  .. '<div class="' .. halfClass .. ' line14 p-bl-fix mb-3">' .. right .. '</div>'
        end
    else
        -- Normal two-column layout
        local left = renderSide(args, 'left', format, frame, defaultLeft, languages)
        local right = renderSide(args, 'right', format, frame, defaultRight, languages)
        output = verseCol .. '<div class="' .. halfClass .. ' font-serif line14 p-bl-fix mb-3">' .. left .. '</div>'
              .. '<div class="' .. halfClass .. ' line14 p-bl-fix mb-3">' .. right .. '</div>'
    end

    return output
end

return p