Module:ImageUnifier

From Roots of Pacha Wiki
Jump to navigation Jump to search

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

-- ----------------------------------------
-- <pre> Module:ImageUnifier
-- ----------------------------------------
local Self = {}                             -- table of functions
local Helper = require("Module:Helper")     -- basic helper functions
local ustr = mw.ustring                     -- quick/short access to ustring table

-- ----------------------------------------
-- Constants
-- ----------------------------------------
local C = require("Module:ImageUnifier/constants")

-- ----------------------------------------
-- Variables
-- ----------------------------------------
local g_ImgCache = {}
local g_ImageFixedCache = {}

-- ----------------------------------------
-- GetImages
-- ----------------------------------------
Self.GetImages = function(frame)
    return Self.GetImagesMain(Self.ProcessParams(Helper.GetArgs(frame), true))
end

function Self.GetImagesMain(images, params, sizeParam, skipCache)
    --[[ Direct Call params
        images = filename array
        params = table of [File:  params
        sizeParam = size group or size
    --]]
    local hash
    if not skipCache then
        hash = Helper.HashArgs(images, params, sizeParam)
        if g_ImgCache[hash] then
            -- if we already did this image then returned the cached version
            return g_ImgCache[hash]
        end
    end
    -- Make the code string
    local code = ""
    for i, img in ipairs(images) do
        local styles, needspan = Self.GetStyles(img, sizeParam)
        local nowrap = styles.whitespace == "nowrap"
        local prefix = ""
        local wiki = ""
        local suffix = ""
        -- --------------
        -- Span Prefix
        -- --------------
        if needspan then
            prefix = prefix.."<span style='"
            for k, v in pairs(styles) do
                -- Size gets added elsewhere; Only do padleft on the first img and padright on last img
                if k ~= "size" and (k ~= "padleft" or i == 1) and (k ~= "padright" or i == #images)then
                    prefix = prefix..(C.SHORTSTYLES[k] or k)..":"..v.."; "
                end
            end
            prefix = prefix.."'>"
            if nowrap then
                prefix = prefix.."&#8288;"
            end
        end
        -- --------------
        -- Image WikiCode
        -- --------------
        wiki = wiki.."[[".."File:"..Self.GetFileExt(img)
        local size = styles.size or sizeParam
        if size then
            wiki = wiki.."|"..size
        end
        local caption = ""
        for k, v in pairs(params) do
            if k == "caption" then
                -- Syntax requires captions to be listed last
                caption = "|"..v
            elseif k == "tooltip" and sizeParam == "medium" then
                -- Temp logic for buggy tooltips
                prefix = prefix.."<span class='tooltip'>"
                suffix = suffix.."<span class='tooltiptext'>"..v.."</span></span>"
            elseif k == "options" then
                -- All the non named parameters
                wiki = wiki.."|"..ustr.gsub(v, ",", "|")
            else
                wiki = wiki.."|"..k.."="..v
            end
        end
        wiki = wiki..caption.."]]"
        -- --------------
        -- Span Suffix
        -- --------------
        if needspan then
            if nowrap then
                suffix = suffix.."&#8288;"
            end
            suffix = suffix.."</span>"
        end
        code = code..prefix..wiki..suffix
    end
    if not skipCache then
        g_ImgCache[hash] = code
    end
    return code
end

function Self.ProcessParams(args, skipCache)
    local images = {}
    local params = {}
    local sizeParam = nil
    for k, v in pairs(args) do
        -- --------------
        -- Image Names
        -- --------------
        if tonumber(k) then
            v = Self.CleanFileName(v)
            if v ~= "" then
                images[k] = v
            end
        -- --------------
        -- Size Param
        -- --------------
        elseif k == "size" then
            if C.DEFAULTS[v] then
                -- The size group was provided
                sizeParam = v
            else
                --width, height, type
                local w, h, t = ustr.match(v or "", "^(%d*)x?(%d*)(pxo?)$") 
                if t == "px" then
                    -- convert existing hardcoded sizes into named size groups
                    local m = math.max(tonumber(w) or 0, tonumber(h) or 0)
                    if m <= 35 then
                        sizeParam = "text"
                    elseif m <= 55 then
                        sizeParam = "medium"
                    elseif m <= 80 then
                        sizeParam = "large"
                    else
                        sizeParam = v
                    end
                elseif t == "pxo" then
                    -- allows for the system to be overridden if necessary
                    sizeParam = ustr.sub(v, 1, -2)
                end
            end
        -- --------------
        -- Other Params
        -- --------------
        else
            params[k] = v
        end
    end
    if not params.caption and params.link and ustr.sub(params.link,1,1) == "#" then
        -- If the link is to a section on the same page, make a caption without the #
        params.caption = ustr.sub(params.link,2)
    end
    return images, params, sizeParam, skipCache
end

function Self.GetStyles(img, sizeParam)
    local styles = {}
    local needspan = false
    if C.DEFAULTS[sizeParam] then
        for k, v in pairs(C.DEFAULTS[sizeParam]) do
            styles[k] = v
            needspan = needspan or k~="size"
        end
    end
    if C.OVERRIDES[sizeParam] and C.OVERRIDES[sizeParam][img] then
        for k, v in pairs(C.OVERRIDES[sizeParam][img]) do
            styles[k] = v
            needspan = needspan or k~="size"
        end
    end
    return styles, needspan
end

-- ----------------------------------------
-- ImageFixed
-- ----------------------------------------
Self.ImageFixed = function(frame)
    return Self.ImageFixedMain(Helper.GetArgs(frame), frame, true)
end

function Self.ImageFixedMain(args, frame, skipCache)
    --[[ Direct Call args =
        [1] = filename,
        [2] = size or size group
        [3] = w or h for scaling to axis
        link = link location, empty string for no link, or nil for image zoom link
    --]]
    local hash
    if not skipCache then
        hash = Helper.HashArgs(args)
        if g_ImageFixedCache[hash] then
            -- if we already did this image then returned the cached version
            return g_ImageFixedCache[hash]
        end
    end
    if not frame then frame = mw.getCurrentFrame() end
    local filename = Self.CleanFileName(args[1])
    filename = Self.GetFileExt(filename or "zzZ___")
    local url = frame:preprocess("{{filepath:"..filename.."}}")
    assert(url, "First parameter needs to be a valid file")
    local size = args[2]
    size = C.SIZE_GROUPS[size] or tonumber(size)
    local scale = ustr.lower(ustr.sub(args[3] or "h", 1, 1)) == "w" and "width" or "height"
    local link = args.link
    local code = ""
    local code_end = ""
    -- Span for mouseover caption and link
    if link == nil then
        -- Default Link
        code = code.."<span>["..url.." "
        code_end = "]</span>"
    elseif link == "" then
        -- No Link
        code = code.."<span>"
        code_end = "</span>"
    else
        -- Supplied Link
        link = ustr.gsub(args.link, " ", "_")
        code = code.."<span title='"..link.."'>[https://rootsofpacha.fandom.com/wiki/"..link.." "
        code_end = "]</span>"
    end
    if size then
        url = ustr.gsub(url, "%?", "/scale-to-"..scale.."-down/"..size.."?")
    end
    code = code..url..code_end
    if Helper.IfDesktop() then
        code = code.."<span style='display:none'>[[".."File:"..filename.."|20px]]</span>"
    end
    if not skipCache then
        g_ImageFixedCache[hash] = code
    end
    return code
end

-- ----------------------------------------
-- Infobox Gallery
-- ----------------------------------------
Self.InfoboxGallery = function(frame)
    return Self.InfoboxGalleryMain(Helper.GetArgs(frame), frame)
end

function Self.InfoboxGalleryMain(args, frame)
    local code = ""
    local isDesktop = Helper.IfDesktop()
    if isDesktop then
        code = code.."<tabber>"
    else
        code = code.."<gallery>"
    end
    for _, image in ipairs(args) do
        image = mw.text.trim(image)
        local file, cap = ustr.match(image, "^([^#]+)#?(.-)$")
        cap = cap ~= "" and cap or file
        file = Self.GetFileExt(file)
        cap = mw.text.decode(cap)
        cap = cap..ustr.char(0x2060) --adds a non-breaking zero width space to the cap to prevent it from matching page headers
        if isDesktop then
            -- \n|-|Wheat=[[File:Wheat.png|300px|thumb|center]]
            code = code.."\n|-|"..cap.."=[[".."File:"..file.."|300px|thumb|center]]"
        else
            -- \nWheat.png|Wheat
            code = code.."\n"..file.."|"..cap
        end
    end
    if isDesktop then
        code = code.."\n</tabber>"
    else
        code = code.."\n</gallery>"
    end
    return frame:preprocess(code)
end

-- ----------------------------------------
-- Infobox Image Type
-- ----------------------------------------
Self.InfoboxImageType = function(frame)
    return Self.InfoboxImageTypeMain(Helper.GetArgs(frame), frame)
end

function Self.InfoboxImageTypeMain(args, frame)
    local image = args[1]
    local stripped = mw.text.killMarkers(image)
    if ustr.match(stripped, "^%s*$") then
        if Helper.IfDesktop() and ustr.match(image, "gallery") then
            image = "Use [["..":Template:InfoboxGallery]] instead[[".."Category:InfoboxGallery]]"
        end
        return image
    else
        return "[[".."File:"..Self.GetFileExt(image).."|300px|thumb|center]]"
    end
end

-- ----------------------------------------
-- Common Functions
-- ----------------------------------------
function Self.GetFileExt(name)
    -- Allow # (which can't be in an image name) to be a stand in for the file extension period
    local file = ustr.gsub(name, "#", ".")
    local ext = ustr.match(name, "%.([^%.]*)$")
    if not ext or not C.VALID_EXTS[ustr.lower(ext)] then
        file = file..".png"
    end
    return file
end

function Self.CleanFileName(name)
    -- Strip out any encoding and get plain text image names
    --uneeded? name = mw.text.decode(name)
    name = mw.uri.decode(name, "WIKI")
    -- Also fix the case insensitivity of the first letter
    name = ustr.upper(ustr.sub(name, 1, 1)) .. ustr.sub(name, 2)
    return name
end

-- ----------------------------------------
-- Required for Modules to function
-- ----------------------------------------
return Self