Mô đun:Track listing
Tài liệu mô đun[tạo]
-- Mô đun này thực hiện [[Bản mẫu:Track listing]]
local yesno = require('Mô đun:Yesno')
local checkType = require('libraryUtil').checkType
local SHOW_WARNINGS = false
local INPUT_ERROR_CATEGORY = 'Danh sách ca khúc có các lỗi đầu vào'
local COLLAPSED_PARAMETER_CATEGORY = 'Danh sách ca khúc sử dụng tham số collapsed'
--------------------------------------------------------------------------------
-- Helper functions
--------------------------------------------------------------------------------
-- Add a mixin to a class.
local function addMixin(class, mixin)
for k, v in pairs(mixin) do
if k ~= 'init' then
class[k] = v
end
end
end
--------------------------------------------------------------------------------
-- Validation mixin
--------------------------------------------------------------------------------
local Validation = {}
function Validation.init(self)
self.warnings = {}
self.categories = {}
end
function Validation:addWarning(msg, category)
table.insert(self.warnings, msg)
table.insert(self.categories, category)
end
function Validation:addCategory(category)
table.insert(self.categories, category)
end
function Validation:getWarnings()
return self.warnings
end
function Validation:getCategories()
return self.categories
end
-- Validate a track length. If a track length is invalid, a warning is added.
-- A type error is raised if the length is not of type string or nil.
function Validation:validateLength(length)
checkType('validateLength', 1, length, 'string', true)
if length == nil then
-- Do nothing if no length specified
return nil
end
local hours, minutes, seconds
-- Try to match times like "1:23:45".
hours, minutes, seconds = length:match('^(%d+):(%d%d):(%d%d)$')
if hours and hours:sub(1, 1) == '0' then
-- Disallow times like "0:12:34"
self:addWarning(string.format(
"Thời gian không hợp lệ '%s' (thời gian dùng định dạng 'h:mm:ss' không thể bắt đầu bằng 0)",
mw.text.nowiki(length)
), INPUT_ERROR_CATEGORY)
return nil
end
if not seconds then
-- The previous attempt didn't match. Try to match times like "1:23".
minutes, seconds = length:match('^(%d?%d):(%d%d)$')
if minutes and minutes:find('^0%d$') then
-- Special case to disallow lengths like "01:23". This check has to
-- be here so that lengths like "1:01:23" are still allowed.
self:addWarning(string.format(
"Thời gian không hợp lệ '%s' (thời gian dùng định dạng 'mm:ss' không thể bắt đầu bằng 0)",
mw.text.nowiki(length)
), INPUT_ERROR_CATEGORY)
return nil
end
end
-- Add a warning and return if we did not find a match.
if not seconds then
self:addWarning(string.format(
"Thời gian không hợp lệ '%s' (thời gian phải dùng định dạng 'm:ss', 'mm:ss' hoặc 'h:mm:ss')",
mw.text.nowiki(length)
), INPUT_ERROR_CATEGORY)
return nil
end
-- Check that the minutes are less than 60 if we have an hours field.
if hours and tonumber(minutes) >= 60 then
self:addWarning(string.format(
"Thời lượng ca khúc không hợp lệ '%s' (nếu giờ được chỉ định, số phút phải nhỏ hơn 60)",
mw.text.nowiki(length)
), INPUT_ERROR_CATEGORY)
return nil
end
-- Check that the seconds are less than 60
if tonumber(seconds) >= 60 then
self:addWarning(string.format(
"Thời lượng ca khúc không hợp lệ '%s' (số giây phải nhỏ hơn 60)",
mw.text.nowiki(length)
), INPUT_ERROR_CATEGORY)
end
return nil
end
--------------------------------------------------------------------------------
-- Track class
--------------------------------------------------------------------------------
local Track = {}
Track.__index = Track
addMixin(Track, Validation)
Track.fields = {
number = true,
title = true,
note = true,
length = true,
lyrics = true,
music = true,
writer = true,
extra = true,
}
Track.cellMethods = {
number = 'makeNumberCell',
title = 'makeTitleCell',
writer = 'makeWriterCell',
lyrics = 'makeLyricsCell',
music = 'makeMusicCell',
extra = 'makeExtraCell',
length = 'makeLengthCell',
}
function Track.new(data)
local self = setmetatable({}, Track)
Validation.init(self)
for field in pairs(Track.fields) do
self[field] = data[field]
end
self.number = assert(tonumber(self.number))
self:validateLength(self.length)
return self
end
function Track:getLyricsCredit()
return self.lyrics
end
function Track:getMusicCredit()
return self.music
end
function Track:getWriterCredit()
return self.writer
end
function Track:getExtraField()
return self.extra
end
-- Note: called with single dot syntax
function Track.makeSimpleCell(wikitext)
return mw.html.create('td')
:css('vertical-align', 'top')
:wikitext(wikitext or ' ')
end
function Track:makeNumberCell()
return mw.html.create('th')
:attr('id', 'track' .. self.number)
:attr('scope', 'row')
:css('padding-right', '10px')
:css('text-align', 'right')
:css('vertical-align', 'top')
:css('font-weight', 'normal')
:wikitext(self.number .. '.')
end
function Track:makeTitleCell()
local titleCell = mw.html.create('td')
titleCell
:css('vertical-align', 'top')
:wikitext(self.title and string.format('"%s"', self.title) or 'Chưa có tiêu đề')
if self.note then
titleCell
:wikitext(' ')
:tag('span')
:wikitext(string.format('(%s)', self.note))
end
return titleCell
end
function Track:makeWriterCell()
return Track.makeSimpleCell(self.writer)
end
function Track:makeLyricsCell()
return Track.makeSimpleCell(self.lyrics)
end
function Track:makeMusicCell()
return Track.makeSimpleCell(self.music)
end
function Track:makeExtraCell()
return Track.makeSimpleCell(self.extra)
end
function Track:makeLengthCell()
return mw.html.create('td')
:css('padding-right', '10px')
:css('text-align', 'right')
:css('vertical-align', 'top')
:wikitext(self.length or ' ')
end
function Track:exportRow(options)
options = options or {}
local columns = options.columns or {}
local row = mw.html.create('tr')
row:css('background-color', options.color or '#fff')
for i, column in ipairs(columns) do
local method = Track.cellMethods[column]
if method then
row:node(self[method](self))
end
end
return row
end
--------------------------------------------------------------------------------
-- TrackListing class
--------------------------------------------------------------------------------
local TrackListing = {}
TrackListing.__index = TrackListing
addMixin(TrackListing, Validation)
TrackListing.fields = {
all_writing = true,
all_lyrics = true,
all_music = true,
headline = true,
extra_column = true,
total_length = true,
title_width = true,
writing_width = true,
lyrics_width = true,
music_width = true,
extra_width = true,
category = true,
}
TrackListing.deprecatedFields = {
writing_credits = true,
lyrics_credits = true,
music_credits = true,
}
function TrackListing.new(data)
local self = setmetatable({}, TrackListing)
Validation.init(self)
-- Check for deprecated arguments
for deprecatedField in pairs(TrackListing.deprecatedFields) do
if data[deprecatedField] then
self:addCategory('Danh sách ca khúc có các tham số không dùng nữa')
break
end
end
-- Validate total length
if data.total_length then
self:validateLength(data.total_length)
end
-- Add properties
for field in pairs(TrackListing.fields) do
self[field] = data[field]
end
-- Evaluate boolean properties
self.showCategories = yesno(self.category) ~= false
self.category = nil
-- Make track objects
self.tracks = {}
for i, trackData in ipairs(data.tracks or {}) do
table.insert(self.tracks, Track.new(trackData))
end
-- Find which of the optional columns we have.
-- We could just check every column for every track object, but that would
-- be no fun^H^H^H^H^H^H inefficient, so we use four different strategies
-- to try and check only as many columns and track objects as necessary.
do
local optionalColumns = {}
local columnMethods = {
lyrics = 'getLyricsCredit',
music = 'getMusicCredit',
writer = 'getWriterCredit',
extra = 'getExtraField',
}
local doneWriterCheck = false
for i, trackObj in ipairs(self.tracks) do
for column, method in pairs(columnMethods) do
if trackObj[method](trackObj) then
optionalColumns[column] = true
columnMethods[column] = nil
end
end
if not doneWriterCheck and optionalColumns.writer then
doneWriterCheck = true
optionalColumns.lyrics = nil
optionalColumns.music = nil
columnMethods.lyrics = nil
columnMethods.music = nil
end
if not next(columnMethods) then
break
end
end
self.optionalColumns = optionalColumns
end
return self
end
function TrackListing:makeIntro()
if self.all_writing then
return string.format(
'Tất cả các ca khúc được viết bởi %s.',
self.all_writing
)
elseif self.all_lyrics and self.all_music then
return string.format(
'Tất cả lời bài hát được viết bởi %s; tất cả nhạc phẩm được soạn bởi %s.',
self.all_lyrics,
self.all_music
)
elseif self.all_lyrics then
return string.format(
'Tất cả lời bài hát được viết bởi %s.',
self.all_lyrics
)
elseif self.all_music then
return string.format(
'Tất cả nhạc phẩm được soạn bởi %s.',
self.all_music
)
else
return ''
end
end
function TrackListing:renderTrackingCategories()
if not self.showCategories or mw.title.getCurrentTitle().namespace ~= 0 then
return ''
end
local ret = ''
local function addCategory(cat)
ret = ret .. string.format('[[Thể loại:%s]]', cat)
end
for i, category in ipairs(self:getCategories()) do
addCategory(category)
end
for i, track in ipairs(self.tracks) do
for j, category in ipairs(track:getCategories()) do
addCategory(category)
end
end
return ret
end
function TrackListing:renderWarnings()
if not SHOW_WARNINGS then
return ''
end
local ret = {}
local function addWarning(msg)
table.insert(ret, string.format(
'<strong class="error">Lỗi danh sách ca khúc: %s</strong>',
msg
))
end
for i, warning in ipairs(self:getWarnings()) do
addWarning(warning)
end
for i, track in ipairs(self.tracks) do
for j, warning in ipairs(track:getWarnings()) do
addWarning(warning)
end
end
return table.concat(ret, '<br>')
end
function TrackListing:__tostring()
-- Find columns to output
local columns = {'number', 'title'}
if self.optionalColumns.writer then
columns[#columns + 1] = 'writer'
else
if self.optionalColumns.lyrics then
columns[#columns + 1] = 'lyrics'
end
if self.optionalColumns.music then
columns[#columns + 1] = 'music'
end
end
if self.optionalColumns.extra then
columns[#columns + 1] = 'extra'
end
columns[#columns + 1] = 'length'
-- Find colspan and column width
local nColumns = #columns
local nOptionalColumns = nColumns - 3
local titleColumnWidth
if nColumns >= 5 then
titleColumnWidth = 40
elseif nColumns >= 4 then
titleColumnWidth = 60
else
titleColumnWidth = 100
end
local optionalColumnWidth = (100 - titleColumnWidth) / nOptionalColumns
titleColumnWidth = titleColumnWidth .. '%'
optionalColumnWidth = optionalColumnWidth .. '%'
-- Root of the output
local root = mw.html.create()
-- Intro
root:node(self:makeIntro())
-- Start of track listing table
local tableRoot = root:tag('table')
tableRoot
:addClass('tracklist')
:css('display', 'block')
:css('border-spacing', '0px')
-- Header row
if self.headline then
tableRoot:tag('tr'):tag('th')
:addClass('tlheader mbox-text')
:attr('colspan', nColumns)
:css('text-align', 'left')
:css('background-color', '#fff')
:css('font-weight', '700')
:wikitext(self.headline or 'Danh sách bài hát')
end
-- Deprecated collapsed parameter
if self.collapsed then
self:addWarning("Tham số collapsed đã sử dụng bị loại bỏ", COLLAPSED_PARAMETER_CATEGORY);
end
-- Headers
local headerRow = tableRoot:tag('tr')
---- Track number
headerRow
:tag('th')
:addClass('tlheader')
:attr('scope', 'col')
:css('width', '2em')
:css('padding-left', '10px')
:css('padding-right', '10px')
:css('text-align', 'right')
:css('background-color', '#eee')
:tag('abbr')
:attr('title', 'Số thứ tự')
:wikitext('STT')
---- Title
headerRow:tag('th')
:addClass('tlheader')
:attr('scope', 'col')
:css('width', self.title_width or titleColumnWidth)
:css('text-align', 'left')
:css('background-color', '#eee')
:wikitext('Nhan đề')
---- Optional headers: writer, lyrics, music, and extra
local function addOptionalHeader(field, headerText, width)
if self.optionalColumns[field] then
headerRow:tag('th')
:addClass('tlheader')
:attr('scope', 'col')
:css('width', width or optionalColumnWidth)
:css('text-align', 'left')
:css('background-color', '#eee')
:wikitext(headerText)
end
end
addOptionalHeader('writer', 'Sáng tác', self.writing_width)
addOptionalHeader('lyrics', 'Phổ lời', self.lyrics_width)
addOptionalHeader('music', 'Phổ nhạc', self.music_width)
addOptionalHeader(
'extra',
self.extra_column or '{{{extra_column}}}',
self.extra_width
)
---- Track length
headerRow:tag('th')
:addClass('tlheader')
:attr('scope', 'col')
:css('width', '8em')
:css('padding-right', '10px')
:css('white-space', 'nowrap')
:css('text-align', 'right')
:css('background-color', '#eee')
:wikitext('Thời lượng')
-- Tracks
for i, track in ipairs(self.tracks) do
tableRoot:node(track:exportRow({
columns = columns,
color = i % 2 == 0 and '#f7f7f7' or '#fff'
}))
end
-- Total length
if self.total_length then
tableRoot
:tag('tr')
:tag('th')
:attr('colspan', nColumns - 1)
:css('padding', 0)
:tag('span')
:css('width', '9em')
:css('float', 'right')
:css('padding-left', '10px')
:css('background-color', '#eee')
:css('margin-right', '2px')
:wikitext("Tổng thời lượng:")
:done()
:done()
:tag('td')
:css('padding', '0 10px 0 0')
:css('text-align', 'right')
:css('background-color', '#eee')
:wikitext(string.format("'''%s'''", self.total_length))
end
-- Warnings and tracking categories
root:wikitext(self:renderWarnings())
root:wikitext(self:renderTrackingCategories())
return tostring(root)
end
--------------------------------------------------------------------------------
-- Exports
--------------------------------------------------------------------------------
local p = {}
function p._main(args)
-- Process numerical args so that we can iterate through them.
local data, tracks = {}, {}
for k, v in pairs(args) do
if type(k) == 'string' then
local prefix, num = k:match('^(%D.-)(%d+)$')
if prefix and Track.fields[prefix] and (num == '0' or num:sub(1, 1) ~= '0') then
-- Allow numbers like 0, 1, 2 ..., but not 00, 01, 02...,
-- 000, 001, 002... etc.
num = tonumber(num)
tracks[num] = tracks[num] or {}
tracks[num][prefix] = v
else
data[k] = v
end
end
end
data.tracks = (function (t)
-- Compress sparse array
local ret = {}
for num, trackData in pairs(t) do
trackData.number = num
table.insert(ret, trackData)
end
table.sort(ret, function (t1, t2)
return t1.number < t2.number
end)
return ret
end)(tracks)
return tostring(TrackListing.new(data))
end
function p.main(frame)
local args = require('Mô đun:Arguments').getArgs(frame, {wrappers = 'Bản mẫu:Danh sách ca khúc'})
return p._main(args)
end
return p
Wiki - Keonhacai copa chuyên cung cấp kiến thức thể thao, keonhacai tỷ lệ kèo, bóng đá, khoa học, kiến thức hằng ngày được chúng tôi cập nhật mỗi ngày mà bạn có thể tìm kiếm tại đây có nguồn bài viết: https://vi.wikipedia.org/wiki/M%C3%B4_%C4%91un:Track_listing