Модуль:Series overview/пісочниця
![]()
ВикористанняВикористовується у Шаблон:Огляд серіалу: {{#invoke:Series overview|main}} Параметри описані у документації шаблону Категорія стеження
-- Цей модуль втілює {{Огляд серіалу}}.
require('strict')
local yesno = require('Module:Yesno')
local HTMLcolor = mw.loadData( 'Module:Color contrast/colors' )
--------------------------------------------------------------------------------
-- SeriesOverview class
-- The main class.
--------------------------------------------------------------------------------
local SeriesOverview = {}
function SeriesOverview.cellspan(SeasonEntries, SeasonEntries_ordered, key, cell, multipart, setspan)
if setspan ~= nil then return setspan end
local spanlength = 1
local firstEntry = SeasonEntries[SeasonEntries_ordered[cell]]
if key == 'network' and firstEntry.networkA and not firstEntry.networkB then spanlength = 2 end
for i = cell+1, #SeasonEntries_ordered do
local entry = SeasonEntries[SeasonEntries_ordered[i]]
-- Split season, then regular season
if entry.startA or entry.releasedA then
if not entry[key..'A'] then spanlength = spanlength + 1
else break end
if not entry[key..'B'] then spanlength = spanlength + 1
else break end
else
if not entry[key] and (key == 'network' or ((string.sub(key,0,7) == 'postaux' or string.sub(key,0,3) == 'aux') and (not entry.special or entry.episodes)) or (string.sub(key,0,4) == 'info') and multipart) then
spanlength = spanlength + 1
else break end
end
end
return spanlength
end
-- Sorting function
function SeriesOverview.series_sort(op1, op2)
local n1,s1 = string.match(op1,"(%d+)(%a*)")
local n2,s2 = string.match(op2,"(%d+)(%a*)")
local n1N,n2N = tonumber(n1),tonumber(n2)
if n1N == n2N then
return s1 < s2
else
return n1N < n2N
end
end
-- Function to add either text or {{N/a}} to cell
function SeriesOverview.season_cell(text, frame)
local cell
if string.find(text or '', 'table-na', 0, true) ~= nil then
local findpipe = string.find(text, ' | ', 0, true)
if findpipe ~= nil then
cell = SeriesOverview.series_attributes( frame:expandTemplate{title='N/A',args={string.sub(text,findpipe+3)}} )
else
cell = SeriesOverview.series_attributes( frame:expandTemplate{title='N/A'} )
end
else
cell = mw.html.create('td'):wikitext(text)
end
return cell
end
-- Allow usages of {{N/A}} cells
function SeriesOverview.series_attributes(infoParam)
local entries = {}
local infoCell = mw.html.create('td')
local attrMatch = '([%a-]*)="([^"]*)"'
while true do
local a,b = string.match(infoParam,attrMatch)
if a == nil or b == nil then break end
infoCell:attr(a,b)
infoParam = string.gsub(infoParam,attrMatch,'',1)
end
infoParam = string.gsub(infoParam,'%s*|%s*','',1)
infoCell:wikitext(infoParam)
return infoCell
end
function SeriesOverview.endtable()
return "</table></div>"
end
function SeriesOverview.new(frame, args)
args = args or {}
local initialArticle = args['1'] or ''
local categories = ''
local title = mw.title.getCurrentTitle()
local oldDateSupport = true
-- Create series overview table
local root = mw.html.create((args.multiseries or not args.series) and 'table' or '')
local cellPadding = '0 8px'
local basePadding = '0.2em 0.4em'
root
:addClass('wikitable')
:addClass('plainrowheaders')
:css('text-align', 'center')
:css('height', '1px')
:css('display', 'table')
-- Sortable
if args.sortable then
root:addClass('sortable');
end
-- "Епізоди" ID
if args.id then
root:attr('id', 'Епізоди')
end
-- Width
if args.width then
root:css('width', args.width)
end
-- Caption
if args.caption then
root:tag('caption'):wikitext(frame:expandTemplate{title='Screen reader-only',args={args.caption}})
end
-- Extract seasons info and place into a 3D array
local SeasonEntries = {}
for k,v in pairs(args) do
local str, num, str2 = string.match(k, '([^%d]*)(%d*)(%a*)')
if tonumber(k) ~= 1 and num ~= '' then
-- Special
local special = false
if string.sub(str2,1,1) == 'S' then
special = true
num = num .. str2
str2 = ''
end
-- Add to entries, create if necessary
if not SeasonEntries[num] then
SeasonEntries[num] = {}
end
SeasonEntries[num][str .. str2] = v
if special then
SeasonEntries[num]['special'] = 'y'
end
end
end
-- Order table by season number
local SeasonEntries_ordered = {}
for k in pairs(SeasonEntries) do
table.insert(SeasonEntries_ordered, k)
end
table.sort(SeasonEntries_ordered,SeriesOverview.series_sort)
local firstRow = args.multiseries and {} or SeasonEntries[SeasonEntries_ordered[1]]
-- Colspan calculation for information cells (0 = no info set)
local numAuxCells = 0
local numInfoCells = 0
for i = string.byte('A'), string.byte('Z') do
local param = 'info' .. string.char(i)
if args[param] then numInfoCells = numInfoCells + 1 end
end
-- Use of colors and network
local noColors = true
local setNetwork = false
local allReleased = true
local anyReleased = false
local endEqualsStartFormat = false
local noEndDates = true
if (args.multiseries and args.network) then setNetwork = true end
if (args.multiseries) then allReleased = false end
for i = 1, #SeasonEntries_ordered do
local season, entry = SeasonEntries_ordered[i], SeasonEntries[SeasonEntries_ordered[i]]
for j0 = string.byte('A')-1, string.byte('Z') do
local j = string.char(j0)
if j0 == string.byte('A')-1 then j = '' end
if entry['color' .. j] then noColors = false end
if entry['network' .. j] then setNetwork = true end
if entry['start' .. j] then allReleased = false end
if entry['released' .. j] and not entry['special'] then anyReleased = true end
if entry['end' .. j] == 'start' then
endEqualsStartFormat = true
noEndDates = false
end
if entry['end' .. j] then noEndDates = false end
end
end
if oldDateSupport and args.allreleased then
allReleased = yesno(args.allreleased)
end
-- if args.released then
if oldDateSupport and args.released then
anyReleased = yesno(args.released)
end
if title.namespace == 0 and endEqualsStartFormat then
categories = categories .. '[[Категорія:Статті, що використовують Шаблон:Огляд серіалу із застарілим форматом параметра end]]'
end
if title.namespace == 0 and not args.multiseries and not allReleased and noEndDates then
categories = categories .. '[[Категорія:Статті, що використовують Шаблон:Огляд серіалу із застарілим форматом параметра start]]'
end
-- Top info cell
-- @ = string.char(64), A = string.char(65)
local topInfoCell = numInfoCells > 0 and string.char(numInfoCells + (string.byte('A') - 1)) or '@'
-- Networks are included if the very first entry sets the first network
local networkTransclude = args.network_transclude
if (networkTransclude == 'onlyinclude' and title.fullText == initialArticle) or (networkTransclude == 'noinclude' and title.fullText ~= initialArticle) then
setNetwork = false
end
-- Headers
do
if args.multiseries or not args.series then
local headerRow = root:tag('tr')
headerRow
:css('text-align', 'center')
local releasedBlurb = anyReleased and 'випуск' or 'показ'
-- Base series/season content on the format of the first date; Series = D M Y, Season = M D, Y
local matchDMY = false
local thisStart = firstRow.start or firstRow.startA or firstRow.released or firstRow.releasedA
if thisStart then
if string.match(thisStart:gsub(" "," "), '(%d+)%s(%a+)%s(%d+)') then
matchDMY = true
end
end
-- Multiple series header
if args.multiseries then
headerRow:tag('th')
:attr('scope', 'col')
:css('padding', cellPadding)
:attr('rowspan', allReleased and 1 or 2)
:wikitext('Серіал')
end
-- Season header
headerRow:tag('th')
:attr('scope', 'col')
:attr('rowspan', allReleased and 1 or 2)
:css('min-width', '50px')
:css('padding', cellPadding)
:wikitext(args.seriesT or args.seasonT or --[[(matchDMY and 'Серія') or]] 'Сезон')
for _a = 1, 3 do
if _a == 1 or _a == 3 then
-- Aux headers
local auxtype = (_a == 3 and 'post' or '') .. 'aux'
for i = string.byte('A'), string.byte('Z') do
local param = auxtype .. string.char(i)
if args[param] then
numAuxCells = numAuxCells + 1
headerRow:tag('th')
:attr('scope', 'col')
:css('padding', cellPadding)
:attr('rowspan', allReleased and 1 or 2)
:wikitext(args[param])
end
end
end
if _a == 2 then
-- Episodes header
headerRow:tag('th')
:attr('scope', 'col')
:attr('rowspan', allReleased and 1 or 2)
:attr('colspan', 2)
:css('padding', cellPadding)
:wikitext(args.episodesT or 'Епізоди')
end
end
-- Originally aired header
local OriginallyColspan = (not allReleased and setNetwork) and 3 or 2
local countryBlurb = ''
if args.country then
countryBlurb = ' (' .. args.country .. ')'
end
headerRow:tag('th')
:attr('scope', (setNetwork and allReleased) and 'col' or 'colgroup')
:attr('colspan', OriginallyColspan)
:wikitext('Оригінальний ' .. releasedBlurb .. countryBlurb)
-- Network subheader for released series
if setNetwork and allReleased then
headerRow:tag('th')
:attr('scope', 'col')
:attr('rowspan', allReleased and 1 or 2)
:css('padding', cellPadding)
:wikitext('Мережа')
end
-- Information headers
if topInfoCell ~= '@' then
for i = string.byte('A'), string.byte(topInfoCell) do
local param = 'info' .. string.char(i)
local infoTransclude = args[param .. '_transclude']
if (infoTransclude == 'onlyinclude' and title.fullText == initialArticle) or (infoTransclude == 'noinclude' and title.fullText ~= initialArticle) then else
headerRow:tag('th')
:attr('scope', 'col')
:attr('rowspan', allReleased and 1 or 2)
:css('padding', cellPadding)
:wikitext(args[param])
end
end
end
-- Subheader row
local subheaderRow = mw.html.create('tr')
if not allReleased then
-- First aired subheader
subheaderRow:tag('th')
:attr('scope', 'col')
:wikitext('Останній ' .. releasedBlurb)
-- Last aired subheader
subheaderRow:tag('th')
:attr('scope', 'col')
:wikitext('Перший ' .. releasedBlurb)
-- Network subheader for aired series
if setNetwork then
subheaderRow:tag('th')
:attr('scope', 'col')
:css('padding', cellPadding)
:wikitext('Мережа')
end
end
-- Check for scenarios with an empty subheaderRow
if not allReleased or numInfoCells > 0 then
root:node(subheaderRow)
end
end
end
-- Season rows
do
if args.multiseries then
-- Multi series individual entries
if args.multiseries ~= "y" then
root:node(args.multiseries)
end
else
-- One row entries, only categorized in the mainspace
if title.namespace == 0 and #SeasonEntries == 1 then
categories = categories .. '[[Категорії:Статті, що використовують Шаблон:Огляд серіалу з лише одним рядком]]'
end
-- Determine number of rows in the whole overview
local SeasonEntriesRows = 0
for X = 1, #SeasonEntries_ordered do
local season, entry = SeasonEntries_ordered[X], SeasonEntries[SeasonEntries_ordered[X]]
local splits = 0
for i = string.byte('A'), string.byte('Z') do
local paramS = 'start' .. string.char(i)
local paramR = 'released' .. string.char(i)
if entry[paramS] or entry[paramR] then splits = splits + 1 end
end
if splits == 0 then splits = 1 end
SeasonEntriesRows = SeasonEntriesRows + splits
end
for X = 1, #SeasonEntries_ordered do
local season, entry = SeasonEntries_ordered[X], SeasonEntries[SeasonEntries_ordered[X]]
-- Determine number of splits in a season
local splits = 0
for i = string.byte('A'), string.byte('Z') do
local paramS = 'start' .. string.char(i)
local paramR = 'released' .. string.char(i)
if entry[paramS] or entry[paramR] then splits = splits + 1 end
end
local splitSeason = (splits > 1)
-- Season rows for each season
for k0 = string.byte('A')-1, string.byte('Z') do
local k = string.char(k0)
if k0 == string.byte('A')-1 then k = '' end
-- Part header
if entry.part and k == '' then
root:node(entry.part)
end
-- New season row
local seasonRow = (entry['start' .. k] or entry['released' .. k]) and root:tag('tr') or mw.html.create('tr')
seasonRow:css('height', '100%')
local borderBottom = '2px solid #8D939A'
-- Series name for group overviews
if X == 1 and (k == '' or k == 'A') and args.series then
seasonRow:tag('th')
:attr('scope', 'row')
:attr('rowspan', SeasonEntriesRows)
:wikitext(args.series)
:css('border-bottom', borderBottom)
end
if X == #SeasonEntries_ordered and args.series then
seasonRow:css('border-bottom', borderBottom)
end
-- Season number link, included only in the first row
local cellColor
if not noColors then
if entry['color' .. k] ~= nil and HTMLcolor[entry['color' .. k]] == nil then
entry['color' .. k] = '#'..(mw.ustring.match(entry['color' .. k], '^[%s#]*([a-fA-F0-9]*)[%s]*$') or '')
end
if splitSeason then
if entry.color then
cellColor = entry.color
else
cellColor = "linear-gradient(to bottom"
for i = 0, splits-1 do
local _color = 'color' .. string.upper(string.char(i+97))
cellColor = cellColor .. ", " .. (entry[_color] or 'rgba(0,0,0,0)') .. " " .. (100/splits *i) .. "%"
.. ", " .. (entry[_color] or 'rgba(0,0,0,0)') .. " " .. (100/splits *(i+1)) .. "%"
end
cellColor = cellColor .. ")"
end
else
cellColor = entry['color' .. k]
end
end
if k == '' or k == 'A' then
local colorWidth = '14px'
-- Overall table cell
local cellRow = mw.html.create(args.series and 'td' or 'th')
:attr('scope', splitSeason and 'rowgroup' or 'row')
:attr('rowspan', splitSeason and splits or nil)
:attr('colspan', entry.special and not entry.episodes and 3+numAuxCells or 1)
:css('height', 'inherit')
:css('padding', '0')
-- Overall inner span
local spanRow = mw.html.create('span')
spanRow
:css('width: 100%')
:css('text-align', 'center')
:css('float', 'left')
:css('width', '100%')
:css('height', '100%')
-- Coloured nested span
local spanRow2 = mw.html.create('span')
spanRow2
:css('width', colorWidth)
:css('background', cellColor)
:css('color', '#202122')
:css('height', '100%')
:css('float', 'left')
:css('box-shadow', 'inset -1px 0 #A2A9B1')
-- Link nested span
local spanRow3 = mw.html.create('span')
spanRow3
:css('height', '100%')
:css('width', not noColors and 'calc(100% - ' .. colorWidth .. ' - 8px)' or '100%')
:css('display', 'flex')
:css('vertical-align', 'middle')
:css('align-items', 'center')
:css('justify-content', 'center')
:css('padding', not noColors and '0 4px' or '')
local spanRow4 = mw.html.create('span')
spanRow4
:addClass('nowrap')
-- Coloured span first into the overall span
if not noColors then
spanRow:node(spanRow2)
end
-- Link into the blank span
spanRow4:wikitext((entry.link and '[[' .. entry.link .. '|' .. (entry.linkT or season) .. ']]' or (entry.linkT or season)) .. (entry.linkR or ''))
-- Blank span into the Link nested span
spanRow3:node(spanRow4)
-- Link span second into the overall span
spanRow:node(spanRow3)
-- Overall span into the actual cell
cellRow:node(spanRow)
-- The actual cell into the season row
seasonRow:node(cellRow)
end
for _a = 1, 3 do
if _a == 1 or _a == 3 then
-- Aux headers
local auxtype = (_a == 3 and 'post' or '') .. 'aux'
-- Aux cells
for i = string.byte('A'), string.byte('Z') do
local param = auxtype .. string.char(i)
if entry[param .. k] then
local thisCell = SeriesOverview.season_cell(entry[param .. k], frame)
:attr('scope', 'col')
:attr('rowspan', SeriesOverview.cellspan(SeasonEntries, SeasonEntries_ordered, param, X, (args.series and true or false), entry[param .. k .. 'span'] or nil))
:css('padding', cellPadding)
seasonRow:node(thisCell)
end
end
end
if _a == 2 then
-- Episodes counts
if ((splitSeason and k == 'A' and entry.episodes ~= 'hide') or not splitSeason) then
if entry.episodes then
local thisCell = SeriesOverview.season_cell(entry.episodes, frame)
:attr('colspan', (splitSeason and entry.episodesA ~= 'hide') and 1 or 2)
:attr('rowspan', splitSeason and splits or nil)
seasonRow:node(thisCell)
elseif not entry.special then
local infoCell = SeriesOverview.series_attributes( frame:expandTemplate{title='N/A',args={'Буде оголошено'}} )
infoCell
:attr('colspan', (splitSeason and entry.episodesA ~= 'hide') and 1 or 2)
:attr('rowspan', splitSeason and splits or nil)
seasonRow:node(infoCell)
end
end
if splitSeason and entry.episodesA ~= 'hide' then
if entry['episodes' .. k] then
local thisCell = SeriesOverview.season_cell(entry['episodes' .. k], frame)
:attr('colspan', (entry.episodes ~= 'hide') and 1 or 2)
seasonRow:node(thisCell)
else
local infoCell = SeriesOverview.series_attributes( frame:expandTemplate{title='N/A',args={'Буде оголошено'}} )
:attr('colspan', (entry.episodes ~= 'hide') and 1 or 2)
seasonRow:node(infoCell)
end
end
end
end
-- Start date
if entry['start' .. k] or entry['released' .. k] then
local thisCell = oldDateSupport and (
SeriesOverview.season_cell(entry['start' .. k] or entry['released' .. k], frame)
) or (
SeriesOverview.season_cell((not allReleased or entry['end' .. k]) and entry['start' .. k] or entry['released' .. k], frame)
)
thisCell:attr('colspan', oldDateSupport and (
((not entry.special and (entry['released' .. k] or entry['end' .. k] == 'start')) or (entry.special and not entry['end' .. k]) or allReleased) and 2 or 1
) or (
(entry['released' .. k] or allReleased) and 2 or 1
))
:css('padding',basePadding)
seasonRow:node(thisCell)
else
local infoCell = SeriesOverview.series_attributes( frame:expandTemplate{title='N/A',args={'TBA'}} )
infoCell:css('padding',basePadding)
seasonRow:node(infoCell)
end
-- End date
local canIncludeEnd = oldDateSupport and (
(not allReleased and entry['end' .. k] ~= 'start' and not entry['released' .. k] and ((entry.special and entry['end' .. k]) or not entry.special)) and 'yes' or 'no'
) or (
(not allReleased and not entry['released' .. k] and ((entry.special and entry['end' .. k]) or not entry.special)) and 'yes' or 'no'
)
if canIncludeEnd == 'yes' then
if entry['end' .. k] then
local thisCell = SeriesOverview.season_cell(entry['end' .. k], frame)
:css('padding',cellPadding)
seasonRow:node(thisCell)
else
local infoCell = SeriesOverview.series_attributes( frame:expandTemplate{title='N/A',args={'Буде оголошено'}} )
infoCell:css('padding',cellPadding)
seasonRow:node(infoCell)
end
end
-- Network
if entry['network' .. k] and setNetwork then
local thisCell = SeriesOverview.season_cell(entry['network' .. k], frame)
:attr('rowspan', SeriesOverview.cellspan(SeasonEntries, SeasonEntries_ordered, 'network', X, (args.series and true or false), entry['network' .. k .. 'span'] or nil))
seasonRow:node(thisCell)
end
-- Information
for i = string.byte('A'), string.byte(topInfoCell) do
local param0 = 'info' .. string.char(i)
local param = 'info' .. string.char(i) .. k
local infoTransclude = args[param .. '_transclude']
if (infoTransclude == 'onlyinclude' and title.fullText == initialArticle) or (infoTransclude == 'noinclude' and title.fullText ~= initialArticle) then else
local infoParam = entry[param]
if infoParam and splitSeason and k == '' and not entry[param .. 'A'] then
entry[param .. 'A'] = entry[param]
entry[param .. 'spanning'] = 'y'
end
local rowspan = (entry[param0 .. 'spanning'] and splits) or
(args.series and SeriesOverview.cellspan(SeasonEntries, SeasonEntries_ordered, param0, X, (args.series and true or false), entry[param0 .. 'span'] or nil))
or nil
if k == 'A' or (k ~= 'A' and not entry[param0 .. 'spanning']) then
-- Cells with {{N/A|...}} already expanded
if infoParam then
if string.sub(infoParam,1,5) == 'style' then
local infoCell = SeriesOverview.series_attributes(infoParam)
infoCell:attr('rowspan', rowspan)
seasonRow:node(infoCell)
else
-- Unstyled content info cell
local thisCell = SeriesOverview.season_cell(infoParam, frame)
:attr('rowspan', rowspan)
seasonRow:node(thisCell)
end
else
if not args.series then
local infoCell = SeriesOverview.series_attributes( frame:expandTemplate{title='N/A',args={'Буде оголошено'}} )
infoCell:attr('rowspan', rowspan)
seasonRow:node(infoCell)
end
end
elseif not entry[param0 .. 'spanning'] then
if not args.series then
local infoCell = SeriesOverview.series_attributes( frame:expandTemplate{title='N/A',args={'Буде оголошено'}} )
infoCell:attr('rowspan', rowspan)
seasonRow:node(infoCell)
end
end
end
end
end -- End k0 string.byte
end -- End 'for' SeasonEntries_ordered
end -- End 'if' multiseries
end -- End 'do' season rows
local rootdiv
if args.multiseries or not args.series then
rootdiv = mw.html.create('div')
rootdiv
:css('display', 'block')
:css('overflow-x', 'auto')
rootdiv:node(root)
rootdiv = tostring(rootdiv)
else
rootdiv = tostring(root)
end
if args.dontclose then
rootdiv = mw.ustring.gsub(rootdiv, "</div>", "")
rootdiv = mw.ustring.gsub(rootdiv, "</table>", "")
end
return rootdiv .. categories
end
--------------------------------------------------------------------------------
-- Exports
--------------------------------------------------------------------------------
local p = {}
function p.main(frame)
local args = require('Module:Arguments').getArgs(frame, {
wrappers = 'Шаблон:Огляд серіалу'
})
return SeriesOverview.new(frame, args)
end
function p._end(frame)
return SeriesOverview.endtable()
end
return p
|
Index:
pl ar de en es fr it arz nl ja pt ceb sv uk vi war zh ru af ast az bg zh-min-nan bn be ca cs cy da et el eo eu fa gl ko hi hr id he ka la lv lt hu mk ms min no nn ce uz kk ro simple sk sl sr sh fi ta tt th tg azb tr ur zh-yue hy my ace als am an hyw ban bjn map-bms ba be-tarask bcl bpy bar bs br cv nv eml hif fo fy ga gd gu hak ha hsb io ig ilo ia ie os is jv kn ht ku ckb ky mrj lb lij li lmo mai mg ml zh-classical mr xmf mzn cdo mn nap new ne frr oc mhr or as pa pnb ps pms nds crh qu sa sah sco sq scn si sd szl su sw tl shn te bug vec vo wa wuu yi yo diq bat-smg zu lad kbd ang smn ab roa-rup frp arc gn av ay bh bi bo bxr cbk-zam co za dag ary se pdc dv dsb myv ext fur gv gag inh ki glk gan guw xal haw rw kbp pam csb kw km kv koi kg gom ks gcr lo lbe ltg lez nia ln jbo lg mt mi tw mwl mdf mnw nqo fj nah na nds-nl nrm nov om pi pag pap pfl pcd krc kaa ksh rm rue sm sat sc trv stq nso sn cu so srn kab roa-tara tet tpi to chr tum tk tyv udm ug vep fiu-vro vls wo xh zea ty ak bm ch ny ee ff got iu ik kl mad cr pih ami pwn pnt dz rmy rn sg st tn ss ti din chy ts kcg ve
Portal di Ensiklopedia Dunia