Module:SocialMedia: Difference between revisions

No edit summary
// via Wikitext Extension for VSCode
 
(43 intermediate revisions by the same user not shown)
Line 1: Line 1:
-- Module:SocialMedia
-- Reusable social media component meant to be called from other modules
-- Reusable social media component meant to be called from other modules
-- ###############################################################
-- Currently supported platforms are:
-- (UPDATE THE LIST BETWEEN THE HOOK EMOJIS AS PLATFORMS GET ADDED OR REMOVED)
-- 🪝Facebook, Github, Instagram, LinkedIn, Telegram, Threads, TikTok, X/Twitter, Youtube🪝
-- ###############################################################


local sf = {}
local sf = {}
local TemplateHelpers = require('Module:TemplateHelpers')


-- This section defines the social platforms; can be added or deleted without consequence
-- This section defines the social platforms; can be added or deleted without consequence
Line 24: Line 32:
         param  = "linkedin",
         param  = "linkedin",
         icon  = "File:SocialLinkedInIcon.svg",
         icon  = "File:SocialLinkedInIcon.svg",
         prefix = "https://www.linkedin.com/",
         prefix = "https://www.linkedin.com/in/",
         label  = "LinkedIn"
         label  = "LinkedIn"
     },
     },
Line 36: Line 44:
         param  = "threads",
         param  = "threads",
         icon  = "File:SocialThreadsIcon.svg",
         icon  = "File:SocialThreadsIcon.svg",
         prefix = "https://www.threads.net/",
         prefix = "https://www.threads.net/@",
         label  = "Threads"
         label  = "Threads"
     },
     },
Line 42: Line 50:
         param  = "tiktok",
         param  = "tiktok",
         icon  = "File:SocialTikTokIcon.svg",
         icon  = "File:SocialTikTokIcon.svg",
         prefix = "https://www.tiktok.com/",
         prefix = "https://www.tiktok.com/@",
         label  = "TikTok"
         label  = "TikTok"
     },
     },
     {
     {
        -- Multiple aliases: the editor can supply "x" or "twitter"
         param  = { "x", "twitter" },
         param  = { "x", "twitter" },
         icon  = "File:SocialXIcon.svg",
         icon  = "File:SocialXIcon.svg",
Line 55: Line 62:
         param  = "youtube",
         param  = "youtube",
         icon  = "File:SocialYouTubeIcon.svg",
         icon  = "File:SocialYouTubeIcon.svg",
         prefix = "https://www.youtube.com/",
         prefix = "https://www.youtube.com/@",
         label  = "YouTube"
         label  = "YouTube"
    },
    {
        param  = "github",
        icon  = "File:SocialGithub.svg",
        prefix = "https://github.com/",
        label  = "Github"
     },
     },
}
}


--------------------------------------------------------------------------------
-- Helper function to check if text is a full URL
-- Helper to find the first non-empty user handle from a param or table of params
local function isFullUrl(text)
-- If param is a single string (e.g. "facebook"), we do args["facebook"]
    return text and text:match("^https?://") ~= nil
-- If it's a table like { "x", "twitter" }, we check args["x"] then args["twitter"]
end
--------------------------------------------------------------------------------
 
-- Platform-specific normalizers table
local normalizers = {
    linkedin = function(handle)
        -- If it's not a URL, return as is
        if not isFullUrl(handle) then return handle end
       
        -- Extract the path from the URL (case insensitive for linkedin.com)
        local path = handle:lower():match("linkedin%.com/(.+)")
        if not path then return handle end
       
        -- Handle /pub/ format
        if path:match("^pub/") then
            -- Extract the name part (first segment after /pub/)
            local name = path:match("^pub/([^/]+)")
            if name then return name end
        end
       
        -- Handle /in/ format - just extract the username
        if path:match("^in/") then
            return path:match("^in/([^/]+)")
        end
       
        -- If we can't parse it, return the original
        return handle
    end
    -- Add other platform normalizers as needed
}
 
-- Helper function to normalize handles based on platform
local function normalizeHandle(platform, handle)
    -- Skip normalization if handle is nil or empty
    if not handle or handle == "" then return handle end
   
    -- Get the platform param (use first one if it's a table)
    local platformParam = platform.param
    if type(platformParam) == "table" then
        platformParam = platformParam[1]
    end
   
    -- If platform has a specific normalizer, use it
    if normalizers[platformParam] then
        return normalizers[platformParam](handle)
    end
   
    -- Default behavior: if it's a full URL, try to extract the handle
    if isFullUrl(handle) then
        -- Try to extract the handle from the URL based on the platform's prefix
        local prefix = platform.prefix:gsub("([%%%-%.])", "%%%1") -- Escape special pattern chars
        return handle:match(prefix .. "(.+)") or handle
    end
   
    -- If not a URL, return as is
    return handle
end
 
-- (Non-generalizable) Helper to find the first non-empty user handle from a param or table of params
local function getUserHandle(args, param)
local function getUserHandle(args, param)
    local key, handle
     if type(param) == "string" then
     if type(param) == "string" then
         return args[param]
         key, handle = TemplateHelpers.getFieldValue(args, { key = param })
     else
     else
         for _, alias in ipairs(param) do
         key, handle = TemplateHelpers.getFieldValue(args, { keys = param })
            local handle = args[alias]
    end
            if handle and handle ~= "" then
    if handle and handle ~= "" then
                return handle
        return mw.text.trim(handle)
            end
        end
        return nil
     end
     end
    return nil
end
end


-- Build a clickable icon link (24px) with alt text for accessibility:
-- Build a clickable icon link (24px) with alt text for accessibility: [[File:Name.svg|24px|class=...|alt=...|link=URL]]
-- [[File:Something.svg|24px|alt=ALT_TEXT|link=URL]]
local function buildIconLink(iconFile, altText, url)
local function buildIconLink(iconFile, altText, url)
     return string.format("[[%s|24px|alt=%s|link=%s]]", iconFile, altText, url)
    -- Add the reusable filter class to the image attributes
     return string.format("[[%s|24px|alt=%s|link=%s|class=filter-icon-green]]", iconFile, altText, url)
end
end


Line 88: Line 156:
local function buildSocialRow(icons)
local function buildSocialRow(icons)
     local iconMarkup = table.concat({
     local iconMarkup = table.concat({
         '<div style="display: flex; flex-wrap: wrap; gap: 0.5em; margin-top: 1em;">',
         '<div class="social-icons link-open-external">',
             table.concat(icons, ""),
             table.concat(icons, ""),
         '</div>'
         '</div>'
     }, "\n")
     }, "\n")
 
   
     return table.concat({
     return table.concat({
         "|-",
         "|-",
         '| colspan="2" | ' .. iconMarkup
         '| colspan="2" |',
        iconMarkup
     }, "\n")
     }, "\n")
end
end
Line 101: Line 170:
-- Main render function, takes a table of arguments (handles), returns wiki markup with social icons
-- Main render function, takes a table of arguments (handles), returns wiki markup with social icons
function sf.render(args)
function sf.render(args)
    -- Early return if args is empty or nil
    if not args or next(args) == nil then
        return ""
    end
   
    -- Pre-allocate icons table based on maximum possible size (number of platforms)
     local icons = {}
     local icons = {}
    icons[#socialPlatforms] = nil  -- Pre-allocate table to avoid reallocation
    local iconCount = 0


     for _, platform in ipairs(socialPlatforms) do
     for _, platform in ipairs(socialPlatforms) do
         local handle = getUserHandle(args, platform.param)
         local handle = getUserHandle(args, platform.param)
         if handle and handle ~= "" then
         if handle and handle ~= "" then
             local url = platform.prefix .. handle
            -- Normalize the handle based on platform
            local normalizedHandle = normalizeHandle(platform, handle)
           
            -- Check for special FULLURL: prefix from Python transformation
             local url
            if handle:match("^FULLURL:") then
                -- Extract the URL part after the FULLURL: prefix
                url = handle:gsub("^FULLURL:", "")
            -- If the handle is already a full URL, use it directly
            elseif isFullUrl(handle) and not isFullUrl(normalizedHandle) then
                -- We have a full URL that was successfully normalized to a handle
                url = platform.prefix .. normalizedHandle
            elseif isFullUrl(handle) then
                -- We have a full URL that couldn't be normalized, use it directly
                url = handle
            else
                -- We have a regular handle, use the standard approach
                url = platform.prefix .. handle
            end
           
             local iconLink = buildIconLink(platform.icon, platform.label, url)
             local iconLink = buildIconLink(platform.icon, platform.label, url)
             table.insert(icons, iconLink)
             iconCount = iconCount + 1
            icons[iconCount] = iconLink  -- Direct index assignment is faster than table.insert
         end
         end
     end
     end


     -- If no handles were provided, return nothing.
     -- If no handles were provided, return nothing
     if #icons == 0 then
     if #icons == 0 then
         return ""
         return ""
     end
     end


     -- Return a row that fits nicely in the table, with a flex container for icons
     -- Return a row with a flex container for icons
     return buildSocialRow(icons)
     return buildSocialRow(icons)
end
end


return sf
return sf