Module:Automatic archive navigator/sandbox

From blackwiki
< Module:Automatic archive navigator
Revision as of 12:02, 8 October 2014 by blackwiki>Mr. Stradivarius (a few more bug fixes)
Jump to navigation Jump to search

Documentation for this module may be created at Module:Automatic archive navigator/sandbox/doc

--
-- This module implements {{Automatic archive navigator}}
--

require('Module:No globals')
local checkType = require('libraryUtil').checkType
local yesno = require('Module:Yesno')

-------------------------------------------------------------------------------
-- Helper functions
-------------------------------------------------------------------------------

local function makeWikilink(page, display)
	return string.format('[[%s|%s]]', page, display)
end

local function escapePattern(s)
	-- Escape punctuation in a string so it can be used in a Lua pattern.
	s = s:gsub('%p', '%%%0')
	return s
end

-------------------------------------------------------------------------------
-- Archive class
-------------------------------------------------------------------------------

local Archive = {}
Archive.__index = Archive

function Archive.new(basePageName, prefix, num)
	checkType('Archive.new', 1, num, 'number')
	local obj = setmetatable({}, Archive)
	obj.basePageName = basePageName
	obj.prefix = prefix
	obj.num = num
	return obj
end

function Archive:makeLink(current)
	local subpage = self.prefix .. tostring(self.num)
	local link = makeWikilink('/' .. subpage, subpage)
	if current and self.num == current then
		link = string.format('<span style="font-size:115%;">%s</span>', link)
	end
	return link
end

-------------------------------------------------------------------------------
-- Navigator class
-------------------------------------------------------------------------------

local Navigator = {}
Navigator.__index = Navigator

function Navigator.new(args, cfg, currentTitle)
	local obj = setmetatable({}, Navigator)
	
	-- Set inputs
	obj.args = args
	obj.cfg = cfg
	obj.currentTitle = currentTitle

	-- Define object structure
	obj.links = {}
	
	-- Archive prefix
	-- Decode HTML entities so users can enter things like "Archive&#32;" from
	-- wikitext.
	obj.archivePrefix = obj.args.prefix or obj:message('archive-prefix')
	obj.archivePrefix = mw.text.decode(obj.archivePrefix)

	-- Current archive number
	do
		local pattern = string.format(
			'^%s([1-9][0-9]*)$',
			escapePattern(obj.archivePrefix)
		)
		obj.currentArchiveNum = obj.currentTitle.subpageText:match(pattern)
		obj.currentArchiveNum = tonumber(obj.currentArchiveNum)
	end
	
	-- Highest archive number
	obj.highestArchiveNum = require('Module:Highest archive number')._main(
		 obj.currentTitle.nsText .. ':' .. obj.currentTitle.baseText .. '/' .. obj.archivePrefix
	)

	return obj
end

function Navigator:message(key, ...)
	local msg = self.cfg[key]
	if select('#', ...) > 0 then
		return mw.message.newRawMessage(msg, ...):plain()
	else
		return msg
	end
end

function Navigator:makeBlurb()
	local args = self.args
	if args[1] == '1' then
		-- The old template used "|1" to suppress the blurb.
		return ''
	else
		local ret = '----\n'
		if args.text then
			ret = ret .. args.text
		else
			local talkLink = makeWikilink(
				self.currentTitle.nsText ..	':' .. self.currentTitle.baseText,
				self:message('blurb-talk-link-display')
			)
			if args.period then
				ret = ret .. self:message('blurb-period', talkLink, args.period)
			else
				ret = ret .. self:message('blurb-noperiod', talkLink)
			end
		end
		return ret
	end
end

function Navigator:makeMessageBox()
	local args = self.args
	
	local image
	if args.image then
		image = args.image
	else
		local icon = args.icon or self:message('default-icon')
		image = string.format(
			'[[File:%s|%s|alt=|link=]]',
			icon,
			self:message('image-size')
		)
	end

	local mbox = require('Module:Message box').main('tmbox', {
		image = image,
		imageright = args.imageright,
		style = args.style or 'width:80%;margin-left:auto;margin-right:auto',
		textstyle = args.textstyle or 'text-align:center',
		text = self:makeBlurb()
	})

	return mbox .. '__NONEWSECTIONLINK__ __NOEDITSECTION__'
end

function Navigator:getArchiveNums()
	-- Returns an array of the archive numbers to format.
	local noArchives = tonumber(self.args.links) or self:message('default-link-count')
	noArchives = math.floor(noArchives)
	local allowRedLinks = not yesno(self.args.noredlinks)
	local current = self.currentArchiveNum
	local highest = self.highestArchiveNum

	if not current or not highest or noArchives < 1 then
		return {}
	elseif noArchives == 1 then
		return {current}
	end
	
	local function getOffset(i, current)
		-- Gets the positive offset for an archive given i, the position in the
		-- array away from the current archive, and the current archive number.
		-- The first two offsets are consecutive; the third offset is rounded
		-- up to the nearest 5; and the fourth and subsequent offsets are
		-- rounded up to the nearest 10. The offsets are calculated in such a
		-- way that archive numbers will not be duplicated.
		if i <= 2 then
			return i
		elseif i <= 3 then
			return 7 - (current + 2) % 5
		else
			return 7 - (current + 7) % 10 + (i - 3) * 10
		end
	end

	local nums = {}
	
	-- Archive nums lower than the current page.
	for i = 1, math.floor((noArchives - 1) / 2) do
		local num = current - getOffset(i, current)
		if num <= 1 then
			table.insert(nums, 1, 1)
			break
		else
			table.insert(nums, 1, num)
		end
	end

	-- Current page.
	table.insert(nums, current)

	-- Higher archive nums.
	for i = 1, math.ceil((noArchives - 1) / 2) do
		local num = current + getOffset(i, current)
		if num <= highest then
			table.insert(nums, num)
		elseif allowRedLinks and (i <= 2 or i <= 3 and num == nums[#nums] + 1) then
			-- Only insert one red link, and only if it is consecutive.
			table.insert(nums, highest + 1)
			break
		else
			break
		end
	end

	return nums
end

function Navigator:makeLinksWikitable()
	-- Makes a single-row wikitable from an array of links
	local ret = {}
	ret[#ret + 1] = '{| style="width:32em;background:transparent;' ..
		'margin:0 auto 0.5em;text-align:center"'
	for _, link in ipairs(self.links) do
		ret[#ret + 1] = '\n| '
		ret[#ret + 1] = link
	end
	ret[#ret + 1] = '\n|}'
	return table.concat(ret)
end

function Navigator:__tostring()
	return self:makeMessageBox()
end

local function getSubpageLink(offset, currentTitle)
	-- Get a formatted link to the subpage a certain distance away, or nil
	-- if that subpage does not exist. e.g. If the current subpage is
	-- /Archive 27, then getSubpageLink(3, currentTitle) returns a link to Archive 30.
	local subpageName
	local archiveNum = currentTitle.subpageText:match('^Archive ([1-9][0-9]*)$')
	archiveNum = tonumber(archiveNum)
	if archiveNum then
		subpageName = 'Archive ' .. (archiveNum + offset)
	elseif tonumber(currentTitle.subpageText) then
		subpageName = currentTitle.subpageText + offset
	else
		return nil  -- Couldn't parse out a subpage number
	end

	local subpageTitle = mw.title.new(currentTitle.baseText .. '/' .. subpageName, currentTitle.namespace)
	if subpageTitle.exists then
		return makeWikilink('/' .. subpageName, subpageName)
	else
		return nil
	end
end

local function getLinksText(args, currentTitle)
	local nbsp = '&nbsp;'
	local arrowSpacer, linkSpacer
	if mw.ustring.len(currentTitle.subpageText) <= 4 then
		-- If page names are short, we want more space.
		arrowSpacer = nbsp:rep(5)
		linkSpacer = nbsp:rep(7)
	else
		arrowSpacer = nbsp:rep(2)
		linkSpacer = nbsp:rep(4)
	end

	local ret = {}

	for offset = -5, -3 do
		local link = getSubpageLink(offset, currentTitle)
		if link then
			if offset == -3 then
				ret[#ret + 1] = link
				ret[#ret + 1] = linkSpacer
			else
				ret[#ret + 1] = link
				ret[#ret + 1] = ' ←'
				ret[#ret + 1] = arrowSpacer
			end
			break
		end
	end

	for offset = -2, -1 do
		local link = getSubpageLink(offset, currentTitle)
		if link then
			ret[#ret + 1] = link
			ret[#ret + 1] = linkSpacer
		end
	end

	ret[#ret + 1] = '<span style="font-size:115%;">'
	ret[#ret + 1] = makeWikilink(currentTitle.prefixedText, currentTitle.subpageText)
	ret[#ret + 1] = '</span>'

	for offset = 1, 2 do
		local link = getSubpageLink(offset, currentTitle)
		if link then
			ret[#ret + 1] = linkSpacer
			ret[#ret + 1] = link
		end
	end

	for offset = 5, 3, -1 do
		local link = getSubpageLink(offset, currentTitle)
		if link then
			if offset == 3 then
				ret[#ret + 1] = linkSpacer
				ret[#ret + 1] = link
			else
				ret[#ret + 1] = arrowSpacer
				ret[#ret + 1] = '→ '
				ret[#ret + 1] = link
			end
			break
		end
	end

	return table.concat(ret)
end

-------------------------------------------------------------------------------
-- Exports
-------------------------------------------------------------------------------

local p = {}

function p._aan(args, cfg, currentTitle)
	cfg = cfg or require('Module:AutomaticArchiveNavigator/config')
	currentTitle = currentTitle or mw.title.getCurrentTitle()
	local aan = Navigator.new(args, cfg, currentTitle)
	return tostring(aan)
end

function p._tan(args)
	args.links = args.links or 3
	args.noredlinks = args.noredlinks or false
	return p._aan(args)
end

function p._exportClasses()
	return {
		Archive = Archive,
		Navigator = Navigator
	}
end

setmetatable(p, {__index = function(t, k)
	return function(frame)
		local args = require('Module:Arguments').getArgs(frame, {
			wrappers = {
				'Template:Automatic archive navigator',
				'Template:Talk archive navigation'
			}
		})
		return p['_' .. k](args)
	end
end})

return p