Module:Character infobox

From Absit Omen Lexicon

Documentation for this module may be created at Module:Character infobox/doc

local p = {}
local ib = require("Module:Infobox")
local cfg = mw.loadData( "Module:Character infobox/config" )
local categories = {}
local indicators = {}
local trackingCats = {}
local comments = ''

function p._infobox(args)

	processArgs(args)

	local html = ib.infoboxtable('character')

	local image = args.image or 'avatar.png'

	local title = args.biography and 
		'[' .. args.biography .. ' ' .. args.fullName .. ']' or args.fullName


	html:node(ib.addHeader(image,title))

	for _, section in ipairs(cfg.sectionOrder) do
		if (section == 'education' and args.house ~= 'None') or section ~= 'education' then
			html:node(ib.addSection(cfg[section], cfg[section .. '_label'], cfg.specialRows, args, args.debug))
		end
	end

	-- Footer
	args.memberBoard = args.memberBoard and args.memberBoard or ''
	html:tag('tr')
		:addClass('ib-footer')
		:tag('td')
		:attr('colspan', '2')
		:wikitext('[' .. args.memberBoard .. ' ' ..  args.member .. ']')
		:node(processCategories(args))
		:wikitext(frame:callParserFunction('DEFAULTSORT', {args.sort_by}))

	ib.cargoStore(args, cfg.cargo.Characters, 'Characters')

	local wrapper = ib.infoboxdiv('character')

	wrapper:node(html)
	wrapper:tag('div')
			:css('display', 'none')
			:wikitext(comments)
	return tostring(wrapper)
end


function p.infobox(frame)
	local getArgs = require('Module:Arguments').getArgs
	local args = getArgs(frame)

	return p._infobox(args)
end

function processArgs(args)

	local Date = require("Module:Date")._Date

	args.status = args.status and args.status or 'inactive'
	if args.status == 'npc' then
		args.npc = args.npc and args.npc or 'closed'
	end

	-- create dates from parts, can be a partial date, year required
	local birthDate = Date(args.birthYear, args.birthMonth or 'partial', args.birthDay or 'partial')
	local deathDate = Date(args.deathYear, args.deathMonth or 'partial', args.deathDay or 'partial')
	-- Set current date for age calculation to either current date or death date
	local currentDate = deathDate or Date('currentdate')
	local dateDiff = currentDate:subtract(birthDate, 'wantrange')
	-- if dateDiff:age returns a table, age is a range otherwise just one number
	if dateDiff then
		args.age = type(dateDiff:age('y', {['range'] = true})) == 'table' and 
		table.concat(dateDiff:age('y', {['range'] = true}), '-') or dateDiff:age('y')
		args.ageCargo = dateDiff:age('y')
		if args.age == nil then
			args.age = ''
			table.insert(trackingCats, 'Age Calculation Issue')
		end
		if args.ageCargo == nil then
			args.ageCargo = ''
			table.insert(trackingCats, 'Age Calculation Issue')
		end
	end
	if birthDate then
		args.birthDate = birthDate:text()
		args.birthDateCargo = birthDate:text('ymd')
		if not(deathDate) then
			args.birthDate = args.birthDate .. ', ' .. args.age
		end
	end
	if deathDate then
		args.deathDate = deathDate:text() .. ', ' .. args.age
		args.deathDateCargo = deathDate:text('ymd')
	end

	-- Full name
	-- If givenname and surname arent set, use the page title
	if args.givenname and args.surname then
		local nameOrder = {'givenname', 'middlename', 'surname', 'suffix', 'maidenname'}
		local sortOrder = {'surname', 'givenname', 'middlename', 'suffix'}
		local name = {}
		local sort = {}
		-- Loop through name parts in args in proper order
		for i = 1, #nameOrder do
			-- If its the pre-married name, add () around it
			if nameOrder[i] == 'maidenname' and args[nameOrder[i]] then
				table.insert(name, '(' .. args[nameOrder[i]] .. ')')
			-- otherwise if the part of the name is set, add it to the table
			elseif args[nameOrder[i]] then
				table.insert(name, args[nameOrder[i]])
			end
		end
		-- Loop through name parts in args in sort order
		for i = 1, #sortOrder do
			-- if the part of the name is set, add it to the table
			if args[sortOrder[i]] then
				table.insert(sort, args[sortOrder[i]])
			end
		end
		-- Concat and add the full name to args
		args.fullName = table.concat(name, ' ')
		args.sort_by = table.concat(sort, ' ')
	else
		args.fullName = mw.title.getCurrentTitle().text
		args.sort_by = mw.title.getCurrentTitle().text
	end

	-- Grad Year
	if args.birthYear and args.birthMonth and args.hideYear ~= 'Yes' then
		args.gradYear = args.birthYear + 11 + 7
		if tonumber(args.birthMonth) >= 9 then
			args.gradYear = args.gradYear + 1
		end
	end

	-- School Year
	if not(args.deathDate) and args.gradYear then
		year = args.gradYear - currentDate:text('%{year}')
		if tonumber(currentDate:text('%{month}')) <= 8 then
			year = year + 1
		end
		if year > 0 and year < 8 then
			local yearTxt = {'Seventh Year', 'Sixth Year', 'Fifth Year', 'Fourth Year', 'Third Year', 'Second Year', 'First Year'}
			args.schoolYear = yearTxt[year]
		end
	end

	-- School
	if args.house then
		args.house = args.house:gsub("(%a)([%w_']*)", ib.tchelper)
	end
	schoolCheck(args)

	-- Bio/Network/Thread links
	if args.biography or args.network or args.threads then
		html = mw.html.create('ul')
		html:addClass('hlist')
		if args.biography then
			html:tag('li'):wikitext('[' .. args.biography .. ' Biography]')
			html:done()
		end
		if args.network then
			html:tag('li'):wikitext('[' .. args.network .. ' Network]')
			html:done()
		end
		if args.threads then
			html:tag('li'):wikitext('[' .. args.threads .. ' Threads]')
			html:done()
		end
		args.links = tostring(html)
	end

	-- Family Processing
	frame =  mw.getCurrentFrame()

	--Parents
	args.fatherProcessed = args.father and frame:callParserFunction('#formredlink', {'test', target=args.father, form="Character"})
	args.stepmotherProcessed = args.stepmother and frame:callParserFunction('#formredlink', {'test', target=args.stepmother, form="Character"})
	args.stepfatherProcessed = args.stepfathr and frame:callParserFunction('#formredlink', {'test', target=args.stepfather, form="Character"})
	args.motherProcessed = args.mother and frame:callParserFunction('#formredlink', {'test', target=args.mother, form="Character"})
	args.adoptiveFatherProcessed = args.adoptiveFather and frame:callParserFunction('#formredlink', {'test', target=args.adoptiveFather, form="Character"})
	args.adoptiveMotherProcessed = args.adoptiveMother and frame:callParserFunction('#formredlink', {'test', target=args.adoptiveMother, form="Character"})

	-- Siblings
	if args.siblings then 
		siblings = mw.html.create('ul')
		siblingsCargo = {}
		for name in string.gmatch(args.siblings, '([^\n]+)\n?') do
			siblings:tag('li')
				:wikitext(frame:callParserFunction('#formredlink', {'test', target=name, form="Character"}))
				:done()
			table.insert(siblingsCargo, name)
		end

	    
		args.siblingsProcessed = tostring(siblings)
		args.siblingsCargo = table.concat( siblingsCargo, ";")
	end
	-- Children
	if args.children then
		children = mw.html.create('ul')
		childrenCargo = {}
		for name in string.gmatch(args.children, '([^\n]+)\n?') do
			children:tag('li')
				:wikitext(frame:callParserFunction('#formredlink', {'test', target=name, form="Character"}))
				:done()
			table.insert(childrenCargo, name)
		end

		args.childrenProcessed = tostring(children)
		args.childrenCargo = table.concat( childrenCargo, ";")
	end

	-- Extended Family
	if args.extFamily then
		extFamily = mw.html.create()
		for name, relation in string.gmatch(args.extFamily, '([^%(%)]+)%s+%(([^\n]+)%)\n?') do
			extFamily:tag('li')
				:wikitext(frame:callParserFunction('#formredlink', {'test', target=name, form="Character"}) .. ' (' .. relation .. ')')
				:done()
		end

		args.extFamilyProcessed = tostring(extFamily)
	end
  -- Family Tree
  -- Weirdness with parser required removing http(s) from link so that it would not be processed and duplicated
  	if args.familyTree then
		familyTree = string.match(args.familyTree or '', '^https?:(.*)')
		if familyTree then
			args.familyTree = '['..  familyTree .. ' Family Tree]'
		end
	end

-- Categories based on parameters except occupation
	if args.deathDate then
		table.insert(categories, 'Deceased')
		if args.species and string.lower(args.species) == 'ghost' then
			table.insert(categories, 'Characters')
		else
			table.insert(categories, 'Retired Characters')
		end
	else
		table.insert(categories, 'Characters')

		status = args.status and string.lower(args.status)
		if status == 'retired' then
			table.insert(categories, 'Retired Characters')
		elseif status == 'active' then
			table.insert(categories, 'Active')
		elseif status == 'inactive' then
			table.insert(categories, 'Inactive')
		elseif status == 'secondary' then
			table.insert(categories, 'Secondary Characters')
		elseif status == 'npc' then
			table.insert(categories, 'Non-Player Character')
			table.insert(indicators, {'character',
				'[[File:' .. args.npc .. '.svg|x35px|link=NPC Permissions|alt=' .. args.npc .. ' permission]]'})
		end
	end
	if (dateDiff and dateDiff.partial) or args.birthDate == nil then
		table.insert(trackingCats, 'Unknown Birthdate')
	end

	if args.species then table.insert(categories, args.species) end

	if args.canon == 'yes' then
		table.insert(categories, 'Canon')
		table.insert(indicators, {'canon', '[[File:Canon.svg|x35px|link=Canon|alt=Canon Character]]'})
	end

	if args.blood then
		table.insert(categories, args.blood)
	else
		table.insert(trackingCats, 'Unknown Blood Status')
	end
	if args.specialAbilities then
		if string.find(args.specialAbilities, ',') then
			for ability in mw.text.gsplit(args.specialAbilities, ',', true) do
				table.insert(categories, ability)
			end
		else
			table.insert(categories, args.specialAbilities)
		end
	end

	if args.biography == nil and args.status ~= 'npc' then
		table.insert(trackingCats, 'Missing Biography')
	end

end

function schoolCheck(args)
	if args.hideYear == 'Yes' then
		table.insert(trackingCats, 'HideYear Tracker')
	end
	for _, value in ipairs(cfg.schools.Hogwarts) do
		if args.house == value then 
			args.school = 'Hogwarts'
			args.house =  args.house
			table.insert(indicators, {'house', '[[File:' .. args.house .. '.png|x35px|link=' .. args.house .. ']]'})
			yearCategories(args)
			return
		end
	end

	for _, value in ipairs(cfg.schools.Schools) do
		if args.house == value then 
			args.school = args.house
			args.house = nil
			if args.school == 'Durmstrang Institute' or args.school == 'Beauxbatons Academy' then
				table.insert(indicators, {'house', '[[File:' .. args.school .. '.png|x35px|link=' .. args.school .. ']]'})
			end
			yearCategories(args)
			return
		end
	end
	if args.house == 'Home Schooled' then
		args.homeschool = 'Home Schooled'
		args.house = nil
		table.insert(categories, 'Home Schooled')
	elseif args.house == 'Undecided' then
		args.schoolHouse = args.house
		args.house = nil
		table.insert(categories, 'Undecided House or School')
	elseif args.house == 'Other' or args.house == 'Other School' then 
		args.school = args.house
		args.house = nil
		table.insert(categories, 'Other School')
	elseif args.house == 'None' then
		table.insert(trackingCats, 'Hidden Education Tracker')
	else
		args.schoolHouse = 'Unknown House or School'
		args.house = nil
		table.insert(trackingCats, 'Unknown House or School')
	end
end

function yearCategories(args)
	local house = args.house and args.house or args.school
	
	if args.gradYear then
		table.insert(categories, 'Class of ' .. args.gradYear)
		table.insert(categories, house .. ' Class of ' .. args.gradYear)
	end

	if args.schoolYear then
		table.insert(categories, args.schoolYear)
		table.insert(categories, house)
		table.insert(categories, house .. ' ' .. args.schoolYear)
	else
		table.insert(categories, house .. ' Alumni')
	end
end


function p.getParams(args)
	local tbl = {}
	for k, v in pairs(args) do
		tbl[#tbl+1] = ('*%s: %s'):format(k, v)
	end
	return table.concat(tbl, '\n')
end

--Sections

function p.classes(frame)
	local getArgs = require('Module:Arguments').getArgs
	local args = getArgs(frame)
	ib.cargoStore(args, cfg.cargo.Classes, 'Classes')
	return '<div style="display:none">Classes Included</div>'
end


function p.occupation(frame)

	local getArgs = require('Module:Arguments').getArgs
	local args = getArgs(frame)

	local occupationOrder = {'office', 'division', 'department', 'organization'}
	local occupation_label = {}

	occupation_label.title = args.current == 'no' and 'Former Title' or 'Title'

	processOccupationArgs(args, occupation_label)

	local html = mw.html.create()
	if args.title then
		html
			:tag('dt')
			:wikitext(occupation_label.title .. ': ' .. args.title)
			:done()
	end
	if args.startDate or args.endDate then
		html
			:tag('dd')
			:wikitext((args.startDate or '') .. '-' .. (args.endDate or ''))
			:done()
	end

	for _, value in ipairs(occupationOrder) do
		if args[value] then
			if occupation_label[value] then
				html
					:tag('dd')
					:wikitext(occupation_label[value]  .. ': [[' .. args[value] .. ']]')
					:done()
			else
				html
					:tag('dd')
					:wikitext('[[' .. args[value] .. ']]')
					:done()

			end
		end
	end
	html:node(processCategories(args))

	ib.cargoStore(args, cfg.cargo.Occupations, 'Occupations')
	return html
end

function processOccupationArgs(args, label)
	local Date = require("Module:Date")._Date

	if args.current == 'yes' then
		args.endDate = nil
	elseif args.endDate then
		endDate = mw.text.split(args.endDate, '%p')
		endDate = Date(endDate[1], endDate[2] or 'partial', endDate[3] or 'partial')
		args.endDate = endDate:text()
		args.endDateISO = endDate:text('ymd')
	end

	if args.startDate then
		startDate = mw.text.split(args.startDate, '%p')
		startDate = Date(startDate[1], startDate[2] or 'partial', startDate[3] or 'partial')
		args.startDate = startDate:text()
		args.startDateISO = startDate:text('ymd')
	end

	if args.type == 'Ministry' then
		args.organization = 'Ministry of Magic'
		args.office = args.ministryOffice
		args.division = args.ministryDivision
		args.department = args.ministryDepartment
		label.office = 'Office'
		label.division = 'Division'
		label.department = 'Department'

		if args.office == 'Auror Office' then
			table.insert(categories, 'Aurors')
		elseif args.office == 'Wizengamot' then
			table.insert(categories, 'Wizengamot Members')
		end
		if args.ministryDepartment then
			table.insert(categories, args.ministryDepartment .. ' Personnel')
		end
		table.insert(categories, 'Ministry Personnel')

		args.ministryOffice = nil
		args.ministryDivision = nil
		args.ministryDepartment = nil

	elseif args.type == 'St. Mungos' then
		args.organization = "St. Mungo's Hospital"
		args.department = args.mungosFloor
		label.department = 'Floor'

		table.insert(categories, args.department)
		table.insert(categories, "St. Mungo's Hospital")

		args.mungosFloor = nil

	elseif args.type == 'Hogwarts' then
		args.organization = 'Hogwarts'
		args.department = args.schoolSubject
		label.department = 'Subject'

		if args.schoolSubject then
			table.insert(categories, 'Hogwarts Professor')
		else
			table.insert(categories, 'Hogwarts Staff')
		end

		args.schoolSubject = nil

	elseif args.type == 'Quidditch' then
		args.organization = args.quidditchLeague
		args.department = args.quidditchTeam
		label.department = 'Team'


		if args.quidditchTeam then
			table.insert(categories, args.quidditchTeam)
		end
			table.insert(categories, 'Quidditch')

		args.quidditchLeague = nil
		args.quidditchTeam = nil

	elseif args.type == 'Other School' then
		args.organization = args.school
		args.department = args.schoolSubject
		label.department = 'Subject'
		if args.school then
			table.insert(categories, args.school .. ' Staff')
		end

		args.school = nil
		args.schoolSubject = nil

	elseif args.type == 'Other' or type == 'Criminal' then
		args.organization = args.employer
		label.organization = 'Employer'
		args.employer = nil

	elseif args.type == 'Journalist' then
		args.organization = args.publication
		label.organization = 'Publication'

		table.insert(categories, 'Journalist')
		args.title = args.title and args.title or 'Journalist'
		args.publication = nil

	elseif args.type == 'Criminal' then
		args.organization = args.employer
		label.organization = 'Employer'
		args.title = args.title and args.title or 'Criminal'

		table.insert(categories, 'Criminal')

		args.employer = nil

	elseif args.type == 'Gringotts' then
		args.organization = 'Gringotts Wizarding Bank'

		table.insert(categories, 'Gringotts')

	elseif args.type == 'Business Owner' then
		args.organization = args.business
		label.organization = 'Business'
		args.title = args.title and args.title or 'Owner'
		table.insert(categories, 'Business Owner')

		args.business = nil
	end

	args.title = args.title and args.title or 'Employee'
end

function p.wand(frame)
	local getArgs = require('Module:Arguments').getArgs
	local args = getArgs(frame)

	html = mw.html.create('li')
	args.wand = args.wand or 'Wand'
	if args.wandmaker then
		html:wikitext(('%s (Made by %s)'):format(args.wand, args.wandmaker))
	else
		html:wikitext(args.wand)
	end

	ib.cargoStore(args, cfg.cargo.Wands, 'Wands')

	return html
end

function p.relationship(frame)
	local Date = require("Module:Date")._Date
	local getArgs = require('Module:Arguments').getArgs
	local args = getArgs(frame)

	if not(args.relationshipWith) then
		return
	end

	if args.relationshipType and args.endDate then
		relationshipType = 'Dated'
	else
		relationshipType = 'Dating'
	end

	if args.endType == 'Divorce' then
		endType = '(Divorced)'
	elseif args.endType == 'Separation' then
		endType = '(Separated)'
	elseif args.endType == 'Death' then
		endType = '(Widowed)'
	else
		endType = ''
	end

	html = mw.html.create('li')

	if args.relationshipType then

		html
			:wikitext(args.relationshipType .. ': ')
	end
	if args.relationshipWith then
		html
			:wikitext('[[' .. args.relationshipWith .. ']] ' .. endType)
	end

	ib.cargoStore(args, cfg.cargo.Relationships, 'Relationships')

	return html

end

function processCategories(args)
	if args.hideCategories then
		return
	end

	html = mw.html.create('')
	for _,value in ipairs(categories) do
		html:wikitext(('[[Category:%s]]'):format(value))
	end
	for _,value in ipairs(trackingCats) do
		html:wikitext(('[[Category:%s]]'):format(value))
	end
	for _,value in ipairs(indicators) do
		local indicator = frame:extensionTag{ name = 'indicator', content = value[2], args = { name = value[1]} }
		html:wikitext(indicator)
	end

	return html
end


return p