User:TheUnit 72/backups/Universal clickable button/Module

local p = {}

local colorMap = {
    red = "#d33333",
    ["deep red"] = "#8b0000",
    ["light red"] = "#ff7f7f",

    orange = "#ff8c00",
    ["light orange"] = "#ffd1a3",

    yellow = "#ffeb3b",
    gold = "#d4af37",
    ["deep yellow"] = "#d4af37",
    ["light yellow"] = "#fff59d",

    green = "#4caf50",
    ["deep green"] = "#1b5e20",
	["light green"] = "#88e788",

    turquoise = "#40e0d0",
    turqoise = "#40e0d0",

    blue = "#3366cc",
    ["deep blue"] = "#0d47a1",
    ["light blue"] = "#90caf9",

    purple = "#7953a9",
    ["deep purple"] = "#4a148c",
    ["light purple"] = "#ce93d8",

    pink = "#e91e63",
    magenta = "#c2185b",

    white = "#ffffff",
    black = "#000000",

    grey = "#a2a9b1",
    gray = "#a2a9b1",
    silver = "#c8ccd1",
    ["light grey"] = "#f8f9fa",
    ["deep grey"] = "#54595d",
	["charcoal"] = "#36454f",

    beige = "#f8eaba",
    ["deep beige"] = "#c0c090",

	["progressive"] = "#3366cc",
    ["destructive"] = "#bf3c2c",
    ["constructive"] = "#008000",
	["effective"] = "#d4af37",
    ["neutral"] = "#f8f9fa"
}

local DEFAULT_BG = "#f8f9fa"
local DEFAULT_FG = "#000000"
local DEFAULT_BORDER = "#a2a9b1"

local function normalizeKey(str)
    if not str then return nil end
    str = tostring(str):lower()
        :gsub("^%s+", "")
        :gsub("%s+$", "")
        :gsub("%s+", " ")
    str = str:gsub("^dark%s+", "deep ")
    return str
end

local function resolveColor(input)
    if not input then return nil end
    local key = normalizeKey(input)
    if colorMap[key] then return colorMap[key] end
    if key:match("^#%x%x%x$") or key:match("^#%x%x%x%x%x%x$") then return key end
    if key:match("^%x%x%x$") or key:match("^%x%x%x%x%x%x$") then return "#" .. key end
    return nil
end

local function expandHex(hex)
    hex = hex:gsub("#", "")
    if #hex == 3 then
        return hex:sub(1,1) .. hex:sub(1,1)
            .. hex:sub(2,2) .. hex:sub(2,2)
            .. hex:sub(3,3) .. hex:sub(3,3)
    elseif #hex == 6 then
        return hex
    end
    return "808080"
end

local function getTextColor(bg)
    local hex = expandHex(bg)
    local r = tonumber(hex:sub(1,2), 16)
    local g = tonumber(hex:sub(3,4), 16)
    local b = tonumber(hex:sub(5,6), 16)
    local lum = (0.299 * r + 0.587 * g + 0.114 * b)
    return (lum > 186) and "#000" or "#fff"
end

local function pickBaseColor(args)
    local raw = args.color or args.colour or args[3]
    local resolved = resolveColor(raw)
    if resolved then
        local border
        if resolved == "#f8f9fa" then
            border = "#a2a9b1"
        elseif resolved == "#f8eaba" then
            border = "#c0c090"
        else
            border = resolved
        end
        return resolved, getTextColor(resolved), border
    end
    return DEFAULT_BG, DEFAULT_FG, DEFAULT_BORDER
end

local function isValidUrl(url)
    if not url or url == "" then return false end
    return url:match("^https?://") ~= nil
        or url:match("^//") ~= nil
end

local function adjustColor(hex, factor)
    local h = expandHex(hex)
    local r = tonumber(h:sub(1,2), 16)
    local g = tonumber(h:sub(3,4), 16)
    local b = tonumber(h:sub(5,6), 16)
    r = math.min(255, math.max(0, math.floor(r * factor)))
    g = math.min(255, math.max(0, math.floor(g * factor)))
    b = math.min(255, math.max(0, math.floor(b * factor)))
    return string.format("#%02x%02x%02x", r, g, b)
end

local function getHoverColor(bg)
    local hex = expandHex(bg)
    local r = tonumber(hex:sub(1,2), 16)
    local g = tonumber(hex:sub(3,4), 16)
    local b = tonumber(hex:sub(5,6), 16)
    local lum = (0.299 * r + 0.587 * g + 0.114 * b)

    if bg == "#000000" or bg == "#000" then
        return adjustColor(bg, 1.15)
    elseif lum >= 220 and lum < 255 then
        -- Near-white: lighten slightly
        return adjustColor(bg, 1.08)
    else
        return adjustColor(bg, 0.82)
    end
end

local function buildDisplay(args, bg, fg, border)
    local span = mw.html.create("span")
    span
        :addClass("ucb-button")
        :css({
            ["background-color"] = bg,
            ["color"]            = fg,
            ["border-color"]     = border,
            ["vertical-align"]   = "middle"
        })
   
    local userClass = args.class and tostring(args.class):lower():gsub("^%s+", ""):gsub("%s+$", "") or nil
    if userClass and userClass ~= "" and userClass ~= "mw-ui-button" then
        span:addClass(userClass)
    end

    if args.tooltip and tostring(args.tooltip) ~= "" then
        span:attr("title", tostring(args.tooltip))
    end

    if args.style then
        span:cssText(args.style)
    end

    local text = args.text or args[2] or args[1] or ""

    if args.icon and tostring(args.icon) ~= "" then
        local filename = tostring(args.icon):gsub("^[Ff]ile:", "")
        span:wikitext(string.format('[[File:%s|16px|link=]]  ', filename))
    end

    span:wikitext(text)
    return tostring(span)
end

function p.main(frame)
    local args = frame:getParent().args

    local styleLink = frame:extensionTag("templatestyles", "", {
        src = "Module:Universal clickable button/styles.css"
    })

    local link = args.url or args[1] or args.link
    if not link or tostring(link) == "" then
        return ""
    end

    local isUrl = args.url ~= nil and tostring(args.url) ~= ""

    if isUrl and not isValidUrl(tostring(args.url)) then
        return '<span class="error">Universal Clickable Button: invalid or unsafe URL.</span>'
    end

    local bg, fg, border = pickBaseColor(args)

    local useHover = not isUrl
        and args.hover and tostring(args.hover):lower():gsub("^%s+", ""):gsub("%s+$", "") == "yes"

    if not isUrl then
        local currentTitle = mw.title.getCurrentTitle()
        local targetTitle = mw.title.new(tostring(link))
        if targetTitle and currentTitle
            and targetTitle.prefixedText == currentTitle.prefixedText then
            return styleLink .. buildDisplay(args, bg, fg, border)
        end
    end

    local display = buildDisplay(args, bg, fg, border)
    local linkHtml
    if useHover then
        linkHtml = string.format('[[%s|%s]]', tostring(link), display)
        return styleLink .. string.format('<span class="plainlinks">%s</span>', linkHtml)
    end

    local href
    if isUrl then
        href = tostring(args.url)
    else
        local titleObj = mw.title.new(tostring(link))
        href = titleObj and titleObj:fullUrl() or tostring(link)
    end

    linkHtml = string.format('[%s %s]', href, display)
    return styleLink .. string.format('<span class="plainlinks">%s</span>', linkHtml)
end

return p

Content Disclaimer

Informasi ini disarikan dari Wikipedia dan disajikan kembali untuk tujuan edukasi. Konten tersedia di bawah lisensi CC BY-SA 3.0. Kami tidak bertanggung jawab atas ketidakakuratan data yang bersumber dari kontribusi publik tersebut.

  1. The information displayed on this website is sourced in part or in whole from Wikipedia and has been adapted for the purpose of restating it. We strive to provide accurate and relevant information, however:
  2. There is no guarantee of absolute accuracy. Wikipedia is an open, collaborative project that can be edited by anyone, so information is subject to change.
  3. It is not intended to constitute professional advice. The content displayed is for informational and educational purposes only. For important decisions (e.g., medical, legal, or financial), please consult a professional.
  4. Content copyright. Wikipedia is licensed under the Creative Commons Attribution-ShareAlike License (CC BY-SA). This means that content may be reused with appropriate attribution and shared under a similar license.
  5. Responsible use. Any risk arising from the use of information from this website is entirely the responsibility of the user.