Module:Graphical updates

Documentation for this module may be created at Module:Graphical updates/doc

--<nowiki>

local p = {}

local params = require('Module:Paramtest')
local clean = require('Module:Clean image2').clean

function p.main(frame)
	local args = frame:getParent().args

	local function endDateFormat(revNum, dateType)
		local dateTypes = {['main']='', ['upper']='up', ['middle']='mid'}
		local endDateType = string.format('rev%s%send', revNum, dateTypes[dateType])

		if params.has_content(args[endDateType]) then															-- If an ending date has been explicitely specified, use that one 
			endDate = args[endDateType]
		elseif params.has_content(args[string.format('rev%s%sstart', revNum +1, dateTypes[dateType])]) then		-- If there is a starting date defined for the next revision, use that as the ending date
			endDate = args[string.format('rev%s%sstart', revNum +1, dateTypes[dateType])]
		else																									-- If not, then the current revision is the last revision
			endDate = 'Present'
		end	
	return endDate
	end
	
	local function imageFormat(revNum, rowLevel)
		
		local rowImages = {['upper']='img', ['middle']='midimg', ['lower']='lowimg'}
		local rowDefaultImage  = rowImages[rowLevel]
		local rowSpecificImage = string.format('rev%s%s', revNum, rowImages[rowLevel])		
		
		if params.has_content(args[rowSpecificImage]) then																	-- If a filename has been specified, use that one
			formattedImage = args[rowSpecificImage]
		elseif params.has_content(args[rowDefaultImage]) then																-- If not, use a variation of the base filename
			local imageArg = args[rowDefaultImage]
			if (params.has_content(args['rev2start']) and params.is_empty(args['rev3start'])) then							-- Check if there are only two revisions in the gallery
				if revNum == 1 then
					formattedImage = string.format('%s (historical)', imageArg)												-- If so, append (historical) to the base filename for the first revision
				elseif revNum == 2 then
					formattedImage = imageArg
				end
			elseif params.is_empty(args['rev'..(revNum+1)..'start']) then													-- We've made it so far, so there must be three or more revisions
				formattedImage = imageArg																					-- In which case, use the base filename for the last revision
			else
				local commaSeparatedHistorical = string.format('[[File:%s (historical, v%s).png]]', imageArg, revNum)
				local cleanComma = clean({file=commaSeparatedHistorical})
				if cleanComma == nil then																					-- Check if there is a file with (historical, v#) appended, and use it if so
					formattedImage = string.format('%s (historical v%s)', imageArg, revNum)
				else																										-- If not, append (historical v#) to the base filename
					formattedImage = string.format('%s (historical, v%s)', imageArg, revNum)
				end
			end
		end
	return formattedImage
	end
	
	local function sizeFormat(revNum, rowLevel)
		local rowSizes = {['upper']='size', ['middle']='midsize', ['lower']='lowsize'}
		local fallbackSizes = {['upper']='|300x150px', ['middle']='', ['lower']=''}				-- Define the base sizes per image row, in case no size is specified at all
		local rowDefaultSize	= rowSizes[rowLevel]
		local rowSpecificSize	= string.format('rev%s%s', revNum, rowSizes[rowLevel])		
		
		if params.has_content(args[rowSpecificSize]) then										-- If a specific size is defined for a revision, use that one 
			formattedSize = '|' .. args[rowSpecificSize]		
		elseif params.has_content(args[rowDefaultSize]) then									-- If not, use the size specified for all images in that row (by the user)
			formattedSize = '|' .. args[rowDefaultSize]
		else																					-- If neither is specified, use the base size for that row (defined in the table above)
			formattedSize = fallbackSizes[rowLevel]
		end
	return formattedSize
	end

--	Iterate through the arguments, and for each rev#start that is defined, compile a list of parameters relevant to that revision and add them as an entry to the rev_list table
--	Parameters pertaining to optional rows are checked for content, and default to nil if empty
--	Formatted size and formatted filename are combined prior to being entered into the table
	local function revisionList()
		local rev_list = {}
		for i=1,10,1 do
			local definedRevision = args['rev'..i..'start']
			if definedRevision and params.has_content(definedRevision) then
				local startDate			= params.default_to(args['rev'..i..'start'],'?')
				local endDate			= endDateFormat(i, 'main')
				local upperStartDate	= params.default_to(args['rev'..i..'upstart'], nil)
				local upperEndDate	    = params.has_content(args['rev'..i..'upstart']) and endDateFormat(i, 'upper') or nil
				local middleStartDate	= params.default_to(args['rev'..i..'midstart'], nil)
				local middleEndDate		= params.has_content(args['rev'..i..'midstart']) and endDateFormat(i, 'middle') or nil
				local upperImage		= imageFormat(i, 'upper')
				local upperSize			= sizeFormat(i, 'upper')
				local middleImage		= imageFormat (i, 'middle')
				local middleSize		= sizeFormat (i, 'middle')
				local lowerImage		= imageFormat (i, 'lower')
				local lowerSize			= sizeFormat (i, 'lower')
				
				local resizedUpperImage = string.format('[[File:%s.png%s]]', upperImage, upperSize)
				local resizedMiddleImage = params.has_content(args['midimg']) and string.format('[[File:%s.png%s]]', middleImage, middleSize) or nil
				local resizedLowerImage = params.has_content(args['lowimg']) and string.format('[[File:%s.png%s]]', lowerImage, lowerSize) or nil

				table.insert(rev_list, {
					startDate = startDate,
					endDate = endDate,
					upperImage = resizedUpperImage,
					middleImage = resizedMiddleImage,
					lowerImage = resizedLowerImage,
					upperStartDate = upperStartDate,
					upperEndDate = upperEndDate,
					middleStartDate = middleStartDate,
					middleEndDate = middleEndDate,
				} )
			end
		end
		return rev_list
	end
	
--	Shove that big table of revisions into a variable called revisions	
	local revisions = revisionList()

--	Build a div containing the starting date, a stylised horizontal rule, and ending date
	local function buildDateRange(revisions, revNum, dateRangeType)
		local startDateTypes = {['main']= 'startDate', ['upper']= 'upperStartDate', ['middle']= 'middleStartDate'}
		local endDateTypes = {['main']= 'endDate', ['upper']= 'upperEndDate', ['middle']= 'middleEndDate'}
		local startingDate = startDateTypes[dateRangeType]
		local endingDate   = endDateTypes[dateRangeType]
		local dateRange = mw.html.create('div')
		dateRange
					:css({ ['text-align'] = 'center'})
					:wikitext(revisions[revNum][startingDate])
						:tag('hr')
							:css({
									['border-bottom'] = '0.175em solid var(--body-dark)',
									['border-top'] = 'hidden',
									['margin'] = '0 0.45em'
								})
							:done()
						:wikitext(revisions[revNum][endingDate])
					:done()
						
	return tostring(dateRange)
	end	

-- The buildRows function will be called by buildColumns later on, and will return a div for each row with content in the revisions table for that specific column
	local function buildRows(revisions, revisionNum)
		local rows = mw.html.create(nil)
			rows
				:tag('div'):wikitext(revisions[revisionNum]['upperImage']):done()
			if params.has_content(args['rev' .. revisionNum .. 'upstart']) then
				rows
					:wikitext(buildDateRange(revisions, revisionNum, 'upper'))
			end
			if params.has_content(args['midimg']) then
				rows:tag('div'):wikitext(revisions[revisionNum]['middleImage']):done()
			end
			if params.has_content(args['rev' .. revisionNum .. 'midstart']) then
				rows
					:wikitext(buildDateRange(revisions, revisionNum, 'middle'))
			end
			if params.has_content(args['lowimg']) then
				rows:tag('div'):wikitext(revisions[revisionNum]['lowerImage']):done()
			end
	return tostring(rows)
	end

--	Determine the number of CSS grid rows to span for the parent div holding a single column
	local function gridSpanFormat(revNum)
		local gridSpan = 2
		if params.has_content(args['midimg']) then
			gridSpan = gridSpan + 1
		end
		if params.has_content(args['lowimg']) then
			gridSpan = gridSpan + 1
		end
		if params.has_content(args['rev' .. revNum .. 'upstart']) then
			gridSpan = gridSpan + 1
		end
		if params.has_content(args['rev' .. revNum .. 'midstart']) then
			gridSpan = gridSpan + 1
		end
		gridSpan = string.format('auto / span %s', gridSpan)
	return gridSpan
	end
	
--  So let's put it all together
--	For each entry into the revisions table, which contains the relevant args for a single historical revision, we're going to build a column
--	First it makes a div holding the column, which is then made a subgrid of the grid container, so that the cells will align vertically between different columns
--	We call the gridSpanFormat function to tell it how many rows the container of the column has to span
--	Then we fill it with the buildRows function, which returns a div for each relevant row entry for that column
--	Finally we put the main date range at the bottom to close out the column
--	Each iteration is then added to the columns variable, which will grow to contain the whole gallery by the end

	local function buildColumns(revisions)
		local columns = mw.html.create(nil)
		for i, v in ipairs(revisions) do
			columns
				:tag('div') 
				:css({
						['display'] = 'grid',
						['grid-template-rows'] = 'subgrid',
						['grid-row'] = gridSpanFormat(i),
						['justify-items'] = 'center',
						['align-items'] = 'center',
					})
				:wikitext(buildRows(revisions, i))
				:wikitext(buildDateRange(revisions, i, 'main'))
		end
		return tostring(columns)
	end
	
-- Now we create a div which will hold all the columns, and we make it a CSS grid container
-- We tell it to fill as many columns as can fit the screen along implicit grid tracks, which are as wide as the content of those columns at minimum
-- We also need to set a max-width to be divvied up by the auto-placed columns
	local revisionsTable = mw.html.create('div')
	local gridColumnWidth	= params.has_content(args['colwidth']) and args['colwidth'] or '150'
	local gridWidth			= params.has_content(args['colwidth']) and (args['colwidth'] + 25) * #revisions or 250 * #revisions
		revisionsTable
				:css({	['margin-top'] = '20px',
						['max-width'] = string.format('%spx', gridWidth),
						['display'] = 'grid',
						['gap'] = '15px 25px',
						['grid-template-columns'] = string.format('repeat(auto-fill, minmax(min-content, %spx) )', gridColumnWidth),
					})	
				:wikitext(buildColumns(revisions))
	
	return revisionsTable
end

return p