This is an old revision of this page, as edited by Mr. Stradivarius (talk | contribs) at 06:19, 17 December 2016 (Protected "Module:Track listing": High-risk Lua module ( (indefinite) (indefinite))). The present address (URL) is a permanent link to this revision, which may differ significantly from the current revision.
Revision as of 06:19, 17 December 2016 by Mr. Stradivarius (talk | contribs) (Protected "Module:Track listing": High-risk Lua module ( (indefinite) (indefinite)))(diff) ← Previous revision | Latest revision (diff) | Newer revision → (diff) Module documentation[view] [edit] [history] [purge]This Lua module is used on approximately 114,000 pages. To avoid major disruption and server load, any changes should be tested in the module's /sandbox or /testcases subpages, or in your own module sandbox. The tested changes can be added to this page in a single edit. Consider discussing changes on the talk page before implementing them. |
This module uses TemplateStyles: |
This module depends on the following other modules: |
This module is used by one or more bots.
If you intend to make significant changes to this module, move it, or nominate it for deletion, please notify the bot operator(s) in advance. The relevant bots are: User:cewbot/log/20201008/configuration. |
This module implements {{track listing}}. Please see the template page for documentation.
The above documentation is transcluded from Module:Track listing/doc. (edit | history)Editors can experiment in this module's sandbox (edit | diff) and testcases (create) pages.
Subpages of this module.
-- This module implements ] local yesno = require('Module:Yesno') -------------------------------------------------------------------------------- -- Track class -------------------------------------------------------------------------------- local Track = {} Track.__index = Track 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) for field in pairs(Track.fields) do self = data end self.number = assert(tonumber(self.number)) 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('td') :css('padding-right', '10px') :css('text-align', 'right') :css('vertical-align', 'top') :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 'Untitled') :wikitext(' ') if self.note then titleCell:tag('span') :css('font-size', '85%') :wikitext(string.format('(%s)', self.note)) else titleCell:wikitext(' ') 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 if method then row:node(self(self)) end end return row end -------------------------------------------------------------------------------- -- TrackListing class -------------------------------------------------------------------------------- local TrackListing = {} TrackListing.__index = TrackListing TrackListing.fields = { all_writing = true, all_lyrics = true, all_music = true, collapsed = 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) -- Add properties for field in pairs(TrackListing.fields) do self = data end -- Check for deprecated arguments for deprecatedField in pairs(TrackListing.deprecatedFields) do if data then self.hasDeprecatedArgs = true break end end -- Evaluate boolean properties self.collapsed = yesno(self.collapsed, false) 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(trackObj) then optionalColumns = true columnMethods = 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( 'All tracks written by %s. ', self.all_writing ) elseif self.all_lyrics and self.all_music then return string.format( 'All lyrics written by %s; all music composed by %s. ', self.all_lyrics, self.all_music ) elseif self.all_lyrics then return string.format( 'All lyrics written by %s. ', self.all_lyrics ) elseif self.all_music then return string.format( 'All music composed by %s. ', self.all_music ) else return '' end end function TrackListing:renderTrackingCategories() local ret = '' if self.showCategories and self.hasDeprecatedArgs and mw.title.getCurrentTitle().namespace == 0 then ret = ret .. ']' end return ret end function TrackListing:__tostring() -- Find columns to output local columns = {'number', 'title'} if self.optionalColumns.writer then columns = 'writer' else if self.optionalColumns.lyrics then columns = 'lyrics' end if self.optionalColumns.music then columns = 'music' end end if self.optionalColumns.extra then columns = 'extra' end columns = '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') :addClass(self.collapsed and 'collapsible collapsed' or nil) :css('display', 'block') :css('border-spacing', '0px') :css('border-collapse', 'collapse') :css('border', self.collapsed and '#aaa 1px solid' or nil) :css('padding', self.collapsed and '3px' or '4px') -- Headline if self.headline then tableRoot:tag('tr'):tag('th') :addClass('tlheader mbox-text') :attr('colspan', nColumns) :css('text-align', 'left') :css('background-color', '#fff') :wikitext(self.headline) end -- Header row for collapsed track listings if self.collapsed then tableRoot:tag('tr'):tag('th') :addClass('tlheader mbox-text') :attr('colspan', nColumns) :css('text-align', 'left') :css('background-color', '#fff') :wikitext('Track listing') 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', 'Number') :wikitext('No.') ---- 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('Title') ---- Optional headers: writer, lyrics, music, and extra local function addOptionalHeader(field, headerText, width) if self.optionalColumns 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', 'Writer(s)', self.writing_width) addOptionalHeader('lyrics', 'Lyrics', self.lyrics_width) addOptionalHeader('music', 'Music', 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', '4em') :css('padding-right', '10px') :css('text-align', 'right') :css('background-color', '#eee') :wikitext('Length') -- 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('td') :attr('colspan', nColumns - 1) :css('padding', 0) :tag('span') :css('width', '7.5em') :css('float', 'right') :css('padding-left', '10px') :css('background-color', '#eee') :css('margin-right', '2px') :wikitext("'''Total length:'''") :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 -- Tracking categories 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 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 = tracks or {} tracks = v else data = 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('Module:Arguments').getArgs(frame, { wrappers = 'Template:Track listing' }) return p._main(args) end return pCategory: