Edge Rewrite
// HTMLRewriter · presentation

This page was redesigned at the edge.

Cloudflare fetched the original article and streamed it through HTMLRewriter to apply an entirely new visual system without rebuilding the source page.

// request.cf · coarse context

A page that knows where it met you.

Only coarse request metadata is shown. This demo does not display or persist visitor IP addresses.

Country
US
Cloudflare location
CMH
Connection
HTTP/2
Language
Not provided

Ray ID: a243d3af3cc41ec6

Jump to content

Module:Sandbox/TheUnit 72/Universal clickable button

From Wikipedia, the free encyclopedia
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=]]&nbsp; ', 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:Sandbox/TheUnit_72/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