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

Module:Portal

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.
Revision as of 20:55, 13 April 2026 by Joe Beaudoin Jr. (talk | contribs)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)

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

-- Module:Portal
-- Provides server-side portal widgets for BattlestarWiki.
--
-- Functions:
--   colonialCalendar  — "On this day in BSG" sidebar widget
--   relatedPortals    — related-portals tile grid
--
-- NOTE: stats, newest article, and image carousel are handled
-- entirely by the portal JS in MediaWiki:Common.js, not Lua.
-- The JS reads data-category / data-listpage attributes set by
-- Template:Portal/Layout and makes live API calls.

local p = {}
local mwHtml    = mw.html
local mwUstring = mw.ustring

------------------------------------------------------------------------
-- Helpers
------------------------------------------------------------------------

local function trim( s )
    return s and mwUstring.match( s, '^%s*(.-)%s*$' ) or ''
end

local function arg( args, key, default )
    local v = args[key]
    return ( v and trim( v ) ~= '' ) and trim( v ) or default
end

------------------------------------------------------------------------
-- Colonial Calendar
-- Keyed "M-D" (no zero-padding) → list of {year, text}
-- Covers real-world BSG broadcast dates.
-- To extend: add entries here, or extract to Module:Portal/Calendar
-- (see DEPLOYMENT.md) so non-Lua editors can add dates directly.
------------------------------------------------------------------------

local CALENDAR = {
    ['1-14'] = {
        { year = '2005', text = '[[33 (episode)|33]] premieres on Sci Fi Channel — first regular RDM episode' },
    },
    ['1-21'] = {
        { year = '2005', text = '[[Water (episode)|Water]] airs on Sci Fi Channel' },
    },
    ['3-4']  = {
        { year = '2005', text = '[[Tigh Me Up, Tigh Me Down]] airs on Sky One (UK)' },
    },
    ['3-18'] = {
        { year = '2005', text = '[[Colonial Day]] airs on Sky One' },
    },
    ['3-20'] = {
        { year = '2009', text = '[[Daybreak, Part II|Daybreak]] finale airs — end of the Re-imagined Series' },
    },
    ['4-1']  = {
        { year = '2005', text = "[[Kobol's Last Gleaming, Part II]] airs — Season 1 finale" },
    },
    ['4-12'] = {
        { year = '2007', text = '[[The Woman King]] airs on Sci Fi Channel' },
        { year = '2009', text = '[[Islanded in a Stream of Stars]] airs — three episodes remain' },
    },
    ['6-3']  = {
        { year = '2005', text = '[[Scattered]] airs — Season 2 premiere' },
    },
    ['9-22'] = {
        { year = '2006', text = '[[Occupation]]/[[Precipice]] two-hour Season 3 premiere airs' },
    },
    ['10-6'] = {
        { year = '2006', text = '[[Exodus, Part II]] airs — fan-favourite Galactica dive sequence' },
    },
    ['12-8'] = {
        { year = '2003', text = '[[Miniseries, Night 1|BSG Miniseries]] premieres on Sci Fi Channel' },
    },
}

local function todayKey()
    local frame = mw.getCurrentFrame()
    local m = tonumber( frame:callParserFunction( '#time', 'n' ) )
    local d = tonumber( frame:callParserFunction( '#time', 'j' ) )
    return m .. '-' .. d
end

function p.colonialCalendar( frame )
    local key    = todayKey()
    local events = CALENDAR[key]

    local dateDisplay = mw.getCurrentFrame():callParserFunction( '#time', 'F j' )

    local root = mwHtml.create( 'div' ):addClass( 'portal-calendar' )
    root:tag( 'div' ):addClass( 'portal-calendar-date' ):wikitext( dateDisplay )
    root:tag( 'div' ):addClass( 'portal-calendar-sub' )
        :wikitext( 'Real-world BSG broadcast history' )

    if events and #events > 0 then
        local list = root:tag( 'ul' ):addClass( 'portal-calendar-events' )
        for _, ev in ipairs( events ) do
            list:tag( 'li' ):addClass( 'portal-calendar-event' )
                :tag( 'span' ):addClass( 'portal-calendar-year' ):wikitext( ev.year ):done()
                :wikitext( ' \xe2\x80\x94 ' .. ev.text )
        end
    else
        root:tag( 'div' ):addClass( 'portal-calendar-none' )
            :wikitext( '<small>No recorded BSG events on this date.</small>' )
    end

    return frame:preprocess( tostring( root ) )
end

------------------------------------------------------------------------
-- Related Portals
-- Args:
--   type     "series" (default) or "topic"
--   exclude  Full page title to hide (this portal itself)
------------------------------------------------------------------------

local SERIES_PORTALS = {
    { title = 'Portal:Battlestar Galactica (TOS)', label = 'Original Series',    sub = 'TOS &middot; 1978' },
    { title = 'Portal:Galactica 1980',             label = 'Galactica 1980',     sub = '1980' },
    { title = 'Portal:Battlestar Galactica (RDM)', label = 'Re-imagined Series', sub = 'RDM &middot; 2003&ndash;2009' },
    { title = 'Portal:Caprica',                    label = 'Caprica',            sub = 'Prequel &middot; 2010&ndash;2011' },
    { title = 'Portal:Blood and Chrome',           label = 'Blood &amp; Chrome', sub = 'Prequel &middot; 2012' },
}

local TOPIC_PORTALS = {
    { title = 'Portal:Characters',  label = 'Characters',  sub = 'All series' },
    { title = 'Portal:Ships',       label = 'Ships',       sub = 'All series' },
    { title = 'Portal:Cylons',      label = 'Cylons',      sub = 'All series' },
    { title = 'Portal:Episodes',    label = 'Episodes',    sub = 'All series' },
    { title = 'Portal:Merchandise', label = 'Merchandise', sub = 'All series' },
}

function p.relatedPortals( frame )
    local args    = frame:getParent().args
    local exclude = arg( args, 'exclude', '' )

    local combined = {}
    for _, v in ipairs( SERIES_PORTALS ) do combined[#combined + 1] = v end
    for _, v in ipairs( TOPIC_PORTALS  ) do combined[#combined + 1] = v end

    local root = mwHtml.create( 'div' ):addClass( 'portal-related-grid' )

    for _, portal in ipairs( combined ) do
        if portal.title ~= exclude then
            root:tag( 'div' ):addClass( 'portal-related-tile' )
                :tag( 'div' ):addClass( 'portal-related-title' )
                    :wikitext( '[[' .. portal.title .. '|' .. portal.label .. ']]' ):done()
                :tag( 'div' ):addClass( 'portal-related-sub' )
                    :wikitext( portal.sub )
        end
    end

    return frame:preprocess( tostring( root ) )
end

return p