5,327 Pages

-- <pre>
local p = {}
 
local skinData  = mw.loadData('Module:SkinData/data')
local lib       = require('Module:Feature')
local color     = require('Module:Color')
local FN        = require('Module:Filename')
local IL        = require('Module:ImageLink')
local userError = require('Dev:User error')
 
function p.skinscount()
    local s           = ""
    local resulttable = {}
    local prices      = {}
    local limited     = 0
    local rare        = 0
    local total       = 0
    local cost        = 0
 
    for championname in pairs(skinData) do
        local t = skinData[championname]["skins"]
        for skinname in pairs(t) do
            if skinname ~= "Original" and t[skinname]["id"] ~= nil and t[skinname]["release"] ~= "N/A" and t[skinname]["availability"] ~= "Upcoming" then
                if t[skinname]["availability"] == "Limited" then
                        limited = limited + 1
                        total   = total   + 1
                else
                    local price = t[skinname]["cost"]
                    if price == 10 or price == 100 or price == "special" then
                        price = 2450
                    end
 
                    if t[skinname]["looteligible"] == false then
                        rare  = rare  + 1
                        total = total + 1
                    else
                        if resulttable[price] == nil then
                            resulttable[price] = 0
                            table.insert(prices, price)
                        end
                        resulttable[price] = resulttable[price] + 1
                        total = total + 1
                        cost  = cost + price
                    end
                end
            end
        end
    end
    table.sort(prices)
 
    s = '<table class="wikitable sortable">'
    s = s .. '  <tr><th data-sort-type="number">Price</th><th>Count</th></tr>'
    for _, price in pairs(prices) do
        s = s ..'  <tr><td data-sort-value="' .. price .. '" style="text-align:right">' .. price .. '</td><td style="text-align:right">' .. resulttable[price] .. '</td></tr>'
    end
 
    s = s .. '  <tr><td data-sort-value="50000" style="text-align:right">Rare</td><td style="text-align:right">' .. rare .. '</td></tr>'
    s = s .. '  <tr><td data-sort-value="60000" style="text-align:right">Limited</td><td style="text-align:right">' .. limited .. '</td></tr>'
    s = s .. '</table>'
    s = s .. 'Total: ' .. total ..' skins (Total: ' .. cost .. ' RP)'
 
    return s
end
 
function p.splashartistpage()
    local searchstring = mw.title.getCurrentTitle().text
 
    local championtable = {}
    for championname in pairs(skinData) do
        table.insert(championtable, championname)
    end
    table.sort(championtable)
 
    local resulttable = {}
    for _, championname in pairs(championtable) do
        local skintable  = {}
        for championname in pairs(skinData[championname]["skins"]) do
            table.insert(skintable, championname)
        end
        table.sort(skintable)
 
        for _, skinname in pairs(skintable) do
            local t = skinData[championname]["skins"][skinname]
 
            if t.splashartist ~= nil then
                for _, splashartistname in pairs(t.splashartist) do
                    if splashartistname == searchstring then
                        table.insert(resulttable, {championname, skinname, t.formatname, t.splashartist})
                    end
                end
            end
        end
    end
 
    if #resulttable == 0 then
        return userError("No results for " .. searchstring, "LuaError")
    end
 
    -- Three random images of work examples
    math.randomseed(os.time())
    local hash = {}
    local rnd  = 0
    local s1   = ""
 
    for i = 1, 3 do
        rnd = math.random(#resulttable)
        if hash[rnd] ~= true then
            s1 = s1 .. "[[File:" .. FN.skin({resulttable[rnd][1], resulttable[rnd][2]}) .. "|thumb|200px|Work example.]]"
        end
        hash[rnd] = true
    end
 
    -- Splash Art list
    local s2 = "<dl><dt>Splash Art</dt></dl><ul><li>"
    local tempval = ""
    for i, val in pairs(resulttable) do
        local championname = val[1]
        local skinname     = val[2]
        local formatname   = val[3]
        local splashartist = val[4]
 
        if tempval == championname then
            s2 = s2 .. ", "
        else
            if i ~= 1 then
                s2 = s2 .. "</li><li>"
            end
            tempval = championname
        end
        s2 = s2 .. tostring(IL.skin{
            ["champion"] = championname,
            ["skin"] = skinname,
            ["link"] = championname .. "/Skins",
            ["text"] = lib.ternary(formatname, formatname, skinname .. " " .. championname),
            ["circle"] = "true"
        })
 
        count = 0
        for i, val in pairs (splashartist) do
            if val ~= searchstring then
                if count == 0 then
                    s2 = s2 .. " <small>(Collaboration with "
                else
                    s2 = s2 .. ", "
                end
                s2 = s2 .. splashartist[i]
                count = count + 1
            end
        end
        if count ~= 0 then
            s2 = s2 .. ")</small>"
        end
    end
    s2 = s2 .. "</li></ul>"
 
    return s1 .. s2
end
 
function p.chromapartner(frame)
    local s = ''
 
    s = s .. '<div id="chromaexhibition" style="position:relative">'
    s = s .. '<b>Partner Program Chromas</b>'
    s = s .. '<div class="chroma-gallery" style="width:718px; text-align:center"><div class="base">[[File:Partner Program LoL Logo.png|150px]]</div>'
 
    local championtable = {}
    for x in pairs(skinData) do
        table.insert(championtable, x)
    end
    table.sort(championtable)
 
    local resulttable = {}
    for _, championname in pairs(championtable) do
        local skintable  = {}
        for championname in pairs(skinData[championname]["skins"]) do
            table.insert(skintable, championname)
        end
        table.sort(skintable)
 
        for _, skinname in pairs(skintable) do
            local chromatable = {}
            local t           = skinData[championname]["skins"][skinname]
            local formatname  = t.formatname
 
            if t.chromas ~= nil then
                t = t.chromas
                for chromaname in pairs(t) do
                    if t[chromaname].distribution == "Partner Program" then
                        s = s .. '<div class="skin-icon" data-champion="' .. championname .. '" data-skin="' .. skinname .. '"><div class="chroma partner-border">[[File:' .. FN.chroma({championname, skinname, chromaname}) .. '|100px|border|link=]]</div> <div class="chroma-caption">[[File:' .. FN.championcircle({championname, skinname}) .. '|20px|link=' .. championname .. ']] [[' .. championname .. '|' .. lib.ternary(formatname, formatname, skinname .. ' ' .. championname) .. ']]</div></div>'
                    end
 
                end
            end
        end
    end
    s = s .. '</div>'
 
    return s
end
 
 
 
function p.chromagallery(frame)
    local args; if frame.args == nil then args = lib.arguments(frame) else args = lib.arguments(frame.args) end
 
    local champname = lib.validateName(args['champion'] or args[1])
    local skinname  =                  args['skin']     or args[2] or "Original"
 
    local t = skinData[champname].skins[skinname]
    if t == nil or t.chromas == nil then
        return userError("No chromas specified for skin ''" .. skinname .. "'' in Module:SkinData/data", "LuaError")
    end
    t = t.chromas
 
    local header = "Chromas"
    local frame = mw.getCurrentFrame()
 
    if skinData[champname].skins[skinname].forms ~= nil then
        header = "forms"
    end
 
    local chromatable  = {}
    local chromastring = args['chromas'] or "true"
    if chromastring == "true" then
        for chromaname in pairs(t) do
            table.insert(chromatable, chromaname)
        end
    else
        chromatable = lib.split(chromastring, ",")
    end
    table.sort(chromatable)
 
    local formatname = skinData[champname].skins[skinname].formatname
    local key        = args["key"] or "true" 
    local imagewidth = "100px"
    local s = ''
    s = s .. '<div id="chromaexhibition" style="position:relative"><b>' .. lib.ternary(formatname, formatname, skinname .. " " .. champname) .. " " .. header .. "</b>"
 
    if key == "true" then
        s = s .. "<div class='glossary' data-param='Chroma exhibition' style='position:absolute; top:5px; right: 5px; z-index:20;'>[[File:Information.svg|30px|link=]]</div>"
    end
 
    if #chromatable > 8 then
        imagewidth = "80px"
        s = s .. '<div class="chroma-gallery-large" style="width:718px; text-align:center">'
    else
        s = s .. '<div class="chroma-gallery" style="width:718px; text-align:center">'
    end
 
    s = s .. '<div class="base">[[File:' .. FN.chroma({champname, skinname, "Base"}) .. '|183px]]</div>'
 
    for i, chromaname in pairs(chromatable) do
        if skinData[champname].skins[skinname].chromas[chromaname] == nil then
            return userError("Chroma ''" .. chromaname .. "'' not specified in Module:SkinData/data for " .. lib.ternary(formatname, formatname, skinname .. " " .. champname), "LuaError")
        end
 
        local availability = skinData[champname].skins[skinname].chromas[chromaname].availability or "Available"
 
        if availability ~= "Canceled" then
            s = s .. "<div><div class='chroma " .. string.lower(availability) .. "-border'>[[File:" .. FN.chroma({champname, skinname, chromaname}) .. "|" .. imagewidth .. "|border]]</div> <div class='chroma-caption'>" .. chromaname .. "</div></div>"
        end
    end
 
    s = s .. '</div>'
 
    return frame:preprocess(s)
end
 
function p.skinpage(frame)
    local args; if frame.args == nil then args = lib.arguments(frame) else args = lib.arguments(frame.args) end
 
    local champname = args['champion'] or args[1] or mw.title.getCurrentTitle().rootText
 
    if skinData[champname] == nil then
        return userError("Champion ''" .. champname .. "'' does not exist in Module:SkinData/data", "LuaError")
    end
 
    local t = skinData[champname]["skins"]
 
    local availabletable = {}
    local legacytable    = {}
    local limitedtable   = {}
    local upcomingtable  = {}
    local canceledtable  = {}
 
    for skinname in pairs(t) do
        if t[skinname].availability == "Available" then
            table.insert(availabletable, {skinname, t[skinname]})
        end
        if t[skinname].availability == "Legacy" then
            table.insert(legacytable,    {skinname, t[skinname]})
        end
        if t[skinname].availability == "Limited" or t[skinname].availability == "Rare" then
            table.insert(limitedtable,   {skinname, t[skinname]})
        end
        if t[skinname].availability == "Upcoming" then
            table.insert(upcomingtable,  {skinname, t[skinname]})
        end
        if t[skinname].availability == "Canceled" then
            table.insert(canceledtable,  {skinname, t[skinname]})
        end
    end
 
    function skinitem(data)
        local lang = mw.language.new("en")
        local skinname     = data[1]
        local formatname   = data[2].formatname or skinname .. ' ' .. champname
        local champid      = skinData[champname]["id"]
        local skinid       = data[2].id
        local cost         = data[2].cost
        local release      = data[2].release
        local distribution = data[2].distribution
 
        if release ~= "N/A" then
            release  = lang:formatDate("d-M-Y", data[2].release)
        end
 
        local s = ""
 
        s = s .. '<div style="display:inline-block; margin:5px; width:342px"><div class="skin-icon" data-champion="' .. champname .. '" data-skin="' .. skinname .. '">[[File:' .. FN.skin({champname, skinname}) .. '|340px|border]]</div><div><div style="float:left">' .. formatname
 
        if skinid ~= nil then
            standardizedname = string.lower(champname:gsub("[' ]", ""))
            if standardizedname == "wukong" then
                standardizedname = "monkeyking"
            end
            teemogg_skinid = standardizedname .. '-' .. skinid
            s = s .. ' <span class="plainlinks">[https://teemo.gg/model-viewer?model-type=champions&skinid=' .. teemogg_skinid .. ' <span class="button" title="View in 3D" style="text-align:center; border-radius: 2px;"><b>View in 3D</b></span>]</span>'
        end
 
        s = s .. '</div><div style="float:right">'
 
        if cost == 'N/A' then
            -- skip
        elseif cost == 150000 then
            s = s .. tostring(IL.basic{["link"] = "Blue Essence", ["text"] = cost, ["alttext"] = cost .. " Blue Essence", ["image"] = "BE icon.png", ["border"] = "false", ["labellink"] = "false"}) .. ' / ' 
        elseif cost == 100 then
            s = s .. tostring(IL.basic{["link"] = "Prestige Point", ["text"] = cost, ["alttext"] = cost .. " Prestige Points", ["image"] = "Hextech Crafting Prestige token.png", ["border"] = "false", ["labellink"] = "false"}) .. ' / ' 
        elseif cost == 10 then
            s = s .. tostring(IL.basic{["link"] = "Gemstone", ["text"] = cost, ["alttext"] = cost .. " Rare Gems", ["image"] = "Rare Gem.png", ["border"] = "false", ["labellink"] = "false"}) .. ' / ' 
        elseif cost == "special" then
            s = s .. "Special pricing" .. ' / ' 
        else
            s = s .. tostring(IL.basic{["link"] = "Riot Points", ["text"] = cost, ["alttext"] = cost .. " RP", ["image"] = "RP icon.png", ["border"] = "false", ["labellink"] = "false"}) .. ' / ' 
        end
 
        s = s .. lib.ternary(release == 'N/A', 'N/A', release) .. '  </div></div></div>'
 
        return s
    end
 
    function chroma(chromatable)
        s = ""
        if #chromatable > 0 then
            for i in ipairs(chromatable) do
                s = s .. '<div style="clear:both"></div>' .. p.chromagallery{champname, chromatable[i]}
            end
        end
        return s
    end
 
    function comp(a, b)
        local a = a[2].id or -1
        local b = b[2].id or -1
 
        if a < b then
            return true
        end
    end
 
    local skintable = {
            {availabletable, text = 'Available'},
            {legacytable,    text = 'Legacy Vault'},
            {limitedtable,   text = 'Rare & Limited'},
            {upcomingtable,  text = 'Upcoming'},
            {canceledtable,  text = 'Canceled'}
        }
    local s = ''
    for i, value in ipairs(skintable) do
        table.sort(skintable[i][1], comp)
        local chromatable = {}
        if #value[1] > 0 then
            s = s .. ('<div style="clear:both"></div>\n==' .. value.text .. '==\n<div style="font-size:small">')
            for i in pairs(value[1]) do
                s = s .. skinitem(value[1][i])
                if value[1][i][2].chromas then
                    table.insert(chromatable, value[1][i][1])
                end
            end
            s = s .. '</div>'
            table.sort(chromatable)
 
            s = s .. chroma(chromatable)
        end
    end
 
    s = s .. '<div style="clear:both"></div>'
    return s
end
 
function p.getAllchampionskins(frame)
    local args; if frame.args == nil then args = lib.arguments(frame) else args = lib.arguments(frame.args) end
 
    local champname = lib.validateName(args['champion'] or args[1])
 
    return skinData[champname]
end
 
function p.getChampionskin(frame)
    local args; if frame.args == nil then args = lib.arguments(frame) else args = lib.arguments(frame.args) end
 
    local champname = lib.validateName(args['champion'] or args[1])
    local skinname  =                  args['skin']     or args[2]
 
    return skinData[champname].skins[skinname or "Original"]
end
 
function p.getFormatname(frame)
    local args; if frame.args == nil then args = lib.arguments(frame) else args = lib.arguments(frame.args) end
 
    local champname = lib.validateName(args['champion'] or args[1])
    local skinname  =                  args['skin']     or args[2]
 
    return skinData[champname].skins[skinname or "Original"].formatname
end
 
function p.getAvailability(frame)
    local args; if frame.args == nil then args = lib.arguments(frame) else args = lib.arguments(frame.args) end
 
    local champname = lib.validateName(args['champion'] or args[1])
    local skinname  =                  args['skin']     or args[2]
 
    return skinData[champname].skins[skinname or "Original"].availability
end
 
function p.getDistribution(frame)
    local args; if frame.args == nil then args = lib.arguments(frame) else args = lib.arguments(frame.args) end
 
    local champname = lib.validateName(args['champion'] or args[1])
    local skinname  =                  args['skin']     or args[2]
 
    return skinData[champname].skins[skinname or "Original"].distribution
end
 
function p.getCost(frame)
    local args; if frame.args == nil then args = lib.arguments(frame) else args = lib.arguments(frame.args) end
 
    local champname = lib.validateName(args['champion'] or args[1])
    local skinname  =                  args['skin']     or args[2]
 
    return skinData[champname].skins[skinname or "Original"].cost
end
 
function p.getRelease(frame)
    local args; if frame.args == nil then args = lib.arguments(frame) else args = lib.arguments(frame.args) end
 
    local champname = lib.validateName(args['champion'] or args[1])
    local skinname  =                  args['skin']     or args[2]
 
    return skinData[champname].skins[skinname or "Original"].release
end
 
function p.getRetired(frame)
    local args; if frame.args == nil then args = lib.arguments(frame) else args = lib.arguments(frame.args) end
 
    local champname = lib.validateName(args['champion'] or args[1])
    local skinname  =                  args['skin']     or args[2]
 
    return skinData[champname].skins[skinname or "Original"].retired
end
 
function p.getEarlysale(frame)
    local args; if frame.args == nil then args = lib.arguments(frame) else args = lib.arguments(frame.args) end
 
    local champname = lib.validateName(args['champion'] or args[1])
    local skinname  =                  args['skin']     or args[2]
 
    return skinData[champname].skins[skinname or "Original"].earlysale
end
 
function p.getSet(frame)
    local args; if frame.args == nil then args = lib.arguments(frame) else args = lib.arguments(frame.args) end
 
    local champname = lib.validateName(args['champion'] or args[1])
    local skinname  =                  args['skin']     or args[2]
 
    local t = skinData[champname].skins[skinname or "Original"].set
 
    if t == nil then
        return nil
    end
 
    for i, setname in ipairs(t) do
        if i ~= 1 then
            s = s .. ", " .. setname:gsub("% ", "&nbsp;")
        else
            s = setname
        end
    end
 
    return s
end
 
function p.getSetlist(frame)
    local championtable = {}
    local sets = {}
    local hash = {}
    local setList = mw.html.create('ul')
 
    setList:newline()
 
    for x in pairs(skinData) do
        table.insert(championtable, x)
    end
    table.sort(championtable)
 
    for _, championname in pairs(championtable) do
        local skintable  = {}
        for championname in pairs(skinData[championname]["skins"]) do
            table.insert(skintable, championname)
        end
        table.sort(skintable)
 
        for _, skinname in pairs(skintable) do
            local t = skinData[championname]["skins"][skinname]
 
            if t.set ~= nil then
                for _, value in pairs(t.set) do
                    if (not hash[value]) then
                        table.insert(sets, value)
                        hash[value] = true
                    end
                end
            end
        end
    end
 
    table.sort(sets)
 
    for _, setname in pairs(sets) do
        setList
            :tag('li')
                :wikitext('[[' .. setname .. ']]')
                :done()
            :done()
            :newline()
    end
 
    return setList
end
 
function p.getSetskins(frame)
    local args; if frame.args == nil then args = lib.arguments(frame) else args = lib.arguments(frame.args) end
 
    local skinList = mw.html.create('ul')
    local championtable = {}
    local result = false
 
    skinList:newline()
 
    for x in pairs(skinData) do
        table.insert(championtable, x)
    end
    table.sort(championtable)
 
    for _, championname in pairs(championtable) do
        local skintable  = {}
 
        for championname in pairs(skinData[championname]["skins"]) do
            table.insert(skintable, championname)
        end
        table.sort(skintable)
 
        for _, skinname in pairs(skintable) do
            local hit = false
            local t = skinData[championname]["skins"][skinname]
 
            if t.set ~= nil then
                for _, subset in pairs(t.set) do
                    if subset == args[1] then
                        hit = true
                        result = true
                    end
                end
            end
            if hit == true then
                skinList
                    :tag('li')
                        :tag('div')
                            :addClass('skin-icon')
                            :attr('data-champion', championname)
                            :attr('data-skin', skinname)
                            :wikitext('[[File:' .. FN.championcircle({championname, skinname}) .. '|20px|link=' .. championname .. ']] [[' .. championname .. '|' .. lib.ternary(t["formatname"] ~= nil, t["formatname"], skinname .. " " .. championname) .. ']]')
                        :done()
                    :done()
                    :newline()
            end
        end
    end
 
    if result == false then 
        skinList
            :tag('li')
                :wikitext('No match found for ' .. args[1] .. '.')
            :done()
            :newline()
    end
 
    return skinList
end
 
function p.getNeweffects(frame)
    local args; if frame.args == nil then args = lib.arguments(frame) else args = lib.arguments(frame.args) end
 
    local champname = lib.validateName(args['champion'] or args[1])
    local skinname  =                  args['skin']     or args[2]
 
    return skinData[champname].skins[skinname or "Original"].neweffects
end
 
function p.getNewrecall(frame)
    local args; if frame.args == nil then args = lib.arguments(frame) else args = lib.arguments(frame.args) end
 
    local champname = lib.validateName(args['champion'] or args[1])
    local skinname  =                  args['skin']     or args[2]
 
    return skinData[champname].skins[skinname or "Original"].newrecall
end
 
function p.getNewanimations(frame)
    local args; if frame.args == nil then args = lib.arguments(frame) else args = lib.arguments(frame.args) end
 
    local champname = lib.validateName(args['champion'] or args[1])
    local skinname  =                  args['skin']     or args[2]
 
    return skinData[champname].skins[skinname or "Original"].newanimations
end
 
function p.getTransforming(frame)
    local args; if frame.args == nil then args = lib.arguments(frame) else args = lib.arguments(frame.args) end
 
    local champname = lib.validateName(args['champion'] or args[1])
    local skinname  =                  args['skin']     or args[2]
 
    return skinData[champname].skins[skinname or "Original"].transforming
end
 
function p.getFilter(frame)
    local args; if frame.args == nil then args = lib.arguments(frame) else args = lib.arguments(frame.args) end
 
    local champname = lib.validateName(args['champion'] or args[1])
    local skinname  =                  args['skin']     or args[2]
 
    return skinData[champname].skins[skinname or "Original"].filter
end
 
function p.getNewquotes(frame)
    local args; if frame.args == nil then args = lib.arguments(frame) else args = lib.arguments(frame.args) end
 
    local champname = lib.validateName(args['champion'] or args[1])
    local skinname  =                  args['skin']     or args[2]
 
    return skinData[champname].skins[skinname or "Original"].newquotes
end
 
function p.getNewvoice(frame)
    local args; if frame.args == nil then args = lib.arguments(frame) else args = lib.arguments(frame.args) end
 
    local champname = lib.validateName(args['champion'] or args[1])
    local skinname  =                  args['skin']     or args[2]
 
    return skinData[champname].skins[skinname or "Original"].newvoice
end
 
function p.getExtras(frame)
    local args; if frame.args == nil then args = lib.arguments(frame) else args = lib.arguments(frame.args) end
 
    local champname = lib.validateName(args['champion'] or args[1])
    local skinname  =                  args['skin']     or args[2]
 
    return skinData[champname].skins[skinname or "Original"].extras
end
 
function p.getLore(frame)
    local args; if frame.args == nil then args = lib.arguments(frame) else args = lib.arguments(frame.args) end
 
    local champname = lib.validateName(args['champion'] or args[1])
    local skinname  =                  args['skin']     or args[2]
 
    return skinData[champname].skins[skinname or "Original"].lore
end
 
function p.getLootEligible(frame)
    local args; if frame.args == nil then args = lib.arguments(frame) else args = lib.arguments(frame.args) end
 
    local champname = lib.validateName(args['champion'] or args[1])
    local skinname  =                  args['skin']     or args[2]
 
    return skinData[champname].skins[skinname or "Original"].looteligible
end
 
function p.getChromas(frame)
    local args; if frame.args == nil then args = lib.arguments(frame) else args = lib.arguments(frame.args) end
 
    local champname = lib.validateName(args['champion'] or args[1])
    local skinname  =                  args['skin']     or args[2]
 
    return skinData[champname].skins[skinname or "Original"].chromas
end
 
function p.getChromacount(frame)
    local args; if frame.args == nil then args = lib.arguments(frame) else args = lib.arguments(frame.args) end
 
    local champname = lib.validateName(args['champion'] or args[1])
    local skinname  =                  args['skin']     or args[2]
 
    local t = skinData[champname].skins[skinname or "Original"].chromas
    local s = ""
 
    local chromatable  = {}
    for chromaname in pairs(t) do
        table.insert(chromatable, chromaname)
    end
 
    return #chromatable or "N/A"
end
 
function p.getChromanames(frame)
    local args; if frame.args == nil then args = lib.arguments(frame) else args = lib.arguments(frame.args) end
 
    local champname = lib.validateName(args['champion'] or args[1])
    local skinname  =                  args['skin']     or args[2]
 
    local t = skinData[champname].skins[skinname or "Original"]
    if t == nil or t.chromas == nil then
        return ""
    end
    t = t.chromas
 
    local chromatable  = {}
    for chromaname in pairs(t) do
        table.insert(chromatable, chromaname)
    end
    table.sort(chromatable)
 
    local s = ""
    for i, chromaname in pairs(chromatable) do
        if i ~= 1 then
            s = s  .. ", " .. chromaname
        else
            s = s .. chromaname
        end
    end
 
    return s
end
 
function p.getForms(frame)
    local args; if frame.args == nil then args = lib.arguments(frame) else args = lib.arguments(frame.args) end
 
    local champname = lib.validateName(args['champion'] or args[1])
    local skinname  =                  args['skin']     or args[2]
 
    return skinData[champname].skins[skinname or "Original"].forms
end
 
function p.getFormnames(frame)
    local args; if frame.args == nil then args = lib.arguments(frame) else args = lib.arguments(frame.args) end
 
    local champname = lib.validateName(args['champion'] or args[1])
    local skinname  =                  args['skin']     or args[2]
 
    local t = skinData[champname].skins[skinname or "Original"].forms
    local s = ""
 
    local formtable  = {}
    for formname in pairs(t) do
        table.insert(formtable, formname)
    end
    table.sort(formtable)
 
    for i, formname in pairs(formtable) do
        if i ~= 1 then
            s = s  .. ", " .. formname
        else
            s = s .. formname
        end
    end
 
    return s
end
 
function p.getFormicon(frame)
    local args; if frame.args == nil then args = lib.arguments(frame) else args = lib.arguments(frame.args) end
 
    local champname = lib.validateName(args['champion'] or args[1])
    local skinname  =                  args['skin']     or args[2]
 
    return skinData[champname].skins[skinname or "Original"].formicon
end
 
function p.getVu(frame)
    local args; if frame.args == nil then args = lib.arguments(frame) else args = lib.arguments(frame.args) end
 
    local champname = lib.validateName(args['champion'] or args[1])
    local skinname  =                  args['skin']     or args[2]
 
    return skinData[champname].skins[skinname or "Original"].vu
end
 
function p.getSplashartist(frame)
    local args; if frame.args == nil then args = lib.arguments(frame) else args = lib.arguments(frame.args) end
 
    local champname = lib.validateName(args['champion'] or args[1])
    local skinname  =                  args['skin']     or args[2]
 
    local t = skinData[champname].skins[skinname or "Original"].splashartist
    local s = ""
 
    if t == nil then
        return "Unknown artist"
    end
 
    for i, splashartistname in ipairs(t) do
        if i ~= 1 then
            s = s .. ", " .. splashartistname:gsub("% ", "&nbsp;")
        else
            s = splashartistname
        end
    end
    return s
end
 
function p.getArtistlist(frame)
    local championtable = {}
    local artists = {}
    local hash = {}
    local artistList = mw.html.create('ul')
 
    artistList:newline()
 
    for x in pairs(skinData) do
        table.insert(championtable, x)
    end
    table.sort(championtable)
 
    for _, championname in pairs(championtable) do
        local skintable  = {}
        for championname in pairs(skinData[championname]["skins"]) do
            table.insert(skintable, championname)
        end
        table.sort(skintable)
 
        for _, skinname in pairs(skintable) do
            local t = skinData[championname]["skins"][skinname]
 
            if t.splashartist ~= nil then
                for _, value in pairs(t.splashartist) do
                    if (not hash[value]) then
                        table.insert(artists, value)
                        hash[value] = true
                    end
                end
            end
        end
    end
    table.sort(artists)
 
    for _, artist in pairs(artists) do
        artistList
            :tag('li')
                :wikitext('[[' .. artist .. ']]')
                :done()
            :done()
            :newline()
    end
 
    return artistList
end
 
function p.getVariant(frame)
    local args; if frame.args == nil then args = lib.arguments(frame) else args = lib.arguments(frame.args) end
    local champname = lib.validateName(args['champion'] or args[1])
    local skinname  =                  args['skin']     or args[2]
 
    local searchid = skinData[champname].skins[skinname or "Original"].variant
    local skintable = {}
 
    if searchid == nil then
        return nil
    end
 
    for skinname, data in pairs(skinData[champname]["skins"]) do
        if data.id == searchid then
            return skinname
        end
    end
 
    return nil
end
 
function p.getVoiceactor(frame)
    local args; if frame.args == nil then args = lib.arguments(frame) else args = lib.arguments(frame.args) end
 
    local champname = lib.validateName(args['champion'] or args[1])
    local skinname  =                  args['skin']     or args[2]
 
    local t = skinData[champname].skins[skinname or "Original"].voiceactor
    local s = ""
 
    if t == nil then
        return "Unknown voice actor"
    end
 
    for i, voiceactorname in ipairs(t) do
        if i ~= 1 then
            s = s .. ", " .. voiceactorname:gsub("% ", "&nbsp;")
        else
            s = voiceactorname
        end
    end
 
    return s
end
 
function p.skinlist(frame)
    local lang = mw.language.new( "en" )
    local sdtable = mw.html.create('table')
 
    sdtable
        :addClass('sortable article-table nopadding sticky-header')
        :css('width','100%')
        :css('text-align','center')
        :newline()
 
        -- TABLE HEADER
        :tag('tr')
            :tag('th')
                :css('width','26px')
            :done()
            :tag('th')
                :wikitext('Skin')
            :done()
            :tag('th')
                :tag('span')
                    :attr('title','Availability')
                    :wikitext('[[File:Availability.png|40px|link=|]]')
                :done()
            :done()
            :tag('th')
                :tag('span')
                    :attr('title','Release')
                    :wikitext('[[File:Release.png|40px|link=|]]')
                :done()
            :done()
            :tag('th')
                :tag('span')
                    :attr('title','Cost')
                    :wikitext('[[File:RP icon.png|40px|link=|]]')
                :done()
            :done()
            :tag('th')
                :css('width','40px')
                :tag('span')
                    :attr('title','Voice filter')
                    :wikitext('[[File:Voice filter.png|40px|link=|]]')
                :done()
            :done()
            :tag('th')
                :css('width','40px')
                :tag('span')
                    :attr('title','Additional/unique quotes')
                    :wikitext('[[File:Additional quotes.png|40px|link=|]]')
                :done()
            :done()
            :tag('th')
                :css('width','40px')
                :tag('span')
                    :attr('title','New voiceover')
                    :wikitext('[[File:New voice.png|40px|link=|]]')
                :done()
            :done()
            :tag('th')
                :css('width','40px')
                :tag('span')
                    :attr('title','New visual and/or sound effects')
                    :wikitext('[[File:New effects.png|40px|link=|]]')
                :done()
            :done()
            :tag('th')
                :css('width','40px')
                :tag('span')
                    :attr('title','New animations and/or new recall animation')
                    :wikitext('[[File:New animations.png|40px|link=|]]')
                :done()
            :done()
            :tag('th')
                :css('width','40px')
                :tag('span')
                    :attr('title','Appearance can be toggled in-game [e.g. Ctrl + 5], or the skin "evolves" over the course of the game that is not part of the champion&#39;s base mechanics.')
                    :wikitext('[[File:Transforming.png|40px|link=|]]')
                :done()
            :done()
            :tag('th')
                :css('width','40px')
                :tag('span')
                    :attr('title','Part of a Collection')
                    :wikitext('[[File:Set piece.png|40px|link=|]]')
                :done()
            :done()
            :tag('th')
                :css('width','40px')
                :tag('span')
                    :attr('title','Features lore')
                    :wikitext('[[File:Mission icon.png|40px|link=|]]')
                :done()
            :done()
            :tag('th')
                :css('width','40px')
                :tag('div')
                    :attr('title','Chromas')
                    :css('width','30px')
                    :css('height','30px')
                    :css('padding','5px')
                    :wikitext('[[File:Chromaskins.png|30px|link=|]]')
                :done()
            :done()
        :done()
        :newline()
 
        -- TABLE ENTRIES
        local championtable = {}
        for x in pairs(skinData) do
            table.insert(championtable, x)
        end
        table.sort(championtable)
 
        local availablenode = mw.html.create('span')
        availablenode
            :css('color', 'green')
            :css('font-size', 'x-large')
            :css('vertical-align', 'text-top')
            :wikitext("✔")
 
        local legacynode = mw.html.create('span')
        legacynode
            :css('color', 'yellow')
            :css('font-size', 'x-large')
            :css('font-weight', '600')
            :css('vertical-align', 'text-top')
            :wikitext("‒")
 
        local limitednode = mw.html.create('span')
        limitednode
            :css('color', 'red')
            :css('font-size', 'x-large')
            :css('vertical-align', 'text-top')
            :wikitext("✘")
        local rarenode = mw.html.create('span')
        rarenode
            :css('color', 'orange')
            :css('font-size', 'x-large')
            :css('vertical-align', 'text-top')
            :wikitext("⭐")
 
 
        for _, championname in pairs(championtable) do
            local skintable  = {}
            for championname in pairs(skinData[championname]["skins"]) do
                table.insert(skintable, championname)
            end
            table.sort(skintable)
 
            for _, skinname in pairs(skintable) do
                if (
                    championname == "Akali"      and skinname == "Crimson" 
                    or 
                    championname == "Amumu"      and skinname == "Sewn Chaos"
                    or
                    championname == "Blitzcrank" and skinname == "Sewn Chaos"
                    or
                    championname == "Ryze"       and skinname == "Human"
                ) then
                    -- skip
                else
                    local t = skinData[championname]["skins"][skinname]
                    local sdnode = mw.html.create('tr')
                    local temp = ""
 
                    -- Skincircle
                    if (skinname == "Original") then
                        temp = "!" .. t["release"]
                    else 
                        temp = t["release"]
                    end
 
                    sdnode
                        :tag('td')
                            :addClass('champion-icon')
                            :attr('data-sort-value', championname .. temp)
                            :attr('data-champion', championname)
                            :attr('data-skin', skinname)
                            :wikitext('[[File:' .. FN.championcircle({championname, skinname}) .. '|20px|link=' .. championname .. ']]')
                        :done()
 
                    -- Skinname
                    sdnode
                        :tag('td')
                            :addClass('skin-icon')
                            :attr('data-champion', championname)
                            :attr('data-skin', skinname)
                            :css('text-align', 'left')
                            :wikitext(lib.ternary(t["formatname"] ~= nil, t["formatname"], skinname .. " " .. championname))
                        :done()
 
                    -- Availability
                    astring = '<span style="color: cornflowerblue;font-size: large;font-weight: 600;">⭘</span>'
                    if (t["availability"] == "Available") then
                        astring = tostring(availablenode)
                    end
                    if (t["availability"] == "Legacy") then
                        astring = tostring(legacynode)
                    end
                    if (t["availability"] == "Limited") then
                        astring = tostring(limitednode)
                    end
                    if (t["availability"] == "Rare") then
                        astring = tostring(rarenode)
                    end
                    sdnode
                        :tag('td')
                            :tag('span')
                                :attr('title', t["availability"] or 'Upcoming')
                                :wikitext(astring)
                        :done()
 
                    -- Release
                    local y, m, d = t["release"]:match("(%d+)-(%d+)-(%d+)")
                    if y == nil or m == nil or d == nil then
                        sdnode
                            :tag('td')
                                :attr('data-sort-value', t["release"])
                                :wikitext(t["release"])
                            :done()
                    else
                        sdnode
                            :tag('td')
                                :attr('data-sort-value', t["release"])
                                :wikitext(lang:formatDate('d-M-Y', t["release"]))
                            :done()
                    end
 
                    -- Cost
                    local image = ""
                    if (tostring(t["cost"]) == "150000") then
                        image = "[[File:BE icon.png|20px|link=]]"
                    end 
                    if (tostring(t["cost"]) == "100") then
                        image = "[[File:Hextech Crafting Prestige token.png|20px|link=]]"
                    end
                    if (tostring(t["cost"]) == "10") then
                        image = "[[File:Rare Gem.png|20px|link=]]"
                    end
                    sdnode
                        :tag('td')
                            :attr('data-sort-value', lib.ternary(tostring(t["cost"]) == "10" or tostring(t["cost"]) == "100", "2450", t["cost"]))
                            :tag('span')
                                :css('color', color.skin({t["cost"] .. ""}))
                                :wikitext(image .. t["cost"])
                            :done()
                        :done()
 
                    -- Filter
                    sdnode
                        :tag('td')
                            :attr('data-sort-value', lib.ternary(type(t["filter"]) ~= "nil", 1, 0))
                            :wikitext(lib.ternary(type(t["filter"]) ~= "nil", tostring(availablenode), ""))
                        :done()
 
                    -- Newquotes
                    sdnode
                        :tag('td')
                            :attr('data-sort-value', lib.ternary(type(t["newquotes"]) ~= "nil", 1, 0))
                            :wikitext(lib.ternary(type(t["newquotes"]) ~= "nil", tostring(availablenode), ""))
                        :done()
 
                    -- Newvoice
                    sdnode
                        :tag('td')
                            :attr('data-sort-value', lib.ternary(type(t["newvoice"]) ~= "nil", 1, 0))
                            :wikitext(lib.ternary(type(t["newvoice"]) ~= "nil", tostring(availablenode), ""))
                        :done()
 
                    -- Neweffects
                    sdnode
                        :tag('td')
                            :attr('data-sort-value', lib.ternary(type(t["neweffects"]) ~= "nil", 1, 0))
                            :wikitext(lib.ternary(type(t["neweffects"]) ~= "nil", tostring(availablenode), ""))
                        :done()
 
                    -- Newanimations
                    sdnode
                        :tag('td')
                            :attr('data-sort-value', lib.ternary(type(t["newanimations"]) ~= "nil", 1, 0))
                            :wikitext(lib.ternary(type(t["newanimations"]) ~= "nil", tostring(availablenode), ""))
                        :done()
 
                    -- Transforming
                    sdnode
                        :tag('td')
                            :attr('data-sort-value', lib.ternary(type(t["transforming"]) ~= "nil", 1, 0))
                            :wikitext(lib.ternary(type(t["transforming"]) ~= "nil", tostring(availablenode), ""))
                        :done()
 
                    -- Set
                    local multiset = false
                    if (type(t["set"]) ~= "nil") then
                        local s = ""
 
                        for i, setname in pairs(t["set"]) do
                            if i == 1 then
                                s = setname
                            else
                                multiset = true
                                s = s .. ", " .. setname
                            end
                        end
 
                        if multiset == true then
                            sdnode
                                :tag('td')
                                    :attr('data-sort-value', '1 Multiple')
                                    :tag('span')
                                        :attr('title', s)
                                        :wikitext(tostring(availablenode))
                                    :done()
                                :done()
                        else
                            sdnode
                                :tag('td')
                                    :attr('data-sort-value', s)
                                    :tag('span')
                                        :attr('title', s)
                                        :wikitext(tostring(availablenode))
                                    :done()
                                :done()
                        end
                    else
                        sdnode
                            :tag('td')
                                :attr('data-sort-value', 0)
                            :done()
                    end
 
                    -- Transforming
                    sdnode
                        :tag('td')
                            :attr('data-sort-value', lib.ternary(type(t["lore"]) ~= "nil", 1, 0))
                            :wikitext(lib.ternary(type(t["lore"]) ~= "nil", tostring(availablenode), ""))
                        :done()
 
                    -- Chromas
                    if (type(t["chromas"]) ~= "nil") then
                        sdnode
                            :tag('td')
                                :addClass('chroma-icon')
                                :attr('data-sort-value', 1)
                                :attr('data-champion', championname)
                                :attr('data-skin', skinname)
                                :wikitext(tostring(availablenode))
                            :done()
                    else
                        sdnode
                            :tag('td')
                                :attr('data-sort-value', 0)
                            :done()
                    end
 
                    -- Add skin row to the table
                    sdtable
                        :node(sdnode)
                        :newline()
                end
            end
        end
        -- TABLE END
 
    sdtable:allDone()
    return tostring(sdtable)
end
 
function p.newestSkins(frame)
    local args; if frame.args == nil then args = lib.arguments(frame) else args = lib.arguments(frame.args) end
 
    local championtable = {}
    local releasetable = {}
 
    for x in pairs(skinData) do
        table.insert(championtable, x)
    end
    table.sort(championtable)
 
    for _, championname in pairs(championtable) do
        local skintable  = {}
 
        for championname in pairs(skinData[championname]["skins"]) do
            table.insert(skintable, championname)
        end
 
        for _, skinname in pairs(skintable) do
            table.insert(releasetable, {championname, skinname, skinData[championname]["skins"][skinname].release})
        end
    end
 
    function comp(a, b)
        if a[3] == "Upcoming" or b[3] == "Upcoming" then return false end
        if a[3] > b[3] then
            return true
        end
    end
 
    table.sort(releasetable, function(a, b) return a[2] > b[2] end)
    table.sort(releasetable, comp)
 
    local lang = mw.language.new("en")
    local count = tonumber(args[1]) or 7
    local s = ""
 
    for i in pairs(releasetable) do
        local champ      = releasetable[i][1]
        local skin       = releasetable[i][2]
        local formatname = releasetable[i][4]
        local cost       = p.getCost({champ, skin})
        local release    = releasetable[i][3]
 
        if release > lang:formatDate("Y-m-d") then
            -- skip if releasedate is in the future
        else
            if skin == "Original" then
                -- skip
            else 
                if count >= 1 then
                    count = count - 1
                    s = s .. "{{Skin portrait|" .. champ .. "|" .. skin
                    if     cost == 10  then s = s .. "|gems="
                    elseif cost == 100 then s = s .. "|prestige="
                    else                    s = s .. "|rp="
                    end
                    s = s .. cost .. "|date=" .. lang:formatDate("d-M-Y", release) .. "}}"
                end
            end
        end
    end
 
    return frame:preprocess(s)
end
 
function p.skinCatalog(frame)
    local dlib          = require("Dev:Datecalc")
    local lang          = mw.language.new( "en" )
    local championtable = {}
    local sdtable       = mw.html.create('table')
 
    sdtable
        :addClass('sortable article-table novpadding hcpadding sticky-header')
        :css('width','100%')
        :css('text-align','center')
        :css('font-size','12px')
        :newline()
 
        -- TABLE HEADER
        :tag('tr')
            :tag('th')
                :css('font-size','12px')
                :css('width','140x')
                :wikitext('Champion')
            :done()
            :tag('th')
                :css('font-size','12px')
                :attr('data-sort-type', "number")
                :wikitext('<div title="Available in-store or through Hextech Crafting.">Available</div>')
            :done()
            :tag('th')
                :css('font-size','12px')
                :attr('data-sort-type', "number")
                :wikitext('<div title="Available through Hextech Crafting or limited sales.">Legacy Vault</div>')
            :done()
            :tag('th')
                :css('font-size','12px')
                :attr('data-sort-type', "number")
                :wikitext('<div title="Only periodically available or acquired through special means.">Rare</div>')
            :done()
            :tag('th')
                :css('font-size','12px')
                :attr('data-sort-type', "number")
                :wikitext('Unavailable')
            :done()
            :tag('th')
                :css('font-size','12px')
                :attr('data-sort-type', "number")
                :wikitext('Total')
            :done()
            :tag('th')
                :css('font-size','12px')
                :attr('data-sort-type', "isoDate")
                :wikitext('Last Skin')
            :done()
            :tag('th')
                :css('font-size','12px')
                :attr('data-sort-type', "number")
                :wikitext('Days Ago')
            :done()
        :done()
        :newline()
 
 
    -- TABLE ENTRIES
    for x in pairs(skinData) do
        table.insert(championtable, x)
    end
    table.sort(championtable)
 
    local availablecounttotal = 0
    local legacycounttotal    = 0
    local rarecounttotal      = 0
    local limitedcounttotal   = 0
    for _, championname in pairs(championtable) do
        local t                = skinData[championname]["skins"]
        local availablecount   = 0
	    local availablecircles = ""
        local legacycount      = 0
	    local legacycircles    = ""
        local rarecount        = 0
	    local rarecircles      = ""
        local limitedcount     = 0
	    local limitedcircles   = ""
        local result           = {"","",""}
        local sdnode           = mw.html.create('tr')
        local border           = ""
        local skintable        = {}
 
        for skinname in pairs(t) do
            if (
                championname        == "Akali"      and skinname == "Crimson" 
                or 
                championname        == "Amumu"      and skinname == "Sewn Chaos"
                or
                championname        == "Blitzcrank" and skinname == "Sewn Chaos"
                or
                championname        == "Ryze"       and skinname == "Human"
                or
                skinname            == "Original"
                or
                t[skinname].release == "N/A"
            ) then
                -- skip
            else
                table.insert(skintable, skinname)
            end
        end
        table.sort(skintable, function(a, b) return t[a].release < t[b].release end)
 
        for i, skinname in pairs(skintable) do
            if t[skintable[i]].release == t[skintable[#skintable]].release then
                border = "border-radius:13px; width:26px; height:26px; box-shadow: 0 0 2px 2px #70fff2, 0 0 4px #111;"
            end
 
            if t[skinname].availability == "Available" then
                availablecount      = availablecount      + 1
                availablecounttotal = availablecounttotal + 1
                availablecircles = availablecircles .. '<li class="skin-icon" data-champion="' .. championname ..'" data-skin="' .. skinname .. '" style="'.. border ..'">[[File:' .. FN.championcircle({championname, skinname}) .. '|26px|link=]]'
            end
            if t[skinname].availability == "Legacy" then
                legacycount         = legacycount         + 1
                legacycounttotal    = legacycounttotal    + 1
                legacycircles = legacycircles .. '<li class="skin-icon" data-champion="' .. championname ..'" data-skin="' .. skinname .. '" style="'.. border ..'">[[File:' .. FN.championcircle({championname, skinname}) .. '|26px|link=]]'
            end
            if t[skinname].availability == "Rare" then
                rarecount           = rarecount           + 1
                rarecounttotal      = rarecounttotal      + 1
                rarecircles = rarecircles .. '<li class="skin-icon" data-champion="' .. championname ..'" data-skin="' .. skinname .. '" style="'.. border ..'">[[File:' .. FN.championcircle({championname, skinname}) .. '|26px|link=]]'
            end
            if t[skinname].availability == "Limited" then
                limitedcount        = limitedcount        + 1
                limitedcounttotal   = limitedcounttotal   + 1
                limitedcircles = limitedcircles .. '<li class="skin-icon" data-champion="' .. championname ..'" data-skin="' .. skinname .. '" style="'.. border ..'">[[File:' .. FN.championcircle({championname, skinname}) .. '|26px|link=]]'
            end
 
            if t[skinname].release > result[2] then
                    result[1] = skinname
                    result[2] = t[skinname].release
                    result[3] = t[skinname].formatname
            end
        end
 
        sdnode
            :tag('td')
                :addClass('skin-icon')
                :attr('data-sort-value', championname)
                :attr('data-champion', championname)
                :attr('data-skin', "Original")
                :css('text-align', 'left')
                :wikitext('[[File:' .. FN.championcircle({championname, "Original"}) .. '|26px|link=' .. championname .. ']] ' .. championname)
            :done()
 
        -- Available skins
        sdnode
            :tag('td')
                :addClass('icon_list')
                :attr('data-sort-value', availablecount)
                :css('text-align', 'left')
                :css('background-color', '#0a1827')
                :wikitext(availablecircles)
            :done()
 
        -- Legacy skins
        sdnode
            :tag('td')
                :addClass('icon_list')
                :attr('data-sort-value', legacycount)
                :css('text-align', 'left')
                :wikitext(legacycircles)
            :done()
 
        -- Rare skins
        sdnode
            :tag('td')
                :addClass('icon_list')
                :attr('data-sort-value', rarecount)
                :css('text-align', 'left')
                :css('background-color', '#0a1827')
                :wikitext(rarecircles)
            :done()
 
        -- Limited skins
        sdnode
            :tag('td')
                :addClass('icon_list')
                :attr('data-sort-value', limitedcount)
                :css('text-align', 'left')
                :wikitext(limitedcircles)
            :done()   
 
        -- Total
 
        sdnode
            :tag('td')
                :css('text-align','right')
                :wikitext(availablecount + legacycount + rarecount + limitedcount)
            :done()
 
        -- Last Skin
        local y, m, d = result[2]:match("(%d+)-(%d+)-(%d+)")
        if y == nil or m == nil or d == nil then
            sdnode
                :tag('td')
                    :addClass('skin-icon')
                    :css('white-space', 'nowrap')
                    :attr('data-sort-value', result[2])
                    :attr('data-champion', championname)
                    :attr('data-skin', result[1])
                    :wikitext(result[2])
                :done()
                :tag('td')
                    :css('text-align','right')
                    :wikitext(result[2])
                :done()
        else
            sdnode
                :tag('td')
                    :addClass('skin-icon')
                    :css('white-space', 'nowrap')
                    :attr('data-sort-value', result[2])
                    :attr('data-champion', championname)
                    :attr('data-skin', result[1])
                    :wikitext(lang:formatDate('d-M-Y', result[2]))
                :done()
                :tag('td')
                    :css('text-align','right')
                    :wikitext(dlib.main{"diff", lang:formatDate('Y-m-d'), result[2]})
                :done()
        end
 
        -- Add skin row to the table
        sdtable
            :newline()
            :node(sdnode)
 
    end
 
 
    --TABLE FOOTER
    local sdfooter = mw.html.create('tr')
    sdfooter
        :tag('th')
            :css('font-size','12px')
            :wikitext('Total')
        :done()
        :tag('th')
            :css('font-size','12px')
            :wikitext(availablecounttotal)
        :done()
        :tag('th')
            :css('font-size','12px')
            :wikitext(legacycounttotal)
        :done()
        :tag('th')
            :css('font-size','12px')
            :wikitext(rarecounttotal)
        :done()
        :tag('th')
            :css('font-size','12px')
            :wikitext(limitedcounttotal)
        :done()
        :newline()
        :tag('th')
            :css('font-size','12px')
            :wikitext(availablecounttotal + legacycounttotal + rarecounttotal + limitedcounttotal)
        :done()
        :tag('th')
        :done()
        :tag('th')
        :done()
 
    sdtable
        :node(sdfooter)
        :newline()
    --TABLE END
 
 
    return sdtable
end
 
function p.skintooltip(frame)
    local args; if frame.args == nil then args = lib.arguments(frame) else args = lib.arguments(frame.args) end
 
    local championname = args["champion"]
    local skinname     = args["skin"] or "Original"
    local variant      = args["variant"]
 
    local filename     = FN.skin{championname, skinname, variant}
    local t            = skinData[championname]["skins"][skinname]
    local newrecall    = t["newrecall"]
    local formatname   = t["formatname"]
    local cost         = t["cost"]
    local distribution = t["distribution"]
    local voiceactor   = p.getVoiceactor{championname, skinname}
    local splashartist = p.getSplashartist{championname, skinname}
    local set          = p.getSet{championname, skinname}
    local lore         = t["lore"]
    local filter       = t["filter"]
    local newquotes    = t["newquotes"]
    local newvoice     = t["newvoice"]
    local neweffects   = t["neweffects"]
    --local newrecall    = t["newrecall"]
    local newanimations= t["newanimations"]
    local transforming = t["transforming"]
    local extras       = t["extras"]
    local chromas      = t["chromas"]
    local looteligible = t["looteligible"]
    local variantof    = p.getVariant{championname, skinname}
 
    local rpskins      = {[260]=true, [585]=true, [790]=true, [880]=true, [390]=true, [460]=true, [500]=true, [520]=true, [750]=true, [975]=true, [1350]=true, [1820]=true, [2775]=true, [3250]=true, [5000]=true}
    local s            = ''
 
 
    s = s .. '<div style="position:relative;">[[File:' .. filename .. '|700px]]'
        s = s .. '<div class="skin-features" style="padding:15px 45px 15px 15px; position:absolute; bottom:16px; left:16px; background-color:RGBA(10, 24, 39, 0.75); max-width:576px;">'
            s = s .. '<div style="font-family:BeaufortLoL;">'
                s = s .. '<span style="font-size:16px; color:#f1e6d0; width:100%; text-transform:uppercase;">'
                    s = s .. lib.ternary(formatname ~= nil, formatname, skinname .. ' ' .. championname) .. lib.ternary(variant ~= nil, '&nbsp;<small>(' .. tostring(variant) .. ')</small>', '')
                s = s .. '</span>&nbsp;<span style="font-size:14px; color:#c9aa71; width:100%;">'
 
                    if cost == 10 then
                        s = s .. '&#x2011;&nbsp;' .. tostring(IL.basic{link = 'Gemstone', text = cost, image = 'Rare Gem.png', alttext = cost .. ' Rare Gems}', border = "false", labellink = "false"})
                    elseif cost == 100 then
                        -- Prestige: distribution string
                    elseif rpskins[cost] == true then
                        s = s .. '&#x2011;&nbsp;' .. tostring(IL.basic{link = 'Riot Points', text = cost, image = 'RP icon.png', alttext = cost .. ' RP', border = "false", labellink = "false"})
                    elseif cost == 150000 then
                        s = s .. '&#x2011;&nbsp;' .. tostring(IL.basic{link = 'Blue Essence', text = cost, image = 'BE icon.png', alttext = cost .. ' BE', border = "false", labellink = "false"})
                    else
                        -- default: do nothing
                    end
 
                s = s .. lib.ternary(distribution ~= nil, ' ' .. tostring(distribution), '') .. '</span>'
            s = s .. '</div>'
            s = s .. '<div style="font-size:11px">'
                s = s .. lib.ternary(voiceactor ~= nil, '<div style="display:inline-block; padding-right:1em;">[[File:Actor.png|20px|link=]]' .. tostring(voiceactor) .. '</div>', '')
                s = s .. lib.ternary(splashartist ~= nil, '<div style="display:inline-block; padding-right:1em;">[[File:Artist.png|20px|link=]]' .. tostring(splashartist) .. '</div>', '')
                s = s .. lib.ternary(set ~= nil, '<div style="display:inline-block; padding-right:1em;">[[File:Set piece.png|20px|link=]]' .. tostring(set) .. '</div>', '')
                s = s .. lib.ternary(looteligible ~= false, '<div style="display:inline-block; padding-right:1em;">[[File:Loot eligible.png|20px|link=]] Loot eligible</div>', '')
                s = s .. lib.ternary(looteligible == false, '<div style="display:inline-block; padding-right:1em;">[[File:Loot ineligible.png|20px|link=]] Loot ineligible </div>', '')
                s = s .. lib.ternary(lore ~= nil, '<div style="line-height: 1.7em; text-align: justify">' .. tostring(lore) .. '</div>', '')
 
            if availability or filter or newquotes or newvoice or neweffects or newanimations or transforming or extras or chromas or looteligible then
                s = s .. '<div>'
 
                if availability == 'Limited' then
                    s = s .. '<div style="display:inline-grid; padding:0 1em; text-align:center;"><div>[[File:Limited skin.png|50px|link=]]</div><div>Limited Edition</div></div>'
                elseif availability == 'Legacy' then
                    s = s .. '<div style="display:inline-grid; padding:0 1em; text-align:center;"><div>[[File:Legacy skin.png|50px|link=]]</div><div>Legacy Vault</div></div>'
                end
 
                s = s .. lib.ternary(filter ~= nil, '<div style="display:inline-grid; padding:0 1em; text-align:center;"><div>[[File:Voice filter.png|50px|link=]]</div><div>Voice Filter</div></div>', '')
                s = s .. lib.ternary(newquotes ~= nil, '<div style="display:inline-grid; padding:0 1em; text-align:center;"><div>[[File:Additional quotes.png|50px|link=]]</div><div>Additional Quotes</div></div>', '')
                s = s .. lib.ternary(newvoice ~= nil, '<div style="display:inline-grid; padding:0 1em; text-align:center;"><div>[[File:New voice.png|50px|link=]]</div><div>New Voice</div></div>', '')
                s = s .. lib.ternary(neweffects ~= nil, '<div style="display:inline-grid; padding:0 1em; text-align:center;"><div>[[File:New effects.png|50px|link=]]</div><div>New SFX/VFX</div></div>', '')
                s = s .. lib.ternary(newanimations ~= nil, '<div style="display:inline-grid; padding:0 1em; text-align:center;"><div>[[File:New animations.png|50px|link=]]</div><div>New Animations</div></div>', '')
                s = s .. lib.ternary(transforming ~= nil, '<div style="display:inline-grid; padding:0 1em; text-align:center;"><div>[[File:Transforming.png|50px|link=]]</div><div>Transforming</div></div>', '')
                s = s .. lib.ternary(extras ~= nil, '<div style="display:inline-grid; padding:0 1em; text-align:center;"><div>[[File:Includes extras.png|50px|link=]]</div><div>Includes Extras</div></div>', '')
                s = s .. lib.ternary(chromas ~= nil, '<div style="display:inline-grid; padding:0 1em; text-align:center;"><div>[[File:Chromas available.png|50px|link=]]</div><div>Chromas</div></div>', '')
                s = s .. '</div>'
            end
            s = s .. lib.ternary(variantof ~= nil, '<div>This skin is a variant of ' .. tostring(IL.skin{champion = championname, skin = variantof, circle = "true", link = '*none*'}) .. '.</div>', '')
        s = s .. '</div>'
    s = s .. '</div>'
 
    return s
end
 
 
function p.chromaList(frame)
    local lang = mw.language.new( "en" )
    local sdtable = mw.html.create('table')
 
    sdtable
        :addClass('sortable article-table nopadding sticky-header')
        :css('text-align','center')
        :newline()
 
        --TABLE HEADER
        :tag('tr')
            :tag('th')
                :css('width','26px')
            :done()
            :tag('th')
                :wikitext('Skin')
            :done()
            :tag('th')
                :tag('span')
                    :attr('title','Availability')
                    :wikitext('[[File:Availability.png|40px|link=|]]')
                :done()
            :done()
            :tag('th')
                :tag('span')
                    :attr('title','Cost')
                    :wikitext('[[File:RP icon.png|30px|link=|]]')
                :done()
            :done()
            :tag('th')
                :css('width','40px')
                :tag('div')
                    :attr('title','Store Chromas')
                    :css('width','30px')
                    :css('height','30px')
                    :css('padding','5px')
                    :wikitext('[[File:Chromaskins.png|30px|link=|]]')
                :done()
            :done()
            :tag('th')
                :css('width','40px')
                :tag('div')
                    :attr('title','Bundle Chromas')
                    :css('width','30px')
                    :css('height','30px')
                    :css('padding','5px')
                    :wikitext('[[File:Legendary_Skin.png|30px|link=|]]')
                :done()
            :done()
            :tag('th')
                :css('width','40px')
                :tag('div')
                    :attr('title','Partner Chromas')
                    :css('width','30px')
                    :css('height','30px')
                    :css('padding','5px')
                    :wikitext('[[File:China Skin Tier 2.png|30px|link=|]]')
                :done()
            :done()
            :tag('th')
                :css('width','40px')
                :tag('div')
                    :attr('title','Loot Chromas')
                    :css('width','30px')
                    :css('height','30px')
                    :css('padding','5px')
                    :wikitext('[[File:China Skin Tier 1.png|30px|link=|]]')
                :done()
            :done()
            :tag('th')
                :css('width','40px')
                :tag('div')
                    :attr('title','Distributed Chromas')
                    :css('width','30px')
                    :css('height','30px')
                    :css('padding','5px')
                    :wikitext('[[File:Limited skin.png|30px|link=|]]')
                :done()
            :done()
        :done()
        :newline()
 
 
        -- TABLE ENTRIES
        local championtable = {}
        for x in pairs(skinData) do
            table.insert(championtable, x)
        end
        table.sort(championtable)
 
        local availablenode = mw.html.create('span')
        availablenode
            :css('color', 'green')
            :css('font-size', 'x-large')
            :css('vertical-align', 'text-top')
            :wikitext("✔")
 
        local legacynode = mw.html.create('span')
        legacynode
            :css('color', 'yellow')
            :css('font-size', 'x-large')
            :css('font-weight', '600')
            :css('vertical-align', 'text-top')
            :wikitext("‒")
 
        local limitednode = mw.html.create('span')
        limitednode
            :css('color', 'red')
            :css('font-size', 'x-large')
            :css('vertical-align', 'text-top')
            :wikitext("✘")
        local rarenode = mw.html.create('span')
        rarenode
            :css('color', 'orange')
            :css('font-size', 'x-large')
            :css('vertical-align', 'text-top')
            :wikitext("⭐")
 
        for _, championname in pairs(championtable) do
            local skintable  = {}
            for championname in pairs(skinData[championname]["skins"]) do
                table.insert(skintable, championname)
            end
            table.sort(skintable)
 
            for _, skinname in pairs(skintable) do
                if skinData[championname]["skins"][skinname]["chromas"] == nil then
                    -- skip
                else
                    local t = skinData[championname]["skins"][skinname]
                    local sdnode = mw.html.create('tr')
                    local temp = ""
 
                    -- Skincircle
                    if (skinname == "Original") then
                        temp = "!" .. t["release"]
                    else 
                        temp = t["release"]
                    end
 
                    sdnode
                        :tag('td')
                            :addClass('champion-icon')
                            :attr('data-sort-value', championname .. temp)
                            :attr('data-champion', championname)
                            :attr('data-skin', skinname)
                            :wikitext('[[File:' .. FN.championcircle({championname, skinname}) .. '|20px|link=' .. championname .. ']]')
                        :done()
 
                    -- Skinname
                    sdnode
                        :tag('td')
                            :addClass('skin-icon')
                            :attr('data-champion', championname)
                            :attr('data-skin', skinname)
                            :css('text-align', 'left')
                            :wikitext('[[' .. championname .. '|' .. lib.ternary(t["formatname"] ~= nil, t["formatname"], skinname .. " " .. championname) .. ']]')
                        :done()
 
                    -- Availability
                    astring = '<span style="color: cornflowerblue;font-size: large;font-weight: 600;">⭘</span>'
                    if (t["availability"] == "Available") then
                        astring = tostring(availablenode)
                    end
                    if (t["availability"] == "Legacy") then
                        astring = tostring(legacynode)
                    end
                    if (t["availability"] == "Limited") then
                        astring = tostring(limitednode)
                    end
                    if (t["availability"] == "Rare") then
                        astring = tostring(rarenode)
                    end
                    sdnode
                        :tag('td')
                            :tag('span')
                                :attr('title', t["availability"] or 'Upcoming')
                                :wikitext(astring)
                        :done()
 
                    -- Cost
                    local image = ""
                    if (tostring(t["cost"]) == "150000") then
                        image = "[[File:BE icon.png|20px|link=]]"
                    end 
                    if (tostring(t["cost"]) == "100") then
                        image = "[[File:Hextech Crafting Prestige token.png|20px|link=]]"
                    end
                    if (tostring(t["cost"]) == "10") then
                        image = "[[File:Rare Gem.png|20px|link=]]"
                    end
                    sdnode
                        :tag('td')
                            :attr('data-sort-value', lib.ternary(tostring(t["cost"]) == "10" or tostring(t["cost"]) == "100", "2450", t["cost"]))
                            :tag('span')
                                :css('color', color.skin({t["cost"] .. ""}))
                                :wikitext(image .. t["cost"])
                            :done()
                        :done()
 
                    -- Chromas
                    local availtable   = {}
                    local bundletable  = {}
                    local partnertable = {}
                    local loottable    = {}
                    local rewardtable  = {}
                    local futuretable  = {}
 
                    for chromaname in pairs(t["chromas"]) do
                        if     t["chromas"][chromaname]["source"] == "Bundle" then
                            table.insert(bundletable, chromaname)
                        elseif t["chromas"][chromaname]["source"] == "Partner" then
                            table.insert(partnertable, chromaname)
                        elseif t["chromas"][chromaname]["source"] == "Loot" then
                            table.insert(loottable, chromaname)
                        elseif t["chromas"][chromaname]["source"] == "Reward" then
                            table.insert(rewardtable, chromaname)
                        elseif t["chromas"][chromaname]["source"] == "Upcoming" then
                            table.insert(futuretable, chromaname)
                        else
                            table.insert(availtable, chromaname)
                        end
                    end
 
                    --normal
                    if (#availtable ~= 0) then
                        local s = table.concat(availtable, ",")
                        sdnode
                            :tag('td')
                                :addClass('chroma-icon')
                                :attr('data-sort-value', 1)
                                :attr('data-champion', championname)
                                :attr('data-skin', skinname)
                                :attr('data-chromas', s)
                                :wikitext(tostring(availablenode))
                            :done()
                    else
                        sdnode
                            :tag('td')
                                :attr('data-sort-value', 0)
                            :done()
                    end
 
                    --bundle
                    if (#bundletable ~= 0) then
                        local s = table.concat(bundletable, ",")
                        sdnode
                            :tag('td')
                                :addClass('chroma-icon')
                                :attr('data-sort-value', 1)
                                :attr('data-champion', championname)
                                :attr('data-skin', skinname)
                                :attr('data-chromas', s)
                                :wikitext(tostring(availablenode))
                            :done()
                    else
                        sdnode
                            :tag('td')
                                :attr('data-sort-value', 0)
                            :done()
                    end
 
                    --partner
                    if (#partnertable ~= 0) then
                        local s = table.concat(partnertable, ",")
                        sdnode
                            :tag('td')
                                :addClass('chroma-icon')
                                :attr('data-sort-value', 1)
                                :attr('data-champion', championname)
                                :attr('data-skin', skinname)
                                :attr('data-chromas', s)
                                :wikitext(tostring(availablenode))
                            :done()
                    else
                        sdnode
                            :tag('td')
                                :attr('data-sort-value', 0)
                            :done()
                    end
 
                    --loot
                    if (#loottable ~= 0) then
                        local s = table.concat(loottable, ",")
                        sdnode
                            :tag('td')
                                :addClass('chroma-icon')
                                :attr('data-sort-value', 1)
                                :attr('data-champion', championname)
                                :attr('data-skin', skinname)
                                :attr('data-chromas', s)
                                :wikitext(tostring(availablenode))
                            :done()
                    else
                        sdnode
                            :tag('td')
                                :attr('data-sort-value', 0)
                            :done()
                    end
 
                    --distributed
                    if (#rewardtable ~= 0) then
                        local s = table.concat(rewardtable, ",")
                        sdnode
                            :tag('td')
                                :addClass('chroma-icon')
                                :attr('data-sort-value', 1)
                                :attr('data-champion', championname)
                                :attr('data-skin', skinname)
                                :attr('data-chromas', s)
                                :wikitext(tostring(availablenode))
                            :done()
                    else
                        sdnode
                            :tag('td')
                                :attr('data-sort-value', 0)
                            :done()
                    end
 
                    -- Add skin row to the table
                    sdtable
                        :node(sdnode)
                        :newline()
                end
            end
        end
        -- TABLE END
 
    sdtable:allDone()
    return tostring(sdtable)
end
 
return p
 
-- </pre>
-- [[Category:Lua]]
Community content is available under CC-BY-SA unless otherwise noted.