League of Legends Wiki
League of Legends Wiki
League of Legends Wiki

Данный набор модулей используется для хранения и выдачи информации о лоре League of Legends и альтернативных вселенных.

Объединяет группу модулей, хранящих и обрабатывающих информацию о произведениях по League of Legends и Legends of Runeterra.

Список модулей[]

From Модуль:UniverseData/doc

-- <pre>

local p = {}

local loreData  = mw.loadData('Модуль:UniverseData/data')
local lib       = require('Модуль:Feature')
local FN        = require('Модуль:Filename')
local IL        = require('Модуль:ImageLink')
local skinData  = require("Модуль:SkinData").collectAllSkins()
local championData = require("Модуль:ChampionData")
local userError = require('Dev:User error')
local ERROR_CATEGORY = "UniverseData errors"

--[[
	Приватные методы
]]

-- Находит образ чемпиона, принадлежащий указанной линейке (если есть)
-- Принимает на вход имя чемпиона и название линейки образов
-- На выходе дает таблицу всех подходящих образов (в т.ч. Престижные и парные)
local function _findSkinforSet(champion, set)
    if(skinData[champion] == nil) then return {} end
    local allSkins = skinData[champion].skins
    local result = {}
    for skinKey, skinValue in pairs(allSkins) do
        if(skinValue.set) then
            if(type(skinValue.set) == "table") then
                for setKey, setValue in pairs(skinValue.set) do
                    if(setValue == set) then
                        table.insert(result, {champion, skinKey})
                    end
                end
            elseif(type(skinValue.set) == "string") then
                if(skinValue.set == set) then 
                    table.insert(result, {champion, skinKey})
                end
            end
        end
    end
    return result
end

--[[
	Публичные методы
]]

function p.get(frame)
    local args; if frame.args == nil then args = lib.arguments(frame) else args = lib.arguments(frame.args) end
    	
    local LORE_TYPES = {
    	["skinline"] = true,
    	["universe"] = true,
    	["multimedia"] = true,
    }
 
    local get       = require ("Module:UniverseData/getter")
    local loretype  = args["loretype"]  or args[1]
    if(not(LORE_TYPES[loretype])) then
    	return userError("Ошибка: Неверный тип лора (loretype)", ERROR_CATEGORY)
    end
    
    local name      = args["name"] or args[2]
    local datatype  = args["datatype"]  or args[3]
    local output    = args["output"]    or args[4] or nil
 
    local result = get[datatype](loretype, name)
 
    if(output ~= nil and type(result) == "table") then
        if(output == "csv") then
            return table.concat(result, ",")
        elseif(output == "template") then
            table.sort(result)
            local expandedResult = {}
            for _, v in ipairs(result) do
            	table.insert(expandedResult, frame:expandTemplate{
            		title = args["t_name"],
            		args = { v }
            	})
            end
            return lib.tbl_concat{ ["tbl"] = expandedResult, separator = args["separator"] }
        elseif(output == "custom") then 
            return frame:preprocess(lib.tbl_concat{
            	result, 
            	prepend = args["prepend"], 
            	append = args["append"], 
            	separator = args["separator"], 
            	index = args["index"]
            })
        end
    elseif(result == nil) then
        return ""
    else
        return result
    end
end

function p.loreTemplate()
    local loreList = mw.html.create('ul')
    local lore = {}
 
    for x in pairs(loreData) do
        table.insert(lore, x)
    end
    table.sort(lore)
 
    for _, piece in pairs(lore) do
        local t = loreData[piece]
        local link = piece
        local name = piece
        local loretype = t.loretype or "N/A"
        local starring = t.starring
        local entry = ''
        
        if starring then
            entry = '[[' .. link .. '|' .. name .. ']] <small>('.. _getChampions(starring) ..')</small>'
        else
            entry = '[[' .. link .. '|' .. name .. ']]'
        end
        
        if loretype == "Рассказ" then
            loreList
                :tag('li')
                    :tag('div')
                        :wikitext(entry)
                    :done()
                :done()
                :newline()
        end
    end
    
    return loreList
end

function p.getBanner(frame) 
    local args; if frame.args == nil then args = lib.arguments(frame) else args = lib.arguments(frame.args) end

    return _bannerNew(args[1])
end


-- Выдает категории, соответствующие данному представителю лора игры
-- Изменения в этой функции полноценное не обновляются на страницах
-- Для подтверждения изменений необходимо сделать нулевую правку с помощью бота
function p.getCategories(frame)
    local args; if frame.args == nil then args = lib.arguments(frame) else args = lib.arguments(frame.args) end
    
    local cData = require("Модуль:ChampionData")
    local name = args['lore'] or args[1]
    if name == nil then return end -- Пустая строка для лора - вернуться
    
    local lorePiece = loreData[name]
    if lorePiece == nil then
        return userError(
            'Указанный объект лора не найден в Модуль:UniverseData/data',
            'LuaError')
    end
    
    local loretype = lorePiece.loretype
    if loretype == nil then
        return userError(
            'Не указан тип лора для ' .. name .. 'Модуль:UniverseData/data',
            'LuaError')
    end
    
    local starring = lorePiece.starring or {}
    local mentioned = lorePiece.mentioned or {}
    local count = 0 -- Счетчик для числа участвующих/упомянутых чемпионов (length не работает)
    
    
    -- Костыль: Lua не воспринимает таблицы от loadData за последовательности, даже если они являются таковыми
    local loreRegions = {}
    if lorePiece.region then
        for k, v in pairs(lorePiece.region) do 
            table.insert(loreRegions, v)
        end
    end
    
    -- Заносит названия категорий в таблицу, а в конце собирает её в строку
    local categoryList = {}
    local result = ""
    
    -- Устаревший лор
    if lorePiece.outdated then
        table.insert(categoryList, "Устаревший лор")
        result = result .. "{{Устаревший лор}}"
    else
        -- Альтернативные вселенные
        if lorePiece.au  then
            if loretype ~= "Вселенная" then
                table.insert(categoryList, mw.ustring.format(
                    "%s альтернативных вселенных", 
                    _loretypeForCategory(loretype))
                )
            else
                table.insert(categoryList, _loretypeForCategory(loretype))
                -- Собрать чемпионов со всех линеек
                starring = _mergeSkinlines(lorePiece.au)
            end
            table.insert(categoryList, lorePiece.au)
        else
            -- Если указан регион (только для основной вселенной)
            if #loreRegions > 0 then
                for i, region in ipairs(loreRegions) do
                    table.insert(categoryList, "Лор " .. _regionGenitive(region))
                end
            else
                table.insert(categoryList, "Лор Рунтерры")
            end
            -- Категория по типу лора
            table.insert(categoryList, _loretypeForCategory(loretype))
        end
        
        -- Ставить категории чемпионов, если речь идет о территории Рунтерры
        if loretype ~= "Территория" then
            if loretype == "Фракция" or loretype == "Вселенная" then
                for i, champion in pairs(starring) do
                    table.insert(categoryList, champion)
                end
            else
                -- Чемпионы
                for i, champion in pairs(starring) do
                    table.insert(categoryList, "Лор " .. cData.get{champion, "genitive"})
                    count = count + 1
                end
                
                for i, champion in pairs(mentioned) do
                    table.insert(categoryList, "Лор " .. cData.get{champion, "genitive"})
                end
                
                if count == 0 then
                   table.insert(categoryList, "Лор без чемпионов")
                end
            end
        end
    end
    
    for i, v in ipairs(categoryList) do
        categoryList[i] = mw.ustring.format("[[Категория:%s]]", v)
    end
    
    result = result .. table.concat(categoryList, "\r\n")
    
    return result
end

--[[
Возвращает категории для указанной линейки образов
В общем случае возвращает категорию альтернативной вселенной и 
титульные категории всех чемпионов-участников
]]
function p.getSkinLineCategories(frame)
    local args; if frame.args == nil then args = lib.arguments(frame) else args = lib.arguments(frame.args) end
    
    local skinlines = mw.loadData("Модуль:UniverseData/skinlines")
    local slineName = args['skinline'] or args[1]
    
    if(skinlines[slineName] == nil) then
        return userError("Линейка образов " .. slineName .. " не найдена в Модуль:UniverseData/skinlines", "UniverseData errors")
    end
    
    local sline = skinlines[slineName]
    local categories = {}
    
    if(sline.au) then 
        table.insert(categories, mw.ustring.format("[[Категория:%s]]", sline.au))
    end
    
    for k, v in pairs(sline.starring) do
        table.insert(categories, mw.ustring.format("[[Категория:%s]]", v))
    end
    
    return table.concat(categories, "\r\n")
end

function p.getAuthor(frame)
    local args; if frame.args == nil then args = lib.arguments(frame) else args = lib.arguments(frame.args) end

    return loreData[args[1]].author or "Неизвестен"
end

function p.getAuthorlist(frame)
    local authors = {}
    local hash = {}
    local authorList = mw.html.create('ul')
 
    authorList:newline()
 
    for _, x in pairs(loreData) do
        if x.author ~= nil then
            if type(x.author) == "table" then
                for _, value in pairs(x.author) do
                    if (not hash[value]) then
                        table.insert(authors, value)
                        hash[value] = true
                    end
                end
            else
                if (not hash[x.author]) then
                    table.insert(authors, x.author)
                    hash[x.author] = true
                end
            end
        end
    end
    table.sort(authors)
 
    for _, author in pairs(authors) do
        authorList
            :tag('li')
                :wikitext('[[' .. author .. ']]')
                :done()
            :done()
            :newline()
    end
 
    return authorList
end

function p.getLorelist(frame)
    local loreList = mw.html.create('ul')
    local lore = {}
 
    for x in pairs(loreData) do
        table.insert(lore, x)
    end
    table.sort(lore)
 
    for _, piece in pairs(lore) do
        local t = loreData[piece]
        local link = piece
        local name = piece
        local loretype = t.loretype or "N/A"
        
        if loretype == "Биография" then
            link = t.starring[1]
        end
        
        loreList
            :tag('li')
                :tag('div')
                    :wikitext('[[' .. link .. '|' .. name .. ']] ('.. loretype ..')')
                :done()
            :done()
            :newline()
    end
    
    return loreList
end

-- Выдает в форме баннеров объекты лора, связанные с указанным чемпионом/персонажем
function p.getMore(frame)
    local args; if frame.args == nil then args = lib.arguments(frame) else args = lib.arguments(frame.args) end
 
    local champion      = args[1]
    local loreList      = ""
    local mentionedList = ""
    local auList        = ""
    local lore          = {}
 
    for x in pairs(loreData) do
        table.insert(lore, x)
    end
    table.sort(lore)
 
    loreList = loreList .. "<h3>Подробнее о чемпионе</h3>"
    for _, piece in pairs(lore) do
        local hit = false
        local t = loreData[piece]
 
        if t.starring ~= nil and t.loretype ~= "Биография" and t.loretype ~= "Фракция" and t.loretype ~= "Территория" and t.outdated ~= true then
            
            for _, substarring in pairs(t.starring) do
                if substarring == champion then
                    hit = true
                end
            end
        end
        if hit == true then
            if not(t.au) then
                loreList = loreList .. _bannerNew(piece)
            else
                auList = auList .. _bannerNew(piece)
            end
        end
    end
 
    for _, piece in pairs(lore) do
        local hit = false
        local t = loreData[piece]
 
        if t.mentioned ~= nil then
            for _, submentioned in pairs(t.mentioned) do
                if submentioned == champion then
                    hit = true
                end
            end
        end
        
        if hit == true then
            if not(t.au) then
                mentionedList = mentionedList .. _bannerNew(piece)
            else
                auList = auList .. _bannerNew(piece)
            end
        end
 
    end
 
    if mentionedList ~= "" then 
       loreList = loreList .. "\n<div class='va-collapsible-content mw-collapsible mw-collapsed' data-expandtext='Показать' data-collapsetext='Скрыть'><h3>Упоминается</h3><div class='va-collapsible-content mw-collapsible-content'>" .. mentionedList .. "</div>"
    end
 
    if auList ~= "" then 
       loreList = loreList .. "\n<div class='va-collapsible-content mw-collapsible mw-collapsed' data-expandtext='Показать' data-collapsetext='Скрыть'><h3>Альтернативная вселенная</h3><div class='va-collapsible-content mw-collapsible-content'>" .. auList .. "</div>"
    end
    
    return loreList
end

function p.getRegionlore(frame)
    local args; if frame.args == nil then args = lib.arguments(frame) else args = lib.arguments(frame.args) end
 
    local loreList = ""
    local lore = {}
    local result = false
 
    for x in pairs(loreData) do
        table.insert(lore, x)
    end
    table.sort(lore)
 
    for _, piece in pairs(lore) do
        local hit = false
        local t = loreData[piece]
        
        if (t.region ~= nil) then
            if type(t.region) == "table" then
                for _, subregion in pairs(t.region) do
                    if subregion == args[1] then
                        hit = true
                        result = true
                    end
                end
            else
                if t.region == args[1] then
                    hit = true
                    result = true
                end
            end
        else 
            if args[1] == "Рунтерра" then 
                hit = true
                result = true
            end
            
        end
    
        if hit == true then
            loreList = loreList .. _bannerNew(piece, t)
        end
    end
 
    if result == false then 
       loreList = "No match found for " .. args[1] .. "."
    end
 
    return loreList .. "[[Category:Test]]"
end

-- Выдает галерею загрузочных иллюстраций чемпионов, принадлежащик к указанной фракции
function p.getRegionExhibition(frame)
    local args; if frame.args == nil then args = lib.arguments(frame) else args = lib.arguments(frame.args) end
        
    local IL = require("Модуль:ImageLink")
    
    local isList = args['islist'] or "false"
    local ICON_SIZE = args["iconsize"] or "64px"
    local result = mw.html.create("div")
    result:addClass("universe-exhibition")
    
    if(isList ~= "false") then
        local championList = args["list"] or args[1]
        championList = mw.text.split(championList, "[,;]%s*")
        for i, v in ipairs(championList) do
        	result:wikitext(tostring(IL.champion({
            	["circle"] = false,
                ["champion"] = v,
                ["size"] = ICON_SIZE,
                ["style"] = "*none*",
                ["class"] = "universe-exhibition-champion-block",
                ["labelclass"] = "universe-exhibition-champion-label"
            })))
        end
        return tostring(result)
    end
    
    local region = args["region"] or args[1]
    local champions = loreData[region].starring
    table.sort(champions)
    
    for _, v in pairs(champions) do 
        result:wikitext(tostring(IL.champion({
            ["circle"] = false,
            ["champion"] = v,
            ["size"] = ICON_SIZE,
            ["style"] = "*none*",
            ["class"] = "universe-exhibition-champion-block",
            ["labelclass"] = "universe-exhibition-champion-label"
        })))
    end
    
    return tostring(result)
end

function p.getSkinlineBanner(frame)
    local args; if frame.args == nil then args = lib.arguments(frame) else args = lib.arguments(frame.args) end
    
    local skinlines = mw.loadData('Модуль:UniverseData/skinlines')
    local sLineName = args['skinline'] or args[1]
    local sLine = skinlines[sLineName]
    local sLineDisplayName = sLine.displayname or sLineName
    
    -- Если линейки нет
    if sLine == nil then
        return userError('Линейка образов не найдена', 'LuaError')
    end
    
    -- Костыль: Lua не считает таблицы, переданные через mw.loadData за последовательности, даже если они считаются таковыми
    local authorTable = {}
    if(sLine.author) then
        for i, v in pairs(sLine.author) do
            table.insert(authorTable, v)
        end
    end
    
    -- Контейнер баннера
    local bannerNode = mw.html.create('div')
    bannerNode
        :attr('id', 'champinfo-container')
        :cssText("width:100%; min-height: 290px; overflow:hidden; position:relative; font-size:14px; color: #dddddd; display:flex")
        :done()
    
    -- Блок изображения
    local imageNode = mw.html.create('div')
    imageNode
        :addClass('FillImage')
        :cssText("width:100%; height:100%; position:absolute; top: 0; left:0; opacity:0.2; z-index: 0")
        :wikitext(mw.ustring.format("[[File:%s|link=]]", sLine.artwork))
        :newline()
    
    -- Ссылки на редактирование
    local editNode = mw.html.create('div')
    editNode
        :cssText("position: absolute; top: 0.1em; right: 1em; font-size:85%;")
        :tag('span')
            :addClass('plainlinks')
            :wikitext("[https://leagueoflegends.fandom.com/ru/wiki/Модуль:UniverseData/skinlines?action=edit Ред.]")
            :done()
        :wikitext(" • [[:File:" .. sLine.artwork .. "|Изобр.]]")
        :done()
    if(sLine.reference) then
        editNode:wikitext(" • [" .. sLine.reference.. " Ссылка]")
    end
    editNode:newline()
    
    -- Блок ID
    local idNode = mw.html.create('div')
    if(sLine.id) then
        idNode
            :cssText("position: absolute; left: 1em; top:0.1em; font-size:70%;")
            :wikitext(sLine.id)
            :done()
        bannerNode:node(idNode)
    end
    
    -- Текст
    local contentNode = mw.html.create('div')
    contentNode
        :cssText("text-align:center; width: 500px; margin:auto; z-index: 1")
        :newline()
    local titleNode = mw.html.create('div')
    -- Текст: общий заголовок
    titleNode
        :css('margin', '3em 0')
        :tag('p')
            :css('margin-top', '1em')
            :wikitext('Набор образов')
            :done()
        :tag('p')
            :attr('id', 'title')
            :cssText("font-size:250%; line-height:100%; margin:0;color:#c9aa71")
            :wikitext("[[" .. sLineName .. "|" .. sLineDisplayName .."]]")
            :done()
        :newline()
    -- Текст: Является частью альтернативной вселенной?
    if(sLine.au) then
        titleNode
            :tag('p')
                :css('margin-top', '1em')
                :wikitext(mw.ustring.format("Вселенная: [[%s]]", sLine.au))
                :done()
            :newline()
    end
    -- Текст: Авторы
    if(sLine.author) then
        titleNode
            :tag('p')
                --:css('margin-top', '-1em')
                :wikitext(mw.ustring.format("%s: %s",
                    lib.ternary(#authorTable == 1, "Автор", "Авторы"),
                    mw.text.listToText(authorTable, ", ")
                    ))
                :done()
            :done()
    end
    -- Текст: Представленные чемпионы
    titleNode
        :tag('p')
            :attr('id', 'featured')
            :css('margin-top', '1em')
            :wikitext("Представители: " .. _getChampions(sLine.starring))
            :done()
        :newline()
    contentNode:node(titleNode)
            
    -- Сборка
    bannerNode
        :node(imageNode)
        :node(editNode)
        :node(contentNode)
    
    return tostring(bannerNode)
end

function p.getSkinlineCategories(frame)
    -- TODO
end

function p.getSkinlineDescription(frame)
    local args; if frame.args == nil then args = lib.arguments(frame) else args = lib.arguments(frame.args) end
    
    local champion = args["champion"] or args[1]
    local skin     = args["skin"] or args[2] or "Классический"
    local isListItem = args["li"]
    
    if(champion == nil) then 
        return userError("Не указан чемпион", ERROR_CATEGORY)
    end
    if(skin == "Классический" or skin == "Классическая") then return end
    
    local skinlineData = mw.loadData("Модуль:UniverseData/skinlines")
    local auData = mw.loadData("Модуль:UniverseData/au")
    
    local setName = skinData[champion]["skins"][skin]["set"]
    if(setName == nil) then return end
    local mainSet = setName[1]
    if(skinlineData[mainSet] == nil) then
        return userError(
            "Линейка образов " .. mainSet .. " не найдена в Модуль:UniverseData/skinlines",
            ERROR_CATEGORY)
    end
    
    -- Построение фразы типа:
    -- "Этот образ в ходит в линейку образов K/DA, которая является частью вселенной Riot Records"
    local s = mw.ustring.format("Этот образ входит в линейку образов [[%s]]", mainSet)
    if(skinlineData[mainSet].au) then
    	if(skinlineData[mainSet].au == mainSet) then
    		s = s .. mw.ustring.format(" из [[%s|одноименной вселенной]]", mainSet)
    	else
    		s = s .. " из вселенной [[" .. skinlineData[mainSet].au .. "]]"
    	end
    end
    s = s .. "."
    
    -- Все остальные чемпионы из этой линейки
    local skinlineChampions = {}
    for i, v in pairs(skinlineData[mainSet].starring) do
        table.insert(skinlineChampions, v)
    end
    
    -- Список пар чемпион-образ из этой линейки
    local csPairs = {} -- csPairs = champion-skin pairs
    for i, slChampion in ipairs(skinlineChampions) do
        -- Найти все входящие в линейку пары "чемпион-образ"
        local currentPairs = _findSkinforSet(slChampion, setName[1])
        -- исключить добавление пустой пары
        if(#currentPairs > 0) then
            for j, pairValue in ipairs(currentPairs) do
                if(slChampion ~= champion or pairValue[2] ~= skin) then
                    table.insert(csPairs, pairValue)
                end
            end
        end
    end
    
    s = s .. " В эту линейку также входят:"
    
    local listNode = mw.html.create("ul")
    if(#csPairs > 5) then -- Если больше 5 образов, то разбить на колонки
        listNode
            :addClass("columntemplate")
            :cssText("-moz-column-count:2; -webkit-column-count:2; column-count:2")
    end
    
    for i, v in ipairs(csPairs) do
    	if(i > 11) then
    		listNode
	            :tag("li")
	                :wikitext("и другие...")
	                :newline()
	            :done()
			break
    	end	
    	
        listNode
            :tag("li")
                :wikitext(tostring(championData.championSkinIcon{
                    ["champion"] = v[1],
                    ["skin"]     = v[2],
                    ["link"]     = v[1] .. "/Коллекция",
                	["circle"]   = "true"
                    }))
                :newline()
            :done()
    end
    
    -- Если нужно сделать частью общего маркированного списка
    local textNode = mw.html.create("ul")
    if(isListItem) then
        textNode
            :tag("li")
                :wikitext(s)
                :newline()
                :node(listNode)
                :done()
            :done()
        return tostring(textNode)
    end
    
    return s .. tostring(listNode)
    
end

function p.getSkinlineDescriptionDisambig(frame)
    local args; if frame.args == nil then args = lib.arguments(frame) else args = lib.arguments(frame.args) end
    
    local champion = args["champion"] or args[1]
    local skin     = args["skin"] or args[2] or "Классический"
    local isListItem = args["li"]
    local auDisplay = args["audisplay"]
    
    if(champion == nil) then 
        return userError("Не указан чемпион", ERROR_CATEGORY)
    end
    if(skin == "Классический" or skin == "Классическая") then return end
    
    local skinlineData = mw.loadData("Модуль:UniverseData/skinlines")
    local auData = mw.loadData("Модуль:UniverseData/au")
    
    local setName = skinData[champion]["skins"][skin]["set"]
    local setDisplay = args["setdisplay"] or setName
    if(setName == nil) then return end
    local mainSet = setName[1]
    if(skinlineData[mainSet] == nil) then
        return userError(
            "Линейка образов " .. mainSet .. " не найдена в Модуль:UniverseData/skinlines",
            ERROR_CATEGORY)
    end
    
    -- Построение фразы типа:
    -- "Этот образ в ходит в линейку образов K/DA, которая является частью вселенной Riot Records"
    local s = mw.ustring.format("Этот образ входит в линейку образов [[%s|" .. setDisplay .."]]", mainSet)
    if skinlineData[mainSet].audisplay == auDisplay then
		if skinlineData[mainSet].displayname == skinlineData[mainSet].audisplay then
    		s = s .. mw.ustring.format(" из [[" .. skinlineData[mainSet].au .. "|одноименной вселенной]]")
    	else
    		s = s .. " из вселенной [[%s|" .. skinlineData[mainSet].audisplay .. "]]"
    	end
	end
    s = s .. "."
    
    -- Все остальные чемпионы из этой линейки
    local skinlineChampions = {}
    for i, v in pairs(skinlineData[mainSet].starring) do
        table.insert(skinlineChampions, v)
    end
    
    -- Список пар чемпион-образ из этой линейки
    local csPairs = {} -- csPairs = champion-skin pairs
    for i, slChampion in ipairs(skinlineChampions) do
        -- Найти все входящие в линейку пары "чемпион-образ"
        local currentPairs = _findSkinforSet(slChampion, setName[1])
        -- исключить добавление пустой пары
        if(#currentPairs > 0) then
            for j, pairValue in ipairs(currentPairs) do
                if(slChampion ~= champion or pairValue[2] ~= skin) then
                    table.insert(csPairs, pairValue)
                end
            end
        end
    end
    
    s = s .. " В эту линейку также входят:"
    
    local listNode = mw.html.create("ul")
    if(#csPairs > 5) then -- Если больше 5 образов, то разбить на колонки
        listNode
            :addClass("columntemplate")
            :cssText("-moz-column-count:2; -webkit-column-count:2; column-count:2")
    end
    
    for i, v in ipairs(csPairs) do
    	if(i > 11) then
    		listNode
	            :tag("li")
	                :wikitext("и другие...")
	                :newline()
	            :done()
			break
    	end	
    	
        listNode
            :tag("li")
                :wikitext(tostring(championData.championSkinIcon{
                    ["champion"] = v[1],
                    ["skin"]     = v[2],
                    ["link"]     = v[1] .. "/Коллекция",
                	["circle"]   = "true"
                    }))
                :newline()
            :done()
    end
    
    -- Если нужно сделать частью общего маркированного списка
    local textNode = mw.html.create("ul")
    if(isListItem) then
        textNode
            :tag("li")
                :wikitext(s)
                :newline()
                :node(listNode)
                :done()
            :done()
        return tostring(textNode)
    end
    
    return s .. tostring(listNode)
    
end

-- Выводит список всех вселенных для шаблона [[Шаблон:Альтернативные вселенные]]
function p.getOfficialUniversesList()
	local officialAu = {
		"Riot Records",
		"Боевые души",
		"Горизонт событий",
		"Звездные защитники",
		"Импульсный Огонь",
		"Ковбойская готика",
		"Луны Ионии",
		"Одиссея",
		"ПРОЕКТ",
	}
	local result = {}
	
	for i, v in ipairs(officialAu) do
		table.insert(result, tostring(IL.universe{["universe"] = v}))
	end
	
	return table.concat(result, " ·&nbsp;")
end

function _bannerNew(name)
    local lorePiece = loreData[name] or loreData["Что-то пошло не так"]
    local lang = mw.language.new('ru')
    local bName = lorePiece.displayname or name
    local artwork = lorePiece.artwork or "Bard promo 2.jpg"
    local author = lorePiece.author or {"неизвестен"}
    local reference = lorePiece.reference or "https://universe.leagueoflegends.com/ru_RU/"
    local link = lorePiece.link or name
    local region = "Рунтерра"
    local duration = lorePiece.duration or "0"
    local starring = "N/A"
    local mentioned = "N/A"
    local canon = lorePiece.outdated or ""
    local loretype = lorePiece.loretype or ""
    local description = lorePiece.description or ""
    local au = lorePiece.au or "Рунтерра"
    
    if loretype == "Биография" then link = lorePiece.starring[1] end
    if type(author) == "string" then author = {author} end
    if lorePiece.region ~= nil then region = lorePiece.region[1] end
    if lorePiece.starring ~= nil then 
        starring = _getChampions(lorePiece.starring)
    elseif au ~= "Рунтерра" then
        starring = _getChampions(_mergeSkinlines(au))
    end
    if lorePiece.mentioned ~= nil then mentioned = _getChampions(lorePiece.mentioned) end
    
    --flag tables - для более быстрого поиска совпадений
    local restrictAuthors = { -- Не показывать блок "Авторы"
        ["Видео"] = true,
        ["Вселенная"] = true,
        ["Аудиоспектакль"] = true,
        ["Территория"] = true,
        ["Фракция"] = true,
    }
    local starringTable = { -- Не "Действующие лица" для некоторых типов лора
        ["Вселенная"] = "Представители",
        ["Территория"] = "Представители",
        ["Фракция"] = "Представители",
    }
    
    local bannerNode = mw.html.create('div')
    bannerNode
        :attr('id', 'champinfo-container')
        :cssText("width:100%; min-height: 290px; overflow:hidden; position:relative; font-size:14px; color: #dddddd; display:flex")
        :newline()
        :done()
    
    -- Блок изображения
    local imageNode = mw.html.create('div')
    imageNode
        :addClass('FillImage')
        :cssText("width:100%; height:100%; position:absolute; top: 0; left:0; opacity:0.2; z-index: 0")
        :wikitext(mw.ustring.format("[[File:%s|link=]]", artwork))
        :newline()
    
    -- Ссылки на редактирование
    local editNode = mw.html.create('div')
    editNode
        :cssText("position: absolute; top: 0.1em; right: 1em; font-size:85%;")
        :tag('span')
            :addClass('plainlinks')
            :wikitext("[https://leagueoflegends.fandom.com/ru/wiki/Модуль:UniverseData/data?action=edit Ред.]")
            :done()
        :wikitext(mw.ustring.format(
            " • [[:File:%s|Изобр.]] • [%s Ссылка]",
            artwork,
            reference))
        :newline()
        :done()
    
    -- Текст
    local contentNode = mw.html.create('div')
    contentNode
        :cssText("text-align:center; width: 500px; margin:auto; z-index:2;")
        :newline()
        :done()
    
    local titleNode = mw.html.create('div')
    -- Текст: иконка региона
    titleNode
        :css('margin', '3em 0')
        :tag('p')
            :attr('id', 'title')
            :css('margin', '1em 0')
            :wikitext(mw.ustring.format(
                "[[File:%s|x56px|link=%s]]",
                FN.faction{['faction'] = region},
                region))
            :done()
        :newline()
    -- Текст: Типа лора (для рассказа - указать длительность)
    if(loretype == "Рассказ" and duration ~= "0") then
        titleNode
            :tag('p')
                :attr('id', 'duration')
                :css('margin-top', '1em')
                :wikitext(mw.ustring.format(
                    "Рассказ • На %d %s",
                    duration,
                    lang:convertPlural(duration, 'минута', 'минуты', 'минут')))
                :done()
            :newline()
    else
        titleNode
            :tag('p')
                :attr('id', 'duration')
                :css('margin-top', '1em')
                :wikitext(loretype)
                :done()
            :newline()
    end
    -- Текст: Название лора
    titleNode
        :tag('p')
            :attr('id', 'title')
            :cssText("font-size:250%; line-height:100%; margin:0; color:#c9aa71")
            :wikitext("[[" .. link .. "|" .. bName .. "]]")
            :done()
        :newline()
    -- Текст: Вселенная
    if(au ~= "Рунтерра" and loretype ~= "Вселенная") then
        titleNode
            :tag('p')
                :css('margin-top', '1em')
                :wikitext(mw.ustring.format("Вселенная: [[%s]]", au))
                :done()
            :newline()
    end
    -- Текст: Авторы
    if(not(restrictAuthors[loretype])) then
        if(author[1] == "Numerous creators") then
            titleNode
                :tag('p')
                    :css('margin-top', '-1em')
                    :wikitext("Несколько авторов")
                    :done()
                :done()
        else
            titleNode
                :tag('p')
                    :css('margin-top', '1em')
                    :wikitext(mw.ustring.format("%s: %s",
                        lib.ternary(#author == 1, "Автор", "Авторы"),
                        mw.text.listToText(author, ", ")
                        ))
                    :done()
                :done()
        end
    end
    -- Текст: Представленные чемпионы
    if(starring ~= "N/A") then
        titleNode
            :tag('p')
                :attr('id', 'featured')
                :css('margin-top', '1em')
                :wikitext(
                    mw.ustring.format("%s: %s",
                    lib.ternary(starringTable[loretype], "Представители", "Действующие лица"),
                    starring))
                :done()
            :newline()
    end
    -- Текст: Упоминаемые чемпионы
    if(mentioned ~= "N/A") then
        titleNode
            :tag('p')
                :attr('id', 'featured')
                :css('margin-top', '1em')
                :wikitext(
                    mw.ustring.format("%s: %s",
                    "Упоминаются",
                    mentioned))
                :done()
            :newline()
    end
    contentNode:node(titleNode)
    
    -- Стрелки
    local previousNode
    if(lorePiece.previous) then 
        previousNode = mw.html.create('div')
        previousNode
            :cssText("position: absolute; top: 50%; left:1em; font-size:3em;")
            :wikitext("[[" .. lorePiece.previous .. "|◄]]")
            :newline()
            :done()
        bannerNode:node(previousNode)
    end
    local followingNode
    if(lorePiece.following) then 
        followingNode = mw.html.create('div')
        followingNode
            :cssText("position: absolute; top: 50%; right: 1em; font-size:3em;")
            :wikitext("[[" .. lorePiece.following .. "|►]]")
            :newline()
            :done()
        bannerNode:node(followingNode)
    end
    
    -- Сборка
    bannerNode
        :node(imageNode)
        :node(editNode)
        :node(contentNode)
        :done()
    return tostring(bannerNode)
end


--[[
========================
Приватные функции модуля
========================
]]
function _getChampions(t)
    local s = ""
	local championData = require("Модуль:ChampionData")
    local championtable  = {}
    for i, champion in pairs(t) do
        table.insert(championtable, champion)
    end
    table.sort(championtable)
 
    for i, champion in pairs(championtable) do
    	local parsedName = champion
    	parsedName = championData.get{champion, "engname"}
    	
        if i ~= 1 then
            s = s  .. ", "
        end
            s = s .. "<span style='white-space:nowrap'>[[File:" .. parsedName .. " OriginalSquare.png|20px|link=" .. champion .. "]] [[" .. champion .. "]]</span>"
    end
 
    return s
end

-- Выдает множественное число из списка, иначе просто возвращает аргумент
-- В Lua нет switch/case!!!
function _loretypeForCategory(ltype)
    local loretypeCategory = {
        ['Биография'] = "Биографии",
        ['Видео']     = "Видеоролики",
        ['Комикс']    = "Комиксы",
        ['Рассказ']   = "Рассказы",
        ['Территория']= "Территории",
        ['Фракция']   = "Фракции",
        ['Клип']      = "Клипы",
        ['Вселенная'] = "Альтернативные вселенные",
    }
    
    if loretypeCategory[ltype] then
        return loretypeCategory[ltype]
    end
    
    return ltype
end

-- Возвращает название региона в родительном падеже
function _regionGenitive(region)
    if region == "Бандл сити" then
        return region
    elseif region == "Сумрачные острова" then
        return "Сумрачных островов"
    end
    
    local lastLetter = mw.ustring.sub(region, -1)
    if lastLetter == "я" then
        return mw.ustring.sub(region, 0, -2) .. "и" -- Демасия - Демасии
    elseif lastLetter == "а" then
        return mw.ustring.sub(region, 0, -2) .. "ы" -- Бездна - Бездны
    elseif lastLetter == "ь" then
        return mw.ustring.sub(region, 0, -2) .. "я" -- Ишталь - Ишталя
    end
   return region .. "а"
end

-- Собирает в одну последовательность всех чемпионов из одной вселенной
-- (если эта вселенная содержит несколько линеек образов)
function _mergeSkinlines(universe)
    local auData  = mw.loadData('Модуль:UniverseData/au')
    local slData  = mw.loadData('Модуль:UniverseData/skinlines')
    
    local fetchedLines = auData[universe]
    if(fetchedLines == nil) then
        return userError([[
            Ошибка при соединении линеек образов. Проверьте, дано ли соответсвие "Вселенная"-"линейки образов" в Модуль:UniverseData/au для 
            ]] .. universe,
            "UniverseData errors")
    end
    
    -- Костыль: Lua не воспринимает таблицы из mw.loadData за последовательности, даже если они являются таковыми
    local skinlines = {}
    for k, v in pairs(fetchedLines) do
        table.insert(skinlines, v)
    end
    
    local championsOfUniverse = {}
    for i, skinline in ipairs(skinlines) do
        local champions = slData[skinline].starring
        for k, champOfSkinline in pairs(champions) do
            if(lib.find(championsOfUniverse, champOfSkinline) == -1) then
                table.insert(championsOfUniverse, champOfSkinline)
            end
        end
    end
    
    return championsOfUniverse
end

return p

-- </pre>
-- [[Category:Lua]]