Module:Quotations

From Wiktionary, the free dictionary
Jump to navigation Jump to search

This module is used by Template:Q.

Coding

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.

Language modules

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.

Note: In order for a language module to be recognized, the corresponding language code must be added to the hasData table in this module.

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 element, '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 data.Plato.

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.

If the source is not wikilinkable, then you'll need an external reference link instead of a reference link. You can use the same method as reference links, except use '.xrlFormat' instead of '.rlFormat', and the URL to link to is the '.xurl' parameter for the work. For example:

data['Julius Firmicus Maternus'].xrlFormat1 = {'.xurl'}

data['Julius Firmicus Maternus'].works = {

		['Matheseos Libri VIII'] = {['year'] = 'c. 334–337', ['xurl'] = 'https://archive.org/details/matheseoslibrivi02firmuoft', ['xrlFormat'] = 1}

}

Language-specific submodules




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

local LanguageModule = {}
LanguageModule.__index = LanguageModule

local hasData = {
	['ae'] = true,
	['ar'] = true,
	['axm'] = true,
	['az'] = true,
	['bra'] = true,
	['chg'] = true,
	['cy'] = true,
	['egy'] = true,
	['en'] = true,
	['fa'] = true,
	['fro'] = true,
	['gmq-ogt'] = true,
	['gmq-pro'] = true,
	['grc'] = true,
	['he'] = true,
	['hi'] = true,
	['hy'] = true,
	['inc-apa'] = true,
	['inc-ash'] = true,
	['inc-mgu'] = true,
	['inc-oaw'] = true,
	['inc-ogu'] = true,
	['inc-ohi'] = true,
	['inc-opa'] = true,
	['pra'] = true,
	['la'] = true,
	['lzz'] = true,
	['mt'] = true,
	['mxi'] = true,
	['oge'] = true,
	['omr'] = true,
	['ota'] = true,
	['peo'] = true,
	['pmh'] = true,
	['sa'] = true,
	['scn'] = true,
	['sd'] = true,
	['sv'] = true,
	['vah'] = true,
	['xce'] = true,
	['xcl'] = true,
}
export.hasData = hasData

function export.create(frame)
	local passed, results = pcall(function () return export.Create(frame:getParent().args) end)
	if passed then
		return results
	else
		--[[Special:WhatLinksHere/Wiktionary:Tracking/Quotations/error]]
		require('Module:debug').track('Quotations/error')
		return '<span class="wiktQuote previewonly error" data-validation="red">'..results..'</span>'
	end
end

local function warn_about_unrecognized_args(unrecognized_args)
	require('Module:debug').track('Quotations/param error')
	
	mw.addWarning('Unrecognized parameters in '
		.. mw.text.nowiki('{{Q}}: '
		.. table.concat(require('Module:table').keysToList(unrecognized_args), ', ')))
end

function export.Create(args)
	-- Set up our initial variables; set empty parameters to false
	local processed_args = {}
	local unrecognized_args = {}
	local params = {
		['thru'] = true,
		['quote'] = true,
		['trans'] = true,
		['transauthor'] = true,
		['transyear'] = true,
		['t'] = true,
		['lit'] = true,
		['style'] = true,
		['object'] = true,
		['notes'] = true,
		['refn'] = true,
		['form'] = true,
		['year'] = true,
		['termlang'] = true,
		['tr'] = true, -- This is simply ignored if quote is in Latin script.
		['ts'] = true, -- This is simply ignored if quote is in Latin script.
		['subst'] = true, -- This is simply ignored if quote is in Latin script.
		['nocat'] = true,
	}

	local max_numbered_param = 4
	for k, v in pairs(args) do
		if type(k) == 'number' then
			if k > max_numbered_param then
				max_numbered_param = k
			end
		elseif not params[k] then
			unrecognized_args[k] = v
		end
		
		if v == '' then
			if k == "lang" then
				processed_args[k] = nil
			else
				processed_args[k] = false
			end
		else
			processed_args[k] = v
		end
	end
	
	if next(unrecognized_args) then
		warn_about_unrecognized_args(unrecognized_args)
	end
	
	-- Ensure that all numbered parameters up to the greatest numbered parameter
	-- are not nil.
	for i = 1, max_numbered_param do
		processed_args[i] = processed_args[i] or false
	end
	
	args = processed_args -- Overwrite original args.
	
	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 = LanguageModule.new(lang)
		ante = m_langModule:expand(args)
	else
		require("Module:debug").track {
			'Quotations/no data',
			'Quotations/no data/' .. lang:getCode(),
		}
	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
		local ref = {}
		for i = 4, 10 do
			if args[i] then
				table.insert(ref, args[i])
			else
				break
			end
		end
		ante.ref = table.concat(ref, '.')
	end
	
	for k,v in pairs(args) do
		if type(k) ~= 'number' then
			ante[k] = args[k]
		end
	end

	local penult = {['year'] = '', ['author'] = '', ['work'] = '', ['object'] = '', ['ref'] = '', ['termlang'] = '',
		['notes'] = '', ['refn'] = '', ['otherLines'] = {}, ['s1'] = '', ['s2'] = '',
		['s3'] = '', ['s4'] = '', ['s5'] = '', ['style1'] = '', ['style2'] = ''}
	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', 'termlang'}
	for _, item in ipairs(otherOtherLineStuff) do
		ante[item] = ante[item] or args[item]
	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.object then
		penult.s5 = (comma and ' ' or '')
		penult.object = '('..ante.object..')'
		comma = true
	end
	if ante.ref then
		penult.s3 = (comma and ' ' or '')
		penult.ref = ante.ref
	end
	if ante.style == 'no' or penult.work == '' then
		penult.style1 = ''
		penult.style2 = ''
	elseif ante.style == 'q' then
		penult.style1 = '“'
		penult.style2 = '”'
	else
		penult.style1 = "''"
		penult.style2 = "''"
	end
	if ante.termlang then
		ante.termlang = require("Module:languages").getByCode(ante.termlang) or require("Module:languages").err(ante.termlang, 1)
		penult.termlang = ' (in '..lang:getCanonicalName()..')'
	end
	
	local form = args['form'] or 'full'
	local ultimate
	if form == 'full' then
		local categories = {}
		local namespace = mw.title.getCurrentTitle().nsText

		if ante.notes then
			penult.s4 = (comma and ', ' or '')
			penult.notes = '('..ante.notes..')'
		end
		if ante.refn then
			penult.refn = ante.refn
		end
		
		if ante.t then
			ante.trans = ante.t
		end
		
		if ante.quote or (ante.trans and ante.trans ~= "-") then
			penult.refn = ":" .. penult.refn
			local translitwithtrans = false
			table.insert(penult.otherLines, "<dl><dd>")
			if ante.quote then
				local sc = lang:findBestScript(ante.quote)
				local quote = ante.quote
				-- fix up links with accents/macrons/etc.
				if quote:find("[[", 1, true) then
					 quote = require("Module:links").language_link{term = quote, lang = lang}
				end
				table.insert(penult.otherLines, m_scriptutils.tag_text(quote, lang, sc, nil, "e-quotation"))
				if (namespace == "" or namespace == "Reconstruction") and not args.nocat then
					if ante.termlang then
						table.insert(categories, ante.termlang:getCanonicalName() .. " terms with quotations")
					else
						table.insert(categories, lang:getCanonicalName() .. " terms with quotations")
					end
				end
				if not m_scriptutils.is_Latin_script(sc) or lang:getCode() == "egy" then

					-- Handle subst=		
					local subbed_quote = require("Module:links").remove_links(quote)
					if args.subst then
						local substs = mw.text.split(args.subst, ",")
						for _, subpair in ipairs(substs) do
							local subsplit = mw.text.split(subpair, mw.ustring.find(subpair, "//") and "//" or "/")
							subbed_quote = mw.ustring.gsub(subbed_quote, subsplit[1], subsplit[2])
						end
					end

					local transliteration = args.tr or (lang:transliterate(subbed_quote, sc))
					if transliteration then
						transliteration = "<dd>" .. m_scriptutils.tag_translit(transliteration, lang, "usex") .. "</dd>"
					end
					local transcription = args.ts and "<dd>/" .. m_scriptutils.tag_transcription(args.ts, lang, "usex") .. "/</dd>"
					if transliteration or transcription then
						local translitend = "</dl>"
						if ante.trans and ante.trans ~= "-" and not ante.transyear and not ante.transauthor then
							translitwithtrans = true
							translitend = ""
						end
						table.insert(penult.otherLines, "<dl>" .. (transliteration or "") .. (transcription or "") .. translitend)
					end
				end
			end

			if ante.trans == "-" then
				ante.trans = nil
				table.insert(categories, "Omitted translation in the main namespace")
			elseif ante.trans then
				local litline = ""
				if ante.lit then
					litline = "<dd>(literally, “"..ante.lit.."”)</dd>"
				end
				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>" .. litline .. "</dl></li></ul>")
				else
					if not ante.quote then
						table.insert(penult.otherLines, ante.trans)
					else
						local transstart = "<dl><dd>"
						if translitwithtrans then
							transstart = "<dd>"
						end
						table.insert(penult.otherLines, transstart .. ante.trans .. "</dd>" .. litline .. "</dl>")
					end
				end
			elseif lang:getCode() ~= "en" and lang:getCode() ~= "und" then
				-- add trreq category if translation is unspecified and language is not English or undetermined
				table.insert(categories, "Requests for translations of " .. lang:getCanonicalName() .. " quotations")
			end
			table.insert(penult.otherLines, "</dd></dl>")
		end
		penult.otherLines = table.concat(penult.otherLines)
		
		ultimate = '<div'..penult.elAttr..penult.year..penult.s1..penult.author..penult.s2..penult.style1..penult.work..penult.termlang..penult.style2..penult.s5..penult.object..penult.s3..penult.ref..penult.s4..penult.notes..penult.refn..penult.otherLines..'</div>'..require("Module:utilities").format_categories(categories, lang)
	elseif form == 'inline' then
		ultimate = '<span'..penult.elAttr..penult.author..penult.s2..penult.style1..penult.work..penult.termlang..penult.style2..penult.s5..penult.object..penult.s3..penult.ref..'</span>'
	elseif form == 'work' then
		ultimate = '<span'..penult.elAttr..penult.style1..penult.work..penult.termlang..penult.style2..penult.s5..penult.object..penult.s3..penult.ref..'</span>'
	elseif form == 'ref' then
		ultimate = '<span'..penult.elAttr..penult.ref..'</span>'
	end
	return ultimate

end

local function add_self(func)
	return function(self, ...)
		return func(...)
	end
end

function LanguageModule.new(lang)
	local sema = require('Module:Quotations/' .. lang:getCode())
	sema.library = mw.loadData("Module:Quotations/" .. lang:getCode() .. "/data")
	
	setmetatable(sema, LanguageModule)
	
	-- Have to insert unused self parameter or Lua–PHP interface complains about
	-- table with boolean keys.
	sema.lower = add_self(mw.ustring.lower)
	
	sema.period = '.'
	
	return sema
end

setmetatable(LanguageModule, { __index = function (self, key)
	if key == 'numToIndian' then
		-- This should only be loaded if needed so that [[Module:foreign numerals]]
		-- is not transcluded on every page that [[Module:Quotations]] is.
		local func = require('Module:foreign numerals').to_Indian
		self.numToIndian = func
		return func
	end
end })

function LanguageModule:changeCode(color)
	if color == 'orange' then
		self.code = 'orange'
	end
	if (color == 'yellow') and (self.code == 'green') then
		self.code = 'yellow'
	end
end

function LanguageModule:reroute(route)
	local temp = {}
	local data = self.library.data
	
	for k, v in pairs(route) do
		temp[k] = self:interpret(v)
	end
	
	for k, v in pairs(temp) do
		self[k] = v
	end
	
	if self.author ~= nil and data[self.author] then
		self.aData = data[self.author]
		if self.work ~= nil and self.aData.works[self.work] then
			self.wData = self.aData.works[self.work]
		end
	end
end

function LanguageModule:choose(choice, optionA, optionB)
	optionB = optionB or ''
	choice = self: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(self.refLink, self:interpret(current))
		else
			break
		end
	end
	local ongoingDecision
	decision = self:interpret(decision)
	return decision
end

function LanguageModule:isLetter(input)
	local isit = not tonumber(input)
	return isit
end

function LanguageModule:digits(width, num)
	local decimal = '%' .. width .. 'd'
	return string.format(decimal, num)
end

function LanguageModule:separ(values, separator)
  return table.concat(values, separator)
end

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

function LanguageModule:chapterSelect(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
	self:changeCode('orange')
end

function LanguageModule:interpret(item)
	local output
	
	if type(item) == 'string' then
		if string.len(item) > 1 and string.sub(item, 1, 1) == '.' then
			local address = string.sub(item, 2)
			local returnable = self[address] or self.library.data.Sundry and self.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 = self:interpret(item[1])
		if type(presumedFunction) == 'function' then
			local parameters = {}
			for i = 2, 30 do
				if item[i] ~= nil then
					table.insert(parameters, self:interpret(item[i]))
				else
					break
				end
			end
			output = presumedFunction(self, unpack(parameters))
		else
			local nested = self
			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
	
	return output
end

function LanguageModule:convert(scheme, initiate)
	if type(scheme) == "table" then
		local initiate = tonumber(initiate) or initiate
		local converted = scheme[initiate]
		if converted == nil then
			self: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
			self:changeCode('orange')
		end
		return converted
	end
	self:changeCode('orange')
end

function LanguageModule:numToRoman(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

-- Iterate through "array" and its sublevels. Find indices in "array" that
-- contain a string matching "valToFind". Return last index where that string
-- (minus its first letter) is the key for a field in "self", as well as last
-- index where that string was found.
-- Used to locate the place where the "rlformat" should be skipped out of,
-- because there's no ".ref" value supplied for what comes next.
-- For instance, if book but not line number has been supplied.
local function findLastValidRefIndex(self, array, valToFind)
	local lastValidIndex, lastIndex
	for i, val in ipairs(array) do
		if type(val) == 'table' then
			local res1, res2 = findLastValidRefIndex(self, val, valToFind)
			if res1 then
				lastValidIndex = i
			end
			if res2 then
				lastIndex = i
			end
		elseif type(val) == 'string' and val:find(valToFind) then
			lastIndex = i
			if self[val:sub(2)] then
				lastValidIndex = i
			end
		end
	end
	return lastValidIndex, lastIndex
end

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

	self.author = args['author'] or args[2]
	self.work = args['work'] or args[3]
	
	for i = 1, 5 do
		local refName = 'ref' .. i
		local paramNumber = i + 3
		self[refName] = args[refName] or args[paramNumber]
	end

	--Check if we've been given an author alias.
	if data.authorAliases[self.author] then
		self.author = data.authorAliases[self.author]
	end

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

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

	--If the database has a link for the work, incorporate it.
	if not self.wData or not self.wData['wLink'] then
		results.work = self.work
	else
		results.work = '[[w:'..self.wData['wLink']..'|'..self.work..']]'
	end
	
	--Some works have info which overrides the author-level info or fills other parameters.
	if self.wData then
		if self.wData['year'] then
			results.year = self.wData.year
		end
		if self.wData['author'] ~= nil then
			results.author = self.wData.author
		end
		if self.wData['object'] then
			results.object = self.wData.object
		end
		if self.wData['style'] then
			results.style = self.wData.style
		end
	end

	--The displayed reference usually consists of all the ref argument(s) joined with a period.
	self.refDisplay = self.ref1 and '' or (self.wData and self.wData['refDefaultDisplay'] or false)
	local separator_num = 1
	for i = 1, 5 do
		local whichRef = 'ref' .. tostring(i)
		if self[whichRef] then
			local ref = self[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 self.aData and self.aData.rdFormat and self.aData.rdFormat.separator then
				separator = self.aData.rdFormat.separator
			elseif self.aData and self.aData.rdFormat and self.aData.rdFormat.separators then
				separator = self.aData.rdFormat.separators[separator_num]
			else
				separator = "."
			end
			
			if i > 1 then
				self.refDisplay = self.refDisplay .. separator
				separator_num = separator_num + 1
			end
			self.refDisplay = self.refDisplay .. self[whichRef]
		else
			break
		end
	end
	if args['through'] then
		args['thru'] = args['through']
	end
	if args['thru'] then
		self.refDisplay = self.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 self.wData and self.wData['rlFormat'] then
		self.rlFormat = self.aData['rlFormat'..tostring(self.wData.rlFormat)]
		if self.rlFormat then
			self.rlTitle = self.wData['rlTitle']
			
			-- Go through indices in "self.rlFormat" that contain a string
			-- beginning in ".ref" (either in the first level of "self.rlFormat"
			-- or a sublevel). Return the index of the string that has a
			-- corresponding field in "self", as well as the index of the last
			-- such string.
			local lastValidIndex, lastIndex = findLastValidRefIndex(self, self.rlFormat, '^%.ref(%d+)$')
			-- If there isn't another ".ref" string after the last valid index,
			-- then there is no need to cut short the rlFormat.
			local indexToStopAt
			if lastIndex and lastValidIndex and lastIndex > lastValidIndex then
				indexToStopAt = lastValidIndex
			else
				indexToStopAt = math.huge
			end
			
			self.refLink = {}
			for i, current in ipairs(self.rlFormat) do
				if i > indexToStopAt then
					break
				end
				
				table.insert(self.refLink, self:interpret(current))
			end
			self.refLink = table.concat(self.refLink)
		end
	end
	if self.wData and self.wData['xrlFormat'] then
		self.xrlFormat = self.aData['xrlFormat'..tostring(self.wData.xrlFormat)]
		if self.xrlFormat then
			self.xurl = self.wData['xurl']
			-- Go through indices in "self.xrlFormat" that contain a string
			-- beginning in ".ref" (either in the first level of "self.xrlFormat"
			-- or a sublevel). Return the index of the string that has a
			-- corresponding field in "self", as well as the index of the last
			-- such string.
			local lastValidIndex, lastIndex = findLastValidRefIndex(self, self.xrlFormat, '^%.ref(%d+)$')
			-- If there isn't another ".ref" string after the last valid index,
			-- then there is no need to cut short the rlFormat.
			local indexToStopAt
			if lastIndex and lastValidIndex and lastIndex > lastValidIndex then
				indexToStopAt = lastValidIndex
			else
				indexToStopAt = math.huge
			end
			
			self.xrefLink = {}
			for i, current in ipairs(self.xrlFormat) do
				if i > indexToStopAt then
					break
				end
				
				table.insert(self.xrefLink, self:interpret(current))
			end
			self.xrefLink = table.concat(self.xrefLink)
		end
	end
 	if self.refLink and self.refDisplay then
		results.ref = '[['..self.refLink..'|'..self.refDisplay..']]'
	elseif self.xrefLink and self.refDisplay then
		results.ref = '['..self.xrefLink..' '..self.refDisplay..']'
	else
		results.ref = self.refDisplay or ''
	end
	if args['notes'] then
		results.notes = args.notes
	end
	results.code = self.code
	
	return results

end

return export