Module:Sandbox/Mlkj

-- This module implements {{archive list}} in Lua, and adds a few

-- new features.

-- Process a numeric argument to make sure it is a positive

-- integer.

local function processNumArg( num )

if num then

num = tonumber( num )

if type( num ) == 'number' then

num = math.floor( num )

if num >= 0 then

return num

end

end

end

return nil

end

-- Checks whether a page exists, going through pcall

-- in case we are over the expensive function limit.

local function checkPageExists( title )

if not title then

error('No title passed to checkArchiveExists', 2)

end

local noError, titleObject = pcall(mw.title.new, title)

if not noError then

-- If we are over the expensive function limit then assume

-- that the page doesn't exist.

return false

else

if titleObject then

return titleObject.exists

else

return false -- Return false if given a bad title.

end

end

end

-- Checks every nth archive to see if it exists, and returns the

-- number of the first archive that doesn't exist. It is

-- necessary to do this in batches because each check is an

-- expensive function call, and we want to avoid making too many

-- of them so as not to go over the expensive function limit.

local function checkArchives( prefix, n, start )

local i = start

local exists = true

while exists do

exists = checkPageExists( prefix .. tostring( i ) )

if exists then

i = i + n

end

end

return i

end

-- Return the biggest archive number, using checkArchives()

-- and starting in intervals of 1000. This should get us a

-- maximum of 500,000 possible archives before we hit the

-- expensive function limit.

local function getBiggestArchiveNum( prefix, start, max )

-- Return the value for max if it is specified.

max = processNumArg( max )

if max then

return max

end

-- Otherwise, detect the largest archive number.

start = start or 1

local check1000 = checkArchives( prefix, 1000, start )

if check1000 == start then

return 0 -- Return 0 if no archives were found.

end

local check200 = checkArchives( prefix, 200, check1000 - 1000 )

local check50 = checkArchives( prefix, 50, check200 - 200 )

local check10 = checkArchives( prefix, 10, check50 - 50 )

local check1 = checkArchives( prefix, 1, check10 - 10 )

-- check1 is the first page that doesn't exist, so we want to

-- subtract it by one to find the biggest existing archive.

return check1 - 1

end

-- Get the archive link prefix (the title of the archive pages

-- minus the number).

local function getPrefix( root, prefix, prefixSpace )

local ret = root or mw.title.getCurrentTitle().prefixedText

ret = ret .. '/'

if prefix then

ret = ret .. prefix

if prefixSpace == 'yes' then

ret = ret .. ' '

end

else

ret = ret .. 'Archive '

end

return ret

end

-- Get the number of archives to put on one line. Set to

-- math.huge if there should be no line breaks.

local function getLineNum( links, nobr, isLong )

local linksToNum = tonumber( links )

local lineNum

if nobr == 'yes' or (links and not linksToNum) then

lineNum = math.huge

-- If links is a number, process it. Negative values and expressions

-- such as links=8/2 produced some interesting values with the old

-- template, but we will ignore those for simplicity.

elseif type(linksToNum) == 'number' and linksToNum >= 0 then

-- The old template rounded down decimals to the nearest integer.

lineNum = math.floor( linksToNum )

if lineNum == 0 then

-- In the old template, values of links between 0 and 0.999

-- suppressed line breaks.

lineNum = math.huge

end

else

if isLong==true then

lineNum = 3 -- Default to 3 links on long

else

lineNum = 10 -- Default to 10 on short

end

end

return lineNum

end

-- Gets the prefix to put before the archive links.

local function getLinkPrefix( prefix, space, isLong )

-- Get the link prefix.

local ret = ''

if isLong==true then ---- Default of old template for long is 'Archive '

if type(prefix) == 'string' then

if prefix == 'none' then -- 'none' overrides to empty prefix

ret = ''

else

ret = prefix

if space == 'yes' then

ret = ret .. ' '

end

end

else

ret = 'Archive '

end

else --type is not long

if type(prefix) == 'string' then

ret = prefix

if space == 'yes' then

ret = ret .. ' '

end

end

end

return ret

end

-- Get the number to start listing archives from.

local function getStart( start )

start = processNumArg( start )

if start then

return start

else

return 1

end

end

-- Get whether to leave a blank cell in the first row and column

-- If links start at 1, and lineNum is a round number, this aligns the first

-- column to start on a multiple of lineNum, which may be a nice round number

local function getLeaveFirstCellBlank( leaveFirstCellBlank )

return leaveFirstCellBlank == 'yes'

or leaveFirstCellBlank == 'y'

or leaveFirstCellBlank == 1

end

-- Process the separator parameter.

local function getSeparator( sep )

if sep and type(sep) == 'string' then

if sep == 'dot'

or sep =='pipe'

or sep == 'comma'

or sep == 'tpt-languages' then

return mw.message.new( sep .. '-separator' ):plain()

else

return sep

end

else

return nil

end

end

-- Generates the list of archive links. glargs.max must be either zero (for

-- no archives) or a positive integer value.

local function generateLinks( glargs )

if type( glargs ) ~= 'table' or not glargs.max or not glargs.prefix then

error('insufficient arguments passed to generateLinks', 2)

end

-- If there are no archives yet, return a message and a

-- link to create Archive one.

if glargs.max == 0 then

if glargs.isLong == true then

glargs.max = 1 -- One archive redlink is displayed for Long format

else -- Short error and a creat link is displayed for short

return 'no archives yet (create)'

end

end

-- Return an html error if the start number is greater than the

-- maximum number.

local start = glargs.start or 1

if start > glargs.max then

return 'Start value "'

.. tostring( start )

.. '" is greater than the most recent archive number "'

.. tostring( glargs.max )

.. '".'

end

local linkPrefix = glargs.linkPrefix or ''

local lineNum = glargs.lineNum or 10

local sep = '' -- Long default separator is cell elements, short is ', '

local lineSep = '' -- Long default linebreak is row elements short is '\n'

if glargs.isLong==true then

sep = glargs.sep or ''

sep = sep .. ''

lineSep = glargs.lineSep or ''

lineSep = lineSep .. ''

else

sep = glargs.sep or mw.message.new( 'comma-separator' ):plain()

lineSep = glargs.lineSep or '
'

end

-- Generate the archive links.

local lineCounter = 1 -- The counter to see whether we need a line break or not.

local ret = {} -- A table containing the strings to be returned.

if glargs.isLong == true then --Long version is a table

table.insert(ret, "

")

end

if glargs.leaveFirstCellBlank then

-- An empty first cell aligns the first column on multiples of lineNum

table.insert(ret, sep)

lineCounter = lineCounter + 1

end

for archiveNum = start, glargs.max do

local link = mw.ustring.format(

'%s%d',

glargs.prefix, archiveNum, linkPrefix, archiveNum

)

table.insert( ret, link )

-- If we don't need a new line, output a comma. We don't need

-- a comma after the last link.

if lineCounter < lineNum and archiveNum < glargs.max then

table.insert( ret, sep )

lineCounter = lineCounter + 1

-- Output new lines if needed. We don't need a new line after

-- the last link.

elseif lineCounter >= lineNum and archiveNum < glargs.max then

table.insert( ret, lineSep )

lineCounter = 1

end

end

if glargs.isLong == true then --Long version is a table

table.insert(ret, "

")

end

return table.concat( ret )

end

-- Determine if format should be long

local function findFormType( auto )

if auto == nil or auto == '' then

return false

elseif auto == 'long' then

return true

else

return false

end

end

-- Get the archive data and pass it to generateLinks().

local function _main( args )

local isLong = findFormType( args.auto )

local prefix = getPrefix( args.root, args.prefix, args.prefixspace )

local lineNum = getLineNum( args.links, args.nobr, isLong )

local linkPrefix = getLinkPrefix( args.linkprefix, args.linkprefixspace, isLong )

local start = getStart( args.start )

local max = getBiggestArchiveNum( prefix, start, args.max )

local sep = getSeparator( args.sep )

local lineSep = getSeparator( args.linesep )

local leaveFirstCellBlank = getLeaveFirstCellBlank( args.leavefirstcellblank )

local glargs = {

start = start,

max = max,

prefix = prefix,

linkPrefix = linkPrefix,

isLong = isLong,

sep = sep,

lineNum = lineNum,

lineSep = lineSep,

leaveFirstCellBlank = leaveFirstCellBlank

}

return generateLinks( glargs )

end

-- A wrapper function to make getBiggestArchiveNum() available from

-- #invoke.

local function _count( args )

local prefix = getPrefix( args.root, args.prefix, args.prefixspace )

local archiveMax = getBiggestArchiveNum( prefix )

return archiveMax

end

function makeWrapper( func )

return function( frame )

-- If we are being called from #invoke, get the args from #invoke

-- if they exist, or else get the arguments passed to the parent

-- frame. Otherwise, assume the arguments are being passed directly

-- in from another module or from the debug console.

local origArgs

if frame == mw.getCurrentFrame() then

origArgs = frame:getParent().args

for k, v in pairs( frame.args ) do

origArgs = frame.args

break

end

else

origArgs = frame

end

-- Ignore blank values for parameters other than "links",

-- which functions differently depending on whether it is

-- blank or absent.

local args = {}

for k, v in pairs( origArgs ) do

if k == 'links' or v ~= '' then

args[k] = v

end

end

return func( args )

end

end

return {

main = makeWrapper( _main ),

count = makeWrapper( _count )

}