Module:Quotations

Dokumentasie vir hierdie module kan geskep word by: Module:Quotations/doc

local m_scriptutils = require("Module:script utilities")
local date_validation = require("Module:Quotations/date validation")
local loadModule -- a forward declaration
local export = {}


local hasData = {
	['grc'] = true,
	['la'] = true,
	['xcl'] = true,
	['he'] = true,
	['hy'] = true,
	['axm'] = true,
	['en'] = true,
	['sa'] = true,
	['hi'] = true,
	['ae'] = true,
}

function export.create(frame)
	local passed = true
	local results = ''
	passed, results = pcall(export.Create, frame)
	if passed then
		return results
	else
		return '<span class="wiktQuote previewonly" data-validation="red">'..results..'</span>'
	end
end

function export.Create(frame)
	-- Set up our initial variables; set empty parameters to false
	local args = {}
	local params = {
		['thru'] = true,
		['quote'] = true,
		['trans'] = true,
		['transauthor'] = true,
		['transyear'] = true,
		['notes'] = true,
		['form'] = true,
		['year'] = true,
--		[''] = true,
	}
	for k, v in pairs(frame:getParent().args) do
		if not ( type(k) == 'number' or params[k] ) then
			-- [[Special:WhatLinksHere/Template:tracking/Quotations/param error]]
			require("Module:debug").track("Quotations/param error")
--			error('The parameter "' .. k .. '" is not recognized. See the template documentation.')
		end
		
		if v == '' then
			if k == "lang" then
				args[k] = nil
			else
				args[k] = false
			end
		else
			args[k] = v
		end
	end
	
	local lang = args[1]
	lang = require("Module:languages").getByCode(lang) or require("Module:languages").err(lang, 1)
	
	local ante = {}
	
	if hasData[lang:getCode()] then
		local m_langModule = loadModule(lang)
		ante = m_langModule.expand(args)
	end
	
	if ante.author == nil then
		ante.author = args[2]
	end
	if ante.work == nil then
		ante.work = args[3]
	end
	if ante.ref == nil then
		ante.ref = ''
		local dot = false
		for i = 4, 10 do
			if args[i] then
				ante.ref = ante.ref..(dot and '.' or '')..args[i]
				dot = true
			else
				break
			end
		end
	end
	
	for k,v in pairs(args) do
		if type(k) ~= 'number' then
			ante[k] = args[k]
		end
	end

	local penult = {['year'] = '', ['author'] = '', ['work'] = '', ['ref'] = '',
		['notes'] = '', ['otherLines'] = {}, ['s1'] = '', ['s2'] = '',
		['s3'] = '', ['s4'] = ''}
	local comma = false
	--Language specific modules are responsible for first line parameters.
	--Base formatting module will poll for other parameters,
	--pulling them only if the language module hasn't returned them.

	local otherOtherLineStuff = {'quote', 'transyear', 'transauthor', 'trans'}
	for i = 1, 4 do
		ante[otherOtherLineStuff[i]] = ante[otherOtherLineStuff[i]] or args[otherOtherLineStuff[i]]
	end

	if not ante.code then
		penult.elAttr = ' class="wiktQuote" data-validation="white">'
	else
		penult.elAttr = ' class="wiktQuote" data-validation="'..ante.code..'">'
	end
	if ante.year then
		penult.year = "'''"..date_validation.main(ante.year).."'''"
		comma = true
	end
	if ante.author then
		penult.s1 = (comma and ', ' or '')
		penult.author = ante.author
		comma = true
	end
	if ante.work then
		penult.s2 = (comma and ', ' or '')
		penult.work = "''"..ante.work.."''"
		comma = true
	end
	if ante.ref then
		penult.s3 = (comma and ' ' or '')
		penult.ref = ante.ref
	end
	if ante.notes then
		penult.s4 = (comma and', ' or '')
		penult.notes = '('..ante.notes..')'
	end
	if ante.quote or ante.trans then
		table.insert(penult.otherLines, '<dl><dd>')
		if ante.quote then
			local sc = require("Module:scripts").findBestScript(ante.quote, lang)
			table.insert(penult.otherLines, m_scriptutils.tag_text(ante.quote, lang, sc, nil, "e-quotation"))
			if not require("Module:script utilities").is_Latin_script(sc) then
				local transliteration = lang:transliterate(require("Module:links").remove_links(ante.quote), sc)
				if transliteration then
					table.insert(penult.otherLines, '<dl><dd>' .. require("Module:script utilities").tag_translit(transliteration, lang, "usex"))
				end
			end
		end
		if ante.trans then
			if ante.transyear or ante.transauthor then
				table.insert(penult.otherLines, '<ul><li>')
				if ante.transyear then
					table.insert(penult.otherLines, "'''"..ante.transyear.."''' translation")
				else
					table.insert(penult.otherLines, 'Translation')
				end
				if ante.transauthor then
					table.insert(penult.otherLines, ' by '..ante.transauthor)
				end
				table.insert(penult.otherLines, '<dl><dd>'..ante.trans..'</dd></dl></li></ul>')
			else
				if not ante.quote then
					table.insert(penult.otherLines, ante.trans)
				else
					table.insert(penult.otherLines, '<dl><dd>'..ante.trans..'</dd></dl>')
				end
			end
		end
		table.insert(penult.otherLines, '</dl></dd>')
	end
	penult.otherLines = table.concat(penult.otherLines)
	
	local form = args['form'] or 'full'
	if form == 'full' then
		ultimate = '<div'..penult.elAttr..penult.year..penult.s1..penult.author..penult.s2..penult.work..penult.s3..penult.ref..penult.s4..penult.notes..penult.otherLines..'</div>'
	elseif form == 'inline' then
		ultimate = '<span'..penult.elAttr..penult.author..penult.s2..penult.work..penult.s3..penult.ref..'</span>'
	elseif form == 'work' then
		ultimate = '<span'..penult.elAttr..penult.work..penult.s3..penult.ref..'</span>'
	elseif form == 'ref' then
		ultimate = '<span'..penult.elAttr..penult.ref..'</span>'
	end
	return ultimate

end

function loadModule(lang)
	local sema = require('Module:Quotations/' .. lang:getCode())
	sema.library = sema.library or mw.loadData("Module:Quotations/" .. lang:getCode() .. "/data")
	
	if not sema.refCount then
		sema.refCount = 0
	end
	
	if not sema.refParamCount then
		sema.refParamCount = 0
	end
	
	sema.changeCode = sema.changeCode or function(color)
		if color == 'orange' then
			sema.code = 'orange'
		end
		if (color == 'yellow') and (sema.code == 'green') then
			sema.code = 'yellow'
		end
	end
	
	sema.reroute = sema.reroute or function(route)
		local temp = {}
		local data = sema.library.data
		
		if not sema.rerouteRefParamCount then
			sema.rerouteRefParamCount = 0
		end
		
		for k, v in pairs(route) do
			temp[k] = sema.interpret(v)
			if string.find(k, "^ref%d$") then
				number = tonumber(string.match(k, "%d$"))
				mw.log(number)
				
				if number > sema.rerouteRefParamCount then
					sema.rerouteRefParamCount = number
				end
			end
		end
		
		for k, v in pairs(temp) do
			sema[k] = v
		end
		
		if sema.author ~= nil and data[sema.author] then
			sema.aData = data[sema.author]
			if sema.work ~= nil and sema.aData.works[sema.work] then
				sema.wData = sema.aData.works[sema.work]
			end
		end
	end
	
	sema.choose = sema.choose or function(choice, optionA, optionB)
		optionB = optionB or ''
		choice = sema.interpret(choice)
		local chosenPath = {}
		if choice then
			chosenPath = optionA
		else
			chosenPath = optionB
		end
		for j=1, 30 do
			local innerCurrent = chosenPath[j]
			if innerCurrent then
				table.insert(sema.refLink, sema.interpret(current))
			else
				break
			end
		end
		local ongoingDecision
		decision = sema.interpret(decision)
		return decision
	end
	
	sema.isLetter = sema.isLetter or function(input)
		local isit = not tonumber(input)
		return isit
	end

	sema.lower = sema.lower or mw.ustring.lower

	sema.roundDown = sema.roundDown or function(period, verse)
		if not tonumber(verse) then
			sema.changeCode('orange')
		else
			local rounded = math.floor(verse/period) * period
			return rounded
		end
	end

	sema.chapterSelect = sema.chapterSelect or function(rubric, verse)
		verse = tonumber(verse)
		for k,v in pairs(rubric) do
			if v[1] <= verse and verse <= v[2] then
				return k
			end
		end
		sema.changeCode('orange')
	end

	sema.interpret = sema.interpret or function(item)
		local output, isRef
		
		if type(item) == 'string' then
			if string.len(item) > 1 and string.sub(item, 1, 1) == '.' then
				address = string.sub(item, 2)
				if string.find(address, "^ref") then
					isRef = true
					local number = string.match(address, "%d+")
					if number then
						number = tonumber(number)
						if number > sema.refCount then
							sema.refCount = number
						end
					end
				end
				local returnable = sema[address] or sema.library.data.Sundry and sema.library.data.Sundry[address]
				output = returnable
			else
				output = item
			end
		elseif type(item) == 'table' then
		--If it's a table, it's either a function call or a nested address.
			local presumedFunction = sema.interpret(item[1])
			if type(presumedFunction) == 'function' then
				local parameters = {}
				for i = 2, 30 do
					if item[i] ~= nil then
						local expanded = sema.interpret(item[i])
						table.insert(parameters, expanded)
					else
						break
					end
				end
				local result = presumedFunction(unpack(parameters))
				output = result
			else
				local nested = sema
				for i = 1, 30 do
					local address = item[i]
					if address and nested then
						nested = nested[address]
					else
						break
					end
				end
				output = nested
			end
		else
			output = item
		end
		
		
		if sema.rerouteRefParamCount and sema.refCount < sema.rerouteRefParamCount or sema.refCount < sema.refParamCount or isRef then
			return output
		else
			return ""
		end
	end

	sema.convert = sema.convert or function(scheme, initiate)
		if type(scheme) == "table" then
			local initiate = tonumber(initiate) or initiate
			local converted = scheme[initiate]
			if converted == nil then
				sema.changeCode('orange')
			end
			return converted
		end
		if type(scheme) == "function" then
			local initiate = tonumber(initiate) or initiate
			local converted = scheme(initiate)
			if converted == nil then
				sema.changeCode('orange')
			end
			return converted
		end
		sema.changeCode('orange')
	end

	sema.numToRoman = sema.numToRoman or function(item)
		local j = tonumber(item)
		if (j == nil) then
			return item
		end
		if (j <= 0) then
			return item
		end

		local ints = {1000, 900,  500, 400, 100,  90, 50,  40, 10,  9,   5,  4,   1}
		local nums = {'M',  'CM', 'D', 'CD','C', 'XC','L','XL','X','IX','V','IV','I'}

		local result = ""
		for k = 1, #ints do
			local count = math.floor(j / ints[k])
			result = result .. string.rep(nums[k], count)
			j = j - ints[k]*count
		end
		return result
	end
	
	sema.numToIndian = sema.numToIndian or require("Module:foreign numerals").to_Indian

	sema.period = sema.period or '.'

	sema.expand = sema.expand or function(args)
		--Instantiate our variables.
		local results = {}
		sema.code = 'green'
		local data = sema.library.data
		local ultimate = ''

		sema.author = args['author'] or args[2]
		sema.work = args['work'] or args[3]
		
		for i = 1, 5 do
			local refName = 'ref' .. i
			local paramNumber = i + 3
			sema[refName] = args[refName] or args[paramNumber]
			
			if i == 1 or sema[refName] then
				sema.refParamCount = i
			end
		end
	
		--Check if we've been given an author alias.
		if data.authorAliases[sema.author] then
			sema.author = data.authorAliases[sema.author]
		end

		if not data[sema.author] then
			sema.changeCode('yellow')
		else
			sema.aData = data[sema.author]
			if sema.aData.reroute then
				sema.reroute(sema.aData.reroute)
			else
				if sema.aData.aliases and sema.aData.aliases[sema.work] then
					sema.work = sema.aData.aliases[sema.work]
				end
				if not sema.aData.works or not sema.aData.works[sema.work] then
					sema.changeCode('yellow')
				else
					sema.wData = sema.aData.works[sema.work]
					if sema.wData.reroute then
						sema.reroute(sema.wData.reroute)
					end
				end
			end
		end

		--Load all author-level data.
		if sema.aData and sema.aData.aLink then
			results.author = '[[w:'..sema.aData.aLink..'|'..sema.author..']]'
		else
			results.author = sema.author
		end
		if sema.aData and sema.aData.year then
			results.year = sema.aData.year
		end

		--If the database has a link for the work, incorporate it.
		if not sema.wData or not sema.wData['wLink'] then
			results.work = sema.work
		else
			results.work = '[[w:'..sema.wData['wLink']..'|'..sema.work..']]'
		end
		
		--Some works have info which overrides the author-level info.
		if sema.wData then
			if sema.wData['year'] then
			results.year = sema.wData.year
			end
			if sema.wData['author'] ~= nil then
				results.author = sema.wData.author
			end
		end
	
		--The displayed reference usually consists of all the ref argument(s) joined with a period.
		sema.refDisplay = sema.ref1 and '' or false
		for i = 1, 5 do
			local whichRef = 'ref' .. tostring(i)
			if sema[whichRef] then
				local ref = sema[whichRef]
				
				local separator
				-- no separator before a letter
				if mw.ustring.match(ref, "^%a$") then
					separator = ""
				-- to allow colon between biblical chapter and verse
				elseif sema.aData and sema.aData.rdFormat and sema.aData.rdFormat.separator then
					separator = sema.aData.rdFormat.separator
				else
					separator = "."
				end
				
				if i > 1 then
					sema.refDisplay = sema.refDisplay .. separator
				end
				sema.refDisplay = sema.refDisplay .. sema[whichRef]
			else
				break
			end
		end
		if args['through'] then
			args['thru'] = args['through']
		end
		if args['thru'] then
			sema.refDisplay = sema.refDisplay..'–'..args['thru']
		end
		
		
		
		--[[	If the work is not in the database,
				or we don't have a source text link,
				the ref is simply the display.
				Otherwise, we have to create a reference link,
				easily the most challenging function of this script. ]]
		if sema.wData and sema.wData['rlFormat'] then
			sema.rlFormat = sema.aData['rlFormat'..tostring(sema.wData.rlFormat)]
			if sema.rlFormat then
				sema.rlTitle = sema.wData['rlTitle']
				sema.refLink = {}
				for i = 1, 30 do
					local current = sema.rlFormat[i]
					if current then
						table.insert(sema.refLink, sema.interpret(current))
					else
						break
					end
				end
				sema.refLink = table.concat(sema.refLink)
			end
		end
		if sema.refLink and sema.refDisplay then
			results.ref = '[['..sema.refLink..'|'..sema.refDisplay..']]'
		else
			results.ref = sema.refDisplay or ''
		end
		if args['notes'] then
			results.notes = args.notes
		end
		results.code = sema.code
		return results

	end

	return sema
end

return export