Toggle menu
Toggle preferences menu
Toggle personal menu
Not logged in
Your IP address will be publicly visible if you make any edits.

Editing Module:BSW/MainPage

From the only original and legitimate Battlestar Wiki: the free-as-in-beer, non-corporate, open-content encyclopedia, analytical reference, and episode guide on all things Battlestar Galactica. Accept neither subpar substitutes nor subpar clones.
Warning: You are not logged in. Your IP address will be publicly visible if you make any edits. If you log in or create an account, your edits will be attributed to your username, along with other benefits.
The edit can be undone. Please check the comparison below to verify that this is what you want to do, and then publish the changes below to finish undoing the edit.
Latest revision Your text
Line 1: Line 1:
-- Module:BSW/MainPage
-- Module:BSW/MainPage
-- MW 1.45 compatible. Uses only sanitizer-safe HTML elements.
-- Generates main page HTML using raw string concatenation.
-- <button> is NOT on the MW 1.45 allowed list — replaced with
-- mw.html is NOT used because its output is passed through
-- <span role="button"> which IS allowed and is wired by JS.
-- MediaWiki's HTML sanitizer which strips <button> and
-- External <a class="..."> replaced with wikitext [url text]
-- <a class="..."> elements.
-- processed via frame:preprocess().
-- Instead we build raw HTML strings and return them via
-- frame:callParserFunction to inject them as strip markers
-- that survive the sanitizer pass.
--
-- Usage: {{#invoke:BSW/MainPage|functionName|param=value|...}}


local p = {}
local p = {}


-- ── Helpers ───────────────────────────────────────────────────────
-- ── HTML helpers ──────────────────────────────────────────────────


local function e( s )
local function esc( s )
     s = tostring( s or '' )
     s = tostring( s or '' )
     s = s:gsub( '&', '&amp;' ):gsub( '<', '&lt;' ):gsub( '>', '&gt;' ):gsub( '"', '&quot;' )
     s = s:gsub( '&', '&amp;' )
    s = s:gsub( '<', '&lt;' )
    s = s:gsub( '>', '&gt;' )
    s = s:gsub( '"', '&quot;' )
     return s
     return s
end
end


local function a( name, val )
-- Build an attribute string, skipping empty values
     if val and val ~= '' then return ' ' .. name .. '="' .. e(val) .. '"' end
local function atr( name, value )
     if value and value ~= '' then
        return ' ' .. name .. '="' .. esc( value ) .. '"'
    end
     return ''
     return ''
end
end


-- Wikitext internal link: [[target|label]]
-- Wrap output so it bypasses the sanitizer.
local function wlink( frame, target, label )
-- We use the <score> trick: any extension tag registered in MW
    return frame:preprocess( '[[' .. target .. '|' .. (label or target) .. ']]' )
-- acts as a strip marker. But the cleanest method available in
end
-- Scribunto is to mark output as safe using the internal
-- mw.text.nowiki approach combined with frame preprocessing.
-- The most reliable cross-version approach: return the HTML
-- inside a <templatestyles> strip tag... but actually the
-- correct documented approach for Scribunto is simply:
--  return frame:preprocess( '<html>' .. html .. '</html>' )
-- but <html> tag needs to be enabled. So we use the
-- well-documented mw workaround: build wikitext that produces
-- the HTML we want via #tag parser functions for the
-- problematic elements (button, a with class).


-- External link via wikitext: [url label] — survives sanitizer
-- Build a <button> via #tag parser function call
local function xlink( frame, url, label )
local function btn( frame, id, cls, onclick, label )
     return frame:preprocess( '<span class="plainlinks">[' .. url .. ' ' .. (label or url) .. ']</span>' )
     local attrs = {}
    if id and id ~= '' then attrs['id'] = id end
    if cls then
        -- #tag doesn't support class directly, embed in attr string
    end
    -- Use frame:callParserFunction with #tag
    -- #tag:button|label|id=...|class=...|aria-label=...
    -- This is the correct way to emit elements the sanitizer would block
    local params = { label }
    params['class'] = cls or ''
    if id and id ~= '' then params['id'] = id end
    if onclick and onclick ~= '' then params['onclick'] = onclick end
    return frame:callParserFunction( '#tag:button', params )
end
end


-- Span acting as a button — sanitizer-safe, JS wires the behaviour
-- Build an <a> tag via #tag
local function spanBtn( id, cls, onclick, label )
local function anchor( frame, href, cls, content, attrs_extra )
     return '<span' ..
     local params = { content }
        a('id', id) ..
    params['href'] = href or '#'
        a('class', cls) ..
     if cls and cls ~= '' then params['class'] = cls end
        a('role', 'button') ..
     if attrs_extra then
        a('tabindex', '0') ..
        for k, v in pairs( attrs_extra ) do
        a('onclick', onclick) ..
            params[ k ] = v
        '>' .. (label or '') .. '</span>'
         end
end
 
-- Check if transcluded content is just a red link (page doesn't exist).
-- Returns true if content should be hidden.
local function isRedlink( content )
     if not content or mw.text.trim( content ) == '' then return true end
    -- MW renders missing transclusions as red links with class="new"
     -- Check if content is ONLY a red link and nothing else meaningful
    local stripped = mw.text.trim( content )
    -- If it starts with the page title link pattern for a missing page
    -- (the whole content is just one red link), hide it
    if stripped:match( '^<a[^>]+class="[^"]*new[^"]*"' ) and
      not stripped:match( '</a>%s*%S' ) then
         return true
     end
     end
     return false
     return frame:callParserFunction( '#tag:a', params )
end
end


-- Wrap a card so JS can hide it if content is a red link
-- ── hero() ────────────────────────────────────────────────────────
local function conditionalCard( id, html )
function p.hero( frame )
     return '<div class="bsw-conditional-card"' .. a('id', id) .. '>' .. html .. '</div>'
     local args  = frame.args
end
    local count = tonumber( args.count ) or 0
    local out  = {}


-- ── welcome() ────────────────────────────────────────────────────
     out[#out+1] = '<div class="bsw-hero">'
function p.welcome( frame )
     local articles  = mw.site.stats.articles
    local formatted = tostring( articles ):reverse():gsub( '(%d%d%d)', '%1,' ):reverse():gsub( '^,', '' )


     local logo = frame:preprocess( '[[File:BSG-WIKI SM.png|40px|link=Main Page|alt=Battlestar Wiki]]' )
     for i = 1, count do
        local pfx    = 'slide' .. i .. '_'
        local article = mw.text.trim( args[ pfx .. 'article'    ] or '' )
        local series  = mw.text.trim( args[ pfx .. 'series'    ] or '' )
        local title  = mw.text.trim( args[ pfx .. 'title'      ] or article )
        local tlink  = mw.text.trim( args[ pfx .. 'titlelink'  ] or article )
        local desc    = mw.text.trim( args[ pfx .. 'desc'      ] or '' )
        local bcolor  = mw.text.trim( args[ pfx .. 'badgecolor' ] or '' )


    local desc = frame:preprocess(
        local active = ( i == 1 ) and ' bsw-active' or ''
         "The free, non-corporate encyclopedia on all things " ..
         out[#out+1] = '<div class="bsw-slide' .. active .. '"' .. atr( 'data-article', article ) .. '>'
         "''Battlestar Galactica'' \226\128\148 " ..
         out[#out+1] =  '<div class="bsw-slide-bg"></div>'
         formatted .. "\194\160articles and counting."
        out[#out+1] =  '<div class="bsw-slide-overlay"></div>'
    )
        out[#out+1] =  '<div class="bsw-slide-content">'
         out[#out+1] =    '<div class="bsw-slide-badge">'


    local inner =
        local dot_style = bcolor ~= '' and ' style="background:' .. bcolor .. '"' or ''
        '<div class="bsw-welcome-logo">' .. logo .. '</div>' ..
         out[#out+1] =      '<span class="bsw-slide-badge-dot"' .. dot_style .. '></span>'
        '<div class="bsw-welcome-text">' ..
         out[#out+1] =      '<span>' .. esc( series ) .. '</span>'
         '<div class="bsw-welcome-title">Battlestar Wiki</div>' ..
         out[#out+1] =    '</div>'
        '<div class="bsw-welcome-desc">' .. desc .. '</div>' ..
        '</div>' ..
         '<div class="bsw-welcome-links">' ..
        frame:preprocess( '[[Special:Random|Random article]]' ) ..
        frame:preprocess( '[[Battlestar_Wiki:About|About]]' ) ..
         frame:preprocess( '[[Battlestar_Wiki:Community_portal|Community]]' ) ..
        '</div>'


     return '<div class="bsw-welcome">' .. inner .. '</div>'
        -- Title: wikilink rendered by preprocessor
end
        local wlink = frame:preprocess( '[[' .. tlink .. '|' .. title .. ']]' )
        out[#out+1] =     '<div class="bsw-slide-title">' .. wlink .. '</div>'
        out[#out+1] =    '<div class="bsw-slide-desc">' .. esc( desc ) .. '</div>'
        out[#out+1] =  '</div>'
        out[#out+1] = '</div>'
    end


    -- Dots using #tag:button via callParserFunction
    out[#out+1] = '<div class="bsw-hero-dots">'
    for i = 1, count do
        local cls = 'bsw-hero-dot' .. ( i == 1 and ' bsw-active' or '' )
        out[#out+1] = btn( frame, nil, cls, nil, '' )
        -- Inject aria-label by replacing the closing > since #tag doesn't
        -- let us set aria-label easily — use a separate call with all attrs
        -- Actually #tag supports arbitrary attrs, rewrite:
    end
    out[#out+1] = '</div>'


function p.hero( frame )
    -- Redo dots properly — #tag:button supports all HTML attrs
     local args  = frame.args
    -- Remove the dots section we just built and redo
    local count = tonumber( args.count ) or 0
    -- (pop back to before the dots div)
     local html  = '<div class="bsw-hero">'
     local dots_html = '<div class="bsw-hero-dots">'
    for i = 1, count do
        local cls = 'bsw-hero-dot' .. ( i == 1 and ' bsw-active' or '' )
        local dot_params = { '' }
        dot_params['class'] = cls
        dot_params['aria-label'] = 'Slide ' .. i
        dots_html = dots_html .. frame:callParserFunction( '#tag:button', dot_params )
    end
    dots_html = dots_html .. '</div>'
 
    -- Remove the bad dots we pushed, replace
    -- (simpler: just build the whole thing cleanly from scratch)
     local result = '<div class="bsw-hero">'


     for i = 1, count do
     for i = 1, count do
         local pfx   = 'slide' .. i .. '_'
         local pfx     = 'slide' .. i .. '_'
         local art    = mw.text.trim( args[pfx..'article']   or '' )
         local article = mw.text.trim( args[ pfx .. 'article'   ] or '' )
         local series = mw.text.trim( args[pfx..'series']     or '' )
         local series = mw.text.trim( args[ pfx .. 'series'     ] or '' )
         local title = mw.text.trim( args[pfx..'title']     or art )
         local title   = mw.text.trim( args[ pfx .. 'title'     ] or article )
         local tlink = mw.text.trim( args[pfx..'titlelink'] or art )
         local tlink   = mw.text.trim( args[ pfx .. 'titlelink' ] or article )
         local desc   = mw.text.trim( args[pfx..'desc']       or '' )
         local desc   = mw.text.trim( args[ pfx .. 'desc'       ] or '' )
         local bcolor = mw.text.trim( args[pfx..'badgecolor'] or '' )
         local bcolor = mw.text.trim( args[ pfx .. 'badgecolor' ] or '' )


         local active   = i == 1 and ' bsw-active' or ''
         local active = ( i == 1 ) and ' bsw-active' or ''
         local dot_sty  = bcolor ~= '' and ' style="background:' .. bcolor .. '"' or ''
         local dot_style = bcolor ~= '' and ' style="background:' .. bcolor .. '"' or ''
         local wl      = wlink( frame, tlink, title )
         local wlink = frame:preprocess( '[[' .. tlink .. '|' .. title .. ']]' )


         html = html ..
         result = result ..
             '<div class="bsw-slide' .. active .. '"' .. a('data-article', art) .. '>' ..
             '<div class="bsw-slide' .. active .. '"' .. atr( 'data-article', article ) .. '>' ..
             '<div class="bsw-slide-bg"></div>' ..
             '<div class="bsw-slide-bg"></div>' ..
             '<div class="bsw-slide-overlay"></div>' ..
             '<div class="bsw-slide-overlay"></div>' ..
             '<div class="bsw-slide-content">' ..
             '<div class="bsw-slide-content">' ..
             '<div class="bsw-slide-badge">' ..
             '<div class="bsw-slide-badge">' ..
             '<span class="bsw-slide-badge-dot"' .. dot_sty .. '></span>' ..
             '<span class="bsw-slide-badge-dot"' .. dot_style .. '></span>' ..
             '<span>' .. e(series) .. '</span>' ..
             '<span>' .. esc( series ) .. '</span>' ..
             '</div>' ..
             '</div>' ..
             '<div class="bsw-slide-title">' .. wl .. '</div>' ..
             '<div class="bsw-slide-title">' .. wlink .. '</div>' ..
             '<div class="bsw-slide-desc">' .. e(desc) .. '</div>' ..
             '<div class="bsw-slide-desc">' .. esc( desc ) .. '</div>' ..
             '</div>' ..
             '</div>' ..
             '</div>'
             '</div>'
     end
     end


     -- Dots: span[role=button] — sanitizer safe
    result = result .. dots_html
     html = html .. '<div class="bsw-hero-dots">'
 
     for i = 1, count do
     -- Nav buttons
        local cls = 'bsw-hero-dot' .. (i == 1 and ' bsw-active' or '')
     local prev_params = { '' }
        html = html .. spanBtn(nil, cls, 'bswGoSlide(' .. (i-1) .. ')', '')
     prev_params['id'] = 'bsw-hero-prev'
     end
    prev_params['aria-label'] = 'Previous slide'
    html = html .. '</div>'
    local next_params = { '' }
    next_params['id'] = 'bsw-hero-next'
     next_params['aria-label'] = 'Next slide'


     -- Prev/next: span[role=button]
     result = result ..
    html = html ..
         '<div class="bsw-hero-nav">' ..
         '<div class="bsw-hero-nav">' ..
         spanBtn('bsw-hero-prev', 'bsw-hero-btn', 'bswPrevSlide()', '&#8249;') ..
         frame:callParserFunction( '#tag:button', prev_params ) ..
         spanBtn('bsw-hero-next', 'bsw-hero-btn', 'bswNextSlide()', '&#8250;') ..
         frame:callParserFunction( '#tag:button', next_params ) ..
         '</div>' ..
         '</div>' ..
         '</div>'
         '</div>'


     return html
     return result
end
end


Line 149: Line 193:
     local count = tonumber( args.count ) or 0
     local count = tonumber( args.count ) or 0


     local html =
     local result =
         '<div class="bsw-portals">' ..
         '<div class="bsw-portals">' ..
         '<div class="bsw-portals-label">∞ Portals of Battlestar Wiki ∞</div>' ..
         '<div class="bsw-portals-label">∞ Portals of Battlestar Wiki ∞</div>' ..
Line 156: Line 200:
     for i = 1, count do
     for i = 1, count do
         local pfx    = 'portal' .. i .. '_'
         local pfx    = 'portal' .. i .. '_'
         local href  = mw.text.trim( args[pfx..'href']   or '#' )
         local href  = mw.text.trim( args[ pfx .. 'href'   ] or '#' )
         local stripe = mw.text.trim( args[pfx..'stripe'] or '' )
         local stripe = mw.text.trim( args[ pfx .. 'stripe' ] or '' )
         local icon  = mw.text.trim( args[pfx..'icon']   or '' )
         local icon  = mw.text.trim( args[ pfx .. 'icon'   ] or '' )
         local name  = mw.text.trim( args[pfx..'name']   or '' )
         local name  = mw.text.trim( args[ pfx .. 'name'   ] or '' )
         local sub    = mw.text.trim( args[pfx..'sub']   or '' )
         local sub    = mw.text.trim( args[ pfx .. 'sub'   ] or '' )
 
        local stripe_style = stripe ~= '' and ' style="background:' .. stripe .. '"' or ''
        local stripe_div = '<div class="bsw-portal-stripe"' .. stripe_style .. '></div>'


        -- Internal links (/Portal:...) work as wikitext links
        -- Use span wrapper styled as link block via CSS
        local stripe_sty = stripe ~= '' and ' style="background:' .. stripe .. '"' or ''
         local inner =
         local inner =
             '<div class="bsw-portal-stripe"' .. stripe_sty .. '></div>' ..
             stripe_div ..
             '<span class="bsw-portal-icon">' .. icon .. '</span>' ..
             '<span class="bsw-portal-icon">' .. icon .. '</span>' ..
             '<span class="bsw-portal-name">' .. e(name) .. '</span>' ..
             '<span class="bsw-portal-name">' .. esc( name ) .. '</span>' ..
             '<span class="bsw-portal-sub">'  .. e(sub) .. '</span>'
             '<span class="bsw-portal-sub">'  .. esc( sub ) .. '</span>'
 
        local a_params = { inner }
        a_params['class'] = 'bsw-portal'
        a_params['href']  = href


         -- Use MW internal link syntax — href is a /Wiki_Path
         result = result .. frame:callParserFunction( '#tag:a', a_params )
        -- Strip leading slash to get page title
        local page = href:gsub('^/', '')
        local link = frame:preprocess( '[[' .. page .. '|<div class="bsw-portal">' .. inner .. '</div>]]' )
        html = html .. link
     end
     end


     html = html .. '</div></div>'
     result = result .. '</div></div>'
     return html
     return result
end
end


-- ── card header helper ────────────────────────────────────────────
-- ── statsblock() ──────────────────────────────────────────────────
-- Returns card-hd div; link is rendered as wikitext [url text]
function p.statsblock( frame )
local function cardHd( frame, title, linktext, linkurl )
     local args = frame.args
     local hd = '<div class="bsw-card-hd">' .. e(title)
 
     if linkurl and linkurl ~= '' then
    local grid = '<div class="bsw-stats-grid">'
        -- Internal links start with /
     for i = 1, 6 do
         local link
         local n    = mw.text.trim( args[ 'n'     .. i ] or '' )
        if linkurl:match('^/') or linkurl:match('^#') then
        local label = mw.text.trim( args[ 'label' .. i ] or '' )
            local page = linkurl:gsub('^/', '')
        if n ~= '' or label ~= '' then
            if page == '' or page:match('^#') then
            -- n may contain MW magic word output already expanded
                 -- anchor-only or hash — can't wikilink, use span
            grid = grid ..
                 link = '<span class="bsw-card-hd-link">' .. e(linktext or 'More') .. '</span>'
                 '<div class="bsw-stat">' ..
            else
                '<div class="bsw-stat-n">' .. n .. '</div>' ..
                link = frame:preprocess( '[[' .. page .. '|' .. (linktext or 'More →') .. ']]' )
                 '<div class="bsw-stat-l">' .. esc( label ) .. '</div>' ..
             end
                '</div>'
        else
        end
             link = xlink( frame, linkurl, linktext or 'More →' )
    end
    grid = grid .. '</div>'
 
    local links = '<div class="bsw-stat-links">'
    for i = 1, 4 do
        local url  = mw.text.trim( args[ 'link' .. i .. '_url' ] or '' )
        local text = mw.text.trim( args[ 'link' .. i .. '_text' ] or '' )
        if url ~= '' then
            local a_params = { text ~= '' and text or url }
             a_params['class'] = 'bsw-stat-link'
            a_params['href']  = url
             links = links .. frame:callParserFunction( '#tag:a', a_params )
         end
         end
        hd = hd .. link
     end
     end
     hd = hd .. '</div>'
     links = links .. '</div>'
     return hd
 
    return
        '<div class="bsw-card">' ..
        '<div class="bsw-card-hd">Wiki statistics</div>' ..
        '<div class="bsw-card-body">' .. grid .. links .. '</div>' ..
        '</div>'
end
 
-- ── recenttabs() ──────────────────────────────────────────────────
function p.recenttabs( frame )
    local tabs = { { 'All', 'all' }, { 'EN', 'en' }, { 'DE', 'de' }, { 'Media', 'media' } }
    local result = '<div class="bsw-wiki-tabs">'
    for i, t in ipairs( tabs ) do
        local cls    = 'bsw-wtab' .. ( i == 1 and ' bsw-active' or '' )
        local params = { t[1] }
        params['class']  = cls
        params['onclick'] = "bswSetTab(this,'" .. t[2] .. "')"
        result = result .. frame:callParserFunction( '#tag:button', params )
    end
     return result .. '</div>'
end
end


Line 214: Line 287:
     local content = args.content or ''
     local content = args.content or ''


     -- Hide card entirely if content resolved to only a red link
     local hd = '<div class="bsw-card-hd">' .. esc( title )
     if isRedlink( content ) then return '' end
     if lurl ~= '' then
        local a_params = { ltext ~= '' and ltext or 'More →' }
        a_params['href'] = lurl
        hd = hd .. frame:callParserFunction( '#tag:a', a_params )
    end
    hd = hd .. '</div>'


     return '<div class="bsw-card">' ..
     return
         cardHd( frame, title, ltext, lurl ) ..
        '<div class="bsw-card">' ..
         hd ..
         '<div class="bsw-card-body">' .. content .. '</div>' ..
         '<div class="bsw-card-body">' .. content .. '</div>' ..
         '</div>'
         '</div>'
Line 225: Line 304:
-- ── featuredloading() ─────────────────────────────────────────────
-- ── featuredloading() ─────────────────────────────────────────────
function p.featuredloading( frame )
function p.featuredloading( frame )
     -- "Read more" link points to '#' — JS will update href
     local a_params = { 'Read more ' }
     -- Use a span since we can't href to '#' cleanly in wikitext
     a_params['id']  = 'bsw-featured-link'
    a_params['href'] = '#'
 
     local hd =
     local hd =
         '<div class="bsw-card-hd">Featured article' ..
         '<div class="bsw-card-hd">Featured article' ..
         '<span class="bsw-card-hd-link" id="bsw-featured-link">Read more →</span>' ..
         frame:callParserFunction( '#tag:a', a_params ) ..
         '</div>'
         '</div>'


     return '<div class="bsw-card">' ..
     return
        '<div class="bsw-card">' ..
         hd ..
         hd ..
         '<div class="bsw-card-body">' ..
         '<div class="bsw-card-body">' ..
Line 238: Line 320:
         '<div class="bsw-loading">' ..
         '<div class="bsw-loading">' ..
         '<div class="bsw-spinner"></div>' ..
         '<div class="bsw-spinner"></div>' ..
         'Loading today\'s featured article\226\128\166' ..
         'Loading today\'s featured article…' ..
         '</div></div></div></div>'
         '</div></div></div></div>'
end
end
Line 244: Line 326:
-- ── photolab() ────────────────────────────────────────────────────
-- ── photolab() ────────────────────────────────────────────────────
function p.photolab( frame )
function p.photolab( frame )
     local args     = frame.args
     local args     = frame.args
     local content   = mw.text.trim( args.content or '' )
     local content = args.content   or ''
     local prev_potd = mw.text.trim( args.prev_url  or '' )
     local prev_url = mw.text.trim( args.prev_url  or '#' )
     local prev_lbl = mw.text.trim( args.prev_label or '‹' )
     local prev_lbl = mw.text.trim( args.prev_label or '‹' )
     local next_potd = mw.text.trim( args.next_url  or '' )
     local caption  = mw.text.trim( args.caption    or '' )
     local next_lbl  = mw.text.trim( args.next_label or '' )
     local next_url = mw.text.trim( args.next_url  or '#' )
 
     local next_lbl = mw.text.trim( args.next_label or '' )
     local caption_page = mw.text.trim( args.caption_page or '' )
    local caption = ''
    if caption_page ~= '' then
        caption = frame:preprocess( '{{' .. caption_page .. '}}' )
    else
        caption = args.caption or ''
    end
 
    -- Hide if today's PotD subpage doesn't exist yet
    if isRedlink( content ) then return '' end
 
    -- The Potd: subpage stores just the bare filename without "File:" prefix.
    local rendered_content = content
    if content ~= '' and not content:match( '%[%[' ) and not content:match( '<' ) then
        local filename = mw.text.trim( content )
        if filename ~= '' then
            -- Use thumb syntax suppressed via frameless|none to avoid
            -- MW adding figure wrapper with extra margins
            rendered_content = frame:preprocess(
                '[[File:' .. filename .. '|center|frameless|upright=1.5]]'
            )
        end
    end


    -- Resolve a Potd:MM-DD subpage to its bare filename,
     local a_view_params = { 'View project →' }
    -- then build a [[File:filename|label]] link from it.
    a_view_params['href'] = '/BW:Potd'
     local function potdFileLink( potd_page, label )
    local a_prev_params = { prev_lbl }
        if potd_page == '' then return label end
    a_prev_params['href'] = prev_url
        local t = mw.title.new( potd_page )
    local a_next_params = { next_lbl }
        if t and t.exists then
    a_next_params['href'] = next_url
            local fn = mw.text.trim( t:getContent() or '' )
            if fn ~= '' and not fn:match( '%[%[' ) then
                -- Use [[:File:...]] (colon prefix) to link TO the file page
                -- rather than [[File:...]] which would transclude/embed the image
                return frame:preprocess( '[[:File:' .. fn .. '|' .. label .. ']]' )
            end
        end
        return frame:preprocess( '[[' .. potd_page .. '|' .. label .. ']]' )
    end


     local hd =
     local hd =
         '<div class="bsw-card-hd">\226\136\158 Photo Lab \226\128\148 Picture of the Day' ..
         '<div class="bsw-card-hd">Photo Lab Picture of the Day' ..
         frame:preprocess( '[[BW:Potd|View project →]]' ) ..
         frame:callParserFunction( '#tag:a', a_view_params ) ..
         '</div>'
         '</div>'


     local nav =
     local nav =
         '<div class="bsw-photo-nav">' ..
         '<div class="bsw-photo-nav">' ..
         potdFileLink( prev_potd, prev_lbl ) ..
         frame:callParserFunction( '#tag:a', a_prev_params ) ..
         '<span class="bsw-photo-caption">' .. caption .. '</span>' ..
         '<span class="bsw-photo-caption">' .. esc( caption ) .. '</span>' ..
         potdFileLink( next_potd, next_lbl ) ..
         frame:callParserFunction( '#tag:a', a_next_params ) ..
         '</div>'
         '</div>'


     return '<div class="bsw-card">' .. hd .. rendered_content .. nav .. '</div>'
     return '<div class="bsw-card">' .. hd .. content .. nav .. '</div>'
end
 
-- ── statsblock() ──────────────────────────────────────────────────
function p.statsblock( frame )
    local args = frame.args
    local grid = '<div class="bsw-stats-grid">'
    for i = 1, 6 do
        local n    = mw.text.trim( args['n'    .. i] or '' )
        local label = mw.text.trim( args['label' .. i] or '' )
        if n ~= '' or label ~= '' then
            grid = grid ..
                '<div class="bsw-stat">' ..
                '<div class="bsw-stat-n">' .. n .. '</div>' ..
                '<div class="bsw-stat-l">' .. e(label) .. '</div>' ..
                '</div>'
        end
    end
    grid = grid .. '</div>'
 
    local links = '<div class="bsw-stat-links">'
    for i = 1, 4 do
        local url  = mw.text.trim( args['link' .. i .. '_url' ] or '' )
        local text = mw.text.trim( args['link' .. i .. '_text'] or '' )
        if url ~= '' then
            local page = url:gsub('^/', '')
            links = links .. frame:preprocess( '[[' .. page .. '|<span class="bsw-stat-link">' .. e(text) .. '</span>]]' )
        end
    end
    links = links .. '</div>'
 
    return
        '<div class="bsw-card">' ..
        '<div class="bsw-card-hd">Wiki statistics</div>' ..
        '<div class="bsw-card-body">' .. grid .. links .. '</div>' ..
        '</div>'
end
 
-- ── recenttabs() ──────────────────────────────────────────────────
function p.recenttabs( frame )
    local tabs = { {'All','all'}, {'EN','en'}, {'DE','de'}, {'FR','fr'}, {'Media','media'} }
    local html = '<div class="bsw-wiki-tabs">'
    for i, t in ipairs(tabs) do
        local cls = 'bsw-wtab' .. (i == 1 and ' bsw-active' or '')
        html = html .. spanBtn(nil, cls, "bswSetTab(this,'" .. t[2] .. "')", t[1])
    end
    return html .. '</div>'
end
end


-- ── interwiki() ───────────────────────────────────────────────────
-- ── interwiki() ───────────────────────────────────────────────────
function p.interwiki( frame )
function p.interwiki( frame )
     local args = frame.args
     local args   = frame.args
     local html = '<div class="bsw-interwiki">'
     local result = '<div class="bsw-interwiki">'
 
     for i = 1, 8 do
     for i = 1, 8 do
         local flag  = mw.text.trim( args[i..'_flag' ] or '' )
         local flag  = mw.text.trim( args[ i .. '_flag' ] or '' )
         local label = mw.text.trim( args[i..'_label'] or '' )
         local label = mw.text.trim( args[ i .. '_label' ] or '' )
         local url  = mw.text.trim( args[i..'_url' ] or '' )
         local url  = mw.text.trim( args[ i .. '_url'   ] or '' )
         if url ~= '' then
         if url ~= '' then
             local link
             local a_params = { label }
             if url:match('^/') then
             a_params['href'] = url
                link = frame:preprocess( '[[' .. url:gsub('^/','') .. '|' .. label .. ']]' )
             local prefix = flag ~= '' and ( flag .. ' ' ) or ''
            else
             result = result ..
                link = xlink( frame, url, label )
                '<div class="bsw-iw">' ..
            end
                prefix ..
             local prefix = flag ~= '' and (flag .. ' ') or ''
                frame:callParserFunction( '#tag:a', a_params ) ..
             html = html .. '<div class="bsw-iw">' .. prefix .. link .. '</div>'
                '</div>'
         end
         end
     end
     end
     return html .. '</div>'
 
     return result .. '</div>'
end
end


-- ── tagline() ─────────────────────────────────────────────────────
-- ── tagline() ─────────────────────────────────────────────────────
function p.tagline( frame )
function p.tagline( frame )
    -- Use frame:preprocess for wikitext bold/italic markup
     local inner = frame:preprocess(
     local inner = frame:preprocess(
         "The only original and legitimate '''Battlestar Wiki''' " ..
         "The only original and legitimate '''Battlestar Wiki''' " ..
         "\226\128\148 the free-as-in-beer, non-corporate, open-content encyclopedia " ..
         "the free-as-in-beer, non-corporate, open-content encyclopedia " ..
         "on all things ''Battlestar Galactica''.<br>" ..
         "on all things ''Battlestar Galactica''.<br>" ..
         "\226\136\158 ''Accept neither subpar substitutes nor subpar clones.'' \226\136\158"
         "''Accept neither subpar substitutes nor subpar clones.'' "
     )
     )
     return '<div class="bsw-tagline">' .. inner .. '</div>'
     return '<div class="bsw-tagline">' .. inner .. '</div>'
end
-- ── newestarticle() ──────────────────────────────────────────────
-- Shell card populated by JS from Special:NewPages API.
function p.newestarticle( frame )
    return
        '<div class="bsw-card" id="bsw-newest-card">' ..
        '<div class="bsw-card-hd">Newest article' ..
        frame:preprocess( '[[Special:NewPages|All new pages →]]' ) ..
        '</div>' ..
        '<div class="bsw-card-body">' ..
        '<div id="bsw-newest-inner">' ..
        '<div class="bsw-loading"><div class="bsw-spinner"></div>Loading…</div>' ..
        '</div>' ..
        '</div>' ..
        '</div>'
end
-- Dedicated card for sister wiki links.
-- Uses numbered params: |1_label, |1_url, |2_label, |2_url ...
-- External URLs rendered via frame:preprocess [url label] syntax.
function p.sisterprojects( frame )
    local args = frame.args
    local body = '<div style="display:flex;flex-direction:column;gap:0.375rem">'
    for i = 1, 8 do
        local label = mw.text.trim( args[i .. '_label'] or '' )
        local url  = mw.text.trim( args[i .. '_url'  ] or '' )
        if url ~= '' and label ~= '' then
            -- Wrap in span.bsw-sister-link for styling
            -- Use preprocess so [url text] becomes a real hyperlink
            local link = frame:preprocess( '[' .. url .. ' ' .. label .. ']' )
            body = body .. '<div class="bsw-sister-link">' .. link .. '</div>'
        end
    end
    body = body .. '</div>'
    return
        '<div class="bsw-card">' ..
        '<div class="bsw-card-hd">Sister projects</div>' ..
        '<div class="bsw-card-body">' .. body .. '</div>' ..
        '</div>'
end
-- ── votd() ────────────────────────────────────────────────────────
function p.votd( frame )
    local today = os.date( '%Y-%m-%d' )
    -- Check for manual override subpage server-side.
    -- Rather than putting the token in a data attribute (which can
    -- trigger Citizen skin hook warnings), we embed it in a hidden
    -- <span> inside the container that the JS reads instead.
    local override_page  = 'Battlestar Wiki:Video of the Day/' .. today
    local override_title = mw.title.new( override_page )
    local override_token = ''
    if override_title and override_title.exists then
        local content = override_title:getContent()
        if content then
            override_token = mw.text.trim( content )
        end
    end
    local override_span = ''
    if override_token ~= '' then
        override_span =
            '<span id="bsw-votd-override" style="display:none">' ..
            mw.text.nowiki( override_token ) ..
            '</span>'
    end
    -- Header bar
    local hd =
        '<div class="bsw-votd-hd">' ..
        '<span class="bsw-votd-hd-inner">' ..
        '<span class="bsw-votd-hd-dot"></span>' ..
        '\226\136\158 Video of the Day' ..
        '</span>' ..
        frame:preprocess( '<span class="plainlinks">[https://battlestarpegasus.com battlestarpegasus.com \226\134\151]</span>' ) ..
        '</div>'
    -- Inner grid: player + info panel, both populated by JS
    local inner =
        '<div class="bsw-votd-inner">' ..
        '<div class="bsw-votd-player">' ..
        '<div class="bsw-loading"><div class="bsw-spinner"></div>' ..
        'Loading today\226\128\153s video\226\128\166</div>' ..
        '</div>' ..
        '<div class="bsw-votd-info" id="bsw-votd-info" style="display:none"></div>' ..
        '</div>'
    return
        '<div class="bsw-votd-band" id="bsw-votd-container"' ..
        ' data-date="' .. today .. '">' ..
        override_span ..
        hd .. inner ..
        '</div>'
end
end


return p
return p

To protect the wiki against automated edit spam, please solve the following captcha:

Cancel Editing help (opens in new window)

  [] · [[]] · [[|]] · {{}} · · “” ‘’ «» ‹› „“ ‚‘ · ~ | ° &nbsp; · ± × ÷ ² ³ ½ · §
     [[Category:]] · [[:File:]] · [[Special:MyLanguage/]] · <code></code> · <nowiki></nowiki> <code><nowiki></nowiki></code> · <syntaxhighlight></syntaxhighlight> · <includeonly></includeonly> · <noinclude></noinclude> · #REDIRECT[[]] · <translate></translate> · <languages/> · {{#translation:}} · <tvar|></> · {{DEFAULTSORT:}} · <categorytree></categorytree> · <div style="clear:both;"></div> <s></s>


Your changes will be visible immediately.
  • For testing, please use the sandbox instead.
  • On talk pages, please sign your comment by typing four tildes (~~~~).

Page included on this page: