Module:Quotations

Definition from Wiktionary, the free dictionary
Jump to: navigation, search
This module is used by Template:Q.

Coding[edit]

The intent of this module was to make a quotations library which can be built and used asynchronously. An editor can reference a work or author which is not yet coded, and the display will still look fine, and if the information is later coded, the template will make use of it at that time. Each language needs its own module, such as that found at Module:Quotations/grc, which can be used as a starting point in creating a language module which does not yet exist. This module takes the information and does the lookups in its data module (in this case Module:Quotations/grc/data), and returns the results to be formatted by the main module. The data module should be a table, with entries for each author. Each author table should include information about the author, such as years active and the title of the Wikipedia article for the author, as well as tables for each of its works, with information such as the year the work was written, the title of its Wikipedia article, and the title of its Wiksource article. Alias tables can be used for convergence, such as when a work might have two common titles, as well as abbreviations. Note that the module is designed to work with whatever data is available, and it should not be considered necessary to add all possible data; some is better than none. One of the more complex aspects of coding the data set lies in the reference link, which is meant to be a fairly dynamic link which formats itself to account for information given. It is formatted in the author's data table as a table of strings and tables. Strings not prefixed by a period are inserted as is. Strings prefixed with a period indicate variable references. Tables which begin with a function run that function with the table's following elements as parameters. Tables not beginning with a function are nested variable addresses. Take the reference link format table "data.Plato.rlFormat2", which is used solely by Plato's Republic.:

{'s:el:', '.rlTitle', '/', {'.chapterSelect', {'authorData', 'republicChapters'}, '.ref1'}, '#p', '.ref1', {'.lower', '.ref2'}}

The first section, "s:el" is a simple string, and will be inserted as-is into the link target; it is the prefix for the Greek Wikisource, where a native language version of the Republic is found. The second element, ".rlTitle", begins with a period, indicating that it should be replaced by the variable "rlTitle", which is given in the Republic's data table as "Πολιτεία". The third element, "/", is a standard string, and will be inserted into the link as-is. The fourth element is a function call, using the function "chapterSelect", which is called with two parameters. The second parameter is the variable "ref1", which is the chapter given by the user. The first parameter is formatted as a nested reference, it will be the variable "republicChapters", which is found within "authorData". There is no getting around the fact that it is not the easiest format in the world, but it does make for a powerful and flexible engine to interpret data and create the proper link.


local m_scriptutils = require("Module:script utilities")
local date_validation = require("Module:Quotations/date validation")
local loadModule = nil -- essentially a forward-declaration
local export = {}
 
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.
	local args = frame:getParent().args
	--Set empty tags to false
		for k,v in pairs(args) do
			if v == '' then
				args[k] = false
			end
		end
	local codedLangs = {['grc'] = true, ['la'] = true, ['xcl'] = true, ['hy'] = true, ['axm'] = true, ['en'] = true}
	local lang = args[1]; if lang == "" then lang = nil end
	lang = require("Module:languages").getByCode(lang)
 
	local ante = {}
 
	if codedLangs[lang:getCode()] then
		local m_langModule = loadModule(lang)
		ante = m_langModule.expand(args)
	else
		ante.author = args[2]
		ante.work = args[3]
		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
		for k,v in pairs(args) do
			if type(k) ~= 'number' then
				ante[k] = args[k]
			end
		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
		penult.otherLines = '<dl><dd>'
		if ante.quote then
			local sc = require("Module:scripts").findBestScript(ante.quote, lang)
			penult.otherLines = penult.otherLines..m_scriptutils.tag_text(ante.quote, lang, sc)
		end
		if ante.trans then
			if ante.transyear or ante.transauthor then
				penult.otherLines = penult.otherLines..'<ul><li>'
				if ante.transyear then
					penult.otherLines = penult.otherLines.."'''"..ante.transyear.."''' translation"
				else
					penult.otherLines = penult.otherLines..'Translation'
				end
				if ante.transauthor then
					penult.otherLines = penult.otherLines..' by '..ante.transauthor
				end
				penult.otherLines = penult.otherLines..'<dl><dd>'..ante.trans..'</dd></dl></li></ul>'
			else
				if not ante.quote then
					penult.otherLines = penult.otherLines..ante.trans
				else
					penult.otherLines = penult.otherLines..'<dl><dd>'..ante.trans..'</dd></dl>'
				end
			end
		end
		penult.otherLines = penult.otherLines..'</dl></dd>'
	end
	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")
 
	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
 
		for k,v in pairs(route) do
			temp[k] = sema.interpret(v)
		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)
		if type(item) == 'string' then
			if string.sub(item, 1, 1) == '.' then
				address = string.sub(item, 2)
				local returnable = sema[address] or sema.library.data.Sundry and sema.library.data.Sundry[address]
				return returnable
			else
				return 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] ~= null then
						local expanded = sema.interpret(item[i])
						table.insert(parameters, expanded)
					else
						break
					end
				end
				local result = presumedFunction(unpack(parameters))
				return result
			else
				local nested = sema
				for i=1, 30 do
					local address = item[i]
					if address then
						nested = nested[address]
					else
						break
					end
				end
				return nested
			end
		else
			return item
		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.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'..tostring(i)
			local paramNumber = i + 3
			sema[refName] = args[refName] or args[paramNumber]
		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 is simply 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
				if i > 1 then
					sema.refDisplay = sema.refDisplay..'.'
				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