Module:Sandbox/Bawolff/image slider

-- Fork from Module:Spatial image viewer. That's why there is some extra meaningless stuff here.


-- Similar to User:Bawolff/earth_rotation except using separate images instead of a sprite sheet.
local p = {}
local heightCache

local function getStart( axis, frame )
	return tonumber(frame.args['start'..axis] or 0)
end

-- We assume all images have roughly the same aspect ratio
local function getHeight(frame, imageList)
	if frame.args['height'] then
		return frame.args['height']
	end

	if heightCache == nil then
		local title = mw.title.new( imageList[1], 'File' )
		assert( title and title.file, "Invalid file")
		heightCache = math.ceil((tonumber(frame.args['width']) or 250) * (title.file.height/title.file.width))
	end
	return heightCache
end

local function parseImageList( images )
	local imageList = mw.text.split( mw.text.trim( images ), "\n+" )
	return imageList
end

local function getImages(frame, imageList)
	local html = mw.html.create( 'div' )
	html
		:css( 'width', (frame.args['width'] or 250) .. 'px' )
		:css( 'height', getHeight(frame, imageList) .. 'px' )
		:css( 'position', 'relative' )

	for imgColNumb, img in ipairs( imageList ) do
		local zDefault = 0
		if (imgColNumb-1 == getStart('X',frame)) then
			zDefault = 1
		end
		local formula = 'ifequal(x,' .. (imgColNumb-1) .. ')'
		html:tag( 'div' )
			:css( 'position', 'absolute' )
			:css( 'background', frame.args['background'] or '#fff' )
			:css( 'width', (frame.args['width'] or 250) .. 'px' )
			:css( 'height', getHeight(frame, imageList) .. 'px' )
			:css( 'z-index', 'var( --calculator-x' .. (imgColNumb-1) .. ',' .. zDefault .. ')')
			:wikitext( frame:preprocess(
				'[[File:' .. img .. '|' .. (frame.args['width'] or 250) .. 'x' .. getHeight(frame,imageList) .. 'px]]' ..
				'{{calculator|type=hidden|id=x' .. (imgColNumb-1) .. '|formula=' .. formula .. '|default=' .. zDefault .. '}}'
			) )
	end
	-- For some reason this doesn't seem to work on Wikipedia android app.
	if frame.args.fallbackImage then
		html:tag( 'div' )
			:css( 'position', 'absolute' )
			:css( 'background', frame.args['background'] or '#fff' )
			:css( 'width', (frame.args['width'] or 250) .. 'px' )
			:css( 'height', getHeight(frame, imageList) .. 'px' )
			:css( 'z-index', '3')
			:addClass( 'calculatorgadget-fallback' ) -- if calc enabled with will be display:none
			:wikitext( frame:preprocess(
				'[[File:' .. frame.args.fallbackImage .. '|' .. (frame.args['width'] or 250) .. 'x' .. getHeight(frame,imageList) .. 'px]]'
			) )
	end
	return tostring(html)
end

function p.makeViewer(frame)
	local pFrame = frame:getParent()
	local args = pFrame.args
	local width = args['width'] or 250
	assert( args['images'], "Images argument required")
	local imageList = parseImageList( args['images'] )

	local descId = mw.uri.anchorEncode( 'spatialviewer-desc-' .. imageList[1] )
	local html = mw.html.create( 'div' )
	html:addClass( 'spatialviewer calculator-container' )
	-- TODO dark mode support. We set an explicit background to make sure that if some of the images
	-- are different dimensions or transparent they don't show through each other. This messes up some
	-- dark mode related styles
	html:addClass( 'notheme' )
	html:attr( 'aria-describedby', descId )
	html:cssText( args['style'] )
	html:css( 'float', args['float'] )
		:css( 'display', 'grid' )
		:css( 'width', 'max-content' )
		:css( 'grid-template-columns',  width .. 'px' )
		:css( 'border', '1px var(--border-color-base,#a2a9b1) solid' )
		:css( 'padding', '0.5em' )
		:css( 'gap', '3px' )
		:css( 'background', args['background'] or '#fff' ) -- Should this be var(--background-color-base, '#fff') ?

	html:tag('div')
		:addClass( 'calculator-skip-link' )
		:wikitext( '[[#' .. descId .. '|Skip image slideshow]]' )
		:wikitext( frame:extensionTag{ name = 'templatestyles', content = '', args = { src = 'Module:Spatial image viewer/skip.css' } } )

	html:tag( 'div' )
		-- The Wikipedia app has some dynamic image loading code that messes up the intrinsic height.
		:css( 'min-height', getHeight(pFrame, imageList) .. 'px' )
		:wikitext(
			getImages( pFrame, imageList )
		)

	local sliderLabel = ''
	if pFrame.args['slider-label'] ~= nil then
		sliderLabel = 'aria-label=' .. pFrame.args['slider-label'] .. '|'
	end
	html:tag( 'div' )
		:css( 'display', 'none' ) -- Only display if calculator gadget enabled
		:addClass( 'calculatorgadget-enabled' )
		:css( 'margin', 'auto' )
		:wikitext(
			frame:preprocess( '{{calculator|type=range|min=0|id=x|max=' .. (#imageList-1) .. '|' .. sliderLabel .. 'default=' .. getStart('X', frame ) .. '}}' )
		)
	if args.caption then 
		html:tag( 'div' )
			:css( 'border-top', '1px solid #eaecf0' ) -- caption styles could probably be improved
			:css( 'margin-top', '0.5em' )
			:css( 'padding-top', '0.5em' )
			:attr( 'id', descId )
			:wikitext( frame:preprocess( args.caption ) )
	end

	return tostring(html)
end
return p