League of Legends Wiki

Want to contribute to this wiki?
Sign up for an account, and get started!
You can even turn off ads in your preferences.

Come join the LoL Wiki community Discord server!

READ MORE

League of Legends Wiki
Advertisement

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

-- <pre><nowiki>
local p    = {}

 
local lib       = require('Module:Feature')
-- local json      = require('Module:JSON')
-- local userError = require('Dev:User error')
local FN        = require('Module:Filename')

--[==[
    https://phabricator.wikimedia.org/source/mediawiki/browse/master/includes/parser/Parser.php
    https://www.mediawiki.org/wiki/Manual:Parser.php
    
    The actual parser does more complicated things in handleInternalLinks
    it uses the base pattern:
        legal chars = " %!\"$&'()*,\\-.\\/0-9:;=?@A-Z\\\\^_`a-z~\\x80-\\xFF+"
        $tc = Title::legalChars() . '#%';
        # Match a link having the form [[namespace:link|alternate]]trail
		$e1 = "/^([{$tc}]+)(?:\\|(.+?))?]](.*)\$/sD";
		# Match cases where there is no "]]", which might still be images
		$e1_img = "/^([{$tc}]+)\\|(.*)\$/sD";
]==]

function p.striplinks(frame)
    local args; if frame.args == nil then args = lib.arguments(frame) else args = lib.arguments(frame.args) end
    return striplinks(args[1])
end

function striplinks(instring)
    local ret = instring
    local subs = 0
    ret, subn = string.gsub(ret, '%[%[([-,:%w]+)|(%w+)%]%]', '%2')
    subs = subs + subn
    ret, subn = string.gsub(ret, '%[%[%s+(:?%w+:%w+)%]%]', ' %1')
    subs = subs + subn
    ret, subn = string.gsub(ret, '%[%[:%s+(%w+:%w+)%]%]', ' %1')
    subs = subs + subn
    ret, subn = string.gsub(ret, '%[%[:?(%s*%w+:%w+)%]%]', '%1')
    subs = subs + subn
    ret, subn = string.gsub(ret, '%[%[:([%s%w]+)%]%]', '%1')
    subs = subs + subn
    ret, subn = string.gsub(ret, '%[%[(%s+:%s+%w+:%w+)%]%]', '%1')
    subs = subs + subn
    ret, subn = string.gsub(ret, '%[%[(%s+:[%s%w]*)%]%]', '%1')
    subs = subs + subn
    ret, subn = string.gsub(ret, '%[%[([-,%w]+)%]%]', '%1')
    subs = subs + subn
    -- ret = string.gsub
    return ret
end

function p.striplinkstest(frame)
    local args; if frame.args == nil then args = lib.arguments(frame) else args = lib.arguments(frame.args) end
    local instring = args[1]
    local ret = instring
    local subs = 0
    ret, subn = string.gsub(ret, '%[%[([:%w]-)|(%w+)%]%]', '%2')
    subs = subs + subn
    ret, subn = string.gsub(ret, '%[%[%s+(:?%w+:%w+)%]%]', ' %1')
    subs = subs + subn
    ret, subn = string.gsub(ret, '%[%[:%s+(%w+:%w+)%]%]', ' %1')
    subs = subs + subn
    ret, subn = string.gsub(ret, '%[%[:?(%s*%w+:%w+)%]%]', '%1')
    subs = subs + subn
    ret, subn = string.gsub(ret, '%[%[:([%s%w]+)%]%]', '%1')
    subs = subs + subn
    ret, subn = string.gsub(ret, '%[%[(%s+:%s+%w+:%w+)%]%]', '%1')
    subs = subs + subn
    ret, subn = string.gsub(ret, '%[%[(%s+:[%s%w]*)%]%]', '%1')
    subs = subs + subn
    ret, subn = string.gsub(ret, '%[%[(%w+)%]%]', '%1')
    subs = subs + subn
    -- ret = string.gsub
    return subs
end

--[=[
    does not strip URI links
    FSM States:
    s=0 Seen 
    s=1 Seen [
    s=2 Seen [\[
    s=3 Seen [\[ +
    s=4 Seen [\[ +:
    s=5 
    s=6 
    s=7 Seen [\[ *:?.*
    s=8 Seen [\[ *:?.*]
    s=9 Seen [\[ *:?.*|
    s=10 Seen [\[ *:?.*|.*
    s=11 Seen [\[ *:?.*|.*]
    s=11 Seen [\[ *:?.*|]
    s=13 Seen [\[: *
    s=. Seen [ +
    s=. Seen [ *.*
    s=. Seen [ *.*
    s=. Seen [ *.* *
    s=. Seen [ *.* *.*
]=]
function p.striplinks2(frame)
    local args; if frame.args == nil then args = lib.arguments(frame) else args = lib.arguments(frame.args) end
    local instring = args[1]
    local ret = ''
    local fstart, fend, bstart, bend
    local fcolon, fpipe = -1, -1
    local s = 0
    -- local strace = '0'
    for i = 1, #instring do
--        local nextchar = string.gmatch(args[1],'.')
        local nextchar = mw.ustring.sub(instring, i, i)
        if s == 0 then
            if nextchar == '[' then
                s = 1
                fstart = i
            else
                s = 0
                ret = ret .. nextchar
            end
        elseif s == 1 then --Seen [
            if nextchar == '[' then
                s = 2
                fend = i
            end
        elseif s == 2 then --Seen [[
            if nextchar == ' ' then
                s = 3
                fend = i-1
            elseif nextchar == ':' then
                s = 13
                fcolon = i
            else
                s = 7
            end
        elseif s == 3 then --Seen [[ +
            if nextchar == ' ' then
                s = 3
                fend = i-1
            elseif nextchar == ':' then
                s = 4
                fcolon = i
            else
                s = 7
                fend = i-1
            end
        elseif s == 4 then --Seen [\[ +:
            if nextchar == ']' then
                s = 8
                fcolon = -1
                bstart = i
            else
                s = 7
            end
        elseif s == 5 then
        elseif s == 6 then 
        elseif s == 7 then --Seen [\[ *:?.*
            if nextchar == '|' then
                s = 9
                fpipe = i
            elseif nextchar == ']' then
                s = 8
                bstart = i
            else
                s = 7
            end
        elseif s == 8 then --Seen [\[ *:?.*]
            if nextchar == ']' then
                s = 0
                if fcolon ~= -1 and fcolon ~= fstart + 2 then
                    ret = ret .. mw.ustring.sub(instring, fcolon, bstart - 1)
                    fcolon = -1
                elseif fcolon ~= -1 then
                    ret = ret .. mw.ustring.sub(instring, fcolon, bstart - 1)
                    fcolon = -1
                else
                    ret = ret .. mw.ustring.sub(instring, fend + 1, bstart - 1)
                    fcolon = -1
                end
            else
                s = 7
            end
        elseif s == 9 then --Seen [\[ *:?.*|
            if nextchar == ']' then
                s = 12
                bstart = i
            else
                s = 10
            end
        elseif s == 10 then --Seen [\[ *:?.*|.*
            if nextchar == ']' then
                s = 11
                bstart = i
            else
                s = 10
            end
        elseif s == 11 then --Seen [\[ *:?.*|.*]
            if nextchar == ']' then
                s = 0
                ret = ret .. mw.ustring.sub(instring, fpipe + 1, bstart - 1)
                fcolon = -1
            else
            end
        elseif s == 12 then --Seen [\[ *:?.*|]
            if nextchar == ']' then
                s = 0
                ret = ret .. mw.ustring.sub(instring, fend + 1, fpipe - 1)
                fcolon = -1
            else
                s = 10
            end
        elseif s == 13 then --Seen [\[: *
            if nextchar == ' ' then
                s = 13
            elseif nextchar == '|' then
                s = 0
                ret = ret .. mw.ustring.sub(instring, fstart, i)
            elseif nextchar == ']' then
                s = 0
                ret = ret .. mw.ustring.sub(instring, fstart, i)
            else
                s = 7
                fcolon = i-1
            end
        elseif s == 14 then 
        elseif s == 15 then 
        elseif s == 16 then
        else
            return instring
        end
        -- strace = strace .. ' ' .. s
    end
    if s ~= 0 then ret = ret .. mw.ustring.sub(instring, fstart, -1) end
    -- if s ~= 0 then return 'error, state trace: ' .. strace .. '; ' .. ret end
    if args[2] ~= nil then ret = '<nowiki>' .. ret .. '</nowiki>' end
    -- if args[2] ~= nil then return ret .. ': ' .. strace end
    return ret
end

-- function p.rutngt(frame)
--     local args; if frame.args == nil then args = lib.arguments(frame) else 
--         if frame:getParent().args ~= nil then args = lib.arguments(lib.mergeFrames(frame, frame:getParent())) else args = lib.arguments(frame.args) end
--     end
--     local orig = tonumber(args[1])
--     local gt = 0.033
--     return math.ceil(orig/gt)*gt
-- end

function p.rutngt(frame)
    local args; if frame.args == nil then args = lib.arguments(frame) else 
        if frame:getParent().args ~= nil then args = lib.arguments(lib.mergeFrames(frame, frame:getParent())) else args = lib.arguments(frame.args) end
    end
    local orig = tonumber(args[1])
    local gt = 0.033
    local FD = require('Module:Fd')
    return FD.get{args = {[1] = tostring(math.ceil(orig/gt)*gt)}}
end

function p.tabber(frame)
    local args; if frame.args == nil then args = lib.arguments(frame) else args = lib.arguments(frame.args) end
    local s = '{{#tag:tabber|' .. args['label1'] .. '=' .. args['content1'] .. '\n{{!}}-{{!}}' .. args['label2'] .. '=' .. args['content2'] .. '}}'
    return frame:preprocess(s)
end

function p.getchampfromability(frame)
    local args; if frame.args == nil then args = lib.arguments(frame) else args = lib.arguments(frame.args) end
    local lolData   = mw.loadData('Module:ChampionData/data')
    local get       = require('Module:ChampionData/getter')
    local ability   = args[1]
    local skills    = {
        [1] = 'skill_i',
        [2] = 'skill_q',
        [3] = 'skill_w',
        [4] = 'skill_e',
        [5] = 'skill_r',
    }
    for champname in pairs(lolData) do
        for _, skill in pairs(skills) do
            local tskills = get[skill](champname)
            for _, abi in pairs(tskills) do
                if ability == abi then
                    return '#REDIRECT [[' .. champname .. '#' .. ability .. ']]'
                end
            end
        end
    end
end

function p.getchampfromability2(frame)
    local args; if frame.args == nil then args = lib.arguments(frame) else args = lib.arguments(frame.args) end
    local lolData   = mw.loadData('Module:ChampionData/data')
    local get       = require('Module:ChampionData/getter')
    local ability   = args[1]
    local skills    = {
        [1] = 'skill_i',
        [2] = 'skill_q',
        [3] = 'skill_w',
        [4] = 'skill_e',
        [5] = 'skill_r',
    }
    for champname in pairs(lolData) do
        for _, skill in pairs(skills) do
            local tskills = get[skill](champname)
            for _, abi in pairs(tskills) do
                if ability == abi then
                    return champname
                end
            end
        end
    end
end

function p.abilitiesList(frame)
    local args; if frame.args == nil then args = lib.arguments(frame) else 
        if frame:getParent().args ~= nil then args = lib.arguments(lib.mergeFrames(frame, frame:getParent())) else args = lib.arguments(frame.args) end
    end
    local lolData   = mw.loadData('Module:ChampionData/data')
    local IL        = require('Module:ImageLink')
 
    local param = args[1] or args['parameter'] or nil
    local value = args[2] or args['value'] or ''
    local compare = args['compare'] or nil
    local ignorecase = args['noignorecase'] ~= 'true'
    if value and ignorecase then value = string.lower(value) end
    local outformat = args['format'] or args['output'] or nil
    local paramDisplayName = args['label'] or nil
    local edit = args['edit'] == 'true'
    
    local ret = ""
    if outformat == 'bullet' or outformat == 'combinedbullet' then
    	ret = mw.html.create('ul')
    elseif outformat == 'table' then
        ret = mw.html.create('table')
        ret
            :addClass('sortable wikitable sticky-header')
            :css('width', '100%')
            --:css('text-align', 'right')
            :css('white-space', 'nowrap')
            :newline()
        local temp = ret
            :tag('tr')
                :tag('th')
                    :wikitext('Champion')
                    --:css('width', '180px')
                    --:attr('class', 'unsortable')
                :done()
                :tag('th')
                    :wikitext('Ability')
                    --:attr('class', 'unsortable')
                :done()
        if param then
        	temp
	    		:tag('th')
	                :wikitext(paramDisplayName or param)
	            :done()
        end
        temp
            :done()
            :newline()
    end
    local sortedChamps = {}
    for champname in pairs(lolData) do
        local release = lolData[champname]["date"]
        if release == "Upcoming"
        or release == "N/A"
        or release == "Cancelled"
        or release == ""
        or champname == "Mega Gnar"
        or champname == "Rhaast"
        or champname == "Shadow Assassin"
        or champname == "Kled & Skaarl"
        then
            --ignore champion
            --skip
        else
            table.insert(sortedChamps, champname)
        end
    end
    table.sort(sortedChamps)
    for _, champname in ipairs(sortedChamps) do
        local abilities = lolData[champname]["skills"]
        local count = 0
        local matched = {}
        local dataprobes = {}
        if param then
            for _, ability in ipairs(abilities) do
                local probe = mw.text.trim(frame:expandTemplate{title = "Data_" .. champname .. "/" .. ability, args = {"pst2", param}})
                dataprobes[ability] = probe
                if ignorecase then probe = string.lower(probe) end
                if (not compare or compare == "equals" and probe == value)
                or compare == "not equals" and probe ~= value
                or compare == "exists" and probe ~= "{{{" .. param .. "}}}"
                or compare == "exists and not empty" and (probe ~= "{{{" .. param .. "}}}" and probe ~= "")
                or compare == "exists and not equals" and (probe ~= "{{{" .. param .. "}}}" and probe ~= value)
                or compare == "exists and not empty and not equals" and (probe ~= "{{{" .. param .. "}}}" and probe ~= "" and probe ~= value)
                or compare == "exists and not empty and not contains" and (probe ~= "{{{" .. param .. "}}}" and probe ~= "" and not string.find(probe, value))
                or compare == "contains" and string.find(probe, value)
                then
                    count = count + 1
                    table.insert(matched, ability)
                end
            end
        else
            matched = abilities
            for _, ability in ipairs(abilities) do
                count = count + 1
            end
        end
        -- output
        if outformat == 'bullet' then
            for _, ability in ipairs(matched) do
            	local item = ret:tag('li')
                if edit then
                    item
                    	:node(IL.champion{args={['champion'] = champname, ['possessive'] = 'true'}})
                    	:wikitext(" ")
                    	:node(IL.ability{args={['champion'] = champname, ['ability'] = ability, ['edit'] = 'true'}})
                    :done()
                    :newline()
                else
                    item
                    	:node(IL.champion{args={['champion'] = champname, ['possessive'] = 'true'}})
                    	:wikitext(" ")
                    	:node(IL.ability{args={['champion'] = champname, ['ability'] = ability}})
                    :done()
                    :newline()
                end
            end
        elseif outformat == 'combinedbullet' then
            if count > 0 then
            	local item = ret:tag('li')
                item
                	:node(IL.champion{args={['champion'] = champname, ['possessive'] = 'true'}})
                	:wikitext(" ")
                if count == 1 then
                    for i, ability in ipairs(matched) do
                        if edit then
                            item:node(IL.ability{args={['champion'] = champname, ['ability'] = ability, ['edit'] = 'true'}})
                        else
                            item:node(IL.ability{args={['champion'] = champname, ['ability'] = ability}})
                        end
                    end
                elseif count == 2 then
                    for i, ability in ipairs(matched) do
                        if edit then
                            item:node(IL.ability{args={['champion'] = champname, ['ability'] = ability, ['edit'] = 'true'}})
                        else
                            item:node(IL.ability{args={['champion'] = champname, ['ability'] = ability}})
                        end
                        if i == count - 1 then
                            item:wikitext(" and ")
                        end
                    end
                else
                    for i, ability in ipairs(matched) do
                        if edit then
                            item:node(IL.ability{args={['champion'] = champname, ['ability'] = ability, ['edit'] = 'true'}})
                        else
                            item:node(IL.ability{args={['champion'] = champname, ['ability'] = ability}})
                        end
                        if i < count then
                            item:wikitext(", ")
                        end
                        if i == count - 1 then
                            item:wikitext("and ")
                        end
                    end
                end
                ret
                	:newline()
            end
        elseif outformat == 'table' then
            for i, ability in ipairs(matched) do
                local tablerow = mw.html.create('tr')
                if i == 1 then
                    tablerow
                        :tag('td')
                            :node(IL.champion{args={['champion'] = champname}})
                            :attr('data-sort-value', champname)
                            :attr('rowspan', count)
                        :done()
                end
                
                if edit then
                    tablerow
                        :tag('td')
                            :node(IL.ability{args={['champion'] = champname, ['ability'] = ability, ['edit'] = 'true'}})
                            :attr('data-sort-value', i)
                        :done()
                else
                    tablerow
                        :tag('td')
                            :node(IL.ability{args={['champion'] = champname, ['ability'] = ability}})
                            :attr('data-sort-value', i)
                        :done()
                end
                if param then
	                local probe = dataprobes[ability]
	                tablerow
	                    :tag('td')
	                        :wikitext(probe)
	                        :attr('data-sort-value', probe)
	                    :done()
	                :done()
	            end
                ret
                    :node(tablerow)
                    :newline()
            end
        else
            for _, ability in ipairs(matched) do
                ret = ret .. "* " .. champname .. " " .. ability .. "\n"
            end
        end
    end
    return tostring(ret)
end

function p.abilitieslist2(frame)
    local args; if frame.args == nil then args = lib.arguments(frame) else args = lib.arguments(frame.args) end
    local abilityData   = mw.loadData('Module:Sandbox/DutyS12345/data3')
    --local lolData   = mw.loadData('Module:ChampionData/data')
    --local get       = require('Module:ChampionData/getter')
    local IL        = require('Module:ImageLink')
    local param = args[1] or nil
    local value = args[2] or nil
    local compare = args['compare'] or nil
    local outformat = args['format'] or args['output'] or nil
    local ret = ""
    
    local sortedChamps = {}
    for champname in pairs(abilityData) do
        table.insert(sortedChamps, champname)
    end
    table.sort(sortedChamps)
    for _, champname in ipairs(sortedChamps) do
        local abilities = abilityData[champname]
        local count = 0
        local matched = {}
        if param then
            for _, ability in ipairs(abilities) do
                local template = "{{Data_" .. champname .. "/" .. ability .. "|pst2|" .. param .. "}}"
                local probe = mw.text.trim(frame:preprocess(template))
                if (not compare or compare == "equals" and probe == value)
                or compare == "not equals" and probe ~= value
                or compare == "exists" and probe ~= "{{{" .. param .. "}}}"
                or compare == "exists and not empty" and (probe ~= "{{{" .. param .. "}}}" and probe ~= "")
                or compare == "exists and not equals" and (probe ~= "{{{" .. param .. "}}}" and probe ~= value)
                or compare == "exists and not empty and not equals" and (probe ~= "{{{" .. param .. "}}}" and probe ~= "" and probe ~= value)
                or compare == "contains" and string.find(probe, value)
                then
                    count = count + 1
                    table.insert(matched, ability)
                end
                
            end
        else
            matched = abilities
            for _, ability in ipairs(abilities) do
                count = count + 1
            end
        end
        -- output
        if outformat == 'bullet' then
            for _, ability in ipairs(matched) do
                ret = ret .. "* " .. tostring(IL.champion{args={['champion'] = champname, ['possessive'] = 'true'}}) .. " " .. tostring(IL.ability{args={['champion'] = champname, ['ability'] = ability}}) .. "\n" 
            end
        elseif outformat == 'combinedbullet' then
            if count > 0 then
                ret = ret .. "* " .. tostring(IL.champion{args={['champion'] = champname, ['possessive'] = 'true'}}) .. " "
                if count == 1 then
                    for i, ability in ipairs(matched) do
                        ret = ret .. tostring(IL.ability{args={['champion'] = champname, ['ability'] = ability}}) .. "\n"
                    end
                elseif count == 2 then
                    for i, ability in ipairs(matched) do
                        ret = ret .. tostring(IL.ability{args={['champion'] = champname, ['ability'] = ability}}) .. " "
                        if i == count - 1 then
                            ret = ret .. "and "
                        end
                    end
                    ret = ret .. "\n"
                else
                    for i, ability in ipairs(matched) do
                        ret = ret .. tostring(IL.ability{args={['champion'] = champname, ['ability'] = ability}})
                        if i < count then
                            ret = ret .. ", "
                        end
                        if i == count - 1 then
                            ret = ret .. "and "
                        end
                    end
                    ret = ret .. "\n"
                end
            end
        else
            for _, ability in ipairs(matched) do
                ret = ret .. "* " .. champname .. " " .. ability .. "\n"
            end
        end
    end
    return ret
end

function p.queryabilities(frame)
    local args; if frame.args == nil then args = lib.arguments(frame) else args = lib.arguments(frame.args) end
    --local lolData   = mw.loadData('Module:ChampionData/data')
    --local get       = require('Module:ChampionData/getter')
    local template = "{{Data Aatrox/The Darkin Blade|pst2|targeting}}"
    local templatetest = frame:preprocess(template)
    return templatetest
end

function p.querychamps(frame)
    local args; if frame.args == nil then args = lib.arguments(frame) else args = lib.arguments(frame.args) end
    local lolData   = mw.loadData('Module:ChampionData/data')
    local get       = require('Module:ChampionData/getter')
    local stat      = args['stat']
    local value     = args['value']
    local output    = args['output']
    local result   = {}
    for champname in pairs(lolData) do
        local v = get[stat](champname)
        if v == nil then
        elseif type(v) == 'table' then
            for _, v2 in pairs(v) do
                if '' .. v2 == value then
                    table.insert(result, champname)
                    break
                end
            end
        elseif '' .. v == value then 
            table.insert(result, champname)
        end
    end
    
    table.sort(result)
    if output ~= nil then
        if output == "custom" then 
            return frame:preprocess(lib.tbl_concat{result, prepend = args['prepend'], append = args['append'], separator = args['separator'], index = args["index"]})
        elseif output == "template" then 
            return frame:preprocess(lib.tbl_concat{result, prepend = "{{" .. args['t_name'] .. "|", append = "}}", separator = args['separator']})
        else
            return lib.tbl_concat{result}
        end
    else
        return lib.tbl_concat{result}
    end
end

function p.queryoptions(frame)
    local args; if frame.args == nil then args = lib.arguments(frame) else args = lib.arguments(frame.args) end
    local lolData   = mw.loadData('Module:ChampionData/data')
    local get       = require('Module:ChampionData/getter')
    local stat      = args['stat']
    local output    = args['output']
    local champ1     = false
    if champ1 == false then
        if args['t_name'] == 'cai' or args['t_name'] == 'ai' then
            champ1 = true
        end
    end
    local result   = {}
    for champname in pairs(lolData) do
        if stat == 'skills' then
            local v = get['skill_i'](champname)
            for k2,v2 in pairs(v) do
                if result[v2] == nil then
                    if output == "template" and champ1 then
                        result[v2] = v2 .. '|' .. champname
                    else
                        result[v2] = v2
                    end
                end
            end
            local v = get['skill_q'](champname)
            for k2,v2 in pairs(v) do
                if result[v2] == nil then
                    if output == "template" and champ1 then
                        result[v2] = v2 .. '|' .. champname
                    else
                        result[v2] = v2
                    end
                end
            end
            local v = get['skill_w'](champname)
            for k2,v2 in pairs(v) do
                if result[v2] == nil then
                    if output == "template" and champ1 then
                        result[v2] = v2 .. '|' .. champname
                    else
                        result[v2] = v2
                    end
                end
            end
            local v = get['skill_e'](champname)
            for k2,v2 in pairs(v) do
                if result[v2] == nil then
                    if output == "template" and champ1 then
                        result[v2] = v2 .. '|' .. champname
                    else
                        result[v2] = v2
                    end
                end
            end
            local v = get['skill_r'](champname)
            for k2,v2 in pairs(v) do
                if result[v2] == nil then
                    if output == "template" and champ1 then
                        result[v2] = v2 .. '|' .. champname
                    else
                        result[v2] = v2
                    end
                end
            end
        elseif stat == 'key' then
            if false and (
                lolData[champname]["date"] == ("Upcoming" or "N/A" or "Cancelled" or "")
                or 
                champname == ("Mega Gnar" or "Rhaast" or "Shadow Assassin") )
                then
                --ignore champion                                           
            else
                table.insert(result, champname)
            end 
        else
            local v = get[stat](champname)
            if type(v) == 'table' then
                for k2,v2 in pairs(v) do
                    if result[v2] == nil then
                        if output == "template" and champ1 then
                            result[v2] = v2 .. '|' .. champname
                        else
                            result[v2] = v2
                        end
                    end
                end
            else
                if v == nil then
                elseif result[v] == nil then
                    result[v] = v
                end
            end
        end
    end
    result[''] = nil
    table.sort(result)
    if output ~= nil then
        if output == "custom" then 
            return frame:preprocess(lib.tbl_concat{result, prepend = args['prepend'], append = args['append'], separator = args['separator'], index = args["index"]})
        elseif output == "template" then 
            return frame:preprocess(lib.tbl_concat{result, prepend = "{{" .. args['t_name'] .. "|", append = "}}", separator = args['separator']})
        elseif output == "table" then
            return tableprettyprint(result)
        else
            return lib.tbl_concat{result}
        end
    else
        return lib.tbl_concat{result}
    end
end

function p.querykeys(frame)
    local args; if frame.args == nil then args = lib.arguments(frame) else args = lib.arguments(frame.args) end
    local data = mw.loadData(args[1] or 'Module:Sandbox/DutyS12345/data4')
    local keys = {}
    for k,v in pairs(data) do
        for k2,v2 in pairs(v) do
            if keys[k2] == nil then
                keys[k2] = k2
            end
        end
    end
    local sorted = {}
    for k, v in pairs(keys) do
        table.insert(sorted, k)
    end
    table.sort(sorted)
    local ret = '{\n'
    for k, v in ipairs(sorted) do
        ret = ret .. v ..',\n'
    end
    ret = ret .. '}'
    return ret
end

function p.reorder(frame)
    -- local args; if frame.args == nil then args = lib.arguments(frame) else args = lib.arguments(frame.args) end
    local data = mw.loadData(--[=[args[1] or ]=]'Module:Sandbox/DutyS12345/data4')
    local order = {
		"image",
		"stacks",
		"unitimage",
		"unitname",
		"code",
		"disp_name",
		"nickname",
		"name",
		"tier",
		"rank",
		"type",
		"exclusive",
		"req",
		"limit",
		"caption",
		"champion",
		"maps",
		"altmaps",
		"mode",
		"avail",
		"menu",
		"menu1a",
		"menu1b",
		"menu2a",
		"menu2b",
		"menu3a",
		"menu3b",
		"menu4a",
		"menu4b",
		"menu5a",
		"menu5b",
		"menu6a",
		"menu6b",
		"ad",
		"ah",
		"ap",
		"armor",
		"rpen",
		"as",
		"cdr",
		"cdrunique",
		"crit",
		"exp",
		"gold",
		"gp10",
		"health",
		"hp",
		"hp5",
		"hp5flat",
		"hsp",
		"hspunique",
		"lifesteal",
		"mana",
		"mp5",
		"mp5flat",
		"mpen",
		"mpenflat",
		"mr",
		"ms",
		"msflat",
		"omnivamp",
		"pvamp",
		"spellvamp",
		"spec",
		"spec2",
		"act",
		"aura",
		"pass",
		"pass2",
		"pass3",
		"pass4",
		"consume",
		"noe",
		"recipe",
		"builds",
		"buy",
		"sell",
		"comb",
		"wr",
		"removed",
	}
	local stats = {
		["ad"] = true,
		["ah"] = true,
		["ap"] = true,
		["armor"] = true,
		["rpen"] = true,
		["as"] = true,
		["cdr"] = true,
		["cdrunique"] = true,
		["crit"] = true,
		["exp"] = true,
		["gold"] = true,
		["gp10"] = true,
		["health"] = true,
		["hp"] = true,
		["hp5"] = true,
		["hp5flat"] = true,
		["hsp"] = true,
		["hspunique"] = true,
		["lifesteal"] = true,
		["mana"] = true,
		["mp5"] = true,
		["mp5flat"] = true,
		["mpen"] = true,
		["mpenflat"] = true,
		["mr"] = true,
		["ms"] = true,
		["msflat"] = true,
		["omnivamp"] = true,
		["pvamp"] = true,
		["spellvamp"] = true,
		["spec"] = true,
		["spec2"] = true,
	}
	local menu = {
		["menu"] = true,
		["menu1a"] = true,
		["menu1b"] = true,
		["menu2a"] = true,
		["menu2b"] = true,
		["menu3a"] = true,
		["menu3b"] = true,
		["menu4a"] = true,
		["menu4b"] = true,
		["menu5a"] = true,
		["menu5b"] = true,
		["menu6a"] = true,
		["menu6b"] = true,
	}
	local effects = {
		["act"] = true,
		["aura"] = true,
		["pass"] = true,
		["pass2"] = true,
		["pass3"] = true,
		["pass4"] = true,
	}
	local numeric = {
		["ad"] = true,
		["ah"] = true,
		["ap"] = true,
		["armor"] = true,
		["rpen"] = true,
		["as"] = true,
		["cdr"] = true,
		["cdrunique"] = true,
		["crit"] = true,
		["exp"] = true,
		["gold"] = true,
		["gp10"] = true,
		["health"] = true,
		["hp"] = true,
		["hp5"] = true,
		["hp5flat"] = true,
		["hsp"] = true,
		["hspunique"] = true,
		["lifesteal"] = true,
		["mana"] = true,
		["mp5"] = true,
		["mp5flat"] = true,
		["mpen"] = true,
		["mpenflat"] = true,
		["mr"] = true,
		["ms"] = true,
		["msflat"] = true,
		["omnivamp"] = true,
		["pvamp"] = true,
		["spellvamp"] = true,
		-- ["buy"] = true,
		-- ["sell"] = true,
		-- ["comb"] = true,
		["code"] = true,
	}
	local has_stats = false
	local has_menu = false
	local has_effects = false
	
    local sorted = {}
    for k in pairs(data) do
        table.insert(sorted, k)
    end
    table.sort(sorted)
    local ret = {}
    ret[#ret + 1] = '<pre>'
    ret[#ret + 1] = '{\n'
    for _, key in ipairs(sorted) do
    	local entry = data[key]
    	if entry["removed"] == true then
    	else
    	ret[#ret + 1] = '\t' .. '["' .. key .. '"] = {\n'
        for _, param in ipairs(order) do
            local v = entry[param]
            if v then
            	if menu[param] and not has_menu then
            		ret[#ret + 1] = '\t\t' .. '["menu"] = {\n'
            		has_menu = true
            	elseif not menu[param] and has_menu then
            		ret[#ret + 1] = '\t\t},\n'
            		has_menu = false
            	end
            	if stats[param] and not has_stats then
            		ret[#ret + 1] = '\t\t' .. '["stats"] = {\n'
            		has_stats = true
            	elseif not stats[param] and has_stats then
            		ret[#ret + 1] = '\t\t},\n'
            		has_stats = false
            	end
            	if effects[param] and not has_effects then
            		ret[#ret + 1] = '\t\t' .. '["effects"] = {\n'
            		has_effects = true
            	elseif not effects[param] and has_effects then
            		ret[#ret + 1] = '\t\t},\n'
            		has_effects = false
            	end
            	if stats[param] or menu[param] or effects[param] then
            		ret[#ret + 1] = '\t'
            	end
            	ret[#ret + 1] = '\t\t' .. '["' .. param .. '"] = '
            	-- if numeric[param] then
            	-- 	if not tonumber(v) then mw.log(key .. ' ' .. param) else
            	-- 	ret[#ret + 1] = tonumber(v) .. ',\n'
            	-- 	end
            	-- else
            	if v == "true" then
            		ret[#ret + 1] =  v .. ',\n'
            	elseif type(v) == "table" then
                    ret[#ret + 1] = '{'
            		for k, tv in ipairs(v) do
            			if k ~= 1 then
            				ret[#ret + 1] = ', "'
            			else
            				ret[#ret + 1] = '"'
            			end
	                    ret[#ret + 1] = tv .. '"'
            		end
            		ret[#ret + 1] = '},\n'
	            elseif type(v) == "string" and (
	            		string.find(v, '"')
                		or string.find(v, '\n')
                	) then
                    ret[#ret + 1] = '[=[' .. v .. ']=],\n'
                elseif type(v) == "string" then
                	ret[#ret + 1] = '"' .. v .. '",\n'
                elseif v == true then
                	ret[#ret + 1] = 'true,\n'
            	elseif v == false then
                	ret[#ret + 1] = 'false,\n'
                else
                	ret[#ret + 1] =  v .. ',\n'
                end
            end
        end
        ret[#ret + 1] = '\t},\n'
        end
    end
    ret[#ret + 1] = '}\n'
    ret[#ret + 1] = '</pre>'
    return table.concat(ret)
end

function p.test(frame)
    -- local args; if frame.args == nil then args = lib.arguments(frame) else args = lib.arguments(frame.args) end
    -- local champname = args[1]
    -- local data1 = mw.loadData('Module:IconData/data')
    local data2 = mw.loadData('Module:Sandbox/DutyS12345/data4')
    -- local data1c = 0
    local data2c = 0
    -- for k in ipairs(data1) do
    --     data1c = data1c + 1
    -- end
    for k, v in pairs(data2) do
        data2c = data2c + 1
        if v["removed"] == nil then
        	mw.log(k)
        end
    end
    -- mw.log(data1c)
    mw.log(data2c)
    return
end

function p.test2(frame)
    -- local args; if frame.args == nil then args = lib.arguments(frame) else args = lib.arguments(frame.args) end
    -- local champname = args[1]
    local data1 = mw.loadData('Module:IconData/data')
    local data2 = mw.loadData('Module:Sandbox/DutyS12345/data4')
    local data1c = 0
    local data2c = 0
    for k in ipairs(data1) do
        data1c = data1c + 1
    end
    for k in ipairs(data2) do
        data2c = data2c + 1
    end
    mw.log(data1c)
    mw.log(data2c)
    return
end

function p.test3(frame)
    local args; if frame.args == nil then args = lib.arguments(frame) else args = lib.arguments(frame.args) end
    return args[1] .. ' returned'
end

function p.test4(frame)
    local args; if frame.args == nil then args = lib.arguments(frame) else args = lib.arguments(frame.args) end
    local contents = args['contents'] or args[1] or ''
    return contents .. ' ' .. string.reverse(contents)
end

function p.transfer(frame)
    local args; if frame.args == nil then args = lib.arguments(frame) else args = lib.arguments(frame.args) end
    local lolData   = mw.loadData('Module:ChampionData/data')
    local get       = require('Module:ChampionData/getter')
    local stat      = args['stat']
    local output    = args['output']
    local result    = ''
    local sorted    = {} 
    for champname in pairs(lolData) do
        table.insert(sorted, champname)
    end
    table.sort(sorted)
    for _,champname in ipairs(sorted) do
        result = result .. '["' .. champname .. '"] = {\n'
        result = result .. '["skill_i"] = {\n'
        local v = get['skill_i'](champname)
        for k2,v2 in ipairs(v) do
            result = result .. '["' .. v2 .. '"] = {\n},\n'
        end
        result = result .. '},\n'
        result = result .. '["skill_q"] = {\n'
        local v = get['skill_q'](champname)
        for k2,v2 in ipairs(v) do
            result = result .. '["' .. v2 .. '"] = {\n},\n'
        end
        result = result .. '},\n'
        result = result .. '["skill_w"] = {\n'
        local v = get['skill_w'](champname)
        for k2,v2 in ipairs(v) do
            result = result .. '["' .. v2 .. '"] = {\n},\n'
        end
        result = result .. '},\n'
        result = result .. '["skill_e"] = {\n'
        local v = get['skill_e'](champname)
        for k2,v2 in ipairs(v) do
            result = result .. '["' .. v2 .. '"] = {\n},\n'
        end
        result = result .. '},\n'
        result = result .. '["skill_r"] = {\n'
        local v = get['skill_r'](champname)
        for k2,v2 in ipairs(v) do
            result = result .. '["' .. v2 .. '"] = {\n},\n'
        end
        result = result .. '},\n'
        result = result .. '},\n'
    end
    return result
end

function p.transfer2(frame)
    return p.dataTemplateDump(frame)
end

function p.dataTemplateDump(frame)
    local args; if frame.args == nil then args = lib.arguments(frame) else 
        if frame:getParent().args ~= nil then args = lib.arguments(lib.mergeFrames(frame, frame:getParent())) else args = lib.arguments(frame.args) end
    end
    local ret = ''
    local param1 = args[1] or nil
    if param1 then
        ret = ret .. '["' .. param1 .. '"] = {\n'
    else
        ret = ret .. '{'
    end
    for k,v in pairs(args) do
        if v ~= ''
        and v ~= '\n'
        and v ~= ' 'then
        	if string.find(v, '"') or string.find(v, '\n') then
        		ret = ret .. '["' .. k .. '"] = [=[' .. v .. ']=],\n'	
        	else
        		ret = ret .. '["' .. k .. '"] = "' .. v .. '",\n'
        	end
        end
    end
    ret = ret .. '},'
    return ret
end

function p.getfi(frame)
    local args; if frame.args == nil then args = lib.arguments(frame) else args = lib.arguments(frame.args) end
    local flashData   = mw.loadData('Module:Sandbox/DutyS12345/data')
    
    local champion = args['champion'] or args[1]
    local ability   = args['ability'] or args[2]
    local output    = args['output']  or args[3] or nil
    local s         = ''
    
    champion = lib.validateName(champion)
    
    local fi = flashData[champion]['skill_i'][ability]
        or flashData[champion]['skill_q'][ability]
        or flashData[champion]['skill_w'][ability]
        or flashData[champion]['skill_e'][ability]
        or flashData[champion]['skill_r'][ability]
        or nil
    
    local notes = flashData[champion]['skill_i']['notes']
        or flashData[champion]['skill_q']['notes']
        or flashData[champion]['skill_w']['notes']
        or flashData[champion]['skill_e']['notes']
        or flashData[champion]['skill_r']['notes']
        or nil
        
    if fi ~= nil and fi ~= '' then
        for i,v in pairs(fi) do
            if s ~= '' then
                s = s .. ', '
            end
            s = s .. i .. ':' .. v
        end
    end
    
    if notes ~= nil and notes ~= '' then
        abilitynotes = notes[ability]
        if abilitynotes ~= nil then
            if s ~= '' then
                s = s .. ', '
            end
            s = s .. abilitynotes
        end
    end
    
    if output ~= nil then
        if output == 'notes' then
            return s
        else
            return ''
        end
    else
        return s
    end
end

function p.fiTable(frame)
    local args; if frame.args == nil then args = lib.arguments(frame) else args = lib.arguments(frame.args) end
    local fiData        = mw.loadData('Module:Sandbox/DutyS12345/data')
    local lolData       = mw.loadData('Module:ChampionData/data')
    local lolTable      = {}
    local tablenode     = mw.html.create('table')
    tablenode
            :addClass('sortable wikitable sticky-header')
            :css('width', '100%')
            --:css('text-align', 'right')
            :css('white-space', 'nowrap')
            :newline()
    
    tablenode
        :tag('tr')
            :tag('th')
                :wikitext('Champion')
                --:css('width', '180px')
                --:attr('class', 'unsortable')
            :done()
            :tag('th')
                :wikitext('Ability')
                --:attr('class', 'unsortable')
            :done()
            :tag('th')
                :wikitext('Targeting Type')
            :done()
            -- :tag('th')
            --     :wikitext('FI 1')
            -- :done()
            -- :tag('th')
            --     :wikitext('FI 2')
            -- :done()
            -- :tag('th')
            --     :wikitext('FI 3')
            -- :done()
        :done()
        :newline()
    
    for champname in pairs(lolData) do
        if  lolData[champname]['date'] == ('Upcoming' or 'N/A' or 'Cancelled' or '')
            then
            --ignore champion
        else
            table.insert(lolTable, champname)
        end
    end
    table.sort(lolTable)
    
    for _, champion in ipairs(lolTable) do
        local champrow  = mw.html.create('')
        local abilities = {}
        local sklist = {'skill_i', 'skill_q', 'skill_w', 'skill_e', 'skill_r'}
        local numabilities = 0
        for _, sk in ipairs(sklist) do
            local skills = lolData[champion][sk]
            for _, skillname in ipairs(skills) do
                numabilities = numabilities + 1
            end
        end
        for i, sk in ipairs(sklist) do
            local abilities = lolData[champion][sk]
            for j, ability in ipairs(abilities) do
                local tablerow  = mw.html.create('tr')
                if i == 1 and j == 1 then
                    tablerow
                        :tag('td')
                            :wikitext('[[File:' .. FN.championsquare{champion} .. '|20px|alt=' .. champion .. '|link=' .. champion .. ']] [[' .. champion .. ']]')
                            :attr('data-sort-value', champion)
                            :attr('rowspan', numabilities)
                        :done()
                end
                local targeting = frame:expandTemplate{title = 'Data_' .. champion .. '/' .. ability, args = {'pst2', 'targeting'}}
                if targeting == nil then
                    targeting = ''
                end
                local linkfreetargeting = striplinks(targeting)
                tablerow
                    :tag('td')
                        :wikitext('[[File:' .. FN.ability{ability} .. '|20px|alt=' .. ability .. '|link=Template:Data_' .. champion .. '/' .. ability .. ']] [' .. 'https://leagueoflegends.fandom.com/wiki/Template:Data_' .. mw.uri.encode(champion,"WIKI") .. '/' .. mw.uri.encode(ability,"WIKI") .. '?action=edit' .. ' ' .. ability .. ']')
                        :attr('data-sort-value', i)
                    :done()
                    :tag('td')
                        :wikitext(targeting)
                        :attr('data-sort-value', linkfreetargeting)
                    :done()
                :done()
                champrow
                    :node(tablerow)
            end
        end
        champrow
        :done()
    
        if champrow ~= nil then
        tablenode
            :node(champrow)
            :newline()
        end
    end
    tablenode:allDone()
    return frame:preprocess(tostring(tablenode))
end

function tableprettyprint(tbl)
    local s = ''
    for k,v in pairs(tbl) do
        if k == nil then
            s = s .. 'nil' .. ' : '
        else
            s = s .. k .. ' : ' 
        end
        if v == nil then
            s = s .. 'nil' .. '<br>'
        else
            s = s .. v .. '<br>'
        end
    end
    return s
end


function p.doforeverychampion(frame)
    local args; if frame.args == nil then args = lib.arguments(frame) else args = lib.arguments(frame.args) end
    -- custom/manual entries
    local function customparams_iter(t)
        local i = 0
        local n = 0
        for i, _ in pairs(t) do
            n = n + 1
        end
        return function ()
            i = i + 1
            if i <= n and t['p' .. i .. 'n'] and t['p' .. i .. 'v'] then
                return t['p' .. i .. 'n'] or '', t['p' .. i .. 'v'] or ''
            else
                return nil
            end
        end
    end
    local lolData   = mw.loadData('Module:ChampionData/data')
    local championTable = {}
    local ret = ''
    for champion in pairs(lolData) do
        local release = lolData[champion]['date']
        if champion == 'Mega Gnar'
        or champion == 'Rhaast'
        or champion == 'Shadow Assassin'
        or champion == 'Kled & Skaarl'
        or release == 'Upcoming'
        or release == 'N/A'
        or release == 'Cancelled'
        or release == ''
        then
            --ignore champion
        else
            table.insert(championTable, champion)
        end
    end
    table.sort(championTable)
    for i, champion in ipairs(championTable) do
        ret = ret .. args['prepend']
        local template = '{{' .. template 
        for key, value in customparams_iter(args) do
            template = template .. '|' .. key .. '=' .. value
        end
        template = template .. '}}'
        ret = ret .. frame:preprocess(template)
        ret = ret .. args['append']
        if i < #championTable then ret = ret .. args['separator'] end
    end
end

function p.doforeverychampionability(frame)
    local args; if frame.args == nil then args = lib.arguments(frame) else args = lib.arguments(frame.args) end
    -- custom/manual entries
    local function customparams_iter(t)
        local i = 0
        local n = 0
        for i, _ in pairs(t) do
            n = n + 1
        end
        return function ()
            i = i + 1
            if i <= n and t['p' .. i .. 'n'] and t['p' .. i .. 'v'] then
                return t['p' .. i .. 'n'] or '', t['p' .. i .. 'v'] or ''
            else
                return nil
            end
        end
    end
    local lolData   = mw.loadData('Module:ChampionData/data')
    local championTable = {}
    local ret = ''
    for champion in pairs(lolData) do
        local release = lolData[champion]['date']
        if champion == 'Mega Gnar'
        or champion == 'Rhaast'
        or champion == 'Shadow Assassin'
        or champion == 'Kled & Skaarl'
        or release == 'Upcoming'
        or release == 'N/A'
        or release == 'Cancelled'
        or release == ''
        then
            --ignore champion
        else
            table.insert(championTable, champion)
        end
    end
    table.sort(championTable)
    for i, champion in ipairs(championTable) do
        ret = ret .. args['prepend']
        local template = '{{' .. template 
        for key, value in customparams_iter(args) do
            template = template .. '|' .. key .. '=' .. value
        end
        template = template .. '}}'
        ret = ret .. frame:preprocess(template)
        ret = ret .. args['append']
        if i < #championTable then ret = ret .. args['separator'] end
    end
end

function p.groupedStatTable(frame)
    local args; if frame.args == nil then args = lib.arguments(frame) else 
        if frame:getParent().args ~= nil then args = lib.arguments(lib.mergeFrames(frame, frame:getParent())) else args = lib.arguments(frame.args) end
    end
    
    args['datatype'] = args['datatype'] or args[1]
    args['label']    = args['label']    or args[2]
    args['filter']   = args['filter']   or args[3]
    local lolData    = mw.loadData('Module:ChampionData/data')
    local get        = require('Module:ChampionData/getter')
    local IL         = require('Module:ImageLink')
    
    local threshold3col = tonumber(args['3col']) or 12
    local threshold2col = tonumber(args['2col']) or 8
    
    local statTable = {}
    -- standard entries with note
    for champion in pairs(lolData) do
        -- filter
        local releasedate = get["date"](champion)
        if  releasedate== "Upcoming" or 
            releasedate == "N/A" or
            releasedate == "Cancelled" or
            releasedate == "" then
            --ignore champion
        elseif args['datatype'] == 'range' and get['rangetype'](champion) ~= args['filter'] then -- do nothing
        elseif args[champion .. '_o'] then -- do nothing 
        else
            local v = get[args['datatype']](champion)
            if v == nil then
            else
                if statTable[v] == nil then statTable[v] = {} end
                local displaystring = {tostring(IL.champion{
                    ["champion"] = champion,
                    -- ["circle"] = "true",
                })}
                if args[champion .. '_m'] then
                    displaystring[#displaystring + 1] = ' '
                    displaystring[#displaystring + 1] = args[champion .. '_m']
                end
                if args[champion .. '_n'] then
                    displaystring[#displaystring + 1] = ' <span title="'
                    displaystring[#displaystring + 1] = args[champion .. '_n']
                    displaystring[#displaystring + 1] = '"><sup>note</sup></span>'
                end
                statTable[v][champion] = table.concat(displaystring)
            end
        end
    end
    -- custom/manual entries
    local function customparams_iter(t)
        local i = 0
        local n = 0
        for i, _ in pairs(t) do
            n = n + 1
        end
        return function ()
            i = i + 1
            if i <= n and (t['c' .. i .. 'c'] or t['c' .. i .. 'm']) and t['c' .. i .. 'v'] then
                return t['c' .. i .. 'c'] or '', t['c' .. i .. 'm'] or '', t['c' .. i ..'n'] or '', t['c' .. i .. 'v']
            else
                return nil
            end
        end
    end
    for champion, modifier, note, value in customparams_iter(args) do
        local v = tonumber(value)
        local displaystring = {}
        if champion ~= '' then
            displaystring[#displaystring + 1] = tostring(IL.champion{
                ["champion"] = champion,
                -- ["circle"] = "true",
            })
        end
        if modifier ~= '' then
            if champion ~= '' then
            	displaystring[#displaystring + 1] = ' '
            end
            displaystring[#displaystring + 1] = modifier
        end
        if note ~= '' then
            displaystring[#displaystring + 1] = '  <span title="'
            displaystring[#displaystring + 1] = note
            displaystring[#displaystring + 1] = '"><sup>note</sup></span>'
        end
        if statTable[v] == nil then statTable[v] = {} end
        if champion ~= '' then 
            statTable[v][champion] = table.concat(displaystring)
        else
            statTable[v][modifier] = table.concat(displaystring)
        end
    end
    
    -- table header row
    local tablenode = mw.html.create('table')
    tablenode
        :addClass('wikitable')
        :css('width', '100%')
        :newline()
    tablenode
        :tag('tr')
            :tag('th')
                :wikitext(args['label'] or args['datatype'])
            :done()
            :tag('th')
                :wikitext('Champion')
            :done()
            -- :tag('th')
            --     :wikitext('Notes')
            -- :done()
        :done()
        :newline()
    
    -- sorting and table rows
    local optionTable = {}
    for option in pairs(statTable) do
        table.insert(optionTable, option)
    end
    table.sort(optionTable, function(a,b) return a > b end)
    for _, option in ipairs(optionTable) do
        local tablerow = mw.html.create('tr')
        local champcell = tablerow
            :tag('td')
                :css('text-align', 'center')
        if option == tonumber('inf') then
            champcell:wikitext('∞')
        else
            champcell:wikitext(option)
        end
        champcell = champcell
            :done()
            :newline()
            :tag('td')
                :tag('div')
                    :addClass('columntemplate')
                    :css('margin','0.4em 0 0.4em 0')
        local championTable = {}
        for champion in pairs(statTable[option]) do
            table.insert(championTable, champion)
        end
        table.sort(championTable)
        if #championTable >= threshold3col then
            champcell
                :css('column-count', 3)
                :css('-webkit-column-count', 3)
                :css('-moz-column-count', 3)
        elseif #championTable >= threshold2col then
            champcell
                :css('column-count', 2)
                :css('-webkit-column-count', 2)
                :css('-moz-column-count', 2)
        end
        champcell = champcell
                    :tag('ul')
        for _, champion in ipairs(championTable) do
            champcell
                :tag('li')
                    :wikitext(statTable[option][champion])
                    :newline()
        end
        tablenode
            :node(tablerow)
            :newline()
    end
    return tostring(tablenode)
end

function p.tocscrollbox(frame)
    local args; if frame.args == nil then args = lib.arguments(frame) else args = lib.arguments(frame.args) end
    local content = args["content"] or args[1] or nil
    if content == nil then return end
    local nav = "\n"
    local content = "\n" .. content
    for patch in string.gmatch(content, "\n;(.-)\n") do
        patch = string.gsub(patch, "%[%[", "")
        patch = string.gsub(patch, "%]%]", "")
        nav = nav .. "* " .. patch .. "\n"
    end
    local navbox = mw.html.create('div')
    navbox
        :css("float", "left")
        :css("min-height", "330px")
        :css("margin", "0 3px")
        :css("border", "1px solid #AAAAAA")
        :css("padding-left", "0.5em")
        :css("padding-right", "0.5em")
        :css("box-sizing", "border-box")
        :css("background", "transparent")
        :css("border-radius", "4px")
        :wikitext(nav)
    local scrollbox = mw.html.create('div')
    scrollbox
        :css({
        	["position"] = "relative",
        	["overflow"] = "auto",
        	["border"] = "1px solid #AAAAAA",
        	["border-radius"] = "4px",
        })
        :tag('div')
        	:css({
        		["position"] = "absolute",
        		["margin-right"] = "3px",
        		["padding-left"] = "0.5em",
        		["padding-right"] = "0.5em",
        	})
        	:wikitext(content)
	        -- :css("overflow", "auto")
	        -- :css("max-height", "330px")
	        -- :css("width", "calc(76% - 9px)")
	        -- :css("box-sizing", "border-box")
	        -- :css("-moz-box-sizing", "border-box")
	        
	        -- :css("background", "transparent")
        
        
        -- :cssText("overflow:auto; max-height:{{{maxheight|330px}}}; width:{{{width|100%}}}; margin:3px 0; border:1px solid #AAAAAA; padding-left:0.5em; box-sizing:border-box; -moz-box-sizing:border-box; -moz-border-radius-topleft:0.5em; background:{{{background|transparent}}}; border-radius:4px;")
    local wrapper = mw.html.create('div')
        :css({
        	["display"] = "grid",
        	["grid-template-columns"] = "max-content 1fr",
        })
        :node(navbox)
        :node(scrollbox)
    return tostring(wrapper)
end

function p.itemefficiency(frame)
    local args; if frame.args == nil then args = lib.arguments(frame) else args = lib.arguments(frame.args) end
    local instring = args[1]
    local ret = ''
    return ret
end

function p.buildefficiency(frame)
    local args; if frame.args == nil then args = lib.arguments(frame) else args = lib.arguments(frame.args) end
    local instring = args[1]
    local ret = ''
    return ret
end

function p.itemStatTable(frame)
	-- local args; if frame.args == nil then args = lib.arguments(frame) else args = lib.arguments(frame.args) end
	--     local args = (frame.args[1] and frame.args) or frame:getParent().args
	local args; if frame.args == nil then args = lib.arguments(frame) else 
		if frame:getParent().args ~= nil then args = lib.arguments(lib.mergeFrames(frame, frame:getParent())) else args = lib.arguments(frame.args) end
	end
	local sub, find = string.sub, string.find
	local get = require('Module:ItemData/getter')
	local IL = require('Module:ImageLink')
	local FD = require('Module:Fd')
	local stat = args[1]
	local wr = args["wr"] ~= nil
	local stattable = mw.html.create("table")
		:addClass("wikitable sortable")
		:css("width", "90%")
		:css("margin-left", "auto")
		:css("margin-right", "auto")
		:css("text-align", "center")
	if stat == "offensive" then
		stattable
			:tag("tr")
				:tag("th")
					:wikitext("Item")
				:done()
				:tag("th")
					:wikitext("Cost")
					:attr("data-sort-type", "number")
				:done()
				:tag("th")
					:wikitext("AD")
				:done()
				:tag("th")
					:wikitext("AS")
				:done()
				:tag("th")
					:wikitext("Crit")
				:done()
				:tag("th")
					:wikitext("LS")
				:done()
				:tag("th")
					:wikitext("APen")
				:done()
				:tag("th")
					:wikitext("Maps")
				:done()
			:done()
	elseif stat == "magical" then
		stattable
			:tag("tr")
				:tag("th")
					:wikitext("Item")
				:done()
				:tag("th")
					:wikitext("Cost")
					:attr("data-sort-type", "number")
				:done()
				:tag("th")
					:wikitext("AP")
				:done()
				:tag("th")
					:wikitext("AH")
				:done()
				:tag("th")
					:wikitext("Mana")
				:done()
				:tag("th")
					:wikitext("MP5")
				:done()
				:tag("th")
					:wikitext("HSP")
				:done()
				:tag("th")
					:wikitext("OVamp")
				:done()
				:tag("th")
					:wikitext("MPen")
				:done()
				:tag("th")
					:wikitext("Maps")
				:done()
			:done()
	elseif stat == "defensive" then
		stattable
			:tag("tr")
				:tag("th")
					:wikitext("Item")
				:done()
				:tag("th")
					:wikitext("Cost")
					:attr("data-sort-type", "number")
				:done()
				:tag("th")
					:wikitext("Health")
				:done()
				:tag("th")
					:wikitext("Armor")
				:done()
				:tag("th")
					:wikitext("MR")
				:done()
				:tag("th")
					:wikitext("HP5")
				:done()
				:tag("th")
					:wikitext("Maps")
				:done()
			:done()
	else
		local header = stattable
			:tag("tr")
				:tag("th")
					:wikitext("Item")
				:done()
				:tag("th")
					:wikitext("Cost")
					:attr("data-sort-type", "number")
				:done()
				:tag("th")
					:wikitext("Amount")
				:done()
		if args["wr"] ~= "true" then
			header
				:tag("th")
					:wikitext("Availability")
				:done()
		end
		if stat == "hp" or stat == "mr" or stat == "armor" or 
		stat == "ad" or stat == "msflat" or stat == "mana" or 
		stat == "ap" or stat == "ah" or stat == "gp10" or
		stat == "as" or stat == "lifesteal" or stat == "omnivamp"or
		stat == "pvamp" or stat == "armpen" then
			stat = "default"
		elseif stat == "ms" or stat == "crit" or stat == "cdr" or stat == "hsp" then
			stat = "unique"
		elseif stat == "mp5" or stat == "hp5" or stat == "mpen" then
			stat = "flat"
		elseif stat == "pykehealth" then
		elseif stat == "baseefficiency" then
			-- pass
		end
	end
	-- local itemdata = (
	-- 	args["wr"] == nil and require("Module:Do for every item").get_items()
	-- 	or require("Module:Do for every WR item").get_items()
	-- )
	
	local f = {}
		
	function f.offensive(itemname)
		local exists = false
		local statcells = mw.html.create("")
			:tag("td")
		if get["ad"](itemname) then
			exists = true
			statcells:wikitext(get["ad"](itemname), nil)
		end
		statcells = statcells:done():tag("td")
		if get["as"](itemname) then
			exists = true
			statcells:wikitext(get["as"](itemname), nil)
		end
		statcells = statcells:done():tag("td")
		if get["crit"](itemname) --[=[or get["critunique"](itemname)]=] then
			exists = true
			statcells:wikitext(
				(get["crit"](itemname) and get["crit"](itemname) or "") --[=[ .. 
				(get["critunique"](itemname) and get["critunique"](itemname) .. " (Unique)" or "")]=]
			)
		end
		statcells = statcells:done():tag("td")
		if get["lifesteal"](itemname) then
			exists = true
			statcells:wikitext(get["lifesteal"](itemname), nil)
		end
		statcells = statcells:done():tag("td")
		if get["armpen"](itemname) then
			exists = true
			statcells:wikitext(get["armpen"](itemname), nil)
		end
		statcells = statcells:done()
		if exists then return statcells
		else return nil
		end
	end
	
	function f.magical(itemname)
		local exists = false
		local statcells = mw.html.create("")
			:tag("td")
		if get["ap"](itemname) then
			exists = true
			statcells:wikitext(get["ap"](itemname), nil)
		end
		statcells = statcells:done():tag("td")
		if get["ah"](itemname) then
			exists = true
			statcells:wikitext(get["ah"](itemname), nil)
		end
		statcells = statcells:done():tag("td")
		if get["mana"](itemname) then
			exists = true
			statcells:wikitext(get["mana"](itemname), nil)
		end
		statcells = statcells:done():tag("td")
		if get["mp5"](itemname) or get["mp5flat"](itemname) then
			exists = true
			statcells:wikitext(
				(get["mp5"](itemname) and get["mp5"](itemname) or "") .. 
				(get["mp5flat"](itemname) and get["mp5flat"](itemname) or "")
			)
		end
		statcells = statcells:done():tag("td")
		if get["hsp"](itemname) then
			exists = true
			statcells:wikitext(get["hsp"](itemname), nil)
		end
		statcells = statcells:done():tag("td")
		if get["omnivamp"](itemname)then
			exists = true
			statcells:wikitext(get["omnivamp"](itemname), nil)
		end
		statcells = statcells:done():tag("td")
		if get["mpen"](itemname) or get["mpenflat"](itemname) then
			exists = true
			statcells:wikitext(
				(get["mpen"](itemname) and get["mpen"](itemname) .. "%" or "") .. 
				(get["mpenflat"](itemname) and get["mpenflat"](itemname) or "")
			)
		end
		statcells = statcells:done()
		if exists then return statcells
		else return nil
		end
	end
	
	function f.defensive(itemname)
		local exists = false
		local statcells = mw.html.create("")
			:tag("td")
		if get["hp"](itemname) then
			exists = true
			statcells:wikitext(get["hp"](itemname), nil)
		end
		statcells = statcells:done():tag("td")
		if get["armor"](itemname) then
			exists = true
			statcells:wikitext(get["armor"](itemname), nil)
		end
		statcells = statcells:done():tag("td")
		if get["mr"](itemname) then
			exists = true
			statcells:wikitext(get["mr"](itemname), nil)
		end
		statcells = statcells:done():tag("td")
		if get["hp5"](itemname) or get["hp5flat"](itemname) then
			exists = true
			statcells:wikitext(
				(get["hp5"](itemname) and get["hp5"](itemname) or "") .. 
				(get["hp5flat"](itemname) and get["hp5flat"](itemname) or "")
			)
		end
		statcells = statcells:done()
		if exists then return statcells
		else return nil
		end
	end
	
	function f.default(itemname)
		if get[args[1]](itemname) then
			return mw.html.create('td'):wikitext(get[args[1]](itemname), nil)
		end
	end
	
	function f.unique(itemname)
		if get[args[1]](itemname) or get[args[1] .. "unique"](itemname) then
		return mw.html.create('td'):wikitext(
			(get[args[1]](itemname) and get[args[1]](itemname) or "") ..
			(get[args[1] .. "unique"](itemname) and (get[args[1] .. "unique"](itemname) .. " (Unique)") or "")
		)
		else return nil
		end
	end
	
	function f.flat(itemname)
		if get[args[1]](itemname) or get[args[1] .. "flat"](itemname) then
			return mw.html.create('td'):wikitext(
				(get[args[1]](itemname) and get[args[1]](itemname) or "") ..
				(get[args[1] .. "flat"](itemname) and get[args[1] .. "flat"](itemname) or "")
			)
		else return nil
		end
	end
	
	function f.pykehealth(itemdata)
		if get["hp"](itemname) then
			if get["ad"](itemname) then
				return mw.html.create('td'):wikitext(math.floor((get["hp"](itemname) / 14 + get["ad"](itemname)) * 10 + 0.5) / 10)
			end
			return mw.html.create('td'):wikitext(math.floor((get["hp"](itemname) / 14) * 10 + 0.5) / 10)
		else return nil
		end
	end
	
	-- function f.baseefficiency(item)
	-- 	if tonumber(item["buy"]) > 0 then
	-- 		local wikidata = require("Module:Feature").split(mw.title.new('Template:Gold_value_math/var'):getContent(),"vardefine:", true)
	-- 		local gold_value = {}
	-- 		local total = 0
	
	-- 		for i = 2, #wikidata do
	-- 			local data = wikidata[i]
	-- 			local i = sub(data,1,find(data,"|",1,true)-1)
	-- 			local v = (find(data,"var:",1,true) and gold_value[sub(data,find(data,"var:",1,true)+4,find(data,"}",1,true)-1)]
	-- 				or sub(data,find(data,"|",1,true)+1,find(data,"}",1,true)-1)
	-- 			)
	-- 			gold_value[i] = v
	-- 			total = total + v * (item[i] or 0)
	-- 		end
	
	-- 		local title = math.floor((total - item["buy"]) * 100 + 0.5) / (100)
	
	-- 		return s('<span style="border-bottom:1px dotted gray;cursor:help;" title="' ..
	-- 			(title < 0 and title or "+" .. title) ..
	-- 			'g">' .. FD.get{item = {math.floor((total / item["buy"] * 100) * 100 + 0.5) / (100) .. "%"}} .. "</span>"
	-- 			, item
	-- 		)
	-- 	end
	-- end
	local m = {}
	
	
	local getRowContents = f[stat]
	local getAvailability
	if stat == "offensive" or stat == "magical" or stat == "defensive" then
		getAvailability = function (itemname)
			local sr = get["SR"](itemname) == true
			local ha = get["HA"](itemname) == true
			-- local tt = get["TT"](itemname) == true
			-- local nb = get["NB"](itemname) == true
			-- local cs = get["CS"](itemname) == true
			local availability = mw.html.create("td")
				:tag("span")
				:css("border-bottom", "1px dotted gray")
				:css("cursor", "help")
			if sr and ha then
				availability
					:attr("title", "Available on all maps")
					:wikitext("All")
			elseif sr then
				availability
					:attr("title", "Summoner's Rift")
					:wikitext("[[Summoner's Rift|S]]")
			elseif ha then
				availability
					:attr("title", "Howling Abyss")
					:wikitext("[[Howling Abyss|H]]")
			else
				return nil
			end
			return availability:allDone()
		end
	elseif not wr then
		getAvailability = function (itemname)
			local sr = get["SR"](itemname) == true
			local ha = get["HA"](itemname) == true
			local tt = get["TT"](itemname) == true
			local nb = get["NB"](itemname) == true
			-- local cs = get["CS"](itemname) == true
			local availability = mw.html.create("td")
			if sr and ha --[=[and tt]=] and nb then
				availability
					:tag("span")
						:css("border-bottom", "1px dotted gray")
						:css("cursor", "help")
						:attr("title", "Available on all maps")
						:wikitext("All")
			else
				local s = {nil, nil, nil, nil}
				local s_len = 0
				if sr then
					s_len = s_len + 1
					s[s_len] = "[[Summoner's Rift]]"
				end
				-- if tt then
				-- 	s_len = s_len + 1
				-- 	s[s_len] = "[[Twisted Treeline]]"
				-- end
				if ha then
					s_len = s_len + 1
					s[s_len] = "[[Howling Abyss]]"
				end
				if nb then
					s_len = s_len + 1
					s[s_len] = "[[Nexus Blitz]]"
				end
				availability
					:wikitext(table.concat(s,", "))		
			end
			return availability:allDone()
		end
	else
		getAvailability = function (itemname) return nil end
	end
	local function createStatTableRow(itemname)
		local itemstats = get["stats"](itemname) --itemdata["stats"]
		if itemstats == nil then return end
		local rowcontents = getRowContents(itemname)
		if rowcontents == nil then return end
		local availability = getAvailability(itemname)
		local itemicon = {["item"] = itemname}
		if wr then itemicon["text"] = string.sub(itemname,1,-12) end
		local buy = get["buy"](itemname)
		local tablerow = mw.html.create("tr")
			:tag("td")
				:css("text-align", "left")
				:attr("data-sort-value", itemname)
				:node(IL.item(itemicon))
			:done()
			:tag("td")
				:node(IL.basic{
					["link"] = (wr and "Gold (Wild Rift)") or "Gold",
					["text"] = FD.get{args = {buy}},
					["label"] = "before",
					["image"] = "Gold.png",
					["alttext"] = (buy) .. " Gold",
					["border"] = "false",
					["style"] = "color:gold;white-space:pre",
					["labellink"] = "false",
					["labelstyle"] = "white-space:pre"
				})
			:done()
			:node(rowcontents)
			:node(availability)
			-- :node(availability)
		:done()
		stattable
			:node(tablerow)
	end
	
	-- if true then
	p.doforeveryitem(createStatTableRow)
	-- else
	-- 	p.doforeverywritem(createStatTableRow)
	-- end
	----------------
	-- for i = 1, #itemdata do
		-- local args = (frame.args[1] and frame.args) or frame:getParent().args
		-- args["buy"] = args["buy"] or 0
		-- args["maps"] = args["maps"] or "STHN"

		-- if args["removed"] == "true" then
		-- 	return('<tr><td style="text-align:left" colspan="3">' .. hs(args) .. "</td><td>" ..
		-- 		(args[2] == "removed" and "True." 
		-- 		or args["wr"] == "true" and "Move from [[Module:itemstats/wrdata]] to [[Module:ItemData/wrdata/removed]]."
		-- 		or "Move from [[Module:ItemData/data]] to [[Module:ItemData/data/removed]]."
		-- 		) ..
		-- 		"</td></tr>"
		-- 	)
		-- end
		
		-- if find(result[len_result],'<strong class="error">',1,true) then
			-- return result[len_result]
		-- end
	-- end
	return tostring(stattable)
end

-- function p.get(frame)
--     local args = (frame.args[1] and frame.args) or frame:getParent().args
-- 	args[2] = args[2] or ""
-- 	args["cha"] = args["cha"] or 1

--     local s = {}
--     local s_len = 0
-- 	local temp_s = {"{{".. args[1]}

-- 	do
-- 		local temp_s_len = 1
-- 		local i = 1

-- 		while args["p"..i.."n"] ~= nil do
-- 			temp_s_len = temp_s_len + 1
-- 			temp_s[temp_s_len] = "|" .. args["p"..i.."n"] .. "=" .. args["p"..i.."v"]
-- 			i = i + 1
-- 		end
		
-- 		temp_s_len = temp_s_len + 1
-- 		temp_s[temp_s_len] = "|" .. args["cha"] .. " = "
-- 	end

-- 	temp_s = table.concat(temp_s)
	
-- 	local itemtable = p.get_items()

--     for i = 1, #itemtable do
--     	s_len = s_len + 1
--         s[s_len] = frame:preprocess(temp_s .. itemtable[i] .. "}}") .. args[2]
--     end

--     return table.concat(s)
-- end

function p.doforeveryitem(do_function)
	local exclude_list = {"Turret Plating", "Minion Dematerializer", "Gusto", "Farsight Ward", "Perfectly Timed Stopwatch", 
		"Broken Stopwatch", "Scarecrow Effigy", "Eye of the Herald", "Base Turret Reinforced Armor (Turret Item)", "Warden's Eye", 
		"Commencing Stopwatch", "Anti-tower Socks", "Phreakish Gusto", "Super Mech Armor", "The Golden Spatula",
		"Reinforced Armor (Turret Item)", "Overcharged", "Ohmwrecker (Turret Item)", "'Your Cut'", "Anti-Tower Socks", 
		"Super Mech Power Field", "Fortification", "Death's Daughter", "Fire at Will", "Raise Morale"}
	local exclude = {}
	local itemData = mw.loadData("Module:ItemData/data")
	for i = 1, #exclude_list do
		exclude[exclude_list[i]] = true
	end
	
	local itemtable = {}
	local len_item = 0

    for itemname in pairs(itemData) do
    	if not exclude[itemname] then
	    	len_item = len_item + 1
	    	itemtable[len_item] = itemname
	    end
    end
    
    table.sort(itemtable)
    for i = 1, len_item do
    	local itemname = itemtable[i]
    	do_function(itemname)
    end
end

function p.itemiditemname(frame)
	local itemnames = require('Module:Do for every item').get_data()
	local getter = require('Module:ItemData/getter')
	local ret = mw.html.create('table')
		:addClass('wikitable sortable')
	ret:tag('tr'):tag('th'):wikitext('ID'):done():tag('th'):wikitext('Item')
	for i = 1, #itemnames do
		local item = itemnames[i]
		local row = mw.html.create('tr')
		row:tag('td'):wikitext(getter.id(item), nil)
		row:tag('td'):wikitext(item)
		ret:node(row)
	end
	return tostring(ret)
end

function p.junk(frame)
	local args; if frame.args == nil then args = lib.arguments(frame) else args = lib.arguments(frame.args) end
	local skinData  = mw.loadData('Module:SkinData/data')
	local champ = args[1]
	local id = tonumber(args[2])
	for k, v in pairs(skinData[champ]["skins"]) do
		if id == v["id"] then return k end
	end
	return ""
end

local SD  = require('Module:SkinData')
local skinData  = mw.loadData('Module:SkinData/data')
local themes    = require('Module:SkinThemes')
-- 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 skinIter(t)
    local keys = {}
    for k in pairs(t) do
        if t[k]['id'] ~= nil then
            keys[#keys+1] = k
        end
    end

    table.sort(keys, function(a,b)
        return (t[a]['custom_sort_id'] or t[a]['id']) < (t[b]['custom_sort_id'] or t[b]['id'])
    end)

    -- return the iterator function
    local i = 0
    return function()
        i = i + 1
        if keys[i] then
            return keys[i], t[keys[i]], i
        end
    end
end

function p.skinpage(frame)
    local args; if frame.args == nil then args = lib.arguments(frame) else args = lib.arguments(frame.args) end
    
    args[1] = args['champion'] or args[1] or mw.title.getCurrentTitle().rootText
    args['champion'] = lib.validateName(args[1])
    
    if skinData[args['champion']] == nil then
        return userError("Champion ''" .. args['champion'] .. "'' does not exist in Module:SkinData/data", "LuaError")
    end
    
    local t = skinData[args['champion']]["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
    
    -- generates all categories of all sets of all skins of said champion
    local k = ""
    for skinname in pairs(t) do
        k = k..(themes.getsetcategory({args["champion"], skinname}) or '')
    end
    k = "[[Category:LoL Champion cosmetics]][[Category:"..args[1].."]]"..k

    function skinitem(data)
        local lang = mw.language.new("en")
        local skinname     = data[1]
        local formatname   = data[2].formatname or skinname .. ' ' .. args['champion']
        local champid      = skinData[args['champion']]["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-game="lol" data-champion="' .. args['champion'] .. '" data-skin="' .. skinname .. '">[[File:' .. FN.skin({args['champion'], skinname}) .. '|340px|border]]</div><div><div style="float:left">' .. formatname
        
        if skinid ~= nil then
            standardizedname = string.lower(args['champion']:gsub("[' ]", ""))
            if standardizedname == "wukong" then
                standardizedname = "monkeyking"
            end
            teemogg_skinid = standardizedname .. '-' .. skinid
            s = s .. ' <span class="plainlinks">[https://teemo.gg/model-viewer?game=league-of-legends&type=champions&object=' .. standardizedname .. '&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>'
		s = s..k
		
        return s
    end

    function chroma(chromatable)
        s = ""
        if #chromatable > 0 then
            for i in ipairs(chromatable) do
                s = s .. '<div style="clear:both"></div>' .. SD.chromagallery{args['champion'], 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.skinpage2(frame)
    local args; if frame.args == nil then args = lib.arguments(frame) else args = lib.arguments(frame.args) end
    
    args[1] = args['champion'] or args[1] or mw.title.getCurrentTitle().rootText
    args['champion'] = lib.validateName(args[1])
    
    if skinData[args['champion']] == nil then
        return userError("Champion ''" .. args['champion'] .. "'' does not exist in Module:SkinData/data", "LuaError")
    end
    
    local t = skinData[args['champion']]["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
    
    -- generates all categories of all sets of all skins of said champion
    local k = ""
    for skinname in pairs(t) do
        k = k .. (themes.getsetcategory({args["champion"], skinname}) or '')
    end
    k = "[[Category:LoL Champion cosmetics]][[Category:"..args[1].."]]" .. k

    function skinitem(data)
        local lang = mw.language.new("en")
        local skinname     = data[1]
        local formatname   = data[2].formatname or skinname .. ' ' .. args['champion']
        local champid      = skinData[args['champion']]["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 standardizedname
        if skinid ~= nil then
            standardizedname = string.lower(args['champion']:gsub("[' ]", ""))
            if standardizedname == "wukong" then
                standardizedname = "monkeyking"
            end
            teemogg_skinid = standardizedname .. '-' .. skinid
            -- s = s .. ' <span class="plainlinks">[https://teemo.gg/model-viewer?game=league-of-legends&type=champions&object=' .. standardizedname .. '&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
        
        local s = ""
        s = s..k
        
        local v = data[2]
        local cost
        if type(v['cost']) ~= 'number' then
            cost = '<div class="skinviewer-price" title="'.. (v['distribution'] and v['distribution'] or 'This skin can only be obtained in a special way.') ..'">Special</div>'
        elseif v['cost'] == 10 then
            cost = '<div class="skinviewer-price" title="This skin is forged from Rare Gemstones in the Hextech workshop.">[[File:Rare Gem.png|20px|Gemstone]] 10</div>'
        elseif v['cost'] == 100 then
            cost = '<div class="skinviewer-price" title="This skin is purchased from the Prestige Point Shop, which is accessed in the Hextech workshop.">[[File:Hextech Crafting Prestige token.png|20px|Prestige Point]] 100</div>'
        elseif v['cost'] == 150000 then
            cost = '<div class="skinviewer-price" title="This skin is available to purchase with Blue Essence during an [[Essence Emporium]].">[[File:BE icon.png|20px|Blue Essence]] 150000)</div>' 
        else
            cost = '<div class="skinviewer-price" title="This skin is available to purchase with RP from the store.">[[File:RP icon.png|20px|RP]] '.. v["cost"] ..'</div>'
        end
        
        local champname = args['champion']
		s = s .. tostring(mw.html.create('div')
			:css('position', 'relative')
            :tag('div') -- image
                -- :css('position', 'relative')
                :css('float', 'left')
                -- :css('font-family', 'BeaufortLoL')
            	:addClass('FullWidthImage')
            	:css('width', 'calc(100% - 289px)')
            	:wikitext('[[File:' .. FN.skin({ champname, skinname }) .. ']]')
                :done()
            :tag('div')
                -- :css({
                --     ['flex'] = '2 1 0px',
                --     ['text-align'] = 'center',
                --     ['color'] = '#d3c7aa',
                --     ['font-weight'] = 700
                -- })
                :css('width', '289px')
                :css('float', 'right')
                :wikitext(formatname)
                :done()
            :tag('div')
                -- :css({
                --     ['flex'] = '1 1 0px',
                --     ['text-align'] = 'right',
                --     ['color'] = '#c9aa71',
                --     ['font-size'] = 'smaller',
                --     ['padding-right'] = '2px'
                -- })
                :css('width', '289px')
                :css('clear', 'right')
                :css('float', 'right')
                :wikitext(release)
                :done()
            :tag('div')
            	:css('clear', 'left')
            	:css('float', 'left')
            	:css('width', 'calc(100% - 289px)')
                :wikitext(v['lore'] ~= nil and ('<div style="line-height: 1.6em; text-align: center; font-size: 15px;">' .. tostring(v['lore']) .. '</div>') or nil)
                :done()
            -- :tag('div') -- overlay bar
            --     :css({
            --         -- ['position'] = 'absolute',
            --         -- ['bottom'] = 0,
            --         -- ['left'] = 0,
            --         -- ['right'] = 0,
            --         -- ['z-index'] = '100',
            --         ['display'] = 'flex',
            --         ['justify-content'] = 'space-between',
            --         ['align-items'] = 'flex-end',
            --         ['background-color'] = 'rgba(0,0,0,0.6)',
            --         ['border-top'] = '1px solid rgba(0,0,0,0.25)',
            --         ['border-left'] = '1px solid rgba(0,0,0,0.25)',
            --         ['border-right'] = '1px solid rgba(0,0,0,0.25)',
            --         ['border-radius'] = '6px 6px 0 0',
            --         ['height'] = '24px',
            --         ['line-height'] = '24px',
            --         ['font-size'] = '20px'
            --     })
            	-- :done()
            :wikitext(cost)
    		:tag('div')
    			:css('width', '289px')
    			:css('position', 'absolute')
    			:css('right', '0px')
    			:css('top', '5em')
	            :tag('div')
	                -- :css({
	                --     ['flex'] = '1 1 0px',
	                --     ['text-align'] = 'left'
	                -- })
	                :wikitext('<span class="plainlinks">[https://teemo.gg/model-viewer?game=league-of-legends&type=champions&object=' .. standardizedname .. '&skinid=' .. teemogg_skinid .. ' <span class="button" title="View in 3D" style="text-align:center; border-radius: 2px;"><b>View in 3D</b></span>]</span>')
	                :done()
	            	
	                -- local releaseSplit = mw.text.split(v['release'], '%D')
	                -- :wikitext(mw.ustring.format('%s.%s.%s', releaseSplit[3], releaseSplit[2], releaseSplit[1]))
	                -- :done()
	            -- :tag('div') -- main text
	                -- :css({
	                --     ['padding'] = '15px',
	                --     ['background-color'] = 'rgba(10, 24, 39, 0.9)',
	                --     ['font-family'] = 'BeaufortLoL',
	                --     ['font-size'] = '14px'
	                -- })
	                    
	            :tag('div')
	                -- :css({
	                --     ['display'] = 'flex',
	                --     ['justify-content'] = 'center'
	                -- })
	                :wikitext(lib.ternary(SD.getVoiceactor{champname, skinname} ~= nil, '<div style="padding-right:1em; text-align:center;">[[File:Actor.png|20px|link=]]' .. tostring(SD.getVoiceactor{champname, skinname}) .. '</div>', ''))
	                :wikitext(lib.ternary(SD.getSplashartist{champname, skinname} ~= nil, '<div style="padding-right:1em; text-align:center;">[[File:Artist.png|20px|link=]]' .. tostring(SD.getSplashartist{champname, skinname}) .. '</div>', ''))
	                :wikitext(lib.ternary(SD.getSet{champname, skinname} ~= nil, '<div style="padding-right:1em; text-align:center;">[[File:Set piece.png|20px|link=]]' .. tostring(SD.getSet{champname, skinname}) .. '</div>', ''))
	                :wikitext(v['looteligible'] == false and '<div style="padding-right:1em; text-align:center;">[[File:Loot ineligible.png|20px|link=]] Loot inelgible</div>' or '<div style="padding-right:1em; text-align:center;">[[File:Loot eligible.png|20px|link=]] Loot eligible</div>')
	                :done()
	            :tag('div')
	                :css({
	                    ['display'] = 'grid',
	                    ['justify-content'] = 'center',
	                    ['grid-template-columns'] = '1fr 1fr',
	                    -- ['padding-top'] = '5px'
	                })
	                :wikitext(v['availability'] == 'Limited' and '<div style="padding:0 1em; text-align:center;"><div>[[File:Limited skin.png|50px|link=]]</div><div>Limited Edition</div></div>' or (v['availability'] == 'Legacy' and '<div style="padding:0 1em; text-align:center;"><div>[[File:Legacy skin.png|50px|link=]]</div><div>Legacy</div></div>' or ''))
	                :wikitext(v['filter'] ~= nil and '<div style="padding:0 1em; text-align:center;"><div>[[File:Voice filter.png|50px|link=]]</div><div>Voice filter</div></div>' or nil)
	                :wikitext(v['newquotes'] ~= nil and '<div style="padding:0 1em; text-align:center;"><div>[[File:Additional quotes.png|50px|link=]]</div><div>Additional/unique quotes</div></div>' or nil)
	                :wikitext(v['newvoice'] ~= nil and '<div style="padding:0 1em; text-align:center;"><div>[[File:New voice.png|50px|link=]]</div><div>New voiceover</div></div>' or nil)
	                :wikitext(v['neweffects'] ~= nil and '<div style="padding:0 1em; text-align:center;"><div>[[File:New effects.png|50px|link=]]</div><div>New SFX/VFX</div></div>' or nil)
	                :wikitext(v['newanimations'] ~= nil and '<div style="padding:0 1em; text-align:center;"><div>[[File:New animations.png|50px|link=]]</div><div>New Animations/Recall</div></div>' or nil)
	                :wikitext(v['transforming'] ~= nil and '<div style="padding:0 1em; text-align:center;"><div>[[File:Transforming.png|50px|link=]]</div><div>Transforming</div></div>' or nil)
	                :wikitext(v['extras'] ~= nil and '<div style="padding:0 1em; text-align:center;"><div>[[File:Includes extras.png|50px|link=]]</div><div>Includes Extras</div></div>' or nil)
	                :wikitext(v['chromas'] ~= nil and '<div style="padding:0 1em; text-align:center;"><div>[[File:Chromas available.png|50px|link=]]</div><div>Chromas</div></div>' or nil)
	                :done()
	            :tag('div')
	                -- :css('text-align', 'center')
	                :wikitext(lib.ternary(SD.getVariant{champname, skinname} ~= nil, '<div>This skin is a variant of  ' .. tostring(IL.skin{champion = champname, skin = SD.getVariant{champname, skinname}, circle = "true", link = '*none*', text = SD.getVariant{champname, skinname}}) .. '. Store variants are discounted for multiple purchases.</div>', ''))
	                :done()
                :done()
            :tag('div') -- chromas
            	:css('clear', 'left')
            	:css('float', 'left')
            	:css('width', 'calc(100% - 289px)')
            	:wikitext(v['chromas'] and SD.chromagallery{champname, skinname} or '')
                :done()
            :tag('div')
            	:css('clear', 'both')
            	:done()
             :done()
        )
		
        return s
    end

    function chroma(chromatable)
        s = ""
        if #chromatable > 0 then
            for i in ipairs(chromatable) do
                s = s .. '<div style="clear:both"></div>' .. SD.chromagallery{args['champion'], 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.skinpage3(frame)
    local args; if frame.args == nil then args = lib.arguments(frame) else args = lib.arguments(frame.args) end
    
    args[1] = args['champion'] or args[1] or mw.title.getCurrentTitle().rootText
    args['champion'] = lib.validateName(args[1])
    
    if skinData[args['champion']] == nil then
        return userError("Champion ''" .. args['champion'] .. "'' does not exist in Module:SkinData/data", "LuaError")
    end
    
    local t = skinData[args['champion']]["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
    
    -- generates all categories of all sets of all skins of said champion
    local categories = "[[Category:LoL Champion cosmetics]][[Category:" .. args[1] .. "]]"
    for skinname in pairs(t) do
        categories = categories .. (themes.getsetcategory({args["champion"], skinname}) or '')
    end
	
    function skinitem(data)
        local lang = mw.language.new("en")
        local skinname     = data[1]
        local formatname   = data[2].formatname or skinname .. ' ' .. args['champion']
        local champid      = skinData[args['champion']]["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 infobox = {
			['title'] = formatname
		}
        
        local standardizedname
        if skinid ~= nil then
            standardizedname = string.lower(args['champion']:gsub("[' ]", ""))
            if standardizedname == "wukong" then
                standardizedname = "monkeyking"
            end
            teemogg_skinid = standardizedname .. '-' .. skinid
            infobox['3d'] = '<center><span class="plainlinks">[https://teemo.gg/model-viewer?game=league-of-legends&type=champions&object=' .. standardizedname .. '&skinid=' .. teemogg_skinid .. ' <span class="button" title="View in 3D" style="text-align:center; border-radius: 2px;"><b>View in 3D</b></span>]</span></center>'
        end
        
        if cost == 'N/A' then
            -- skip
        elseif cost == 150000 then
            infobox['cost'] = tostring(IL.basic{["link"] = "Blue Essence", ["text"] = cost, ["alttext"] = cost .. " Blue Essence", ["image"] = "BE icon.png", ["border"] = "false", ["labellink"] = "false"})
        elseif cost == 100 then
            infobox['cost'] = 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
            infobox['cost'] = tostring(IL.basic{["link"] = "Gemstone", ["text"] = cost, ["alttext"] = cost .. " Rare Gems", ["image"] = "Rare Gem.png", ["border"] = "false", ["labellink"] = "false"})
        elseif cost == "special" then
            infobox['cost'] = "Special pricing"
        else
            infobox['cost'] = tostring(IL.basic{["link"] = "Riot Points", ["text"] = cost, ["alttext"] = cost .. " RP", ["image"] = "RP icon.png", ["border"] = "false", ["labellink"] = "false"})
        end
        
        infobox['release'] = release
        
        local champname = args['champion']
        
        if SD.getVoiceactor{champname, skinname} ~= nil then
		    infobox['voiceactor'] = tostring(SD.getVoiceactor{champname, skinname})
		end
	    if SD.getSplashartist{champname, skinname} ~= nil then
		    infobox['splashartist'] = tostring(SD.getSplashartist{champname, skinname})
	    end
	    if SD.getSet{champname, skinname} ~= nil then
		    infobox['set'] = tostring(SD.getSet{champname, skinname})
		end
        if SD.getVariant{champname, skinname} ~= nil then
            infobox['variant'] = 'This skin is a variant of  ' .. tostring(IL.skin{champion = champname, skin = SD.getVariant{champname, skinname}, circle = "true", link = '*none*', text = SD.getVariant{champname, skinname}}) .. '. Store variants are discounted for multiple purchases.'
        end
        
		local s = ""
		
        local v = data[2]
        -- local cost
        -- if type(v['cost']) ~= 'number' then
        --     cost = '<div class="skinviewer-price" title="'.. (v['distribution'] and v['distribution'] or 'This skin can only be obtained in a special way.') ..'">Special</div>'
        -- elseif v['cost'] == 10 then
        --     cost = '<div class="skinviewer-price" title="This skin is forged from Rare Gemstones in the Hextech workshop.">[[File:Rare Gem.png|20px|Gemstone]] 10</div>'
        -- elseif v['cost'] == 100 then
        --     cost = '<div class="skinviewer-price" title="This skin is purchased from the Prestige Point Shop, which is accessed in the Hextech workshop.">[[File:Hextech Crafting Prestige token.png|20px|Prestige Point]] 100</div>'
        -- elseif v['cost'] == 150000 then
        --     cost = '<div class="skinviewer-price" title="This skin is available to purchase with Blue Essence during an [[Essence Emporium]].">[[File:BE icon.png|20px|Blue Essence]] 150000)</div>' 
        -- else
        --     cost = '<div class="skinviewer-price" title="This skin is available to purchase with RP from the store.">[[File:RP icon.png|20px|RP]] '.. v["cost"] ..'</div>'
        -- end
        infobox['loot'] = v['looteligible'] ~= false and 'true' or 'false'
        local attributes = ''
        if v['availability'] == 'Limited' then
        	attributes = attributes .. '<div style="flex: 1 0 100px; text-align:center;"><span style="display:block">[[File:Limited skin.png|50px|link=]]</span><span style="display:block">Limited Edition</span></div>'
	    elseif v['availability'] == 'Legacy' then
	    	attributes = attributes .. '<div style="flex: 1 0 100px; text-align:center;"><span style="display:block">[[File:Legacy skin.png|50px|link=]]</span><span style="display:block">Legacy</span></div>'
    	end
        if v['filter'] ~= nil then
        	attributes = attributes .. '<div style="flex: 1 0 100px; text-align:center;"><span style="display:block">[[File:Voice filter.png|50px|link=]]</span><span style="display:block">Voice filter</span></div>'
        end
        if v['newquotes'] ~= nil then
        	attributes = attributes .. v['newquotes'] ~= nil and '<div style="flex: 1 0 100px; text-align:center;"><span style="display:block">[[File:Additional quotes.png|50px|link=]]</span><span style="display:block">Additional/unique quotes</span></div>'
    	end
    	if v['newvoice'] ~= nil then
        	attributes = attributes .. '<div style="flex: 1 0 110px; text-align:center;"><span style="display:block">[[File:New voice.png|50px|link=]]</span><span style="display:block">New voiceover</span></div>'
        end
        if v['neweffects'] ~= nil then
        	attributes = attributes .. '<div style="flex: 1 0 110px; text-align:center;"><span style="display:block">[[File:New effects.png|50px|link=]]</span><span style="display:block">New SFX/VFX</span></div>'
        end
        if v['newanimations'] ~= nil then
        	attributes = attributes .. '<div style="flex: 1 0 110px; text-align:center;"><span style="display:block">[[File:New animations.png|50px|link=]]</span><span style="display:block">New Animations/Recall</span></div>'
    	end
    	if v['transforming'] ~= nil then
    		attributes = attributes .. '<div style="flex: 1 0 110px; text-align:center;"><span style="display:block">[[File:Transforming.png|50px|link=]]</span><span style="display:block">Transforming</span></span>'
    	end
    	if v['extras'] ~= nil then
        	attributes = attributes .. '<div style="flex: 1 0 110px; text-align:center;"><span style="display:block">[[File:Includes extras.png|50px|link=]]</span><span style="display:block">Includes Extras</span></div>'
        end
        if v['chromas'] ~= nil then
        	attributes = attributes .. '<div style="flex: 1 0 110px; text-align:center;"><span style="display:block">[[File:Chromas available.png|50px|link=]]</span><span style="display:block">Chromas</span></div>'
        end
        if attributes ~= '' then
	        infobox['attributes'] = tostring(mw.html.create('div')
	            :css({
	                ['display'] = 'flex',
	                ['flex-flow'] = 'row wrap',
	                ['justify-content'] = 'center',
	                -- ['grid-template-columns'] = '1fr 1fr',
	                -- ['padding-top'] = '5px',
	                -- ['width'] = '248px',
	
	            })
	            -- :wikitext(v['looteligible'] == false and '<div style="padding:0 1em; text-align:center;"><div>[[File:Loot ineligible.png|50px|link=]]</div><div>Loot inelgible</div></div>' or '<div style="padding-right:1em; text-align:center;"><div>[[File:Loot eligible.png|50px|link=]]</div><div>Loot eligible</div></div>')
	            :wikitext(attributes)
	            :done()
	        )
	    end
        s = s .. tostring(mw.html.create('')
        	:tag('h3')
        		:wikitext(formatname)
        		:done()
        	:tag('div') -- image
	            -- :css('position', 'relative')
	            :css('float', 'left')
	            -- :css('font-family', 'BeaufortLoL')
	        	:addClass('FullWidthImage')
	        	:css('width', 'calc(100% - 289px)')
	        	:wikitext('[[File:' .. FN.skin({ champname, skinname }) .. ']]')
	            :done())
        s = s .. '<div>' .. mw.text.trim(frame:expandTemplate{ title = 'User:DutyS12345/Sandbox6', args = infobox}) .. '</div>'
		s = s .. tostring(mw.html.create('')
            :tag('div')
            	:css('clear', 'left')
            	:css('float', 'left')
            	:css('width', 'calc(100% - 289px)')
                :wikitext(v['lore'] ~= nil and ('<p style="line-height: 1.6em; text-align: center; font-size: 15px;">' .. tostring(v['lore']) .. '</p>') or nil)
                :done()
            -- :tag('div') -- overlay bar
            --     :css({
            --         -- ['position'] = 'absolute',
            --         -- ['bottom'] = 0,
            --         -- ['left'] = 0,
            --         -- ['right'] = 0,
            --         -- ['z-index'] = '100',
            --         ['display'] = 'flex',
            --         ['justify-content'] = 'space-between',
            --         ['align-items'] = 'flex-end',
            --         ['background-color'] = 'rgba(0,0,0,0.6)',
            --         ['border-top'] = '1px solid rgba(0,0,0,0.25)',
            --         ['border-left'] = '1px solid rgba(0,0,0,0.25)',
            --         ['border-right'] = '1px solid rgba(0,0,0,0.25)',
            --         ['border-radius'] = '6px 6px 0 0',
            --         ['height'] = '24px',
            --         ['line-height'] = '24px',
            --         ['font-size'] = '20px'
            --     })
            	-- :done()
        )
    	-- s = s .. tostring(mw.html.create('')
    	-- 	:tag('div')
    	-- 		:css('width', '289px')
    	-- 		:css('float', 'right')
    	-- 		:css('clear', 'right')
	    --         :tag('div')
	    --             :css({
	    --                 ['display'] = 'grid',
	    --                 ['justify-content'] = 'center',
	    --                 ['grid-template-columns'] = '1fr 1fr',
	    --                 -- ['padding-top'] = '5px'
	    --             })
	    --             -- :wikitext(v['looteligible'] == false and '<div style="padding:0 1em; text-align:center;"><div>[[File:Loot ineligible.png|50px|link=]]</div><div>Loot inelgible</div></div>' or '<div style="padding-right:1em; text-align:center;"><div>[[File:Loot eligible.png|50px|link=]]</div><div>Loot eligible</div></div>')
	    --             :wikitext(v['availability'] == 'Limited' and '<div style="padding:0 1em; text-align:center;"><div>[[File:Limited skin.png|50px|link=]]</div><div>Limited Edition</div></div>' or (v['availability'] == 'Legacy' and '<div style="padding:0 1em; text-align:center;"><div>[[File:Legacy skin.png|50px|link=]]</div><div>Legacy</div></div>' or ''))
	    --             :wikitext(v['filter'] ~= nil and '<div style="padding:0 1em; text-align:center;"><div>[[File:Voice filter.png|50px|link=]]</div><div>Voice filter</div></div>' or nil)
	    --             :wikitext(v['newquotes'] ~= nil and '<div style="padding:0 1em; text-align:center;"><div>[[File:Additional quotes.png|50px|link=]]</div><div>Additional/unique quotes</div></div>' or nil)
	    --             :wikitext(v['newvoice'] ~= nil and '<div style="padding:0 1em; text-align:center;"><div>[[File:New voice.png|50px|link=]]</div><div>New voiceover</div></div>' or nil)
	    --             :wikitext(v['neweffects'] ~= nil and '<div style="padding:0 1em; text-align:center;"><div>[[File:New effects.png|50px|link=]]</div><div>New SFX/VFX</div></div>' or nil)
	    --             :wikitext(v['newanimations'] ~= nil and '<div style="padding:0 1em; text-align:center;"><div>[[File:New animations.png|50px|link=]]</div><div>New Animations/Recall</div></div>' or nil)
	    --             :wikitext(v['transforming'] ~= nil and '<div style="padding:0 1em; text-align:center;"><div>[[File:Transforming.png|50px|link=]]</div><div>Transforming</div></div>' or nil)
	    --             :wikitext(v['extras'] ~= nil and '<div style="padding:0 1em; text-align:center;"><div>[[File:Includes extras.png|50px|link=]]</div><div>Includes Extras</div></div>' or nil)
	    --             :wikitext(v['chromas'] ~= nil and '<div style="padding:0 1em; text-align:center;"><div>[[File:Chromas available.png|50px|link=]]</div><div>Chromas</div></div>' or nil)
	    --             :done()
     --           :done()
     --   )
        s = s .. tostring(mw.html.create('')
			:tag('div') -- chromas
				:css('clear', 'both')
            	-- :css('clear', 'left')
            	:css('float', 'left')
            	-- :css('width', 'calc(100% - 289px)')
            	:wikitext(v['chromas'] and p.chromagallery2{champname, skinname, formatname} or '')
                :done()
            :tag('div')
            	:css('clear', 'both')
            	:done()
        )
        return s
    end

    function chroma(chromatable)
        s = ""
        if #chromatable > 0 then
            for i in ipairs(chromatable) do
                s = s .. '<div style="clear:both"></div>' .. SD.chromagallery{args['champion'], 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 = ''
    s = s .. categories
    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.skinpage4(frame)
    local args; if frame.args == nil then args = lib.arguments(frame) else args = lib.arguments(frame.args) end
    
    args[1] = args['champion'] or args[1] or mw.title.getCurrentTitle().rootText
    args['champion'] = lib.validateName(args[1])
    
    if skinData[args['champion']] == nil then
        return userError("Champion ''" .. args['champion'] .. "'' does not exist in Module:SkinData/data", "LuaError")
    end
    
    local t = skinData[args['champion']]["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
    
    -- generates all categories of all sets of all skins of said champion
    local categories = "[[Category:LoL Champion cosmetics]][[Category:" .. args[1] .. "]]"
    for skinname in pairs(t) do
        categories = categories .. (themes.getsetcategory({args["champion"], skinname}) or '')
    end

    function skinitem(data)
        local lang = mw.language.new("en")
        local skinname     = data[1]
        local formatname   = data[2].formatname or skinname .. ' ' .. args['champion']
        local champid      = skinData[args['champion']]["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 standardizedname
        if skinid ~= nil then
            standardizedname = string.lower(args['champion']:gsub("[' ]", ""))
            if standardizedname == "wukong" then
                standardizedname = "monkeyking"
            end
            teemogg_skinid = standardizedname .. '-' .. skinid
            -- s = s .. ' <span class="plainlinks">[https://teemo.gg/model-viewer?game=league-of-legends&type=champions&object=' .. standardizedname .. '&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
        
        local s = ""
        
        
        local v = data[2]
        -- local cost
        -- if type(v['cost']) ~= 'number' then
        --     cost = '<div class="skinviewer-price" title="'.. (v['distribution'] and v['distribution'] or 'This skin can only be obtained in a special way.') ..'">Special</div>'
        -- elseif v['cost'] == 10 then
        --     cost = '<div class="skinviewer-price" title="This skin is forged from Rare Gemstones in the Hextech workshop.">[[File:Rare Gem.png|20px|Gemstone]] 10</div>'
        -- elseif v['cost'] == 100 then
        --     cost = '<div class="skinviewer-price" title="This skin is purchased from the Prestige Point Shop, which is accessed in the Hextech workshop.">[[File:Hextech Crafting Prestige token.png|20px|Prestige Point]] 100</div>'
        -- elseif v['cost'] == 150000 then
        --     cost = '<div class="skinviewer-price" title="This skin is available to purchase with Blue Essence during an [[Essence Emporium]].">[[File:BE icon.png|20px|Blue Essence]] 150000)</div>' 
        -- else
        --     cost = '<div class="skinviewer-price" title="This skin is available to purchase with RP from the store.">[[File:RP icon.png|20px|RP]] '.. v["cost"] ..'</div>'
        -- end
        
        local champname = args['champion']
		s = s .. tostring(mw.html.create('div')
			:wikitext(formatname)
            :tag('div') -- image
                -- :css('position', 'relative')
                -- :css('float', 'left')
                -- :css('font-family', 'BeaufortLoL')
            	:addClass('FullWidthImage')
            	-- :css('width', 'calc(100% - 289px)')
            	:wikitext('[[File:' .. FN.skin({ champname, skinname }) .. ']]')
                :done()
            
            :tag('div')
                -- :css({
                --     ['flex'] = '1 1 0px',
                --     ['text-align'] = 'right',
                --     ['color'] = '#c9aa71',
                --     ['font-size'] = 'smaller',
                --     ['padding-right'] = '2px'
                -- })
                -- :css('width', '289px')
                -- :css('clear', 'right')
                -- :css('float', 'right')
                :wikitext(release)
                :done()
            :tag('div')
            	-- :css('clear', 'left')
            	-- :css('float', 'left')
            	-- :css('width', 'calc(100% - 289px)')
                :wikitext(v['lore'] ~= nil and ('<div style="line-height: 1.6em; text-align: center; font-size: 15px;">' .. tostring(v['lore']) .. '</div>') or nil)
                :done()
            -- :tag('div') -- overlay bar
            --     :css({
            --         -- ['position'] = 'absolute',
            --         -- ['bottom'] = 0,
            --         -- ['left'] = 0,
            --         -- ['right'] = 0,
            --         -- ['z-index'] = '100',
            --         ['display'] = 'flex',
            --         ['justify-content'] = 'space-between',
            --         ['align-items'] = 'flex-end',
            --         ['background-color'] = 'rgba(0,0,0,0.6)',
            --         ['border-top'] = '1px solid rgba(0,0,0,0.25)',
            --         ['border-left'] = '1px solid rgba(0,0,0,0.25)',
            --         ['border-right'] = '1px solid rgba(0,0,0,0.25)',
            --         ['border-radius'] = '6px 6px 0 0',
            --         ['height'] = '24px',
            --         ['line-height'] = '24px',
            --         ['font-size'] = '20px'
            --     })
            	-- :done()
	            :wikitext(cost)
    		:tag('div')
    			-- :css('width', '289px')
    			-- :css('position', 'absolute')
    			-- :css('right', '0px')
    			-- :css('top', '5em')
	            :tag('div')
	                -- :css({
	                --     ['flex'] = '1 1 0px',
	                --     ['text-align'] = 'left'
	                -- })
	                :wikitext('<span class="plainlinks">[https://teemo.gg/model-viewer?game=league-of-legends&type=champions&object=' .. standardizedname .. '&skinid=' .. teemogg_skinid .. ' <span class="button" title="View in 3D" style="text-align:center; border-radius: 2px;"><b>View in 3D</b></span>]</span>')
	                :done()
	            	
	                -- local releaseSplit = mw.text.split(v['release'], '%D')
	                -- :wikitext(mw.ustring.format('%s.%s.%s', releaseSplit[3], releaseSplit[2], releaseSplit[1]))
	                -- :done()
	            -- :tag('div') -- main text
	                -- :css({
	                --     ['padding'] = '15px',
	                --     ['background-color'] = 'rgba(10, 24, 39, 0.9)',
	                --     ['font-family'] = 'BeaufortLoL',
	                --     ['font-size'] = '14px'
	                -- })
	                    
	            :tag('div')
	                :css({
	                    ['display'] = 'flex',
	                    ['justify-content'] = 'center'
	                })
	                :wikitext(lib.ternary(SD.getVoiceactor{champname, skinname} ~= nil, '<div style="padding-right:1em; text-align:center;">[[File:Actor.png|20px|link=]]' .. tostring(SD.getVoiceactor{champname, skinname}) .. '</div>', ''))
	                :wikitext(lib.ternary(SD.getSplashartist{champname, skinname} ~= nil, '<div style="padding-right:1em; text-align:center;">[[File:Artist.png|20px|link=]]' .. tostring(SD.getSplashartist{champname, skinname}) .. '</div>', ''))
	                :wikitext(lib.ternary(SD.getSet{champname, skinname} ~= nil, '<div style="padding-right:1em; text-align:center;">[[File:Set piece.png|20px|link=]]' .. tostring(SD.getSet{champname, skinname}) .. '</div>', ''))
	                :wikitext(v['looteligible'] == false and '<div style="padding-right:1em; text-align:center;">[[File:Loot ineligible.png|20px|link=]] Loot inelgible</div>' or '<div style="padding-right:1em; text-align:center;">[[File:Loot eligible.png|20px|link=]] Loot eligible</div>')
	                :done()
	            :tag('div')
	                :css({
	                    ['display'] = 'flex',
	                    ['justify-content'] = 'center',
	                    -- ['padding-top'] = '5px'
	                })
	                :wikitext(v['availability'] == 'Limited' and '<div style="padding:0 1em; text-align:center;"><div>[[File:Limited skin.png|50px|link=]]</div><div>Limited Edition</div></div>' or (v['availability'] == 'Legacy' and '<div style="padding:0 1em; text-align:center;"><div>[[File:Legacy skin.png|50px|link=]]</div><div>Legacy</div></div>' or ''))
	                :wikitext(v['filter'] ~= nil and '<div style="padding:0 1em; text-align:center;"><div>[[File:Voice filter.png|50px|link=]]</div><div>Voice filter</div></div>' or nil)
	                :wikitext(v['newquotes'] ~= nil and '<div style="padding:0 1em; text-align:center;"><div>[[File:Additional quotes.png|50px|link=]]</div><div>Additional/unique quotes</div></div>' or nil)
	                :wikitext(v['newvoice'] ~= nil and '<div style="padding:0 1em; text-align:center;"><div>[[File:New voice.png|50px|link=]]</div><div>New voiceover</div></div>' or nil)
	                :wikitext(v['neweffects'] ~= nil and '<div style="padding:0 1em; text-align:center;"><div>[[File:New effects.png|50px|link=]]</div><div>New SFX/VFX</div></div>' or nil)
	                :wikitext(v['newanimations'] ~= nil and '<div style="padding:0 1em; text-align:center;"><div>[[File:New animations.png|50px|link=]]</div><div>New Animations/Recall</div></div>' or nil)
	                :wikitext(v['transforming'] ~= nil and '<div style="padding:0 1em; text-align:center;"><div>[[File:Transforming.png|50px|link=]]</div><div>Transforming</div></div>' or nil)
	                :wikitext(v['extras'] ~= nil and '<div style="padding:0 1em; text-align:center;"><div>[[File:Includes extras.png|50px|link=]]</div><div>Includes Extras</div></div>' or nil)
	                :wikitext(v['chromas'] ~= nil and '<div style="padding:0 1em; text-align:center;"><div>[[File:Chromas available.png|50px|link=]]</div><div>Chromas</div></div>' or nil)
	                :done()
	            :tag('div')
	                :css('text-align', 'center')
	                :wikitext(lib.ternary(SD.getVariant{champname, skinname} ~= nil, '<div>This skin is a variant of  ' .. tostring(IL.skin{champion = champname, skin = SD.getVariant{champname, skinname}, circle = "true", link = '*none*', text = SD.getVariant{champname, skinname}}) .. '. Store variants are discounted for multiple purchases.</div>', ''))
	                :done()
                :done()
            :tag('div') -- chromas
            	-- :css('clear', 'left')
            	-- :css('float', 'left')
            	-- :css('width', 'calc(100% - 289px)')
            	:wikitext(v['chromas'] and SD.chromagallery{champname, skinname} or '')
                :done()
            :tag('div')
            	:css('clear', 'both')
            	:done()
             
        )
		
        return s
    end

    function chroma(chromatable)
        s = ""
        if #chromatable > 0 then
            for i in ipairs(chromatable) do
                s = s .. '<div style="clear:both"></div>' .. SD.chromagallery{args['champion'], 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 = ''
    s = s .. categories
    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.skinpage5(frame)
    local args; if frame.args == nil then args = lib.arguments(frame) else args = lib.arguments(frame.args) end
    
    args[1] = args['champion'] or args[1] or mw.title.getCurrentTitle().rootText
    args['champion'] = lib.validateName(args[1])
    
    if skinData[args['champion']] == nil then
        return userError("Champion ''" .. args['champion'] .. "'' does not exist in Module:SkinData/data", "LuaError")
    end
    
    local t = skinData[args['champion']]["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
    
    -- generates all categories of all sets of all skins of said champion
    local k = ""
    for skinname in pairs(t) do
        k = k..(themes.getsetcategory({args["champion"], skinname}) or '')
    end
    k = "[[Category:LoL Champion cosmetics]][[Category:"..args[1].."]]"..k

    function skinitem(data)
        local lang = mw.language.new("en")
        local skinname     = data[1]
        local formatname   = data[2].formatname or skinname .. ' ' .. args['champion']
        local champid      = skinData[args['champion']]["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-game="lol" data-champion="' .. args['champion'] .. '" data-skin="' .. skinname .. '">[[File:' .. FN.skin({args['champion'], skinname}) .. '|340px|border]]</div><div><div style="float:left">' .. formatname
        local standardizedname
        if skinid ~= nil then
            standardizedname = string.lower(args['champion']:gsub("[' ]", ""))
            if standardizedname == "wukong" then
                standardizedname = "monkeyking"
            end
            teemogg_skinid = standardizedname .. '-' .. skinid
            -- s = s .. ' <span class="plainlinks">[https://teemo.gg/model-viewer?game=league-of-legends&type=champions&object=' .. standardizedname .. '&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>'
		s = s..k
		local champname = args['champion']
		local v = data[2]
		  local cost
        if type(v['cost']) ~= 'number' then
            cost = '<div class="skinviewer-price" title="'.. (v['distribution'] and v['distribution'] or 'This skin can only be obtained in a special way.') ..'">Special</div>'
        elseif v['cost'] == 10 then
            cost = '<div class="skinviewer-price" title="This skin is forged from Rare Gemstones in the Hextech workshop.">[[File:Rare Gem.png|20px|Gemstone]] 10</div>'
        elseif v['cost'] == 100 then
            cost = '<div class="skinviewer-price" title="This skin is purchased from the Prestige Point Shop, which is accessed in the Hextech workshop.">[[File:Hextech Crafting Prestige token.png|20px|Prestige Point]] 100</div>'
        elseif v['cost'] == 150000 then
            cost = '<div class="skinviewer-price" title="This skin is available to purchase with Blue Essence during an [[Essence Emporium]].">[[File:BE icon.png|20px|Blue Essence]] 150000)</div>' 
        else
            cost = '<div class="skinviewer-price" title="This skin is available to purchase with RP from the store.">[[File:RP icon.png|20px|RP]] '.. v["cost"] ..'</div>'
        end
		s = s .. tostring(mw.html.create('div')
            -- :addClass('skinviewer-tab-content')
            -- :addClass(i==1 and 'skinviewer-active-tab' or '')
            -- :attr('id', 'item-'..i)
            -- :css('display', i~=1 and 'none' or 'block')
            :tag('div')
                -- :addClass('skinviewer-tab-skin')
                :css('position', 'relative')
                :css('font-family', 'BeaufortLoL')
                :wikitext('<div class="FullWidthImage">[[File:' .. FN.skin({ champname, skinname }) .. ']]</div>')
                :wikitext(cost)
                :tag('div')
                    :css({
                        ['position'] = 'absolute',
                        ['bottom'] = 0,
                        ['left'] = 0,
                        ['right'] = 0,
                        ['z-index'] = '100',
                        ['display'] = 'flex',
                        ['justify-content'] = 'space-between',
                        ['align-items'] = 'flex-end',
                        ['background-color'] = 'rgba(0,0,0,0.6)',
                        ['border-top'] = '1px solid rgba(0,0,0,0.25)',
                        ['border-left'] = '1px solid rgba(0,0,0,0.25)',
                        ['border-right'] = '1px solid rgba(0,0,0,0.25)',
                        ['border-radius'] = '6px 6px 0 0',
                        ['height'] = '24px',
                        ['line-height'] = '24px',
                        ['font-size'] = '20px'
                    })
                    :tag('div')
                        :css({
                            ['flex'] = '1 1 0px',
                            ['text-align'] = 'left'
                        })
                        :wikitext('<span class="plainlinks">[https://teemo.gg/model-viewer?game=league-of-legends&type=champions&object=' .. standardizedname .. '&skinid=' .. teemogg_skinid .. ' <span class="button" title="View in 3D" style="text-align:center; border-radius: 2px;"><b>View in 3D</b></span>]</span>')
                        :done()
                    :tag('div')
                        :css({
                            ['flex'] = '2 1 0px',
                            ['text-align'] = 'center',
                            ['color'] = '#d3c7aa',
                            ['font-weight'] = 700
                        })
                        :wikitext(formatname)
                        :done()
                    :tag('div')
                        :css({
                            ['flex'] = '1 1 0px',
                            ['text-align'] = 'right',
                            ['color'] = '#c9aa71',
                            ['font-size'] = 'smaller',
                            ['padding-right'] = '2px'
                        })
                        :wikitext(release)
                        -- :wikitext(mw.ustring.format('%s.%s.%s', releaseSplit[3], releaseSplit[2], releaseSplit[1]))
                        :done()
                    :done()
                :done()
            :tag('div')
                :addClass('skinviewer-info')
                :css({
                    ['padding'] = '15px',
                    ['background-color'] = 'rgba(10, 24, 39, 0.9)',
                    ['font-family'] = 'BeaufortLoL',
                    ['font-size'] = '14px'
                })
                    :tag('div')
                        -- :addClass('skinviewer-info-lore')
                        :wikitext(lib.ternary(v['lore'] ~= nil, '<div style="line-height: 1.6em; text-align: center; font-size: 15px;">' .. tostring(v['lore']) .. '</div>', ''))
                        :done()
                    :tag('div')
                        -- :addClass('skinviewer-info-kleine-bilder')
                        :css({
                            ['display'] = 'flex',
                            ['justify-content'] = 'center'
                        })
                        :wikitext(lib.ternary(SD.getVoiceactor{champname, skinname} ~= nil, '<div style="padding-right:1em; text-align:center;">[[File:Actor.png|20px|link=]]' .. tostring(SD.getVoiceactor{champname, skinname}) .. '</div>', ''))
                        :wikitext(lib.ternary(SD.getSplashartist{champname, skinname} ~= nil, '<div style="padding-right:1em; text-align:center;">[[File:Artist.png|20px|link=]]' .. tostring(SD.getSplashartist{champname, skinname}) .. '</div>', ''))
                        :wikitext(lib.ternary(SD.getSet{champname, skinname} ~= nil, '<div style="padding-right:1em; text-align:center;">[[File:Set piece.png|20px|link=]]' .. tostring(SD.getSet{champname, skinname}) .. '</div>', ''))
                        :wikitext(lib.ternary(v['looteligible'] ~= false, '<div style="padding-right:1em; text-align:center;">[[File:Loot eligible.png|20px|link=]] Loot eligible</div>', ''))
                        :wikitext(lib.ternary(v['looteligible'] == false, '<div style="padding-right:1em; text-align:center;">[[File:Loot ineligible.png|20px|link=]] Loot inelgible</div>', ''))
                        :done()
                    :tag('div')
                        -- :addClass('skinviewer-info-grosse-bilder')
                        :css({
                            ['display'] = 'flex',
                            ['justify-content'] = 'center',
                            ['padding-top'] = '5px'
                        })
                        :wikitext(v['availability'] == 'Limitiert' and '<div style="padding:0 1em; text-align:center;"><div>[[File:Limited skin.png|50px|link=]]</div><div>Limited Edition</div></div>' or (v['availability'] == 'Legacy' and '<div style="padding:0 1em; text-align:center;"><div>[[File:Legacy skin.png|50px|link=]]</div><div>Klassisch</div></div>' or ''))
                        :wikitext(lib.ternary(v['filter'] ~= nil, '<div style="padding:0 1em; text-align:center;"><div>[[File:Voice filter.png|50px|link=]]</div><div>Voice filter</div></div>', ''))
                        :wikitext(lib.ternary(v['newquotes'] ~= nil, '<div style="padding:0 1em; text-align:center;"><div>[[File:Additional quotes.png|50px|link=]]</div><div>Additional/unique quotes</div></div>', ''))
                        :wikitext(lib.ternary(v['newvoice'] ~= nil, '<div style="padding:0 1em; text-align:center;"><div>[[File:New voice.png|50px|link=]]</div><div>New voiceover</div></div>', ''))
                        :wikitext(lib.ternary(v['neweffects'] ~= nil, '<div style="padding:0 1em; text-align:center;"><div>[[File:New effects.png|50px|link=]]</div><div>New SFX/VFX</div></div>', ''))
                        :wikitext(lib.ternary(v['newanimations'] ~= nil, '<div style="padding:0 1em; text-align:center;"><div>[[File:New animations.png|50px|link=]]</div><div>New Animations/Recall</div></div>', ''))
                        :wikitext(lib.ternary(v['transforming'] ~= nil, '<div style="padding:0 1em; text-align:center;"><div>[[File:Transforming.png|50px|link=]]</div><div>Transforming</div></div>', ''))
                        :wikitext(lib.ternary(v['extras'] ~= nil, '<div style="padding:0 1em; text-align:center;"><div>[[File:Includes extras.png|50px|link=]]</div><div>Includes Extras</div></div>', ''))
                        :wikitext(lib.ternary(v['chromas'] ~= nil, '<div style="padding:0 1em; text-align:center;"><div>[[File:Chromas available.png|50px|link=]]</div><div>Chromas</div></div>', ''))
                        :done()
                    :tag('div')
                        -- :addClass('skinviewer-info-variantof')
                        :css('text-align', 'center')
                        :wikitext(lib.ternary(SD.getVariant{champname, skinname} ~= nil, '<div>This skin is a variant of  ' .. tostring(IL.skin{champion = champname, skin = SD.getVariant{champname, skinname}, circle = "true", link = '*none*', text = SD.getVariant{champname, skinname}}) .. '. Store variants are discounted for multiple purchases.</div>', ''))
                        :done()
                :done()
            :tag('div')
                -- :addClass('skinviewer-tab-chroma')
                :wikitext(v['chromas'] and SD.chromagallery{champname, skinname} or '')
                :allDone()
        )
        return s
    end

    function chroma(chromatable)
        s = ""
        if #chromatable > 0 then
            for i in ipairs(chromatable) do
                s = s .. '<div style="clear:both"></div>' .. SD.chromagallery{args['champion'], 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.skintabber(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] or mw.title.getCurrentTitle().rootText)
    if skinData[champname] == nil then
        return userError("Champion ''" .. champname .. "'' does not exist in the Module:SkinData/data", "LuaError")
    end
    local t = skinData[champname]["skins"]
    local standardizedname = string.lower(champname:gsub("[' ]", ""))
        if standardizedname == "wukong" then
            standardizedname = "monkeyking"
        end
    
    local container = mw.html.create('div'):addClass('lazyimg-wrapper')
    local navContainer = mw.html.create('div')
        :addClass('skinviewer-nav')
        :addClass('hidden')
        :css({
            ['display'] = 'flex',
            ['justify-content'] = 'center',
            ['margin-bottom'] = '10px',
        })
    local tabContainer = mw.html.create('div'):addClass('skinviewer-tab-container')
    
    for k,v,i in skinIter(t) do
        local releaseSplit = mw.text.split(v['release'], '%D')
        local cost
        if type(v['cost']) ~= 'number' then
            cost = '<div class="skinviewer-price" title="'.. (v['distribution'] and v['distribution'] or 'This skin can only be obtained in a special way.') ..'">Special</div>'
        elseif v['cost'] == 10 then
            cost = '<div class="skinviewer-price" title="This skin is forged from Rare Gemstones in the Hextech workshop.">[[File:Rare Gem.png|20px|Gemstone]] 10</div>'
        elseif v['cost'] == 100 then
            cost = '<div class="skinviewer-price" title="This skin is purchased from the Prestige Point Shop, which is accessed in the Hextech workshop.">[[File:Hextech Crafting Prestige token.png|20px|Prestige Point]] 100</div>'
        elseif v['cost'] == 150000 then
            cost = '<div class="skinviewer-price" title="This skin is available to purchase with Blue Essence during an [[Essence Emporium]].">[[File:BE icon.png|20px|Blue Essence]] 150000)</div>' 
        else
            cost = '<div class="skinviewer-price" title="This skin is available to purchase with RP from the store.">[[File:RP icon.png|20px|RP]] '.. v["cost"] ..'</div>'
        end
        
        navContainer
            :tag('span')
                :attr('id', i)
                :addClass('show')
                :css({
                    ['position'] = 'relative',
                    ['width'] = '52px',
                    ['height'] = '52px'
                })
                :wikitext(v['chromas'] and '[[File:Chromas available.png|x52px||link=]]' or '')
                :tag('span')
                    :css({
                        ['position'] = 'absolute',
                        ['top'] = 0,
                        ['left'] = 0,
                        ['right'] = 0,
                        ['bottom'] = 0,
                        ['z-index'] = 100,
                        ['padding'] = '2px',
                        ['cursor'] = 'pointer'
                    })
                    :attr('title', k)
                    :wikitext('[[File:' .. FN.championcircle({champname, k}) .. '|x48px|link=' .. ']]')
                    :done()
                :done()
        tabContainer
            :tag('div')
                :addClass('skinviewer-tab-content')
                :addClass(i==1 and 'skinviewer-active-tab' or '')
                :attr('id', 'item-'..i)
                :css('display', i~=1 and 'none' or 'block')
                :tag('div')
                    :addClass('skinviewer-tab-skin')
                    :css('position', 'relative')
                    :css('font-family', 'BeaufortLoL')
                    :wikitext('<div class="FullWidthImage">[[File:' .. FN.skin({ champname, k }) .. ']]</div>')
                    :wikitext(cost)
                    :tag('div')
                        :css({
                            ['position'] = 'absolute',
                            ['bottom'] = 0,
                            ['left'] = 0,
                            ['right'] = 0,
                            ['z-index'] = '100',
                            ['display'] = 'flex',
                            ['justify-content'] = 'space-between',
                            ['align-items'] = 'flex-end',
                            ['background-color'] = 'rgba(0,0,0,0.6)',
                            ['border-top'] = '1px solid rgba(0,0,0,0.25)',
                            ['border-left'] = '1px solid rgba(0,0,0,0.25)',
                            ['border-right'] = '1px solid rgba(0,0,0,0.25)',
                            ['border-radius'] = '6px 6px 0 0',
                            ['height'] = '24px',
                            ['line-height'] = '24px',
                            ['font-size'] = '20px'
                        })
                        :tag('div')
                            :css({
                                ['flex'] = '1 1 0px',
                                ['text-align'] = 'left'
                            })
                            :wikitext('<span class="plainlinks">[https://teemo.gg/model-viewer?game=league-of-legends&type=champions&object=' .. standardizedname ..'-'.. (v['id'] or 0) ..' <span class="button" title="View in 3D" style="text-align:center; border-radius: 2px;"><b>View in 3D</b></span>]</span>')
                            :done()
                        :tag('div')
                            :css({
                                ['flex'] = '2 1 0px',
                                ['text-align'] = 'center',
                                ['color'] = '#d3c7aa',
                                ['font-weight'] = 700
                            })
                            :wikitext(k)
                            :done()
                        :tag('div')
                            :css({
                                ['flex'] = '1 1 0px',
                                ['text-align'] = 'right',
                                ['color'] = '#c9aa71',
                                ['font-size'] = 'smaller',
                                ['padding-right'] = '2px'
                            })
                            :wikitext(mw.ustring.format('%s.%s.%s', releaseSplit[3], releaseSplit[2], releaseSplit[1]))
                            :done()
                        :done()
                    :done()
                :tag('div')
                    :addClass('skinviewer-info')
                    :css({
                        ['padding'] = '15px',
                        ['background-color'] = 'rgba(10, 24, 39, 0.9)',
                        ['font-family'] = 'BeaufortLoL',
                        ['font-size'] = '14px'
                    })
                        :tag('div')
                            :addClass('skinviewer-info-lore')
                            :wikitext(lib.ternary(v['lore'] ~= nil, '<div style="line-height: 1.6em; text-align: center; font-size: 15px;">' .. tostring(v['lore']) .. '</div>', ''))
                            :done()
                        :tag('div')
                            :addClass('skinviewer-info-kleine-bilder')
                            :css({
                                ['display'] = 'flex',
                                ['justify-content'] = 'center'
                            })
                            :wikitext(lib.ternary(SD.getVoiceactor{champname, k} ~= nil, '<div style="padding-right:1em; text-align:center;">[[File:Actor.png|20px|link=]]' .. tostring(SD.getVoiceactor{champname, k}) .. '</div>', ''))
                            :wikitext(lib.ternary(SD.getSplashartist{champname, k} ~= nil, '<div style="padding-right:1em; text-align:center;">[[File:Artist.png|20px|link=]]' .. tostring(SD.getSplashartist{champname, k}) .. '</div>', ''))
                            :wikitext(lib.ternary(SD.getSet{champname, k} ~= nil, '<div style="padding-right:1em; text-align:center;">[[File:Set piece.png|20px|link=]]' .. tostring(SD.getSet{champname, k}) .. '</div>', ''))
                            :wikitext(lib.ternary(v['looteligible'] ~= false, '<div style="padding-right:1em; text-align:center;">[[File:Loot eligible.png|20px|link=]] Loot eligible</div>', ''))
                            :wikitext(lib.ternary(v['looteligible'] == false, '<div style="padding-right:1em; text-align:center;">[[File:Loot ineligible.png|20px|link=]] Loot inelgible</div>', ''))
                            :done()
                        :tag('div')
                            :addClass('skinviewer-info-grosse-bilder')
                            :css({
                                ['display'] = 'flex',
                                ['justify-content'] = 'center',
                                ['padding-top'] = '5px'
                            })
                            :wikitext(v['availability'] == 'Limitiert' and '<div style="padding:0 1em; text-align:center;"><div>[[File:Limited skin.png|50px|link=]]</div><div>Limited Edition</div></div>' or (v['availability'] == 'Legacy' and '<div style="padding:0 1em; text-align:center;"><div>[[File:Legacy skin.png|50px|link=]]</div><div>Klassisch</div></div>' or ''))
                            :wikitext(lib.ternary(v['filter'] ~= nil, '<div style="padding:0 1em; text-align:center;"><div>[[File:Voice filter.png|50px|link=]]</div><div>Voice filter</div></div>', ''))
                            :wikitext(lib.ternary(v['newquotes'] ~= nil, '<div style="padding:0 1em; text-align:center;"><div>[[File:Additional quotes.png|50px|link=]]</div><div>Additional/unique quotes</div></div>', ''))
                            :wikitext(lib.ternary(v['newvoice'] ~= nil, '<div style="padding:0 1em; text-align:center;"><div>[[File:New voice.png|50px|link=]]</div><div>New voiceover</div></div>', ''))
                            :wikitext(lib.ternary(v['neweffects'] ~= nil, '<div style="padding:0 1em; text-align:center;"><div>[[File:New effects.png|50px|link=]]</div><div>New SFX/VFX</div></div>', ''))
                            :wikitext(lib.ternary(v['newanimations'] ~= nil, '<div style="padding:0 1em; text-align:center;"><div>[[File:New animations.png|50px|link=]]</div><div>New Animations/Recall</div></div>', ''))
                            :wikitext(lib.ternary(v['transforming'] ~= nil, '<div style="padding:0 1em; text-align:center;"><div>[[File:Transforming.png|50px|link=]]</div><div>Transforming</div></div>', ''))
                            :wikitext(lib.ternary(v['extras'] ~= nil, '<div style="padding:0 1em; text-align:center;"><div>[[File:Includes extras.png|50px|link=]]</div><div>Includes Extras</div></div>', ''))
                            :wikitext(lib.ternary(v['chromas'] ~= nil, '<div style="padding:0 1em; text-align:center;"><div>[[File:Chromas available.png|50px|link=]]</div><div>Chromas</div></div>', ''))
                            :done()
                        :tag('div')
                            :addClass('skinviewer-info-variantof')
                            :css('text-align', 'center')
                            :wikitext(lib.ternary(SD.getVariant{champname, k} ~= nil, '<div>This skin is a variant of  ' .. tostring(IL.skin{champion = champname, skin = SD.getVariant{champname, k}, circle = "true", link = '*none*', text = SD.getVariant{champname, k}}) .. '. Store variants are discounted for multiple purchases.</div>', ''))
                            :done()
                    :done()
                :tag('div')
                    :addClass('skinviewer-tab-chroma')
                    :wikitext(v['chromas'] and SD.chromagallery{champname, k} or '')
                    :done()
    end
    
    return container:node(navContainer):node(tabContainer)
end


function p.chromagallery(frame)
    local args; if frame.args == nil then args = lib.arguments(frame) else args = lib.arguments(frame.args) end
        
    args['champion'] = lib.validateName(args['champion'] or args[1])
    args['skin']	 =                  args['skin']     or args[2] or "Original"
    args['variant']  =					args['variant']  or args[3] or nil
    
    local t = skinData[args['champion']].skins[args['skin']]
    if t == nil or t.chromas == nil then
        return userError("No chromas specified for skin ''" .. args['skin'] .. "'' in Module:SkinData/data", "LuaError")
    end
    t = t.chromas
    
    local header = "Chromas"
    local headerpre = ""
    if args['variant'] then
        headerpre = "Old "
    end
    local frame = mw.getCurrentFrame()
    
    if skinData[args['champion']].skins[args['skin']].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, ",", true)
    end
    table.sort(chromatable)
    
    local formatname = skinData[args['champion']].skins[args['skin']].formatname
    local key        = args["key"] or "true" 
    local imagewidth = "100px"
    local s = ''
    s = s .. '<div id="chromaexhibition" style="position:relative"><b>' .. headerpre .. lib.ternary(formatname, formatname, args['skin'] .. " " .. args['champion']) .. " " .. header .. "</b>"
    
    if key == "true" then
        s = s .. "<div class='glossary' data-game='lol' data-tip='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({args['champion'], args['skin'], "Base", args['variant']}) .. '|183px]]</div>'
    
    for i, chromaname in pairs(chromatable) do
        if skinData[args['champion']].skins[args['skin']].chromas[chromaname] == nil then
            return userError("Chroma ''" .. chromaname .. "'' not specified in Module:SkinData/data for " .. lib.ternary(formatname, formatname, args['skin'] .. " " .. args['champion']), "LuaError")
        end
        
        local availability = skinData[args['champion']].skins[args['skin']].chromas[chromaname].availability or "Available"
        
        if availability ~= "Canceled" then
            s = s .. "<div><div class='chroma " .. string.lower(availability) .. "-border'>[[File:" .. FN.chroma({args['champion'], args['skin'], chromaname, args['variant']}) .. "|" .. imagewidth .. "|border]]</div> <div class='chroma-caption'>" .. chromaname .. "</div></div>"
        end
    end
    
    s = s .. '</div></div>'
    
    return frame:preprocess(s)
end

function p.chromagallery2(frame)
    local args; if frame.args == nil then args = lib.arguments(frame) else args = lib.arguments(frame.args) end
        
    args['champion'] = lib.validateName(args['champion'] or args[1])
    args['skin']	 =                  args['skin']     or args[2] or "Original"
    -- args['variant']  =					args['variant']  or args[3] or nil
	local formatname =				  args['formatname'] or args[3] or args['skin'] .. ' ' .. args['champion']
	
	local chromaGalleryParams = {
		['caption'] = formatname .. ' Chromas'
	}
	local chromaGalleryContent = ""
	
	local t = skinData[args['champion']].skins[args['skin']]
    if t == nil or t.chromas == nil then
        return userError("No chromas specified for skin ''" .. args['skin'] .. "'' in Module:SkinData/data", "LuaError")
    end
    t = t.chromas
    
    local header = "Chromas"
    local headerpre = ""
    if args['variant'] then
        headerpre = "Old "
    end
    local frame = mw.getCurrentFrame()
    
    if skinData[args['champion']].skins[args['skin']].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, ",", true)
    end
    table.sort(chromatable)
    
    chromaGalleryContent = chromaGalleryContent  .. FN.chroma({args['champion'], args['skin'], "Base", args['variant']}) .. "|Base\n"
    
    for i, chromaname in pairs(chromatable) do
		if skinData[args['champion']].skins[args['skin']].chromas[chromaname] == nil then
            return userError("Chroma ''" .. chromaname .. "'' not specified in Module:SkinData/data for " .. lib.ternary(formatname, formatname, args['skin'] .. " " .. args['champion']), "LuaError")
        end
        
        local availability = skinData[args['champion']].skins[args['skin']].chromas[chromaname].availability or "Available"
        -- string.lower(availability)
        if availability ~= "Canceled" then
            chromaGalleryContent = chromaGalleryContent .. FN.chroma({args['champion'], args['skin'], chromaname, args['variant']}) .. "|" .. chromaname .. "\n"
        end
	end
	local chromas = mw.getCurrentFrame():extensionTag( 'gallery' , chromaGalleryContent, chromaGalleryParams )
	return chromas
end

function p.lolchampcount(frame)
	local args; if frame.args == nil then args = lib.arguments(frame) else args = lib.arguments(frame.args) end
	local lorData   = mw.loadData('Module:LoRData/data')
	
end

function p.lorcardcount(frame)
	local args; if frame.args == nil then args = lib.arguments(frame) else args = lib.arguments(frame.args) end
	local lorData   = mw.loadData('Module:LoRData/data')
	local queries = {
		total				= {function (id, card) return true end},
		collectible 		= {function (id, card) if card["collectible"] == true then return true end end},
		noncollectible		= {function (id, card) if card["collectible"] == false then return true end end},
		region				= {function (id, card) return true end, function (id, card) return string.sub(id, 3, 4) end},
		collectibleregion	= {function (id, card) if card["collectible"] == true then return true end end, function (id, card) return string.sub(id, 3, 4) end},
		noncollectibleregion = {function (id, card) if card["collectible"] == false then return true end end, function (id, card) return string.sub(id, 3, 4) end},
		subtype				= {function (id, card) return true end, function (id, card) return card["subtype"] end},
		collectiblesubtype 	= {function (id, card) if card["collectible"] == true then return true end end, function (id, card) return card["subtype"] end},
		noncollectiblesubtype = {function (id, card) if card["collectible"] == false then return true end end, function (id, card) return card["subtype"] end},
		spellkeywords		= {function (id, card) if card["type"] == 'Spell' or card["type"] == 'Trap' then return true end end, function (id, card) return card["keywords"] end},
	}
	local function createQuery(query, querytype, index)
		if querytype == 'values' then
			local count = {}
			return
				function (id, card)
					if query(id, card) then
						local data = index(id, card)
						if data == nil then return end
						if type(data) == 'table' then
							for k, v in pairs(data) do
								if count[v] == nil then count[v] = 0 end
								count[v] = count[v] + 1
							end
						else
							if count[data] == nil then count[data] = 0 end
							count[data] = count[data] + 1
						end
					end
				end,
				function () return count end
		else --if querytype == 'keys' then
			local count = 0
			return
				function (id, card)
					if query(id, card) then count = count + 1 end
				end,
				function () return count end
		end
	end
	local incrementCounts = {nil,nil,nil}
	local getCounts = {nil,nil,nil}
	for i, v in ipairs(args) do
		local query = queries[v]
		local querytype = nil
		if #query > 1 then querytype = 'values' end
		-- local test = query[1] or defaultTest
		-- local index = query[2] or defaultIndex
		incrementCounts[i], getCounts[i] = createQuery(query[1], querytype, query[2])
	end
	for k, v in pairs(lorData) do
		for i, incrementCount in ipairs(incrementCounts) do
			incrementCount(k, v)
		end
	end
	for i, getCount in ipairs(getCounts) do
		local count = getCount()
		if type(count) == 'table' then
			mw.log(args[i])
			local parity = 0
			for k, v in pairs(count) do
				mw.log(k .. " : " .. v)
				parity = parity + v
			end
			mw.log(args[i] .. "_total : " .. parity)
		else
			mw.log(args[i] .. " : " .. count)
		end
	end
	return
end
-- 	local subtypecount = {}
-- 	local collectiblesubtypecount = {}
-- 	local noncollectiblesubtypecount = {}
-- 	for k, v in pairs(lorData) do
-- 		count = count + 1
-- 		local collectible = v['collectible']
-- 		local region = string.sub(k, 3, 4)
-- 		if regioncount[region] == nil then regioncount[region] = 0 end
-- 		regioncount[region] = regioncount[region] + 1
-- 		if collectible == true then
-- 			collectiblecount = collectiblecount + 1
-- 			if collectibleregioncount[region] == nil then collectibleregioncount[region] = 0 end
-- 			collectibleregioncount[region] = collectibleregioncount[region] + 1
-- 		elseif collectible == false then
-- 			noncollectiblecount = noncollectiblecount + 1
-- 			if noncollectibleregioncount[region] == nil then noncollectibleregioncount[region] = 0 end
-- 			noncollectibleregioncount[region] = noncollectibleregioncount[region] + 1
-- 		end
-- 		if region == 'NA' then mw.log(k) end
-- 		if v["subtype"] ~= nil then
-- 			for k, v in ipairs(v["subtype"]) do
-- 				if subtypecount[v] == nil then subtypecount[v] = 0 end
-- 				subtypecount[v] = subtypecount[v] + 1
-- 				if collectible == true then
-- 					if collectiblesubtypecount[v] == nil then collectiblesubtypecount[v] = 0 end
-- 					collectiblesubtypecount[v] = collectiblesubtypecount[v] + 1
-- 				elseif collectible == false then
-- 					if noncollectiblesubtypecount[v] == nil then noncollectiblesubtypecount[v] = 0 end
-- 					noncollectiblesubtypecount[v] = noncollectiblesubtypecount[v] + 1
-- 				end
-- 			end
-- 		end
-- 	end
-- 	mw.log('card count: ' .. count)
-- 	mw.log('collectible card count: ' .. collectiblecount)
-- 	mw.log('noncollectible card count: ' .. noncollectiblecount)
-- 	mw.log('total card count: ' .. collectiblecount + noncollectiblecount)
-- 	local regiontotal = 0
-- 	local collectibleregiontotal = 0
-- 	local noncollectibleregiontotal = 0
-- 	mw.log('region count')
-- 	for k, v in pairs(regioncount) do
-- 		mw.log(k .. ': ' .. tostring(v))
-- 		regiontotal = regiontotal + v
-- 	end
-- 	mw.log(regiontotal)
-- 	mw.log('collectible region count')
-- 	for k, v in pairs(collectibleregioncount) do
-- 		mw.log(k .. ': ' .. tostring(v))
-- 		collectibleregiontotal = collectibleregiontotal + v
-- 	end
-- 	mw.log(collectibleregiontotal)
-- 	mw.log('noncollectible region count')
-- 	for k, v in pairs(noncollectibleregioncount) do
-- 		mw.log(k .. ': ' .. tostring(v))
-- 		noncollectibleregiontotal = noncollectibleregiontotal + v
-- 	end
-- 	mw.log(noncollectibleregiontotal)
	
-- 	local subtypetotal = 0
-- 	local collectiblesubtypetotal = 0
-- 	local noncollectiblesubtypetotal = 0
-- 	mw.log('subtype count')
-- 	for k, v in pairs(subtypecount) do
-- 		mw.log(k .. ': ' .. tostring(v))
-- 		subtypetotal = subtypetotal + v
-- 	end
-- 	mw.log(subtypetotal)
-- 	mw.log('collectible subtype count')
-- 	for k, v in pairs(collectiblesubtypecount) do
-- 		mw.log(k .. ': ' .. tostring(v))
-- 		collectiblesubtypetotal = collectiblesubtypetotal + v
-- 	end
-- 	mw.log(collectiblesubtypetotal)
-- 	mw.log('noncollectible subtype count')
-- 	for k, v in pairs(noncollectiblesubtypecount) do
-- 		mw.log(k .. ': ' .. tostring(v))
-- 		noncollectiblesubtypetotal = noncollectiblesubtypetotal + v
-- 	end
-- 	mw.log(noncollectiblesubtypetotal)
	
-- 	return
-- end

return p
-- </pre>
-- [[Category:Lua sandbox]]
Advertisement