Minecraft Wiki
(похоже, придётся делать именно так)
(удалён давно не используемый устаревший формат спрайтов без категорий)
Строка 203: Строка 203:
 
local disallowCats = args["некат"] or title.isSubpage or title.isTalkPage or title:inNamespace(2)
 
local disallowCats = args["некат"] or title.isSubpage or title.isTalkPage or title:inNamespace(2)
 
if idData then
 
if idData then
 
if idData["устарел"] and not disallowCats then
if type(idData) == 'table' then -- новый формат полей ID
 
 
table.insert(categories, '[[Категория:Страницы с устаревшими названиями спрайтов]]')
if idData["устарел"] and not disallowCats then
 
table.insert(categories, '[[Категория:Страницы с устаревшими названиями спрайтов]]')
 
end
 
 
args["поз"] = idData["поз"]
 
args["страница"] = idData["страница"]
 
else -- старый формат
 
args["поз"] = idData
 
 
end
 
end
 
 
args["поз"] = idData["поз"]
 
args["страница"] = idData["страница"]
 
elseif not disallowCats then
 
elseif not disallowCats then
 
table.insert(categories, '[[Категория:Страницы с отсутствующими спрайтами]]')
 
table.insert(categories, '[[Категория:Страницы с отсутствующими спрайтами]]')

Версия от 12:26, 5 февраля 2019

Этот модуль реализует систему спрайтов Minecraft Wiki. Больше информации расположено на указанной странице и на страницах документации шаблонов спрайтов.

Аргументы страницы, вызвавшей модуль, автоматически объединяются с аргументами, переданными модулю напрямую (последние перезаписывают первые). Также все аргументы нормализуются (при этом удаляются стоящие в начале и в конце пробелы, а пустые аргументы задаются как равные nil).

Зависит от

-- Модуль для отображения иконок из таблиц спрайтов.
-- Внимание: Неосторожные изменения модуля могут привести к проблемам на большом количестве статей!

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 = {}
	
	local page
	if setting('страница') ~= '' then
		page = setting('страница')
	elseif setting("главная_страница") ~= '' then
		page = setting("главная_страница")
	end
	if not page then
		page = ''
	end
	
	if setting('таблстилей') then -- использовать CSS-классы для указания файла
		sprite:addClass(setting('имякласса') or mw.ustring.lower(setting('имя'):gsub(' ', '-')) .. '-sprite')
	else -- указывать файл напрямую
		table.insert(styles, 'background-image: {{FileUrl|' .. (setting('изобр') or setting('имя') .. page .. 'CSS.png') .. '}}')
	end
	
	local class = setting('класс') -- CSS-класс спрайта
	if class then
		sprite:addClass(class)
	end
	
	-- Настройки страницы многостраничного спрайта
	local scaleq = 1
	if setting(page) then
		args['масштаб'] = args['масштаб'] or 1
		scaleq = setting(page)['множитель'] or 1
		args['разм'] = setting(page)['разм'] or setting('разм')
		args['формат'] = setting(page)['формат'] or setting('формат')
	else
		scaleq = setting('множитель') or 1
	end
	
	local size = setting('разм') -- размер спрайта в пикселях
	local v_size = setting('верт_разм') or 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 ) * v_size -- вертикальная координата спрайта
	local scale = setting('масштаб') * scaleq -- масштаб спрайта (во сколько раз увеличить или уменьшить размер)
	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: ' .. v_size * scale .. 'px')
		table.insert(styles, 'width: ' .. size * scale .. 'px')
	end
	
	-- Выравнивание
	if align ~= defaultStyle["выравн"] then
		table.insert(styles, 'vertical-align: ' .. align)
	end
	
	-- Дополнительный CSS-код, указанный в параметре
	table.insert(styles, setting('css'))
	
	-- Применение полученных CSS-стилей к спрайту.
	sprite:cssText(table.concat(styles, ';'))
	
	-- Собственно спрайт.	
	local root

	-- Текстовые данные
	local text = setting('текст')
	local spriteText
	if text and (text ~= 'нет') then
		root = mw.html.create('span'):addClass('nowrap')
		spriteText = mw.html.create('span'):addClass('sprite-text'):wikitext(text)
	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

-- Данная функция предварительно готовит данные для функции p.base, а затем вызывает её. Её следует вызывать на страницах вики-проекта
-- (через любой из спрайтовых шаблонов)
function p.sprite(f)
	-- Параметры
	local args = f
	if f == mw.getCurrentFrame() then
		args = require('Модуль: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 = args["некат"] or title.isSubpage or title.isTalkPage or title:inNamespace(2)
	if idData then
		if idData["устарел"] and not disallowCats then
			table.insert(categories, '[[Категория:Страницы с устаревшими названиями спрайтов]]')
		end
		
		args["поз"] = idData["поз"]
		args["страница"] = idData["страница"]
	elseif not disallowCats then
		table.insert(categories, '[[Категория:Страницы с отсутствующими спрайтами]]')
	end
	
	-- Передача полученных данных функции p.base, её вызов и простановка категорий.
	return p.base(args), table.concat(categories, '')
end

-- Ссылки
function p.link(f)
	-- Параметры
	local args = f
	if f == mw.getCurrentFrame() then
		args = require('Модуль: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
	
	-- Указание аргументов для функции p.sprite
	args[1] = args["ID"] or args[1]
	args["ссылка"] = args["ссылка"] or link
	args["текст"] = text
	
	-- Вызов p.sprite
	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