Module:BCIChapters: Difference between revisions
No edit summary |
No edit summary |
||
Line 1: | Line 1: | ||
local p = {} | local p = {} | ||
-- | -- Utility function to log tables | ||
local | local function logTable(tbl, prefix) | ||
local result = {} | |||
local function log(tbl, level) | |||
for k, v in pairs(tbl) do | |||
local key = prefix .. string.rep(" ", level * 2) .. tostring(k) | |||
if type(v) == "table" then | |||
result[#result + 1] = key .. " = table" | |||
log(v, level + 1) | |||
else | |||
result[#result + 1] = key .. " = " .. tostring(v) | |||
end | |||
end | |||
end | |||
log(tbl, 0) | |||
mw.log(result) | |||
end | |||
function p.urlDecode(str) | function p.urlDecode(str) | ||
Line 16: | Line 29: | ||
str = string.gsub(str, "%%27", "'") | str = string.gsub(str, "%%27", "'") | ||
return str | return str | ||
end | end | ||
Line 50: | Line 47: | ||
local chapters = {} | local chapters = {} | ||
for seq, title, date, pagecount in string.gmatch(content, "{{BCIChapter|seq=(%d+)|title=([^|]+)|date=([^|]+)|pagecount=(%d+)}}") do | for seq, title, date, pagecount in string.gmatch(content, "{{BCIChapter|seq=(%d+)|title=([^|]+)|date=([^|]+)|pagecount=(%d+)}}") do | ||
table.insert(chapters, {seq = | table.insert(chapters, {seq = seq, title = title, date = date, pagecount = tonumber(pagecount)}) | ||
end | end | ||
logTable(chapters, "Chapters list = ") | |||
return chapters | return chapters | ||
end | end | ||
function p. | function p.getChapterDetails(chapterTitle, detail) | ||
local chapters = p.getChaptersList() | local chapters = p.getChaptersList() | ||
local decodedTitle = p.urlDecode(chapterTitle) | |||
local normalizedTitle = p.normalizeApostrophes(decodedTitle) | |||
for _, chapter in ipairs(chapters) do | for _, chapter in ipairs(chapters) do | ||
if chapter. | if chapter.title == normalizedTitle then | ||
mw. | mw.log("Found detail for title:", normalizedTitle, detail, chapter[detail]) | ||
return chapter | return chapter[detail] | ||
end | end | ||
end | end | ||
mw.log("No | mw.log("No detail found for title:", chapterTitle, "detail:", detail) | ||
return nil | return nil | ||
end | end | ||
function p.getChapterDetailsBySeq(seq, detail) | function p.getChapterDetailsBySeq(seq, detail) | ||
local chapters = p.getChaptersList() | local chapters = p.getChaptersList() | ||
for _, chapter in ipairs(chapters) do | for _, chapter in ipairs(chapters) do | ||
if chapter. | if tonumber(chapter.seq) == tonumber(seq) then | ||
mw.log(" | mw.log("Found detail for seq:", seq, detail, chapter[detail]) | ||
return chapter[detail] | return chapter[detail] | ||
end | end | ||
end | end | ||
mw.log("No detail found for | mw.log("No detail found for seq:", seq, "detail:", detail) | ||
return nil | return nil | ||
end | end | ||
Line 97: | Line 82: | ||
function p.getNextChapter(chapterTitle) | function p.getNextChapter(chapterTitle) | ||
local chapters = p.getChaptersList() | local chapters = p.getChaptersList() | ||
local decodedTitle = p.urlDecode(chapterTitle) | |||
local decodedTitle = p.urlDecode( | |||
local normalizedTitle = p.normalizeApostrophes(decodedTitle) | local normalizedTitle = p.normalizeApostrophes(decodedTitle) | ||
for i, chapter in ipairs(chapters) do | for i, chapter in ipairs(chapters) do | ||
if chapter.title == normalizedTitle and i < #chapters then | if chapter.title == normalizedTitle and i < #chapters then | ||
mw.log("Next chapter for title " | mw.log("Next chapter for title:", normalizedTitle, "is", chapters[i + 1].title) | ||
return chapters[i + 1].title | return chapters[i + 1].title | ||
end | end | ||
end | end | ||
mw.log("No next chapter found for title: " | mw.log("No next chapter found for title:", chapterTitle) | ||
return nil | return nil | ||
end | end | ||
function p. | function p.getNextChapterBySeq(seq) | ||
local chapters = p.getChaptersList() | local chapters = p.getChaptersList() | ||
for i, chapter in ipairs(chapters) do | for i, chapter in ipairs(chapters) do | ||
if chapter. | if tonumber(chapter.seq) == tonumber(seq) and i < #chapters then | ||
mw.log(" | mw.log("Next chapter for seq:", seq, "is", chapters[i + 1].title) | ||
return chapters[i | return chapters[i + 1].title | ||
end | end | ||
end | end | ||
mw.log("No | mw.log("No next chapter found for seq:", seq) | ||
return nil | return nil | ||
end | end | ||
function p. | function p.getPreviousChapter(chapterTitle) | ||
local chapters = p.getChaptersList() | local chapters = p.getChaptersList() | ||
local decodedTitle = p.urlDecode(chapterTitle) | |||
local normalizedTitle = p.normalizeApostrophes(decodedTitle) | |||
for i, chapter in ipairs(chapters) do | for i, chapter in ipairs(chapters) do | ||
if chapter. | if chapter.title == normalizedTitle and i > 1 then | ||
mw.log(" | mw.log("Previous chapter for title:", normalizedTitle, "is", chapters[i - 1].title) | ||
return chapters[i | return chapters[i - 1].title | ||
end | end | ||
end | end | ||
mw.log("No | mw.log("No previous chapter found for title:", chapterTitle) | ||
return nil | return nil | ||
end | end | ||
Line 140: | Line 123: | ||
local chapters = p.getChaptersList() | local chapters = p.getChaptersList() | ||
for i, chapter in ipairs(chapters) do | for i, chapter in ipairs(chapters) do | ||
if chapter.seq == tonumber(seq) and i > 1 then | if tonumber(chapter.seq) == tonumber(seq) and i > 1 then | ||
mw.log("Previous chapter for seq " | mw.log("Previous chapter for seq:", seq, "is", chapters[i - 1].title) | ||
return chapters[i - 1].title | return chapters[i - 1].title | ||
end | end | ||
end | end | ||
mw.log("No previous chapter found for seq: " | mw.log("No previous chapter found for seq:", seq) | ||
return nil | return nil | ||
end | end | ||
Line 160: | Line 143: | ||
function p.infoboxBCIChapter(frame) | function p.infoboxBCIChapter(frame) | ||
local chapterTitle = frame.args.chapter_title or mw.title.getCurrentTitle().text | local chapterTitle = frame.args.chapter_title or mw.title.getCurrentTitle().text | ||
local | local decodedTitle = p.urlDecode(chapterTitle) | ||
local normalizedTitle = p.normalizeApostrophes(decodedTitle) | |||
local seq = frame.args.seq | |||
local image = frame.args.image | local image = frame.args.image | ||
local caption = frame.args.caption | local caption = frame.args.caption | ||
Line 207: | Line 153: | ||
caption = caption and caption ~= "" and caption or nil | caption = caption and caption ~= "" and caption or nil | ||
local date, pagecount, next_chapter, prev_chapter | |||
if seq and special_cases[normalizedTitle] then | |||
date = p.getChapterDetailsBySeq(seq, "date") or "N/A" | |||
pagecount = p.getChapterDetailsBySeq(seq, "pagecount") or "N/A" | |||
next_chapter = p.getNextChapterBySeq(seq) or "N/A" | |||
prev_chapter = p.getPreviousChapterBySeq(seq) or "N/A" | |||
else | |||
date = p.getChapterDetails(normalizedTitle, "date") or "N/A" | |||
pagecount = p.getChapterDetails(normalizedTitle, "pagecount") or "N/A" | |||
next_chapter = p.getNextChapter(normalizedTitle) or "N/A" | |||
prev_chapter = p.getPreviousChapter(normalizedTitle) or "N/A" | |||
end | |||
mw.log("Infobox data for regular chapter =", { | |||
chapterTitle = chapterTitle, | |||
date = date, | |||
pagecount = pagecount, | |||
next_chapter = next_chapter, | |||
prev_chapter = prev_chapter | |||
}) | |||
local infobox = mw.html.create('table') | local infobox = mw.html.create('table') | ||
infobox:addClass('infobox') | infobox:addClass('infobox') | ||
-- Add title as heading | |||
-- Add title as heading | |||
infobox:tag('tr') | infobox:tag('tr') | ||
:tag('th') | :tag('th') |
Revision as of 13:13, 27 May 2024
Documentation for this module may be created at Module:BCIChapters/doc
local p = {}
-- Utility function to log tables
local function logTable(tbl, prefix)
local result = {}
local function log(tbl, level)
for k, v in pairs(tbl) do
local key = prefix .. string.rep(" ", level * 2) .. tostring(k)
if type(v) == "table" then
result[#result + 1] = key .. " = table"
log(v, level + 1)
else
result[#result + 1] = key .. " = " .. tostring(v)
end
end
end
log(tbl, 0)
mw.log(result)
end
function p.urlDecode(str)
str = string.gsub(str, '%%(%x%x)', function(x) return string.char(tonumber(x, 16)) end)
return str
end
function p.normalizeApostrophes(str)
str = string.gsub(str, "’", "'")
str = string.gsub(str, "'", "'")
str = string.gsub(str, "%%27", "'")
return str
end
function p.getChaptersList()
local listPage = mw.title.new('Template:BCIChapterList')
if not listPage then
return {}
end
local content = listPage:getContent()
if not content then
return {}
end
-- Normalize apostrophes in content
content = p.normalizeApostrophes(content)
local chapters = {}
for seq, title, date, pagecount in string.gmatch(content, "{{BCIChapter|seq=(%d+)|title=([^|]+)|date=([^|]+)|pagecount=(%d+)}}") do
table.insert(chapters, {seq = seq, title = title, date = date, pagecount = tonumber(pagecount)})
end
logTable(chapters, "Chapters list = ")
return chapters
end
function p.getChapterDetails(chapterTitle, detail)
local chapters = p.getChaptersList()
local decodedTitle = p.urlDecode(chapterTitle)
local normalizedTitle = p.normalizeApostrophes(decodedTitle)
for _, chapter in ipairs(chapters) do
if chapter.title == normalizedTitle then
mw.log("Found detail for title:", normalizedTitle, detail, chapter[detail])
return chapter[detail]
end
end
mw.log("No detail found for title:", chapterTitle, "detail:", detail)
return nil
end
function p.getChapterDetailsBySeq(seq, detail)
local chapters = p.getChaptersList()
for _, chapter in ipairs(chapters) do
if tonumber(chapter.seq) == tonumber(seq) then
mw.log("Found detail for seq:", seq, detail, chapter[detail])
return chapter[detail]
end
end
mw.log("No detail found for seq:", seq, "detail:", detail)
return nil
end
function p.getNextChapter(chapterTitle)
local chapters = p.getChaptersList()
local decodedTitle = p.urlDecode(chapterTitle)
local normalizedTitle = p.normalizeApostrophes(decodedTitle)
for i, chapter in ipairs(chapters) do
if chapter.title == normalizedTitle and i < #chapters then
mw.log("Next chapter for title:", normalizedTitle, "is", chapters[i + 1].title)
return chapters[i + 1].title
end
end
mw.log("No next chapter found for title:", chapterTitle)
return nil
end
function p.getNextChapterBySeq(seq)
local chapters = p.getChaptersList()
for i, chapter in ipairs(chapters) do
if tonumber(chapter.seq) == tonumber(seq) and i < #chapters then
mw.log("Next chapter for seq:", seq, "is", chapters[i + 1].title)
return chapters[i + 1].title
end
end
mw.log("No next chapter found for seq:", seq)
return nil
end
function p.getPreviousChapter(chapterTitle)
local chapters = p.getChaptersList()
local decodedTitle = p.urlDecode(chapterTitle)
local normalizedTitle = p.normalizeApostrophes(decodedTitle)
for i, chapter in ipairs(chapters) do
if chapter.title == normalizedTitle and i > 1 then
mw.log("Previous chapter for title:", normalizedTitle, "is", chapters[i - 1].title)
return chapters[i - 1].title
end
end
mw.log("No previous chapter found for title:", chapterTitle)
return nil
end
function p.getPreviousChapterBySeq(seq)
local chapters = p.getChaptersList()
for i, chapter in ipairs(chapters) do
if tonumber(chapter.seq) == tonumber(seq) and i > 1 then
mw.log("Previous chapter for seq:", seq, "is", chapters[i - 1].title)
return chapters[i - 1].title
end
end
mw.log("No previous chapter found for seq:", seq)
return nil
end
function p.formatDate(date, frame)
local year, month, day = string.match(date, "(%d+)%-(%d+)%-(%d+)")
if year and month and day then
return frame:expandTemplate{title = "Start date", args = {year, month, day}}
else
return date
end
end
function p.infoboxBCIChapter(frame)
local chapterTitle = frame.args.chapter_title or mw.title.getCurrentTitle().text
local decodedTitle = p.urlDecode(chapterTitle)
local normalizedTitle = p.normalizeApostrophes(decodedTitle)
local seq = frame.args.seq
local image = frame.args.image
local caption = frame.args.caption
local major_characters = frame.args.major_characters
local minor_characters = frame.args.minor_characters
local locations = frame.args.locations
caption = caption and caption ~= "" and caption or nil
local date, pagecount, next_chapter, prev_chapter
if seq and special_cases[normalizedTitle] then
date = p.getChapterDetailsBySeq(seq, "date") or "N/A"
pagecount = p.getChapterDetailsBySeq(seq, "pagecount") or "N/A"
next_chapter = p.getNextChapterBySeq(seq) or "N/A"
prev_chapter = p.getPreviousChapterBySeq(seq) or "N/A"
else
date = p.getChapterDetails(normalizedTitle, "date") or "N/A"
pagecount = p.getChapterDetails(normalizedTitle, "pagecount") or "N/A"
next_chapter = p.getNextChapter(normalizedTitle) or "N/A"
prev_chapter = p.getPreviousChapter(normalizedTitle) or "N/A"
end
mw.log("Infobox data for regular chapter =", {
chapterTitle = chapterTitle,
date = date,
pagecount = pagecount,
next_chapter = next_chapter,
prev_chapter = prev_chapter
})
local infobox = mw.html.create('table')
infobox:addClass('infobox')
-- Add title as heading
infobox:tag('tr')
:tag('th')
:attr('colspan', '2')
:css('text-align', 'center')
:css('font-size', '125%')
:css('padding', '5px')
:wikitext(chapterTitle)
:done()
:done()
-- Optionally add image with standard size
if image and image ~= "" then
infobox:tag('tr')
:tag('td')
:attr('colspan', '2')
:css('text-align', 'center')
:wikitext('[[File:' .. image .. '|250px]]')
:done()
:done()
-- Add caption below image if provided
if caption then
infobox:tag('tr')
:tag('td')
:attr('colspan', '2')
:css('text-align', 'center')
:css('font-size', '90%')
:css('padding', '5px')
:wikitext(caption)
:done()
:done()
end
end
infobox:tag('tr')
:tag('th'):wikitext('Published'):done()
:tag('td'):wikitext(p.formatDate(date, frame)):done()
:done()
infobox:tag('tr')
:tag('th'):wikitext('Page count'):done()
:tag('td'):wikitext(pagecount):done()
:done()
-- Add additional manual entry parameters
if major_characters and major_characters ~= "" then
infobox:tag('tr')
:tag('th'):wikitext('Major characters'):done()
:tag('td'):wikitext(major_characters):done()
:done()
end
if minor_characters and minor_characters ~= "" then
infobox:tag('tr')
:tag('th'):wikitext('Minor characters'):done()
:tag('td'):wikitext(minor_characters):done()
:done()
end
if locations and locations ~= "" then
infobox:tag('tr')
:tag('th'):wikitext('Locations'):done()
:tag('td'):wikitext(locations):done()
:done()
end
-- Add chapter navigation heading
infobox:tag('tr')
:tag('th')
:attr('colspan', '2')
:css('text-align', 'center')
:css('font-size', '110%')
:css('padding', '5px')
:css('background-color', '#f0f0f0')
:wikitext('Chapter navigation')
:done()
:done()
-- Add chapter navigation links in separate columns
local nav_row = infobox:tag('tr')
nav_row:tag('td')
:css('text-align', 'left')
:wikitext(prev_chapter ~= "N/A" and string.format("[[%s:%s|← %s]]", mw.title.getCurrentTitle().nsText, prev_chapter, prev_chapter) or "← Previous")
:done()
nav_row:tag('td')
:css('text-align', 'right')
:wikitext(next_chapter ~= "N/A" and string.format("[[%s:%s|%s →]]", mw.title.getCurrentTitle().nsText, next_chapter, next_chapter) or "Next →")
:done()
nav_row:done()
-- Add link to read chapter in BCI Members' Library
infobox:tag('tr')
:tag('td')
:attr('colspan', '2')
:css('text-align', 'center')
:wikitext("[https://bittersweetcandybowl.com/members/ Read in the BCI Members' Library]")
:done()
:done()
-- Return the complete wikitext
return frame:preprocess(tostring(infobox))
end
return p