Module:Biobox

From Porn Base Central, the free encyclopedia of gay porn
Jump to navigation Jump to search

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

local p = {}

local PBDIB = require('Module:PornBaseDataIB')
local CategoryMapping = require('Module:BioboxCategoryMapping')
local BioboxPersonal = require('Module:BioboxPersonal')
local BioboxBody = require('Module:BioboxBody')
local BioboxPerformances = require('Module:BioboxPerformances')
local BioboxPersonalPages = require('Module:BioboxPersonalPages')
local BioboxDatabases = require('Module:BioboxDatabases')
local BioboxSharing = require('Module:BioboxSharing')
local BioboxVODDVD = require('Module:BioboxVODDVD')
local Utils = require('Module:BioboxUtils')

local ALIAS_TO_PRIMARY = {}
do
    for primary, aliases in pairs(CategoryMapping.PARAM_ALIASES) do
        for _, alias in ipairs(aliases) do
            ALIAS_TO_PRIMARY[alias] = primary
        end
        ALIAS_TO_PRIMARY[primary] = primary
    end
end

local STYLES = {
    INFOBOX = 'class="infoboxnew" cellspacing="1" cellpadding="1" style="text-align:center; font-size:88%; line-height:1.5em; white-space:normal;"',
    HEADER = 'style="color:white; text-align:center; border: 1px solid white; background:#657D91; font-size:112%;"',
    LABEL = 'width:40%; padding-left:0.1em; overflow-wrap: break-word; font-weight:normal; text-align:left;',
    SECTION_HEADER = 'style="color:white; text-align:center; border: 1px solid white; background:#657D91; font-size:100%;"',
    ODD_ROW = 'background-color:#F7F7F7;',
    EVEN_ROW = 'background-color:white;'
}

local function isEmpty(value)
    return Utils.isEmpty(value)
end

local function isPositive(value)
    return Utils.isPositive(value)
end

local POSITIVE_ONLY_FIELDS = {
}

local function normalizeKeys(args)
    local normalized = {}
    local seenParams = {}
    local processedAliases = {}
    local firstOccurrence = {}
    
    for key, value in pairs(args) do
        local normalizedKey = tostring(key):gsub(" ", "_"):lower()
        local primaryKey = ALIAS_TO_PRIMARY[normalizedKey] or normalizedKey
        
        if not firstOccurrence[primaryKey] then
            firstOccurrence[primaryKey] = #seenParams + 1
        end
        
        table.insert(seenParams, {
            key = key,
            value = value,
            normalizedKey = normalizedKey,
            primaryKey = primaryKey,
            position = firstOccurrence[primaryKey]
        })
    end
    
    table.sort(seenParams, function(a, b) 
        return a.position < b.position
    end)
    
    for _, param in ipairs(seenParams) do
        local primaryKey = param.primaryKey
        
        if not processedAliases[primaryKey] then
            if not isEmpty(param.value) then
                normalized[primaryKey] = param.value
                processedAliases[primaryKey] = true
            elseif not normalized[primaryKey] then
                normalized[primaryKey] = param.value
            end
        end
    end
    
    return normalized
end

local function isNumeric(value)
    if type(value) == "number" then return true end
    if type(value) ~= "string" then return false end
    return tonumber(value) ~= nil
end

local function shouldDisplayField(fieldName, value)
    if not isEmpty(value) then
        if POSITIVE_ONLY_FIELDS[fieldName] then
            return isPositive(value)
        end
        return true
    end
    return false
end

local function getPBDIBValue(propertyId, args, options)
    return PBDIB._getValue({
        [1] = propertyId,
        qid = args.qid,
        format = options and options.format,
        fpbd = "ALL"
    })
end

local function extractNumber(str)
    if type(str) == 'number' then 
        return str 
    end

    if type(str) ~= 'string' or str == '' then 
        return nil 
    end

    local num = tonumber(str)
    if num then return num end
    
    num = str:match("^%s*(%d+%.?%d*)[%s%a]*$")
    if num then return tonumber(num) end
    
    num = str:match("^%s*(%d+%.?%d*)%a*$")
    if num then return tonumber(num) end
    
    num = str:match("^%s*(%d+,%d+)%s*$")
    if num then return tonumber(num:gsub(",", ".")) end
    
    return nil
end

local State = {}

function State:new()
    local state = {}
    setmetatable(state, self)
    self.__index = self
    state.STYLES = STYLES
    return state
end

function State:getRowStyle(index)
    return index % 2 == 1 and STYLES.ODD_ROW or STYLES.EVEN_ROW 
end

function State:splitByComma(str)
    return Utils.splitByComma(str)
end

function State:formatCategoryLink(value, prefix, displayText)
    if isEmpty(value) then return nil end
    
    local normalized = mw.text.trim(value):gsub("^%l", string.upper)
    displayText = displayText or normalized
    
    local result = string.format("[[:Category:%s%s|%s]]",
        prefix,
        normalized,
        displayText
    )
    
    if mw.title.getCurrentTitle().namespace == 0 then
        result = result .. string.format("[[Category:%s%s]]",
            prefix,
            normalized
        )
    end
    
    return result
end

function State:collectNumberedParams(args, baseParam)
    local values = {}
    local numbered = {}
    
    if not isEmpty(args[baseParam]) then
        table.insert(values, {index = 0, value = args[baseParam]})
    end
    
    for param, value in pairs(args) do
        local index = param:match("^" .. baseParam .. "(%d+)$")
        if index and not isEmpty(value) then
            index = tonumber(index)
            if index and index > 0 and index < 100 then
                table.insert(values, {index = index, value = value})
            end
        end
    end
    
    table.sort(values, function(a, b) return a.index < b.index end)
    
    local result = {}
    for _, item in ipairs(values) do
        table.insert(result, item.value)
    end
    
    return result
end

function State:formatField(label, value, fieldName, index)
    if isEmpty(value) then
        return ''
    end
    
    if POSITIVE_ONLY_FIELDS[fieldName] and not isPositive(value) then
        return ''
    end
    
    local rowStyle = self:getRowStyle(index)
    
    return string.format('|-\n! style="%s %s" | \'\'\'%s\'\'\'\n| style="%s text-align:left;" | %s', 
        rowStyle,
        STYLES.LABEL,
        label,
        rowStyle,
        value)
end

function State:formatFeatureList(values, featureType)
    if not values or #values == 0 then return nil end
    
    local displayValues = {}
    local addedCategories = {}
    local isMainNamespace = mw.title.getCurrentTitle().namespace == 0
    
    if featureType == 'foreskin' then
        for _, value in ipairs(values) do
            if value then
                local feature = mw.text.trim(value):lower()
                local mapping = CategoryMapping.MAPPINGS[featureType][feature]
                
                if mapping then
                    local result = string.format("[[:Category:%s|%s]]",
                        mapping.category,
                        mapping.display
                    )
                    
                    if isMainNamespace then
                        result = result .. string.format("[[Category:%s]]",
                            mapping.category
                        )
                    end
                    
                    return result
                end
            end
        end
        return nil
    end
    
    if featureType == 'ass_type' then
        local foundMappings = {}
        local hasMainType = false
        
        for _, value in ipairs(values) do
            if value then
                local features = self:splitByComma(value)
                for _, feature in ipairs(features) do
                    feature = mw.text.trim(feature):lower()
                    
                    for _, mapping in ipairs(CategoryMapping.MAPPINGS[featureType]) do
                        for _, alias in ipairs(mapping.aliases) do
                            if feature == alias:lower() then
                                local isMainSize = mapping.priority >= 1 and mapping.priority <= 3

                                if (not isMainSize or not hasMainType) and not addedCategories[mapping.category] then
                                    table.insert(foundMappings, mapping)
                                    addedCategories[mapping.category] = true
                                    
                                    if isMainSize then
                                        hasMainType = true
                                    end
                                end
                            end
                        end
                    end
                end
            end
        end
        
        table.sort(foundMappings, function(a, b)
            return a.priority < b.priority
        end)
        
        for i, mapping in ipairs(foundMappings) do
            if i > 1 then
                table.insert(displayValues, ", ")
            end
            
            local result = string.format("[[:Category:%s|%s]]",
                mapping.category,
                mapping.display
            )
            
            if isMainNamespace then
                result = result .. string.format("[[Category:%s]]",
                    mapping.category
                )
            end
            
            table.insert(displayValues, result)
        end
        
    else
        for _, value in ipairs(values) do
            if value then
                local features = self:splitByComma(value)
                for _, feature in ipairs(features) do
                    feature = mw.text.trim(feature):lower()
                    
                    for _, mapping in ipairs(CategoryMapping.MAPPINGS[featureType]) do
                        local found = false
                        for _, alias in ipairs(mapping.aliases) do
                            if feature == alias:lower() then
                                found = true
                                if not addedCategories[mapping.category] then
                                    if #displayValues > 0 then
                                        table.insert(displayValues, ", ")
                                    end
                                    
                                    local result = string.format("[[:Category:%s|%s]]",
                                        mapping.category,
                                        mapping.display
                                    )
                                    
                                    if isMainNamespace then
                                        result = result .. string.format("[[Category:%s]]",
                                            mapping.category
                                        )
                                    end
                                    
                                    table.insert(displayValues, result)
                                    addedCategories[mapping.category] = true
                                end
                                break
                            end
                        end
                        if found then break end
                    end
                end
            end
        end
    end
    
    if #displayValues > 0 then
        return table.concat(displayValues)
    end
    
    return nil
end

local function loadTemplateStyles()
    local frame = mw.getCurrentFrame()
    
    local base_templatestyles = frame:extensionTag{
        name = 'templatestyles', args = { src = 'Module:Biobox/styles.css' }
    }
    
    return base_templatestyles
end

function p.main(frame)
    local parentFrame = frame:getParent()
    local args = frame.args
    if not next(args) then
        args = parentFrame.args
    end

    args = normalizeKeys(args)
    
    if not args.qid or args.qid == "" then
        args.qid = mw.wikibase.getEntityIdForCurrentPage()
    end
    
    local title = mw.title.getCurrentTitle().text
    local state = State:new()

    local personalSection, rowIndex = BioboxPersonal.formatPersonalSection(state, args, title)
    local bodySection, bodyRowIndex = BioboxBody.formatBodySection(state, args, rowIndex)
    local performancesSection, perfRowIndex = BioboxPerformances.formatPerformancesSection(state, args, bodyRowIndex)
    
    local personalPagesSection, ppRowIndex = BioboxPersonalPages.formatPersonalPagesSection(state, args, perfRowIndex)
    local databasesSection, dbRowIndex = BioboxDatabases.formatDatabasesSection(state, args, ppRowIndex)
    local sharingSection, shRowIndex = BioboxSharing.formatSharingSection(state, args, dbRowIndex)
    local vodDvdSection, vodRowIndex = BioboxVODDVD.formatVODDVDSection(state, args, shRowIndex)
    
    local result = personalSection .. '\n' .. bodySection .. '\n' .. performancesSection
    
    if #personalPagesSection > 0 then
        result = result .. '\n' .. table.concat(personalPagesSection, '\n')
    end
    
    if #databasesSection > 0 then
        result = result .. '\n' .. table.concat(databasesSection, '\n')
    end
    
    if #sharingSection > 0 then
        result = result .. '\n' .. table.concat(sharingSection, '\n')
    end
    if #vodDvdSection > 0 then
        result = result .. '\n' .. table.concat(vodDvdSection, '\n')
    end

    if args.qid then
        local pbcLink = mw.wikibase.getSitelink(args.qid)
        if pbcLink then
            result = result .. '\n|-class="stop-section-collapse"\n! colspan="2" style="background:#F0E68C;text-align:center;" | [[File:Pbd.svg | 45px | alt=' .. args.qid .. ' | link=pbd:' .. args.qid .. ']]&nbsp;[[:pbd:' .. args.qid .. '|' .. pbcLink .. ']]&nbsp;([[:pbd:' .. args.qid .. '|' .. args.qid .. ']])'
        end

        local newFrame = {
            args = {
                qid = args.qid,
                wiki = "pbcimagerepo"
            }
        }
        local imageRepoLink = PBDIB.getSiteLink(newFrame)
        
        if imageRepoLink and imageRepoLink ~= '' then
            result = result .. '\n|-class="stop-section-collapse"\n! colspan="2" style="background:#F0E68C;text-align:center;" | [[File:Image-icon.svg | 18px | link=:' .. imageRepoLink .. ']]&nbsp;[[:' .. imageRepoLink .. '| PBC Image Repository]]'
        end

        newFrame.args.wiki = "pbcvideorepo"
        local videoRepoLink = PBDIB.getSiteLink(newFrame)
        
        if videoRepoLink and videoRepoLink ~= '' then
            result = result .. '\n|-class="stop-section-collapse"\n! colspan="2" style="background:#F0E68C;text-align:center;" | [[File:Video-icon.svg | 18px | link=:' .. videoRepoLink .. ']]&nbsp;[[:' .. videoRepoLink .. '| PBC Video Repository]]'
        end
    end

    result = result .. '\n|}'

    local categories = BioboxPersonal.getPBDCategories(args)
    if #categories > 0 then
        local categoryStrings = {}
        for _, category in ipairs(categories) do
            table.insert(categoryStrings, '[[Category:' .. category .. ']]')
        end
        result = result .. '\n' .. table.concat(categoryStrings, '\n')
    end

    return loadTemplateStyles() .. '\n' .. result
end

return p