模块:Recipe table

来自The Land of StarLight

此模块的文档可以在模块:Recipe table/doc创建

local m = {}

local i18n = {
	headingDescription = '描述',
	headingIngredients = '材料',
	headingName = '名称',
	headingRecipe = '[[$1]]配方',
	moduleSlot = [[Module:Inventory slot]],
	moduleUi = [[Module:UI]],
	separator = ' +',
	setSeparator = '或',
	tableDescription = '$1配方',
}
local slot = require( i18n.moduleSlot )
local prefixes = slot.i18n.prefixes

--[[Merges a list, or inserts a string
	or table into a table
--]]
local function mergeList( parentTable, content )
	local i = #parentTable + 1
	if content[1] then
		-- Merge list into table
		for _, v in ipairs( content ) do
			parentTable[i] = v
			i = i + 1
		end
	else
		-- Add strings or tables to table
		parentTable[i] = content
	end
end

--[[Loops through the input and output args and parses them into a single table,
	with alias reference data
	
	Identical slots reuse the same table, to allow them to be compared like strings
--]]
local function parseRecipeArgs( args, ingredientArgVals, outputArgs )
	local recipeArgs = {}
	for _, arg in pairs( ingredientArgVals ) do
		recipeArgs[arg] = args[arg]
	end
	for _, arg in pairs( outputArgs ) do
		recipeArgs[arg] = args[arg]
	end
	
	local parsedFrameText = {}
	local parsedRecipeArgs = {}
	for arg, frameText in pairs( recipeArgs ) do
		if frameText then
			local randomise
			for _, oArg in pairs( outputArgs ) do
				if arg == oArg then
					randomise = 'never'
					break
				end
			end
			local frames = not randomise and parsedFrameText[frameText]
			if not frames then
				frames = slot.parseFrameText( frameText, randomise, true )
				parsedFrameText[frameText] = frames
			end
			parsedRecipeArgs[arg] = frames
		end
	end
	
	return parsedRecipeArgs
end

--[[Creates a link (with mod name if specified) with
	any prefix moved outside
--]]
function m.prefixedLink( name, mod )
	local prefix = ''
	for _, thisPrefix in pairs( prefixes ) do
		if name:find( '^' .. thisPrefix .. ' ' ) then
			prefix = thisPrefix .. ' '
			name = name:sub( #prefix + 1 )
			break
		end
	end
    
    if prefix == 'Any ' then
        prefix = slot.i18n.prefixes.any
    elseif prefix == 'Matching ' then
        prefix = slot.i18n.prefixes.matching
    elseif prefix == 'Damaged ' then
        prefix = slot.i18n.prefixes.damaged
    elseif prefix == 'Applicable ' then
        prefix = slot.i18n.prefixes.applicable
    end

	local page = name
	if mod then
		page = slot.i18n.modLink:gsub( '%$1', mod ):gsub( '%$2', name )
	end
	local Autolink = require( [[Module:Autolink]] )
	return table.concat{ prefix, '[[', Autolink.invlink( page, 'linkonly' ), '|', Autolink.invlink( name, 'nolink' ), ']]' }
end

--[[Creates sets of unique items from a set of slots, using the
	original alias name if available
	
	Each set of items are the frames of that slot
--]]
function m.makeItemSets( argVals, parsedArgs )
	local usedItems = {}
	local function addItem( items, arg, frame, alias )
		if alias then
			frame = alias.frame
		end
		
		local uniqName = ( frame.mod or '' ) .. ':' .. frame.name
		if not usedItems[uniqName] then
			usedItems[uniqName] = true
			items[#items + 1] = frame
		end
		
		return alias and alias.length or 1
	end
	
	local itemSets = {}
	local i = 1
	for _, arg in ipairs( argVals ) do
		local frames = parsedArgs[arg]
		if frames then
			local items = {}
			local frameNum = 1
			while frameNum <= #frames do
				local frame = frames[frameNum]
				if frame[1] then
					local subframeNum = 1
					while subframeNum <= #frame do
						local subframe = frame[subframeNum]
						if subframe.name ~= '' then
							local alias = frame.aliasReference and frame.aliasReference[subframeNum]
							subframeNum = subframeNum + addItem( items, arg, subframe, alias )
						else
							subframeNum = subframeNum + 1
						end
					end
					frameNum = frameNum + 1
				elseif frame.name ~= '' then
					local alias = frames.aliasReference and frames.aliasReference[frameNum]
					frameNum = frameNum + addItem( items, arg, frame, alias )
				else
					frameNum = frameNum + 1
				end
			end
			if #items > 0 then
				itemSets[i] = items
				i = i + 1
			end
		end
	end
	
	return itemSets
end

--[[Creates links for the name/ingredients columns out of
	item sets, with the appropriate separators, and optionally
	"Any" and "Matching" prefixes removed.
--]]
function m.makeItemLinks( itemSets, removePrefixes )
	local links = {}
	for i, itemSet in ipairs( itemSets ) do
		local linkSet = {}
		for i2, item in ipairs( itemSet ) do
			local name = item.name
			if removePrefixes then
				-- Remove prefixes and uppercase first letter
				name = name
					:gsub( '^' .. prefixes.any .. ' ', '' )
					:gsub( '^' .. prefixes.matching .. ' ', '' )
					:gsub( '^%l', string.upper )
			end
			local disjunctionA, disjunctionB = name:match("(.-) or (.+)") -- hardcoding "A or B" names in English
			if disjunctionA then
				linkSet[i2] = m.prefixedLink( disjunctionA, item.mod ) 
				    .. '或' 
				    .. m.prefixedLink( disjunctionB, item.mod )
			else
				linkSet[i2] = m.prefixedLink( name, item.mod )
			end
		end
		links[i] = table.concat( linkSet, i18n.setSeparator ..'<br>' )
	end
	
	return table.concat( links, i18n.separator .. '<br>' )
end

-- Creates the table header
function m.makeHeader( recipeType, class, showName, showDescription, multirow )
	class = class or ''
	
	local nameCell = ''
	if showName then
		nameCell = i18n.headingName .. '!!'
	end
	
	local descriptionCell = ''
	if showDescription then
		descriptionCell = '!!class="unsortable"|' .. i18n.headingDescription
	end
	
	local recipeAttribs = ''
	if multirow then
		class = 'sortable collapsible ' .. class
		recipeAttribs = 'class="unsortable"|'
	end
	
	local header = table.concat( {
		' {| class="wikitable ' .. class .. '" data-description="' .. i18n.tableDescription:gsub( '%$1', recipeType ) .. '"',
		'!' .. nameCell ..
		i18n.headingIngredients .. '!!' ..
		recipeAttribs .. i18n.headingRecipe:gsub( '%$1', recipeType ) ..
		descriptionCell
	}, '\n' )
	return header
end

-- Create the contents for the name cell
function m.makeNameCell( name, outputArgs, parsedRecipeArgs, upcoming )
	local cell = {}
	if name then
		cell[1] = name
	else
		cell[1] = m.makeItemLinks( m.makeItemSets( outputArgs, parsedRecipeArgs ), true )
	end
	
	if upcoming then
		if upcoming:find( '%[%[' ) then
			cell[2] = '(' .. upcoming .. ')'
		else
			cell[2] = '([[' .. upcoming .. ']])'
		end
	end
	
	return table.concat( cell, '<br>' )
end

-- Create the contents for the ingredients cell
function m.makeIngredientsCell( ingredients, itemSets )
	return ingredients or m.makeItemLinks( itemSets )
end

--[[Main entry point, creates the table with the relevant DPL vars
	to allow multiple table rows from separate template calls
	
	Also returns the unique ingredients, for categorisation purposes in
	Module:Crafting
--]]
function m.table( args, settings )
	local f = mw.getCurrentFrame()
	
	local multirow = f:callParserFunction( '#dplvar', 'recipetable-multirow' )
	if multirow == '' then
		multirow = nil
	end
	
	local showHead = args.head
	local showFoot = args.foot
	if multirow then
		showHead = nil
	elseif showHead and not showFoot then
		multirow = true
		f:callParserFunction( '#dplvar:set', 'recipetable-multirow', '1' )
	else
		showHead = true
		showFoot = true
	end
		
	local showName = args.showname
	local showDescription = args.showdescription
	if multirow then
		if showHead then
			showName = args.showname or '1'
			f:callParserFunction( '#dplvar:set', 'recipetable-name', showName, 'recipetable-description', showDescription )
		else
			showName = f:callParserFunction( '#dplvar', 'recipetable-name' )
			showDescription = f:callParserFunction( '#dplvar', 'recipetable-description' )
		end
	end
	if showName ~= '1' then
		showName = nil
	end
	if showDescription == '' then
		showDescription = nil
	end
	
	local out = {}
	
	if showHead then
		out[1] = m.makeHeader( settings.type, args.class, showName, showDescription, multirow )
	end
	
	local ingredientArgVals = settings.ingredientArgs
	local outputArgs = settings.outputArgs
	
	local parsedRecipeArgs = args
	if not args.parsed then
		parsedRecipeArgs = parseRecipeArgs( args, ingredientArgVals, outputArgs )
	end
	
	local cells = {}
	
	if showName then
		cells[1] = '!' .. m.makeNameCell( args.name, outputArgs, parsedRecipeArgs, args.upcoming )
	end
	
	local ingredientsItemSets = m.makeItemSets( ingredientArgVals, parsedRecipeArgs )
	cells[#cells + 1] = '|' .. m.makeIngredientsCell( args.ingredients, ingredientsItemSets )
	
	cells[#cells + 1] = '|style="padding:1px;text-align:center"|' .. require( i18n.moduleUi )[settings.uiFunc]( args )
	
	if showDescription then
		cells[#cells + 1] = '|' .. ( args.description or '' )
	end
	
	out[#out + 1] = table.concat( cells, '\n' )
	
	out[#out + 1] = showFoot and '|}' or ''
	if showFoot then
		f:callParserFunction( '#dplvar:set',
			'recipetable-multirow', '',
			'recipetable-name', '',
			'recipetable-description', ''
		)
	end
	
	return table.concat( out, '\n|-\n' ), ingredientsItemSets
end

return m