Module:SegmentQuery: Difference between revisions
From Bodhicitta
(VSCode edit) Tag: Manual revert |
(VSCode edit) |
||
| Line 1: | Line 1: | ||
-- Module:SegmentQuery | -- Module:SegmentQuery | ||
-- | -- Unified module for bilingual segment queries and formatting. | ||
-- | -- Handles chunked deferred queries, row formatting, and language rendering. | ||
-- | -- | ||
-- {{#invoke:SegmentQuery|renderChunks|{{ | -- Entry points: | ||
-- {{#invoke:SegmentQuery|renderChunks|chapter}} — from DisplayTmSection | |||
-- {{#invoke:SegmentQuery|formatRow}} — from Template:DisplaySegmentRow (SMW result template) | |||
local p = {} | local p = {} | ||
------------------------------------------------------------------------------- | |||
-- Configuration | |||
------------------------------------------------------------------------------- | |||
local CHUNK_SIZE_NORMAL = 20 | local CHUNK_SIZE_NORMAL = 20 | ||
local CHUNK_SIZE_LARGE = 3 -- smaller chunks for chapters with "Large content" segments | local CHUNK_SIZE_LARGE = 3 -- smaller chunks for chapters with "Large content" segments | ||
local MAX_SEGMENTS = 500 -- absolute safety cap | local MAX_SEGMENTS = 500 -- absolute safety cap | ||
-- TransMemID prefixes that indicate a non-English SegmentTranslation | |||
local TRANSMEM_LANG = { | |||
['004-Tsadra-BCA-Root-Padmakara-French'] = 'french', | |||
['021-Tsadra-BCA-Padmakara-Spanish'] = 'spanish', | |||
} | |||
-- All printouts the #ask query can request. | |||
-- LeaveOutArgs suppresses individual printouts by name. | |||
local ALL_PRINTOUTS = { | |||
'SegmentSource', 'SourceTranslit', 'SegmentTranslation', | |||
'SegmentOrder', 'RootTextChapVerseNum', 'SegmentFormat', | |||
'SanskritSource', 'SanskritTranslit', 'TransMemNum', | |||
'SegmentSourceTsadra', 'SegmentTranslitTsadra', | |||
'SourceSabcheNumber', 'QuoteSource', 'Flag', 'TransMemID', | |||
} | |||
-- Printouts that are always included regardless of LeaveOutArgs | |||
local ALWAYS_INCLUDE = { | |||
SegmentOrder = true, RootTextChapVerseNum = true, SegmentFormat = true, | |||
TransMemNum = true, SourceSabcheNumber = true, QuoteSource = true, | |||
Flag = true, TransMemID = true, | |||
} | |||
-- Priority order for default visible language per side | |||
local leftPriority = { 'tibetan', 'tibetan-tsadra', 'devanagari', 'wylie', 'wylie-tsadra', 'sanskrit' } | |||
local rightPriority = { 'english', 'french', 'spanish', 'wylie', 'wylie-tsadra' } | |||
------------------------------------------------------------------------------- | |||
-- Language helpers (shared by formatRow) | |||
------------------------------------------------------------------------------- | |||
-- 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 | |||
-- 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 | |||
-- Filter languages to only the requested one (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 | |||
-- Check if a language id belongs to a priority list | |||
local function inPriorityList(priorityList, langId) | |||
for _, id in ipairs(priorityList) do | |||
if id == langId then return true end | |||
end | |||
return false | |||
end | |||
------------------------------------------------------------------------------- | |||
-- Formatting helpers (absorbed from FormatSegmentRow) | |||
------------------------------------------------------------------------------- | |||
-- 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 | |||
------------------------------------------------------------------------------- | |||
-- Staff group check (cached per page parse) | |||
------------------------------------------------------------------------------- | |||
local isStaffCache = nil | |||
local function isStaff(frame) | |||
if isStaffCache == nil then | |||
isStaffCache = mw.text.trim(frame:preprocess('{{#ifingroup:staff|1|0}}')) == '1' | |||
end | |||
return isStaffCache | |||
end | |||
------------------------------------------------------------------------------- | |||
-- SingleLanguage / SingleColumn check (cached per page parse) | |||
------------------------------------------------------------------------------- | |||
local singleLangCache -- nil = not checked yet, false = not set | |||
local singleColumnCache | |||
local function getSingleLang(frame) | |||
if singleLangCache == nil then | |||
local pageTitle = mw.title.getCurrentTitle().prefixedText | |||
local raw = mw.text.trim(frame:callParserFunction('#show', pageTitle, '?SingleLanguage')) | |||
singleLangCache = (raw ~= '') and raw:lower() or false | |||
end | |||
return singleLangCache | |||
end | |||
local function getSingleColumn(frame) | |||
if singleColumnCache == nil then | |||
local pageTitle = mw.title.getCurrentTitle().prefixedText | |||
local raw = mw.text.trim(frame:callParserFunction('#show', pageTitle, '?SingleColumn')):lower() | |||
singleColumnCache = (raw == 'true' or raw == '1' or raw == 't') | |||
end | |||
return singleColumnCache | |||
end | |||
------------------------------------------------------------------------------- | |||
-- formatRow: called by Template:DisplaySegmentRow for each SMW result | |||
-- (absorbs the old DisplaySegmentRow template wrapper + FormatSegmentRow module) | |||
------------------------------------------------------------------------------- | |||
function p.formatRow(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) | |||
local singleLang = getSingleLang(frame) | |||
local singleColumn = getSingleColumn(frame) | |||
local singleIsLeft = singleLang and inPriorityList(leftPriority, singleLang) | |||
local singleIsRight = singleLang and inPriorityList(rightPriority, singleLang) | |||
-- Build content columns | |||
local content | |||
if singleColumn then | |||
local rendered = renderSide(args, 'left', format, frame, defaultLeft, languages) | |||
content = '<div class="col-12 font-serif line14 p-bl-fix mb-3">' .. rendered .. '</div>' | |||
elseif singleLang then | |||
if singleIsLeft then | |||
local rendered = renderSide(args, 'left', format, frame, singleLang, filterLanguages(languages, singleLang)) | |||
content = '<div class="col-12 font-serif line14 p-bl-fix mb-3">' .. rendered .. '</div>' | |||
elseif singleIsRight then | |||
local rendered = renderSide(args, 'right', format, frame, singleLang, filterLanguages(languages, singleLang)) | |||
content = '<div class="col-12 line14 p-bl-fix mb-3">' .. rendered .. '</div>' | |||
else | |||
local left = renderSide(args, 'left', format, frame, defaultLeft, languages) | |||
local right = renderSide(args, 'right', format, frame, defaultRight, languages) | |||
content = '<div class="col-md-6 font-serif line14 p-bl-fix mb-3">' .. left .. '</div>' | |||
.. '<div class="col-md-6 line14 p-bl-fix mb-3">' .. right .. '</div>' | |||
end | |||
else | |||
local left = renderSide(args, 'left', format, frame, defaultLeft, languages) | |||
local right = renderSide(args, 'right', format, frame, defaultRight, languages) | |||
content = '<div class="col-md-6 font-serif line14 p-bl-fix mb-3">' .. left .. '</div>' | |||
.. '<div class="col-md-6 line14 p-bl-fix mb-3">' .. right .. '</div>' | |||
end | |||
-- Wrap in row div (previously done by Template:DisplaySegmentRow) | |||
local parts = { '<div class="row bilingual-row py-1 filterable">' } | |||
-- Staff debug info | |||
if isStaff(frame) then | |||
local resultNum = args['#'] or '' | |||
local pageTitle = args[1] or '' | |||
local segOrder = getParamValue(args, {'SegmentOrder'}) or '' | |||
table.insert(parts, | |||
'<div class="col-12 text-center font-sans-wiki text-90 py-1 mb-2">' | |||
.. 'Query result #' .. resultNum | |||
.. ' · ' | |||
.. '[' .. mw.uri.fullUrl(pageTitle):gsub('^//', 'https://') .. ' ' | |||
.. '<i class="fa-sharp-duotone fa-memo fa-sm"></i> TM segment #' .. segOrder | |||
.. ']</div>' | |||
) | |||
end | |||
table.insert(parts, content) | |||
table.insert(parts, '</div>') | |||
return table.concat(parts) | |||
end | |||
------------------------------------------------------------------------------- | |||
-- renderChunks: emits deferred #ask queries, called from DisplayTmSection | |||
-- Now also builds the #ask wikitext directly (absorbs Template:SegmentQuery) | |||
------------------------------------------------------------------------------- | |||
function p.renderChunks(frame) | function p.renderChunks(frame) | ||
local currentTitle = mw.title.getCurrentTitle() | local currentTitle = mw.title.getCurrentTitle() | ||
local pargs = frame:getParent().args | |||
local leaveOutArgs = pargs.LeaveOutArgs or '' | |||
local transMemID = mw.text.trim(pargs.TransMemID or '') | |||
-- Mirror SegmentQuery's fallback: if TranslationChapter not passed, use SUBPAGENAME | -- Mirror SegmentQuery's fallback: if TranslationChapter not passed, use SUBPAGENAME | ||
| Line 22: | Line 389: | ||
local hasChapter = chapterRaw ~= '' | local hasChapter = chapterRaw ~= '' | ||
-- Build chapter condition | -- Build chapter condition | ||
local chapterCondition = '' | local chapterCondition = '' | ||
if hasChapter then | if hasChapter then | ||
| Line 32: | Line 398: | ||
chapterCondition = '[[TranslationChapter::' .. table.concat(values, '||') .. ']]' | chapterCondition = '[[TranslationChapter::' .. table.concat(values, '||') .. ']]' | ||
end | end | ||
local baseConditions = | local baseConditions = | ||
| Line 43: | Line 404: | ||
'[[SegmentFormat::!Deprecated]]' | '[[SegmentFormat::!Deprecated]]' | ||
-- 1. Count total segments | -- 1. Count total segments | ||
local countResult = mw.smw.ask( | local countResult = mw.smw.ask( | ||
baseConditions .. | baseConditions .. | ||
| Line 51: | Line 412: | ||
) | ) | ||
local totalSegments = countResult and #countResult or 0 | local totalSegments = countResult and #countResult or 0 | ||
if totalSegments == 0 then return '<!-- SegmentQuery: 0 segments found for TransMemID=' .. transMemID | if totalSegments == 0 then | ||
return '<!-- SegmentQuery: 0 segments found for TransMemID=' .. transMemID .. ' -->' | |||
end | |||
-- 2. Check for any "Large content" flag in this chapter | -- 2. Check for any "Large content" flag in this chapter | ||
local largeCheck = mw.smw.ask( | local largeCheck = mw.smw.ask( | ||
baseConditions .. | baseConditions .. | ||
| Line 63: | Line 426: | ||
local chunkSize = (largeCheck and #largeCheck > 0) and CHUNK_SIZE_LARGE or CHUNK_SIZE_NORMAL | local chunkSize = (largeCheck and #largeCheck > 0) and CHUNK_SIZE_LARGE or CHUNK_SIZE_NORMAL | ||
-- 3. | -- 3. Build printout list, respecting LeaveOutArgs | ||
local printouts = {} | |||
for _, name in ipairs(ALL_PRINTOUTS) do | |||
-- | if ALWAYS_INCLUDE[name] or not leaveOutArgs:find(name, 1, true) then | ||
table.insert(printouts, '|?' .. name) | |||
end | |||
end | |||
local printoutStr = table.concat(printouts) | |||
-- 4. Emit one deferred #ask per chunk | |||
local parts = {} | local parts = {} | ||
local offset = 0 | local offset = 0 | ||
while offset < totalSegments do | while offset < totalSegments do | ||
-- Build the full #ask wikitext | |||
local ask = '{{#ask:' | |||
.. baseConditions | |||
.. printoutStr | |||
.. '|named args=true' | |||
.. '|sort=SegmentOrder' | |||
.. '|order=asc' | |||
.. '|format=template' | |||
.. '|template=DisplaySegmentRow' | |||
.. '|limit=' .. chunkSize | |||
.. '|offset=' .. offset | |||
.. '|link=none' | |||
.. '|intro=<div class="filterableContent mb-4">' | |||
.. '|outro=</div>' | |||
.. '|searchlabel=' | |||
.. '|default=' | |||
.. '|@deferred' | |||
.. '}}' | |||
table.insert(parts, frame:preprocess(ask)) | |||
offset = offset + chunkSize | offset = offset + chunkSize | ||
end | end | ||
Revision as of 14:08, 10 April 2026
Documentation for this module may be created at Module:SegmentQuery/doc
-- Module:SegmentQuery
-- Unified module for bilingual segment queries and formatting.
-- Handles chunked deferred queries, row formatting, and language rendering.
--
-- Entry points:
-- {{#invoke:SegmentQuery|renderChunks|chapter}} — from DisplayTmSection
-- {{#invoke:SegmentQuery|formatRow}} — from Template:DisplaySegmentRow (SMW result template)
local p = {}
-------------------------------------------------------------------------------
-- Configuration
-------------------------------------------------------------------------------
local CHUNK_SIZE_NORMAL = 20
local CHUNK_SIZE_LARGE = 3 -- smaller chunks for chapters with "Large content" segments
local MAX_SEGMENTS = 500 -- absolute safety cap
-- TransMemID prefixes that indicate a non-English SegmentTranslation
local TRANSMEM_LANG = {
['004-Tsadra-BCA-Root-Padmakara-French'] = 'french',
['021-Tsadra-BCA-Padmakara-Spanish'] = 'spanish',
}
-- All printouts the #ask query can request.
-- LeaveOutArgs suppresses individual printouts by name.
local ALL_PRINTOUTS = {
'SegmentSource', 'SourceTranslit', 'SegmentTranslation',
'SegmentOrder', 'RootTextChapVerseNum', 'SegmentFormat',
'SanskritSource', 'SanskritTranslit', 'TransMemNum',
'SegmentSourceTsadra', 'SegmentTranslitTsadra',
'SourceSabcheNumber', 'QuoteSource', 'Flag', 'TransMemID',
}
-- Printouts that are always included regardless of LeaveOutArgs
local ALWAYS_INCLUDE = {
SegmentOrder = true, RootTextChapVerseNum = true, SegmentFormat = true,
TransMemNum = true, SourceSabcheNumber = true, QuoteSource = true,
Flag = true, TransMemID = true,
}
-- Priority order for default visible language per side
local leftPriority = { 'tibetan', 'tibetan-tsadra', 'devanagari', 'wylie', 'wylie-tsadra', 'sanskrit' }
local rightPriority = { 'english', 'french', 'spanish', 'wylie', 'wylie-tsadra' }
-------------------------------------------------------------------------------
-- Language helpers (shared by formatRow)
-------------------------------------------------------------------------------
-- 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
-- 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
-- Filter languages to only the requested one (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
-- Check if a language id belongs to a priority list
local function inPriorityList(priorityList, langId)
for _, id in ipairs(priorityList) do
if id == langId then return true end
end
return false
end
-------------------------------------------------------------------------------
-- Formatting helpers (absorbed from FormatSegmentRow)
-------------------------------------------------------------------------------
-- 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
-------------------------------------------------------------------------------
-- Staff group check (cached per page parse)
-------------------------------------------------------------------------------
local isStaffCache = nil
local function isStaff(frame)
if isStaffCache == nil then
isStaffCache = mw.text.trim(frame:preprocess('{{#ifingroup:staff|1|0}}')) == '1'
end
return isStaffCache
end
-------------------------------------------------------------------------------
-- SingleLanguage / SingleColumn check (cached per page parse)
-------------------------------------------------------------------------------
local singleLangCache -- nil = not checked yet, false = not set
local singleColumnCache
local function getSingleLang(frame)
if singleLangCache == nil then
local pageTitle = mw.title.getCurrentTitle().prefixedText
local raw = mw.text.trim(frame:callParserFunction('#show', pageTitle, '?SingleLanguage'))
singleLangCache = (raw ~= '') and raw:lower() or false
end
return singleLangCache
end
local function getSingleColumn(frame)
if singleColumnCache == nil then
local pageTitle = mw.title.getCurrentTitle().prefixedText
local raw = mw.text.trim(frame:callParserFunction('#show', pageTitle, '?SingleColumn')):lower()
singleColumnCache = (raw == 'true' or raw == '1' or raw == 't')
end
return singleColumnCache
end
-------------------------------------------------------------------------------
-- formatRow: called by Template:DisplaySegmentRow for each SMW result
-- (absorbs the old DisplaySegmentRow template wrapper + FormatSegmentRow module)
-------------------------------------------------------------------------------
function p.formatRow(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)
local singleLang = getSingleLang(frame)
local singleColumn = getSingleColumn(frame)
local singleIsLeft = singleLang and inPriorityList(leftPriority, singleLang)
local singleIsRight = singleLang and inPriorityList(rightPriority, singleLang)
-- Build content columns
local content
if singleColumn then
local rendered = renderSide(args, 'left', format, frame, defaultLeft, languages)
content = '<div class="col-12 font-serif line14 p-bl-fix mb-3">' .. rendered .. '</div>'
elseif singleLang then
if singleIsLeft then
local rendered = renderSide(args, 'left', format, frame, singleLang, filterLanguages(languages, singleLang))
content = '<div class="col-12 font-serif line14 p-bl-fix mb-3">' .. rendered .. '</div>'
elseif singleIsRight then
local rendered = renderSide(args, 'right', format, frame, singleLang, filterLanguages(languages, singleLang))
content = '<div class="col-12 line14 p-bl-fix mb-3">' .. rendered .. '</div>'
else
local left = renderSide(args, 'left', format, frame, defaultLeft, languages)
local right = renderSide(args, 'right', format, frame, defaultRight, languages)
content = '<div class="col-md-6 font-serif line14 p-bl-fix mb-3">' .. left .. '</div>'
.. '<div class="col-md-6 line14 p-bl-fix mb-3">' .. right .. '</div>'
end
else
local left = renderSide(args, 'left', format, frame, defaultLeft, languages)
local right = renderSide(args, 'right', format, frame, defaultRight, languages)
content = '<div class="col-md-6 font-serif line14 p-bl-fix mb-3">' .. left .. '</div>'
.. '<div class="col-md-6 line14 p-bl-fix mb-3">' .. right .. '</div>'
end
-- Wrap in row div (previously done by Template:DisplaySegmentRow)
local parts = { '<div class="row bilingual-row py-1 filterable">' }
-- Staff debug info
if isStaff(frame) then
local resultNum = args['#'] or ''
local pageTitle = args[1] or ''
local segOrder = getParamValue(args, {'SegmentOrder'}) or ''
table.insert(parts,
'<div class="col-12 text-center font-sans-wiki text-90 py-1 mb-2">'
.. 'Query result #' .. resultNum
.. ' · '
.. '[' .. mw.uri.fullUrl(pageTitle):gsub('^//', 'https://') .. ' '
.. '<i class="fa-sharp-duotone fa-memo fa-sm"></i> TM segment #' .. segOrder
.. ']</div>'
)
end
table.insert(parts, content)
table.insert(parts, '</div>')
return table.concat(parts)
end
-------------------------------------------------------------------------------
-- renderChunks: emits deferred #ask queries, called from DisplayTmSection
-- Now also builds the #ask wikitext directly (absorbs Template:SegmentQuery)
-------------------------------------------------------------------------------
function p.renderChunks(frame)
local currentTitle = mw.title.getCurrentTitle()
local pargs = frame:getParent().args
local leaveOutArgs = pargs.LeaveOutArgs or ''
local transMemID = mw.text.trim(pargs.TransMemID or '')
-- Mirror SegmentQuery's fallback: if TranslationChapter not passed, use SUBPAGENAME
local chapterRaw = mw.text.trim(frame.args[1] or '')
local hasChapter = chapterRaw ~= ''
-- Build chapter condition
local chapterCondition = ''
if hasChapter then
local values = {}
for v in chapterRaw:gmatch('[^;]+') do
table.insert(values, mw.text.trim(v))
end
chapterCondition = '[[TranslationChapter::' .. table.concat(values, '||') .. ']]'
end
local baseConditions =
'[[TransMemID::' .. transMemID .. ']]' ..
chapterCondition ..
'[[SegmentFormat::!Deprecated]]'
-- 1. Count total segments
local countResult = mw.smw.ask(
baseConditions ..
'|?SegmentOrder' ..
'|limit=' .. MAX_SEGMENTS ..
'|link=none'
)
local totalSegments = countResult and #countResult or 0
if totalSegments == 0 then
return '<!-- SegmentQuery: 0 segments found for TransMemID=' .. transMemID .. ' -->'
end
-- 2. Check for any "Large content" flag in this chapter
local largeCheck = mw.smw.ask(
baseConditions ..
'[[Flag::Large content]]' ..
'|?SegmentOrder' ..
'|limit=1' ..
'|link=none'
)
local chunkSize = (largeCheck and #largeCheck > 0) and CHUNK_SIZE_LARGE or CHUNK_SIZE_NORMAL
-- 3. Build printout list, respecting LeaveOutArgs
local printouts = {}
for _, name in ipairs(ALL_PRINTOUTS) do
if ALWAYS_INCLUDE[name] or not leaveOutArgs:find(name, 1, true) then
table.insert(printouts, '|?' .. name)
end
end
local printoutStr = table.concat(printouts)
-- 4. Emit one deferred #ask per chunk
local parts = {}
local offset = 0
while offset < totalSegments do
-- Build the full #ask wikitext
local ask = '{{#ask:'
.. baseConditions
.. printoutStr
.. '|named args=true'
.. '|sort=SegmentOrder'
.. '|order=asc'
.. '|format=template'
.. '|template=DisplaySegmentRow'
.. '|limit=' .. chunkSize
.. '|offset=' .. offset
.. '|link=none'
.. '|intro=<div class="filterableContent mb-4">'
.. '|outro=</div>'
.. '|searchlabel='
.. '|default='
.. '|@deferred'
.. '}}'
table.insert(parts, frame:preprocess(ask))
offset = offset + chunkSize
end
return table.concat(parts)
end
return p