Difference between revisions of "Module:Automatic archive navigator/sandbox"

From blackwiki
Jump to navigation Jump to search
blackwiki>Mr. Stradivarius
(a few more bug fixes)
blackwiki>Mr. Stradivarius
(finish the archive link formatting function)
Line 19: Line 19:
 
s = s:gsub('%p', '%%%0')
 
s = s:gsub('%p', '%%%0')
 
return s
 
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
 
end
  
Line 61: Line 36:
 
obj.currentTitle = currentTitle
 
obj.currentTitle = currentTitle
  
-- Define object structure
 
obj.links = {}
 
 
 
-- Archive prefix
 
-- Archive prefix
 
-- Decode HTML entities so users can enter things like "Archive&#32;" from
 
-- Decode HTML entities so users can enter things like "Archive&#32;" from
Line 87: Line 59:
 
return obj
 
return obj
 
end
 
end
 +
 +
-- Static methods
 +
 +
function Navigator.makeWikitable(t)
 +
-- Makes a wikitable from an array of strings.
 +
if #t < 1 then
 +
return ''
 +
end
 +
local ret = {}
 +
ret[#ret + 1] = '{| style="width:32em;background:transparent;' ..
 +
'margin:0 auto 0.5em;text-align:center"'
 +
for _, s in ipairs(t) do
 +
ret[#ret + 1] = '\n| '
 +
ret[#ret + 1] = s
 +
end
 +
ret[#ret + 1] = '\n|}'
 +
return table.concat(ret)
 +
end
 +
 +
-- Normal methods
  
 
function Navigator:message(key, ...)
 
function Navigator:message(key, ...)
Line 144: Line 136:
 
})
 
})
  
return mbox .. '__NONEWSECTIONLINK__ __NOEDITSECTION__'
+
return mbox
 
end
 
end
  
 
function Navigator:getArchiveNums()
 
function Navigator:getArchiveNums()
 
-- Returns an array of the archive numbers to format.
 
-- Returns an array of the archive numbers to format.
local noArchives = tonumber(self.args.links) or self:message('default-link-count')
+
local noLinks = tonumber(self.args.links) or self:message('default-link-count')
noArchives = math.floor(noArchives)
+
noLinks = math.floor(noLinks)
 
local allowRedLinks = not yesno(self.args.noredlinks)
 
local allowRedLinks = not yesno(self.args.noredlinks)
 
local current = self.currentArchiveNum
 
local current = self.currentArchiveNum
 
local highest = self.highestArchiveNum
 
local highest = self.highestArchiveNum
  
if not current or not highest or noArchives < 1 then
+
if not current or not highest or noLinks < 1 then
 
return {}
 
return {}
elseif noArchives == 1 then
+
elseif noLinks == 1 then
 
return {current}
 
return {current}
 
end
 
end
Line 180: Line 172:
 
 
 
-- Archive nums lower than the current page.
 
-- Archive nums lower than the current page.
for i = 1, math.floor((noArchives - 1) / 2) do
+
for i = 1, math.floor((noLinks - 1) / 2) do
 
local num = current - getOffset(i, current)
 
local num = current - getOffset(i, current)
 
if num <= 1 then
 
if num <= 1 then
Line 192: Line 184:
 
-- Current page.
 
-- Current page.
 
table.insert(nums, current)
 
table.insert(nums, current)
 +
-- Record the index of the current page so we can use it later when
 +
-- making the archive links.
 +
self.currentArchiveIndex = #nums
  
 
-- Higher archive nums.
 
-- Higher archive nums.
for i = 1, math.ceil((noArchives - 1) / 2) do
+
for i = 1, math.ceil((noLinks - 1) / 2) do
 
local num = current + getOffset(i, current)
 
local num = current + getOffset(i, current)
 
if num <= highest then
 
if num <= highest then
Line 210: Line 205:
 
end
 
end
  
function Navigator:makeLinksWikitable()
+
function Navigator:makeArchiveLinksWikitable()
-- Makes a single-row wikitable from an array of links
+
local lang = mw.language.getContentLanguage()
local ret = {}
+
local nums = self:getArchiveNums()
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)
+
-- Make the table of links.
if subpageTitle.exists then
+
local links = {}
return makeWikilink('/' .. subpageName, subpageName)
+
for i, num in ipairs(nums) do
else
+
local subpage = self.archivePrefix .. tostring(num)
return nil
+
local link = makeWikilink('/' .. subpage, subpage)
end
+
if num == self.currentArchiveNum then
end
+
link = string.format('<span style="font-size:115%%;">%s</span>', link)
 
+
end
local function getLinksText(args, currentTitle)
+
table.insert(links, link)
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
 
end
  
local ret = {}
+
-- Add the arrows.
 
+
-- We must do the forwards arrow first as we are adding elements to the
for offset = -5, -3 do
+
-- links table. If we did the backwards arrow first the index for the
local link = getSubpageLink(offset, currentTitle)
+
-- current archive would be wrong.
if link then
+
local currentIndex = self.currentArchiveIndex or math.floor(#links / 2)
if offset == -3 then
+
for i = currentIndex + 1, #links do
ret[#ret + 1] = link
+
if nums[i] - nums[i - 1] > 1 then
ret[#ret + 1] = linkSpacer
+
table.insert(links, i, lang:getArrow('forwards'))
else
 
ret[#ret + 1] = link
 
ret[#ret + 1] = ' ←'
 
ret[#ret + 1] = arrowSpacer
 
end
 
 
break
 
break
 
end
 
end
 
end
 
end
 
+
for i = currentIndex - 1, 1, -1 do
for offset = -2, -1 do
+
if nums[i + 1] - nums[i] > 1 then
local link = getSubpageLink(offset, currentTitle)
+
table.insert(links, i + 1, lang:getArrow('backwards'))
if link then
+
break
ret[#ret + 1] = link
 
ret[#ret + 1] = linkSpacer
 
 
end
 
end
 
end
 
end
  
ret[#ret + 1] = '<span style="font-size:115%;">'
+
return Navigator.makeWikitable(links)
ret[#ret + 1] = makeWikilink(currentTitle.prefixedText, currentTitle.subpageText)
+
end
ret[#ret + 1] = '</span>'
 
  
for offset = 1, 2 do
+
function Navigator:__tostring()
local link = getSubpageLink(offset, currentTitle)
+
return self:makeMessageBox() ..
if link then
+
'\n' ..
ret[#ret + 1] = linkSpacer
+
self:makeArchiveLinksWikitable() ..
ret[#ret + 1] = link
+
' __NONEWSECTIONLINK__ __NOEDITSECTION__'
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
 
end
  
Line 338: Line 269:
 
function p._exportClasses()
 
function p._exportClasses()
 
return {
 
return {
Archive = Archive,
 
 
Navigator = Navigator
 
Navigator = Navigator
 
}
 
}

Revision as of 13:03, 8 October 2014

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

-------------------------------------------------------------------------------
-- 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

	-- 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

-- Static methods

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

-- Normal methods

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
end

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

	if not current or not highest or noLinks < 1 then
		return {}
	elseif noLinks == 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((noLinks - 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)
	-- Record the index of the current page so we can use it later when
	-- making the archive links.
	self.currentArchiveIndex = #nums

	-- Higher archive nums.
	for i = 1, math.ceil((noLinks - 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:makeArchiveLinksWikitable()
	local lang = mw.language.getContentLanguage()
	local nums = self:getArchiveNums()

	-- Make the table of links.
	local links = {}
	for i, num in ipairs(nums) do
		local subpage = self.archivePrefix .. tostring(num)
		local link = makeWikilink('/' .. subpage, subpage)
		if num == self.currentArchiveNum then
			link = string.format('<span style="font-size:115%%;">%s</span>', link)
		end
		table.insert(links, link)
	end

	-- Add the arrows.
	-- We must do the forwards arrow first as we are adding elements to the
	-- links table. If we did the backwards arrow first the index for the
	-- current archive would be wrong.
	local currentIndex = self.currentArchiveIndex or math.floor(#links / 2)
	for i = currentIndex + 1, #links do
		if nums[i] - nums[i - 1] > 1 then
			table.insert(links, i, lang:getArrow('forwards'))
			break
		end
	end
	for i = currentIndex - 1, 1, -1 do
		if nums[i + 1] - nums[i] > 1 then
			table.insert(links, i + 1, lang:getArrow('backwards'))
			break
		end
	end

	return Navigator.makeWikitable(links)
end

function Navigator:__tostring()
	return self:makeMessageBox() ..
		'\n' .. 
		self:makeArchiveLinksWikitable() .. 
		' __NONEWSECTIONLINK__ __NOEDITSECTION__'
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 {
		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