FANDOM


local p = {}
 
-- Основа спрайта
function p.base( f )
	local args = f
	if f == mw.getCurrentFrame() then 
		args = require( 'Модуль:ProcessArgs' ).merge( true )
	else
		f = mw.getCurrentFrame()
	end
 
	-- Настройки по умолчанию
	local default = {
		["масштаб"] = 1,
		["формат"] = 256,
		["разм"] = 16,
		["поз"] = 1,
		["выравн"] = 'text-top'
	}
 
	local defaultStyle = default
	if args["настройки"] then
		local settings = mw.loadData( 'Модуль:' .. args["настройки"] )
		if not settings["таблстилей"] then
			-- Создаём отдельную копию текущих настроек по умолчанию:
			defaultStyle = mw.clone( default )
		end
		for k, v in pairs( settings ) do
			default[k] = v
		end
	end
 
	-- Выбрать настройки указанные или по умолчанию
	local setting = function( arg )
		return args[arg] or default[arg]
	end
 
	-- Начало сборки кода
	local sprite = mw.html.create( 'span' ):addClass( 'sprite' ) -- создать <span class="sprite">...</span>
	sprite:tag( 'br' ) -- добавить <br>
 
	-- CSS-стили
	-- Метод css от mw.html производит очень медленное экранирование входных данных, что тормозит работу в два раза. В обход
	-- этого стили будут создаваться вручную, и будут передаваться через метод cssText, который делает только экранирование HTML,
	-- что куда быстрее.
	local styles = {}
 
	if setting('таблстилей') then -- использовать CSS-классы для общих настроек
		sprite:addClass(setting( 'имякласса' ) or mw.ustring.lower( setting( 'имя' ):gsub( ' ', '-' ) ) .. '-sprite')
	else
		table.insert(styles, 'background-image: {{FileUrl|' .. ( setting( 'изобр' ) or setting( 'имя' ) .. 'CSS.png' ) .. '}}')
	end
 
	local class = setting('класс')
	if class then
		sprite:addClass(class)
	end
 
	local size = setting( 'разм' )
	local pos = math.abs( setting( 'поз' ) ) - 1
	local sheetWidth = setting( 'формат' )
	local tiles = sheetWidth / size
	local left = pos % tiles * size
	local top = math.floor( pos / tiles ) * size
	local scale = setting( 'масштаб' )
	local autoscale = setting( 'автомасштаб' )
	local align = setting('выравн')
 
	if left > 0 or top > 0 then
		table.insert(styles, 'background-position: -' .. left * scale .. 'px -' .. top * scale .. 'px')
	end
 
	if not autoscale and scale ~= defaultStyle["масштаб"] then
		table.insert(styles, 'background-size: ' .. sheetWidth * scale .. 'px auto')
	end
 
	if size ~= defaultStyle["разм"] or ( not autoscale and scale ~= defaultStyle["масштаб"] ) then
		table.insert(styles, 'height: ' .. size * scale .. 'px')
		table.insert(styles, 'width: ' .. size * scale .. 'px')
	end
 
	if align ~= defaultStyle["выравн"] then
		table.insert(styles, 'vertical-align: ' .. align)
	end
 
	table.insert(styles, setting('css'))
 
	sprite:cssText(table.concat(styles, ';'))
 
	-- Текстовые данные
	local root
	local test = setting('текст')
	local spriteText
	if test then
		root = mw.html.create( 'span' ):addClass( 'nowrap' )
		spriteText = mw.html.create( 'span' ):addClass( 'sprite-text' ):wikitext(test)
	end
 
	local title = setting( 'назв' )
	if title then
		( root or sprite ):attr('title', title)
	end
 
	if not root then
		root = mw.html.create( '' )
	end
	root:node( sprite )
	if spriteText then
		root:node( spriteText )
	end
 
	-- Ссылка
	local link = setting( 'ссылка' ) or ""
	if link ~= "" and mw.ustring.lower( link ) ~= 'none' then
		-- Внешняя ссылка
		if link:find( '//' ) then
			return '[' .. link .. ' ' .. tostring( root ) .. ']'
		end
 
		-- Внутренняя ссылка
		local linkPrefix = setting( 'предссылки' ) or ''
		return '[[' .. linkPrefix .. link .. '|' .. tostring( root ) .. ']]'
	end
 
	return tostring( root )
end
 
-- Весь спрайт
function p.sprite( f )
	local args = f
	if f == mw.getCurrentFrame() then
		args = require( 'Module:ProcessArgs' ).merge( true )
	end
 
	local categories = {}
	local idData = args["данныеID"]
	if not idData then
		local default = {}
		if args["настройки"] then
			default = mw.loadData( 'Модуль:' .. args["настройки"] )
		end
 
		local name = args["имя"] or default["имя"]
		local ids = mw.loadData( 'Модуль:' .. ( args["IDы"] or default["IDы"] or name .. 'Спрайт/ID' ) )["IDы"]
		ids = ids["IDы"] or ids
		local id = mw.text.trim( tostring(args[1] or '') )
		if tonumber(id) then
			idData = {["поз"] = id}
			table.insert(categories, '[[Категория:Страницы, использующие для спрайтов числовые идентификаторы]]')
		else
			idData = ids[id] or ids[mw.ustring.lower( id ):gsub( '[_%-%s%+]+', '-' )]
		end
	end 
 
	local title = mw.title.getCurrentTitle()
 
	-- запретить категории соответственно в подстраницах, в пространствах участников и на страницах обсуждений
	local disallowCats = title.isSubpage or title:inNamespace(2) or (title.namespace % 2 == 1)
	if idData then
		if type(idData) == 'table' then -- новый формат полей ID
			if idData["устарел"] and not disallowCats then
				table.insert( categories, '[[Категория:Страницы с устаревшими названиями спрайтов]]' )
			end
 
			args["поз"] = idData["поз"]
		else -- старый формат
			args["поз"] = idData
		end
	elseif not disallowCats then
		table.insert( categories, '[[Категория:Страницы с отсутствующими спрайтами]]' )
	end
 
	return p.base( args ), table.concat( categories, '' )
end
 
-- Ссылки
function p.link( f )
	local args = f
	if f == mw.getCurrentFrame() then
		args = require( 'Module:ProcessArgs' ).merge( true )
	end
 
	if not args["ID"] then
		args["ID"] = args["ид"] -- для совместимости
	end
 
	local link = args[1]
	if args[1] and not args["ID"] then
		link = args[1]:match( '^(.-)%+' ) or args[1]
	end
	local text = args["текст"] or args[2] or link
 
	args[1] = args["ID"] or args[1]
	args["ссылка"] = args["ссылка"] or link
	args["текст"] = text
 
	return p.sprite( args )
end
 
-- Документация
function p.doc( f )
    local args = f
    if f == mw.getCurrentFrame() then
        args = f.args
    else
        f = mw.getCurrentFrame()
    end
    local settingsPage = mw.text.trim( args[1] )
    local settings = mw.loadData( 'Модуль:' .. settingsPage )
    local idsPage = 'Модуль:' .. ( settings["IDы"] or settings["имя"] .. 'Спрайт/ID' )
    local spriteargs = {}
    for k, v in pairs(args) do
        if type(k) ~= 'number' then
            spriteargs[k] = v
        end
    end
 
    local getProtection = function( title, action, extra )
        local protections = { 'edit' }
        if extra then
            table.insert( protections, extra )
        end
 
        local addProtection = function( protection )
            if protection == 'autoconfirmed' then
                protection = 'editsemiprotected'
            elseif protection == 'sysop' then
                protection = 'editprotected'
            end
 
            table.insert( protections, protection )
        end
 
        local direct = title.protectionLevels[action]
        for _, protection in ipairs( direct ) do
            addProtection( protection )
        end
        local cascading = title.cascadingProtection.restrictions[action] or {}
        if #cascading > 0 then
            table.insert( protections, 'protect' )
        end
        for _, protection in ipairs( cascading ) do
            addProtection( protection )
        end
 
        return table.concat( protections, ',' )
    end
 
    local body
    if args.refresh then
        body = mw.html.create( '' )
    else
        local idsTitle = mw.title.new( idsPage )
        local spritesheet = settings["изобр"] or settings["имя"] .. 'CSS.png'
        local spriteTitle = mw.title.new( 'Файл:' .. spritesheet )
        local idsProtection = getProtection( idsTitle, 'edit' )
        local spriteProtection = getProtection( spriteTitle, 'upload', 'upload,reupload' )
        body = mw.html.create( 'div' ):attr( {
            id = 'spritedoc',
            ['data-idspage'] = idsTitle.id,
            ['data-idsprotection'] = idsProtection,
            ['data-idstimestamp'] = f:callParserFunction( 'REVISIONTIMESTAMP', idsPage ),
            ['data-spritesheet'] = spritesheet,
            ['data-spriteprotection'] = spriteProtection,
            ['data-pos'] = settings["поз"] or 1,
            ['data-refreshtext'] = mw.text.nowiki( '{{#invoke:Спрайт|doc|' .. settingsPage .. '|обновить=1}}' )
        } )
    end
 
    local data = mw.loadData( idsPage )
 
    local sections = {}
    for _, sectionData in ipairs( data["разделы"] or {"Некатегоризованные"} ) do
        local sectionTag = body:tag( 'div' ):addClass( 'spritedoc-section' ):attr( 'data-section-id', sectionData["ID"] )
        -- https://phabricator.wikimedia.org/T73594
        sectionTag:wikitext( '<h3>', sectionData[1], '</h3>' )
        sections[sectionData["ID"]] = { boxes = sectionTag:tag( 'ul' ):addClass( 'spritedoc-boxes' ) }
    end
 
    local keyedData = {}
    for name, idData in pairs( data["IDы"] ) do
        table.insert( keyedData, {
            ["ключ"] = mw.ustring.lower( name ),
            ["имя"] = name,
            ["данные"] = idData
        } )
    end
    table.sort( keyedData, function( a, b )
        return a["ключ"] < b["ключ"]
    end )
 
    for _, data in ipairs( keyedData ) do
        local idData = data["данные"]
        local pos = idData["поз"]
        local section = sections[idData["раздел"]]
        local names = section[pos]
        if not names then
            local box = section.boxes:tag( 'li' ):addClass( 'spritedoc-box' ):attr( 'data-pos', pos )
            local lspriteargs = mw.clone(spriteargs)
            lspriteargs["поз"] = pos
            lspriteargs["настройки"] = settingsPage
 
            box:tag( 'div' ):addClass( 'spritedoc-image' )
                :wikitext( p.base(lspriteargs) )
 
            names = box:tag( 'ul' ):addClass( 'spritedoc-names' )
            section[pos] = names
        end
        local nameElem = mw.html.create( 'li' ):addClass( 'spritedoc-name' )
        local codeElem = nameElem:tag( 'code' ):wikitext( data["имя"] )
 
        if idData["устарел"] then
            codeElem:addClass( 'spritedoc-deprecated' )
        end
        names:wikitext( tostring( nameElem ) )
    end
 
    if args["обновить"] then
        return '', tostring( body )
    end
    return f:callParserFunction( '#widget:Stylesheet', { page = 'SpriteDoc' } ), tostring( body )
end
 
return p