Open main menu

Difference between revisions of "Module:Listen"

m (1 revision imported)
m (1 revision imported)
 
(One intermediate revision by one other user not shown)
Line 1: Line 1:
-- This module implements {{listen}}.
 
 
 
local mFileLink = require('Module:File link')
 
local mFileLink = require('Module:File link')
 
local mTableTools = require('Module:TableTools')
 
local mTableTools = require('Module:TableTools')
 
local mSideBox = require('Module:Side box')
 
local mSideBox = require('Module:Side box')
 +
local lang = mw.language.new('en')
  
 
local p = {}
 
local p = {}
local hasMissing -- For the tracking category
 
  
function p.main(frame)
+
local function formatLength(length)
local origArgs = frame:getParent().args
+
-- Formats a duration in seconds in "(h:)mm:ss" (minutes are zero-padded
local args = {}
+
-- only if there are hours).
for k, v in pairs(origArgs) do
+
if not length or length == 0 then
v = v:match('^%s*(.-)%s*$')
+
return nil
if v ~= '' then
+
end
args[k] = v
+
 
 +
-- Add 0.5 to offset the rounding down
 +
local t = lang:getDurationIntervals(length + 0.5, { 'hours', 'minutes', 'seconds' })
 +
local s = t.seconds and string.format('%02d', t.seconds) or '00'
 +
local m = t.minutes or 0
 +
 
 +
local span = mw.html.create('span'):addClass('duration')
 +
if t.hours then
 +
span
 +
:tag('span')
 +
:addClass('h')
 +
:wikitext(t.hours)
 +
:done()
 +
:wikitext(':')
 +
m = string.format('%02d', m)
 +
end
 +
span
 +
:tag('span')
 +
:addClass('min')
 +
:wikitext(m)
 +
:done()
 +
:wikitext(':')
 +
:tag('span')
 +
:addClass('s')
 +
:wikitext(s)
 +
:done()
 +
return tostring(span)
 +
end
 +
 
 +
local function renderRow(filename, title, play, alt, description, start, length, hasImage)
 +
-- Renders the HTML for one file description row.
 +
if not filename then
 +
return nil
 +
end
 +
 
 +
length = formatLength(length)
 +
length = length and string.format(' (%s)', length) or ''
 +
 
 +
local root = mw.html.create('')
 +
root:tag('div')
 +
:addClass('haudio')
 +
:newline()
 +
:tag('div')
 +
:addClass('listen-file-header')
 +
:wikitext(string.format(
 +
'[[:File:%s|%s]]%s',
 +
filename,
 +
title or '',
 +
length
 +
))
 +
:done()
 +
:newline()
 +
:tag('div')
 +
:wikitext(play ~= 'no' and mFileLink._main{
 +
file = filename,
 +
size = hasImage and '232px' or '215px',
 +
alt = alt,
 +
start = start
 +
}
 +
or nil
 +
)
 +
:done()
 +
:newline()
 +
:tag('div')
 +
:addClass('description')
 +
:wikitext(description)
 +
:done()
 +
:done()
 +
return tostring(root)
 +
end
 +
 
 +
local function renderTrackingCategories(isPlain, hasMissing, isEmpty, titleObj)
 +
-- Renders all tracking categories produced by the template.
 +
-- isPlain, hasMissing and isEmpty are passed through from p._main,
 +
-- and the titleObj is only used for testing purposes.
 +
local cats = {}
 +
local currentTitle = titleObj or mw.title.getCurrentTitle()
 +
if currentTitle.namespace == 0 then
 +
-- We are in mainspace.
 +
if not isEmpty then
 +
cats[#cats + 1] = 'Articles with hAudio microformats'
 +
end
 +
if hasMissing then
 +
cats[#cats + 1] = 'Articles with empty listen template'
 
end
 
end
 
end
 
end
return p._main(args)
+
if isPlain then
 +
cats[#cats + 1] = 'Listen template using plain parameter'
 +
end
 +
for i, cat in ipairs(cats) do
 +
cats[i] = string.format('[[Category:%s]]', cat)
 +
end
 +
return table.concat(cats)
 
end
 
end
  
 
function p._main(args)
 
function p._main(args)
-- Find whether we are outputting a plain or an embedded box.
+
-- Organise the arguments by number.
 
local isPlain = args.plain == 'yes'
 
local isPlain = args.plain == 'yes'
 
local isEmbedded = args.embed and true
 
local isEmbedded = args.embed and true
 +
local hasImage = not isPlain and not isEmbedded and args.image ~= 'none'
  
-- Organise the arguments by number.
+
local numArgs, missingFiles = {}, {}
local numArgs = {}
 
 
do
 
do
 
local origNumArgs = mTableTools.numData(args)
 
local origNumArgs = mTableTools.numData(args)
Line 33: Line 120:
 
for i, t in ipairs(origNumArgs) do
 
for i, t in ipairs(origNumArgs) do
 
-- Check if the files exist.
 
-- Check if the files exist.
local obj = t.filename and mw.title.new('Media:' .. t.filename)
+
local obj = t.filename and mw.title.makeTitle(-2, t.filename)
 
if obj and obj.exists then
 
if obj and obj.exists then
 +
if t.length == 'yes' or
 +
-- Show length if the video height would be less than 150px
 +
obj.file.width / obj.file.height > (hasImage and 1.547 or 1.434)
 +
then
 +
t.length = obj.file.length
 +
else
 +
t.length = nil
 +
end
 
numArgs[#numArgs + 1] = t
 
numArgs[#numArgs + 1] = t
 
else
 
else
hasMissing = true
+
missingFiles[#missingFiles + 1] = t.filename or i
 
end
 
end
 
end
 
end
-- Exit early if none exist.
+
end
if #numArgs == 0 then
+
 
return p.renderTrackingCategories(isPlain, true)
+
-- Render warning
 +
local hasMissing = #missingFiles ~= 0
 +
local previewWarning = ''
 +
if hasMissing then
 +
for i, v in ipairs(missingFiles) do
 +
missingFiles[i] = type(v) == 'string'
 +
and string.format('missing file "%s"', v)
 +
or string.format('empty filename #%s', v)
 
end
 
end
 +
previewWarning = string.format(
 +
'Page using [[Template:Listen]] with %s',
 +
mw.text.listToText(missingFiles)
 +
)
 +
previewWarning = require('Module:If preview')._warning({previewWarning})
 +
end
 +
 +
-- Exit early if none exist.
 +
if #numArgs == 0 then
 +
return previewWarning .. renderTrackingCategories(isPlain, hasMissing, true)
 
end
 
end
  
 
-- Build the arguments for {{side box}}
 
-- Build the arguments for {{side box}}
local sbargs = {}
+
local sbArgs = {
sbargs.class = 'noprint'
+
metadata = 'no',
sbargs.metadata = 'no'
+
position = (isPlain or isEmbedded) and 'left' or args.pos,
sbargs.position = args.pos
+
style = args.style,
 +
templatestyles = 'Module:Listen/styles.css'
 +
}
  
-- Style arguments
+
-- Class arguments
 
do
 
do
local style = {}
+
local class = {
 +
'listen',
 +
'noprint'
 +
}
 
if isPlain then
 
if isPlain then
style[#style + 1] = 'border:none'
+
table.insert(class, 'listen-plain')
style[#style + 1] = 'background:transparent'
 
style[#style + 1] = 'float:none'
 
 
end
 
end
 
if isEmbedded then
 
if isEmbedded then
style[#style + 1] = 'border-collapse:collapse'
+
table.insert(class, 'listen-embedded')
style[#style + 1] = 'border-width:1px 0 0 0'
 
style[#style + 1] = 'background:transparent'
 
style[#style + 1] = 'float:none'
 
style[#style + 1] = 'margin:0 -5px'
 
 
end
 
end
if args.pos == 'left' then
+
if not hasImage then
style[#style + 1] = 'float:left'
+
table.insert(class, 'listen-noimage')
 +
end
 +
if args.pos == 'left' and not isPlain and not isEmbedded then
 +
table.insert(class, 'listen-left')
 
elseif args.pos == 'center' then
 
elseif args.pos == 'center' then
style[#style + 1] = 'float:none'
+
table.insert(class, 'listen-center')
style[#style + 1] = 'margin-left:auto'
 
style[#style + 1] = 'margin-right:auto'
 
 
end
 
end
+
 
style[#style + 1] = args.style
+
sbArgs.class = table.concat(class, ' ')
sbargs.style = table.concat(style, '; ')
 
 
end
 
end
sbargs.textstyle = 'line-height:1.1em'
 
  
 
-- Image
 
-- Image
 
if not isPlain and not isEmbedded then
 
if not isPlain and not isEmbedded then
 
if args.image then
 
if args.image then
sbargs.image = args.image
+
sbArgs.image = args.image
 
else
 
else
 
local images = {
 
local images = {
 
speech = 'Audio-input-microphone.svg',
 
speech = 'Audio-input-microphone.svg',
music = 'Gnome-mime-audio-openclipart.svg'
+
music = 'Gnome-mime-audio-openclipart.svg',
 +
default = 'Gnome-mime-sound-openclipart.svg'
 
}
 
}
local image = args.type
+
sbArgs.image = mFileLink._main{
and images[args.type]
+
file = args.type and images[args.type] or images.default,
or 'Gnome-mime-sound-openclipart.svg'
 
sbargs.image = mFileLink._main{
 
file = image,
 
 
size = '65x50px',
 
size = '65x50px',
 
location = 'center',
 
location = 'center',
Line 107: Line 215:
 
if args.header then
 
if args.header then
 
header = mw.html.create('div')
 
header = mw.html.create('div')
header:css{
+
header:addClass('listen-header')
background = 'transparent',
 
['text-align'] = 'left',
 
padding = args.embed and '2px 0' or '2px'
 
}
 
 
:wikitext(args.header)
 
:wikitext(args.header)
header = tostring(header)
+
header = tostring(header) .. '\n'
header = header .. '\n'
 
 
else
 
else
 
header = ''
 
header = ''
Line 120: Line 223:
 
local text = {}
 
local text = {}
 
for i, t in ipairs(numArgs) do
 
for i, t in ipairs(numArgs) do
text[#text + 1] = p.renderRow(
+
text[#text + 1] = renderRow(
t.filename, t.title, t.play, t.alt, t.description, t.start
+
t.filename, t.title, t.play, t.alt, t.description, t.start,
 +
t.length, hasImage
 
)
 
)
 
if numArgs[i + 1] then
 
if numArgs[i + 1] then
Line 127: Line 231:
 
end
 
end
 
end
 
end
sbargs.text = header .. table.concat(text)
+
sbArgs.text = header .. table.concat(text)
 
end
 
end
  
 
-- Below
 
-- Below
 
if not isPlain and not isEmbedded and args.help ~= 'no' then
 
if not isPlain and not isEmbedded and args.help ~= 'no' then
sbargs.below = string.format(
+
sbArgs.below = string.format(
 
'<hr/><i class="selfreference">Problems playing %s? See [[Help:Media|media help]].</i>',
 
'<hr/><i class="selfreference">Problems playing %s? See [[Help:Media|media help]].</i>',
 
#numArgs == 1 and 'this file' or 'these files'
 
#numArgs == 1 and 'this file' or 'these files'
Line 139: Line 243:
  
 
-- Render the side box.
 
-- Render the side box.
local sideBox = mSideBox._main(sbargs)
+
local sideBox = mSideBox._main(sbArgs)
  
 
-- Render the tracking categories.
 
-- Render the tracking categories.
local trackingCategories = p.renderTrackingCategories(isPlain)
+
local trackingCategories = renderTrackingCategories(isPlain, hasMissing)
  
return sideBox .. trackingCategories
+
return previewWarning .. sideBox .. trackingCategories
 
end
 
end
  
function p.renderRow(filename, title, play, alt, description, start)
+
function p.main(frame)
-- Renders the HTML for one file description row.
+
local origArgs = frame:getParent().args
if not filename then
+
local args = {}
return nil
+
for k, v in pairs(origArgs) do
end
+
if v ~= '' then
local root = mw.html.create('')
+
args[k] = v
root:tag('div')
 
:addClass('haudio')
 
:newline()
 
:tag('div')
 
:css('padding', '4px 0')
 
:wikitext(string.format('[[:File:%s|%s]]', filename, title or ''))
 
:done()
 
:newline()
 
:tag('div')
 
:wikitext(
 
play ~= 'no'
 
and mFileLink._main{
 
file = filename,
 
size = '220px',
 
alt = alt,
 
start = start
 
}
 
or nil
 
)
 
:done()
 
:newline()
 
:tag('div')
 
:css('padding', '2px 0 0 0')
 
:addClass('description')
 
:wikitext(description)
 
:done()
 
:done()
 
return tostring(root)
 
end
 
 
 
function p.renderTrackingCategories(isPlain, isEmpty, titleObj)
 
-- Renders all tracking categories produced by the template.
 
-- isPlain and isEmpty are passed through from p._main,
 
-- and the titleObj is only used for testing purposes.
 
local cats = {}
 
local currentTitle = titleObj or mw.title.getCurrentTitle()
 
if currentTitle.namespace == 0 then
 
-- We are in mainspace.
 
if not isEmpty then
 
cats[#cats + 1] = 'Articles with hAudio microformats'
 
end
 
if hasMissing then
 
cats[#cats + 1] = 'Articles with empty listen template'
 
 
end
 
end
 
end
 
end
if isPlain then
+
return p._main(args)
cats[#cats + 1] = 'Listen template using plain parameter'
 
end
 
for i, cat in ipairs(cats) do
 
cats[i] = string.format('[[Category:%s]]', cat)
 
end
 
return table.concat(cats)
 
 
end
 
end
  
 
return p
 
return p

Latest revision as of 00:37, 19 January 2024

This module implements the {{listen}} template.

Usage from wikitext

This module cannot be used directly from wikitext. It can only be used through the {{listen}} template. Please see the template page for documentation.

Usage from Lua modules

To use this module from other Lua modules, first load the module.

local mListen = require('Module:Listen')

You can then generate the listen box by using the _main function.

mListen._main(args)

The args variable should be a table containing the arguments to pass to the module. To see the different arguments that can be specified and how they affect the module output, please refer to the {{listen}} template documentation.

Tracking/maintenance categories


local mFileLink = require('Module:File link')
local mTableTools = require('Module:TableTools')
local mSideBox = require('Module:Side box')
local lang = mw.language.new('en')

local p = {}

local function formatLength(length)
	-- Formats a duration in seconds in "(h:)mm:ss" (minutes are zero-padded
	-- only if there are hours).
	if not length or length == 0 then
		return nil
	end

	-- Add 0.5 to offset the rounding down
	local t = lang:getDurationIntervals(length + 0.5, { 'hours', 'minutes', 'seconds' })
	local s = t.seconds and string.format('%02d', t.seconds) or '00'
	local m = t.minutes or 0

	local span = mw.html.create('span'):addClass('duration')
	if t.hours then
		span
			:tag('span')
				:addClass('h')
				:wikitext(t.hours)
				:done()
			:wikitext(':')
		m = string.format('%02d', m)
	end
	span
		:tag('span')
			:addClass('min')
			:wikitext(m)
			:done()
		:wikitext(':')
		:tag('span')
			:addClass('s')
			:wikitext(s)
			:done()
	return tostring(span)
end

local function renderRow(filename, title, play, alt, description, start, length, hasImage)
	-- Renders the HTML for one file description row.
	if not filename then
		return nil
	end

	length = formatLength(length)
	length = length and string.format(' (%s)', length) or ''

	local root = mw.html.create('')
	root:tag('div')
		:addClass('haudio')
		:newline()
		:tag('div')
			:addClass('listen-file-header')
			:wikitext(string.format(
				'[[:File:%s|%s]]%s',
				filename,
				title or '',
				length
			))
			:done()
		:newline()
		:tag('div')
			:wikitext(play ~= 'no' and mFileLink._main{
					file = filename,
					size = hasImage and '232px' or '215px',
					alt = alt,
					start = start
				}
				or nil
			)
			:done()
		:newline()
		:tag('div')
			:addClass('description')
			:wikitext(description)
			:done()
		:done()
	return tostring(root)
end

local function renderTrackingCategories(isPlain, hasMissing, isEmpty, titleObj)
	-- Renders all tracking categories produced by the template.
	-- isPlain, hasMissing and isEmpty are passed through from p._main,
	-- and the titleObj is only used for testing purposes.
	local cats = {}
	local currentTitle = titleObj or mw.title.getCurrentTitle()
	if currentTitle.namespace == 0 then
		-- We are in mainspace.
		if not isEmpty then
			cats[#cats + 1] = 'Articles with hAudio microformats'
		end
		if hasMissing then
			cats[#cats + 1] = 'Articles with empty listen template'
		end
	end
	if isPlain then
		cats[#cats + 1] = 'Listen template using plain parameter'
	end
	for i, cat in ipairs(cats) do
		cats[i] = string.format('[[Category:%s]]', cat)
	end
	return table.concat(cats)
end

function p._main(args)
	-- Organise the arguments by number.
	local isPlain = args.plain == 'yes'
	local isEmbedded = args.embed and true
	local hasImage = not isPlain and not isEmbedded and args.image ~= 'none'

	local numArgs, missingFiles = {}, {}
	do
		local origNumArgs = mTableTools.numData(args)
		origNumArgs[1] = origNumArgs.other -- Overwrite args.filename1 etc. with args.filename etc.
		origNumArgs = mTableTools.compressSparseArray(origNumArgs)
		for i, t in ipairs(origNumArgs) do
			-- Check if the files exist.
			local obj = t.filename and mw.title.makeTitle(-2, t.filename)
			if obj and obj.exists then
				if t.length == 'yes' or
					-- Show length if the video height would be less than 150px
					obj.file.width / obj.file.height > (hasImage and 1.547 or 1.434)
				then
					t.length = obj.file.length
				else
					t.length = nil
				end
				numArgs[#numArgs + 1] = t
			else
				missingFiles[#missingFiles + 1] = t.filename or i
			end
		end
	end

	-- Render warning
	local hasMissing = #missingFiles ~= 0
	local previewWarning = ''
	if hasMissing then
		for i, v in ipairs(missingFiles) do
			missingFiles[i] = type(v) == 'string'
				and string.format('missing file "%s"', v)
				or string.format('empty filename #%s', v)
		end
		previewWarning = string.format(
			'Page using [[Template:Listen]] with %s',
			mw.text.listToText(missingFiles)
		)
		previewWarning = require('Module:If preview')._warning({previewWarning})
	end

	-- Exit early if none exist.
	if #numArgs == 0 then
		return previewWarning .. renderTrackingCategories(isPlain, hasMissing, true)
	end

	-- Build the arguments for {{side box}}
	local sbArgs = {
		metadata = 'no',
		position = (isPlain or isEmbedded) and 'left' or args.pos,
		style = args.style,
		templatestyles = 'Module:Listen/styles.css'
	}

	-- Class arguments
	do
		local class = {
			'listen',
			'noprint'
		}
		if isPlain then
			table.insert(class, 'listen-plain')
		end
		if isEmbedded then
			table.insert(class, 'listen-embedded')
		end
		if not hasImage then
			table.insert(class, 'listen-noimage')
		end
		if args.pos == 'left' and not isPlain and not isEmbedded then
			table.insert(class, 'listen-left')
		elseif args.pos == 'center' then
			table.insert(class, 'listen-center')
		end

		sbArgs.class = table.concat(class, ' ')
	end

	-- Image
	if not isPlain and not isEmbedded then
		if args.image then
			sbArgs.image = args.image
		else
			local images = {
				speech = 'Audio-input-microphone.svg',
				music = 'Gnome-mime-audio-openclipart.svg',
				default = 'Gnome-mime-sound-openclipart.svg'
			}
			sbArgs.image = mFileLink._main{
				file = args.type and images[args.type] or images.default,
				size = '65x50px',
				location = 'center',
				link = '',
				alt = ''
			}
		end
	end

	-- Text
	do
		local header
		if args.header then
			header = mw.html.create('div')
			header:addClass('listen-header')
				:wikitext(args.header)
			header = tostring(header) .. '\n'
		else
			header = ''
		end
		local text = {}
		for i, t in ipairs(numArgs) do
			text[#text + 1] = renderRow(
				t.filename, t.title, t.play, t.alt, t.description, t.start,
				t.length, hasImage
			)
			if numArgs[i + 1] then
				text[#text + 1] = '<hr/>'
			end
		end
		sbArgs.text = header .. table.concat(text)
	end

	-- Below
	if not isPlain and not isEmbedded and args.help ~= 'no' then
		sbArgs.below = string.format(
			'<hr/><i class="selfreference">Problems playing %s? See [[Help:Media|media help]].</i>',
			#numArgs == 1 and 'this file' or 'these files'
		)
	end

	-- Render the side box.
	local sideBox = mSideBox._main(sbArgs)

	-- Render the tracking categories.
	local trackingCategories = renderTrackingCategories(isPlain, hasMissing)

	return previewWarning .. sideBox .. trackingCategories
end

function p.main(frame)
	local origArgs = frame:getParent().args
	local args = {}
	for k, v in pairs(origArgs) do
		if v ~= '' then
			args[k] = v
		end
	end
	return p._main(args)
end

return p