Jump to content

Module:Animation

From Creopedia

This documentation is transcluded from Module:Animation/doc.

Implements Template:Animation.


require( 'strict' )

--- @class AnimationProps
--- @field images string[]
--- @field size string|nil
--- @field width string|nil
--- @field height string|nil
--- @field fps number|nil
--- @field offset table|nil
--- @field offset.top string|nil
--- @field offset.right string|nil
--- @field offset.bottom string|nil
--- @field offset.left string|nil

local p = {}


--- @param props AnimationProps
local function normalizeProps( props )
	props.size = props.size or '80px'
	props.width = props.width or props.size
	props.height = props.height or 'auto'
	props.fps = tonumber( props.fps ) or 12
	props.offset = props.offset or {}
end

--- Process the wikitext args
---
--- @param args table
--- @return table
local function processArgs( args )
	local images = {}
	for k, v in pairs( args ) do
		if k ~= 'size' and k ~= 'fps' then
			images[k] = v
		end
	end
	return {
		images = images,
		size = args.size,
		width = args.width,
		height = args.height,
		offset = {
			top = args.offsetTop,
			right = args.offsetRight,
			bottom = args.offsetBottom,
			left = args.offsetLeft,
		},
		fps = args.fps,
	}
end

--- Determine the frame count based on the images
--- We start from the end because mw.title.file.exists is expensive
---
--- @param images string[]
--- @return number
local function determineFrameCount( images )
	for i = #images, 1, -1 do
		local title = mw.title.new( images[i], 'File' )
		if title and title.file.exists then
			return i
		end
	end
	return 0
end

--- @param file string
--- @param size string
--- @return string
local function getFileWikitext( file, size )
	-- `link=` is needed make sure the image is not clickable.
	return string.format( '[[File:%s|%s|link=]]', file, size )
end

--- @param props AnimationProps
--- @param frameCount number
--- @return mw.html
local function getFramesHtml( props, frameCount )
	local html = mw.html.create( 'div' ):addClass( 't-animation-frames' )

	for i = 1, frameCount do
		local image = props.images[i]
		html:tag( 'div' ):addClass( 't-animation-frame' )
			:css( '--t-animation-frame-index', tostring( i - 1 ) )
			:wikitext( getFileWikitext( image, props.size ) )
			:done()
	end

	return html
end

--- @param props AnimationProps
--- @return string
function p.render( props )
	normalizeProps( props )

	local frameCount = determineFrameCount( props.images )

	local root = mw.html.create( 'div' ):addClass( 't-animation' )

	root
		:css( '--t-animation-width', props.width )
		:css( '--t-animation-height', props.height )
		:css( '--t-animation-fps', tostring( props.fps ) )
		:css( '--t-animation-frame-count', frameCount )
		:css( '--t-animation-offset-top', props.offset.top )
		:css( '--t-animation-offset-right', props.offset.right )
		:css( '--t-animation-offset-bottom', props.offset.bottom )
		:css( '--t-animation-offset-left', props.offset.left )
		:node( getFramesHtml( props, frameCount ) )

	return mw.getCurrentFrame():extensionTag {
		name = 'templatestyles', args = { src = 'Module:Animation/styles.css' }
	} .. tostring( root )
end

--- Wikitext entry point for the module
---
--- @param frame mw.frame
--- @return string
function p.main( frame )
	local getArgs = require( 'Module:Arguments' ).getArgs
	return p.render( processArgs( getArgs( frame ) ) )
end

return p