Module:Sports rbr table

-- This module implements {{Sports rbr table}}local p = {}-- Internationalisationlocal labels = {teamround = 'Team ╲ Round',source = 'Source:',notes = 'Notes:',matches = 'match(es)',updatedto = 'Updated to <matches> played on <date>.',firstplayed = 'First <matches> will be played on <date>.',futuredate = '?',complete = 'complete',future = 'future'}local modname = 'Module:Sports rbr table'local templatestyles = 'Module:Sports rbr table/styles.css'local args = nillocal preview, tracking = '', ''local hasnotes = falselocal colorlist = {}local textlist = {}local color_map = {green1='#BBF3BB', green2='#CCF9CC', green3='#DDFCDD', green4='#EEFFEE',blue1='#BBF3FF', blue2='#CCF9FF', blue3='#DDFCFF', blue4='#EEFFFF',yellow1='#FFFFBB', yellow2='#FFFFCC', yellow3='#FFFFDD', yellow4='#FFFFEE',red1='#FFBBBB', red2='#FFCCCC', red3='#FFDDDD', red4='#FFEEEE',black1='#BBBBBB', black2='#CCCCCC', black3='#DDDDDD', black4='#EEEEEE',['1st']='#FFD700', ['2nd']='#C0C0C0', ['3rd']='#CC9966'}local legend_symbols = {O='W/O'}local legend_order_default = {'A', 'H', 'N', 'B', 'W', 'D', 'L', 'Ab', 'P', 'O'}local function isnotempty(s)return s and s:match( '^%s*(.-)%s*$' ) ~= ''endlocal function zeropad(n)if n>=0 and n < 10 thenreturn '00' .. nendif n>=0 and n < 100 thenreturn '0' .. nendreturn '' .. nendlocal function pad_key(k)-- Zero pad, fix ranges and dashesif k thenk = k .. ' 'k = mw.ustring.gsub(k, '–', '-')k = mw.ustring.gsub(k, '_([%d][^%d])', '_0%1')k = mw.ustring.gsub(k, '%-([%d][^%d])', '-0%1')k = mw.ustring.gsub(k, '_([%d][%d][^%d])', '_0%1')k = mw.ustring.gsub(k, '%-([%d][%d][^%d])', '-0%1')k = mw.ustring.gsub(k, '([^%d])%-([%d])', '%1000-%2')k = mw.ustring.gsub(k, '([%d])%-%s*$', '%1-999')k = mw.ustring.gsub(k, '^%s*(.-)%s*$', '%1')endreturn kendlocal function matches_date(text, m, d)return mw.ustring.gsub(mw.ustring.gsub(text .. '', '<matches>', m), '<date>', d)endlocal function escapetag(text)return mw.ustring.gsub(text, '</', '<FORWARDSLASH')endlocal function unescapetag(text)return mw.ustring.gsub(text, '<FORWARDSLASH', '</')endlocal function get_color(p)if p thenp = mw.ustring.gsub(p, '</?[Aa][Bb][Bb][Rr][^<>]*>', '')p = mw.ustring.gsub(p, '<[Ss][Uu][Pp]>[^<>]*</[Ss][Uu][Pp]>', '')p = mw.ustring.gsub(p, '</?[Ss][^<>]*>', '')p = mw.ustring.gsub(p, '†%s*$', '')p = mw.ustring.gsub(p, '=%s*$', '')p = mw.ustring.gsub(p, '%[%[[^%[%]|]*|([^%[%]|]*)%]%]', '%1')if p:match('^%a%a*$') thenif args['text_' .. p] == nil thentracking = tracking .. '[[Category:Pages using sports rbr table with an undescribed result|' .. p:match('^(%a).*$') .. ']]'endendendlocal c = colorlist[p] or colorlist[zeropad(tonumber(p) or -1)]if c thenreturn color_map[c] or cendp = tonumber(p or '0') or 0if p <= 0 thenreturn nilend-- ranges in order of specificitylocal offset1, offset2 = 999, 999for k,v in pairs( colorlist ) dolocal r1 = tostring(k):match( '^%s*([%d]+)%-[%d]+%s*$' )local r2 = tostring(k):match( '^%s*[%d]+%-([%d]+)%s*$' )if r1 and r2 thenr1 = tonumber(r1)r2 = tonumber(r2)if (r1 <= p) and (r2 >= p) thenif (c == nil) or ((p - r1) <= offset1 and (r2 - p) <= offset2) thenc = color_map[v] or voffset1 = p - r1offset2 = r2 - pendendendendreturn cendlocal function check_arg(k, st)k = tostring(k) or ''if k == 'firstround' or k == 'sortable' or k == 'updated' or k == 'update'or k =='source' or k =='notes' or k == 'legendpos' or k == 'date' or k == 'header' or k == 'title' or k == 'start_date' or k == 'labelnowrap'or k == 'labelalign' or k == 'toptext' or st.addtl_args(k) thenelseif k == 'legendorder' thentracking = tracking .. '[[Category:Pages using sports rbr table with legendorder]]'elseif tostring(k):match( '^%s*text_?(.-)%s*$' ) thenelseif tostring(k):match( '^%s*colou?r_?(.-)%s*$' ) thenelseif tostring(k):match( '^%s*team[%d]+%s*$' ) thenelseif tostring(k):match( '^%s*label[%d]+%s*$' ) thenif args['header'] thenelsetracking = tracking .. '[[Category:Pages using sports rbr table with unsupported parameters|ψ]]'endelseif tostring(k):match( '^%s*opp[%d]+%s*$' ) thenelseif tostring(k):match( '^%s*pos[%d]+%s*$' ) thenelseif tostring(k):match( '^%s*grnd[%d]+%s*$' ) thenelseif tostring(k):match( '^%s*res[%d]+%s*$' ) thenelseif tostring(k):match( '^%s*posc[%d]+%s*$' ) thenelseif tostring(k):match( '^%s*grndc[%d]+%s*$' ) thenelseif tostring(k):match( '^%s*resc[%d]+%s*$' ) thenelseif tostring(k):match( '^%s*split[%d]+%s*$' ) thenelseif k == 'rnd1' thentracking = tracking .. '[[Category:Pages using sports rbr table with rnd parameters]]'elseif tostring(k):match( '^%s*rnd[%d]+%s*$' ) thenelseif tostring(k):match( '^%s*opp_' ) thenelseif tostring(k):match( '^%s*pos_' ) thenelseif tostring(k):match( '^%s*grnd_' ) thenelseif tostring(k):match( '^%s*res_' ) thenelseif tostring(k):match( '^%s*posc_' ) thenelseif tostring(k):match( '^%s*grndc_' ) thenelseif tostring(k):match( '^%s*resc_' ) thenelseif tostring(k):match( '^%s*name_' ) thenelseif tostring(k):match( '^%s*note_' ) thenelseif tostring(k):match( '^%s*pos[%d]+_rnd[%d]+_colou?r%s*$' ) thentracking = tracking .. '[[Category:Pages using sports rbr table with per team and round coloring]]'elseif tostring(k):match( '^%s*res[%d]+_rnd[%d]+_colou?r%s*$' ) thentracking = tracking .. '[[Category:Pages using sports rbr table with per team and round coloring]]'elseif tostring(k):match( '^%s*pos[%d]+_rnd[%d]+_note%s*$' ) thenelseif tostring(k):match( '^%s*res[%d]+_rnd[%d]+_note%s*$' ) thenelselocal vlen = mw.ustring.len(k)k = mw.ustring.sub(k, 1, (vlen < 25) and vlen or 25) k = mw.ustring.gsub(k, '[^%w%-_ ]', '?')preview = preview .. 'Unknown: "' .. k .. '"<br>'tracking = tracking .. '[[Category:Pages using sports rbr table with unsupported parameters|' .. k .. ']]'endendfunction p.table(frame)local getArgs = require('Module:Arguments').getArgslocal yesno = require('Module:Yesno')args = getArgs(frame, {wrappers = {'Template:Sports rbr table'}})local style_def = args['style']local p_style = require(modname)if style_def ~= nil then p_style = require(modname .. '/' .. style_def) endargs = p_style.defaults(args,yesno,color_map)local rounds = tonumber(args['rounds'] or '0') or 0local firstround = tonumber(args['firstround'] or 1) or 1local sortable = yesno(args['sortable'] or 'no')local updated = args['updated'] or args['update']local source = args['source']local notes = args['notes']local delimiter = args['delimiter'] or '/'local addlegend = nillocal legendpos = (args['legendpos'] or 'tr'):lower()local header, footer, prenotes  = '', '', ''-- Lowercase two labels --labels['complete'] = string.lower(labels['complete'])labels['future'] = string.lower(labels['future'])-- Adjust roundsrounds = rounds - (firstround - 1)-- Trackingif updated and updated:match(' %d%d%d%d$') thenlocal YY = mw.ustring.gsub(updated, '^.*(%d%d)$', '%1')local pn = frame:getParent():getTitle() or ''if pn:match('^User:') or pn:match('^User talk:') or pn:match('^Draft:') or pn:match('^Talk:') thenelseif pn:match('%d%d' .. YY) or pn:match('[–%-]' .. YY) thenelsetracking = tracking .. '[[Category:Pages using sports rbr table with dubious updated parameter]]'endend    end-- Require a sourceif source == nil thensource = frame:expandTemplate{ title = 'citation needed', args = { reason='No source parameter defined', date=args['date'] or os.date('%B %Y') } }elseif source and source:match('[^%[]#') then if source:match('eason#') or source:match('%d%d#') thentracking = tracking .. '[[Category:Pages using sports rbr table with an unusual source]]'elseif source:match('^[Hh][Tt][Tt][Pp]') thentracking = tracking .. '[[Category:Pages using sports rbr table with an unusual source|Φ]]'endend-- Process team, pos, and color argslocal team_list = {}local maxrounds = 0local rowlength = {}for k, v in pairs( args ) docheck_arg(k, p_style)-- Preprocess rangesif tostring(k):match( '^%s*text_?(.-)%s*$' ) thenk = pad_key(k)endif tostring(k):match( '^%s*colou?r_?(.-)%s*$' ) thenk = pad_key(k)end-- Create the list of teams and count roundslocal i = tonumber(tostring(k):match( '^%s*team([%d]+)%s*$' ) ortostring(k):match( '^%s*label([%d]+)%s*$' ) or '0')if ( i > 0 and isnotempty(v) ) thentable.insert(team_list, i)local p = p_style.get_argvalues_for_maxround(args,i)if args['name_' .. v] thenlocal t = args['team' .. i] or args['label' .. i] or ''p = p_style.get_argvalues_for_maxround(args,t,'_')endlocal pos = mw.text.split(escapetag(p), '%s*' .. delimiter .. '%s*')table.insert(rowlength, #pos)maxrounds = (#pos > maxrounds) and #pos or maxrounds-- maxrounds = p_style.get_maxrounds(args,team_list,i,v,rowlength,maxrounds,delimiter)end-- Create the list of colorslocal s = tostring(k):match( '^%s*colou?r_?(.-)%s*$' )if ( s and isnotempty(v) ) thencolorlist[s] = v:lower()end-- Check if we are adding a legends = tostring(k):match( '^%s*text_?(.-)%s*$' )if ( s and isnotempty(v) ) thentextlist[s] = vaddlegend = 1endendmaxrounds = p_style.get_rounds_or_maxrounds(rounds,maxrounds,args,team_list)table.sort(rowlength)for k=2,#rowlength doif rowlength[k] ~= rowlength[k-1] thentracking = tracking .. '[[Category:Pages using sports rbr table with unequal row lengths|k]]'endend-- sort the teamstable.sort(team_list)local fs = 95if ((maxrounds - firstround) > 37 ) thenfs = fs - 2*(maxrounds - firstround - 37)fs = (fs < 80) and 80 or fsend-- Build the tablelocal root = mw.html.create('table')root:addClass('wikitable')root:addClass(sortable and 'sortable' or nil)root:addClass('sportsrbrtable')root:css('font-size', fs .. '%')if args['title'] thenroot:tag('caption'):wikitext(args['title'])endlocal navbar = ''if args['template_name'] thennavbar = '<br />' .. frame:expandTemplate{ title = 'navbar', args = { mini=1, style='', brackets=1, args['template_name']}}-- remove the next part if https://en.wikipedia.org/w/index.php?oldid=832717047#Sortable_link_disables_navbar_links?-- is ever fixedif sortable thennavbar = mw.ustring.gsub(navbar, '<%/?abbr[^<>]*>', ' ')endend-- Heading rowlocal row = p_style.header(root,args,labels,maxrounds,navbar,team_list,firstround)-- Team positionslocal prefixes = {'pos', 'res', 'grnd'}for k=1,#team_list dolocal i = team_list[k]local t = args['team' .. i] or args['label' .. i] or ''local o = args['opp' .. i] or ''local n = args['note' .. i] or ''local efnname = 'note' .. ilocal suf = iif args['name_' .. t] theno = args['opp_' .. t] or ''n = args['note_' .. t] or ''efnname = 'note' .. tsuf = '_' .. tt = args['name_' .. t]endif n ~= '' thenif args['note_' .. n] then n = frame:expandTemplate{ title = 'efn', args = { name='note' .. n, ''} }elsen = frame:expandTemplate{ title = 'efn', args = { name=efnname, n} }endhasnotes = trueendlocal resfound = (args['grnd' .. i] and 1 or 0) + (args['pos' .. i] and 1 or 0) + (args['res' .. i] and 1 or 0)if args['name_' .. t] thenresfound = (args['grnd_' .. t] and 1 or 0) + (args['pos_' .. t] and 1 or 0) + (args['res_' .. t] and 1 or 0)endif (resfound > 1) thentracking = tracking .. '[[Category:Pages using sports rbr table with conflicting parameters]]'endlocal rowsdisp = 0for subrow,lbl in ipairs(prefixes) dolocal p = args[lbl .. suf] or ''local pc = args[lbl .. 'c' .. suf] or ''if p ~= '' or (rowsdisp == 0 and subrow == 3) thenrowsdisp = rowsdisp + 1row = root:tag('tr')row:tag('th'):addClass(args['team' .. i] and 'sportsrbrtable-team' or 'sportsrbrtable-lbl'):css('text-align', args['labelalign']):css('white-space', args['labelnowrap'] and 'nowrap' or nil):attr('scope', 'row'):wikitext(mw.ustring.gsub(t,'^%s*%-%s*$', '&nbsp;') .. n)if t:match('<%s*[Cc][Ee][Nn][Tt][Ee][Rr]%s*>') thentracking = tracking .. '[[Category:Pages using sports rbr table with unsupported parameters|χ]]'endlocal opp = mw.text.split(escapetag(o), '%s*' .. delimiter .. '%s*')local pos = mw.text.split(escapetag(p), '%s*' .. delimiter .. '%s*')local clr = mw.text.split(escapetag(pc), '%s*' .. delimiter .. '%s*')for r=1,maxrounds dolocal s = args['team' .. i .. '_rnd' .. r .. '_' .. 'color'] orargs['team' .. i .. '_rnd' .. r .. '_' .. 'colour'] orargs[lbl .. i .. '_rnd' .. r .. '_' .. 'color'] orargs[lbl .. i .. '_rnd' .. r .. '_' .. 'colour'] or nillocal n = args['team' .. i .. '_rnd' .. r .. '_' .. 'note'] orargs[lbl .. i .. '_rnd' .. r .. '_' .. 'note'] or nilif s then s = color_map[s] or s endlocal opprt, posrt = unescapetag(opp[r] or ''), unescapetag(pos[r] or '')local posrc = isnotempty(clr[r]) and clr[r] or posrtif posrt:match('^%s*<[Uu]>[%d–]+[A-Za-z][A-Za-z0-9]*') thenposrc = posrc:match('^%s*<[Uu]>[%d–]+([A-Za-z][A-Za-z0-9]*)')posrt = mw.ustring.gsub(posrt, '^%s*(<[Uu]>[%d–]+)[A-Za-z][A-Za-z0-9]*', '%1')elseif posrt:match('^%s*[%d–]+[A-Za-z][A-Za-z0-9]*') thenposrc = posrc:match('^%s*[%d–]+([A-Za-z][A-Za-z0-9]*)')posrt = mw.ustring.gsub(posrt, '^%s*([%d–]+)[A-Za-z][A-Za-z0-9]*', '%1')endlocal dsif args['sortable'] and (opprt or posrt):match('^%s*[%d]+[^%d%s]') thends = mw.ustring.gsub(opprt or posrt, '^%s*([%d]+)[^%d%s].*$', '%1')endif n thenif args['note_' .. n] thenn = frame:expandTemplate{ title = 'efn', args = { name='note' .. n, args['note_' .. n]} }elsen = frame:expandTemplate{ title = 'efn', args = { name='note' .. i .. '_rnd_' .. r, n} }endhasnotes = trueendrow:tag('td'):attr('data-sort-value', ds):css('background-color', s or get_color(p_style.rowbg(posrc, opprt))):wikitext(p_style.rowtext(frame,args,legend_symbols,posrt,opprt) .. (n or ''))endif args['split' .. i] and k ~= #team_list thenrow = root:tag('tr'):css('background-color', '#BBBBBB'):css('line-height', '3pt')row:tag('td'):attr('colspan', maxrounds + 1)endendendend-- build the legendif addlegend then-- Sort the keys for the legendlocal legendkeys = {}for k,v in pairs( textlist ) dotable.insert(legendkeys, k)endtable.sort(legendkeys)if args['legendorder'] thenlegendkeys = mw.text.split(args['legendorder'] .. delimiter ..table.concat(legend_order_default, delimiter) .. delimiter ..table.concat(legendkeys, delimiter), '%s*' .. delimiter .. '%s*')elselegendkeys = mw.text.split(table.concat(legend_order_default, delimiter) .. delimiter ..table.concat(legendkeys, delimiter), '%s*' .. delimiter .. '%s*')endlocal lrootif (legendpos == 't' or legendpos == 'b') thenlroot = mw.html.create('')local firsttag = truefor k,v in pairs( legendkeys ) doif v and textlist[v] thenif firsttag == false then lroot:wikitext('; ') endlocal c = colorlist[v] or ''local l = lroot:tag('span'):css('margin', '0'):css('white-space', 'nowrap'):tag('span'):addClass('legend-text'):css('border', 'none'):css('padding', '1px .3em'):css('background-color', color_map[c] or c):css('font-size', '95%'):css('border', '1px solid #BBB'):css('line-height', '1.25'):css('text-align', 'center'):wikitext(p_style.legendtext(legend_symbols,v)):done():wikitext(' = ' .. textlist[v])textlist[v] = nilfirsttag = falseendendelselroot = mw.html.create('table')lroot:addClass('wikitable')lroot:css('font-size', '88%')if legendpos ~= 'tl' and legendpos ~= 'bl' thenlroot:css('float', 'right')lroot:css('clear', 'right')-- lroot:css('width', 'auto')endfor k,v in pairs( legendkeys ) doif v and textlist[v] thenlocal c = colorlist[v] or ''local row = (legendpos == 'tl' or legendpos == 'bl') and lroot or lroot:tag('tr')local l = row:tag('th'):css('background-color', color_map[c] or c)if legend_symbols[v] thenl:css('font-weight', 'normal'):css('padding', '1px 3px'):wikitext(legend_symbols[v])elsel:css('width', '10px')endrow:tag('td'):css('padding', '1px 3px'):wikitext(textlist[v])textlist[v] = nilendendendif (legendpos == 'bl' or legendpos == 'br') thenfooter = footer .. tostring(lroot)elseif (legendpos == 'b') thenprenotes = prenotes .. tostring(lroot)elseif (legendpos == 't') thenargs['toptext'] = (args['toptext'] or '').. frame:expandTemplate{ title = 'refbegin' }.. tostring(lroot).. frame:expandTemplate{ title = 'refend' }elseheader = header .. tostring(lroot)endend-- simplify updated == complete caselocal lupdated = updated and string.lower(updated) or ''if lupdated == labels['complete'] or lupdated == 'complete' thenlupdated = ''end-- add note listif hasnotes thenfooter = footer .. frame:expandTemplate{ title = 'notelist' }end-- build the footerif prenotes ~= '' or notes or source or lupdated ~= '' thenfooter = footer .. frame:expandTemplate{ title = 'refbegin' }if lupdated ~= '' thenlocal mtext = args['matches_text'] or labels['matches']if lupdated == labels['future'] or lupdated == 'future' thenfooter = footer .. matches_date(labels['firstplayed'] .. ' ',mtext, args['start_date'] or labels['futuredate'])elsefooter = footer .. matches_date(labels['updatedto'] .. ' ',mtext, updated)endendif source thenfooter = footer .. labels['source'] .. ' ' .. sourceendif prenotes ~= '' thenif lupdated ~= '' or source thenfooter = footer .. '<br>'endfooter = footer .. prenotesendif notes thenif prenotes ~= '' or lupdated ~= '' or source thenfooter = footer .. '<br>'endfooter = footer .. labels['notes'] .. ' ' .. notesendfooter = footer .. frame:expandTemplate{ title = 'refend' }end-- add clear right for the legend if necessaryfooter = footer .. ((addlegend and (legendpos == 'bl' or legendpos == 'br'))and '<div style="clear:right"></div>' or '')if tracking ~= '' thenif frame:preprocess( "{{REVISIONID}}" ) == "" thentracking = previewendendreturn frame:extensionTag{ name = 'templatestyles', args = { src = templatestyles} }.. header .. (args['toptext'] or '') .. '<div style="overflow:hidden">'.. '<div class="noresize overflowbugx" style="overflow:auto">'.. tostring(root) .. '</div></div>' .. footer .. trackingendfunction p.get_argvalues_for_maxround(args, x, del)del = del or ''return args['pos' .. del .. x] or args['res' .. del .. x] or ''endfunction p.get_rounds_or_maxrounds(rounds, maxrounds)return (rounds > maxrounds) and rounds or maxroundsendfunction p.addtl_args(k)-- just return 'true', no additional argsreturn trueendfunction p.defaults(args)-- set nothingreturn argsendfunction p.header(root,args,labels,maxrounds,navbar,team_list,firstround)local row = root:tag('tr')row:tag('th'):attr('rowspan', args['sortable'] and 2 or nil):wikitext((args['header'] or labels['teamround']) .. navbar)for r=1,maxrounds dorow:tag('th'):addClass(args['sortable'] and 'sportsrbrtable-rnd-sort' or 'sportsrbrtable-rnd'):attr('scope', 'col'):wikitext(args['rnd' .. (r + (firstround - 1))]or (r + (firstround - 1)))endif args['sortable'] thenrow = root:tag('tr')for r=1,maxrounds dorow:tag('th'):addClass('sportsrbrtable-rnd-toggle')endendreturn rowendfunction p.rowtext(frame,args,legend_symbols,posrt,postrc,opprt,opprc)return legend_symbols[posrt] or posrtendfunction p.rowbg(posrc)return posrcendfunction p.legendtext(legend_symbols,v)return legend_symbols[v] or (v:match('^[^%d][^%d]?$') and v) or '&nbsp;'endreturn p