Module:Ref

From Coral Island Wiki
Jump to navigation Jump to search

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

-- <nowiki>
local ref = {}
local getArgs = require('Dev:Arguments').getArgs
local lang = mw.language.getContentLanguage()

--sourcedata used for autofill functions
ref.sourcedata = {}

--[[--------------------------< A U T O F I L L  D A T A >-------------------------------------------------

looks for a module containing data that autofills a reference, aby finding its entry on a table using a shorthand

for example, "|game=FFVII" to autofill a reference for "Final Fantasy VII" based on a module containing as much

]]
--helper function for all to allow for recursion
local function recover(data, default_source, alternate_source, category, new, author_type)
	local target = new or category
	
	--use the currently selected version by default; if it has nothing, use the base case
	local source = default_source[category] or alternate_source[category]
	
	--if the data is an author table, split to first[n]= and last[n]=
    if type(source) == 'table' then
        if target == 'author' and author_type ~= 'developer' then
        	if type(source[1]) == 'table' then
		        for i, y in ipairs(source) do
		            data['first' .. i] = source[i][1]
		            data['last' .. i] = source[i][2]
		        end
	            if (author_type) then
	            	data['author-type'] = author_type
	            end
	        else
	            data.first = source[1]
    	        data.last = source[2]
	            if (author_type) then
	            	data['author-type'] = author_type
	            end
            end
    	else
    		--if it is a table, split to target[n]
	        for i, y in ipairs(source) do
	            data[target .. i] = y
	        end
	    end
    else
    	--if no table was found, assign as desired
    	data[target] = source
    end
end

local function gameRef(data, game, game_version)
    recover(data, game_version, game, 'developer', 'author', 'developer')
    recover(data, game_version, game, 'year')
    recover(data, game_version, game, 'month')
    recover(data, game_version, game, 'day')
    data['link'] = game_version['link'] or game['link']
    data['title'] = game_version['title'] or game['title']
    data['media'] = 'game'
    data['publisher']  = game_version['publisher'] or game['publisher']
    recover(data, game_version, game, 'platform')
end

--Automatically creates a reference for movies based on shorthand data module
local function movieRef(data, movie, movie_version)
    recover(data, movie_version, movie, 'director', 'author')
	data['author-type'] = 'Director'
    recover(data, movie_version, movie, 'year')
    recover(data, movie_version, movie, 'month')
    recover(data, movie_version, movie, 'day')
    data['link'] = movie['link']
    data['title'] = movie['title']
    data['title'] = movie_version['title'] or movie['title']
    data['media'] = 'film'
    data['publisher'] = movie_version['studio'] or movie['studio']
end

--Automatically creates a reference for series based on shorthand data module
local function seriesRef(data, series)
    recover(data, series, series, 'executive', 'author', 'Executive Producer')
    recover(data, series, series, 'year')
    data['link'] = series['link']
    data['title'] = series['title']
    data['media'] = 'series'
    data['publisher'] = series['studio']
end

--Automatically creates a reference for episode based on shorthand data module
local function episodeRef(data, episode, series)
    recover(data, episode, episode, 'writer', 'author', 'Writer')
    recover(data, episode, episode, 'director', 'author', 'Director')
    recover(data, episode, episode, 'writerdirector', 'author', 'Writer & Director')
    recover(data, episode, episode, 'year')
    recover(data, episode, episode, 'month')
    recover(data, episode, episode, 'day')
    data['entry-link'] = episode['link']
    data['entry'] = episode['title']
    data['link'] = series['link']
    data['title'] = series['title']
    data['media'] = 'episode'
    data['number'] = episode['number']
    data['season'] = episode['season']
    data['publisher'] = episode['studio']
    data['showfulldate'] = true
end

--Automatically creates a reference for books or manga based on shorthand data module
--Can also be used with a guide or design bible for a game
local function bookRef(data, book)
    recover(data, book, book, 'author')
    recover(data, book, book, 'year')
    recover(data, book, book, 'month')
    recover(data, book, book, 'day')
    data['link'] = book['link']
    data['title'] = book['title']
    data['media'] = 'book'
    data['additional-authors'] = book['additional-authors']
    data['publisher'] = book['publisher']
    data['isbn'] = book['isbn']
end

--Automatically creates a reference for an album based on shorthand data module
local function albumRef(data, album, album_version)
    recover(data, album_version, album, 'artist', 'author')
    recover(data, album_version, album, 'year')
    recover(data, album_version, album, 'month')
    recover(data, album_version, album, 'day')
    data['link'] = album['link']
    data['title'] = album['title']
    data['title'] = album_version['title'] or album['title']
    data['media'] = 'album'
    data['publisher'] = album_version['label']
end

--Automatically creates a reference for an album based on shorthand data module
local function songRef(data, song, song_version)
    recover(data, song_version, song, 'artist', 'author')
    recover(data, song_version, song, 'year')
    recover(data, song_version, song, 'month')
    recover(data, song_version, song, 'day')
    data['entry-link'] = song['link']
    data['entry'] = song_version['title'] or song['title']
    data['title'] = song_version['album'] or song['album']
    data['additional-authors'] = song_version['featured']
    data['media'] = 'song'
    data['publisher'] = song_version['label']
end

--[[--------------------------< H E L P E R  F U N C T I O N S >-------------------------------------------------

helper functions for parts of the reference, applies to all formats

]]
--Assistant to generate the id
local function makeId(data)
	local id = ''
    if data['id'] then
    	id = data['id']
    else
    	--first half of concatenated id, either author or publisher
    	local authorid = ''
    	if data['last'] then authorid = data['last']
		elseif data['last1'] then
			for n = 1, 10, 1 do
				if data['last' .. n] then
					authorid = authorid .. data['last' .. n]
				end
			end
    	elseif data['author'] or data['author1'] or data['publisher'] then
    		if data['author'] then authorid = data['author']
    		elseif data['author1'] then authorid = data['author1']
    		elseif data['publisher'] then authorid = data['publisher'] end

			authorid = authorid:gsub('%[%[(.-)%]%]', '')
            authorid = authorid:gsub('%s+', '')
            authorid = authorid:match("%|(.*)") or authorid
    	end
    	
    	--last half of concatenated id, either year or nothing
    	year = ''
    	if data['year'] then
    		year = data['year']
    	elseif data['year1'] then
    		year = data['year1']
    	end
    	
    	id = string.format('%s%s', authorid, year)
	end
	
	return id
end

--Assistant for dates
local function formatDates(data)
	local dates = ''
	
	--up to 10 dates
	for n = 0, 10, 1 do
		local m
		if n == 0 then m = '' else m = n end
		local to_append = ''
		
		--different output depending on chosen date format; defaults to just the year
		if data['month'..m] and data['showmonth'] then
			to_append = lang:formatDate('Y, F', data['year'..m] .. '-' .. data['month'..m])
		elseif data['day'..m] and data['showfulldate'] then
			to_append = lang:formatDate('Y, F j', data['year'..m] .. '-' .. data['month'..m] .. '-' .. data['day'..m])
		elseif data['dateformat'] then
			to_append = lang:formatDate(data['dateformat'], data['year'..m] .. '-' .. data['month'..m] .. '-' .. data['day'..m])
		else
			to_append = data['year'..m] or ''
		end
		
		if not (dates == '') and not (to_append == '') then dates = dates .. '; ' end
		dates = dates .. to_append
	end
	
	return dates
end

--Assistant for author section
local function formatAuthors(data)
	local authors = ''
	--up to 10 authors
	for n = 0, 10, 1 do
		local m
		if n == 0 then m = '' else m = n end
		local to_append = ''
		local separator = ','

		--either the first/last name of a person author, or name of the authoring group/organization
		if data['last'..m] then
    		to_append = string.format('%s, %s', data['last'..m], data['first'..m])
    		separator = ';'
    		if data['author-link'..m] then
    			to_append = string.format('[[%s|%s]]', data['author-link'..m], to_append)
    		end
    	elseif data['author'..m] then
    		to_append = data['author'..m]
    		if data['author-link'..m] then
    			to_append = string.format('[[%s|%s]]', data['author-link'..m], to_append)
    		end
    	end
    		
    	--the type of each author, if applicable; if all authors are the same type, 'author-type' applies
        if n > 0 and data['author-type'..m] then
        	to_append = to_append .. ' (' .. (data['author-type'..m]:gsub('^%l', string.upper)) .. ')'
		end
    	
		if not (authors == '') and not (to_append == '') then authors = authors .. separator .. ' ' end
		authors = authors .. to_append
	end
    
    --if referencing a tweet, add handle; a tweet can only have one author	
    if data['author-id'] and data['tweet-id'] then
    	twitter_user = data['author-id']
    	authors = authors .. string.format(
    		' \[[https://www.twitter.com/%s @%s]\]',
    		twitter_user, twitter_user)
    end
    
    --if referencing youtube, add channel name; applies to all authors 
    if data['channel'] or data['author-id'] and data['youtube-id'] then
    	channel = data['channel'] or data['author-id']
        authors = authors .. string.format(
        	' \[[https://www.youtube.com/@%s @%s]\]',
        	channel, channel)
    end
    
    --if referencing instagram, add account name; applies to all authors 
    if data['author-id'] and data['instagram-id'] then
    	instagram_user = data['author-id']
        authors = authors .. string.format(
        	' \[[https://www.instagram.com/%s %s]\]',
        	instagram_user, instagram_user)
    end
    
    --the type of all authors, if applicable; should not be used if individual 'author-typen' are set
    if data['author-type'] then
    	authors = authors .. string.format(' (%s)', data['author-type'])
    end
    
    return authors
end

--Assistant for url links section; formats either entry or title to be a link
local function formatUrl(data, to_format)
	if data['archive-url'] and data['brokenlink'] then
    	to_format = string.format('[%s %s]', data['archive-url'], to_format)
    elseif data['url'] then
    	to_format = string.format('[%s %s]', data['url'], to_format)
    elseif data['youtube-id'] then
    	to_format = string.format('[https://www.youtube.com/watch?v=%s %s]', data['youtube-id'], to_format)
    elseif data['tweet-id'] then
    	to_format = string.format('[https://twitter.com/%s/status/%s %s]', data['author-id'], data['tweet-id'], to_format)
    elseif data['instagram-id'] then
    	to_format = string.format('[https://instagram.com/p/%s %s]', data['instagram-id'], data['instagram-id'], to_format)
    end
    
    return to_format
end
    
--Assistant for title section
local function formatTitle(data)
    local title = assert(data['title'], 'Title is mandatory')
    
    if data['link'] then
    	title = string.format("[[%s|%s]]", (data['link']), title)
    elseif not data['entry'] and data['url'] then
    	title = formatUrl(data, title)
    elseif data['youtube-id'] then
    	title = string.format('[https://www.youtube.com/watch?v=%s %s]', data['youtube-id'], title)
    elseif data['tweet-id'] then
    	title = string.format('[https://twitter.com/%s/status/%s %s]', data['author-id'], data['tweet-id'], title)
    elseif data['instagram-id'] then
    	title = string.format('[https://www.instagram.com/p/%s %s]', data['instagram-id'], title)
    end
    
 	if not data['noitalictitle'] then
 		title = string.format("''%s''", title)
 	else
 		title = string.format('"%s"', title)
    end
	
	return title
end

--Assistant for url archives links
local function formatArchive(url, accessdate, archiveurl, archivedate)
    local link = ''
    if archiveurl then
        link = archiveurl
    else
        link = string.format('https://web.archive.org/web/%s', url)
    end
    
    if archivedate then
    	return string.format('Accessed %s. [%s Archived] from the original on %s. ', accessdate, link, archivedate)
    else
        return string.format('[%s Archived] from the original on %s. ', link, accessdate)
    end
end

--Assistant for additional content on the end of a reference
local function additionalContent(data)
	local additional = ''
	
    if data['extra'] then
    	additional = '. ' .. data['extra']
    end
    
    if data['publisher'] then
        additional = additional .. string.format(' %s. ', data['publisher'])
    end
    
    if data['additional-authors'] then
    	additional = additional .. string.format('%s. ', (data['additional-authors']))
    end
    
    if data['youtube-id'] then
    	additional = additional .. '[[wikipedia:YouTube|YouTube]]. '
    elseif data['tweet-id'] then
    	additional = additional .. '[[wikipedia:Twitter|Twitter]]. '
    elseif data['instagram-id'] then
    	additional = additional .. '[[wikipedia:Instagram|Instagram]]. '
    elseif data['platform'] then
    	additional = additional .. string.format(' %s. ', data['platform'])
    end

    if data['isbn'] then
        additional = additional .. string.format('[[wikipedia:ISBN|ISBN]] [[Special:Booksources/%s|%s]]. ', (data['isbn']), (data['isbn']))
    end
    
    if data['url'] and not data['broken-link'] then
    	additional = additional .. formatArchive(data['url'], assert(data['access-date'], 'Access dates are required for website references'), data['archive-url'], data['archive-date'])
    elseif data['broken-link'] then
    	additional = additional .. string.format('Archived from the [%s original] on %s. ', data['url'], assert(data['access-date'], 'Access dates are required for website references') or data['archive-date'])
    end
	
	return additional
end

--[[--------------------------< C O N S T R U C T O R  F U N C T I O N S >-------------------------------------------------

fuctions to construct the full reference

(note: only constructs Wikipedia CS1 ref at this stage)

]]

--constructs reference, in standard style similar to Wikipedia's CS1.
local function constructWikiRef(data)
	local ref_content = ''
	
	--if author and date, print Author (Date)
	--n.d. if no date, no author if no author found
	has_author = (data['last1'] or data['last'] or data['author1'] or data['author'])
	if has_author then
		ref_content = formatAuthors(data) .. ' '
	end
	has_date = (data['year1'] or data['year'])
	if has_date then
		ref_content = ref_content .. string.format('(%s). ', formatDates(data))
	else
		ref_content = ref_content .. '(n.d.) . '
	end
    
    --if pointing to a location in a source, Location in Title
    if data['location'] then
    	ref_content = ref_content .. string.format('"%s" in ', data['location'])
    end
    
    --if the media type is included, add this either after title or entry name
    local media = ''
    if data['media'] then
    	--first letter of the media type is capitalized
    	media = string.format(' [%s]', data['media']:gsub("^%l", string.upper))
	end
    
    --if pointing to an entry of a work, "Entry" before Title
    if data['entry'] then
    	local entry = data['entry']
    	if data['entry-link'] then
    		entry = string.format('[[%s|%s]]', data['entry-link'], data['entry'])
    	elseif data['url'] then
    		entry = formatUrl(data, entry)
    	end
    	ref_content = ref_content .. string.format('"%s"', entry)
	    if data['season'] or data['number'] then
	    	ref_content = ref_content .. ' (' 
	    	if data['season'] then
	    		ref_content = ref_content .. data['season']
    		end
	    	if data['season'] and data['number'] then
	    		ref_content = ref_content .. ', '
    		end
	    	if data['number'] then
	    		ref_content = ref_content .. data['number']
	    	end
    		ref_content = ref_content .. ') '
	    end
    	if data['media'] then 
	    	ref_content = ref_content .. media
	    end
	    ref_content = ref_content .. '. From '
    end
    
    --Title is mandatory
    ref_content = ref_content .. formatTitle(data)
    
	--if media type here, append title
	if data['media'] and not data['entry'] then 
    	ref_content = ref_content .. media
    end
    --if volume or issue no., append title
    if data['volume'] or data['issue'] then
    	ref_content = ref_content .. ' (' 
    	if data['volume'] then
    		ref_content = ref_content .. data['volume']
		end
    	if data['volume'] and data['issue'] then
    		ref_content = ref_content .. ', '
		end
    	if data['issue'] then
    		ref_content = ref_content .. data['issue']
    	end
		ref_content = ref_content .. ') '
    end
    --period after title either way
	ref_content = ref_content .. '. '
    
    --if pointing to a page number, add page number after Title
    if data['p'] or data['page'] then
    	ref_content = ref_content .. string.format(' p. %s.', (data['p'] or data['page']))
    end
    
    --added additional fields to be appended at the end
    ref_content = ref_content .. string.format(' %s', additionalContent(data))
    
    return ref_content
end

--[[--------------------------< M A I N  F U N C T I O N S >-------------------------------------------------

process parameter input to return reference

]]
--function to take argument inputs to pass to core functions
--parameter inputs override autogeeration
local function coreInput(frame)
	--parameters/data
	local args = getArgs(frame)
	local data = {}
	
	if args['game'] then
	    game = assert(ref.sourcedata[args['game']], 'Could not find an entry in shorthand data module matching this input')
	    game_version = game
	    if args['version'] then
	    	game_version = assert(game['versions'][args['version']], 'Could not find version matching this input')
    	end
    	gameRef(data, game, game_version)
	elseif args['movie'] then
	    movie = assert(ref.sourcedata[args['movie']], 'Could not find an entry in shorthand data module matching this input')
	    movie_version = movie
	    if args['version'] then
	    	movie_version = assert(movie['versions'][args['version']], 'Could not find version matching this input')
    	end
    	movieRef(data, movie, movie_version)
	elseif args['episode'] then
	    episode = assert(ref.sourcedata[args['episode']], 'Could not find an entry in shorthand data module matching this input')
	    series = ref.sourcedata[episode['series']]
    	episodeRef(data, episode, series)
	elseif args['series'] then
	    series = assert(ref.sourcedata[args['series']], 'Could not find an entry in shorthand data module matching this input')
    	seriesRef(data, series)
	elseif args['book'] then
	    book = assert(ref.sourcedata[args['book']], 'Could not find an entry in shorthand data module matching this input')
	    if args['version'] then
	    	book = assert(book['versions'][args['version']], 'Could not find version matching this input')
    	end
    	bookRef(data, book)
	elseif args['db'] then 
	    game = assert(ref.sourcedata[args['db']], 'Could not find an entry in shorthand data module matching this input')
		db = assert(game['db'], 'Could not find a design bible for this game')
	    if args['version'] then
	    	db = assert(db[args['version']], 'Could not find version matching this input')
    	end
    	bookRef(data, db)
	elseif args['guide'] then
	    game = assert(ref.sourcedata[args['guide']], 'Could not find an entry in shorthand data module matching this input')
		guide = assert(game['guide'], 'Could not find a guide for this game')
	    if args['version'] then
	    	guide = assert(guide[args['version']], 'Could not find version matching this input')
    	end
    	bookRef(data, guide)
	elseif args['album'] then
	    album = assert(ref.sourcedata[args['album']], 'Could not find an entry in shorthand data module matching this input')
	    album_version = album
	    if args['version'] then
	    	album_version = assert(album['versions'][args['version']], 'Could not find version matching this input')
    	end
    	albumRef(data, album, album_version)
	elseif args['song'] then
	    song = assert(ref.sourcedata[args['song']], 'Could not find an entry in shorthand data module matching this input')
	    song_version = song
	    if args['version'] then
	    	song_version = assert(song['versions'][args['version']], 'Could not find version matching this input')
    	end
    	songRef(data, song, song_version)
    end

	--merge data and args, where args takes priority
	for k, v in pairs(args) do
		data[k] = v or data[k]
	end
	
	--automatic types for refs not auto-generated
	if data['url'] then
		data['showfulldate'] = true
	elseif data['youtube-id'] then
		data['media'] = 'video'
    	data['showfulldate'] = true
	elseif data['tweet-id'] then
		data['media'] = 'tweet'
    	data['showfulldate'] = true
	elseif data['instagram-id'] then
		data['media'] = 'instagram'
    	data['showfulldate'] = true
	end
	
    local id = makeId(data)
	
	return data, id
end

--pass to this function to build reference
local function coreOutput(ref_content, id)
    refoutput = mw.html.create('span')
    	:attr('id', id)
    	:wikitext(ref_content)
	
	return refoutput
end

--returns reference in wiki-style format
function ref.wiki(frame)
	local data, id = coreInput(frame)
	local ref_content = constructWikiRef(data)
	return coreOutput(ref_content, id)
end

--main function (defaults to wiki reference)
ref.main = ref.wiki

--when importing this module, fill sourcedata table from another given module or input
function ref.custom_sourcedata(...)
	local sourcedata = {...}
	for i,source in ipairs(sourcedata) do
	    for k, val in pairs(source) do
    		ref.sourcedata[k] = val
	    end
	end
    return ref
end

return ref