Module:LibraryRows
Documentation for this module may be created at Module:LibraryRows/doc
-- Bibliographic rows for the Library pages (Library, Library/Books, Library/Secondary
-- Sources, ...). Ported from BCA's Module:LibraryRows, Nov 2026: the rows use the same
-- markup as BCA's Template:TopicBiblioRow (.topic-biblio-*), so the shared tws.js
-- (type pills, text filter, "N of M loaded" status) and tws.main.scss styles apply.
--
-- They replace the PageTile grids, which sent 2.2 MB and 665 images for Secondary
-- Sources in one block and ~5 MB for the Library hub.
--
-- {{#invoke:LibraryRows|controls|types=Book;Dissertation;Article}}
-- text filter plus one pill per type (BuNayPageType), with counts
-- {{#invoke:LibraryRows|section|heading=Books|link=Library/Books|query=[[BuNayPageType::Book]]}}
-- a counted heading, then all rows, loaded in deferred chunks of CHUNK
-- ...|class=Multimedia
-- the pill class for every row of this section, instead of each item's own
-- BuNayPageType (texts store their language there: "Sanskrit Text")
-- ...|preview=5|sort=Creation date|order=desc|anchor=Books
-- only the first N rows, not deferred, then a "See all" link (the Library hub)
--
-- Rows are drawn by Template:LibraryRow; the printout labels below are its parameter
-- names (named args).
local p = {}
local CHUNK = 100
local PRINTOUTS = '|?Citation|?BuNayTitle=Title|?PageTilePhoto=Cover|?BuNayPageType=Type' ..
'|?subclass=Subclass|?Pubdate|?TileDescription|?ArticleAbstract|?Description' ..
-- Not displayed: carried in the row as hidden text so the filter also finds an
-- item by its topics and full title, as the old tiles' InvisibleFilterContent did.
'|?BuNayTopicTags=Topics|?Fulltitle|?Fulltitletrans=English'
-- Pill labels. The pill filters on the row's data-class, which is the singular type.
local PLURAL = {
Book = 'Books', Dissertation = 'Dissertations', Article = 'Articles',
Interview = 'Interviews', Video = 'Videos', Audio = 'Audio', Event = 'Events',
Sutra = 'Sutras', Commentary = 'Commentaries', Multimedia = 'Multimedia',
}
local function trim( s )
s = mw.text.trim( s or '' )
return s ~= '' and s or nil
end
local function fmt( n )
return mw.language.getContentLanguage():formatNum( n )
end
local function count( frame, query )
local n = frame:preprocess( '{{#ask:' .. query .. '|format=count}}' )
return tonumber( ( n or '' ):match( '%d+' ) ) or 0
end
function p.controls( frame )
local args = frame.args
local base = trim( args.base ) or ''
local pills, total = {}, 0
for t in mw.text.gsplit( trim( args.types ) or '', '%s*;%s*' ) do
if t ~= '' then
local n = count( frame, trim( args[ 'q:' .. t ] ) or ( base .. '[[BuNayPageType::' .. t .. ']]' ) )
if n > 0 then
total = total + n
table.insert( pills, '<span class="topic-biblio-pill" data-filter-class="' .. mw.text.encode( t ) ..
'">' .. ( PLURAL[ t ] or t ) .. ' <span class="rel-count">' .. fmt( n ) .. '</span></span>' )
end
end
end
-- One type only: the pills would just repeat the total, so show the filter alone.
-- The All pill is still emitted (hidden): tws.js reads the total from it for the
-- "N of M loaded" status.
local all = '<span class="topic-biblio-pill is-active"' .. ( #pills > 1 and '' or ' style="display:none"' ) ..
'>All <span class="rel-count">' .. fmt( total ) .. '</span></span>'
return '<div class="topic-biblio-controls"><div class="topic-biblio-filter">' ..
frame:preprocess( '{{#widget:FilterInput|placeholder=' .. ( trim( args.placeholder ) or
( 'Filter ' .. fmt( total ) .. ' items by title, author, year or topic…' ) ) .. '}}' ) ..
'</div><div class="topic-biblio-pills">' .. all .. ( #pills > 1 and table.concat( pills ) or '' ) .. '</div></div>'
end
function p.section( frame )
local args = frame.args
local query = trim( args.query )
if not query then return '' end
local heading = trim( args.heading )
local link = trim( args.link )
local sort = trim( args.sort ) or 'Citation'
local order = trim( args.order ) or 'asc'
local preview = tonumber( trim( args.preview ) or '' )
local class = trim( args.class )
local n = count( frame, query )
if n == 0 then return '' end
local out = { '<div class="topic-biblio-section">' }
-- anchor=: a fixed id for the heading, for links such as the Library sidebar's
-- contents (#Books). The heading's own id would include the count.
if trim( args.anchor ) then
table.insert( out, '<span id="' .. mw.text.encode( trim( args.anchor ) ) .. '"></span>' )
end
if heading then
local label = link and ( '[[' .. link .. '|' .. heading .. ']]' ) or heading
table.insert( out, '<h2 class="link-no-border"><span class="rel-count">' .. fmt( n ) .. '</span> ' .. label .. '</h2>' )
end
table.insert( out, '<div class="topic-biblio-list">' )
local base = '{{#ask:' .. query .. PRINTOUTS ..
'|format=template|template=LibraryRow|named args=yes|link=none' ..
'|sort=' .. sort .. '|order=' .. order .. '|searchlabel=|default=' ..
( class and ( '|userparam=' .. class ) or '' )
if preview then
table.insert( out, frame:preprocess( base .. '|limit=' .. preview .. '}}' ) )
else
for offset = 0, n - 1, CHUNK do
-- Each deferred chunk is parsed on its own, so the page's __NOGLOSSARY__ does
-- not reach it; a wrapper whose class is exactly "noglossary" keeps Lingo out
-- (on BCA a chunk of 100 rows went from 800 to 440 ms).
table.insert( out, frame:preprocess( base .. '|limit=' .. CHUNK .. '|offset=' .. offset ..
'|intro=<div class="noglossary">|outro=</div>|@deferred}}' ) )
end
end
table.insert( out, '</div>' )
if preview and link and n > preview then
table.insert( out, '<div class="library-see-all mt-2 font-sans">[[' .. link .. '|See all ' .. fmt( n ) .. ' ' ..
mw.ustring.lower( heading or 'items' ) .. ' ›]]</div>' )
end
table.insert( out, '</div>' )
return table.concat( out )
end
return p