Module:Sports table: Difference between revisions

Content deleted Content added
add parsing of both args and parentargs which means we no longer need to pass (several) parameters between pages transcluding and the child page housing the table
fix text color in dark mode; this generally works but may cause problems, in which case revert or try specifying a different CSS value
 
(114 intermediate revisions by 9 users not shown)
Line 2:
-- See documentation for details
 
require('Module:No globalsstrict')
 
local p = {}
 
-- Helper functions
local function isnotblank(s)
return s and s:match( '^%s*(.-)%s*$' ) ~= ''
end
 
local function firstnonblank(s1,s2)
if ( s1 and s1:match( '^%s*(.-)%s*$' ) ~= '' ) then
return s1
elseif ( s2 and s2:match( '^%s*(.-)%s*$' ) ~= '' ) then
return s2
end
return nil
end
-- Main function
function p.main(frame)
-- Declare locals
local ArgsgetArgs = framerequire('Module:Arguments').argsgetArgs
local Args = getArgs(frame, {parentFirst = true})
local Pargs = frame:getParent().args
local ii_start, ii_end, N_rows_res = 0
local text_field_result
Line 33 ⟶ 19:
local team_list = {}
local jj, jjj
local table_anchor = mw.ustring.gsub(Args['section'] and 'sports-table-' .. Args['section'] or '', ' ', '_')
-- Exit early if we are using section transclusion for a different section
if(local tsection = isnotblankframe:getParent(Pargs).args['transcludesection']) andor isnotblankframe:getParent(Args).args['section']) )or then''
if( local Pargs['transcludesection']bsection ~= Argsframe.args['section'] )or then''
if( tsection ~= '' and bsection ~= '' ) then
if( tsection ~= bsection ) then
return ''
end
end
 
local templatestyles = frame:extensionTag{
-- Get the custom start point for the table (most will start by default at 1
name = 'templatestyles', args = { src = 'Module:Sports table/styles.css' }
}
-- Edit links if requested
local baselink = frame:getParent():getTitle()
if baselink == 'Module:Excerpt' then baselink = '' end
if mw.title.getCurrentTitle().fullText == baselink then baselink = '' end
local template_name = (baselink ~= '' and (':' .. baselink .. (table_anchor ~= '' and '#' .. table_anchor or '')))
or ''
 
-- Get the custom start point for the table (most will start by default at 1)
local top_pos = tonumber(Args['highest_pos']) or 1
-- Get the custom end point for the table (unrestricted if bottom_pos is < top_pos)
local bottom_pos = tonumber(Args['lowest_pos']) or 0
local N_teams = top_pos - 1 -- Default to 0 at start, but higher number needed to skip certain entries
Line 53 ⟶ 55:
local p_style = require('Module:Sports table/'..style_def)
local p_sub = require('Module:Sports table/sub')
local p_matches = require('Module:Sports results')
-- Random value used for uniqueness
Line 65 ⟶ 66:
yellow1='#FFFFBB', yellow2='#FFFFCC', yellow3='#FFFFDD', yellow4='#FFFFEE',
red1='#FFBBBB', red2='#FFCCCC', red3='#FFDDDD', red4='#FFEEEE',
black1='#BBBBBB', black2='#CCCCCC', black3='#DDDDDD', black4='#EEEEEE'},
orange1='#FEDCBA', orange2='#FEEAD5',
white1='inherit',['']='inherit'
}
-- Show all stats in table or just matches played and points
local full_table = true
local hide_results = yesno(Args['hide_results'] or 'no')
local hide_footer = yesno(Args['hide_footer'] or 'no')
local pld_pts_val = string.lower(Args['only_pld_pts'] or 'no')
local show_class_rules = yesno(Args['show_class_rules'] or 'yes') and true or false
-- True if par doesn't exist, false otherwise
if yesno(pld_pts_val) then
full_table = false
elseif pld_pts_val=='no_hide_class_rules' then
full_table = true
show_class_rules = false
end
-- Declare results column header
Line 71 ⟶ 89:
results_header = {Q='Qualification', QR='Qualification or relegation',
P='Promotion', PQR='Promotion, qualification or relegation',
PR='Promotion or relegation', RPQ='RelegationPromotion or qualification'},
R='Relegation'}
local results_defined = false -- Check whether this would be needed
-- Possible prefix for result fields
local respre = (Args['result_prefix'] or '') .. '_'
respre = (respre == '_') and '' or respre
-- Now define line for column header (either option or custom)
local local_res_header = results_header[Args[respre..'res_col_header']] or Args[respre..'res_col_header'] or ''
-- Check whether it includes a note
local res_head_note = Args['note_header_res']
local res_head_note_text = ''
if full_table and res_head_note then
notes_exist = true
res_head_note_text = frame:expandTemplate{ title = 'efn', args = { group='Table_noteslower-alpha', res_head_note} }
end
local results_header_txt = '! scope="col" |'..local_res_header..res_head_note_text..'\n'
Line 86 ⟶ 108:
-- Get status option
local t_status = p_style.status(Args)
-- Alternative syntax for team list
if Args['team_order'] and Args['team_order'] ~= '' then
local team_order_offset = (tonumber(Args['team_order_start']) or 1) - 1
local tlist = mw.text.split(Args['team_order'], '%s*[;,]%s*')
for k, tname in ipairs(tlist) do
if tname ~= '' then
Args['team' .. (k + team_order_offset)] = tname
end
end
end
-- Read in number of consecutive teams (ignore entries after skipping a spot)
while Args['team'..N_teams+1] ~= nil and (bottom_pos < top_pos or N_teams < bottom_pos) do
N_teams = N_teams+1
-- Sneakily add it twice to the team_list parameter, once for the actual
Line 96 ⟶ 129:
team_list[Args['team'..N_teams]] = N_teams -- team X entry is position i
end
 
-- Optional totals
-- Show all stats in table or just matches played and points
local total_row_name = 'SPORTS_TABLE_TOTAL'
local pld_pts_val = firstnonblank(Pargs['only_pld_pts'], Args['only_pld_pts']) or 'no'
if yesno(Args['show_totals'] or 'no') then
local full_table
N_teams = N_teams+1
local hide_class_rules = false
Args['team' .. N_teams] = total_row_name
-- True if par doesn't exist, false otherwise
Args['name_' .. total_row_name] = 'Total'
-- First convert to lower case if it is a string
Args['result' .. N_teams] = total_row_name
pld_pts_val = string.lower(pld_pts_val)
Args['col_' .. total_row_name] = '#eee'
if yesno(pld_pts_val) then
team_list[N_teams] = Args['team' .. N_teams]
full_table = false
team_list[Args['team'..N_teams]] = N_teams
elseif pld_pts_val=='no_hide_class_rules' then
full_table = true
hide_class_rules = true
else
full_table = true
end
 
-- Show position
local position_col = yesno(Args['show_positions'] or 'yes') and true or false
-- Show groups or note
local show_groups_valgroup_col = firstnonblankyesno(PargsArgs['show_groups'], Args[or 'show_groupsno']) and true or 'no'false
 
local group_col = false
if yesno(show_groups_val) then group_col = true end
-- Show match_table or not
local show_matches_valmatch_table = firstnonblankyesno(PargsArgs['show_matches'], Args[or 'show_matchesno']) and true or 'no'false
local match_tablep_matches = falsematch_table and
(style_def == 'Chess' and require('Module:Sports results/'..style_def) or require('Module:Sports results'))
if yesno(show_matches_val) then match_table = true end
-- Custom position column label or note
local pos_label = Args['postitle'] or '<abbr title="Position">Pos</abbr>'
if pos_labelposition_col == ''false then pos_label = nil end
-- If empty (or undeclared) then default
-- Show status or not
pos_label = '<abbr title="Position">Pos</abbr>'
local show_status = yesno(Args['show_status'] or 'yes') and true or false
end
 
-- Get VTE button text (but only for non-empty text)
local template_name = Args['template_name'] or ''
local VTE_text = ''
if (template_name ~= '') then
VTE_text = require('Module:Navbar')._navbar({
VTE_text = frame:expandTemplate{ title = 'navbar', args = { mini=1, style='float:right', template_name} }
template_name,
mini=1,
style='float:right',
brackets=1
})
-- remove the next part if https://en.wikipedia.org/w/index.php?oldid=832717047#Sortable_link_disables_navbar_links?
-- is ever fixed
if yesno(Args['sortable_table'] or 'no') then
VTE_text = mw.ustring.gsub(VTE_text, '<%/?abbr[^<>]*>', ' ')
end
end
-- Add source to title if specified and possible
local title_source = false
if Args['title'] and Args['title_source'] then
Args['title'] = Args['title'] .. Args['title_source']
title_source = true
elseif Args['table_header'] and Args['table_header_source'] then
Args['table_header'] = Args['table_header'] .. Args['table_header_source']
title_source = true
end
 
-- Add a table anchor
if table_anchor ~= '' then
table.insert(t, '<span class="anchor" id="' .. table_anchor .. '"></span>\n')
end
 
Line 142 ⟶ 197:
-- Add empty column header
t_return.count = t_return.count+1
table.insert(t_return.tab_text,'! scope="row" class="unsortable" style="background-color:white;color:black;border-top:white;border-bottom:white;line-width:3pt;"| \n')
-- Add rest of header
t_return = p_matches.header(t_return,Args,p_sub,N_teams,team_list,tonumber(Args['legs']) or 1)
end
t = t_return.tab_text
Line 152 ⟶ 207:
-- Determine what entries go into table
-- Find out which team to show (if any)
local ii_show = team_list[firstnonblank(PargsArgs['showteam'], Args['showteam'])or nil] -- nil if non-existant
-- Start and end positions to show
local n_to_show = tonumber(Args['show_limit']) or N_teams
Line 185 ⟶ 240:
local new_res_ii = ii_start
-- Pre-check for existence of column
if not hide_results then
for ii = ii_start, ii_end do
for ii = ii_start, ii_end do
if Args['result'..ii] then results_defined = true end
if Args[respre..'result'..ii] and Args[respre..'text_' .. Args[respre..'result'..ii]] then results_defined = true end
end
end
-- Remove results header if it is unused
Line 213 ⟶ 270:
-- Does it need a promotion/qualification/relegation tag
local result_local = Args[respre..'result'..ii] or nil
local bg_col = nil
 
-- Get local background colour
if result_local then
bg_col = result_col[Args[respre..'col_'..result_local]] or Args[respre..'col_'..result_local] or 'whiteinherit'
if bg_col == 'background-color:inherit'.. then bg_col = bg_col .. 'background-color:inherit;' --color: Full styleinherit' tag
else
end
if not bg_col then bg_col = 'background-color:transparent'..bg_col..';color:black;' end -- Becomes defaultFull ifstyle undefinedtag
end
end
if not bg_col then bg_col = 'background-color:transparent; color: inherit;' end -- Becomes default if undefined
-- Bold this line or not
local ii_fw = ii == ii_show and 'font-weight: bold;' or 'font-weight: normal;'
if yesno(Args['show_totals'] or 'no') and team_code_ii == total_row_name then
ii_fw = 'font-weight: bold;'
end
-- Check whether there is a note or not, if so get text ready for it
Line 241 ⟶ 305:
-- Call refn template
note_string = frame:expandTemplate{ title = 'efn', args = { group='Table_noteslower-alpha', name=note_id, note_local} }
else
-- Option 2
Line 259 ⟶ 323:
-- Call refn template
note_string = frame:expandTemplate{ title = 'efn', args = { group='Table_noteslower-alpha', name=note_id, Args['note_'..note_local]} }
end
end
Line 268 ⟶ 332:
-- Insert status when needed
local status_string = ''
local status_local = show_status and Args[respre .. 'status_'..team_code_ii] or nil
local status_let_first = true
local curr_letter
Line 288 ⟶ 352:
end
end
end
-- Only add brackets/dash and bolding if it exist
if not status_let_first then
if t_status.position == 'before' then
Line 300 ⟶ 364:
-- Now build the rows
if yesno(Args['show_totals'] or 'no') and team_code_ii == total_row_name then
table.insert(t,'|- \n') -- New row
table.insert(t,'!|- scopeclass="rowsortbottom" style="text-align: center;'..ii_fw..bg_col..'"| '..pos_num..'\n') -- PositionNew numberrow
else
table.insert(t,'|- \n') -- New row
end
if position_col then
table.insert(t,'| style="text-align: center;'..ii_fw..bg_col..'"| '..pos_num..'\n') -- Position number
end
if full_table and group_col then
table.insert(t,'| style="'..ii_fw..bg_col..'" |'..group_txt..'\n') -- Group number/name
Line 312 ⟶ 382:
team_string = team_name..note_string..status_string
end
table.insert(t,'|! scope="row" style="text-align: left; white-space:nowrap;'..ii_fw..bg_col..'"| '..team_string..'\n')-- Team (with possible note)
-- Call to subfunction
t_return = p_style.row(frame,t,Args,p_sub,notes_exist,hth_id_list,full_table,rand_val,team_list,team_code_ii,ii_start,ii_end,ii_fw,bg_col,N_teams,ii,ii_show)
Line 326 ⟶ 396:
N_rows_res = 1
jjj = ii+1
result_local = Args[respre..'result'..ii] or ''
local cont_loop = true
while (jjj<=ii_end) and cont_loop do
Line 333 ⟶ 403:
new_res_ii = jjj
else
res_jjj = Args[respre..'result'..jjj] or ''
if result_local == res_jjj then
N_rows_res = N_rows_res+1
Line 352 ⟶ 422:
-- Get background colour
bg_col = nil
 
if Args['result'..ii] then
if Args[respre..'result'..ii] then
bg_col = result_col[Args['col_'..result_local]] or Args['col_'..result_local] or 'white'
bg_col = result_col[Args[respre..'col_'..result_local]] or Args[respre..'col_'..result_local] or 'inherit'
bg_col = 'background-color:'..bg_col..';' -- Full style tag
if bg_col == 'inherit' then bg_col = bg_col .. '; color: inherit'
else
bg_col = 'background-color:'..bg_col..';color:black;' -- Full style tag
end
end
if not bg_col then bg_col = 'background-color:transparent; color: inherit;' end -- Becomes default if undefined
-- Check for notes
local note_res_string, note_ref, note_text = '', '', ''
if Args['note_res_'..result_local] then
notes_exist = true
local note_res_local = Args['note_res_'..result_local]
if not Args['note_res_'..note_res_local] then
-- Split the note_res_local into a table if all the entries are valid
-- It does not point to another result note
local multiref = 1
note_ref = 'res_'..result_local
local note_res_local_table = mw.text.split(note_res_local, '%s*,%s*')
note_id = '"table_note_res_'..result_local..rand_val..'"' -- Identifier
if (#note_res_local_table > 1) then
note_text = note_res_local
for k, note_res_loc in ipairs(note_res_local_table) do
multiref = multiref * (Args['note_res_' .. note_res_loc] and 1 or 0)
end
else
multiref = 0
-- It does point to another result note
note_ref = 'res_'..note_res_local
note_id = '"table_note_res_'..note_res_local..rand_val..'"' -- Identifier
note_text = Args['note_res_'..note_res_local]
end
 
-- Check whether it is already printed
-- Split failed, so make a single entry table with hth_local inside
if not note_id_list[note_ref] then
--if Printmultiref it< 1 then
note_res_local_table = { note_res_local }
note_id_list[note_ref] = note_id
end
note_res_string = frame:expandTemplate{ title = 'efn', args = { group='Table_notes', name=note_id, note_text} }
 
else
for k,note_res_local in ipairs(note_res_local_table) do
-- Refer to it
if not Args['note_res_'..note_res_local] then
note_res_string = frame:extensionTag{ name = 'ref', args = { group = 'lower-alpha', name = note_id} }
-- It does not point to another result note
note_ref = respre..'res_'..result_local
note_id = '"table_note_res_'..result_local..rand_val..'"' -- Identifier
note_text = note_res_local
else
-- It does point to another result note
note_ref = respre..'res_'..note_res_local
note_id = '"table_note_res_'..note_res_local..rand_val..'"' -- Identifier
note_text = Args['note_res_'..note_res_local]
end
-- Check whether it is already printed
if not note_id_list[note_ref] then
-- Print it
note_id_list[note_ref] = note_id
note_res_string = note_res_string .. frame:expandTemplate{ title = 'efn', args = { group='lower-alpha', name=note_id, note_text} }
else
-- Refer to it
note_res_string = note_res_string .. frame:extensionTag{ name = 'ref', args = { group = 'lower-alpha', name = note_id} }
end
end
else
note_res_string = ''
end
-- Get text
local text_result = Args[respre..'text_'..result_local] or ''
if text_result:match('fbmulticomp') then
ii_fw = 'padding:0;' .. ii_fw
if text_result:match('fbmulticompefn') then
notes_exist = true
end
end
text_field_result = '| style="'..ii_fw..bg_col..'" rowspan="'..tostring(N_rows_res)..'" |'..text_result..note_res_string..'\n'
-- See whether it is needed (only when blank for all entries)
Line 395 ⟶ 492:
-- Insert match row if needed
if match_table then
local legs = tonumber(Args['legs']) or 1
-- Add empty cell
table.insert(t,'| style="background-color:white;color:black;border-top:white;border-bottom:white;"| \n')
-- Now include note to match results if needed
Line 404 ⟶ 502:
-- Nothing
else
for l=1,legs do
local match_note = Args['match_'..team_code_ii..'_'..team_code_jj..'_note']
local m = (legs == 1) and 'match_' or 'match' .. l .. '_'
if match_note then
local match_note = Args[m..team_code_ii..'_'..team_code_jj..'_note']
notes_exist = true
--if Onlymatch_note when it existthen
notes_exist = true
-- First check for existence of reference for note
-- Only when it exist
if not Args['note_'..match_note] then
-- It'sFirst thecheck entryfor existence of reference for note
if not (Args['note_'..match_note] or Args[m..match_note..'_note']) then
note_string = frame:expandTemplate{ title = 'efn', args = { group='Table_notes', match_note} }
else -- It's the entry
note_id = '"table_note_'..team_code_ii..'_'..team_code_jj..rand_val..'"' -- Add random end for unique ID if more tables are present on article (which might otherwise share an ID)
-- Check for existence elsewhere
note_id_list[team_code_ii..'_'..team_code_jj] = note_id
note_local_num = team_list[match_note]
if note_id_list[match_note] or ((note_local_num >= ii_start) and (note_local_num <= ii_end)) then
note_string = frame:expandTemplate{ title = 'efn', args = { group='lower-alpha', name=note_id, match_note} }
-- It exists
else
note_string = frame:extensionTag{ name = 'ref', args = { group = 'lower-alpha', name = note_id} }
-- Check for existence elsewhere
else
note_local_num = team_list[match_note] or ii_end + 1
-- Now define the identifier for this
if note_id_list[match_note] or ((note_local_num >= ii_start) and (note_local_num <= ii_end)) then
note_id = '"table_note_'..match_note..rand_val..'"' -- Add random end for unique ID
note_id_list[match_note] -- =It note_idexists
note_id = '"table_note_'..match_note..rand_val..'"' -- Identifier
-- Call refn template
note_string = frame:expandTemplateextensionTag{ titlename = 'efnref', args = { group = 'Table_noteslower-alpha', name =note_id, Args['note_'..match_note]note_id} }
else
-- Now define the identifier for this
note_id = '"table_note_'..match_note..rand_val..'"' -- Add random end for unique ID
note_id_list[match_note] = note_id
-- Call refn template
note_string = frame:expandTemplate{ title = 'efn', args = { group='lower-alpha', name=note_id, Args['note_'..match_note]} }
end
end
 
-- Now append this to the match result string
Args[m..team_code_ii..'_'..team_code_jj] = (Args[m..team_code_ii..'_'..team_code_jj] or '')..note_string
end
 
-- Now append this to the match result string
Args['match_'..team_code_ii..'_'..team_code_jj] = Args['match_'..team_code_ii..'_'..team_code_jj]..note_string
end
end
Line 434 ⟶ 539:
-- Add rest of match row
t = p_matches.row(t,Args,N_teams,team_list,ii,ii_show,legs)
end
Line 446 ⟶ 551:
-- Close table
table.insert(t, '|}\n')
-- Get info for footer
local update = Args['update'] or 'unknown'
local start_date = Args['start_date'] or 'unknown'
local source = Args['source'] or (title_source == true and '')
or frame:expandTemplate{ title = 'citation needed', args = { reason='No source parameter defined', date=os.date('%B %Y') } }
local class_rules = Args['class_rules'] or nil
Line 457 ⟶ 563:
-- Date updating
local matches_text = Args['matches_text'] or 'match(es)'
if string.lower(update)=='complete' or hide_footer then
-- Do nothing
elseif update=='' then
Line 464 ⟶ 570:
elseif string.lower(update)=='future' then
-- Future start date
table.insert(t_footer,'First '..matches_text..' will be played on: '..start_date..'. ')
else
table.insert(t_footer,'Updated to '..matches_text..' played on '..update..'. ')
end
table.insert(t_footer,'Source: '..source)
-- Stack footer or not
if class_rules and full_table and (not hide_class_rules) then
local footer_break = yesno(Args['stack_footer'] or 'no') and true or false
table.insert(t_footer,'<br>Rules for classification: '..class_rules)
-- Variable for linebreak
local stack_string = '<br>'
if footer_break and (not (string.lower(update)=='complete')) and not hide_footer then table.insert(t_footer,stack_string) end
if source ~= '' and not hide_footer then
table.insert(t_footer,'Source: '..source)
end
if class_rules and full_table and show_class_rules and not hide_footer then
if (#t_footer > 0) then table.insert(t_footer,'<br>') end
table.insert(t_footer,'Rules for classification: '..class_rules)
end
-- Now for the named status
local status_exist = false
Line 479 ⟶ 598:
curr_letter = mw.ustring.upper(mw.ustring.sub(t_status.letters,jjj,jjj))
if t_status.called[curr_letter] then
if (footer_break and status_exist) then
status_string = status_string..stack_string
end
if t_status.position == 'before' then
status_string = status_string..'<span style="font-weight:bold">'..string.lower(curr_letter)..' &ndash;</span> '..t_status.code[curr_letter]..'; '
Line 487 ⟶ 609:
end
end
-- Now end it with a point instead (if it contains entries the '; ' needs to be removed)
if status_exist and not hide_footer then
if (#t_footer > 0) then table.insert(t_footer,'<br>') end
status_string = '<br>'..mw.ustring.sub(status_string,1,mw.ustring.len(status_string)-2)..'.'
status_string = mw.ustring.sub(status_string,1,mw.ustring.len(status_string)-2)
table.insert(t_footer,status_string)
end
-- Add notes (if applicable)
if notes_exist then
if (#t_footer > 0) then table.insert(t_footer,'<br>Notes:') end
table.insert(t_footer,'Notes:')
-- As reflist size text
t_footer = '<div class="reflistsports-table-notes">'..table.concat(t_footer)..'</div>'
t_footer = t_footer..frame:expandTemplate{ title = 'notelist', args = { group='Table_noteslower-alpha'} }
else
-- As reflist size text
t_footer = '<div class="reflistsports-table-notes">'..table.concat(t_footer)..'</div>'
end
-- Add footer to main text table
table.insert(t,t_footer)
-- Rewrite anchor links
return table.concat(t)
for k=1,#t do
if t[k]:match('%[%[#[^%[%]]*%|') then
t[k] = mw.ustring.gsub(t[k], '(%[%[)(#[^%[%]]*%|)', '%1' .. baselink .. '%2')
end
end
-- Generate tracking
if not Args['notracking'] then
local getTracking = require('Module:Sports table/argcheck').check
local warning_categories, tracking_categories = getTracking(Args, frame:getParent().args)
if #warning_categories > 0 then
if frame:preprocess( "{{REVISIONID}}" ) == "" then
for k=1,#warning_categories do
warning_categories[k] = mw.ustring.gsub(warning_categories[k], '^%[%[Category:Pages using sports table with (.*)|(.*)%]%]$', '<div style="color:red">Warning: %1 = %2</div>')
end
end
end
for k=1,#warning_categories do
table.insert(t, warning_categories[k])
end
for k=1,#tracking_categories do
table.insert(t, tracking_categories[k])
end
if(Args['showteam'] == nil) then
local getWarnings = require('Module:Sports table/totalscheck').check
local total_warnings = getWarnings(Args, team_list, ii_start, ii_end)
if #total_warnings > 0 then
if frame:preprocess( "{{REVISIONID}}" ) == "" then
for k=1,#total_warnings do
table.insert(t, '<div style="color:green">Possible problem: ' .. total_warnings[k] .. '</div>')
end
end
end
end
else
table.insert(t, '[[Category:Pages using sports table with notracking]]')
end
if Args['float'] then
return frame:expandTemplate{ title = 'stack begin', args = {clear = 'true', margin = '1', float = Args['float']} }
.. templatestyles .. '\n' .. table.concat(t) .. frame:expandTemplate{ title = 'stack end'}
end
 
return templatestyles .. '\n' .. table.concat(t)
end