Saltar para o conteúdo

Módulo:Cycling race

Permanently protected module
Origem: Wikipédia, a enciclopédia livre.

local p = {}
local wiki = string.match(mw.site.server, "%a+")
if wiki == "www" then
    wiki = "fr"
end
--import translation
local l10n = mw.loadData("Module:Cycling race/l10n")

--import data
local data = mw.loadData("Module:Cycling race/data")

local contentLanguage = mw.getContentLanguage()
local wikilang = contentLanguage:getCode()
local wikibase = mw.wikibase
local localframe  -- Value may be given by functions which use frame functions like getReference
local arwiki_totemplate = mw.getCurrentFrame():getParent().args["totemplate"] or mw.getCurrentFrame().args["totemplate"]
arwiki_totemplate = (wiki == "ar" and arwiki_totemplate and arwiki_totemplate ~= "") or false
-- == Structure of the code ==
-- I) Constant
-- II) Translation
-- III) Basic functions
-- IV) Functions less basic called from other functions
-----A) Time functions
-----B) Link functions
-----C) Functions for the output, like table
-----D) Jersey, flag functions
-----E) Other (winner)
-- V) Main functions
----- A) Function race reference
----- B) Calendar
----- C) Victory
----- Cbis) Function for infobox
----- D) Stage infobox
----- E) List of teams
----- F) Classifications
----- G) Infobox
----- H) Race infobox
----- I) Team roster
----- J) Function list of winners (palmarès)
----- K) List of stages
----- L) List of stages classification
----- M) Start list
----- N) Rider ranking
----- O) Rider infobox
----- P) Team infobox
-- ..................
----- Z) Miscellaneous / Other / Tests

--Tip: search "--==" to navigate between the sections

--== I) Classes declared as global ==

--dictionnaries
local teamCats=data.teamCats
local class_sort=data.class_sort
local class_dic=data.class_dic
local UCI_Circuits=data.UCI_Circuits
local stages=data.stages
local bg_color_table = data.bg_color_table
local NationalRoadCyclingChampionships=data.NationalRoadCyclingChampionships
local NationalITTCyclingChampionships=data.NationalITTCyclingChampionships
local womenNcRoadtable=data.womenNcRoadtable
local womenNcITTtable=data.womenNcITTtable
local menNcRoadtable=data.menNcRoadtable
local menNcITTtable=data.menNcITTtable
local amateurcat=data.amateurcat
local nationalcat=data.nationalcat

local available, translations = pcall(require, "Module:Cycling race/lang")
local available_list = available and type(translations.list) == "function"
local available_lang_priority = available == true and type(translations.lang_priority) == "table"

local textalign = "left"
local floattable = "left"
local floatinfobox = "right"
if wiki == "ar" or wiki == "fa" or wiki == "ur" or wiki == "he" then
    textalign = "right"
    floattable = "right"
    floatinfobox = "left"
end

local Wikidatalogosize = "12px"
if wiki == "ar" then Wikidatalogosize = "15px" end
local standardtablecss=data.standardtablecss_part1..textalign..data.standardtablecss_part2

local no_country_calendar={'ru','ar'}
local no_country_victories={'ru','ar'}
local no_country_classification={'es','da','no','ru','ar'}
local no_roll_startlist={'fr','da','no','ar','ru','de'}
local display_language_in_riderinfobox={'ru'}
local display_flag_in_riderinfobox={'ru'}
local display_birthnameastitle_in_riderinfobox={'ru'}
local display_noweight_in_riderinfobox={'fr','pl'}
local display_noage_in_riderinfobox={'pl'}

local silver_theme_countries={'da'}

local backgroundColor="#FFDF80"
local backgroundColorLight="#FFF7DF"
for _, value in pairs(silver_theme_countries) do -- get data if country should be printed in this wiki
    if value == wiki then
        backgroundColor="#EAECF0"
        backgroundColorLight="#EFEFEF"
    end
end

local function istrue(x)
    if x and (x == 1 or x == "1" or x == "true") then return true end
    return nil
end

--== II) Translation ==
local function translate(func_name_short, index, womenrace_bool, title)
    if index == nil then index=1 end -- for prologue
    if index==1000 then --code for some custom function
        return title
    else
        if func_name_short then
            local func_name
            if womenrace_bool then
                func_name=func_name_short.."_women_translate"
                if l10n[func_name] and l10n[func_name][index] then
                    return l10n[func_name][index]
                end
            end
            func_name=func_name_short.."_translate"
            if l10n[func_name][index] then
                return l10n[func_name][index]
            end
            return "translation for "..func_name.." index ".. tostring(index).." not found"
        else
            return "error: no func_name found"
        end
    end
end

function plural(num)
    local plural=false --latin language
    local gen_singular=false --for slavic language
    local gen_plural=false --for slavic language
    if num then
        if num > 1 then
            plural=true
            if num < 5 then -- 2, 3 and 4
                gen_singular = true
            elseif num > 20 then
                local modulo = math.fmod( num, 10)
                --modulo==1 --> nothing, it is singular
                if modulo>1 and modulo<5 then
                    gen_singular = true
                elseif modulo>4 then
                    gen_plural=true
                end
            else
                gen_plural=true
            end
        end
    end
    
    return plural, gen_singular, gen_plural
end

function black_list( Label)
    local black_list=l10n.black_list
    --[[ List of Wikipedia articles with the same lemma as the non existing rider article. Those lemmas are printed
        as text "black" in the tables, not "blue" or "red". This way there will be no false wikilinks at the WhatLinksHere entry.
        List should be updated maybe once a year. ]]
    return black_list[Label]
end

local function stageLink(x, a, b) -- x= 10a: a = 10, b = a. x = 5: a = 5, b = ""
    local l10nDef = {["fr"]="étape", ["en"]="stage", ["ar"]="المرحلة", ["br"]="Tennad", ["ca"]="etapa", ["cs"]="etapa", ["de"]="Etappe", ["da"]="etape", ["eo"]="Etapo",
    ["es"]="etapa", ["eu"]="Etapa", ["fi"]="Etappi", ["fo"]="teinur", ["hu"]="szakasz", ["it"]="Tappa", ["ja"]="ステージ", ["la"]="Statio", ["lb"]="Etapp",
    ["lv"]="Posms", ["mk"]="Етапа", ["nl"]="Etappe", ["no"]="etappe", ["pl"]="Etap", ["pt"]="Etapa", ["ro"]="Etapa", ["ru"]="Этап", ["sk"]="Etapa",
    ["sv"]="Etapp", ["ast"]="etapa" }

    local word1, word2
    word2=l10nDef[wiki]
    if word2 == nil then word2=l10nDef["en"] end -- if no translation, show en translation
    local word = word2

    if wiki=="ar" then return word2 .. " " .. ( a or "" ) , "#" .. word2 .. " " .. ( a or "" ) end

-- fr: {{1re}} étape, {{2e}} étape
    if wiki=="fr" then
        if b == "" then -- series_ordinal without character
            if a == "1" then word1 = "1<sup>re</sup> "..word else word1 = a.."<sup>e</sup> "..word end -- table text = {{1re}} étape, {{2ae}} étape,
            if a == "1" then word2 = "#1re "..word else word2 = "#"..a.."e  "..word end --text of section header = #1re étape, #2e étape
            return word1, word2
        end
        if b ~= "" then -- series_ordinal with character: instead of eg "1a re" it is "1re a"
            if a == "1" then word1 = "1<sup>re</sup> "..b.." "..word else word1 = a.."<sup>e</sup> "..b.." "..word end -- table text = {{1re}} étape, {{2ae}} étape,
            if a == "1" then word2 = "#1re "..b.." "..word else word2 = "#"..a.."e"..b.." "..word end --text of section header = #1re étape, #2e étape
            return word1, word2
        end
    end
    if wiki=="hu" then
        if b == "" then return a..". "..word, "#"..a..". "..word
        else return a..b.." "..word, "#"..a..b.." "..word end
    end
    if wiki=="de" or wiki=="da" or wiki=="fo" or wiki=="lb" or wiki=="no" then return a..". "..b.." "..word, "#"..a..". "..b.." "..word end
    if wiki=="ca" then return a.."a "..b.." "..word, "#"..a..". "..b.." "..word end
    if wiki=="es" then return a..".ª "..word.." "..b, "#"..a..".ª "..word.." "..b end
    if wiki=="ast" then
        if b == "" then -- series_ordinal without character
            if a == "1" or a == "3" then word1 = a.."ᵉʳ "..word else word1 = a.."ª "..word end -- table text = 1ᵉʳ etapa, 2ª etapa, 3ᵉʳ etapa,
            if a == "1" or a == "3" then word2 = "#"..a.."ᵉʳ "..word else word2 = "#"..a.."ª  "..word end --text of section header = #1ᵉʳ etapa, #2ª etapa, #3ᵉʳ etapa
            return word1, word2
        end
        if b ~= "" then -- series_ordinal with character: instead of eg "1a re" it is "1re a"
            if a == "1" or a == "3" then word1 = a.."ᵉʳ "..b.." "..word else word1 = a.."ª "..b.." "..word end -- table text = {{1ᵉʳ}} etapa, {{2ª}} etapa,
            if a == "1" or a == "3" then word2 = "#"..a.."ᵉʳ "..b.." "..word else word2 = "#"..a.."ª"..b.." "..word end --text of section header = #1ᵉʳ etapa, #2ª etapa
            return word1, word2
        end
    end

    -- default
    word1 = x                  -- table text = 1, 2a, 3
    word2 = "#"..word.." ".. x -- text of section header = #Etappe 2a, #Stage 4
    return word1, word2
end

local function typeofstage(x, typ, noborder)
    -- plain, hilly, inter, ... must be "" or "any text"
    -- l10nDef[""] = {plain = "", hilly="", inter='', mount='', time_prologue='', time_team='', time_indiv='', uphill='', rest=''}
    local l10nDef = {
        ["ar"] = {plain = "مرحلة مستوية", hilly="مرحلة التلال", inter='مرحلة متوسطة', mount='مرحلة جبلية', time_prologue='مرحلة سباق زمني', time_team='مرحلة سباق الزمن للفرق', time_indiv='مرحلة سباق الزمن فردي', uphill='مرحلة تسلق الجبل زمني', rest='يوم راحة'},
        ["ast"] = {plain = "etapa llana", hilly="etapa escarpada", inter='etapa de mediu monte', mount='etapa de monte', time_prologue='prólogu', time_team='contrarreló per equipos', time_indiv='contrarreló individual', uphill='cronoescalada', rest='xornada de descansu'},
        ["fr"] = {plain = "étape de plaine", hilly="étape vallonnée", inter='étape de moyenne montagne', mount='étape de montagne', time_prologue='prologue', time_team='contre-la-montre par équipes', time_indiv='contre-la-montre individuel', uphill='contre-la-montre en côte', rest='étape de repos'},
        ["en"] = {plain = "plain stage", hilly="hilly stage", inter='intermediate stage', mount='mountain stage', time_prologue='time trial stage', time_team='team time trial stage', time_indiv='individual time trial stage', uphill='uphill time trial stage', rest='rest day'},
        ["br"] = {plain = "tennad plaen", hilly="tennad digompez", inter='tennad damveneziek', mount='tennad meneziek', time_prologue='prolog', time_team='ABEU a-skipailhoù', time_indiv='ABEU', uphill='', rest='devezh diskuizh'},
        ["ca"] = {plain = "etapa plana", hilly="etapa accidentada", inter='etapa de mitja muntanya', mount='etapa de muntanya', time_prologue='pròleg', time_team='contrarellotge per equips', time_indiv='contrarellotge individual', uphill='', rest='etapa de descans'},
        ["cs"] = {plain = "rovinatá etapa", hilly="", inter='kopcovitá etapa', mount='horská etapa', time_prologue='prolog', time_team='týmová časovka', time_indiv='individuální časovka', uphill='', rest=''},
        ["da"] = {plain = "flad etape", hilly="kuperet etape", inter='middel bjergetape', mount='bjergetape', time_prologue='prolog', time_team='holdtidskørsel', time_indiv='enkeltstart', uphill='bjergenkeltstart', rest='hviledag'},
        ["de"] = {plain = "Flachetappe", hilly="Hügelige Etappe", inter='Mittelschwere Etappe', mount='Hochgebirgsetappe', time_prologue='Prolog', time_team='Teamzeitfahren', time_indiv='Einzelzeitfahren', uphill='Bergzeitfahren', rest='Ruhetag'},
        ["eo"] = {plain = "ebena etapo", hilly="malebena etapo", inter='mezgranda montaro etapo', mount='montara etapo', time_prologue='prologo', time_team='teama kontraux-la-kronometro', time_indiv='individua kontraux-la-kronometro', uphill='malebena kontraux-la-kronometro', rest='ripoza etapo'},
        ["es"] = {plain = "etapa llana", hilly="etapa escarpada", inter='etapa de media montaña', mount='etapa de montaña', time_prologue='prólogo', time_team='contrarreloj por equipos', time_indiv='contrarreloj individual', uphill='cronoescalada', rest='jornada de descanso'},
        ["eu"] = {plain = "etapa laua", hilly="etapa gorabeheratsua", inter='bitarteko etapa', mount='mendiko etapa', time_prologue='aitzinetapa', time_team='taldekako erlojupekoa', time_indiv='banakako erlojupekoa', uphill='erlojupeko igoera', rest='atseden eguna'},
        ["fi"] = {plain = "Tasamaaetappi", hilly="Mäkietappi", inter='Keskivaikea vuorietappi', mount='Vuorietappi', time_prologue='', time_team='Joukkueaika-ajo', time_indiv='Henkilökohtainen aika-ajo', uphill='mäkiaika-ajo', rest='välipäivä'},
        ["fo"] = {plain = "Slætt", hilly="Slætt við brúgvasteinum", inter='Óslætt', mount='Fjallateinur', time_prologue='Forteinur', time_team='Liðsúkkling', time_indiv='Einkultstartur', uphill='', rest='Hvílidagur'},
        ["hu"] = {plain = "sík szakasz", hilly="dombos szakasz", inter='közepes hegyi szakasz', mount='hegyi szakasz', time_prologue='prolog', time_team='csapat időfutam', time_indiv='egyéni időfutam', uphill='hegyi időfutam', rest=''},
        ["ja"] = {plain = "平坦ステージ", hilly="丘陵ステージ", inter='中間ステージ', mount='山岳ステージ', time_prologue='タイムトライアルステージ', time_team='チームタイムトライアルステージ', time_indiv='個人タイムトライアルステージ', uphill='アップヒルタイムトライアルステージ', rest='休養日'},
        ["lb"] = {plain = "Flaach Etapp", hilly="Hiwweleg Etapp", inter='Mëttelschwéier Etapp', mount='Biergetapp', time_prologue='Prolog', time_team='Contre-la-montre (Ekipp)', time_indiv='Contre-la-montre (Eenzel)', uphill='Biergcourse', rest='Roudag'},
        ["lv"] = {plain = "līdzenuma posms", hilly="paugurains posms", inter='vidēju kalnu posms', mount='kalnu posms', time_prologue='individuālais brauciens', time_team='komandu brauciens', time_indiv='individuālais brauciens', uphill='individuālais brauciens kalnā', rest='atpūtas diena'},
        ["mk"] = {plain = "рамна етапа", hilly="ридеста етапа", inter='среднопланинска етапа', mount='планинска етапа', time_prologue='пролог', time_team='екипен хронометар', time_indiv='индивидуален хронометар', uphill='', rest='ден за одмор'},
        ["nl"] = {plain = "vlakke rit", hilly="heuvelrit", inter='heuvelrit', mount='bergrit', time_prologue='proloog', time_team='ploegentijdrit', time_indiv='individuele tijdrit', uphill='klimtijdrit', rest='rustdag'},
        ["no"] = {plain = "flat etappe", hilly="kupert etappe", inter='middels klatreetappe', mount='klatreetappe', time_prologue='prolog', time_team='lagtempo', time_indiv='temporitt', uphill='klatretempoetappe', rest='hviledag'},
        ["pl"] = {plain = "płaski", hilly="pagórkowaty", inter='górzysty', mount='górski', time_prologue='prolog', time_team='jazda drużynowa na czas', time_indiv='jazda indywidualna na czas', uphill='jazda indywidualna na czas pod górę', rest='dzień przerwy'},
        ["pt"] = {plain = "etapa plana", hilly="etapa escarpada", inter='média montanha', mount='alta montanha', time_prologue='prólogo', time_team='contrarrelógio por equipes', time_indiv='contrarrelógio individual', uphill='cronoescalada', rest='jornada de descanso'},
        ["ro"] = {plain = "etapă de plat", hilly="etapă valonată", inter='etapă intermediară', mount='etapă de munte', time_prologue='prolog', time_team='contratimp pe echipe', time_indiv='contratimp individual', uphill='', rest='zi de repaus'},
        ["ru"] = {plain = "равнинный", hilly="холмистый", inter='среднегорный', mount='горный', time_prologue='пролог', time_team='командная разделка', time_indiv='индивидуальная разделка', uphill='горная разделка', rest='день отдыха'},
        ["sv"] = {plain = "Flack etapp", hilly="", inter='Kuperat', mount='Bergsetapp', time_prologue='Prolog', time_team='Lagtempoetapp', time_indiv='Tempoetapp', uphill='', rest='Vilodag'},
    }
    local l10n = l10nDef[wiki]
    if not l10n then l10n = l10nDef["en"] end  -- default

    local border
    if noborder then border="" else border="|border|right" end
    local stages = {
        ["plain stage"] = "[[File:Plainstage.svg"..border.."|20px|"..l10n.plain.."]]",
        ["hilly stage"] = "[[File:Hillystage.svg"..border.."|20px|"..l10n.hilly.."]]",
        ["intermediate stage"] = "[[File:Mediummountainstage.svg"..border.."|20px|"..l10n.inter.."]]",
        ["mountain stage"] = "[[File:Mountainstage.svg"..border.."|20px|"..l10n.mount.."]]",
        ["uphill time trial stage"] = "[[File:Mountain Time Trial Stage.svg"..border.."|20px|"..l10n.uphill.."]]",
        ["rest day"] = "[[File:Stage rest day.svg"..border.."|20px|"..l10n.rest.."]]"
    }
    if stages[x] then
        return stages[x]
    end
    if x=='time trial stage' then
        if noborder then border="" else border="|right" end
        if typ=="Q2348250" then return "[[File:Team Time Trial Stage.svg"..border.."|20px|"..l10n.time_team.."]]" end
        if typ=="Q2266066" then return "[[File:Time Trial.svg"..border.."|20px|"..l10n.time_indiv.."]]" end
        if typ=="Q485321"  then return "[[File:Time Trial.svg"..border.."|20px|"..l10n.time_prologue.."]]" end
    end
end

local function typeofstagelogo(stageID, noborder)
    local sType
    p = mw.wikibase.getBestStatements(stageID, 'P31') -- P31 is 'instance of'
    for _,t in pairs(p) do
        if t.mainsnak.snaktype == 'value' then
            local iOf = t.mainsnak.datavalue.value.id
            if iOf == "Q20646667" then sType = typeofstage('plain stage', nil, noborder) break end
            if iOf == "Q20646670" then sType = typeofstage('hilly stage', nil,noborder) break end
            if iOf == "Q20680270" then sType = typeofstage('intermediate stage', nil,noborder) break end
            if iOf == "Q20646668" then sType = typeofstage('mountain stage',nil, noborder) break end
            if iOf == "Q485321" then sType = typeofstage('time trial stage', "Q485321", noborder) break end -- prologue
            if iOf == "Q2266066" then sType = typeofstage('time trial stage', "Q2266066", noborder) break end -- individual time trial
            if iOf == "Q2348250" then sType = typeofstage('time trial stage', "Q2348250", noborder) break end -- team time trial
            if iOf == "Q20679712" then sType = typeofstage('uphill time trial stage', nil, noborder) break end
        end
    end
    return sType or ''
end

--== III) basic functions
--[[ Get any value for a property which is not deprecated ]]
local function firstValue(QID, PID, field)
    if QID then
        local ss = wikibase.getAllStatements(QID, PID)
        for _, s in pairs(ss) do
            if s.rank ~= 'deprecated' and s.mainsnak.snaktype == 'value' then
                return field and s.mainsnak.datavalue.value[field] or s.mainsnak.datavalue.value
            end
        end
    else
        return nil
    end
end

--[[ Go from season of a team to the team ]]
local function getParentID(teamID)
    return firstValue(teamID, 'P5138', 'id') -- P361 is 'part of'
        or firstValue(teamID, 'P361', 'id') -- P5138 is 'season of club or team'
end

--[[ Get a label in any of the languages in the fallback list of language codes ]]
local function getLabelFallback(itemID, fallback)
    local label
    for _, lang in ipairs(fallback) do
        label = mw.wikibase.getLabelByLang(itemID, lang)
        if label then break end
    end
    return label
end

--[[ Get a sitelink from the local wiki or from the fallback list of language codes ]]
local function getSitelinkFallback(itemID, fallback)
    local link = mw.wikibase.getSitelink(itemID)
    if link then return link end
    for _, lang in ipairs(fallback) do
        link = mw.wikibase.getSitelink(itemID, lang .. 'wiki')
        if link then return link end
    end
    return nil
end

local function make_IllWD2_link(q, arlabel, enlabel, text)
    local argse = { ["المعرف"] = q, target='en' }
    if arlabel and arlabel ~= '' then
        argse.label = arlabel
    elseif enlabel and enlabel ~= '' then
        argse.enlabel = enlabel
    end   
    if text and text ~= "" then
        argse.text = text
    end
    local final = mw.getCurrentFrame():expandTemplate{ title = 'Ill-WD2', args = argse }
    if arwiki_totemplate then
        final = "{{Ill-WD2"
        for k,v in pairs(argse) do
            final = final .. "|" .. k .. "=" .. v
        end
        final = final .. "}}"
    end
    return final
end

local function change_listofstages(tab, raceID, header, Id)
    -- code used in arwiki only
    return tab
end

--[[ Iterator to get all statements for an entity and property which are not deprecated and have a value]]
local function nextStatement(state, i)
    repeat
        i = i + 1
        local s = state[i]
        if s and s.rank ~= 'deprecated' and s.mainsnak.snaktype == 'value' then
            return i, s
        end
    until s == nil
end
local function statements(QID, PID)
    return nextStatement, wikibase.getAllStatements(QID, PID), 0
end

--[[ Iterator to get all qualifier values for a property for a statement]]
local function nextQualifier(state, i)
    repeat
        i = i + 1
        local q = state[i]
        if q and q.snaktype == 'value' then
            return i, q.datavalue
        end
    until q == nil
end
local function qualifiers(statement, PID)
    return nextQualifier, statement.qualifiers and statement.qualifiers[PID] or {}, 0
end

local function qualifieramount(element, property)
    local result
    for _, q in qualifiers(element, property) do
        result = tonumber(q.value.amount)
        break
    end
    return result
end

local function dispmoney(amount, unit)
    if amount and unit then
        local cost = contentLanguage:formatNum(tonumber(amount))
        if wiki == 'fo' then cost = string.gsub(cost, "%.", ",") end
        if unit == "http://www.wikidata.org/entity/Q4916" then
            cost = cost .. ' €'
        elseif unit == "http://www.wikidata.org/entity/Q4917" then
            cost = cost .. ' $'
        end
        return cost
    end
    return nil
end

--== IV) Functions less basic called from other functions ==
--=== A) Time functions ===
--[[ Get a Wikidata statement for an entity and property valid at the given timevalue ]]
local function checktime(s,q, time)
    local start, startPrecision, END, endPrecision
    if not q then
        return s
    end
    if q.P580 and q.P580[1] and q.P580[1].snaktype == 'value' then -- P580 is start time
        start = q.P580[1].datavalue.value.time
        startPrecision = q.P580[1].datavalue.value.precision
        if startPrecision == 9 then -- precision is years
            start = string.sub(start, 1, 5) -- Cut of everything after year
        elseif startPrecision == 10 then -- precision is months
            start = string.sub(start, 1, 8) -- Cut of everything after month
        end
    end
    if q.P582 and q.P582[1] and q.P582[1].snaktype == 'value' then -- P582 is end time
        END = q.P582[1].datavalue.value.time
        endPrecision = q.P582[1].datavalue.value.precision
    end
    if not start or start <= time then
        if not END then
            return s
        end
        if endPrecision == 9 then -- precision 9 is 'years'
            END = string.sub(END, 1, 6) .. '13' -- Set month to 13
        elseif endPrecision == 10 then -- precision 10 is 'months'
            END = string.sub(END, 1, 9) .. '32' -- Set day to 32
        end
        if END >= time then
            return s
        end
    end
    return nil
end

local function getStatementForTime(ID, property, time)
    local temp
    for _, s in statements(ID, property) do
        temp =checktime(s, s.qualifiers, time)
        if temp then return temp end
    end
    return nil
end

--Display date interval in a natural way:
--long:
--4-5 January 2020
--4 January - 2 February 2020
--4 January 2020 - 2 February 2021
--small is the same with short month names

--Does not work properly if the precision is not sufficient
local function getStartEndTime(sTime, eTime, mode)
    -- Note: Add the 4formats to "formats" and use funcDate
    local lang = contentLanguage
    local starttime, endtime
    
    local month_pl = {"stycznia", "lutego", "marca", "kwietnia", "maja", "czerwca", "lipca", "sierpnia",
    "września", "października", "listopada", "grudnia"}
    --local format = formats[wiki] or formats['']
    if mode==nil then mode='long' end
    -- Timevalues is like "+2015-07-04T00:00:00Z"
    local y, m = string.match(sTime, "(%d+)-(%d+)-%d+")
    local y2, m2 = string.match(eTime, "(%d+)-(%d+)-%d+")
    
    if m=='00' then --manage the 30 November issue
        if  mode=='long' then
            starttime =lang:formatDate( "Y", sTime )
        else
            starttime ='-'
        end
    else
        if y ~= y2 then
            if mode=='long' then
                starttime = lang:formatDate( "j F Y", sTime )
            else
                starttime = lang:formatDate( "j M Y", sTime )
            end
        elseif m ~= m2 then
            if mode=='long' then
                starttime = lang:formatDate( "j F", sTime )
            else
                starttime = lang:formatDate( "j M", sTime )
            end
        else
            starttime = lang:formatDate( "j", sTime )
        end
    
        if wiki == "ar" then
            if y ~= y2 then starttime = lang:formatDate( "d F Y", sTime )
            elseif m ~= m2 then starttime = lang:formatDate( "d F", sTime )
            else starttime = lang:formatDate( "d ", sTime ) end
        elseif wiki == "br" then
            if y ~= y2 then starttime = lang:formatDate( "j", sTime ) .." a viz ".. lang:formatDate( "F Y", sTime )
            elseif m ~= m2 then starttime = lang:formatDate( "j", sTime ) .." a viz ".. lang:formatDate( "F", sTime )
            else starttime = lang:formatDate( "j", sTime ) .." "
            end
        elseif wiki == "ca" or wiki == "es" or wiki == "ast" then
            if y ~= y2 then
                starttime = lang:formatDate( "j", sTime ) .." de ".. lang:formatDate( "F", sTime ) .." de ".. lang:formatDate( "Y", sTime )
            elseif m ~= m2 then
                starttime = lang:formatDate( "j", sTime ) .." de ".. lang:formatDate( "F", sTime )
            else starttime = lang:formatDate( "j", sTime ) .." "
            end
        elseif wiki == "cs" then
            if y ~= y2 then starttime = lang:formatDate( "j. xg Y", sTime )
            elseif m ~= m2 then starttime = lang:formatDate( "j. xg", sTime )
            else starttime = lang:formatDate( "j", sTime )
            end
        elseif wiki == "de" or wiki == "da" or wiki == "fo" or wiki == "lb" or wiki == "no" then
            if y ~= y2 then starttime = lang:formatDate( "j. F Y", sTime )
            elseif m ~= m2 then starttime = lang:formatDate( "j. F", sTime )
            else starttime = lang:formatDate( "j.", sTime )
            end
        elseif wiki == "fi" then
            if y ~= y2 then starttime = lang:formatDate( 'j. F"ta" Y', sTime )
            elseif m ~= m2 then starttime = lang:formatDate( 'j. F"ta"', sTime )
            else starttime = lang:formatDate( "j.", sTime )
            end
        elseif wiki == "eo" then
            if y ~= y2 then starttime = lang:formatDate( "j", sTime ) .."-a de ".. lang:formatDate( "F Y", sTime )
            elseif m ~= m2 then starttime = lang:formatDate( "j", sTime ) .."-a de ".. lang:formatDate( "F", sTime )
            else starttime = lang:formatDate( "j", sTime ) .."-a "
            end
        elseif wiki == "eu" then
            if y ~= y2 then starttime = lang:formatDate( "Y", sTime ) ..".eko ".. lang:formatDate( "F", sTime ) .."k ".. lang:formatDate( "j", sTime )
            elseif m ~= m2 then starttime = lang:formatDate( "F", sTime ) .."k ".. lang:formatDate( "j", sTime )
            else starttime = lang:formatDate( "F", sTime ) .."k ".. lang:formatDate( "j", sTime )
            end
        elseif wiki == "hu" then
            starttime = lang:formatDate( "Y. F j.", sTime)
        elseif wiki == "ja" then
            if y ~= y2 then starttime = lang:formatDate( "Y年m月d日", sTime )
            elseif m ~= m2 then starttime = lang:formatDate( "Y年m月d日", sTime )
            else starttime = lang:formatDate( "Y年m月d日", sTime )
            end
        elseif wiki == "lv" then
            if m ~= m2 then starttime = lang:formatDate( "Y. \\g\\a\\d\\a j. F", sTime )
            else starttime = lang:formatDate( "Y. \\g\\a\\d\\a j.", sTime )
            end
        elseif wiki == "pl" then
            if y ~= y2 then starttime = lang:formatDate( "j ", sTime ) .. month_pl[tonumber(lang:formatDate( "n", sTime ))] .. lang:formatDate( " Y", sTime )
            elseif m ~= m2 then starttime = lang:formatDate( "j ", sTime ) .. month_pl[tonumber(lang:formatDate( "n", sTime ))]
            else starttime = lang:formatDate( "j", sTime )
            end
        end
    end
    
    if m2=='00' then --manage the 30 November issue
        if  mode=='long' then
            endtime= lang:formatDate( "Y", eTime )
        else
            endtime= '-'
        end
    else
        if mode=='long' and y ~= y2 then
            endtime = lang:formatDate("j F Y", eTime)
        elseif y ~= y2 then --small
            endtime = lang:formatDate("j M Y", eTime)
        elseif mode=='long' then
            endtime = lang:formatDate("j F", eTime)
        else
            endtime = lang:formatDate("j M", eTime)   
        end
        if wiki == "ar" then
            if mode=='long' or y ~= y2 then endtime = lang:formatDate( "d F Y", eTime )
            elseif m ~= m2 then endtime = lang:formatDate( "d F Y", eTime )
            else endtime = lang:formatDate( "d F Y", eTime )
            end
        elseif wiki == "br" then endtime = lang:formatDate( "j", eTime ) .." a viz ".. lang:formatDate( "F Y", eTime )
        elseif wiki == "ca" or wiki == "es" or wiki == "ast" then
            if mode=='long' or y ~= y2 then
                endtime = lang:formatDate( "j", eTime ) .." de "..
                lang:formatDate( "F", eTime ) .." de ".. lang:formatDate( "Y", eTime )
            else
                endtime = lang:formatDate( "j", eTime ) .." de "..
                lang:formatDate( "F", eTime )
            end
        elseif wiki == "cs" then endtime = lang:formatDate( "j. xg Y", eTime )
        elseif wiki == "de" or wiki == "da" or wiki == "fi" or wiki == "fo" or wiki == "lb" or wiki == "no" then
            if mode=='long' or y ~= y2 then
                endtime = lang:formatDate( "j. F Y", eTime )
            else
                endtime = lang:formatDate( "j. M", eTime )
            end
        elseif wiki == "eo" then endtime = lang:formatDate( "j", eTime ) .."-a de ".. lang:formatDate( "F Y", eTime )
        elseif wiki == "eu" then endtime = lang:formatDate( "Y", eTime ) ..".eko ".. lang:formatDate( "F", eTime ) .."k "..
            lang:formatDate( "j", eTime )
        elseif wiki == "fi" then endtime = lang:formatDate('j F"ta" Y', eTime)
        elseif wiki == "hu" then
            if y ~= y2 then endtime = lang:formatDate( "Y. F j.", eTime )
            elseif m ~= m2 then endtime = lang:formatDate( "F j.", eTime )
            else endtime = lang:formatDate( "j.", eTime )
            end
            --endtime = lang:formatDate( "Y", eTime ) ..". ".. lang:formatDate( "F j", eTime ) .."."
        elseif wiki == "ja" then
            if y ~= y2 then endtime = lang:formatDate( "Y年m月d日", eTime )
            elseif m ~= m2 then endtime = lang:formatDate( "m月d日", eTime )
            else endtime = lang:formatDate( "d日", eTime )
            end
        elseif wiki == "lv" then
            if y ~= y2 then endtime = lang:formatDate( "Y. \\g\\a\\d\\a j. F", eTime )
            else endtime = lang:formatDate( "j. F", eTime )
            end
        elseif wiki == "pl" then
            endtime = lang:formatDate( "j ", eTime ) .. month_pl[tonumber(lang:formatDate( "n", eTime ))] ..
            lang:formatDate( " Y", eTime )
        end
    end
    return starttime, endtime
end

local formats = data.formats

-- Display the date, the mode changes the format
local function funcDate(date, mode)
    -- local date = '+2016-05-20'
    -- local mode = 'small'
    local format = formats[wiki] or formats['']
    local lang = contentLanguage
    
    local month_pl = {"stycznia", "lutego", "marca", "kwietnia", "maja", "czerwca", "lipca", "sierpnia",
    "września", "października", "listopada", "grudnia"}

    --handle problems with lack of precision
    local daycorrect=true
    local monthcorrect=true

    if string.sub(date,10,11)=='00' then daycorrect=false end
    if string.sub(date,7,8)=='00' then monthcorrect=false end

    if mode == 'Y' then
        return string.sub(date,2,5) --only year, note: contentLanguage:formatDate("Y", date) returns the wrong year
    end

    if daycorrect and monthcorrect then
        if wiki=="pl" then-- overcome declination issue
            return lang:formatDate( "j ", date ) .. month_pl[tonumber(lang:formatDate( "n", date ))] ..
            lang:formatDate( " Y", date )
        else
            return contentLanguage:formatDate(format[mode], date)
        end
    else
        if daycorrect or (monthcorrect==false and daycorrect==false) then --no month
            if mode=='long' then
                return string.sub(date,2,5) --only year, note: contentLanguage:formatDate("Y", date) returns the wrong year
            else --otherwise we don't know
                return '-'
            end
        else --month correct, but day incorrect
            if mode=='onlyday' then
                return '-'
            else
                local newdate=string.sub(date,1,9).."01"..string.sub(date,12)
                if wiki=="pl" then
                    return month_pl[tonumber(lang:formatDate( "n", date ))] ..lang:formatDate( " Y", date )
                else
                    return string.sub(contentLanguage:formatDate(format[mode], newdate), 3) --cut the day
                end
            end
        end
    end   
end

--[[ get the year for a race as a string, or an empty string]]
local function getYear(raceID)
    local year = firstValue(raceID, 'P580', 'time') or -- P580 is 'start time'
        firstValue(raceID, 'P585', 'time') -- P585 is 'point in time'
    if year then
        return string.sub(year, 2, 5)
    end
    return ''
end

function isdisqualified(p,q) --disqualification can use deprecated or P1534
    local cancelled=""
    local disqualified=false
    
    if p and p.rank=='deprecated' then
        cancelled='text-decoration:line-through;'
        disqualified=true
    else
        if q and q.P1534 and q.P1534[1].snaktype == 'value' then
            local tempdsq=q.P1534[1].datavalue.value.id
            if tempdsq=='Q1229261' then
                cancelled='text-decoration:line-through;'
                disqualified=true
            end --disqualified
        end
    end
    return cancelled, disqualified
end

--=== B) Link functions ===
local function getOfficialName(teamID, timeOfRace,season,strict) -- for team
    --return officialName, isLocal
    local strictLang = {mk = true}
    local cyrillic = {mk = true, ru = true}
    local strictLangBool= strictLang[wiki] or strict
    local correcttime, best, name, nametemp
    local wantedLanguages = {}
    
       if available_lang_priority then
        for i, lang in ipairs(translations.lang_priority) do --from Module:Cycling_race/lang
            wantedLanguages[lang] = i
        end
    end
    --case one, one official name / period overloaded with other languages as qualifier
    --for instance https://www.wikidata.org/wiki/Q195833
    for _, p1448 in statements(teamID, 'P1448') do
        correcttime=true
        if timeOfRace and timeOfRace ~='' then
            correcttime =checktime(p1448, p1448.qualifiers, timeOfRace)
        end
        if correcttime then
            if available_lang_priority and p1448.qualifiers and p1448.qualifiers.P1448 then
                local q = p1448.qualifiers.P1448
                best = 999
                for _, l in pairs(q) do
                    if l.snaktype == 'value' then
                        local lang = l.datavalue.value.language
                        if wantedLanguages[lang] and wantedLanguages[lang] < best then
                            best = wantedLanguages[lang]
                            name = l.datavalue.value.text
                        end
                    end
                end
                if name then return name, true end
            end
            --p1448 and correct time, look in the not qualifier part
       
            lang=p1448.mainsnak.datavalue.value.language
            best = 999
            if available_lang_priority and wantedLanguages[lang] and wantedLanguages[lang] < best then
                best = wantedLanguages[lang]
                name = p1448.mainsnak.datavalue.value.text
            elseif strictLangBool then
                if wiki==lang then
                    name = p1448.mainsnak.datavalue.value.text
                end
            else
                if cyrillic[lang]==nil then --don't display cyrillic for latin wiki
                    nametemp = p1448.mainsnak.datavalue.value.text
                end
            end
        end
    end
    if name then
        return name, true
    elseif not strictLangBool then
        return nametemp, false
    end

    --no official name, get label
    local label=wikibase.getLabel(teamID)
    if season and season==true then
        if label then return string.sub(label,1,label:len()-5),true end --
    else
        return label, true -- No official name, try label
    end
end

local function revertfirstlast(name)
    local nametable = mw.text.split(name, ",")
    if nametable[2] then --there is a coma
        return nametable[2].." "..nametable[1]
    else
        return nametable[1]
    end
end

-- RiderID --> RiderLink
local function getRiderLink(riderID, startOfSeason) --startOfSeason optional
    --Priority order
    --#1 P1813, short name, in correct alphabet, correct time
    --#2 P1448, official name, in correct alphabet, correct time
    --#3 sitelink (so label from wikipedia) in correct language
    --#4 label from wikidata in correct language
    --#5 label from wikidata in another language
    local strictLang = {mk = true, ru = true}
    local strictLangBool= strictLang[wiki]

    local sitelink = wikibase.getSitelink(riderID)
    local officialname,officialnametemp, language, name
    local correctlanguage=false
    local listOfProperty={'P1813','P1448'}

    for _, prop in ipairs(listOfProperty) do
        for _, p1813 in statements(riderID, prop) do
            if not officialname or not correctlanguage then
                language = p1813.mainsnak.datavalue.value.language
                officialnametemp = p1813.mainsnak.datavalue.value.text
                if strictLangBool then
                    if wiki==language then
                        name=officialnametemp --only exact language
                        correctlanguage=true
                    end
                else
                    if wiki==language then --exact language --> ok
                        name=officialnametemp
                        correctlanguage=true
                    elseif strictLang[language]==nil and not officialname then
                        --normally all "latin" languages use the same name, except for cyrillic translation
                        local russianLabel= wikibase.getLabelByLang(riderID, "ru")
                        if russianLabel then
                            local russianEnd=string.sub(russianLabel, -3)
                            if russianEnd~="вна" and russianEnd~="вич" then --otherwise rejected
                                name=officialnametemp
                                correctlanguage=false
                            end
                        else -- no russian label, it is most probably not a cyrillic translation
                            name=officialnametemp --any language latin
                            correctlanguage=false
                        end
                    end
                end
                if startOfSeason~= nil then
                    local q = p1813.qualifiers
                    if q then
                        local temp = checktime(name,q,startOfSeason)
                        if temp then officialname = name end--if the time is correct than it is finished
                    else
                        officialname = name
                    end
                else
                    officialname = name
                end
            end
        end
    end

    if sitelink and officialname then --if there is an official name, then use it
        return "[[" .. sitelink .. "|" ..officialname.."]]", correctlanguage
    else
        if officialname then return officialname end
        if sitelink then
            if wiki == "de" then
                local label = wikibase.getLabelByLang(riderID, wiki)
                if label then
                    local p27 = wikibase.getBestStatements(riderID, 'P27') -- P27 is country of citizenship
                    if p27[1] and p27[1].mainsnak.snaktype == 'value' then
                        local c = p27[1].mainsnak.datavalue.value.id
                        if c=="Q159" or c=="Q184" or c=="Q212" or c=="Q232" then -- Q159, Q184, Q212, Q232 is Russia, Belarus, Ukraine, Kazakhstan
                            return "[[" .. sitelink .. "|" .. label .. "]]", correctlanguage
                        end
                    end
                end
            end
            if wiki == 'ru' then
                local label = revertfirstlast(mw.text.trim(string.gsub(sitelink, "%b()", "")))
                return "[[" .. sitelink .. "|" .. label.. "]]", correctlanguage
            else
                return "[[" .. sitelink .. "|" .. mw.text.trim(string.gsub(sitelink, "%b()", "")) .. "]]", correctlanguage
            end
        end

        -- No WP article. Display label, and make it a red link if no other article uses the title
        local link
        local label = wikibase.getLabelByLang(riderID, wiki)
        if label then
            if wiki == 'ar' then
                link = make_IllWD2_link(riderID, label)
            else
                if wiki=='ru' then
                    label=revertfirstlast(label)
                end

                if black_list( label) then
                    link = label
                else
                    local title = mw.title.new(label)
                    if title and title.exists then
                        link = label
                    else
                        link = "[[" .. label.. "]]"
                    end
                end
            end
            return link, correctlanguage
        end

        -- No label in the local language. Try other languages, but don't link.
        correctlanguage=false
        if wiki == 'ar' then
            link = make_IllWD2_link(riderID)
        else
            link = getLabelFallback(riderID, {'en', 'de', 'fr','es'})
            if link then
                link = string.gsub(link, "%b()", "")
            else
                link = "(label missing)"
            end
        end
        return link, correctlanguage
    end
end

--[[ Get the name of a country ]]
local function getCountryName(countryID)
    local name = ''
    if available_list then
        name = translations.list(countryID)
    end
    if name == '' then
        local label, lang = wikibase.getLabelWithLang(countryID)
        --[[ Uses standard language fallback. Should not return nil, nil, as all countries have English labels. ]]
        if lang == wikilang then
            name = label
        elseif lang then
            name = label .. ' (' .. lang .. ')'
        end
    end
    return name
end

--[[ Get sitelink with no wiki no formating ]]
local function getRawTeamLink(teamID)
    local sitelink
    local parentID = getParentID(teamID)
    if parentID then -- try parent team first
        sitelink = mw.wikibase.getSitelink(parentID)
    end
    if not sitelink then
        sitelink = mw.wikibase.getSitelink(teamID)
    end
    return sitelink
end

--[[ Get sitelink, categoryID and maybe country for a team.
    Returns sitelink, team category ID, countryID (only countryID if country arg is true ]]
local function getTeamLinkCat(teamID, timeOfRace, country, season)
    local name, sitelink, parentID, catID
    local national_team_boolean=false
    -- Find team category
       
    --Hypothesis, it is a season, look in P2094
    for _, p2094 in statements(teamID, 'P2094') do
        if checktime(p2094, p2094.qualifiers, timeOfRace) then
            local natureID = p2094.mainsnak.datavalue.value.id
            if teamCats[natureID] then
                catID = natureID
                break
            end
        end
    end   
    
    -- Fallback with P31 (deprecated)
    for _, p31 in statements(teamID, 'P31') do
        if checktime(p31, p31.qualifiers, timeOfRace) then
            local natureID = p31.mainsnak.datavalue.value.id
            if teamCats[natureID] then
                catID = natureID
                break
            end
        end
    end

    --look by the parent, then P31 is used
    if not catID then
        parentID = getParentID(teamID)
        if parentID then
            local p31 = getStatementForTime(parentID, 'P31', timeOfRace)
            if p31 then catID = p31.mainsnak.datavalue.value.id end
        end
        catID = catID or 'Q53534649'
    end
    -- Find country if needed
    local countryID
    if country or catID == 'Q23726798' or catID == 'Q20738667' or catID == 'Q54555994' then
        local stm = getStatementForTime(teamID, 'P1532', timeOfRace) -- P1532 is country for sport
        if stm == nil then
            stm = getStatementForTime(teamID, 'P17', timeOfRace) -- P17 is country
        end
        if stm then countryID = stm.mainsnak.datavalue.value.id end
    end
    if countryID and (catID == 'Q23726798' or catID == 'Q20738667'
         or catID == 'Q54660600' or catID == 'Q54555994' or catID == 'Q99658502') then
        -- It is a national cycling team
        national_team_boolean=true
        if countryID=='Q145' then
            name = getCountryName('Q23666')
        else --to solve the United-Kingdom/Great Britain problem by national team
            name = getCountryName(countryID)
        end
        if catID == 'Q20738667' then -- national cycling team U23
            local s
            if wiki == 'fr' then s = ' espoirs'
            elseif wiki == 'mk' then s = ' под 23 години'
            elseif wiki == 'ar' then s = ' تحت 23'
            elseif wiki == 'es' then s = ' sub-23'
            else s = ' U23'
            end
            name = name .. s
        elseif catID == 'Q54555994' then -- national cycling team U19
            local s
            if wiki == 'fr' then s = ' juniors'
            elseif wiki == 'mk' then s = ' под 19 години'
            elseif wiki == 'ar' then s = ' تحت 19'
            elseif wiki == 'es' then s = ' sub-19'
            else s = ' U19'
            end
            name = name .. s
        elseif catID == 'Q99658502' then -- national cycling team "B"
            local s
            if wiki == 'fr' then s = ' "B"'
            elseif wiki == 'mk' then s = ' "B"'
            elseif wiki == 'ar' then s = ' "ب"'
            elseif wiki == 'es' then s = ' "B"'
            else s = ' "B"'
            end
            name = name .. s
        end   
        sitelink = getRawTeamLink(teamID)
    else
        -- It is not a national cycling team
        local isLocal
        if season and season == true then
            sitelink = wikibase.getSitelink(teamID)
            name, isLocal = getOfficialName(teamID, timeOfRace,true)
            if not sitelink then
                parentID = getParentID(teamID)
                if parentID then sitelink = wikibase.getSitelink(parentID) end
            end
        else
            parentID = getParentID(teamID)
            if parentID then -- try parent team first
                sitelink = wikibase.getSitelink(parentID)
                name, isLocal = getOfficialName(parentID, timeOfRace)
            end
            if not sitelink then
                sitelink = wikibase.getSitelink(teamID)
            end
        end
        
        if not name or (not isLocal and available_lang_priority) then
            local partName, partIsLocal = getOfficialName(teamID, timeOfRace)
            if partName and (not name or partIsLocal) then
                name = partName
            end
        end
    end
    if sitelink then
        if name then
            sitelink = '[[' .. sitelink .. '|' .. name .. ']]'
        else
            sitelink = '[[' .. sitelink .. ']]'
        end
    else
        if wiki == "ar" then
            local arlabel = mw.wikibase.getLabelByLang((parentID or ''), 'ar') or ""
            local texte = mw.wikibase.getLabelByLang(teamID, 'ar') or ""
            sitelink = make_IllWD2_link((parentID or teamID), arlabel, name, texte)
        else
            if name then
                sitelink = name
            else
                sitelink = (parentID and wikibase.getLabel(parentID)) or
                    wikibase.getLabel(teamID) or 'No name'
            end
        end
    end
    return sitelink, catID, countryID, national_team_boolean
end

local function getTeamCodeCat(teamID, timeOfRace)
    -- Find team category
    local codeUCI
    local p1998 =getStatementForTime(teamID, 'P1998', timeOfRace)
    if p1998 then
        codeUCI = p1998.mainsnak.datavalue.value
    else
        local parentID = getParentID(teamID)
        if parentID then
            p1998 =getStatementForTime(parentID, 'P1998', timeOfRace)
            if p1998 then
                codeUCI = p1998.mainsnak.datavalue.value
            end
        end
    end
    return codeUCI
end

local function getReference(statement, outputLocal)
    local function formatRefDate(date, precision)
        if precision == 9 then -- Precision is year
            return string.sub(date, 2, 5)
        elseif precision == 10 then -- Precision is month
            return contentLanguage:formatDate("F Y", string.sub(date, 2, 8))
        elseif precision >= 11 then -- Precision is day (or less)
            return funcDate(date, 'long')
        end
    end

    local ref = statement.references
    if not ref or not ref[1] then
        return nil
    end
    ref = ref[1].snaks
    if ref.P854 and ref.P854[1] and ref.P854[1].snaktype == 'value' then -- P854 is 'reference URL'
        local refURL = ref.P854[1].datavalue.value
        local refTitle = ''
        local refDate = ''
        local refRetrieved = ''
        local refLang = ''
        if ref.P1476 and ref.P1476[1] and ref.P1476[1].snaktype == 'value' then -- P1476 is 'title URL'
            refTitle = ref.P1476[1].datavalue.value.text
            local lang = ref.P1476[1].datavalue.value.language
            if lang ~= wikilang then
                refLang = '(' .. lang .. ')'
                if wiki == 'ar' then refLang = lang end
            end
        end
        if ref.P577 and ref.P577[1] and ref.P577[1].snaktype == 'value' then -- P577 is 'publication date'
            local value = ref.P577[1].datavalue.value
            refDate = formatRefDate(value.time, value.precision)
            if (wiki == 'ar') then refDate = refDate
            else refDate = ', ' .. refDate
            end
        end
        if ref.P813 and ref.P813[1] and ref.P813[1].snaktype == 'value' then -- P813 is 'retrieved'
            local value = ref.P813[1].datavalue.value
            refRetrieved = formatRefDate(value.time, value.precision)
            if wiki == "de" then
                refRetrieved = ", (abgerufen am " .. refRetrieved .. ')'
            elseif wiki == "ar" then
                refRetrieved = refRetrieved
            elseif wiki == "fr" then
                refRetrieved = " (consulté le " .. refRetrieved .. ')'
            elseif wiki == "da" then
                refRetrieved = " Hentet " .. refRetrieved .. '.'
            else
                refRetrieved = " Retrieved " .. refRetrieved .. '.'
            end
        end
        local domain = string.match(refURL, '//([^/]+)')
        if string.sub(domain, 1, 4) == 'www.' then
            domain = string.sub(domain, 5)
        end
        local refText
        if wiki == "ar" then
            refText = '{{web cite' ..
            '|url = ' .. refURL ..
            '|title= ' .. refTitle ..
            '|lang = ' .. refLang ..
            '|website=' .. domain ..
            '|date=' .. refDate ..
            '|accessdate=' .. refRetrieved ..
            '}}'
        elseif wiki == "fr" then
                -- fr: "(en) « Lloyd Mondory ... EPO », sur velonews.competitor.com (consulté le 30 april 2016), 30 octobre 2015."
            local sur = ', sur <span style="font-style:italic;"> ' .. domain .. '</span>'
            refText = refLang .. ' « ['.. refURL .. ' '.. refTitle .. '] »' .. sur .. refRetrieved .. refDate .. '.'
        elseif wiki == "de" then
            local In = ' In: <span style="font-style:italic;">' .. domain .. '</span>'
            refText = '<span style="font-style:italic;">['.. refURL.. ' '.. refTitle.. '.]</span> ' ..
                In .. refDate .. refRetrieved ..'.'
        else
            local at = ', <span style="font-style:italic;"> ' .. domain .. '</span>'
            refText = refLang .. ' [' .. refURL .. ' ' .. refTitle .. ']' .. at .. refDate .. '.' .. refRetrieved
        end
        if outputLocal==1 then
            return refText
        else
            local refargs = {}
            if wiki ~= "ar" then
                refargs.name = refText
            end
            return localframe:extensionTag('ref', refText, refargs)
        end
    end
end

local function getImageOrMap(QID, PID)
    local p18 = wikibase.getBestStatements(QID, PID) -- P18 is 'image'
    local first
    for _, image in pairs(p18) do
        if image.mainsnak.snaktype == 'value' then
            if not first then
                first = image.mainsnak.datavalue.value
            end
            local q = image.qualifiers
            if q and q.P2096 then
                for _, caption in pairs(q.P2096) do -- P2096 is 'caption'
                    if caption.snaktype == 'value' and caption.datavalue.value.language == wikilang then
                        return image.mainsnak.datavalue.value, caption.datavalue.value.text
                    end
                end
            end
        end
    end
    return first
end

local function getLogo(QID)
    return getImageOrMap(QID, 'P154')
end

local function getImage(QID)
    return getImageOrMap(QID, 'P18')
end

local function getMap(QID)
    return getImageOrMap(QID, 'P242')
end

local function getSectionalView(QID)
    return getImageOrMap(QID, 'P2713')
end

--[[ Get link for race or competition]]
local function raceLink(QID)
    local sitelink = wikibase.getSitelink(QID)
    local instanceOf = firstValue(QID, 'P3450', 'id')
    if instanceOf == nil then
        instanceOf = firstValue(QID, 'P31', 'id') -- P31 is 'instance of'
    end
    if instanceOf == 'Q1137352' then -- Q1137352 is 'French Road Cycling Cup'
        local label2 = wikibase.getLabel(instanceOf)
        if sitelink then
            if label2 then return '[[' .. sitelink .. '|' .. label2 .. ']]' end
            return '[[' .. sitelink .. ']]'
        end
        local sitelink2 = wikibase.getSitelink(instanceOf)
        if sitelink2 then return '[[' .. sitelink2 ..'|' .. string.gsub(sitelink2, " %b()", "") .. ']]' end
        if label2 then return label2 end
    end
    if sitelink then return "[[".. sitelink.. "]]" end
    return wikibase.getLabel(QID) or ''
end

local function getPlaceLink(placeID,timeOfRace)
    local sitelink = wikibase.getSitelink(placeID)
    local name

    if available_list then
        name = translations.list(placeID) --return '' if nothing
    end
    if name==nil or name=='' then
        name=getOfficialName(placeID, timeOfRace,nil,true) --name should be in the right language
    end
    if sitelink then
        -- Delete " (...)" form e.g. "Unley (South Australia)"
        if name ~=nil then
            return '[[' .. sitelink .. '|' .. name .. ']]'
        else
            return '[[' .. sitelink .. '|' .. string.gsub(sitelink, ' %b()', '') .. ']]'
        end
    end
    local label = wikibase.getLabel(placeID) or ''
    if wiki == 'ar' then
        arlabel = wikibase.getLabelByLang(placeID, "ar")
        return make_IllWD2_link(placeID, arlabel)
    end
    return contentLanguage:ucfirst(label)
end   

-- ClassID --> ClassLink
-- some WPs use a unique article for this case
local function classLinkFn(class, circuitID)
    local link, label
    if wiki~="fr" then --not used
        link= wikibase.getSitelink('Q22348500') -- Q22348500 is 'cycling race class'
    elseif circuitID then
        link =wikibase.getSitelink(circuitID) --optional parameter to obtain [circuitlink|class label]
    end

    if class=="Q18536594" then
        if wiki=="fr" then
            label="JO"
        else
            label="OG"
        end
    else
         label = getLabelFallback(class, {wikilang, 'en', 'fr', 'de'})
    end

    if label and link then
        link = '[[' .. link .. '|' .. label .. ']]'
    elseif link then
        link = '[[' .. link .. ']]'
    elseif label then
        link = label
    else
        link=''
    end
    if wiki == "ar" then-- right now Q22348500 has no link in "ar"
        link = make_IllWD2_link(class , "", label)
    end
    return link
end
--[[ Get local content to a infoboxe from template args ]]
local function getLocalContent(contents, args)

    for _, content in pairs(contents) do
        local name = content.name
        if not name then  error('translation missing in Module:Cycling race/l10n of your wikipedia') end
        local nameNoShy = string.gsub(name, '&#173;', '')  -- filter soft hyphen out
        local nameNoShyLow, name_pluralNoShyLow
        if nameNoShy then
            nameNoShyLow = mw.ustring.lower(nameNoShy)
        end
        local name_plural = content.name_plural
        local name_pluralNoShy = name_plural and string.gsub(name_plural, '&#173;', '')  -- filter soft hyphen out
        if name_pluralNoShy then
            name_pluralNoShyLow = mw.ustring.lower(name_pluralNoShy)
        end
       
        if args[nameNoShy] and args[nameNoShy] ~= '' then
            if content.special then
                local newname, value = string.match(args[nameNoShy], '([^:]+):(.*)')
                if value and mw.text.trim(value) ~= '' then
                    content.name = mw.text.trim(newname)
                    content.content = mw.text.trim(value)
                end
            else
                content.content = mw.text.trim(args[nameNoShy])
            end
        elseif nameNoShyLow and args[nameNoShyLow] and args[nameNoShyLow] ~= '' then
            if content.special then
                local newname, value = string.match(args[nameNoShyLow], '([^:]+):(.*)')
                if value and mw.text.trim(value) ~= '' then
                    content.name = mw.text.trim(newname)
                    content.content = mw.text.trim(value)
                end
            else
                content.content = mw.text.trim(args[nameNoShyLow])
            end
        elseif args[name_pluralNoShy] and args[name_pluralNoShy] ~= '' then
            content.name = content.name_plural
            content.content = mw.text.trim(args[name_pluralNoShy])
        elseif name_pluralNoShyLow and args[name_pluralNoShyLow] and args[name_pluralNoShyLow] ~= '' then
            content.name = content.name_plural
            content.content = mw.text.trim(args[name_pluralNoShyLow])
        end
    end
end

local function checkDis(q)
    dis="road"
    if q and q.P642 and q.P642[1] and q.P642[1].snaktype == 'value' then
        if q.P642[1].datavalue.value.id == 'Q520611' or q.P642[1].datavalue.value.id =='Q1031445' then
            onlyRoad=false
            dis="mountainBike"
        elseif  q.P642[1].datavalue.value.id == 'Q335638' then
            onlyRoad=false
            dis="cycloCross"
        elseif q.P642[1].datavalue.value.id == 'Q221635' then
            onlyRoad=false
            dis="track"           
        end
    end
    return dis
end

-- Rider --> Team link
local function getTeam(riderID, timeOfRace, q)
    -- q: qualifiers of statement in race entity where the rider is the value
    local teamID, link, catID, countryID
    if q and q.P54 and q.P54[1].snaktype == 'value' then -- P54 is member of sports team
        teamID = q.P54[1].datavalue.value.id
        link, catID, countryID, national_team_boolean = getTeamLinkCat(teamID, timeOfRace)
    else
        for _, s in statements(riderID, 'P54') do
            if not link then --like a break
                p54 =checktime(s, s.qualifiers, timeOfRace)
                if p54 then
                    dis=checkDis(p54.qualifiers)
                    if dis=='road' then --by default
                        teamID = p54.mainsnak.datavalue.value.id
                        link, catID, countryID, national_team_boolean = getTeamLinkCat(teamID, timeOfRace)
                    end
                end
            end
        end
    end
    return link, teamID, catID, countryID, national_team_boolean
end

--RiderID --> UCI code
local function getTeamCode(riderID, timeOfRace, q)
    -- q: qualifiers of statement in race entity where the rider is the value
    local teamID, code
    if q and q.P54 and q.P54[1].snaktype == 'value' then -- P54 is member of sports team
        teamID = q.P54[1].datavalue.value.id
        code = getTeamCodeCat(teamID, timeOfRace)
    else
        local p54 = getStatementForTime(riderID, 'P54', timeOfRace)
        if p54 then
            teamID = p54.mainsnak.datavalue.value.id
            code= getTeamCodeCat(teamID, timeOfRace)
        end
    end
    return code
end

local function seasonToTeamID(teamID)
    if teamID then
        local parentID=getParentID(teamID)
        if parentID then--season was used
            return parentID
        else
            return teamID
        end
    else
        return nil
    end
end

local function wdLink(id)
    local text = "[[File:Wikidata-logo S.svg| " .. Wikidatalogosize .. "|link=d:" .. id .. "]]"     
    if arwiki_totemplate then
        text = '{{عدل في ويكي بيانات|type1=1|id=' .. id .. '}}'
    end
    return text
end

local function WPlinkpure(Qnumber)
    local link=''
    local Sitelink = wikibase.getSitelink(Qnumber) -- link to WParticle
    local Label = getLabelFallback(Qnumber, {wikilang, 'en', 'fr', 'de'}) or ''

    if Sitelink ~= nil then link = "[[" .. Sitelink .. "|" .. mw.text.trim(string.gsub(Sitelink, "%b()", "")..' ') .. "]]"
    elseif wiki == 'ar' then
        arlabel = mw.wikibase.getLabelByLang(Qnumber, 'ar') or ""
        link = make_IllWD2_link(Qnumber , arlabel , Label)
    else
        link = mw.ustring.gsub(Label, "^(%a)", function (x) return mw.ustring.upper(x) end)
    end

    return link
end

--=== C) Function for the output ===
local function getCountryBool(no_country_list)
    local country = true
    for _, value in pairs(no_country_list) do -- get data if country should be printed in this wiki
        if value == wiki then country = false end
    end
    return country
end

local function tableA(s)
    local error_message = ''
    if wiki == "ar" and s.item == "" or not s.item then return "" end
    if s.error_message == 1 then
        error_message = func_error_message( 1)
        error_message = mw.ustring.gsub(error_message, "<1>", s.property)
        error_message = mw.ustring.gsub(error_message, "<2>", mw.wikibase.label( s.item ))
        error_message = mw.ustring.gsub(error_message, "<3>", s.item)
        error_message = ' [[File:Exclam icon.svg|12px|'.. error_message .. ']]'
    end
    
    local table = mw.html.create('table')
        :addClass('sortable')
        :attr('cellpadding', '0')
        :attr('cellspacing', '0')
        :css('border' , '1px solid rgb(200,200,200)')
        :css('padding', '3px')
    
    local title =translate(s.header_function,s.header_1, s.womenrace_bool, s.title)
    if s.header_1 == 19 and wiki == "ar" then title = title .. " " .. s.year end   
       
    local wd_link = mw.html.create('span'):cssText('float:left; margin: 0 5px'):wikitext(wdLink(s.item..'#'..s.property))
    if arwiki_totemplate then wd_link = wdLink(s.item..'#'..s.property) end
    local caption = table:tag('tr'):tag('th'):attr('colspan', tostring(#s.header_2 + 1))
    :cssText('padding:2px; text-align:center; line-height: 1.8em;')
    :css('background-color',backgroundColor)
    caption:wikitext(tostring(wd_link)..' '..error_message ..' '..title)

    local country=getCountryBool(s.no_country)

    local header = table:tag('tr')
    for i,k in ipairs(s.header_2) do
        if i == s.country_column then
            if available_list and country == true then
                header:tag('th')
                    :cssText('text-align:center;padding:2px 20px 2px 2px;white-space:nowrap')
                    :wikitext(translate(s.header_function,k,s.womenrace_bool))
            end
        end
        if i ~= s.country_column then
            local column = header:tag('th')
                    :cssText('text-align:center;padding:2px 20px 2px 2px;white-space:nowrap')
                    :wikitext(translate(s.header_function,k,s.womenrace_bool))
            if s.data_sort_type[i] == 'unsortable' then
                column:addClass('unsortable')
            end
        end
    end

    return table
end

local function tableB(s) --for startlist
    local error_message = ''
    if wiki == "ar" and s.item == "" or not s.item then return "" end
    if s.error_message == 1 then
        error_message = func_error_message( 1)
        error_message = mw.ustring.gsub(error_message, "<1>", s.property)
        error_message = mw.ustring.gsub(error_message, "<2>", mw.wikibase.label( s.item ))
        error_message = mw.ustring.gsub(error_message, "<3>", s.item)
        error_message = ' [[File:Exclam icon.svg|12px|'.. error_message .. ']]'
    end
    local roll = true
    for _, value in pairs(s.no_roll_startlist) do -- get data if country should be printed in this wiki
        if value == wiki then roll = false end
    end

    local cssTable= "border: 1px solid rgb(200,200,200); margin: 0 0 0 0;"..
                "background-color: rgb(255, 255, 255); padding: 0px; float: left;"..
                "clear: left; ; text-align: left; vertical-align: top; font-size: 85%; line-height: 1.8em;"
    local wdlink_span = mw.html.create('span'):css('float',floattable):wikitext(wdLink(s.item.. '#'.. s.property)..' '..error_message)
    if arwiki_totemplate then wdlink_span = wdLink(s.item..'#'..s.property) end
    if roll == true then
        local rollTable1 = mw.html.create('div'):addClass("NavFrame")
        :cssText('center = margin: 0 0.5em 0;clear:both; border: 1px solid rgb(200,200,200);' ..
        'cellpadding="4" cellspacing="0" style="width:100%; background-color: rgb(255, 255, 255);padding: 5px;'..
        'margin-bottom:1em; background-color:'..backgroundColor..';')
        :attr('title','['..translate("startlist",14)..']/['..translate("startlist",15)..']')
        local tDiv= rollTable1:tag('div'):addClass("NavHead")
        :cssText('text-align:'.. textalign .." =;height:1.8em; color:black;font-weight:bold;")
        :css('background-color',backgroundColor)
        tDiv:tag('span')

        local tSpan=tDiv:wikitext(tostring(wdlink_span))
        tDiv:wikitext(translate("startlist",1))
       
        tDiv = rollTable1:tag('div'):addClass("NavContent"):cssText("margin:0; background:white; display:block; text-align:left;")
    
        local tTable= tDiv:tag('table'):cssText("border:1px solid rgb(200,200,200)")
        local tCell = tTable:tag('tr'):tag('td')
        local insideTable =tCell:tag('table'):attr('cellpadding','4')  -- cellspacing="0" style="width:100%;"  color: black;
        :cssText(cssTable)

        return rollTable1, insideTable
    else
        --otherwise problem of clear
        local tab = mw.html.create('table')
        tCell=tab:tag('td')
       
        local tTable =tCell:tag('table')
        :attr('cellpadding','0')
        :cssText(cssTable)
        tCell = tTable:tag('tr'):tag('th')
        :css("background-color",backgroundColor)
        :attr('colspan','3'):attr('align','center')
        tCell:node(wdlink_span)
        tCell:wikitext(translate("startlist",1,s.womenrace_bool or false))
        local tRow=tCell:tag('tr')

        return tab, tRow
    end
end

--=== D) Jersey, flag functions ===
--used from 2 functions
local flags = {
        Q16 = {'CAN', {'Flag of Canada.svg', '+1965-02-15'}},
        Q17 = {'JPN', {'Flag of Japan.svg', '+1999-08-13'},
            {'Flag of Japan (1870–1999).svg', '+1870-02-27', '+1999-08-12'}},
        Q20 = {'NOR', {'Flag of Norway.svg', '+1821-07-13'}},
        Q27 = {'IRL', {'Flag of Ireland.svg', '+1937-12-29'}},
        Q28 = {'HUN', {'Flag of Hungary.svg', '+1957-05-23'}},
        Q29 = {'ESP', {'Flag of Spain.svg', '+1981-12-06'},
            {'Flag of Spain (1977–1981).svg', '+1977-01-21', '+1981-12-06'},
            {'Flag of Spain (1945–1977).svg', '+1945-10-11', '+1977-01-21'},
            {'Flag of Spain (1938–1945).svg', '+1939', '+1945-10-11'},
            {'Flag of the Second Spanish Republic.svg', '+1931-04-14', '+1939'},
            {'Flag of Spain (1785–1873, 1875–1931).svg', '+1874', '+1931-04-13'}},
        Q30 = {'USA', {'Flag of the United States.svg', '+1960-07-04'}},
        Q31 = {'BEL', {'Flag of Belgium (civil).svg'}},
        Q32 = {'LUX', {'Flag of Luxembourg.svg'}},
        Q33 = {'FIN', {'Flag of Finland.svg', '+1918-05-29'}},
        Q34 = {'SWE', {'Flag of Sweden.svg'}},
        Q35 = {'DEN', {'Flag of Denmark.svg'}},
        Q36 = {'POL', {'Flag of Poland.svg'}},
        Q37 = {'LTU', {'Flag of Lithuania.svg', '+2004-09-01'},
            {'Flag of Lithuania (1988-2004).svg', '+1990-03-11', '+2004-09-01'}},
        Q38 = {'ITA', {'Flag of Italy.svg', '+1946-06-19'},
            {'Flag of Italy (1861–1946).svg', '+1861', '+1946-06-19'}},
        Q39 = {'SUI', {'Flag of Switzerland.svg', '+1889-12-12'},
            {'Flag of Switzerland.svg', '+1879-01-01'}},
        Q40 = {'AUT', {'Flag of Austria.svg', '+1945-05-01'},
            {'Flag of Austria.svg', '+1919-10-21', '+1938-03-13'}},
        Q41 = {'GRE', {'Flag of Greece.svg', '+1978'}},
        Q43 = {'TUR', {'Flag of Turkey.svg'}},
        Q45 = {'POR', {'Flag of Portugal.svg', '+1911-06-30'}},
        Q55 = {'NED', {'Flag of the Netherlands.svg', '+1806'}},
        Q77 = {'URU', {'Flag of Uruguay.svg'}},
        Q96 = {'MEX', {'Flag of Mexico.svg', '+1968-09-16'},
            {'Flag of Mexico (1934-1968).svg', '+1934', '+1968-09-16'}},
        Q114 = {'KEN', {'Flag of Kenya.svg'}},
        Q115 = {'ETH', {'Flag of Ethiopia.svg', '+1996-10-31'}},
        Q117 = {'GHA', {'Flag of Ghana.svg', '+1966-02-28'}},
        Q142 = {'FRA', {'Flag of France.svg', '+1794-05-20'}},
        Q145 = {'GBR', {'Flag of the United Kingdom.svg'}},
        Q148 = {'CHN', {"Flag of the People's Republic of China.svg", '+1985'}},
        Q155 = {'BRA', {'Flag of Brazil.svg', '+1992-05-11'},
            {'Flag of Brazil (1968–1992).svg', '+1968-05-28', '+1992-05-11'}},
        Q159 = {'RUS', {'Flag of Russia.svg', '+1993-12-11'},
            {'Flag of Russia (1991–1993).svg', '+1991-08-22', '+1993-12-11'},
            {'Flag of the Russian Soviet Federative Socialist Republic.svg', '+1954', '+1991-08-22'},
            {'Flag of the Russian Soviet Federative Socialist Republic (1937–1954).svg', '+1937', '+1954'}},
        Q183 = {'GER', {'Flag of Germany.svg', '+1949-05-23'},
            {'Flag of the German Reich (1935–1945).svg', '+1935-09-15', '+1945-05-23'},
            {'Flag of the German Reich (1933–1935).svg', '+1933-03-12', '+1935-09-15'},
            {'Flag of Germany (3-2 aspect ratio).svg', '+1919-04-11', '+1933-03-12'},
            {'Flag of the German Empire.svg', '+1871-04-16', '+1919-04-11'}},
        Q184 = {'BLR', {'Flag of Belarus.svg', '+2012-05-11'},
            {'Flag of Belarus (1995–2012).svg', '+1995-06-07', '+2012-05-11'},
            {'Flag of Belarus (1918, 1991–1995).svg', '+1991-09-19', '1995-06-07'}},
        Q189 = {'ISL', {'Flag of Iceland.svg', '+1944-06-17'}},
        Q191 = {'EST', {'Flag of Estonia.svg'}},
        Q211 = {'LAT', {'Flag of Latvia.svg'}},
        Q212 = {'UKR', {'Flag of Ukraine.svg', '+1992-01-28'}},
        Q213 = {'CZE', {'Flag of the Czech Republic.svg', '+1920-03-30'}},
        Q214 = {'SVK', {'Flag of Slovakia.svg'}},
        Q215 = {'SLO', {'Flag of Slovenia.svg'}},
        Q217 = {'MDA', {'Flag of Moldova.svg'}},
        Q218 = {'ROU', {'Flag of Romania.svg', '+1989-12-27'},
            {'Flag of Romania (1965-1989).svg', '+1989-12-27', '+1965'},
            {'Flag of Romania (1952-1965).svg', '+1952', '+1965'},
            {'Flag of Romania (1948-1952).svg', '+1948-01-08', '+1952'},
            {'Flag of Romania.svg', '12. april 1867-04-12', '+1948-01-08'}},
        Q219 = {'BUL', {'Flag of Bulgaria.svg', '+1990-11-22'},
            {'Flag of Bulgaria (1971 – 1990).svg', '+1971-05-18', '+1990-11-22'}},
        Q222 = {'ALB', {'Flag of Albania.svg', '+1992'}},
        Q224 = {'CRO', {'Flag of Croatia.svg', '+1990-12-21'},
            {'Flag of Croatia (white chequy).svg', '+1990-06-27', '+1990-12-21'}},
        Q227 = {'AZE', {'Flag of Azerbaijan.svg'}},
        Q228 = {'AND', {'Flag of Andorra.svg'}},
        Q229 = {'CYP', {'Flag of Cyprus.svg', '+2006-08-20'},
            {'Flag of Cyprus (1960-2006).svg', '+1960-08-16', '+2006-08-20'}},
        Q232 = {'KAZ', {'Flag of Kazakhstan.svg'}},
        Q235 = {'MON', {'Flag of Monaco.svg'}},
        Q238 = {'SMR', {'Flag of San Marino.svg'}},
        Q241 = {'CUB', {'Flag of Cuba.svg'}},
        Q244 = {'BAR', {'Flag of Barbados.svg'}},
        Q252 = {'INA', {'Flag of Indonesia.svg'}},
        Q258 = {'RSA', {'Flag of South Africa.svg', '+1994-04-27'},
            {'Flag of South Africa (1928-1982).svg', '+1928-05-31', '+1994-04-27'}},
        Q262 = {'ALG', {'Flag of Algeria.svg'}},
        Q265 = {'UZB', {'Flag of Uzbekistan.svg'}},
        Q298 = {'CHI', {'Flag of Chile.svg'}},
        Q334 = {'SGP', {'Flag of Singapore.svg'}},
        Q347 = {'LIE', {'Flag of Liechtenstein.svg'}},
        Q398 = {'BRN', {'Flag of Bahrain.svg', '+2002-02-14'}},
        Q403 = {'SRB', {'Flag of Serbia.svg', '+2004-08-18'},
            {'Flag of Serbia (1992–2004).svg', '+1992-04-27', '+2004-08-17'}},
        Q408 = {'AUS', {'Flag of Australia.svg'}},
        Q414 = {'ARG', {'Flag of Argentina.svg'}},
        Q419 = {'PER', {'Flag of Peru.svg', '+1950'},
            {'Flag of Peru (1825-1950).svg', '+1825-02-25', '+1950'}},
        Q424 = {'CAM', {'Flag of Cambodia.svg', '+1993-06-30'},
            {'Flag of Cambodia.svg', '+1948-10-20', '+1970-10-09'}},       
        Q664 = {'NZL', {'Flag of New Zealand.svg'}},
        Q711 = {'MGL', {'Flag of Mongolia.svg'}},
        Q717 = {'VEN', {'Flag of Venezuela.svg', '+2006-03-12'},
            {'Flag of Venezuela (1930–2006).svg', '+1930','+2006-03-12'}},
        Q733 = {'PAR', {'Flag of Paraguay.svg', '+2013-07-15'},
            {'Flag of Paraguay (1990–2013).svg', '+1990', '+2013-07-14'}},
        Q736 = {'ECU', {'Flag of Ecuador.svg'}},
        Q739 = {'COL', {'Flag of Colombia.svg'}},
        Q750 = {'BOL', {'Flag of Bolivia.svg', '+1851-10-31'}},
        Q754 = {'TTO', {'Flag of Trinidad and Tobago.svg'}},
        Q774 = {'GUA', {'Flag of Guatemala.svg'}},
        Q778 = {'BAH', {'Flag of the Bahamas.svg'}, '+1973-07-10'},
        Q783 = {'HON', {'Flag of Honduras.svg'}, '+1949'},
        Q786 = {'DOM', {'Flag of the Dominican Republic.svg'}},
        Q794 = {'IRI', {'Flag of Iran.svg', '+1980-07-29'},
            {'Flag of Iran (1964–1980).svg', '+1964', '+1980-07-29'}},
        Q800 = {'CRC', {'Flag of Costa Rica (state).svg', '+1906-11-27'}},
        Q801 = {'ISR', {'Flag of Israel.svg'}},
        Q804 = {'PAN', {'Flag of Panama.svg'}},
        Q813 = {'KGZ', {'Flag of Kyrgyzstan.svg', '+1992-03-03'}},
        Q817 = {'KUW', {'Flag of Kuwait.svg', '+1961-09-07'}},
        Q833 = {'MAS', {'Flag of Malaysia.svg', '+1963-09-16'}},
        Q842 = {'OMA', {'Flag of Oman.svg', '+1995'}},
        Q846 = {'QAT', {'Flag of Qatar.svg'}},
        Q858 = {'SYR', {'Flag of Syria.svg', '+1980-03-29'}},
        Q865 = {'TPE', {'Flag of the Republic of China.svg', '+1928-12-17'}},
        Q869 = {'THA', {'Flag of Thailand.svg'}},
        Q878 = {'UAE', {'Flag of the United Arab Emirates.svg'}},
        Q881 = {'VIE', {'Flag of Vietnam.svg', '+1976-02-07'}},
        Q884 = {'KOR', {'Flag of South Korea.svg', '+1997-10'}},
        Q916 = {'ANG', {'Flag of Angola.svg', '+1975-11-11'}},
        Q921 = {'BRU', {'Flag of Brunei.svg', '+1959-09-29'}},
        Q928 = {'PHI', {'Flag of the Philippines.svg', '+1998'}},
        Q948 = {'TUN', {'Flag of Tunisia.svg', '+1999-07-03'}},
        Q954 = {'ZIM', {'Flag of Zimbabwe.svg', '+1980-04-18'}},
        Q965 = {'BUR', {'Flag of Burkina Faso.svg'}},
        Q983 = {'GEQ', {'Flag of Equatorial Guinea.svg', '+1979-08-21'},
            {'Flag of Equatorial Guinea (1973–1979).svg', '+1973', '+1979-08-21'},
            {'Flag of Equatorial Guinea (without coat of arms).svg', '+1968-10-12', '+1973'}},
        Q986 = {'ERI', {'Flag of Eritrea.svg'}},
        Q1000 = {'GAB', {'Flag of Gabon.svg', '+1960-08-09'}},
        Q1007 = {'GBS', {'Flag of Guinea-Bissau.svg', '+1973-09-24'}},
        Q1008 = {'CIV', {"Flag of Côte d'Ivoire.svg"}},
        Q1009 = {'CMR', {'Flag of Cameroon.svg'}},
        Q1027 = {'MRI', {'Flag of Mauritius.svg', '+1968-03-13'}},
        Q1028 = {'MAR', {'Flag of Morocco.svg'}},
        Q1030 = {'NAM', {'Flag of Namibia.svg', '+1990-03-21'}},
        Q1036 = {'UGA', {'Flag of Uganda.svg', '+1962-10-09'}},
        Q1037 = {'RWA', {'Flag of Rwanda.svg', '+2001-10-25'},
            {'Flag of Rwanda (1962–2001).svg', '+1962', '+2001-10-25'}},
        Q1183 = {'PUR', {'Flag of Puerto Rico.svg'}},
        Q9676 = {'IMN', {'Flag of the Isle of Man.svg'}},
        Q15180 = {'URS', {'Flag of the Soviet Union.svg', '+1980-08-15', '+1991-12-25'},
            {'Flag of the Soviet Union (1955–1980).svg', '+1955-08-19', '+1980-08-14'},
            {'Flag of the Soviet Union (1924–1955).svg', '+1923-11-13', '+1955-08-18'}},
        Q16957 = {'GDR', {'Flag of East Germany.svg', '+1959-10-01'},
            {'Flag of Germany.svg', '+1949-10-07', '+1959-10-01'}}, --German Democratic Republic
        Q8646 = {'HKG', {'Flag of Hong Kong.svg'}},
        Q25228 = {'AIA', {'Flag of Anguilla.svg'}},
        Q29999 = {'NED', {'Flag of the Netherlands.svg', '+1690'}}, --Kingdom of the Netherlands
        Q33946 = {'TCH', {'Flag of the Czech Republic.svg', '+1920'}}, -- Czechoslovakia (1918–1992)
        Q36704 = {'YUG', {'Flag of Yugoslavia (1992–2003).svg', '+1992-04-27', '+2003-02-04'}, --Yugoslavia
            {'Flag of Yugoslavia (1943–1992).svg', '+1946', '+1992-04-27'}},
        Q41304 = {'GER', {'Flag of Germany (3-2 aspect ratio).svg', '+1918-11-09'}}, -- Weimar Republic
        Q83286 = {'YUG', {'Flag of Yugoslavia (1943–1992).svg'}}, --Socialist Federal Republic of Yugoslavia
        Q172579 = {'ITA', {'Flag of Italy (1861–1946).svg'}}, --Kingdom of Italy (1861-1946)
        Q216923 = {'TPE', {'Flag of Chinese Taipei for Olympic games.svg'}}, -- Chinese Taipei
        Q268970 = {'AUT', {'Flag of Austria.svg', '+1918-11-12', '+1919-09-10'}}, -- German-Austria (1918-1919)
        Q713750 = {'FRG', {'Flag of Germany.svg'}}, --West Germany
        Q853348 = {'TCH', {'Flag of the Czech Republic.svg'}, '+1960-07-11', '+1990-03-29'}, -- Czechoslovak Socialist Republic (1960-1990)
        Q2415901 = {'GER', {'Merchant flag of Germany (1946–1949).svg', '+1945-05-09', '+1949-05-23'}}, -- Allied-occupied Germany
        Q13474305 = {'ESP', {'Flag of Spain (1945–1977).svg', '+1945-10-11', '+1977-01-21'}, -- Francoist Spain (1935-1976)
            {'Flag of Spain (1938–1945).svg', '+1939', '+1945-10-11'},
            {'Flag of the Second Spanish Republic.svg', '+1931-04-14', '+1939'}},
        Q113486069={'NEUTRAL', {'Flag white.svg'}} --Russia and Belarus during the ban, cannot replace the flags above, because there are cases where it does not apply
    }

local function flag(countryID, date)
    local trackingCategory = ''
    --[[ If you uncomment the line under this comment, all pages with look-up misses in
    the flag table will be placed in a tracking category. You can use this to find more flags
    to add to the table. ]]
    -- trackingCategory = '[[Category:Missing flag in Module:Cycling race]]'

    local entry = flags[countryID]
    local IOC
    local file
    local result = ""
    if entry then
        for i, v in ipairs(entry) do
            if i == 1 then
                IOC = v
            else
                if not date then
                    file = v[1]
                    break
                else
                    local from = v[2]
                    local to = v[3]
                    if (not from or from <= date) and (not to or to > date) then
                        file = v[1]
                        break
                    end
                end
            end
        end
    end
    local flagpxSize = '20px'
    if countryID == 'Q39' then flagpxSize = '16px'end -- Small size for an square flag as Switzerland
    if file then
        result = '[[File:' .. file .. '|border|' .. flagpxSize ..'|' .. IOC .. ']]'
        if arwiki_totemplate then
            result = '{{flagicon|' .. IOC .. '}}'
        end
    elseif not date then
        local p41 = mw.wikibase.getBestStatements(countryID, "P41") -- P41 is flag image
        if p41[1] and p41[1].mainsnak.snaktype == 'value' then
            result = '[[File:' .. p41[1].mainsnak.datavalue.value .. '|border|' .. flagpxSize ..'|(Wikidata:' .. countryID .. ')]]'
            if arwiki_totemplate then
                result = '{{flagicon image|' .. p41[1].mainsnak.datavalue.value .. '}}'
            end
        end
    else
        -- Search flag for specific date
        local p41 = getStatementForTime(countryID, "P41", date) -- P41 is flag image
        if p41 then
            result = '[[File:' .. p41.mainsnak.datavalue.value .. '|border|' .. flagpxSize ..'|(Wikidata:' .. countryID .. ')]]'
            if arwiki_totemplate then
                result = '{{flagicon image|' .. p41.mainsnak.datavalue.value .. '}}'
        end
    end
    end
    return result .. trackingCategory
end

-- countryID --> shape ([[France|FRA]])
local function uciCodeCountry(countryID)
    local uciCode, countryName
    local blacklist={Q736=true}
    if countryID then
        --get UCI code
        if flags[countryID] then
            uciCode=flags[countryID][1]
        end
        --get link, assumed for a country the label is equal to the link, where not correct in the blacklist
        --if the black list becomes too long, we could create a second list for the sitelinks
        if available_list then
            if type(translations.list) == "function" then
                countryName = translations.list(countryID)
            end
        end
        if countryName == nil or countryName=='' or blacklist[countryID] then
            countryName = mw.wikibase.getSitelink(countryID)
        end
        if uciCode and countryName then
            return ' <small>([['..countryName..'|'..uciCode..']])</small> '
        end
    end
    return '' --else
end

local function jersey_infobox( winner_classification, item, timeOfRace)
    local jersey, jersey_name = '', ''
    local jerseyWPID = ''

    -- 1. Item of race, e.g. Tour de France = 'Q33881'
    -- 2. type of winner, names are the ones in variable t_s
    -- 3. and 4. start and end time. '+2500' means year 2500. Always beginning with a '+'
    -- 5. item of the jersey
    -- 6. item of the Wikipedia article of that jersey

    local data = {
        {'Q33881', 'montagne', '+1975', '+2500', 'Q25265958', 'Q927157'}, -- Tour de France
        {'Q33881', 'leader', '+1919', '+2500', 'Q24257871', 'Q738903'},
        {'Q33881', 'points', '+1953', '+1967', 'Q24645209', 'Q175399'}, -- Jersey green.svg
        {'Q33881', 'points', '+1968', '+1968', 'Q26919974', 'Q175399'}, -- Jersey red.svg
        {'Q33881', 'points', '+1969', '+2500', 'Q24645209', 'Q175399'}, -- Jersey green.svg
        {'Q33881', 'jeune', '+1975', '+2500', 'Q640430', 'Q2254180'}, -- Jersey white.svg
        {'Q33881', 'winner_fighting', '+2003', '+2500', 'Q27644113', 'Q2094179'}, -- Jersey red number.svg
        {'Q33881', 'winner_fighting2', '+2003', '+2500', 'Q27644113', 'Q2094179'}, -- Jersey red number.svg
        {'Q33881', 'equipe', '+2006', '+2500', 'Q27644112', 'Q1436680'}, -- Jersey yellow number.svg

        {'Q33861', 'leader', '+1931', '+2500', 'Q24257763', 'Q1164275'}, -- Giro d'Italia, Jersey pink.svg
        {'Q33861', 'points', '+1967', '+1968', 'Q26919974', 'Q641083'}, -- Jersey red.svg
        {'Q33861', 'points', '+1969', '+2009', 'Q26945272', 'Q641083'}, -- Jersey violet.svg
        {'Q33861', 'points', '+2010', '+2016', 'Q26919974', 'Q641083'}, -- Jersey red.svg
        {'Q33861', 'points', '+2017', '+2500', 'Q26945272', 'Q641083'}, -- Jersey violet.svg
        {'Q33861', 'montagne', '+1974', '+2011', 'Q24645209', 'Q641060'}, -- Jersey green.svg
        {'Q33861', 'montagne', '+2012', '+2500', 'Q24687409', 'Q641060'}, -- Jersey blue.svg
        {'Q33861', 'jeune', '+1976', '+2500', 'Q640430', 'Q641662'}, -- Jersey white.svg

        {'Q33937', 'leader', '+1935', '+1936', 'Q24258056', 'Q3278226'}, -- Vuelta a España, Jersey orange.svg
        {'Q33937', 'leader', '+1941', '+1941', 'Q26696171', 'Q640430'}, -- Jersey white.svg
        {'Q33937', 'leader', '+1942', '+1942', 'Q24258056', 'Q3278226'}, -- Jersey orange.svg
        {'Q33937', 'leader', '+1945', '+1945', 'Q24257872', 'Q2534046'}, -- Jersey red.svg
        {'Q33937', 'leader', '+1946', '+1950', 'Q26696171', 'Q640430'}, -- Jersey white.svg
        {'Q33937', 'leader', '+1955', '+1976', 'Q24257871', 'Q738903'}, -- Jersey yellow.svg
        {'Q33937', 'leader', '+1977', '+1977', 'Q24258056', 'Q3278226'}, -- Jersey orange.svg
        {'Q33937', 'leader', '+1978', '+1998', 'Q24257871', 'Q738903'}, -- Jersey yellow.svg
        {'Q33937', 'leader', '+1999', '+2009', 'Q24257991', 'Q27665179'}, -- Jersey gold.svg
        {'Q33937', 'leader', '+2010', '+2500', 'Q24257872', 'Q2534046'}, -- Jersey red.svg
        {'Q33937', 'points', '+1945', '+1986', 'Q24687409', 'Q2746711'}, -- Jersey blue.svg
        {'Q33937', 'points', '+1987', '+1989', 'Q24645209', 'Q11638007'}, -- Jersey green.svg
        {'Q33937', 'points', '+1990', '+2009', 'Q24687409', 'Q2746711'}, -- Jersey blue.svg
        {'Q33937', 'points', '+2010', '+2500', 'Q24645209', 'Q11638007'}, -- Jersey green.svg
        {'Q33937', 'montagne', '+1935', '+1985', 'Q27670182', 'Q11638007'}, -- Jersey green.svg
        {'Q33937', 'montagne', '+1986', '+1986', 'Q27670174', 'Q3278226'}, -- Jersey orange.svg
        {'Q33937', 'montagne', '+1987', '+1987', 'Q27670178', 'Q2534046'}, -- Jersey red.svg
        {'Q33937', 'montagne', '+1988', '+1989', 'Q27670105', 'Q27670115'}, -- Jersey blackdots.png
        {'Q33937', 'montagne', '+1990', '+2005', 'Q27670182', 'Q11638007'}, -- Jersey green.svg
        {'Q33937', 'montagne', '+2006', '+2008', 'Q27670174', 'Q3278226'}, -- Jersey orange.svg
        {'Q33937', 'montagne', '+2009', '+2009', 'Q27670126', 'Q27670163'}, -- Jersey granate.svg
        {'Q33937', 'montagne', '+2010', '+2500', 'Q25265959', 'Q27670167'}, -- Jersey bluedots.svg
        {'Q33937', 'jeune', '+2019', '+2500', 'Q640430', 'Q60233927'}, -- Jersey white.svg

        {'Q2091354', 'leader', '+2011', '+2500', 'Q24257871'}, -- Tour of Norway, Jersey yellow.svg
        {'Q2091354', 'sprints', '+2011', '+2011', 'Q26806427'}, -- Jersey green.svg
        {'Q2091354', 'points', '+2012', '+2017', 'Q24645209'}, -- Jersey green.svg
        {'Q2091354', 'points', '+2018', '+2018', 'Q28820618'}, -- MaillotCyan.PNG
        {'Q2091354', 'points', '+2019', '+2500', 'Q47945989'}, -- Jersey dark blue.svg
        {'Q2091354', 'montagne', '+2011', '+2015', 'Q25265958'}, -- Jersey polkadot.svg
        {'Q2091354', 'montagne', '+2016', '+2017', 'Q27670174'}, -- Jersey orange.svg
        {'Q2091354', 'montagne', '+2018', '+2500', 'Q25265958'}, -- Jersey polkadot.svg
        {'Q2091354', 'jeune', '+2011', '+2500', 'Q640430'}, -- Jersey white.svg
        {'Q2091354', 'winner_fighting', '+2017', '+2017', 'Q29957114'}, -- MaillotCyan.PNG
        {'Q128713', 'leader', '+2013', '+2017', 'Q24257871'}, -- Tour des Fjords, Jersey yellow.svg
        {'Q128713', 'leader', '+2018', '+2018', 'Q29594434'}, -- MaillotCyan.PNG
        {'Q128713', 'points', '+2013', '+2014', 'Q24645209'}, -- Jersey green.svg
        {'Q128713', 'points', '+2015', '+2017', 'Q24687409'}, -- Jersey blue.svg
        {'Q128713', 'points', '+2018', '+2018', 'Q25265938'}, -- Jersey violet.svg
        {'Q128713', 'montagne', '+2013', '+2018', 'Q25265958'}, -- Jersey polkadot.svg
        {'Q128713', 'jeune', '+2013', '+2018', 'Q640430'}, -- Jersey white.svg
        {'Q128713', 'winner_fighting', '+2015', '+2015', 'Q30035038'}, -- Jersey green.svg
        {'Q128713', 'winner_fighting', '+2016', '+2017', 'Q30035039'}, -- Jersey orange.svg
        {'Q128961', 'leader', '+2013', '+2500', 'Q24687408'}, -- Arctic Race of Norway, Jersey blue.svg
        {'Q128961', 'points', '+2013', '+2500', 'Q24645209'}, -- Jersey green.svg
        {'Q128961', 'montagne', '+2013', '+2014', 'Q27670178'}, -- Jersey red.svg
        {'Q128961', 'montagne', '+2015', '+2500', 'Q27670174'}, -- Jersey orange.svg
        {'Q128961', 'jeune', '+2013', '+2500', 'Q640430'}, -- Jersey white.svg
        {'Q128961', 'winner_fighting', '+2014', '+2500', 'Q27644113'}, -- Jersey red number.svg
        {'Q17619325', 'leader', '+2014', '+2014', 'Q24257871'}, -- Ladies Tour of Norway, Jersey yellow.svg
        {'Q17619325', 'leader', '+2015', '+2016', 'Q26945272'}, -- Jersey violet.svg
        {'Q17619325', 'leader', '+2017', '+2500', 'Q24257871'}, -- Jersey yellow.svg
        {'Q17619325', 'points', '+2014', '+2500', 'Q24645209'}, -- Jersey green.svg
        {'Q17619325', 'montagne', '+2014', '+2500', 'Q25265958'}, -- Jersey polkadot.svg
        {'Q17619325', 'jeune', '+2014', '+2500', 'Q640430'}, -- Jersey white.svg
        {'Q17619325', 'winner_fighting', '+2016', '+2500', 'Q30035039'}, -- Jersey orange.svg
    }
    --timeOfRace = '+1968-07-01T00:00:00Z'
    timeOfRace = string.match(timeOfRace, "+%d%d%d%d") or ''
    for _, v in pairs(item) do
        for _, value in pairs(data) do
            if v == value[1] then
                if winner_classification == value[2] then
                    if (timeOfRace >= value[3]) and (timeOfRace <= value[4]) then
                        jersey = value[5]
                        jerseyWPID = value[6]
                    end
                end
            end
        end
    end

    -- local starttime, endtime = '', '+2500'
    if jersey ~= '' then --and (timeOfRace > starttime) and (timeOfRace < endtime) then
        local entity_jersey = mw.wikibase.getEntity(jersey)
            jersey = entity_jersey.claims['P18'][1].mainsnak.datavalue.value
            jersey_name = entity_jersey:getLabel(wikilang) or ''
        if jerseyWPID ~= '' then
            local entity = mw.wikibase.getEntity( jerseyWPID )
            local Sitelink = entity:getSitelink(wiki..'wiki') -- link to WParticle
            if Sitelink ~= nil then jerseyWPID = wiki..':'..Sitelink else jerseyWPID = '' end
        end
        return jersey, jersey_name, jerseyWPID
    else return '', '', ''
    end
end

local function jersey(h)
    local jersey_string = ' '
    local jerseys = {
    ['Q24257871'] = {file = 'Jersey yellow.svg',
        name_ar = 'قميص أصفر لمتصدر الترتيب العام',
        name_fr = 'maillot jaune de leader du classement général',
        name_es = 'maillot amarillo de líder de la clasificación general',
        name_ru = 'жёлтая майка лидера генеральной классификации'
        },
    ['Q24645209'] = {file = 'Jersey green.svg',
        name_ar = 'قميص أخضر لمتصدر ترتيب النقاط',
        name_fr = 'maillot vert de leader du classement par points',
        name_es = 'maillot verde de líder de la clasificación por puntos',
        name_ca = 'mallot verd del líder de la classificació per punts',
        name_ru = 'зелёная майка лидера очковой классификации'
        },
    ['Q640430'] = {file = 'Jersey white.svg',
        name_ar = 'قميص أبيض لمتصدر ترتيب الشباب',
        name_fr = 'maillot blanc de leader du classement du meilleur jeune',
        name_es = 'maillot blanco de líder de la clasificación de los jóvenes',
        name_ru = 'белая майка лидера молодёжной классификации',
        name_de = 'weißes Trikot des Führenden der Nachwuchswertung'
        },
    }

    if type(h) == 'table' and h[1] then
        for _, v in ipairs(h) do
            local jersey_name
            if jerseys[v] then
                jersey_string = jersey_string .. '[[File:' .. jerseys[v].file .. '|20px'
                jersey_name = jerseys[v]['name_' .. wiki] or mw.wikibase.getLabel(v) or jerseys[v]['name_fr']
                if jersey_name then
                    jersey_string = jersey_string .. '|' .. jersey_name
                end
                jersey_string = jersey_string .. ']]'
            else
                local p18 = mw.wikibase.getBestStatements(v, 'P18')
                if p18[1] and p18[1].mainsnak.snaktype == 'value' then
                    jersey_string = jersey_string .. '[[File:' .. p18[1].mainsnak.datavalue.value .. '|20px'
                    jersey_name = getLabelFallback(v, {wikilang, 'en', 'fr'})
                    if jersey_name then
                        jersey_string = jersey_string .. '|' .. jersey_name
                    end
                    jersey_string = jersey_string .. ']]'
                end
            end
        end
    end
    return jersey_string
end -- function end

--=== E) Other (winner, getkm) ===
local function isHuman(riderId)
    local isHuman = false
    if riderId then
        local p31 = wikibase.getBestStatements(riderId, 'P31')
        for _, iOf in pairs (p31) do
            if iOf.mainsnak.snaktype == 'value' and iOf.mainsnak.datavalue.value.id == "Q5" then
                isHuman = true
                break
            end
        end
    end
    return isHuman
end

local function isCountry(riderId)
    local isCountry = false
    if riderId then
        local p31 = wikibase.getBestStatements(riderId, 'P31')
        for _, iOf in pairs (p31) do
            -- exception Hong-Kong and Taiwan
            if iOf.mainsnak.snaktype == 'value' and (iOf.mainsnak.datavalue.value.id == "Q6256" or iOf.mainsnak.datavalue.value.id =="Q15634554" or iOf.mainsnak.datavalue.value.id =="Q779415") then
                isCountry = true
                break
            end
        end
    end
    return isCountry
end

function isWomenrace(raceID) --for translation
    for _, p2094 in statements(raceID, 'P2094') do
        if p2094.mainsnak.datavalue.value.id == "Q1451845" then
            return true
        end
    end
    return false
end

local function getNationality(wID, timeOfRace,q) --for a rider
    local p27, countryID
    --allow overload of the property, for cases like Russian/BLR ban, or Commonwealth games, only for P1532
    if q and q.P1532 and q.P1532[1].snaktype == 'value' then
        countryID = q.P1532[1].datavalue.value.id
    else
        local listOfProperty={'P1532','P27'}
        if wID then
            for _, prop in ipairs(listOfProperty) do
                if countryID==nil then
                    p27 = getStatementForTime(wID, prop, timeOfRace) --P27 is country of citizenshi
                    if p27 then
                        countryID = p27.mainsnak.datavalue.value.id
                    end
                end
            end
        end
    end
    return countryID
end

local function subwinner(riderId, timeOfRace, q)
    local outTable={}
    local riderTeam, riderLink, countryID

    if riderId then
        if isHuman(riderId) then
            riderLink = getRiderLink(riderId, timeOfRace)
            countryID = getNationality(riderId, timeOfRace,q)
            if countryID then
                riderLink = flag(countryID, timeOfRace) .. ' ' .. riderLink
            end
            riderTeam = getTeam(riderId, timeOfRace, q) or ''
        else
            local _
            riderLink, _, countryID = getTeamLinkCat(riderId, timeOfRace, true)
            if countryID then
                riderLink = flag(countryID, timeOfRace) .. ' ' .. riderLink
            end
        end
    end
    return riderLink, riderTeam
end

local function winner(raceID, winners, timeOfRace, country, WDlink_on, team, ref, winnersId)
    local p1346 = wikibase.getAllStatements(raceID, 'P1346') -- P1346 is 'winner'
    for _, winner in pairs(p1346) do
        local wID = winner.mainsnak.snaktype == 'value' and winner.mainsnak.datavalue.value.id
        local wOf, wCause, wCriterion, riderLink
        local q = winner.qualifiers
        if q then
            local _, disqualified =isdisqualified(winner,q)
           
            if q.P642 and q.P642[1].snaktype == 'value' then
                for _, q642 in pairs(q.P642) do
                    wOf = q642.datavalue.value.id -- P642 is 'of'
                    if not wOf then
                        -- Try P1346 (winner) instead
                        -- Assume Q20882667 ('overall winner general classification') if neither are found
                        wOf = q.P1346 and q.P1346[1].snaktype == 'value' and q.P1346[1].datavalue.value.id or 'Q20882667'
                    end
                    wCause = q.P828 and q.P828[1].snaktype == 'value' and q.P828[1].datavalue.value.id
                        -- P828 is 'has cause'
                    wCriterion = q.P1013 and q.P1013[1].snaktype == 'value' and q.P1013[1].datavalue.value.id
                        -- P1013 is 'criterion used'

                    if winners[wOf] then
                        if wID then
                            local reference = ref and getReference(winner)
                            local _, countryID
                            if isHuman(wID) then
                                riderLink = getRiderLink(wID, timeOfRace)
                                if reference then
                                    riderLink = riderLink .. reference
                                end
                                if team then
                                    local riderTeam = getTeam(wID, timeOfRace, q)
                                    if riderTeam then
                                        riderLink = riderLink .. ' (' .. riderTeam .. ')'
                                    end
                                end
                            elseif isCountry(wID) then
                                riderLink = flag(wID, timeOfRace).." "..getCountryName(wID)
                                if reference then
                                    riderLink = riderLink .. reference
                                end
                                country=true   
                            else --team
                                local _
                                riderLink, _, countryID = getTeamLinkCat(wID, timeOfRace, country)
                                if reference then
                                    riderLink = riderLink .. reference
                                end
                            end
                            if not country then
                                if not countryID then
                                    if isHuman(wID) then
                                        countryID = getNationality(wID, timeOfRace,q)
                                    else
                                        local p17 = getStatementForTime(wID, 'P17', timeOfRace) --P27 is country of citizenship
                                        if p17 then
                                            countryID = p17.mainsnak.datavalue.value.id
                                        end
                                    end
                                end
                                if countryID then
                                    riderLink = flag(countryID, timeOfRace) .. ' ' .. riderLink
                                end
                            end
                            if WDlink_on then
                                riderLink = riderLink .. ' ' .. wdLink(wID)
                            end
                        else
                            riderLink = wCriterion and contentLanguage:ucfirst(wikibase.getLabel(wCriterion) or '') or ''
                            if wCause then
                                local cause = wikibase.getLabel(wCause)
                                if cause then
                                    riderLink = riderLink .. ' (' .. cause .. ')'
                                end
                            end
                        end
                        if disqualified==true then
                            riderLink='<s>'..riderLink..'</s>'
                        end
                        if winnersId and winnersId[wOf] then
                            if disqualified or ((not wID) and wCriterion) then
                                winnersId[wOf]= 'Q666' --to identify disqualification
                            else
                                winnersId[wOf]= wID --identify cancelled
                            end
                        end
                        if winners[wOf] == '' then
                            winners[wOf] = riderLink
                        else
                            winners[wOf] = winners[wOf] .. '<br/>' .. riderLink
                        end
                    end
                end
            end
        end
    end
end

local function sortAndConcat(t_Body, resultTable)
    table.sort(t_Body, function(a, b) return a[1] < b[1] end)
    for _, m in ipairs(t_Body) do resultTable:node(m[2]) end
    return resultTable
end

--------- Definition sub-functions for calendar and victory ------
local function getTimeOfRace(raceID)
    local timeOfRace = firstValue(raceID, 'P580', 'time')
    if timeOfRace==nil then
        timeOfRace = firstValue(raceID, 'P585', 'time') -- P585 is 'point in time'
        if timeOfRace==nil then
            timeOfRace = firstValue(raceID, 'P582', 'time')
            if timeOfRace==nil then
                local link = getSitelinkFallback(raceID, {'en', 'fr', 'de'})
                if link then
                    local year = string.match(link, '%d%d%d%d')
                    if year then
                        timeOfRace = year .. '-01-01T00:00:00Z'
                    end
                end
            end
        end
    end
    if timeOfRace == nil and wiki == "ar" then
        timeOfRace = '+1970-01-01T00:00:00Z'
    end
    return timeOfRace, '> Wikidata is missing data about start time (P580) or point in time (P582)'
end

local function fn_date(entityID, functionName)  --to move as a general function
    local tempdate, timeOfRace, sortkey, sortkeyDate
    local outTable={}
    local sTime = firstValue(entityID, 'P580', 'time') -- P580 is 'start time'
    local eTime = firstValue(entityID, 'P582', 'time') -- P582 is 'end time'
    if sTime and eTime then
        local startTime, endTime = getStartEndTime(sTime, eTime, 'small')
        if functionName==nil then --calendar
            tempdate = startTime .. ' – ' .. endTime  --mettre year en option!
            sortkeyDate = sTime
        else  --victory, general classification
            tempdate =endTime
            sortkeyDate =eTime
        end
        timeOfRace = eTime
    else
        -- This function give a format to dates when P585 (date) is used in a single day race
        local pTime = firstValue(entityID, 'P585', 'time') -- P585 is 'point in time'
        if pTime then
            tempdate = funcDate(pTime, 'small')
            timeOfRace = pTime
            sortkeyDate = pTime
        end
    end
    local _, _, y, m, d = string.find(sortkeyDate or "", "(%d+)-(%d+)-(%d+)")
    if y~= nil and m~= nil and d~=nil then
        sortkey = y..m..d
    elseif y~= nil and m~= nil then
        sortkey = y..m
    elseif y~= nil then
        sortkey = y
    else sortkey = '0000'
    end

    local tCell = mw.html.create('td'):attr('data-sort-value',sortkey)
    :cssText("style=text-align:right;padding:0 0.5em")
    :wikitext(tempdate)
    
    outTable["timeOfRace"]=timeOfRace
    outTable["tCell"]=tostring(tCell)
    outTable["sortkey"]=sortkey
    return outTable
end

local function fn_country(entityID, timeOfRace,countrybool, raceCell, parentID)
    -- This function gives countries where the race take place
    -- parentID taken from fn_race, optional

    local country, countryname, outTable= {}, {}, {}
    local countryID

    local cssCell="text-align:" .. textalign .. ";padding:0 0.5em"
    local tCell= mw.html.create('td'):cssText(cssCell)
    
    local listOfProperty={'P1532','P17'} -- P1512 is 'country for sport' to handle problems with Hong Kong etc.
    local listOfID = {entityID, parentID}
    
    for _, thisID in ipairs(listOfID) do
        if thisID~=nil then
            for _, prop in ipairs(listOfProperty) do
                if countryID == nil then --like "break"
                    for _, p1532 in statements(thisID, prop) do
                        countryID = p1532.mainsnak.datavalue.value.id
                        countryname[#countryname + 1] = getCountryName(countryID)
                        if countrybool==false or not countryname[#countryname] then
                            country[#country + 1]=flag(countryID, timeOfRace)
                        else
                            country[#country + 1]=flag(countryID, timeOfRace).." "..countryname[#countryname]
                        end
                        outTable["flag"]=flag(countryID, timeOfRace)
                    end
                end
            end
        end
    end

    if countryID == nil then outTable["flag"]="no flag" end
    if countryname[1] then tCell:attr('data-sort-value',countryname[1]) end
    if countrybool==false then
        tCell:wikitext((country[1] or '').." "..(raceCell or ''))
        outTable["countryname"]=''
    else
        if countryname[1] then
            outTable["countryname"]=countryname[1]
            if country[1] then tCell:wikitext(country[1]) end
        else
            outTable["countryname"]=''
        end
       
    end
    outTable["tCell"]=tCell
    return outTable
end

local function commaStage(stageID,raceLabel) --how to write "stage, "
    local outTable={}
    local stageNumber=''
    local subStage = ''
    local stageNumberonly, stageLetter

    local temp=firstValue(stageID, 'P1545')
    if temp then stageNumber = temp end

    if stageNumber=='0' then --prologue
        stageNumber= translate("victories",9)
    else
        if stageNumber==nil then
            stageNumber= translate("victories",8)
        else
            --look for subStage
            local i,j = string.find(stageNumber, "%a+") --if letter in the stage number
            if i ~= nil then --we have to do something
                local k,l = string.find(stageNumber, "%d+") --select the number in the stage number
                stageNumberonly = string.sub(stageNumber, k, l)--cut the string in 2
                stageLetter = string.sub(stageNumber, i, j)
                stageNumber=stageNumberonly
                if stageLetter ~= nil then subStage=stageLetter end
            end
            if wiki == 'ar' then
                stageNumber= translate("victories",8)..' '..number('f', tonumber(stageNumber), wiki)
            else
                stageNumber= number('f', tonumber(stageNumber), wiki)..subStage..' '..translate("victories",8)
            end
        end
    end

    local comma = ", "
    if wiki == 'ar' then comma = " ، " end
    if wiki == 'fr' then
        local correpondance={
        {name="^Trois", article= " des "},
        {name="^Quatre", article= " des "},
        {name="^Boucles", article= " des "},
        {name="^Triptyque", article= " du "},
        {name="^Tour", article= " du "},
        {name="^Grand Prix", article= " du "},
        {name="^Circuit", article= " du "},
        {name="^Mémorial", article= " du "},
        {name="^Trophée", article= " du "},
        {name="^Ronde", article= " de la "},
        {name="^Semaine", article= " de la "},
        {name="^Classica", article= " de la "},
        {name="^Flèche", article= " de la "},
        {name="^Course", article= " de la "},
        {name="^Classique", article= " de la "},
        {name="Race", article= " de la "},
        {name="^Étoile", article= " de l'"},
        {name="^La", article= " de "}
        }

        for _, v in ipairs(correpondance) do
            if string.find(raceLabel, v.name) then
                comma = v.article
                break
            end
        end
    end

    if wiki == 'fr' or wiki=="ca" or wiki=="es" or wiki=="ast" then
        outTable["prefix"]=stageNumber..comma
        outTable["postfix"]=''
    else
        --if wiki=="de" or wiki=="da" or wiki=="fo" or wiki == "lb" or wiki=="no" or wiki=="ru" or wiki=="ar" or wiki=="lv" or wiki=="pl" then
        outTable["prefix"]=''
        outTable["postfix"]=comma..stageNumber
    end
    return outTable
end

local function getMainRaceLink(entityID,entity_type,stageID, functionName,timeOfRace) --the link to the edition but with a general name
    local instanceOf, instanceOfTemp, label, Sitelink, isclass, prefix, postfix
    local arlabel
    local stage_link=false
    
    if stageID then
        Sitelink=wikibase.getSitelink(stageID)
        if Sitelink then stage_link=true end
    end
    if Sitelink==nil then
        Sitelink=wikibase.getSitelink(entityID)
    end
    prefix=''; postfix='' --general classification
    listOfProperty={'P2561','P1448'}
    
    --system with P3450 and P2094
    instanceOf=firstValue(entityID, 'P3450', 'id')
    --else use P31
    if instanceOf==nil then
        for _, p31 in statements(entityID, 'P31') do
            instanceOfTemp = p31.mainsnak.datavalue.value.id
            if instanceOfTemp ~= "Q27020041" and class_dic[instanceOfTemp]==nil then    --we don't want the class, but the main race
                instanceOf=instanceOfTemp
            end
        end
    end
    
    --get information from the parent
    if instanceOf then
        --look for
        for _, prop in ipairs(listOfProperty) do
            for _, p2561 in statements(instanceOf, prop) do --name for championship
                if label==nil then
                    local lang_WD = p2561.mainsnak.datavalue.value.language
                    if wiki == lang_WD then
                        local nametemp = p2561.mainsnak.datavalue.value.text
                        if timeOfRace~= nil then
                            local q = p2561.qualifiers
                            if q then
                                local temp = checktime(nametemp,q,timeOfRace)
                                if temp then label = nametemp end--if the time is correct than it is finished
                            else
                                label = nametemp
                                arlabel = label
                            end
                        end
                    end
                end
            end
        end

        if label==nil then
            label=wikibase.label(instanceOf)
            if wiki == 'ar' then arlabel = mw.wikibase.getLabelByLang(instanceOf, 'ar') end
            if not label then
                label = getLabelFallback(entityID, {'en', 'fr', 'de'}) or ''
            end
        end
        if Sitelink==nil and entity_type~=0 then --only if no link to the race direct
            Sitelink=wikibase.getSitelink(instanceOf)
        end
        if Sitelink==nil and entity_type==0 then --only for champ
            local temp=firstValue(entityID, 'P361','id') --temp is NC France 2019 for instance
            if temp then
                Sitelink= wikibase.getSitelink(temp)
            end
            if Sitelink == nil then
                local temp2=firstValue(entityID, 'P31','id') -- French NC Men ITT
                if temp2 then
                    Sitelink= wikibase.getSitelink(temp2) 
                    if Sitelink == nil then
                        local temp3=firstValue(temp2, 'P361','id') -- French NC ITT
                        if not temp3 then temp3=firstValue(temp2, 'P31','id') end
                        if temp3 then
                            Sitelink= wikibase.getSitelink(temp3) 
                            if Sitelink == nil then
                                local temp4=firstValue(temp3, 'P361','id') -- French NC
                                if not temp4 then temp4=firstValue(temp3, 'P31','id') end
                                if temp4 then
                                    Sitelink= wikibase.getSitelink(temp4) 
                                end
                            end
                        end
                    end
                end
            end   
        end
    end
    --affect the label
    if label==nil then
        label=wikibase.label(entityID)
        if wiki == 'ar' then arlabel = mw.wikibase.getLabelByLang(entityID, 'ar') end
        if not label then
            label = getLabelFallback(entityID, {'en', 'fr', 'de'}) or ''
        end
    end
    --look for link to the race if nothing
    --if different languages have to be added, a language table can be created
    if entity_type==2 then
        if functionName~=nil then --calendar=nil
            if wiki == 'fr' then prefix= translate("victories",1)..', ' --general classification
            elseif wiki == 'ar' then postfix ='، '..translate("victories",1)
            else postfix = ', '..translate("victories",1)
            end
        end
    elseif entity_type=='stage' then
        --how to write "stage, " is concentrated in one function
        local commaTable=commaStage(stageID, label)
        prefix= commaTable["prefix"]
        postfix=commaTable["postfix"]
    end

    if Sitelink == nil then
        if wiki == 'ar' then
            label = make_IllWD2_link(entityID,arlabel,label)
        end
        return prefix..label..postfix
    elseif stage_link then
        return '[['..Sitelink..'|'..prefix..label..postfix..']]'
    else
        return prefix..'[['..Sitelink..'|'..label..']]'..postfix
    end
end

--look for the circuitID to create a link as [[World Tour|1.UWT]]
--a bit redundant with classLink which needs less computation
--for infobox classLink gives enough info
local function classToCircuit(classID, entityID, child, q)
    local displayedCircuitID, circuitID
    
    if classID then
        if classID=='Q23005601' or classID=='Q23005603' then --1WWT 2WWT clear
            displayedCircuitID = 'Q21075974'
        elseif classID=='Q22231106' or classID=='Q22231107' then --1UWT 2UWT clear
            displayedCircuitID = 'Q635366'
        else --we have to look in the item
            if child then --for instance Flèche wallonne 2020
                for _, p361 in statements(entityID, 'P361') do
                    circuitID = p361.mainsnak.datavalue.value.id
                    for _, p31 in statements(circuitID, 'P31') do --is it a UCI circuit?
                        parentCircuitID = p31.mainsnak.datavalue.value.id
                        if UCI_Circuits[parentCircuitID] then
                            displayedCircuitID=circuitID
                        end
                    end
                end
            else --for instance Flèche wallonne
                if q then
                    if q.P642 and q.P642[1].snaktype == 'value' and q.P642[1].datavalue.value.id then   
                        displayedCircuitID = q.P642[1].datavalue.value.id
                    end
                end
            end
        end
    end
    return displayedCircuitID
end

local function getStartEndfromQuali(q) --return sTime and eTime as date
    local sTime, eTime
    if q then
        if q.P580 and q.P580[1] and q.P580[1].snaktype == 'value' then -- P580 is start time
            sTime = q.P580[1].datavalue.value.time
        end
        if q.P582 and q.P582[1] and q.P582[1].snaktype == 'value' then -- P582 is end time
            eTime = q.P582[1].datavalue.value.time
        end
    end
    return sTime, eTime
end

local function funcDateFigure(date,mode)
    local y, m = string.match(date, "(%d+)-(%d+)-%d+")
    
    if mode=='Y' or m=='00' or not m then
        return y
    elseif y then
        return string.gsub(m,'0','').."."..y
    else
        return nil
    end
end

local function getPeriodSub(sTime, eTime, brackets)
    local startTime, endTime, y, m, y2, m2

    if sTime then
        y, m = string.match(sTime, "(%d+)-(%d+)-%d+")
        if m=='00' or m=='01' then
            startTime= funcDateFigure(sTime, 'Y')
        else
            startTime= funcDateFigure(sTime,'m')
        end
    end
    
    if eTime then
        y2, m2 = string.match(eTime, "(%d+)-(%d+)-%d+")
        if m2=='00' or m2=='12' then
            endTime=funcDateFigure(eTime, 'Y')
        else
            endTime=funcDateFigure(eTime, 'm')
        end
    end

    local period
    if sTime and eTime then
        if startTime==endTime then
            period=startTime --only (1990)
        else
            period=startTime .. '-'..endTime
        end
    elseif sTime then
        period=startTime .. '-'
    elseif eTime then
        period='-'..endTime
    else
        period=""
    end
    
    if brackets and period~="" then
        period="("..period..")"
    end
    return period, sTime
end

-- for display period with only year, for instance (2020-2021)
local function getPeriod(q, brackets)
    local sTime, eTime = getStartEndfromQuali(q)
    return getPeriodSub(sTime, eTime, brackets)
end

local function getClassCalendar_sub(entityID)
    local classID=firstValue(entityID, 'P279', 'id')
    
    if classID==nil then
        for _, p31 in statements(entityID, 'P31') do
            if class_dic[p31.mainsnak.datavalue.value.id]~=nil then
                classID=p31.mainsnak.datavalue.value.id
                break
            end
        end
    end
    return classID
end

-- For infobox
local function getClass(entityID)
    local classLink, circuitID, circuitLink
    local classTable={}

    for ii, p279 in statements(entityID, 'P279') do
        if p279 and p279.mainsnak.snaktype == 'value' then
            local classID = p279.mainsnak.datavalue.value.id

            if class_dic[classID]~=nil then
                circuitID=classToCircuit(classID, entityID, false, p279.qualifiers)
                classLink=classLinkFn(classID,circuitID)
                if circuitID and classLink then
                    local period, sTime=getPeriod(p279.qualifiers, true)
                    local classStr = classLink .. " <small>"..period.."</small>"
                    table.insert(classTable, {sTime, classStr, circuitID})
                end
            end
        end
    end
    if #classTable~=0 then
        table.sort(classTable, function(a, b) return a[1] < b[1] end)
    end
    for _, class in pairs(classTable) do
        if not str then str='' else str=str..'<br>' end
        str=str..class[2]
        circuitLink=WPlinkpure(class[3])
    end
    return str, circuitLink, #classTable
end

local function fn_race(entityID,displayed_class,display_class,timeOfRace, functionName,country)--return link to the race and class
    --first function read from victory main
    local Sitelink, entity_type, classID, stageID
    local outTable={}

    for _, p31 in statements(entityID, 'P31') do
        if stages[p31.mainsnak.datavalue.value.id] then
            entity_type = 'stage'  --then the class is one stage above!
            local parentID = getParentID(entityID)
            classID=getClassCalendar_sub(parentID)
           
            outTable["parentID"] = parentID --as we read it here, no need to read it afterwards
            stageID= entityID --everything slide from one rank
            entityID = parentID
        end
    end
    
    if classID==nil then
        classID=getClassCalendar_sub(entityID)
    end
    --Now we have the class and know the type of race it is
    if entity_type == 'stage' then
        Sitelink=getMainRaceLink(entityID,entity_type,stageID, functionName,timeOfRace)
    else
        Sitelink=getMainRaceLink(entityID,class_dic[classID],nil, functionName,timeOfRace)
    end
    
    if country~=false then
        local tCell=mw.html.create('td'):cssText("text-align:".. textalign ..";padding:0 2.3em"):wikitext(Sitelink)
        outTable["raceCell"]=tostring(tCell)
    else
        outTable["raceCell"]=Sitelink --already opened
    end
    
    if display_class == true and classID~=nil and (displayed_class==nil or displayed_class[classID]~=nil) then
        local circuitID=classToCircuit(classID, entityID, true,nil)
        local classLink=classLinkFn(classID,circuitID) --return '' worst case

        local tCell=mw.html.create('td')
        :attr('data-sort-value',class_sort[classID]) --sortkey
        :cssText("text-align:center;padding:0 0.5em")
        :wikitext(classLink)
       
        outTable["classCell"]=tCell
    end
    return outTable
end

local function fn_rider(entityID,timeOfRace,display_team,only_winner,country)
    local winners, countrytemp, result
    local WDlink_on = (wiki == "mk" or wiki == "ja")
    local thereisawinner=false
    
    if only_winner == 1 then
        winners = {Q20882667 = '', Q20882747=''} -- first, general or stage
    elseif only_winner == 0 then
        winners = { Q20882667 = '', Q20882668 = '',Q20882669 = ''} -- Q20882668 is 'second overall'
    else --3
        winners = { Q47640757='' } -- World Tour -- name not used here
    end
    if country==nil then countrytemp=false else countrytemp=country end
    winner(entityID, winners, timeOfRace, countrytemp, WDlink_on, display_team, true)
    
    local tCell=mw.html.create('td'):css("text-align:".. textalign ..";padding:0 0.5em")
    
    if only_winner == 0 then
        tCell:wikitext(winners.Q20882667)
        result=tostring(tCell)
        tCell=mw.html.create('td'):css("text-align:".. textalign ..";padding:0 0.5em"):wikitext(winners.Q20882668)
        result=result..tostring(tCell)
        tCell=mw.html.create('td'):css("text-align:".. textalign ..";padding:0 0.5em"):wikitext(winners.Q20882669)
        return result..tostring(tCell)
    else
        local tempwinner
        if only_winner == 1 then
            if winners.Q20882667~=nil and winners.Q20882667~='' then
                tempwinner=winners.Q20882667
            else
                tempwinner=winners.Q20882747
            end
        else
            tempwinner=winners.Q47640757
        end
        if tempwinner~='' and tempwinner~=nil then thereisawinner=true end
        return tCell:wikitext(tempwinner), thereisawinner
    end
end

local function compareDate(tdate) --test future
    if tdate then
        local today=os.date("*t")
        local _, _, y, m, d = string.find(tdate, "(%d+)%p(%d+)%p(%d+)")
        local tYear=tonumber(y)
        local tMonth=tonumber(m)
        local tDay=tonumber(d)
       
        if tYear>today['year'] then
            return true
        elseif tYear<today['year'] then
            return false  --the last race is the future
        else
            if tMonth>today['month'] then
                return true
            elseif  tMonth<today['month'] then
                return false
            else
                if tDay>today['day'] then   
                    return true
                elseif  tDay<today['day'] then   
                    return false
                else
                    return false --arbitrary
                end
            end
        end
    else
        return false --arbitrary
    end
end

local function calculateAge(birthDate, endDate) --test future
    local eYear, eMonth, eDay
    if birthDate then
        if not endDate then
            local today=os.date("*t")
            eYear=today['year']
            eMonth=today['month']
            eDay=today['day']
        else
            local _, _, y, m, d = string.find(endDate, "(%d+)%p(%d+)%p(%d+)")
            eYear=tonumber(y)
            eMonth=tonumber(m)
            eDay=tonumber(d)
        end

        local _, _, y, m, d = string.find(birthDate, "(%d+)%p(%d+)%p(%d+)")
        local tYear=tonumber(y)
        local tMonth=tonumber(m)
        local tDay=tonumber(d)
        local alreadyThisYear

        if eMonth>tMonth then
            alreadyThisYear=true   
        elseif  eMonth<tMonth then
            alreadyThisYear=false
        else
            if eDay>tDay then   
                alreadyThisYear=true
            elseif  eDay<tDay then   
                alreadyThisYear=false
            else
                alreadyThisYear=true
            end
        end
       
        if alreadyThisYear then
            return eYear-tYear, tYear, eYear+1
        else
            return eYear-tYear-1, tYear, eYear+1
        end
    else
        return 0, tYear, eYear+1
    end
end
           

local function evaluateWinnerMax(t)
    local winners = t.vainqueur
    local result
    local most_wins = 0
    local most_wins_ID = {}
    for winnerID, winner in pairs(winners) do
        if winner.count > most_wins then
            most_wins = winner.count
            most_wins_ID = { winnerID }
        elseif winner.count == most_wins then
            most_wins_ID[#most_wins_ID + 1] = winnerID
        end
    end

    if most_wins > 1 then
        for _, id in pairs(most_wins_ID) do
            if not result then
                result=winners[id].link
            else
                result=result.."<br>"..winners[id].link
            end
        end
       
        local _, gen_singular, gen_plural=plural(most_wins)
        if gen_singular then --slavic plural, 1 victory is not displayed
            word_victory=translate("raceinfobox",29)
        elseif gen_plural then
            word_victory=translate("raceinfobox",30)
        else
            word_victory=translate("raceinfobox",32) --singular
        end
        result=result.."<br>("..tostring(most_wins).." "..word_victory..")"
    end
    t.maxWinner=result
end

local function listOfWinners(itemID,t, team)
    local winners = {    Q20882667 = '',}-- Q20882667 is 'overall winner general classification'
    local winnersId={    Q20882667 = '',}--to detect disqualification
    local WDlink_on, sitelink

    -- WDlink_on is used to decide if a Wikidata flag will be shown
    if wiki == "mk" or wiki == "ja" or wiki == "ru" then WDlink_on = true else WDlink_on = false end

    -- Get the date to sort the editions
    for _, p527 in statements(itemID, 'P527') do  --_, p527
        local raceDate, year, raceID, entity_race, a, b
        raceId = p527.mainsnak.datavalue.value.id -- Qnumbers of the parts of a tour
        raceDate=getTimeOfRace(raceId)
        table.insert(t.race, { raceId=raceId, raceDate=raceDate, future=compareDate(raceDate)} ) --check if future
        table.sort(t.race, function(a,b) return a['raceDate'] < b['raceDate'] end) -- t.race is sorted after year
    end
    --look for the next race
    local lastRunEdition, lastEditionDate, nextEdition
    
    for num, race in ipairs(t.race) do
        if race['future'] then
            nextEdition=num
            break
        end
    end
        --Get the winners
    local numberOfEditions=0
    local lastWinner, winnerId

    if not team then --for race, a test shall be performed
        for num=1,#t.race do
            winners.Q20882667=''
            winnersId.Q20882667=''
            winner(t.race[num]['raceId'], winners, t.race[num]['raceDate'], false, WDlink_on, nil, nil, winnersId )
            if t.race[num]['future']==false then --in the past
                if winnersId.Q20882667~="Q30108381" then --cancelled
                    numberOfEditions=numberOfEditions+1
                    lastRunEdition=num
                    lastEditionDate=t.race[num]['raceDate']
                    lastWinner=winners.Q20882667
                end
            end
       
            winnerId=winnersId.Q20882667
            if winnerId~=nil and winnerId~='' and winnerId~='Q666' and winnerId~='Q30108381' then --code for disqualification
                if not t.vainqueur[winnerId] then
                    t.vainqueur[winnerId]={}
                    t.vainqueur[winnerId].link=winners.Q20882667
                    t.vainqueur[winnerId].count=0
                end
                t.vainqueur[winnerId].count=t.vainqueur[winnerId].count+1
            end   
        end
    else --for team there is nothing to check
        num=#t.race
        numberOfEditions=num
        lastRunEdition=num
        lastEditionDate=t.race[num]['raceDate']
    end

    local monthId=firstValue(itemID, 'P2922','id')
    if monthId then
        t.lastEditionMonth=getLabelFallback(monthId, {wikilang, 'en', 'fr', 'de'}) or ''
    else
        t.lastEditionMonth=contentLanguage:formatDate("M", lastEditionDate)
    end

    t.lastEditionYear=contentLanguage:formatDate("Y", lastEditionDate)
    t.numberOfEditions=numberOfEditions
    if not team then evaluateWinnerMax(t) end
    
    if lastRunEdition then
        t.lastWinner=lastWinner or '' --t.vainqueur[lastRunEdition]['link']
        sitelink = wikibase.getSitelink(t.race[lastRunEdition]['raceId'])
        if sitelink ~= nil then
            t.lastLink = "[[" .. sitelink .. "]]"
        else
            t.lastLink = nil
        end
    end
    
    if nextEdition then
        sitelink = wikibase.getSitelink(t.race[nextEdition]['raceId'])
        if sitelink ~= nil then
            t.nextLink = "[[" .. sitelink .. "]]"
        else
            t.nextLink = nil
        end
    end   
end

function getPeriodicity(itemID, t)
    local p = wikibase.getBestStatements(itemID, 'P2257')
    if p[1] and p[1].mainsnak.snaktype == 'value' then
        local period=p[1].mainsnak.datavalue.value.amount
        local periodunit=p[1].mainsnak.datavalue.value.unit
        if tonumber(period)==1 and periodunit == 'http://www.wikidata.org/entity/Q577' then
            return translate("raceinfobox",1).." ("..t.lastEditionMonth ..")"
        elseif tonumber(period)==1 and periodunit == 'http://www.wikidata.org/entity/Q5151' then
            return translate("raceinfobox",2)
        else
            return nil
        end
    else
        return nil
    end
end

local function getType(itemID)
    local result, typeID
    typeID =firstValue(itemID, 'P31', 'id')
    if typeID ~= nil then
        if typeID=="Q2912397" and wiki=="fr" then
            result="[[Cyclisme_sur_route#Épreuve_d'un_jour|Course d'un jour]]"
        else
            result=WPlinkpure(typeID)
        end
    end --else result=nil
       return result 
end

local function getFormerNames(itemID, PID)
    local listOfNames={}
    local langFallback, officialname,language

    if wiki=="mk" then
        langFallback= {wiki} --only exact language
    else
        langFallback= {wiki, 'en','fr', 'de', 'es', 'nl', 'it'} --all languages, but tested one at a time
    end
    
    local kk=1
    while #listOfNames == 0 and kk<=#langFallback do
        lang=langFallback[kk]
        kk=kk+1
        for _, prop in ipairs({PID}) do
            for _, p1813 in statements(itemID, prop) do
                language = p1813.mainsnak.datavalue.value.language
                officialname = p1813.mainsnak.datavalue.value.text
                if lang==language then --only exact language
                    local period, sTime=getPeriod(p1813.qualifiers)
                    if not sTime then sTime="+1900-01-01T00:00:00Z" end --first
                    table.insert(listOfNames,{sTime, period, officialname, language})
                end
            end
        end
    end           
    table.sort(listOfNames, function(a, b) return a[1] < b[1] end)
    return listOfNames
end

local function officialSite(itemID)
    local p856 = wikibase.getBestStatements(itemID, 'P856')
    if p856[1] and p856[1].mainsnak.snaktype == 'value' then
        local url = p856[1].mainsnak.datavalue.value
        return '['..url.." "..translate("raceinfobox",3)..']'
    end
    return nil
end

local function getKm(wiki)
    local km
    if wiki == "ar" then km = 'كم'
    elseif wiki == "mk" then km = 'км'
    elseif wiki == "ru" then km = 'км'
    elseif wiki == "ja" then km = 'キロメートル'
    else km = 'km' end
    return km
end

local function getm(wiki)
    local m
    if wiki == "ar" then m = 'م'
    elseif wiki == "mk" then m = 'м'
    elseif wiki == "ru" then m = 'м'
    elseif wiki == "ja" then m = ''
    else m = 'm' end
    return m
end

local function getKg(wiki)
    local kg
    if wiki == "ar" then kg = 'كجم'
    elseif wiki == "mk" then kg = 'кг'
    elseif wiki == "ru" then kg = 'кг'
    elseif wiki == "ja" then kg = ''
    else kg = 'kg' end
    return kg
end

local function checkkm(p)
    local km, unit
    if p[1] and p[1].mainsnak.snaktype == 'value' then
        km = tonumber(p[1].mainsnak.datavalue.value.amount)
        unit = p[1].mainsnak.datavalue.value.unit
        if unit == 'http://www.wikidata.org/entity/Q828224' then
            return km
        end
    end
    return nil
end

local function checkm(p)
    local m, unit
    if p[1] and p[1].mainsnak.snaktype == 'value' then
        m = tonumber(p[1].mainsnak.datavalue.value.amount)
        unit = p[1].mainsnak.datavalue.value.unit
        if unit == 'http://www.wikidata.org/entity/Q11573' then
            return m
        elseif unit=='http://www.wikidata.org/entity/Q174728' then --cm
            return m*0.01
        end
    end
    return nil
end

local function checkkg(p)
    local kg, unit
    if p[1] and p[1].mainsnak.snaktype == 'value' then
        kg = tonumber(p[1].mainsnak.datavalue.value.amount)
        unit = p[1].mainsnak.datavalue.value.unit
        if unit == 'http://www.wikidata.org/entity/Q11570' then
            return kg
        end
    end
    return nil
end

local function getHeight(entityID)
    local m
    local text
    local lang = contentLanguage

    local p = mw.wikibase.getBestStatements(entityID, 'P2048')   
    m= checkm(p)
    if m then
        text = lang:formatNum(m)
        if wiki == 'fo' then
            text = string.gsub(text, "%.", ",")
        end
        text = text .. ' ' .. getm(wiki)
    end
    return text
end

local function getWeight(entityID)
    local kg
    local text
    local lang = contentLanguage
    
    local p = mw.wikibase.getBestStatements(entityID, 'P2067')   
    kg = checkkg(p)

    if kg then
        text = lang:formatNum(kg)
        if wiki == 'fo' then
            text = string.gsub(text, "%.", ",")
        end
        text = text .. ' ' .. getKg(wiki)
    end
    return text
end

local function getDistance(raceID, addUnit)
    local km
    local p = mw.wikibase.getBestStatements(raceID, 'P3157') -- P3157 is 'event distance'
    km =checkkm(p)
    if not km then --for stage race we can sum the distances from each stage
        local stagep, tempkm
        for _, p527 in statements(raceID,'P527') do
            stageID = p527.mainsnak.datavalue.value.id
            stagep=mw.wikibase.getBestStatements(stageID, 'P3157')
            tempkm=checkkm(stagep)
            if tempkm then
                if not km then
                    km=tempkm
                else
                    km=km+tempkm
                end
            end
        end
    end
    local text
    local lang = contentLanguage
    if km then
        -- The unit should always be km. Skip if it isn't.
        text = lang:formatNum(km)
        if wiki == 'fo' then
            text = string.gsub(text, "%.", ",")
        end
        if addUnit then
            text = text .. ' ' .. getKm(wiki)
        end
    end
    return text, km
end

local function getKmh(wiki)
    local kmh
    if wiki == "ar" then kmh = 'كم/س'
    elseif wiki == "da" then kmh = 'km/t'
    elseif wiki == "fo" then kmh = 'km/t'
    elseif wiki == "nl" then kmh = 'km/u'
    elseif wiki == "no" then kmh = 'km/t'
    elseif wiki == "mk" then kmh = 'км/ч'
    elseif wiki == "ru" then kmh = 'км/ч'
    elseif wiki == "ja" then kmh = 'キロメートル毎時'
    else kmh = 'km/h' end
    return kmh
end

local function getElevation(raceID)
    local l10nDef = {
        ["en"] = " m",
        ["ar"] = " م",
    }
    local l10n = l10nDef[wiki] or l10nDef["en"]
    local temp = mw.wikibase.getBestStatements(raceID, 'P7297')
    if temp[1] and temp[1].mainsnak.snaktype == 'value' then
        local unit = temp[1].mainsnak.datavalue.value.unit
        if unit == 'http://www.wikidata.org/entity/Q11573' then
            return tonumber(temp[1].mainsnak.datavalue.value.amount) .. l10n
        end
    end
    return nil
end

local function getSpeed(raceID, addUnit,kmdistance, property)
    local p = mw.wikibase.getBestStatements(raceID, 'P2052') -- P2052 is 'speed'
    local kmh, unit, text, found, timeOfRace
    local lang = contentLanguage
    if p[1] and p[1].mainsnak.snaktype == 'value' then
        kmh = tonumber(p[1].mainsnak.datavalue.value.amount)
        unit = p[1].mainsnak.datavalue.value.unit
        if unit == 'http://www.wikidata.org/entity/Q180154' then -- Q180154 is 'kilometre per hour'
            found=true
        end
    end
    if not found and kmdistance then --calculate speed
        local p2321= wikibase.getBestStatements(raceID, property) --winner supposed to be first of overall classification
        if p2321 and p2321[1] and p2321[1].mainsnak.snaktype == 'value' then
            local q = p2321[1].qualifiers
            if q and q.P1352 and q.P1352[1].snaktype == 'value' then --rank
                for _, q1352 in pairs(q.P1352) do
                    rank = tonumber(q1352.datavalue.value.amount)
                end
                if rank == 1 then
                    timeOfRace=qualifieramount(p2321[1], 'P2781') --get time
                end
            end

            if timeOfRace then
                found=true
                kmh=math.modf(1000*kmdistance/(timeOfRace/3600))/1000
            end
        end
    end
    if found then   
        -- The unit should always be km/h. Skip if it isn't.
        text = lang:formatNum(kmh)
        if wiki == 'fo' then
            text = string.gsub(text, "%.", ",")
        end
        if addUnit then
            text = text .. ' ' .. getKmh(wiki)
        end
    end
    return text
end

local function getGenderCode(riderID, default)
    local gender
    local p21 = mw.wikibase.getBestStatements(riderID, 'P21') -- P21 is gender
    if p21[1] and p21[1].mainsnak.snaktype == 'value' then
        local g = p21[1].mainsnak.datavalue.value.id
        if g == 'Q6581097' then gender = 'm' -- Male
        elseif g == 'Q6581072' then gender = 'f' -- Female
        elseif g == 'Q1052281' then gender = 't' -- Transgenre
        end
    end
    return gender or default -- default is for teams, n or f
end

function number(gender, b, wiki)
    local str
    if b==nil or b=="" then return "" end
    if wiki=="ar" then
        str = b
    elseif wiki == "ca" then
    if b==1 then str = b.."r"
        elseif b==2 then str = b.."n"
        elseif b==3 then str = b.."r"
        elseif b==4 then str = b.."t"
        else str = b.."è"
        end
    elseif wiki=="es" then
        if gender == 'm' or gender == 'n' then str = b..".º"
        elseif gender == 'f' then str = b..".ª"
        else str = b.."."
        end
    elseif wiki=="fr" then
        if b==1 then
            if gender == 'm' then str="1<sup>er</sup>"
            elseif gender == 'f' or gender == 'n' then str="1<sup>re</sup>"
            else str="1<sup>e</sup>"
            end
        else str=b.."<sup>e</sup>"
        end
    elseif wiki=="nl" then str=b.."e"
    elseif wiki=="ru" then str=b.."-й"
    elseif wiki=="eo" then str=b.."-a"
    elseif wiki=="ast" then
        if gender == 'm' or gender == 'n' then str = b.."ᵘ"
        elseif gender == 'f' then str = b.."ª"
        else str = b.."."
        end
    else str = b .. ". "
    end
    return str
end

local function calculateTime(t)
    local time = tonumber(t)
    local h, m, s = 0, 0, 0
    local str = ''

    if time == nil then return '' end
    if time < 60 then s = time
    elseif time < 3600 then m = math.modf(time/60) s = time - m*60
    else h = math.modf(time/3600) m = math.modf((time - h*3600)/60) s = time - h*3600 - m*60
    end

    if h>0 then str = str..mw.ustring.format ('%i'..translate("unit",2), h) end
    if m>=0 and h>0 then str = str.. mw.ustring.format('%02i'..translate("unit",3), m) end
    if m>0 and h==0 then str = str.. mw.ustring.format('%i'..translate("unit",3), m) end
    if s>=0 and (h>0 or m>0) then str = str.. mw.ustring.format('%02i'..translate("unit",4), s) end
    if s>=0 and h==0 and m==0 then str = str.. mw.ustring.format('%i'..translate("unit",4), s) end
    return str --time..': '..h..' '..m..' '..s
end

function func_error_message(x)
    local l10nDef = {
        ["en"] = {'Property <1> is missing in item "<2>" (<3>)'},
        ["ar"] = {'الخاصية <1> غير موجودة في العنصر "<2>" (<3>)'},
    }
    local l10n = l10nDef[wiki]
    if not l10n then l10n = l10nDef["en"] end  -- default
    return l10n[x]
end

local function getMissingLabelTrackingCategory()
    local l10nDef = {
        ["//cs.wikipedia.org"] = '[[Kategorie:Údržba:Doplnit štítek na Wikidatech]]',
        ["//lv.wikipedia.org"] = '[[Category:Vikidatos trūkst nosaukuma latviešu valodā]]',
        ["//he.wikipedia.org"] = '[[קטגוריה:ויקינתונים:ערכים חסרי תווית בעברית: קבוצת אופניים]]',
    }
    local l10n = l10nDef[mw.site.server]
    if not l10n then
        l10n = ''
    end
    return l10n
end

local function getStageLabel(inp)
    local a
    local b=''
    local this_label=''
    if inp then
        a, _ = string.gsub(inp, "%a", "") -- 20, not 20a
        if string.find(inp, "%a") then
            b = string.sub(inp, string.find(inp, "%a"))
        end
        if inp == "0" then
            this_label = translate("func_prologue")
        else
            this_label = stageLink(inp, a, b)
        end
    end
    return this_label
end

--[[ Make a table row for infoboxes with links to previous and next ]]
local function getPreviousNextLine(raceID, stage)
    local previousID = firstValue(raceID, 'P155', 'id') -- P155 is 'follows'
    local nextID = firstValue(raceID, 'P156', 'id') -- P156 is 'followed by'
    if not nextID or not previousID then
        for _, s in statements(raceID, 'P3450') do        -- for items using P3450
            local q = s.qualifiers
            if q then
                if not previousID and q.P155 and q.P155[1] and    q.P155[1].snaktype == 'value' then
                    previousID = q.P155[1].datavalue.value.id
                end
                if not nextID and q.P156 and q.P156[1] and    q.P156[1].snaktype == 'value' then
                    nextID = q.P156[1].datavalue.value.id
                end
            end
        end
    end
    if not previousID and not nextID then
        return ''
    end

    local previousText, nextText = '', ''
    
    local direction = contentLanguage:getDir()
    local previous_sign = (direction == 'ltr') and '&#x25C0;' or '&#x25B6;'
    local next_sign = (direction == 'ltr') and '&#x25B6;' or '&#x25C0;'
    
    local this_label
    if previousID then
        if stage  then
             local series_ordinal= firstValue(previousID, 'P1545', 'value')
             this_label=getStageLabel(series_ordinal)
        else
            this_label = getYear(previousID)
        end
        local link = wikibase.getSitelink(previousID)
        if link then
            previousText = '<span style="color:#3366CC">[[' .. link .. '| ' .. previous_sign .. this_label .. ']]</span>'
        else
            previousText = '<span style="color:#3366CC">' .. previous_sign .. '</span> ' .. this_label
        end
    end
    if nextID then
        if stage then
            local series_ordinal= firstValue(nextID, 'P1545', 'value')
            this_label=getStageLabel(series_ordinal)
        else
            this_label = getYear(nextID)
        end
        local link = wikibase.getSitelink(nextID)
        if link then
            nextText = '<span style="color:#3366CC">[[' .. link .. '|' .. this_label .. next_sign .. ']]</span>'
        else
            nextText = this_label .. ' <span style="color:#3366CC">' .. next_sign .. '</span>'
        end
    end
    local direction = contentLanguage:getDir()
    
    local outTable = mw.html.create('tr')
    local tCell=outTable:tag('td')
    tCell:cssText("text-align:" .. ((direction == 'ltr') and 'left' or 'right')):wikitext(previousText)
    if stage ~= nil and wiki=="ar" then
        tCell:css('width','50%')
    end
     tCell=outTable:tag('td')
     :cssText("text-align:" .. ((direction == 'ltr') and 'right' or 'left')):wikitext( nextText)
    if stage ~= nil and wiki=="ar" then
        tCell:css('width','50%')
    end
    return outTable
end

--== Functions for infobox
-- functions for infoboxs
local function infoGetOthers(others, entityID)
    if not others[1].content then --picture
        others[1].content, others[2].content = getLogo(entityID)
        if not others[1].content then
            others[1].content, others[2].content = getImage(entityID) -- picture, caption
        end
    end

    if not others[3].content then  -- map
        others[3].content, others[5].content = getMap(entityID)  -- P242 is 'locator map image'
    end
    
    if not others[4].content then  -- map
        others[4].content, others[6].content = getSectionalView(entityID) -- sectional_view
    end   
end

local function infoGetPlaceOrCountry(details,index, entityID, timeOfRace, PID) --generalized infoGetCountry
    if not details[index].content then -- country
        -- This function gives countries where the race take place
        local place = {}

        if not place[1] then
            for _, p17 in statements(entityID, PID) do -- P17 is 'country'
                local countryID = p17.mainsnak.datavalue.value.id
                if PID=='P17' then
                    place[#place + 1] = flag(countryID, timeOfRace) .. ' ' .. getCountryName(countryID)
                else
                    place[#place + 1] =  wikibase.getLabel(countryID)
                end
            end
        end

        if place[1] then
            if #place > 1 then
                details[index].name = details[index].name_plural
            end
            details[index].content = table.concat(place, '<br/>')
        end
    end
end

local function infoGetPlace(details,index, entityID, timeOfRace)
    infoGetPlaceOrCountry(details,index, entityID, timeOfRace, "P276")
end

local function infoGetCountry(details,index, entityID, timeOfRace)
    infoGetPlaceOrCountry(details,index, entityID, timeOfRace, "P17")
end

local function infoGetStartEnd(details,index, entityID, timeOfRace)
    if not details[index].content then -- start place
        local place = firstValue(entityID, 'P1427', 'id') -- P1427 is 'start point'
        details[index].content = place and getPlaceLink(place, timeOfRace)
    end

    if not details[index+1].content then -- end place
        local place = firstValue(entityID, 'P1444', 'id') -- P1444 is 'destination point'
        details[index+1].content = place and getPlaceLink(place, timeOfRace)
    end
end

local function infoGetParticipants(details,index, entityID)
        -- Function that give the number of cyclists at the beginning and at the finishing of a race
    for _, p1132 in statements(entityID, 'P1132') do -- P1132 is 'number of participants'
        local amount = tonumber(p1132.mainsnak.datavalue.value.amount) -- tonumber to remove starting '+'
        for _, q in qualifiers(p1132, 'P276') do -- P276 is 'location'
            local location = q.value.id
            if location == "Q529711" then -- Q529711 is 'beginning'
                if not details[index].content then details[index].content = amount end -- participants at start
            elseif location == "Q12769393" then -- Q12769393 is 'end'
                if not details[index+1].content then details[index+1].content = amount end -- participants at end
            end
        end
    end
end

local function infoInitTab(width, name, icon, cellpadding)
    if width==nil then width= '320px' end
    
    local tab = mw.html.create('table')
    if wiki == "eo" then
        tab:cssText(standardtablecss):css('width','23em')
        :addClass('infobox')
    else
        cellpadding=tostring(cellpadding or 4)
        tab:attr('cellpadding',cellpadding)
        :attr('cellspacing','0')
        :cssText(standardtablecss)
        :cssText("float:"..floatinfobox.."; max-width:"..width)
    end
    local tCell=tab:tag('tr'):tag('td'):attr('colspan','2')
    :cssText('border-bottom:5px solid white; font-size:175%; text-align:center')
    :css('background-color',backgroundColor)
    local topTable = tCell:tag('table')
    :cssText('width:100%')
    local tRow=topTable:tag('tr')
    tRow:tag('td'):wikitext(name or '')
    tRow:tag('td'):wikitext(icon or '')
    
    return tab
end

local function addARow(name, content)
    local tRow
    if content then
        tRow= mw.html.create('tr'):css('vertical-align','top')
        tRow:tag('td'):css('width','40%'):css('font-weight','bold')
        :wikitext(name)
        tRow:tag('td'):wikitext(content)
    end
    return tRow
end

local function addATitle(title)
    local tRow
    if title then
        tRow= mw.html.create('tr'):tag('td'):attr('colspan','2')
        :css('text-align','center')
        :css('background-color',backgroundColor)
        :css('font-weight','bold')
        :wikitext(title)
    end
    return tRow
end

local function infoFillOthersDetails(tab, others, details,title, pxmax)
    if not pxmax then
        pxmax="300px"
    end
    
    if others and others[1].content then -- picture
        tab:tag('tr'):tag('td'):attr('colspan','2'):css('text-align','center')
        :wikitext("[[File:" .. others[1].content .."|center|"..pxmax.."]]")
        if others and others[2].content then -- caption
            tab:tag('tr'):tag('td'):attr('colspan','2'):css('text-align','center'):css('font-size','80%')
            :wikitext(others[2].content)
        end
    end
    if details then
        tab:node(addATitle(title))
        for _, row in ipairs(details) do
            tab:node(addARow(row.name, row.content)) --node check itself if nil
        end
    end
end

local function infoFillOthersMap(tab, others)
    if others[3].content then -- map
        tab:tag('tr'):tag('td'):attr('colspan','2'):css('text-align','center')
        :wikitext("[[File:".. others[3].content .. "|center|300px]]")
        if others[5].content then -- caption
            tab:tag('tr'):tag('td'):attr('colspan','2'):css('text-align','center'):css('font-size','80%')
            :wikitext(others[5].content)
        end
    end
    if others[4].content then -- map
        tab:tag('tr'):tag('td'):attr('colspan','2'):css('text-align','center')
        :wikitext("[[File:".. others[4].content .. "|center|300px]]")
        if others[6].content then -- caption
            tab:tag('tr'):tag('td'):attr('colspan','2'):css('text-align','center'):css('font-size','80%')
            :wikitext(others[6].content)
        end
    end
end

local function wdDoc(tab, s, translation, ID)
    local tCell=tab:tag('tr'):tag('td')
    local tC
    local commons_cat=firstValue(ID, 'P373', 'id')
    
    if commons_cat then
        commons_cat=string.gsub(commons_cat, '%s', '_')
        local icon="[[File:Commons-logo.svg|12px|link=https://commons.wikimedia.org/wiki/Category:"..commons_cat.."]]"
        tC=tCell:cssText('text-align:left; border-top:3px solid '..backgroundColor..'; font-size:75%')
        :wikitext(icon):tag('td')
    else
        tC=tCell:attr('colspan','2')
    end
    local wd_link = wdLink(ID)
    local link = "[[" .. s .. "|" .. translation .. "]] "..wd_link
    if wiki == "ar" then
        link = wd_link .." [[" .. s .. "|" .. translation .. "]]"
    end
    tC:cssText('text-align:right; border-top:3px solid '..backgroundColor..'; font-size:75%')
    :wikitext(link)
end

local function listWPlink(details, index, entityID, PID, bool_link)
    local org={}
    for _, p in statements(entityID, PID) do
        if p and p.mainsnak.snaktype == 'value' then
            if bool_link then
                table.insert(org,WPlinkpure(p.mainsnak.datavalue.value.id))
            else
                local label=wikibase.getLabelByLang(p.mainsnak.datavalue.value.id, wiki)
                table.insert(org,label)
            end
        end
    end
    if org[1] then
        if #org > 1 then
            details[index].name = details[index].name_plural
        end
        details[index].content = table.concat(org, '<br/>')
    end   
end

--Display in a chronological order fields in a table
local function listWPlinkChrono(details, index, entityID, listOfProperty, option, initialYear, display_flag, comma)
    local period, sTime, value, ID, temp
    local list={}
    
    if not initialYear then initialYear="1900" end
    
    if not details[index].content then   
        for _, prop in ipairs(listOfProperty) do
            if #list==0 then --if P1532 is used P17 is not used
                for _, p in statements(entityID, prop) do
                    if p and p.mainsnak.snaktype == 'value' then
                        ID=p.mainsnak.datavalue.value.id       
                        if p.qualifiers then
                            period, sTime=getPeriod( p.qualifiers, true)
                        end
                        if not sTime then sTime="+"..initialYear.."-01-01T00:00:00Z" end --first
                   
                        if option =='label' then
                            value=wikibase.getLabelByLang(ID, wiki)   
                        elseif option == 'country' then
                            if display_flag then   
                                value= flag(ID, sTime).." "..getCountryName(ID)
                            else   
                                value=getCountryName(ID)
                            end   
                        elseif option=='officialname' then
                            value=getOfficialName(ID, sTime,false) --official name is necessary because of continental team change in ProTeam
                        elseif option =='place' then
                            value=getPlaceLink(ID, sTime)
                        elseif option=='UCIcode' then
                            value=getTeamCodeCat(entityID, sTime) --! getTeamCodeCat uses teamID
                        elseif option=='money' then
                            local amount=p.mainsnak.datavalue.value.amount
                            local unit=p.mainsnak.datavalue.value.unit
                            value=dispmoney(amount, unit) or ''
                        else --rider
                            value=getRiderLink(ID, sTime)
                        end
                        if value then
                            table.insert(list,{sTime,period,value})
                        end
                    end
                end
            end
        end
        if #list ~=0 then
            table.sort(list, function(a, b) return a[1] < b[1] end)
        end
        local separator='<br/>'
        if comma then separator=', ' end
       
       
        if list and #list==1 then       
            details[index].content=list[1][3] or ''
        elseif list and #list~=0 then
            details[index].name = details[index].name_plural   
            details[index].content=''           
            for _, v in pairs(list) do
                temp=v[3] or ''
                if v[2] then
                    temp=temp..' <small>'..v[2]..'</small>'..separator
                else
                    temp=temp..separator
                end
                details[index].content=details[index].content..temp
            end   
        end
    end   
end

-- == Functions for team roster
local function getReason(riderReason, riderRef, p527,timeOfRace,riderEnd) --reason for end
    local listofproperty={'P1642','P1643','P1534'}
    local outTable={}
    local seasonYear, endYear
    if timeOfRace then
        seasonYear=tonumber(string.sub(timeOfRace, 2, 5))
    end
    if riderEnd then
        endYear=tonumber(string.sub(riderEnd, 2, 5))
    end
    
    --if not the last season, do not display the reason for end
    if (riderReason == nil and (not endYear or
        (seasonYear and endYear and (seasonYear== endYear)))) then --if no riderReason before then look for it, otherwise don't touch it
        for _,v in ipairs(listofproperty) do
            for _, q in qualifiers(p527, v) do
                riderReason = q.value.id
            end
        end
        if riderReason then
            local label = string.gsub(wikibase.label(riderReason), "%b()", "") or getLabelFallback(riderReason,{'en', 'fr', 'de'})
            riderRef = getReference(p527, 1)
            riderReason = ', ' .. label
        end
    end
    return riderReason, riderRef
end

local function getPosition(riderPosition,v)
    local stagiaire
    if riderPosition == nil then -- find the 'position' (P39) of a rider
        for _, q in qualifiers(v, 'P39') do
            stagiaire = q.value.id
            local label = string.gsub(wikibase.label(stagiaire), "%b()", "") or getLabelFallback(stagiaire,{'en', 'fr', 'de'})
            Sitelink = wikibase.getSitelink('Q2328847')
            if Sitelink then
                riderPosition=', ' .. "[["..Sitelink .."|"..label.."]]"
            else
                riderPosition =', ' .. label
            end
        end
    end
    return riderPosition
end

local function trans(date, month, day)
    if date ~= '' and date~=nil then
        local _, _, y, m, d = string.find(date, "(%d+)-(%d+)-(%d+)")
        if m == '00' then m = month end
        if d == '00' then d = day end
        date = '+'..y..'-'..m..'-'..d..'T00:00:00Z'
        return date
    end
    return nil
end

local function parseDate(date, defaultYear, defaultMonth, defaultDay, errortext, etext)
    local y, m, d
    local date=trans(date, defaultMonth, defaultDay)
    if not date then    
        date = '+'..defaultYear..'-'..defaultMonth..'-'..defaultDay..'T00:00:00Z'
        y=defaultYear
        m=defaultMonth
        d=defaultDay
        errortext=errortext..etext
    else
        _, _, y, m, d = string.find(date, "(%d+)-(%d+)-(%d+)")
        if not y or y=="0000" then
            y=defaultYear
            errortext=errortext..etext
        end
        date = '+'..y..'-'..m..'-'..d..'T00:00:00Z'
    end
    
    return date, y, m, d, errortext
end

local function findLastName(label,wiki)
    if not label then label = '' end
    local _, count = string.gsub(label, " ", " ")
    local names
    local a,b,c,d = '', '', '', ''
    local done = false
    if count ~= nil then count = count + 1 else count = 1 end

    if count > 1 then
        if count == 2 then
            if label ~= '' then
                a, b = string.match(label, "(%S+)%s+(%S+)")
                names = b..' '..a
            end
        else
            local name_parts_mk = {'да', 'ди', 'де', 'Де', 'ла', 'Ле', 'тен', 'ван', 'Ван'}
            local name_parts_ru = {'да', 'ди', 'де', 'Де', 'ла', 'Ле', 'тен', 'ван', 'Ван'}
            local name_parts    = {'da', 'de', 'di', 'De', 'la', 'Le', 'ten', 'van', 'Van'}
            if count == 3 and label ~= '' then
                a, b, c = string.match(label, "(%S+)%s+(%S+)%s+(%S+)")
                if wiki == 'mk' then
                    for _,v in ipairs(name_parts_mk) do if b == v then names = b..' '..c..' '..a done = true break end end
                elseif wiki == 'ru' then
                    for _,v in ipairs(name_parts_ru) do if b == v then names = b..' '..c..' '..a done = true break end end
                else
                    for _,v in ipairs(name_parts) do if b == v then names = b..' '..c..' '..a done = true break end end
                end
                if not done then
                    names = c..' '..a..' '..b
                    done = true
                end
            end
            if count > 3 and label ~= '' then
                a, b, c, d = string.match(label, "(%S+)%s+(%S+)%s+(%S+)%s+(%S+)")
                if wiki == 'mk' then
                    for _,v in ipairs(name_parts_mk) do if c == v then names = c..' '..d..' '..a..' '..b done = true break end end
                    for _,v in ipairs(name_parts_mk) do if b == v then names = b..' '..c..' '..d..' '..a done = true break end end
                elseif wiki == 'ru' then
                    for _,v in ipairs(name_parts_ru) do if c == v then names = c..' '..d..' '..a..' '..b done = true break end end
                    for _,v in ipairs(name_parts_ru) do if b == v then names = b..' '..c..' '..d..' '..a done = true break end end
                else
                    for _,v in ipairs(name_parts) do if c == v then names = c..' '..d..' '..a..' '..b done = true break end end
                    for _,v in ipairs(name_parts) do if b == v then names = b..' '..c..' '..d..' '..a done = true break end end

                end
                if not done then names = label.."%"..b end --b..' '..c..' '..d..' '..a end
            end
        end
    end
    return names or ''
end

local function findSortKey(riderID, correctlanguage, wikiIsSlavic)
    --find the last name to sort
    if wikiIsSlavic and correctlanguage then
        local label = wikibase.getLabelByLang(riderID, wiki)
        if not label then
            label = getLabelFallback(riderID, {'en', 'fr', 'de', 'es'})
            return findLastName(label,wiki)
        else
            local nametable = mw.text.split(label, ",")
            if nametable[2] then --there is a coma so the lastname is first
                return nametable[1]..nametable[2]
            else --no coma
                return findLastName(label,wiki)
            end
        end
    else
        local label = getLabelFallback(riderID, {'en', 'fr', 'de', 'es'})
        return findLastName(label,wiki)
    end
end

--== V) Main functions ==
--=== A) Function race reference ===
local function race_reference(raceID)
    -- Allow to display the reference below the classifications --
    local bases={
        {"ProCyclingStats", "P2327", "http://www.procyclingstats.com/race.php?id="},
        {"Cycling Quotient", "P2648", "http://www.cqranking.com/men/asp/gen/race.asp?raceid="},
        {"Cycling Archives", "P2330", "http://www.cyclingarchives.com/ritfiche.php?ritid="},
        {"Cycling Quotient", "P2708", "http://www.cqranking.com/women/asp/gen/race.asp?raceid="}
    }
    local links = {}
    local ref
    for _, base in pairs(bases) do
        local p = mw.wikibase.getBestStatements(raceID, base[2])
        if p[1] and p[1].mainsnak.snaktype == 'value' then
            if base[2]=="P2648" and p[1].mainsnak.datavalue.value=="1" then --code for general reference of results
                ref=getReference(p[1], 1)
                if ref then    table.insert(links, ref) end
            else
                table.insert(links, ' [' .. base[3] .. p[1].mainsnak.datavalue.value .. " " .. base[1] ..']')
            end
        end
    end
    if #links == 1 then
        return translate("race_reference", 1) .. table.concat(links)
    elseif #links > 1 then
        return translate("race_reference", 2) .. table.concat(links)
    else
        return ''
    end
end

--=== B) Calendar ===
function p.calendarcustom(frame)
    local headers={2} --date

    if wiki == "ar" and string.match(frame:getParent():getTitle(), '%P+') == mw.site.namespaces.Template.name
    then frame = frame:getParent() end

    if frame.args[1] ~= nil then calendarID = string.gsub(frame.args[1], "%c", "") end

    local display_numbering=false --default
    local country_column=2
    if istrue(frame.args['display_numbering']) then
        display_numbering=true
        table.insert(headers, 3)
        country_column=3
    end
    --no_country modify the way the country is displayed
    local no_country={}
    if istrue(frame.args['no_country']) or wiki == "ar" then
        no_country={wiki}
    end
    -- country --
    table.insert(headers, 5)
    --race--
    table.insert(headers, 4)

    local display_class=false
    if istrue(frame.args['display_class']) or wiki == "ar" then
        display_class=true
        table.insert(headers, 6)
    end
    
    table.insert(headers, 7) --winner
    local only_winner=1
    if istrue(frame.args['podium']) or wiki == "ar" then
        only_winner =0
        table.insert(headers, 8) --second
        table.insert(headers, 9)    --third   
    end
    
    local display_leader=false
    if istrue(frame.args['display_leader']) then
         display_leader=true
         table.insert(headers, 10)
    end
    local display_team =false
    if istrue(frame.args['display_team']) then
        display_team =true
    end

    local data_type={}
    for ii=1,#headers do
        table.insert(data_type,'')
    end
    
    local womenrace_bool=isWomenrace(calendarID)
    local s = {
        header_function = "calendar", -- translations are in function Calendar
        header_1 = 1000, -- translation 1 in function Calendar is printed in the upper part of the table header
        header_2 = headers,-- translations 2, 3, 4, 5, 6 in function Calendar are printed in this order
        title=wikibase.getLabel(calendarID),  -- in the lower part of the table header. The second value 3 in {4, 3} tells where the icon will go.
        country_column = country_column,
        data_sort_type = data_type, -- see https://meta.wikimedia.org/wiki/Help:Sorting
        item = calendarID,
        property = 'P527',
        no_country = no_country,
        only_winner = only_winner,
        display_numbering =  display_numbering,
        error_message = 0,
        displayed_class =nil,
        display_team=display_team,
        display_class=display_class,
        display_leader= display_leader,
        frame=frame,
        womenrace_bool=womenrace_bool
    }
    return calendar_main(s, tableA(s))
end

function p.calendar(frame)
    ----- function to display UCI calendar of one year ----
    ----- based on WWTcalendar function -----
    ----- author: Mr. Ibrahem -----
    local calendarID
    if wiki == "ar" then frame = frame:getParent() end
    local UCI = data.UCIYearToQ
    
    local header_1_tab = {["UWT"]=13 ,["europe"]=14 ,["asia"]=15,["america"]=16 ,["africa"]=17 ,["oceania"]=18, ["WWT"]=11, ["women"]=1, ["Pro"]=22}
    local display_code_tab=  {["UWT"]=1 ,["europe"]=2 ,["asia"]=2,["america"]=2 ,["africa"]=2 ,["oceania"]=2, ["WWT"]=1, ["women"]=2, ["Pro"]=2}
    local header_1_number = 12
    
    local tempdic
    local tempdic1 = {
        header_2 =  {2, 3,5, 4, 7, 8, 9, 10},
        only_winner =0,
        display_numbering=true,
        display_team=false,
        display_class=false,
        display_leader=true
    }   
    local tempdic2 = {
        header_2 =   {2, 5, 4, 6, 7},
        only_winner =1,
        display_numbering=false,
        display_team=true,
        display_class=true,
        display_leader=false
    }

    for key, v in pairs(UCI) do
        if not calendarID  and frame.args[key] then
            local year = frame.args[key]
            year = string.gsub(year , "%c", "")
            if v[year] then
                calendarID = v[year]
                header_1_number = header_1_tab[key]
                display_code = display_code_tab[key]
            end
        end
    end
    if wiki == "ar" then
        if not (frame.args["code"] and frame.args["code"] == "2") then
            display_code = 1
        end
        if calendarID == "" and frame.args.test then
            calendarID = frame.args.test
        end
    end
    if not calendarID or calendarID == "" then return "" end
    if display_code == 1 then
        tempdic=tempdic1
    else
        tempdic=tempdic2
    end
    if istrue(frame.args['display_numbering']) then
        tempdic.display_numbering=true
    elseif frame.args['display_numbering'] and istrue(frame.args['display_numbering']) == nil then
        tempdic.display_numbering=false
    end
    local womenrace_bool=isWomenrace(calendarID)
    local s = {
        header_function = "calendar", -- translations are in function Calendar
        header_1 = header_1_number, -- t
        header_2 = tempdic.header_2,
                    -- in the lower part of the table header. The second value 3 in {4, 3} tells where the icon will go.
        country_column = 3,
        data_sort_type ={'', 'unsortable', '', '', '','',''},   -- -- see https://meta.wikimedia.org/wiki/Help:Sorting
        item = calendarID,
        property = 'P527',
        no_country = no_country_calendar,
        only_winner = tempdic.only_winner,
        display_numbering = tempdic.display_numbering,
        error_message = 0,
        displayed_class = nil, --all
        display_team=tempdic.display_team,
        display_class=tempdic.display_class,
        display_leader=tempdic.display_leader,
        frame=frame,
        womenrace_bool=womenrace_bool
    }
    return calendar_main(s, tableA(s))
end

function calendar_main(s, resultTable)--Display the UCI women calendar of one year
    localframe=s.frame
    local calendarID=s.item
    local fn_racetable,fn_datetable, fn_countrytable
    local t_Body ={}
    local womenrace_bool=isWomenrace(calendarID)

    local temp=firstValue(calendarID, s.property)
    if not temp then s.error_message = 2 return '' end

    local country=getCountryBool(s.no_country)
    if available_list==false then country=false end --otherwise the display put no "country" column...

    ----- Begin of the main part of the code
    for kk, p527 in statements(calendarID, 'P527') do
        local RaceID = p527.mainsnak.datavalue.value.id
        ---- Create a row ----
        fn_datetable = fn_date(RaceID)
        fn_racetable= fn_race(RaceID,s.displayed_class,s.display_class,fn_datetable["timeOfRace"],nil,country)

        if fn_racetable["raceCell"]~=nil then --otherwise the class is not display
            fn_countrytable=fn_country(RaceID,
                                       fn_datetable["timeOfRace"],
                                       country,
                                       fn_racetable["raceCell"],
                                       fn_racetable["parentID"]
                                       )
           
            local tRow = mw.html.create('tr'):cssText( "line-height: 1.8em; padding: 5px;")

            tRow:node(fn_datetable["tCell"])
            if s.display_numbering == true then
                tRow:tag('td'):cssText("text-align:center;padding:0 0.5em"):wikitext(tostring(kk))
            end
           
            tRow:node(fn_countrytable["tCell"])
            if country then
                tRow:node(fn_racetable["raceCell"])
            end
            if fn_racetable["classCell"] then tRow:node(fn_racetable["classCell"]) end --class
            local tCell, _ =fn_rider(RaceID,fn_datetable["timeOfRace"],s.display_team,s.only_winner)
            tRow:node(tCell)
            if s.display_leader==true then
                tRow:node(fn_rider(RaceID,fn_datetable["timeOfRace"],s.display_team,3))
            end
            ---- Add the row to the table
            t_Body[#t_Body + 1] = {fn_datetable["sortkey"], tRow}
        end
    end

    return sortAndConcat(t_Body, resultTable)
end

function p.nationalchampionships(frame)
    local calendarroadID, calendarITTID, year
    if wiki == "ar" then
        frame = frame:getParent()
    end
    local listOfCalendar={NationalRoadCyclingChampionships,NationalITTCyclingChampionships}

    for ii, thisCalendar in pairs(listOfCalendar) do --road/ITT
        for key, v in pairs(thisCalendar) do --look for the key of the dictionnary, here women/men
            if ((ii==1 and calendarroadID==nil) or (ii==2 and calendarITTID ==nil)) and frame.args[key] then
                year = frame.args[key]
                year = string.gsub( year , "%c", "")   
                if v[year] then
                    if ii==1 then
                        calendarroadID = v[year]
                    else
                        calendarITTID = v[year]
                    end
                end
            end
        end
    end
    
    local womenrace_bool=isWomenrace(calendarroadID)
    local s = {
        header_function = "calendar", -- translations are in function Calendar
        header_1 = 19, --
        header_2 = {5, 20, 21},
        country_column = 1,
        data_sort_type = {'', '', ''},   -- -- see https://meta.wikimedia.org/wiki/Help:Sorting
        item= calendarroadID,
        calendarroadID = calendarroadID,
        calendarITTID = calendarITTID,
        property = 'P527',
        year = year,
        no_country = {}, --no sense here to hide the country
        error_message = 0,
        display_team = true,
        display_countrylink = true,
        frame = frame,
        womenrace_bool=womenrace_bool
    }
    return nationalchampionships_main(s,tableA(s))
end

function nationalchampionships_main(s, resultTable)--Display the list of national champions for one year
    localframe=s.frame
    local tableChamp, fn_countrytable, t_Body = {}, {}, {}
    local timeOfRace ='+'..tostring(s.year).."-01-01T00:00:00Z"
    local tRace, thereisawinner, parentID, parentParentID, sitelink

    local temp=firstValue(s.calendarroadID, s.property)
    if temp then else s.error_message = 2 return '' end

    local listOfCalendarID={s.calendarroadID, s.calendarITTID}

    --create the table with the information
    for ii, thisCalendarID in ipairs(listOfCalendarID) do
        if thisCalendarID ~= nil then
            for _, p527 in statements(thisCalendarID, 'P527') do
                thisID = p527.mainsnak.datavalue.value.id
                fn_countrytable=fn_country(thisID,timeOfRace,s.country)
                sortkey=string.gsub(fn_countrytable["countryname"], 'É', 'E') --case États Unis

                --create the table
                if tableChamp[sortkey]==nil then
                    tableChamp[sortkey]={}
                    tableChamp[sortkey]['countryname']=fn_countrytable["countryname"] --raw
                    tableChamp[sortkey]['roadwinner']='<td></td>'
                    tableChamp[sortkey]['ITTwinner']='<td></td>'
                    --look for sitelink to championship
                    sitelink=nil --reinit
                    if s.display_countrylink then --expensive
                        parentID = firstValue(thisID, 'P361', 'id') --part of
                        if parentID then
                            parentParentID = firstValue(parentID, 'P31', 'id')
                            if parentParentID then sitelink = wikibase.getSitelink(parentParentID) end
                        end
                    end
                    tableChamp[sortkey]['sitelink']=sitelink
                    tableChamp[sortkey]['flag']=fn_countrytable["flag"]
                end
               
                --fill the table
                tRace, thereisawinner=fn_rider(thisID,timeOfRace,s.display_team,1,true)
                if tableChamp[sortkey]['thereisawinner']~=true then --all other cases
                    tableChamp[sortkey]['thereisawinner']=thereisawinner
                end
               
                if ii==1 then
                    tableChamp[sortkey]['roadwinner']=tRace
                else
                    tableChamp[sortkey]['ITTwinner']=tRace
                end
            end
        end
    end

    -- structure the display
    for key, thisRow in pairs(tableChamp) do
        if thisRow['thereisawinner'] then --there is a winner
            local tRow = mw.html.create('tr'):cssText( "line-height: 1.8em; padding: 5px;")
            if thisRow['sitelink']~=nil then
                tRow:tag('td'):wikitext(thisRow['flag']..' [['..thisRow['sitelink']..'|'..thisRow['countryname']..']]')
            else
                tRow:tag('td'):wikitext(thisRow['flag']..' '..thisRow['countryname'])
            end
            tRow:node(thisRow['roadwinner'])
            tRow:node(thisRow['ITTwinner'])
            t_Body[#t_Body + 1] = {key, tRow}
        end --no winner
    end --end list of key

    return sortAndConcat(t_Body, resultTable) 
end

--=== C) Victory ===
function p.victories(frame)
    local IDtemp=frame.args[1]
    local womenrace_bool=isWomenrace(IDtemp)
    
    local s = {
        header_function = "victories", -- translations are in function victories
        header_1 = 2, -- translation 1 in function victories is printed in the upper part of the table header
        header_2 = {3, 4, 5, 6, 7},-- translations 2, 3, 4, 5, 6 in function victories are printed in this order
                                        -- in the lower part of the table header. The second value 3 in {4, 3} tells where the icon will go.
        data_type = {'date', 'race', 'country', 'class', 'rider'},
        country_column = 3,
        data_sort_type = {'', 'unsortable', '', '', ''}, -- see https://meta.wikimedia.org/wiki/Help:Sorting
        item = IDtemp,
        property = 'P2522',
        no_country = no_country_victories,
        error_message = 0,
        frame=frame,
        womenrace_bool=womenrace_bool
    }
    return victory_main(s ,tableA(s))
end

function victory_main(s, resultTable)
    localframe=s.frame
    local _
    _, _, s.item = string.find(s.item, "(%w+)")

    local temp=firstValue(s.item, s.property,'id')
    if temp then else s.error_message = 2 return '' end

    local country=getCountryBool(s.no_country)
    if available_list==false then country=false end

    local sortkey
    local t_Body = {}

    for _, p2522 in statements(s.item, 'P2522') do
        local RaceID = p2522.mainsnak.datavalue.value.id
        local tRow = mw.html.create('tr'):cssText( "line-height: 1.8em; padding: 5px;")

        fn_datetable = fn_date(RaceID, 'victory')
        fn_racetable=fn_race(RaceID,nil ,true,fn_datetable["timeOfRace"], 'victory',country)--displayed_class=nil
       
        if fn_racetable["raceCell"]~= nil then --otherwise class not to be displayed
            fn_countrytable=fn_country(RaceID,
                                       fn_datetable["timeOfRace"],
                                       country,
                                       fn_racetable["raceCell"],
                                       fn_racetable["parentID"]
                                       )
           
            tRow:node(fn_datetable["tCell"])
            if country==true then
                tRow:node(fn_racetable["raceCell"])  --race site link is in fn_countrytable
            end
            tRow:node(fn_countrytable["tCell"])
            tRow:node(fn_racetable["classCell"]) --class
            tRow:node(fn_rider(RaceID,fn_datetable["timeOfRace"],false,1))
            t_Body[#t_Body + 1] = {fn_datetable["sortkey"], tRow}
        end --no winner
    end --end list of key
    
    return sortAndConcat(t_Body, resultTable)
end

--== D) Stage infobox
function p.stageinfobox(frame)
    local entityID = mw.text.trim(frame.args[1])
    if type(entityID) ~= 'string' then error('parameter must be a string') end
    if not entityID:match('Q%d+') then error('parameter must be a valid Wikidata item (ex: Q42)') end
    
    local womenrace_bool=isWomenrace(entityID)
       
    local details = {
        { name = translate("stageinfobox",2,womenrace_bool)}, -- course / not used
        { name =  translate("stageinfobox",2,womenrace_bool)}, -- competition
        { name = translate("stageinfobox",3,womenrace_bool), name_plural = translate("infobox",4,womenrace_bool)}, -- stage type
        { name = translate("stageinfobox",4,womenrace_bool), name_plural = translate("infobox",7,womenrace_bool)}, -- date
        { name = translate("stageinfobox",6,womenrace_bool)}, -- distance
        { name = translate("stageinfobox",7,womenrace_bool), name_plural = translate("infobox",10,womenrace_bool)}, -- country
        { name = translate("stageinfobox",9,womenrace_bool)}, -- start place
        { name = translate("stageinfobox",10,womenrace_bool)}, -- endplace
        { name = translate("stageinfobox",11,womenrace_bool)}, -- participants at start
        { name = translate("stageinfobox",12,womenrace_bool)}, -- participants at end
        { name = translate("stageinfobox",13,womenrace_bool)}, -- speed
        { name = translate("stageinfobox",44,womenrace_bool)}, -- elevation
        { name = translate("infobox",32,womenrace_bool), special = true}, -- special 1
        { name = translate("infobox",33,womenrace_bool), special = true}, -- special 2
    }
    local others = {
        { name = translate("infobox",29,womenrace_bool)}, -- picture
        { name = translate("infobox",30,womenrace_bool)}, -- caption
        { name = translate("infobox",31,womenrace_bool)}, -- map
        { name = 'sectional'},             -- sectional
        { name = translate("infobox",30,womenrace_bool)}, -- caption map
        { name = translate("infobox",30,womenrace_bool)}, -- caption sectional
    }
            --begin of the function
    local t_P642 = {
            Q20882747={'results', 'first'},
            Q20882748={'results', 'second'},
            Q20882749={'results', 'third'},
            Q21686770={'results', 'winner_fighting'},
            Q2250962={'results', 'cima_coppi'},
            Q10452933={'results', 'cima_pantani'},
            Q20882763={'gen', 'leader'},
            Q20882764={'gen', 'deuxieme'},
            Q20882765={'gen', 'troisieme'},
            Q20883213={'annex', 'montagne'},
            Q20883140={'annex', 'jeune'},
            Q20883008={'annex', 'points'},
            Q20883329={'annex', 'sprints'},
            Q20893984={'annex', 'super_combatif'},
            Q20965880={'annex', 'combine'},
            Q27104688={'annex', 'stage_volantes'},
            Q27104684={'annex', 'regularite'},
            Q20882922={'annex', 'equipe'},
            Q27104271={'annex', 'equipe_points'},
            Q20882667={'gen', 'leader'},
            Q20882668={'gen', 'deuxieme'},
            Q20882669={'gen', 'troisieme'},
            Q20883212={'annex', 'montagne'},
            Q20883139={'annex', 'jeune'},
            Q20883007={'annex', 'points'},
            Q20883328={'annex', 'sprints'},
            Q20893983={'annex', 'super_combatif'},
            Q20893979={'annex', 'combine'},
            Q27067359={'annex', 'stage_volantes'},
            Q27067170={'annex', 'regularite'},
            Q27907747={'annex', 'azzurri_ditalia'},
            Q27907748={'annex', 'azzurri_ditalia'},
            Q27907714={'annex', 'breakaway'},
            Q27907715={'annex', 'breakaway'},
            Q20882921={'annex', 'equipe'},
            Q27104269={'annex', 'equipe_points'}
    }
    
    local localframe
    if string.match(frame:getParent():getTitle(), '%P+') == mw.site.namespaces.Template.name then
        localframe = frame:getParent()
    else
        localframe = frame
    end
    
    getLocalContent(details, localframe.args)
    getLocalContent(others, localframe.args)

    local timeOfRace
    local temp = firstValue(entityID, 'P31','id')
    icon = ''
    if temp and temp ~= 'Q18131152' then
        if temp=='Q2266066' or temp=='Q2348250' or temp=='Q485321' then
            icon = " [[File:Cycling (track) pictogram.svg|35px]]"
        else
            icon = " [[File:Cycling (road) pictogram.svg|35px]]"
        end
        details[3].content = typeofstagelogo(entityID, true).." "..WPlinkpure(temp)
    end
    
    local name =  getLabelFallback(entityID, {wikilang, 'en', 'fr', 'de'}) or ''
    if wiki == 'fr' and name ~= nil then
        name= mw.ustring.gsub(name, "^(%d+)([re]+)", "%1<sup>%2</sup> ")
    end
    name= mw.ustring.gsub(name, "^(%a)",function (x) return mw.ustring.upper(x) end)
       
    infoGetOthers(others, entityID)   

    --name
    if course==nil then
        temp = firstValue(entityID, 'P1545')
        if temp then
            details[2].content =getStageLabel(temp)
            raceId = firstValue(entityID, 'P361','id')
            if raceId then
                details[2].content = (details[2].content or '') .. '، '.. WPlinkpure(raceId)
                for k, p31 in statements(raceId, 'P31') do
                    if race==nil then race={} end
                    race[k] = p31.mainsnak.datavalue.value.id --for the jersey
                end
            end
        end
    end

    -- This function give a format to dates when P585 (date) is used in a single day race
    local pTime = firstValue(entityID, 'P585', 'time') -- P585 is 'point in time'
    if pTime then
        details[4].content = funcDate(pTime, 'long')
        timeOfRace = pTime
    end
    
    local kmdistance
    if not details[5].content then details[5].content, kmdistance = getDistance(entityID, true) end -- distance
    
    infoGetCountry(details,6, entityID, timeOfRace)
    infoGetStartEnd(details,7, entityID, timeOfRace)
    infoGetParticipants(details,9, entityID)
    if not details[11].content then details[11].content = getSpeed(entityID, true, kmdistance, 'P2417') end --speed
    if not details[12].content then
        local elevation=getElevation(entityID)
        if  elevation ~= nil then details[12].content =elevation else details[12].content = nil end
    end --Elevation

    local jerseyWPID, jersey_name
    local t_s = {
        order={'results', 'gen', 'annex'},
        results={show=false,
            header=15,
            order = {'first','second','third','winner_fighting','winner_fighting2','cima_coppi','cima_pantani'},
            first={translation=16},
            second={translation=17},
            third={translation=18},
            winner_fighting={translation=19},
            winner_fighting2={translation=19}, -- two winner_fighting possible
            cima_coppi={translation=40},
            cima_pantani={translation=41}
            },
        gen={show=false,
            header=20,
            order = {"leader", "deuxieme", "troisieme"},
            leader={translation=21},
            deuxieme={translation=22},
            troisieme={translation=23}
            },
        annex={show=false,
            header=24,
            order={"points","montagne","sprints","jeune","super_combatif","combine",
            "stage_volantes","regularite","azzurri_ditalia","breakaway","equipe","equipe_points"},
            points={translation=25},
            montagne={translation=26},
            sprints={translation=27},
            jeune={translation=28},
            super_combatif={translation=29},
            combine={translation=30},
            stage_volantes={translation=31},
            regularite={translation=32},
            azzurri_ditalia={translation=42},
            breakaway={translation=43},
            equipe={translation=33},
            equipe_points={translation=34}
            }
        }

    --Winner
    for _, p1346 in statements(entityID, 'P1346') do
        local id_speed, id_time, id_time_gap, id_points_a, id_points_b, type_ofclas, name_ofclas
        local q = p1346.qualifiers
        local riderId = p1346.mainsnak.datavalue.value.id

        id_time = qualifieramount(p1346, 'P2781')
        id_time_gap =qualifieramount(p1346, 'P2911')
        id_speed =qualifieramount(p1346, 'P2052')
        id_points_a = qualifieramount(p1346, 'P1358')
        id_points_b =qualifieramount(p1346, 'P1351')

        if riderId ~= nil then
            local riderLink,riderTeam  = subwinner(riderId, timeOfRace, q) --sub function to avoid code in double
            -- looks into race item if the winner has a P642 statement for showing the type of winner(points, mountain, ..)
            if q.P642 and q.P642[1].snaktype == 'value' then
                for _, vv in pairs(q.P642) do
                    local qual = vv.datavalue.value.id
                    if qual~=nil and deprecated~='deprecated' and t_P642[qual] then
                        if qual=="Q21686770" and t_s['results']['winner_fighting'][1] ~= "" then
                            t_P642[qual][2] = 'winner_fighting2'
                        end
                        type_ofclas=t_P642[qual][1] --annex or gen
                        name_ofclas=t_P642[qual][2] --name of ranking
                        local v=t_s[type_ofclas][name_ofclas]

                        v['link']=riderLink   
                        v['team']=riderTeam
                        v['rank']=isdisqualified(p1346,q)
                        v['time']=id_time
                        v['gap']=id_time_gap
                        if id_points_a then v['points']=id_points_a end
                        if id_points_b then v['points']=id_points_b end
                        v['speed']=id_speed
                        if qual=="Q27104271" and t_s.annex.equipe_points['link']==nil then
                            t_s.annex.equipe_points['link']=riderId
                        end
                        if qual=="Q20882922" and t_s.annex.equipe['link']==nil then
                            t_s.annex.equipe['link']=riderId
                        end
                        v['genre'] = getGenderCode(riderId,'f')
                    end
                end
            end
        end
    end
    local rank, deprecated, prop, order, thisorder
    local listoftable = {'results','gen'}

    -- look into P2417, stage classification, then p2321 gen classification
    for ii, thistable in ipairs(listoftable) do
        if ii==1 then
            prop='P2417'
            order = {'first', 'second', 'third'}
        else
            prop='P2321'
            order = {'leader', 'deuxieme', 'troisieme'}
        end
       
        for _, p2417 in statements(entityID, prop) do
            local q = p2417.qualifiers
            if q.P1352 and q.P1352[1].snaktype == 'value' then
                for _, q1352 in pairs(q.P1352) do
                    rank = tonumber(q1352.datavalue.value.amount)
                end
                if rank == 1 or rank == 2 or rank == 3 then
                    thisorder=order[rank]
                    local v=t_s[thistable][thisorder]
                    v['rank'] = isdisqualified(p2417, q)
                    local thisid= p2417.mainsnak.datavalue.value.id
                    v['link'],_  = subwinner(thisid, timeOfRace, q)

                    if v['gap'] == nil and v['time'] == nil then
                        v['gap'] = qualifieramount(p2417, 'P2911')
                    end
                    if v['gap'] == nil and v['time'] == nil then
                        v['time'] = qualifieramount(p2417, 'P2781')
                    end
                    v['speed'] = qualifieramount(p2417, 'P2052')
                    v['genre'] = getGenderCode(thisid, 'f')
                end
            end
        end
    end

    listoftable={t_s.results,t_s.gen,t_s.annex}
    
    for _, thistable in ipairs(listoftable) do
        for _, v in ipairs(thistable.order) do --order is the list of all classification names
            if thistable[v]['link'] then
                thistable.show = true
            end
        end
    end
    
    ---General table
    local temp
    local width= '320px' -- size standard 320px, special 340px
    if t_s.annex.show == true and (wiki == 'no' or wiki == '..') then width= '340px' end
    tab= infoInitTab(width, name, icon)
    infoFillOthersDetails(tab, others, details,translate("stageinfobox",1,womenrace_bool))

-- ranking table, general and stage
    for _, value_order in ipairs(t_s.order) do
        local thistable =t_s[value_order] --results or gen or annex
       
        if thistable.show then -- if a section of the stageinfobox should be shown
            tCell=tab:tag('tr'):tag('td'):attr('colspan','2')
            tTab=tCell:tag('table'):attr('cellpadding','0'):attr('cellspacing','0'):css('width','100%')
            tCell=tTab:tag('tr'):tag('td'):attr('colspan','3')
            :cssText('border-bottom:5px solid #fff2cc; background-color:#FFE7A0; text-align:center')
            :css('font-weight','bold')
            :wikitext(translate("stageinfobox",thistable.header,womenrace_bool))

            for key, value in ipairs(thistable.order) do --value is the name of the class
                local v=thistable[value]
                if v['link'] then
                    local a1
                    a1, jersey_name, jerseyWPID = jersey_infobox( value, race, timeOfRace)
                    if a1~='' then v['jersey'] = a1 end
                    if v['speed']  then
                        if wiki == 'fo' then
                            v['speed'] = string.gsub(v['speed'], "%.", ",")
                        else
                            local lang = mw.language.getContentLanguage()
                            v['speed'] = '('.. lang:formatNum(v['speed'])..translate("unit",5,womenrace_bool)..')'
                        end
                    end
                    if v['points'] then
                        if v['points'] > 1 then
                            temp=translate("unit",7,womenrace_bool)
                        else
                            temp=translate("unit",6,womenrace_bool)
                        end
                        v['points'] = v['points']..temp
                    end
                    local title, k = string.gsub(translate("stageinfobox",v['translation'],womenrace_bool), " ", "&nbsp;")
                    if k > 0 then title = string.gsub(title, "&nbsp;", "<br>", 1) end --&#32;
               
                    --Create an empty column on the left
                    tRow=tTab:tag('tr'):css('vertical-align','top')
                    tCell=tRow:tag('td')
                    :css('font-weight','bold')
                    if v['team']~=nil or v['speed'] ~=nil then
                        tCell:attr('rowspan','2')
                    end
                    tCell:cssText("width:1%;background-color:#fff2cc;text-align:" ..
                    textalign .. ";padding:0 2px 0 2px;white-space:nowrap")
               
                    if value_order~='annex' and v['translation']~=40 and v['translation']~=41 then -- Cima Coppi, Cima Pantani with a line break
                        if v['jersey'] == nil then
                            if (value_order=='results') and (value=='winner_fighting' or value=='winner_fighting2' or value=='cima_coppi' or value=='cima_pantanii') then
                                tCell:wikitext(translate("stageinfobox",v['translation'],womenrace_bool))
                            else
                                tCell:wikitext(number(v['genre'], key, wiki))
                            end
                        else
                            if jerseyWPID=='' then
                                temp=''
                            else
                                temp="|link="..jerseyWPID
                            end
                            tCell:wikitext("[[File:"..v['jersey'].."|20px|"..title..temp.."]]")
                        end
                    else
                        if v['jersey'] == nil then
                            tCell:wikitext(title)
                        else
                            if jerseyWPID=='' then
                                if jersey_name ~= '' then
                                    temp = "|"..jersey_name
                                else
                                    temp=''
                                end
                            else
                                temp= "|link="..jerseyWPID
                            end
                            tCell:wikitext("[[File:"..v['jersey'].."|20px"..temp.."]]" .. title)
                        end
                    end
                   
                    tRow:tag('td'):cssText("padding:0 0.5em 0 0.5em;"..v['rank'])
                    :wikitext( v['link'])
                    tCell=tRow:tag('td'):cssText('text-align:right;font-size:85%;white-space:nowrap')

                    if v['time'] then
                        tCell:wikitext(calculateTime(v['time']))
                    end
                    if v['gap'] then
                        tCell:wikitext('+ '.. calculateTime(v['gap']))
                    end
                    tCell:wikitext(v['points'])
                end
               
                tCell=tTab:tag('tr'):tag('td'):attr('colspan','2')
                if v['team']~=nil and v['speed'] ~=nil then -- team row
                    tTab2=tCell:tag('table'):attr('cellpadding','0'):attr('cellspacing','0'):css('width','100%')
                    tRow = tTab2:tag('tr')
                    tRow:tag('td'):cssText('width:100%;text-align:" .. textalign .. ";padding-left:2px')
                    :wikitext("("..v['team']..")") --add the team
                    tRow:tag('td'):cssText('font-size:85%;vertical-align:top;white-space:nowrap')
                    :wikitext(v['speed'])
                else
                    if v['team']~=nil or v['speed'] ~=nil then
                        tCell:cssText("text-align:" .. textalign .. ";padding-left:2px")
                        if v['team'] ~= nil then
                            tCell:wikitext("("..v['team']..")") --add the team
                        end
                        tCell:tag('span'):cssText("float:right;font-size:85%;"):wikitext(v['speed'])
                    end
                end
            end
        end
    end
    
    infoFillOthersMap(tab, others)
    tab:node(getPreviousNextLine(entityID,true))
    wdDoc(tab, "d:Wikidata:WikiProject Cycling/Documentation/stageinfobox", translate("stageinfobox",39,womenrace_bool), frame.args[1])
    return tab
end

--== E) List of teams
function p.listofteams(frame)
    local raceID = frame.args[1]
    local teams = {} -- values will be {teamLink, teamCat, sortkey, index}
    local WDlink_on = (wiki == "mk" or wiki == "ja")

    local timeOfRace, errorMessage = getTimeOfRace(raceID)
    if not timeOfRace then return errorMessage end
    
    local womenrace_bool=isWomenrace(raceID)

    local teamCats = { -- {c,d,e} c = singular team type, d = plural team type, e = print order of the team types
        ["Q6154783"]  = {4,5,1},   -- WorldTeam
        ["Q80425135"] = {4,5,2},   -- UCI Women’s WorldTeam
        ["Q20638319"] = {6,7,3},   -- ProTeam (2005-2014)
        ["Q78464255"] = {6,7,4},   -- ProTeam (2020-)
        ["Q382927"]   = {8,9,5},   -- UCI Professional Continental Team (2005-2019)
        ["Q1756006"]  = {10,11,6}, -- UCI Continental Team
        ["Q20639847"] = {16,17,7}, -- professional cycling team       
        ["Q20653563"] = {20,21,8}, -- Groupe Sportif I
        ["Q20653564"] = {22,23,9}, -- Groupe Sportif II
        ["Q20653566"] = {24,25,10}, -- Groupe Sportif III   
        ["Q2466826"]  = {28,29,11}, -- UCI Women’s Team
        ["Q23726798"] = {12,13,12}, -- national cycling team
        ["Q99658502"] = {12,13,13}, -- national cycling team "B"
        ["Q20738667"] = {12,13,14}, -- national cycling team U23
        ["Q54555994"] = {12,13,15}, -- national cycling team U19       
        ["Q28492441"] = {12,13,16}, -- national cycling team with sponsor name
        ["Q20652655"] = {18,19,17}, -- amateur cycling team
        ["Q26849121"] = {30,31,18}, -- Women's amateur cycling team
        ["Q20639848"] = {14,15,19}, -- club and region cycling team
        ["Q20653570"] = {14,15,20}, -- club and region cycling team
    }

    local p1923 = mw.wikibase.getBestStatements(raceID, 'P1923') -- P1923 is participating teams
    local no = 0 -- Index used for stable sorting
    for _, v in pairs(p1923) do
        if v.mainsnak.snaktype == 'value' then
            no = no + 1
            local teamLink, teamCat, countryID = getTeamLinkCat(v.mainsnak.datavalue.value.id, timeOfRace, true, true)
            local flagImage = countryID and flag(countryID, timeOfRace) or ''
            teams[#teams + 1] = {flagImage .. ' ' .. teamLink, teamCat,
                teamCats[teamCat] and teamCats[teamCat][3] or 999, no}
        end
    end

    table.sort(teams, function(a,b)
        if a[3] < b[3] then return true end -- First sort key: Order from table teamCats
        if a[3] > b[3] then return false end
        return a[4] < b[4] -- Second key is the index to ensure stable sorting
    end)

    local function getHeader(CatID, count)
        local header, sitelink
        if teamCats[CatID] then
            local done=false
            if CatID=="Q2466826" then --name changed after 2020
                local year = timeOfRace and tonumber(string.sub(timeOfRace, 2, 5))
                if year and year>2019 then
                    if count == 1 then
                        header_label = translate("headoftableIII",32, womenrace_bool) -- singular name
                    else
                        header_label = translate("headoftableIII",33, womenrace_bool) -- plural name
                    end
                    done=true
                end
            end
           
            if done==false then
                if count == 1 then
                    header_label = translate("headoftableIII",teamCats[CatID][1], womenrace_bool) -- singular name
                else
                    header_label = translate("headoftableIII",teamCats[CatID][2], womenrace_bool) -- plural name
                end
            end
            if CatID=='Q78464255' then
                sitelink=wikibase.getSitelink('Q382927') --continental
            else
                sitelink=wikibase.getSitelink(CatID)
            end

            if sitelink ~= nil then
                header = '[['..sitelink..'|'..header_label..']]'
            else
                header= header_label
            end
        end

        local tHeader=  mw.html.create('span'):css('font-size','1.2em'):css('font-weight','bold')
        if not header then
            -- Unknown team category. Get the label for the entity to display if possible
            header = (CatID and getLabelFallback(CatID, {wikilang, 'en', 'fr', 'de'})) or 'Unknown team category'
            tHeader:css('text-transform','capitalize')
        end
        tHeader:wikitext(header)
       
        -- Set parameter to show team count in front of each category
        local tTag=''
        local showcounter = 2
        if count >= showcounter then
            tTag=mw.html.create('small'):wikitext(' (' .. count ..')')
        end
        return tostring(tHeader)..tostring(tTag)
    end

    local oldOrder = 0
    local oldCatID
    local count = 0
    local list = ''
    local header
    
    local resultTable = mw.html.create('table')
    :cssText("max-width:95%; padding:0.5em; margin-right:1em; border:1px solid rgb(200,200,200)")
    local tCell = resultTable:tag('tr'):tag('td')

    for _, team in ipairs(teams) do
        local order = team[3]
        if order ~= oldOrder then --new cat
            if oldOrder > 0 then
                header = getHeader(oldCatID, count)
                tCell:wikitext(header)
                tCell:node(tOl)
            end
            count = 1
            oldOrder = order
            tOl = mw.html.create('ul') --reinit
        else
            count = count + 1
        end
        oldCatID = team[2]
        tOl:tag('li')
        :cssText("width:20em;display:inline-block;vertical-align:text-top")
        :wikitext(team[1])
    end
    --add last row
    header = getHeader(oldCatID, count)
    tCell:wikitext(header)
    tCell:node(tOl)

    local wd_link = mw.html.create('span'):css('float',floattable):wikitext(wdLink(raceID .. '#P1923'))
    if arwiki_totemplate then wd_link = wdLink(raceID .. '#P1923') end
    local tableFooter1=mw.html.create('tr')
    tCell=tableFooter1:tag('td')
    :addClass('navigation-only')
    :attr('colspan',2)
    :cssText('border-top: 2px '..backgroundColor..' solid; font-size: 80%;')
    tCell:wikitext(tostring(wd_link))
    
    resultTable:node(tableFooter1)
    return resultTable
end

--== F) Classifications
function p.UCIclassification(frame)
    local s = {
        header_function = "headoftableII", -- translations are in function headoftableII
        header_1 = 19, -- translation 10 in function headoftableII is printed in the upper part of the table header
        header_2 = {1, 2, 7}, -- translations 1, 2, 3, 7 in function headoftableII are printed in this order in the lower part of the table header
        item = string.gsub(frame.args[1] or frame:getParent().args[1], "%c", ""),
        property = 'P3494', -- property to use for this table
        team_classification = false, -- it is not a team classification table, its a rider classification table
        background = 'color', -- there is a background color for the first row
        display_team=false,
        max_rank_displayed=1000 --unlimited the whole team must be displayed
        }
    return new_classification(frame, s)
end

function p.pointsclassification(frame)
    local s = {
        header_function = "headoftableII", -- translations are in function headoftableII
        header_1 = 10, -- translation 10 in function headoftableII is printed in the upper part of the table header
        header_2 = {1, 2, 3, 7}, -- translations 1, 2, 3, 7 in function headoftableII are printed in this order in the lower part of the table header
        item = string.gsub(frame.args[1] or frame:getParent().args[1], "%c", ""),
        property = 'P3494', -- property to use for this table
        team_classification = false, -- it is not a team classification table, its a rider classification table
        background = 'color', -- there is a background color for the first row
        max_rank_displayed=10
        }
    return new_classification(frame, s)
end

function p.teamsclassificationbytime(frame)
    local s = {
        header_function = "headoftableII", -- translations are in function headoftableII
        header_1 = 14, -- translation 10 in function headoftableII is printed in the upper part of the table header
        header_2 = {3, 2, 4}, -- translations 3, 2, 4 in function headoftableII are printed in this order in the lower part of the table header
        item = frame.args[1],
        property = 'P3497', -- property to use for this table
        team_classification = true, -- it is a team classification table, its not a rider classification table
        background = 'strong', -- there is no background color for the first row, but the first row is formated strong
        max_rank_displayed=10
    }
    return new_classification(frame, s)
end

function p.teamsclassificationbypoints(frame)
    local s = {
        header_function = "headoftableII", -- translations are in function headoftableII
        header_1 = 15, -- translation 10 in function headoftableII is printed in the upper part of the table header
        header_2 = {3, 2, 7}, -- translations 3, 2, 7 in function headoftableII are printed in this order in the lower part of the table header
        item = frame.args[1],
        property = 'P3496', -- property to use for this table
        team_classification = true, -- it is a team classification table, its not a rider classification table
        background = 'strong', -- there is no background color for the first row, but the first row is formated strong
        max_rank_displayed=10
        }
    return new_classification(frame, s)
end

function p.stageclassification(frame)
    local s = {
        header_function = "headoftableII", -- translations are in function headoftableII
        header_1 = 8, -- translation 10 in function headoftableII is printed in the upper part of the table header
        header_2 = {1, 2, 3, 4}, -- translations 1, 2, 3, 4 in function headoftableII are printed in this order in the lower part of the table header
        item = frame.args[1],
        property = 'P2417', -- property to use for this table
        team_classification = false, -- it is not a team classification table, its a rider classification table
        background = false, -- there is no background color for the first row
        display_ref = tonumber(frame.args[2]) == 0 and 0 or 1,
        max_rank_displayed=10
        }
    return new_classification(frame, s)
end

function p.generalclassification(frame)
    local s = {
        header_function = "headoftableII", -- translations are in function headoftableII
        header_1 = 9, -- translation 10 in function headoftableII is printed in the upper part of the table header
        header_2 = {1, 2, 3, 4}, -- translations 1, 2, 3, 4 in function headoftableII are printed in this order in the lower part of the table header
        item = frame.args[1],
        property = 'P2321', -- property to use for this table
        team_classification = false, -- it is not a team classification table, its a rider classification table
        background = 'color', -- there is a background color for the first row
        display_ref = tonumber(frame.args[2]) == 0 and 0 or 1,
        max_rank_displayed=25,
        }
    return new_classification(frame, s)
end

function p.generalclassificationpoint(frame)       
    local s = {   
        header_function = "headoftableII", -- translations are in function headoftableII
        header_1 = 9, -- translation 10 in function headoftableII is printed in the upper part of the table header
        header_2 = {1, 2, 3, 7}, -- translations 1, 2, 3, 7 in function headoftableII are printed in this order in the lower part of the table header
        item = frame.args[1],
        property = 'P2321', -- property to use for this table
        team_classification = false, -- it is not a team classification table, its a rider classification table
        background = 'color', -- there is a background color for the first row
        display_ref = tonumber(frame.args[2]) == 0 and 0 or 1,
        max_rank_displayed=25
        }
    return new_classification(frame, s)   
end       

function p.generalclassificationforttt(frame)
    local s = {
        header_function = "headoftableII", -- translations are in function headoftableII
        header_1 = 9, -- translation 10 in function headoftableII is printed in the upper part of the table header
        header_2 = {3, 2, 4, 5, 6}, -- translations 3, 2, 4, 5, 6 in function headoftableII are printed in this order in the lower part of the table header
        item = frame.args[1],
        property = 'P2321', -- property to use for this table
        team_classification = true, -- it is a team classification table, its not a rider classification table
        background = false, -- there is no background color for the first row
        display_ref = tonumber(frame.args[2]) == 0 and 0 or 1,
        max_rank_displayed=10
    }
    return new_classification(frame, s)
end

function p.teamtimetrialclassification(frame)
    local s = {
        header_function = "headoftableII", -- translations are in function headoftableII
        header_1 = 8, -- translation 10 in function headoftableII is printed in the upper part of the table header
        header_2 = {3, 2, 4, 5, 6}, -- translations 3, 2, 4, 5, 6 in function headoftableII are printed in this order in the lower part of the table header
        item = frame.args[1],
        property = 'P2417', -- property to use for this table
        team_classification = true, -- it is a team classification table, its not a rider classification table
        background = false, -- there is no background color for the first row
        display_ref = tonumber(frame.args[2]) == 0 and 0 or 1,
        max_rank_displayed=25
    }
    return new_classification(frame, s)
end

function p.mountainsclassification(frame)
    local s = {
        header_function = "headoftableII", -- translations are in function headoftableII
        header_1 = 11, -- translation 10 in function headoftableII is printed in the upper part of the table header
        header_2 = {1, 2, 3, 7}, -- translations 1, 2, 3, 7 in function headoftableII are printed in this order in the lower part of the table header
        item = string.gsub(frame.args[1] or frame:getParent().args[1], "%c", ""),
        property = 'P4320', -- property to use for this table
        team_classification = false, -- it is not a team classification table, its a rider classification table
        background = 'color', -- there is a background color for the first row
        max_rank_displayed=10
        }
    return new_classification(frame, s)
end

function p.sprintsclassification(frame)
    local s = {
        header_function = "headoftableII", -- translations are in function headoftableII
        header_1 = 12, -- translation 10 in function headoftableII is printed in the upper part of the table header
        header_2 = {1, 2, 3, 7}, -- translations 1, 2, 3, 7 in function headoftableII are printed in this order in the lower part of the table header
        item = string.gsub(frame.args[1] or frame:getParent().args[1], "%c", ""),
        property = 'P4322', -- property to use for this table
        team_classification = false, -- it is not a team classification table, its a rider classification table
        background = 'color', -- there is a background color for the first row
        max_rank_displayed=10
        }
    return new_classification(frame, s)
end

function p.bestyoungclassificationbypoints(frame)
    local s = {
        header_function = "headoftableII", -- translations are in function headoftableII
        header_1 = 13, -- translation 10 in function headoftableII is printed in the upper part of the table header
        header_2 = {1, 2, 3, 7}, -- translations 1, 2, 3, 7 in function headoftableII are printed in this order in the lower part of the table header
        item = string.gsub(frame.args[1] or frame:getParent().args[1], "%c", ""),
        property = 'P4323', -- property to use for this table
        team_classification = false, -- it is not a team classification table, its a rider classification table
        background = 'color', -- there is a background color for the first row
        max_rank_displayed=10
        }
    return new_classification(frame, s)
end

function p.bestyoungclassification(frame)
    local s = {
        header_function = "headoftableII", -- translations are in function headoftableII
        header_1 = 13, -- translation 10 in function headoftableII is printed in the upper part of the table header
        header_2 = {1, 2, 3, 4}, -- translations 1, 2, 3, 7 in function headoftableII are printed in this order in the lower part of the table header
        item = string.gsub(frame.args[1] or frame:getParent().args[1], "%c", ""),
        property = 'P4323', -- property to use for this table
        team_classification = false, -- it is not a team classification table, its a rider classification table
        background = 'color', -- there is a background color for the first row
        max_rank_displayed=10
        }
    return new_classification(frame, s)
end

function p.u23classification(frame)
    local s = {
        header_function = "headoftableII", -- translations are in function headoftableII
        header_1 = 18, -- translation 10 in function headoftableII is printed in the upper part of the table header
        header_2 = {1, 2, 3, 4}, -- translations 1, 2, 3, 7 in function headoftableII are printed in this order in the lower part of the table header
        item = string.gsub(frame.args[1] or frame:getParent().args[1], "%c", ""),
        property = 'P4323', -- property to use for this table (same as best young classification)
        team_classification = false, -- it is not a team classification table, its a rider classification table
        background = 'color', -- there is a background color for the first row
        max_rank_displayed=10
        }
    return new_classification(frame, s)
end

function p.combinationclassification(frame)
local s = {
        header_function = "headoftableII", -- translations are in function headoftableII
        header_1 = 16, -- translation 10 in function headoftableII is printed in the upper part of the table header
        header_2 = {1, 2, 3, 7}, -- translations 1, 2, 3, 7 in function headoftableII are printed in this order in the lower part of the table header
        item = string.gsub(frame.args[1] or frame:getParent().args[1], "%c", ""),
        property = 'P4324', -- property to use for this table
        team_classification = false, -- it is not a team classification table, its a rider classification table
        background = 'color', -- there is a background color for the first row
        max_rank_displayed=10
        }
    return new_classification(frame, s)
end

function p.combativeclassification(frame)
    local s = {
        header_function = "headoftableII", -- translations are in function headoftableII
        header_1 = 17, -- translation 10 in function headoftableII is printed in the upper part of the table header
        header_2 = {1, 2, 3, 7}, -- translations 1, 2, 3, 7 in function headoftableII are printed in this order in the lower part of the table header
        item = string.gsub(frame.args[1] or frame:getParent().args[1], "%c", ""),
        property = 'P4321', -- property to use for this table
        team_classification = false, -- it is not a team classification table, its a rider classification table
        background = 'color', -- there is a background color for the first row
        max_rank_displayed=10
        }
    return new_classification(frame, s)
end

function p.custompointsclassification(frame)
    local team_title
    if frame.args[4] and string.find(frame.args[4],"{{{")==nil then team_title=string.gsub(frame.args[4], "%c", "") end
    
    local s = {
        header_function = "headoftableII", -- translations are in function headoftableII
        header_2 = {1, 2, 3, 7}, -- translations 1, 2, 3, 7 in function headoftableII are printed in this order in the lower part of the table header
        header_1_text=string.gsub(frame.args[3], "%c", ""),
        item = string.gsub(frame.args[1] or frame:getParent().args[1], "%c", ""),
        property = string.gsub(frame.args[2], "%c", ""), -- property to use for this table
        team_title=team_title, --for old races where there was no team, only bike brands
        team_classification = false, -- it is not a team classification table, its a rider classification table
        background = 'color', -- there is a background color for the first row
        max_rank_displayed=10
        }
    return new_classification(frame, s)
end

function p.customtimeclassification(frame)
    local team_title
    if frame.args[4] and string.find(frame.args[4],"{{{")==nil then team_title=string.gsub(frame.args[4], "%c", "") end
    
    local s = {
        header_function = "headoftableII", -- translations are in function headoftableII
        header_2 = {1, 2, 3, 4}, -- translations 1, 2, 3, 7 in function headoftableII are printed in this order in the lower part of the table header
        header_1_text=string.gsub(frame.args[3], "%c", ""),
        item = string.gsub(frame.args[1] or frame:getParent().args[1], "%c", ""),
        property = string.gsub(frame.args[2], "%c", ""), -- property to use for this table
        team_title=team_title, --for old races where there was no team, only bike brands
        team_classification = false, -- it is not a team classification table, its a rider classification table
        background = 'color', -- there is a background color for the first row
        max_rank_displayed=10
        }
    return new_classification(frame, s)
end

function new_classification(frame, s)
    local country = true
    for _, value in pairs(no_country_classification) do -- get data if country should be printed in this wiki
        if value == wiki then country = false end
    end
    local raceID = s.item
    local womenrace_bool=isWomenrace(raceID)

    --[=[ It is possible to give the classification tables in the article commands to change the standard behaviour. They could look like this:
    {{Cycling race/teamsclassificationbytime|Q18574623|newline=false|country=true}}
    {{Cycling race/teamsclassificationbytime|Q18574623|country= false|newline=false}}
    {{Cycling race/teamsclassificationbypoints|Q18574623|newline =true|country=true}}
    {{Cycling race/teamsclassificationbypoints|Q18574623|newline= true}}
    {{Cycling race/teamsclassificationbypoints|Q18574623|newline = false|country=false}}
    {{Cycling race/teamsclassificationbytime|Q18574623|newline=true|country=true}}

    One additional parameter is "newline" with the values "true" or "false". "newline" says, if there is a line brake after the table. Standard is
    no line break after the tables stageclassification and teamtimetrialclassification.
    The second parameter is "country" with the values "true" or "false". "country" tells the module to print the country column or not.
    Most wikis have as standard to print the country columns, some wikis prefer as standard not to show the country column. A few lines above,
    the command "if wiki == 'da' then country = false end" tells that daWiki do not want to see the country colums as standard. You can add your wiki
    here in, if you do not want to see them as standard. With the new parameter editors are able to tell the module in the article what to do.
    ]=]

    local timeOfRace, errorMessage = getTimeOfRace(raceID)
    if not timeOfRace then return errorMessage end

    local plus = ''
    if string.match(frame:getParent():getTitle(), '%P+') == mw.site.namespaces.Template.name
    then localframe = frame:getParent() else localframe = frame end
    if localframe.args[1] ~= nil then localframe.args[1] = string.gsub(localframe.args[1], "%c", "") end

    if localframe.args.country ~= nil then -- switch country column on or off in the article
        if localframe.args.country == 'true' then country = true end
        if localframe.args.country == 'false' then country = false end
    end
    local tableHeader2_size = #s.header_2
    
    local max_rank_displayed=s.max_rank_displayed
    if localframe.args['max_rank_displayed'] and localframe.args['max_rank_displayed'] ~= '' then
        max_rank_displayed=tonumber(localframe.args['max_rank_displayed'])
    end

    if s.header_1_text ==nil then s.header_1_text=translate(s.header_function,s.header_1,womenrace_bool) end --for custom title
    local team_translation_index=3
    if s.team_title == nil then s.team_title=translate(s.header_function,team_translation_index,womenrace_bool) end --translation for team has index "3"
    
    local tableBody = mw.html.create('table')
        :addClass('sortable')
        :attr('cellpadding', '0')
        :attr('cellspacing', '0')
        :css('border' , '0')

    local wd_link = wdLink( raceID .. '#' .. s.property )
    local wd_span = mw.html.create('span'):css('float','left'):wikitext(wd_link)
    if wiki == "ar" then
        if arwiki_totemplate then wd_span = wd_link
        else wd_span = mw.html.create('span'):css('float','right'):wikitext(wd_link)
        end end

    tableBody:tag('tr'):tag('th')
    :attr('colspan', tostring(tableHeader2_size + 1)):cssText("padding:2px 2px; text-align:center; background-color:"..backgroundColor)
    :wikitext(tostring(wd_span)..s.header_1_text)

    header= tableBody:tag('tr'):cssText("text-align:center;padding:2px 2px;white-space:nowrap")
    for i, k in ipairs(s.header_2) do
        if i ~= 2 or (country and available_list) then
            local header_text
            if k == team_translation_index then --for team
                header_text=s.team_title
            else
                header_text=translate(s.header_function,k,womenrace_bool)
            end
            local head =header:tag('th'):wikitext(header_text)
            if i == 1 then
                head:attr('colspan','2')
            end
        end
    end

    local t_Body = {} --contains all rows
    local tCell, bg_color, tStyle, temp, temp2
    local claims = mw.wikibase.getAllStatements(raceID, s.property)
    for l, m in pairs(claims) do -- look into all statements
        if m.mainsnak.snaktype == 'value' then
            local riderID = m.mainsnak.datavalue.value.id
            local q = m.qualifiers or {}
            local rank, riderLink, gender, countryID, teamLink
            local flagLink, countryName = '', ''
            local h = {
                jersey = {}, -- lots of jerseyID
                value = {'', '', '', ''} -- points, time, time_gap, speed
            }
           
            if q.P1352 and q.P1352[1].snaktype == 'value' then -- P1352 is ranking
                rank = tonumber(q.P1352[1].datavalue.value.amount)
            else
                rank = ''
            end
           
            if q.P1534 and q.P1534[1].snaktype == 'value' then
                local dnf=q.P1534[1].datavalue.value.id
                if dnf=='Q1210380' then riderDNF =translate("startlist",6,womenrace_bool)--"HD","NP","DQ"
                    elseif dnf=='Q54881674' or dnf=='Q7113430' then riderDNF =translate("startlist",7,womenrace_bool)
                    elseif dnf=='Q1210382' then riderDNF =translate("startlist",8,womenrace_bool)
                    elseif dnf=='Q1229261' then riderDNF =translate("startlist",9,womenrace_bool)
                    else riderDNF=''
                end
            else
                riderDNF=''   
            end

            local cancelled=isdisqualified(m,q)

            if wiki == 'es' or wiki == 'fr' or wiki == 'ast' then
                --[[ These wikis need the gender to display the rank correct. Other wikis can skip this. ]]
                gender = getGenderCode(riderID, 'n')
            end

            h.value[1] = qualifieramount(m, 'P1358')
            h.value[2] = qualifieramount(m, 'P2781')
            if q.P2911 and q.P2911[1].snaktype == 'value' then -- P2911 is time gap
                h.value[3] = tonumber(q.P2911[1].datavalue.value.amount)
                plus = '+ '
            end
            h.value[4] = qualifieramount(m, 'P2052')
            if q.P2912 then -- P2912 is distinctive jersey
                for _, v in pairs(q.P2912) do
                    if v.snaktype == 'value' then
                        table.insert(h.jersey, v.datavalue.value.id)
                    end
                end
            end

            if s.team_classification then
                local _
                teamLink, _, countryID = getTeamLinkCat(riderID, timeOfRace, true)
            else
                riderLink = getRiderLink(riderID,timeOfRace)..(getReference(m) or '')
                teamLink = getTeam(riderID, timeOfRace, q)
                countryID = getNationality(riderID, timeOfRace,q)
            end
            if countryID then
                flagLink = flag(countryID, timeOfRace)
                if available_list and country then
                    if type(translations.list) == "function" then
                        countryName = translations.list(countryID)
                    end
                    if countryName == '' then
                        local label, lang = mw.wikibase.getLabelWithLang(countryID)
                        --[[ Uses standard language fallback. Should not be nil, as all countries have English labels. ]]
                        if lang == wikilang then
                            countryName = label
                        else
                            countryName = label .. ' (' .. lang .. ')'
                        end
                    end
                end
            end

            -- find the right background color if a rider has more then one jersey
            -- see Wikidata:WikiProject Cycling/Kit to translate/Jerseys
            bg_color=nil
            if h.jersey[1] then
                for _, jersey in pairs(h.jersey) do
                    if bg_color_table[jersey] then
                        bg_color = bg_color_table[jersey]
                        break
                    end
                end
            end
           
            tStyle=''
            if rank == 1 then
                if s.background then -- values are 'strong' or 'color'
                    tStyle = tStyle ..'font-weight:bold;' -- winner is formated bold
                    if s.background == 'color' then
                        if h.jersey[1] and bg_color then -- background color of winner depending on jersey
                            tStyle = tStyle .. 'background-color:' ..bg_color
                        end
                    end
                end
            end

            local tBody = mw.html.create('tr'):cssText(tStyle) -- a row
            tBody:tag('td'):cssText("text-align:center;padding:2px 0.5em 2px 0.5em;white-space:nowrap;"..cancelled)
            :wikitext(number(gender, rank, wiki))
            tCell= tBody:tag('td'):cssText("text-align:" .. textalign .. ";padding:0 0.2em 0 0.2em;"..cancelled)

            if not s.team_classification then
                if not teamLink then teamLink = '' end
                if not available_list then
                    tCell:wikitext(flagLink .. ' '.. riderLink .. jersey(h.jersey))
                    if s.display_team~=false then
                        tBody:tag('td'):wikitext(teamLink)
                    end
                else
                    if country == true then
                        tCell:wikitext(riderLink .. jersey(h.jersey) )
                        tBody:tag('td'):wikitext( flagLink ..' '.. countryName)
                    else
                        tCell:wikitext(flagLink .. ' ' .. riderLink .. jersey(h.jersey))
                    end
                    if s.display_team~=false then
                        tBody:tag('td'):cssText("text-align:".. textalign ..";padding:0 0.2em 0 0.2em")
                        :wikitext(teamLink)
                    end
                end
            else --team
                if available_list==true and country then
                    tCell:wikitext(teamLink .. jersey(h.jersey))
                    tBody:tag('td'):wikitext(flagLink .. ' ' .. countryName)
                else
                    tCell:wikitext(flagLink .. ' ' .. teamLink .. jersey(h.jersey))
                end
            end

                if s.header_2[4] == 4 then -- for table stageclassification, generalclassification, adds time and time gap
                if riderDNF=='' then
                    if rank == 1 and h.value[2] then
                        temp=calculateTime(h.value[2])
                    elseif rank == 1 and h.value[3]==nil then --avoid a plus with nothing
                        temp=''
                    else
                        temp=plus .. calculateTime(h.value[3])
                    end
                else
                    temp=riderDNF
                end
                tBody:tag('td'):cssText("text-align:right;padding:0 0.2em 0 0.2em"):wikitext(temp)
            end

            if s.header_2[4] == 7 or (s.header_2[3] == 7 and s.header_2[1] == 1) then -- for table pointsclassification, adds points
                --trick for UCI classification
                if riderDNF=='' then
                    if h.value[1] then temp=h.value[1] else temp='' end
                    tCell=tBody:tag('td'):cssText("text-align:right;padding:0 0.2em 0 0.2em")
                    :wikitext(temp)   
                    if type(h.value[1]) == "number" then
                        if h.value[1] > 1 then
                            temp2=translate("unit",7,womenrace_bool)
                        else
                            temp2=translate("unit",6,womenrace_bool)
                        end
                        tCell:tag('span'):cssText("font-size:80%"):wikitext(temp2)   
                    end
                else
                    tBody:tag('td'):cssText("text-align:right;padding:0 0.2em 0 0.2em"):wikitext(riderDNF)   
                end
            end

            if s.header_2[3] == 4 then
                if s.property == 'P2417' or s.property == 'P2321' then
                    -- for tables teamtimetrialclassification or generaltttclassification, adds time
                    tBody:tag('td'):cssText("text-align:right;padding:0 0.2em 0 0.2em")
                    :wikitext(calculateTime(h.value[2]))
                end
            end

            if s.property == 'P3497' then -- for table teambytimeclassification, adds time and time gap
                if rank == 1 then
                    temp=calculateTime(h.value[2])
                else
                    temp=plus .. calculateTime(h.value[3])
                end
                tBody:tag('td'):cssText("text-align:right;padding:0 0.2em 0 0.2em"):wikitext(temp)
            end

            if s.property == 'P3496' then -- for table teambypointsclassification, adds points
                tCell=tBody:tag('td'):cssText("text-align:right;padding:0 0.2em 0 0.2em")
                :wikitext(h.value[1])
                if type(h.value[1]) == "number" then
                    if h.value[1] > 1 then
                        temp2=translate("unit",7,womenrace_bool)
                    else
                        temp2=translate("unit",6,womenrace_bool)
                    end
                    tCell:tag('span'):cssText("font-size:80%"):wikitext(temp2)   
                end
            end

            if s.header_2[4] == 5 then -- for table teamtimetrialclassification, adds time gap
                if l > 1 then temp= plus else temp='' end
                tBody:tag('td'):cssText("text-align:right;padding:0 0.2em 0 0.2em"):wikitext(temp..calculateTime(h.value[3]))   
            end

            if s.header_2[5] == 6 then -- for table teamtimetrialclassification, adds speed
                tCell=tBody:tag('td'):cssText("text-align:right;padding:0 0.2em 0 0.2em")
                if type(h.value[4]) == "number" then
                    tCell:wikitext(mw.ustring.format('%.3f', h.value[4]))
                    :tag('span'):cssText("font-size:80%"):wikitext(translate("unit",5,womenrace_bool))
                end
            end
           
            if rank~='' and rank<=max_rank_displayed then --else no display
                if riderDNF=='' then
                    t_Body[#t_Body + 1] = {(type(rank) == 'number') and rank or 999, tostring(tBody)}
                else --disqualified should be higher than not disqualified if the ranking was revided
                    t_Body[#t_Body + 1] = {(type(rank) == 'number') and rank-0.1 or 999, tostring(tBody)}
                end
            end
        end
    end
    
    tableBody=sortAndConcat(t_Body, tableBody)
    local tableFooter1,tableFooter2
    if s.display_ref == 1 or wiki == "ar" then
        tableFooter1=mw.html.create('tr')
        tCell=tableFooter1:tag('td')
        :addClass('navigation-only')
        :attr('colspan',tostring(tableHeader2_size + 1))
        :cssText('border-top: 2px '..backgroundColor..' solid; font-size: 80%;')
        tableFooter2=mw.html.create('tr')
        tCell=tableFooter2:tag('td')
        :attr('colspan',tostring(tableHeader2_size + 1))
        :cssText("text-align:right")
        tCell:tag('small')
        :wikitext(race_reference(raceID))
    end
    
    --general table style and last line
    local tableStyle, tableNewline
    if localframe.args.newline == 'false' then -- parameter newline in WP article is 'false'
        tableStyle = "float:" .. floattable .. "; margin-right:0.5em; border:1px solid rgb(200,200,200)"
        tableNewline = ''
    end
    if localframe.args.newline == 'true' then -- parameter newline in WP article is 'true'
        tableStyle = "border:1px solid rgb(200,200,200)"
        tableNewline = '<br style="clear:left;">'
    end
    if localframe.args.newline == nil then -- no second parameter, compatible to the old code
        if s.property == 'P2417' then --stageclassification
            tableStyle = "float:"..floattable.."; margin-right:0.5em; border:1px solid rgb(200,200,200)"
            tableNewline = ''
        else
            tableStyle = "border:1px solid rgb(200,200,200)"
            tableNewline = '<br style="clear:left;">' -- everything else
        end
    end

    local finalTable= mw.html.create('table'):cssText(tableStyle)
    finalTable:tag('tr'):tag('td')
    :node(tableBody)
    if tableFooter1 then
        finalTable:node(tableFooter1)
        finalTable:node(tableFooter2)
    end

    return tostring(finalTable)..tableNewline
end

--=== G) Infobox ===
function p.infobox(frame)
    return infobox_main(frame,0)
end

function p.infoboxseason(frame)
    return infobox_main(frame,1)
end

function p.infoboxChamp(frame)
    return infobox_main(frame,2)
end

function infobox_main(frame, selector)
    localframe = frame
    -- If true, winners will have Wikidata logos with link to Wikidata
    local WDlink_on = (wiki == "mk" or wiki == "ja")

    -- If true, winners will the team of the cyclist
    local team = true
    local details, others, winners
    
    local entityID = mw.text.trim(frame.args[1])
    if type(entityID) ~= 'string' then error('parameter must be a string') end
    if not entityID:match('Q%d+') then error ('parameter must be a valid Wikidata item (ex: Q42)') end
    local womenrace_bool=isWomenrace(entityID)
    
    if selector==0 then --normal infobox
        details = {
            { name = translate("infobox",2,womenrace_bool)}, -- course
            { name = translate("infobox",3,womenrace_bool), name_plural = translate("infobox",4,womenrace_bool)}, -- competition
            { name = translate("infobox",5,womenrace_bool)}, -- stages
            { name = translate("infobox",6,womenrace_bool), name_plural = translate("infobox",7,womenrace_bool)}, -- date
            { name = translate("infobox",8,womenrace_bool)}, -- distance
            { name = translate("infobox",9,womenrace_bool), name_plural = translate("infobox",10,womenrace_bool)}, -- country
            { name = translate("infobox",11,womenrace_bool)}, -- start place
            { name = translate("infobox",12,womenrace_bool)}, -- endplace
            { name = translate("infobox",13,womenrace_bool)}, -- teams
            { name = translate("infobox",14,womenrace_bool)}, -- participants at start
            { name = translate("infobox",15,womenrace_bool)}, -- participants at end
            { name = translate("infobox",16,womenrace_bool)}, -- speed
            { name = translate("infobox",43,womenrace_bool)}, -- elevation
            { name = translate("infobox",17,womenrace_bool)}, -- cost
            { name = translate("infobox",32,womenrace_bool), special = true}, -- special 1
            { name = translate("infobox",33,womenrace_bool), special = true}, -- special 2
        }
    elseif selector==1 then  --season infobox
        details = {
            { name = translate("infobox",46,womenrace_bool)}, -- edition (1)
            { name = translate("infobox",3,womenrace_bool), name_plural = translate("infobox",4,womenrace_bool)}, -- competition (2)
            { name = translate("infobox",6,womenrace_bool), name_plural = translate("infobox",7,womenrace_bool)}, -- date (3)
            { name = translate("infobox",45,womenrace_bool)}, -- rasing (4)       
            { name = translate("infobox",47,womenrace_bool), name_plural = translate("infobox",48,womenrace_bool)}, -- location (country) (5)
            { name = translate("infobox",49,womenrace_bool), name_plural = translate("infobox",50,womenrace_bool)}, -- organizer (6)
            { name = translate("infobox",63,womenrace_bool), name_plural = translate("infobox",63,womenrace_bool)}, -- team class (7)   
            { name = translate("infobox",32,womenrace_bool), special = true}, -- special 1
            { name = translate("infobox",33,womenrace_bool), special = true}, -- special 2
        }
    else  --champ
         details = {
            { name = translate("infobox",46,womenrace_bool)}, -- edition (1)
            { name = translate("infobox",6,womenrace_bool), name_plural = translate("infobox",7,womenrace_bool)}, -- date (2)
            { name = translate("infobox",9,womenrace_bool), name_plural = translate("infobox",10,womenrace_bool)}, -- country (3)   
            { name = translate("infobox",67,womenrace_bool), name_plural = translate("infobox",68,womenrace_bool)}, -- location (city) (4)
            { name = translate("infobox",61,womenrace_bool), name_plural = translate("infobox",62,womenrace_bool)}, -- arena / stadion (5)
            { name = translate("infobox",60,womenrace_bool)}, -- medals (6)
            { name = translate("infobox",13,womenrace_bool)}, -- team (7)           
            { name = translate("infobox",49,womenrace_bool), name_plural = translate("infobox",50,womenrace_bool)}, -- organizer (8)
            { name = translate("infobox",32,womenrace_bool), special = true}, -- special 1
            { name = translate("infobox",33,womenrace_bool), special = true}, -- special 2
        }
    end
    
    others = {  --same for 3 infobox
        { name = translate("infobox",29,womenrace_bool)}, -- picture
        { name = translate("infobox",30,womenrace_bool)}, -- caption
        { name = translate("infobox",31,womenrace_bool)}, -- map
        { name = 'sectional'},             -- sectional
        { name = translate("infobox",30,womenrace_bool)}, -- caption map
        { name = translate("infobox",30,womenrace_bool)}, -- caption sectional
    }

    if selector==0 then
         winners = {
            { name = translate("infobox",19,womenrace_bool), QID = 'Q20882667' }, -- first
            { name = translate("infobox",20,womenrace_bool), QID = 'Q20882668' }, -- second
            { name = translate("infobox",21,womenrace_bool), QID = 'Q20882669' }, -- third
            { name = translate("infobox",22,womenrace_bool), QID = 'Q20883007' }, -- points
            { name = translate("infobox",23,womenrace_bool), QID = 'Q20883212' }, -- mountains
            { name = translate("infobox",24,womenrace_bool), QID = 'Q20883328' }, -- sprints
            { name = translate("infobox",25,womenrace_bool), QID = 'Q20883139' }, -- youth
            { name = translate("infobox",26,womenrace_bool), QID = 'Q101246973' }, -- supercombativity           
            { name = translate("infobox",26,womenrace_bool), QID = 'Q20893983' }, -- combativity
            { name = translate("infobox",35,womenrace_bool), QID = 'Q27067359' }, -- volantes
            { name = translate("infobox",36,womenrace_bool), QID = 'Q27067170' }, -- regularity
            { name = translate("infobox",27,womenrace_bool), QID = 'Q20893979' }, -- combination
            { name = translate("infobox",38,womenrace_bool), QID = 'Q27907715' }, -- breakaway
            { name = translate("infobox",39,womenrace_bool), QID = 'Q27907747' }, -- azzurri
            { name = translate("infobox",40,womenrace_bool), QID = 'Q28092831' }, -- rookie
            { name = translate("infobox",28,womenrace_bool), QID = 'Q20882921' }, -- teams
            { name = translate("infobox",37,womenrace_bool), QID = 'Q27104269' }, -- teamspoints
            { name = translate("infobox",41,womenrace_bool), QID ='Q61976850' },-- amateur
            { name = translate("infobox",42,womenrace_bool), QID ='Q61976872' } --nationality
        }
    elseif selector==1 then
         winners = {
            { name = translate("infobox",52,womenrace_bool), QID = 'Q20882667' }, -- individual (first)
            { name = translate("infobox",53,womenrace_bool), QID = 'Q20883139' }, -- youth
            { name = translate("infobox",54,womenrace_bool), QID = 'Q27104269' }, -- team (teamspoints)
            { name = translate("infobox",55,womenrace_bool), QID = 'Q98959152' }, -- team GS-I
            { name = translate("infobox",56,womenrace_bool), QID = 'Q98959153' }, -- team GS-II
            { name = translate("infobox",57,womenrace_bool), QID = 'Q98959155' }, -- team GS-III
            { name = translate("infobox",58,womenrace_bool), QID = 'Q72068715' }, -- country
            { name = translate("infobox",59,womenrace_bool), QID = 'Q72068724' }  -- country U23
        }
    end --Champ has no winners

    local localframe
    if string.match(frame:getParent():getTitle(), '%P+') == mw.site.namespaces.Template.name then
        localframe = frame:getParent()
    else
        localframe = frame
    end
    getLocalContent(details, localframe.args)
    getLocalContent(others, localframe.args)
    
    if selector==0 or selector==1 then
        getLocalContent(winners, localframe.args)
    end

    local timeOfRace, class
    local icon = (firstValue(entityID, 'P641','id') == "Q3609") and -- P641 is 'sport', Q3609 is 'road bicycle racing'
        ' [[File:Cycling (road) pictogram.svg|35px]]' or ''

    local name =  getLabelFallback(entityID, {wikilang, 'en', 'fr', 'de'}) or ''
    infoGetOthers(others, entityID)   

    if not details[1].content then -- course
        -- For FR Wiki and Wikidata, exception that permit to display 1er, 2e... for the edition number ;
        -- for RU  -й is written after the value of P393
        local nr = firstValue(entityID, 'P393') -- P393 is 'edition number'
        if nr then
            if wiki == 'fr' then nr = (nr == 1) and "1<sup>re</sup> " or (nr .. "<sup>e</sup> ")
            elseif wiki == "nl" then nr = nr .. "e "
            elseif wiki == "ru" then nr = nr .. "-й "
            elseif wiki == "eo" then nr = nr .. "-a "
            elseif wiki == "hu" then nr = nr .. ". "
            else nr = nr .. ". "
            end
        end
        local is_a

        local classID = firstValue(entityID, 'P279', 'id')
        --fallback
        if classID then
            class = classLinkFn(classID)
        else
            for _, p31 in statements(entityID, 'P31') do -- P31 is 'instance of'
                local instanceOf = p31.mainsnak.datavalue.value.id
                if instanceOf ~= "Q27020041" and class_dic[instanceOf] then
                    class = classLinkFn(instanceOf)
                    break
                end
            end
        end
       
        local season = firstValue(entityID, 'P3450', 'id') -- P3450 is 'sports season of league or competition'
        if season then
            is_a = raceLink(season)
        else
            for _, p31 in statements(entityID, 'P31') do -- P31 is 'instance of'
                local instanceOf = p31.mainsnak.datavalue.value.id
                if instanceOf ~= 'Q27968055' and instanceOf ~= 'Q27020041' and class_dic[instanceOf] == nil then -- Q27020041 is 'sports season'
                    is_a = raceLink(instanceOf)
                    break
                end
            end
        end

        if nr and is_a then
            details[1].content = nr .. ' ' .. is_a
        end
    end

    if selector==0 or selector==1 then
        if not details[2].content then -- competition
            -- Class of a cycling race. Class is: 1.UWT, 2.UWT, 1.HC, ... add new classes, no problem
            -- Competition of the cycling race : UCI World Tour 2016, UCI Europe Tour 2016...
            local tours = {}
            for _, p361 in statements(entityID, 'P361') do -- P361 is 'part of'
                tours[#tours + 1] = raceLink(p361.mainsnak.datavalue.value.id)
            end
            if tours[1] then
                if #tours > 1 then
                    details[2].name = details[2].name_plural
                end
                if class then
                    tours[1] = tours[1] .. ' ' .. class
                end
                details[2].content = table.concat(tours, '<br/>')
            end
        end
    end
    
    if selector==0 then
        if not details[3].content then -- stages
            local stages = #wikibase.getAllStatements(entityID, 'P527') -- P527 is 'has part'
            if stages > 0 then
                details[3].content = stages
            end
        end
    end
    
    local index_date
    if selector==0 then
        index_date=4
    elseif selector==1 then
        index_date=3
    else
        index_date=2
    end

    if selector==0 or selector==1 then
        if not details[index_date].content then -- date
            local sTime = firstValue(entityID, 'P580', 'time') -- P580 is 'start time'
            local eTime = firstValue(entityID, 'P582', 'time') -- P582 is 'end time'
            if sTime and eTime then
                local startTime, endTime = getStartEndTime(sTime, eTime)
                details[index_date].content = startTime .. ' – ' .. endTime
                details[index_date].name = details[index_date].name_plural
                timeOfRace = eTime
            else
                -- This function give a format to dates when P585 (date) is used in a single day race
                local pTime = firstValue(entityID, 'P585', 'time') -- P585 is 'point in time'
                if pTime then
                    details[index_date].content = funcDate(pTime, 'long')
                    timeOfRace = pTime
                end
            end
        end
    end
    
    --from this point the functions differ fundamentally
    if selector==0 then
        local kmdistance
        if not details[5].content then details[5].content, kmdistance = getDistance(entityID, true) end -- distance
    
        infoGetCountry(details,6, entityID, timeOfRace)
        infoGetStartEnd(details,7, entityID, timeOfRace)
    
        if not details[9].content then -- teams
            local teams = #wikibase.getBestStatements(entityID, 'P1923') -- P1923 is 'participating teams'
            if teams > 0 then
                details[9].content = teams
            end
        end
    
        infoGetParticipants(details,10, entityID)
        if not details[10].content or not details[11].content then
            local Allp710= wikibase.getAllStatements(entityID, 'P710')
            if Allp710 and #Allp710~=0 then
                if not details[10].content then details[10].content=#Allp710 end
                if not details[11].content then
                    local maxrank=1
                    for _, p710 in pairs(Allp710) do -- look into all statements
                        local q = p710.qualifiers
                        if q and q.P1352 and q.P1352[1].snaktype == 'value' then -- P1352 is ranking
                            local riderRank = tonumber(q.P1352[1].datavalue.value.amount)
                            if riderRank > maxrank then maxrank = riderRank end
                        end
                    end
                    if maxrank~=1 then details[11].content=maxrank end
                end
            end
        end
    
        if not details[12].content then details[12].content = getSpeed(entityID, true, kmdistance, 'P2321') end --speed
        if not details[13].content then
            local elevation=getElevation(entityID)
            if  elevation then details[13].content =elevation else details[13].content = nil end
        end --Elevation
       
        if not details[14].content then -- cost
            local cost = firstValue(entityID, 'P2130') -- P2130 is cost
            if cost then
                details[14].content = dispmoney(cost.amount, cost.unit)
            end
        end

    elseif selector==1 then
        if not details[4].content then -- racing
            local stages = #wikibase.getAllStatements(entityID, 'P527') -- P527 is 'has part'
            if stages > 0 then
                details[4].content = stages
            end
        end
        if not details[5].content then -- location
            infoGetPlace(details,5, entityID, timeOfRace) --in GAN version, the separator is , not <br />
        end
        if not details[6].content then -- organizer sitelink
            listWPlink(details, 6, entityID,'P644',true) --org
        end
        if not details[7].content then -- organizer sitelink
            listWPlink(details, 7, entityID,'P2670',true) --team ????
        end
    else --champ
        infoGetCountry(details,3, entityID, timeOfRace)
        if not details[4].content then -- location
            infoGetPlace(details,4, entityID, timeOfRace) --in GAN version, the separator is , not <br />
        end
        if not details[5].content then -- arena / stadion
            listWPlink(details, 5, entityID,'P115',true)
        end   
        if not details[6].content then -- racing
            local stages = #wikibase.getAllStatements(entityID, 'P527') -- P527 is 'has part'
            if stages > 0 then
                details[6].content = stages
            end
        end
        if not details[7].content then -- teams
            local teams = #wikibase.getBestStatements(entityID, 'P1923') -- P1923 is 'participating teams'
            if teams > 0 then
                details[7].content = teams
            end
        end
        if not details[8].content then -- organizer sitelink
            listWPlink(details, 8, entityID,'P644',true) --org
        end
    end

    tab = infoInitTab("300px", name, icon)
    infoFillOthersDetails(tab, others, details,translate("infobox",1,womenrace_bool))
    
    if selector==0 or selector==1 then --no winners for champ
        local winRows=''
        local win = {}
        for _, v in pairs(winners) do
            if not v.content then
                win[v.QID] = ''
            end
        end
        winner(entityID, win, timeOfRace, false, WDlink_on, team, true)
        for _, v in pairs(winners) do
            if not v.content then
                if win[v.QID] ~= '' then
                    v.content = win[v.QID]
                end
            end
            if v.content then
                tRow= mw.html.create('tr') :css('vertical-align','top')
                tRow:tag('td'):css('font-weight','bold'):wikitext(v.name)
                tRow:tag('td'):wikitext(v.content)
                winRows=winRows..tostring(tRow) --not elegant
            end
        end
        if winRows~= '' then
            tab:tag('tr'):tag('td'):attr('colspan','2')
            :cssText('border-bottom:5px solid white; background-color:'..backgroundColor..'; text-align:center')
            :css('font-weight','bold')
            :wikitext(translate("infobox",18,womenrace_bool))
            tab:wikitext(winRows)
        end
    end

    if others[3].content then -- map
        tab:tag('tr'):tag('td'):attr('colspan','2'):css('text-align','center')
        :wikitext("[[File:".. others[3].content .. "|center|300px]]")
        if others[5].content then -- caption
            tab:tag('tr'):tag('td'):attr('colspan','2'):css('text-align','center'):css('font-size','80%')
            :wikitext(others[5].content)
        end
    end
    
    tab:node(getPreviousNextLine(entityID))
    wdDoc(tab, "d:Wikidata:WikiProject Cycling/Documentation/infobox", translate("infobox",34,womenrace_bool), entityID)
    return tab
end

--=== H) race infobox
function p.raceinfobox(frame)
    localframe = frame
    local lang = contentLanguage
    -- If true, winners will have Wikidata logos with link to Wikidata
    local WDlink_on = (wiki == "mk" or wiki == "ja")
    
    local tRace = {race={
            raceId,
            raceDate,
            future,
            },
          vainqueur= {},
          lastEditionMonth,
          lastEditionYear,
          numberOfEditions,
          lastLink,
          nextLink,
          lastWinner,
          maxWinner,
          }
    
    local entityID = mw.text.trim(frame.args[1])
    if type(entityID) ~= 'string' then error('parameter must be a string') end
    if not entityID:match('Q%d+') then error('parameter must be a valid Wikidata item (ex: Q42)') end         
    local womenrace_bool=isWomenrace(entityID)
    
    local details = {
        { name = translate("raceinfobox",4,womenrace_bool)}, -- sport
        { name = translate("raceinfobox",5,womenrace_bool)}, -- creation date
        { name = translate("raceinfobox",6,womenrace_bool)}, -- disparition date
        { name = translate("raceinfobox",7,womenrace_bool)}, -- number of editions
        { name = translate("raceinfobox",8,womenrace_bool)}, -- periodicity
        { name = translate("raceinfobox",9,womenrace_bool)}, -- type , name_plural = translate("infobox",10)
        { name = translate("raceinfobox",33,womenrace_bool), name_plural = translate("raceinfobox",34,womenrace_bool)},                                                    --country
        { name = translate("raceinfobox",10,womenrace_bool), name_plural = translate("raceinfobox",11,womenrace_bool)}, -- place
        { name = translate("raceinfobox",12,womenrace_bool), name_plural = translate("raceinfobox",13,womenrace_bool)}, --org
        { name = translate("raceinfobox",27,womenrace_bool), name_plural = translate("raceinfobox",28,womenrace_bool)}, --race director
        { name = translate("raceinfobox",14,womenrace_bool)}, -- official web site
        { name = translate("raceinfobox",15,womenrace_bool), name_plural = translate("raceinfobox",16,womenrace_bool)}, -- Cat
        { name = translate("raceinfobox",17,womenrace_bool)}, -- circuit
    }
    
    local others = {
        { name = translate("infobox",29,womenrace_bool)}, -- picture
        { name = translate("infobox",30,womenrace_bool)}, -- caption
        { name = translate("infobox",31,womenrace_bool)}, -- map
        { name = 'sectional'},             -- sectional
        { name = translate("infobox",30,womenrace_bool)}, -- caption map
        { name = translate("infobox",30,womenrace_bool)}, -- caption sectional
    }

    local name =  getLabelFallback(entityID, {wikilang, 'en', 'fr', 'de'}) or ''
    infoGetOthers(others, entityID)   
       
    local localframe
    if string.match(frame:getParent():getTitle(), '%P+') == mw.site.namespaces.Template.name then
        localframe = frame:getParent()
    else
        localframe = frame
    end
    getLocalContent(details, localframe.args)
    getLocalContent(others, localframe.args)
    
    local timeOfRace, class

    local listOfNames=getFormerNames(entityID, 'P1448')
    
    local sport_id=firstValue(entityID, 'P641', 'id')
    local icon = (sport_id == "Q3609") and -- P641 is 'sport', Q3609 is 'road bicycle racing'
        ' [[File:Cycling (road) pictogram.svg|35px]]' or ''

    --1st ist sport
    if not details[1].content and sport_id then
        details[1].content = WPlinkpure(sport_id)
    end
    
    --creation
    local creation=firstValue(entityID, 'P571', 'time')
    if not details[2].content and creation then
        details[2].content = funcDate(creation, "Y" )
    end

    --disparition
    local disparition=firstValue(entityID, 'P576', 'time')
    if not details[3].content and disparition then
        details[3].content =  funcDate(disparition,"Y")
    end
    
    --populate tRace
    listOfWinners(entityID, tRace)
    
    --number of editions
    if not details[4].content and tRace.numberOfEditions and tRace.lastEditionYear then
        details[4].content = tostring(tRace.numberOfEditions).." (" .. translate("raceinfobox",31,womenrace_bool) .. " "..tostring(tRace.lastEditionYear)..")"
    end
    --periodicity
    if not details[5].content then
        details[5].content = getPeriodicity(entityID, tRace)
    end
    --type
    if not details[6].content then
        details[6].content = getType(entityID)
    end
    timeOfRace=nil --could be from last edition
    if not details[7].content then
        infoGetCountry(details,7, entityID, timeOfRace)
    end
    if not details[8].content then
        infoGetPlace(details,7, entityID, timeOfRace)
    end
    
    if not details[9].content then
        listWPlinkChrono(details, 9, entityID, {'P664'}, true, initialYear) --organiser
    end
    
    if not details[10].content then
        listWPlinkChrono(details, 10, entityID, {'P488'}, 'rider', initialYear)     --race dir
    end
    
    if not details[11].content then
        details[11].content = officialSite(entityID)
    end
    --Class and circuit   
    
    local classContent, circuitLink, numberClass= getClass(entityID)
    if not details[12].content then
        details[12].content = classContent
        if numberClass >1 then
             details[12].name = details[12].name_plural
        end
     end
    
     if not details[13].content then
        details[13].content = circuitLink
    end
    --Build the table
    tab = infoInitTab("300px", name, icon)
    --former names
    wiki_listOfNamesAtBottom={'ru'}
    
    local listOfNamesAtBottom = false
    for _, value in pairs(wiki_listOfNamesAtBottom) do --
        if value == wiki then listOfNamesAtBottom = true end
    end
    --picture at the top
    infoFillOthersDetails(tab, others, nil,translate("raceinfobox",19,womenrace_bool),"260px")
    if not listOfNamesAtBottom then
        if listOfNames and #listOfNames>1 then
            tab:node(addATitle(translate("raceinfobox",18,womenrace_bool))) 
            for _, v in pairs(listOfNames) do
                tab:node(addARow(v[2],v[3])) --period, name
            end
        end
    end
    
    infoFillOthersDetails(tab, nil, details,translate("raceinfobox",19,womenrace_bool),"260px")

    if listOfNamesAtBottom then
        if listOfNames and #listOfNames>0 then -- except for the ru-wiki, no one uses the display of official names at the bottom anyway
            tab:node(addATitle(translate("raceinfobox",18,womenrace_bool))) 
            for _, v in pairs(listOfNames) do
                tab:node(addARow(v[2],v[3])) --period, name
            end
        end
    end

    if (tRace.lastWinner and tRace.lastWinner~='') or
    (tRace.maxWinner and tRace.maxWinner~='') then
        tab:node(addATitle(translate("raceinfobox",20,womenrace_bool)))
        if (tRace.lastWinner and tRace.lastWinner~='') then
            tab:node(addARow(translate("raceinfobox",21,womenrace_bool),tRace.lastWinner))
        end
        if (tRace.maxWinner and tRace.maxWinner~='') then
            tab:node(addARow(translate("raceinfobox",22,womenrace_bool),tRace.maxWinner))
        end
    end

    if tRace.nextLink or tRace.lastLink then
        tab:node(addATitle(translate("raceinfobox",23,womenrace_bool)))
        local outTable

        if tRace.lastLink then
            outTable = mw.html.create('tr')
            local tCell=outTable:tag('td'):attr('colspan','2'):css('text-align','center')
            local lastText="[[File:Crystal Clear app kworldclock.png|left|37px]]"..
            translate("raceinfobox",24,womenrace_bool)..
            ":<br>'''"..
            tRace.lastLink.."'''"
            tCell:wikitext(lastText)
            tab:node(outTable)
        end   
       
        if tRace.nextLink then
            outTable = mw.html.create('tr')
            local tCell=outTable:tag('td'):attr('colspan','2'):css('text-align','center')
            local nextText = "[[File:Crystal Clear app kworldclock.png|left|37px]]"..
            translate("raceinfobox",25,womenrace_bool)..
            ":<br>'''"..
            tRace.nextLink.."'''"
            tCell:cssText("text-align:center"):wikitext(nextText)
            tab:node(outTable)
        end
    end
    wdDoc(tab, "d:Wikidata:WikiProject Cycling/Documentation/raceinfobox", translate("raceinfobox",26,womenrace_bool), entityID)
    return tab
end

--=== I) Team roster
function p.teamroster(frame)
    localframe=frame
    local squadID
    if frame.args[1] then squadID = string.gsub(frame.args[1], "%c", "") end
    local flags, pays = {}, {}
    local riderName, riderBirthday,riderTeam, timeTeam, correctlanguage,riderStart, riderEnd
    local riderPosition, riderReason, riderRef, errortext
    local riderReasonTable, riderTablecorrect, riderTablenotcorrect, riderTable = {}, {}, {}, {}
    local labelMissing = false
    local teamID, startOfSeason, stagiaire

    local slavicWikis = {mk = true, ru = true}
    local wikiIsSlavic = slavicWikis[wiki]
    local WDlink_on = wiki == "mk" or wiki == "ja" or wiki == "ru" or wiki == "he"
    local tableEndText = ''

    local sort
    --[[
    The word 'sort' is used to sort the riders after the surname. It could look like this in the Wikipedia article
    {{Cycling race/teamroster|Q21769847
    | sort
    }}
    A rider called 'Laurens De Vreese' is sorted after 'De Vreese Laurens'. If you want to sort after 'Vreese Laurens De'
    change that in the code. In lv mkWiki and ruWiki sorting is standard, there is no need to switch sorting on in the article
    ]]
    if string.match(frame:getParent():getTitle(), '%P+') == mw.site.namespaces.Template.name
    then localframe = frame:getParent() else localframe = frame end
    if localframe.args[2] ~= nil then
        if mw.ustring.find(mw.ustring.lower(localframe.args[2]), "sort") then sort = true else sort = false end
    end
    if wiki == "lv" or wiki == "mk" or wiki == "ru" then sort = true end

    local womenrace_bool=isWomenrace(squadID)
    local temp = firstValue(squadID, 'P361', 'id')
    if temp then teamID = temp end

    temp = firstValue(squadID, 'P580', 'time')
    if temp then
        startOfSeason = temp
    else
        local Sitelink=getSitelinkFallback(squadID,{'en', 'fr', 'de'})
        if Sitelink == nil then return '> Wikidata is missing data about the start time (P580) and end time (P582) of the season'
        else startOfSeason = '+'..string.match(Sitelink, '%d%d%d%d' ) ..'-01-01T00:00:00Z'
        end
    end

    for _, p527 in statements(squadID, 'P527') do
        --re-init
        riderName, riderBirthday, correctlanguage=nil, nil, nil
        riderTeam, timeTeam, riderReason, riderRef=nil, nil, nil, nil
        riderStart, riderEnd=nil, nil

        local riderID = p527.mainsnak.datavalue.value.id
        riderName, correctlanguage =getRiderLink(riderID, startOfSeason) --label
        if WDlink_on==true then riderName=riderName..wdLink(riderID) end
        local timeOfRace = startOfSeason
        _, startOfSeasonYear, startOfSeasonMonth, startOfSeasonDay, _=parseDate(startOfSeason, '2040', '12', '31', '','')

        riderBirthday=firstValue(riderID, 'P569','time')

        if not wikiIsSlavic then correctlanguage=true end  --actually we never take a cyrillic name if no latin is found
        local sortkey = findSortKey(riderID, correctlanguage,  wikiIsSlavic)

        for _, q in qualifiers(p527, 'P580') do
            local startdate = q.value['time']
            timeOfRace = startdate
            riderStart = funcDate(trans(startdate,'01', '01') or '', 'small')
        end
        for _, q in qualifiers(p527, 'P582') do
            local enddate=q.value['time']
            riderEnd = funcDate(trans(enddate,'12', '31') or '', 'small')
        end
        riderPosition=getPosition(riderPosition,p527)
        riderReason, riderRef=getReason(riderReason,riderRef,p527, timeOfRace,enddate)

        local beginYear, beginMonth, beginDay, endYear, endMonth, endDay, beginDate, endDate, endDatefound, endDatetemp
        local changedTime = '+0000-00-00'

        if teamID == nil then
            local p54 = getStatementForTime(riderID, 'P54', timeOfRace)
            if p54 then teamID = p54.mainsnak.datavalue.value.id end
        else
            for _, v in statements(riderID, 'P54') do -- look into all P54 teams
                stagiaire=nil
                errortext=''
                local thisteamID = v.mainsnak.datavalue.value.id
                if thisteamID == teamID then
                    endDatefound=true
                    beginDate, endDate = getStartEndfromQuali(v.qualifiers)
                    beginDate, beginYear, beginMonth, beginDay, errortext = parseDate(beginDate, '2040', '01', '01', errortext, ' missing qualifiers by rider')

                    if not endDate then endDatefound=false end
                    endDate, endYear, endMonth, endDay, _ = parseDate(endDate, beginYear, '12', '31', errortext,'')
                    riderReason, riderRef=getReason(riderReason,riderRef,v,timeOfRace,endDate)

                    if (beginYear == startOfSeasonYear or endYear == startOfSeasonYear) and ((beginYear == startOfSeasonYear and (beginMonth ~= '01' or beginDay ~= '01')) or (endYear == startOfSeasonYear and (endMonth ~= '12' or endDay ~= '31'))) then
                        -- riders who start after 1 January or end earlier then 31 December in the season
                        riderStart = funcDate(beginDate, 'small')
                        if endDatefound then
                            riderEnd = funcDate(endDate, 'small')
                        else
                            riderEnd = funcDate('+'..beginYear..'-12-31T00:00:00Z', 'small')
                        end
                        riderPosition=getPosition(riderPosition,v)
                    end
                else
                    for _, q in qualifiers(v, 'P39') do
                        stagiaire =q.value.id
                    end
                    if not stagiaire then
                        endDatefound=true
                        beginDate, endDatetemp=getStartEndfromQuali(v.qualifiers)
                        if not endDatetemp then endDatefound=false end
                        beginDate, beginYear, beginMonth, beginDay, errortext = parseDate(beginDate, '2040', '01', '01', errortext, ' missing qualifiers by rider')
                        endDate, endYear, endMonth, endDay, _ = parseDate(endDatetemp, beginYear, '12', '31', errortext, '')

                        if beginYear < startOfSeasonYear or (beginYear == startOfSeasonYear and beginMonth < startOfSeasonMonth) or
                        (beginYear == startOfSeasonYear and beginMonth == startOfSeasonMonth and beginDay < startOfSeasonDay) then -- start time < season time
                            if endDatefound then
                                if (endDate or '') >= changedTime then -- find maximum end time
                                    -- Case Pierre-Roger Latour: Chambéry CF (2012 - 2014), time season at 2013
                                    -- Task: changedTime should be after start time, but before startOfSeason
                                    if endYear > startOfSeasonYear then
                                        changedTime = '+'..startOfSeasonYear..'-12-31T00:00:00Z'
                                    else
                                        changedTime = endDate or ''
                                    end
                                end
                            end
                        end
                        if changedTime ~= '+0000-00-00' then
                            riderTeam = getTeam(riderID, changedTime, nil)
                            local _, _, endYear, _, _ = string.find(changedTime, "(%d+)-(%d+)-(%d+)")
                            timeTeam = ' ('..endYear..')'
                            if wiki == "ar" then timeTeam = endYear end
                        end
                    end
                end
            end
        end
        --get the country
        local countryID = getNationality(riderID, timeOfRace,q)
        if countryID then
            pays = getCountryName(countryID)
            flags = flag(countryID, timeOfRace)
        end
        --save
        local tRider={
                sortkey=sortkey,
                riderName=riderName,
                riderBirthday=riderBirthday,
                riderTeam=riderTeam,
                timeTeam=timeTeam,
                riderStart=riderStart,
                riderEnd=riderEnd,
                riderPosition=riderPosition,
                riderReason=riderReason,
                riderRef=riderRef,
                errortext=errortext,
                pays=pays,
                flags=flags
                }
       
        if correctlanguage == true then
            table.insert(riderTablecorrect,tRider )
        else
            table.insert(riderTablenotcorrect, tRider)
        end
    end

    -- sorting names
--    if sort == true and #riderTablecorrect~=0 and #riderTablenotcorrect~=0 then -- It was
    if sort == true and #riderTablecorrect==0 and #riderTablenotcorrect==0 then -- replaced with this to display the team roster in the ru-wiki
        table.sort(riderTablecorrect, function(a,b) return a[1]<b[1] end)
        table.sort(riderTablenotcorrect, function(a,b) return a[1]<b[1] end)
    end
    --merge
    for _, v in pairs (riderTablecorrect) do
        table.insert(riderTable, v)
    end
    for _, v in pairs (riderTablenotcorrect) do
        table.insert(riderTable, v)
    end
    local wd_link = mw.html.create('span'):css('float','left'):wikitext(wdLink(frame.args[1]..'#P527'))
    if arwiki_totemplate then wd_link = wdLink(frame.args[1] .. '#P527') end
    local outTable = mw.html.create('table')
                            :addClass('sortable')
                            :attr('cellpadding', '2')
                            :attr('cellspacing', '0')
                            :css('border' , '1px solid rgb(200,200,200)')
                            :css('padding', '3px')
    local th_colspan = 4
    if wiki == "ar" then th_colspan = 5 end
    outTable:tag('tr'):css('line-height','1.8em')
        :css('background-color',backgroundColor)
        :tag('th'):attr('colspan', th_colspan):cssText('text-align:center;white-space:nowrap')
        :wikitext(tostring(wd_link))
        :wikitext(translate("getSquadTableColumn",7,womenrace_bool))
    local header = outTable:tag('tr')
    header:tag('th'):cssText('text-align:center;padding:2px 20px 2px 2px;white-space:nowrap'):wikitext(translate("getSquadTableColumn",1,womenrace_bool))
    local textalign = 'center'
    if wiki=='ar' then textalign = 'right' end
    header:tag('th'):cssText('text-align:'..textalign..';padding:2px 20px 2px 2px;white-space:nowrap'):wikitext(translate("getSquadTableColumn",2,womenrace_bool))
    if available_list and wiki ~= 'lv' then
        header:tag('th'):cssText('text-align:center;padding:2px 20px 2px 2px;white-space:nowrap'):wikitext(translate("getSquadTableColumn",6,womenrace_bool))
    end
    if wiki == "ar" then
        header:tag('th'):attr('colspan', 2):cssText('text-align:center;padding:2px 20px 2px 2px;white-space:nowrap'):wikitext(translate("getSquadTableColumn",3,womenrace_bool))
    else
        header:tag('th'):cssText('text-align:center;padding:2px 20px 2px 2px;white-space:nowrap'):wikitext(translate("getSquadTableColumn",3,womenrace_bool))
    end
    local temp
    local iii = 1
    for i, v in pairs (riderTable) do
        local tRow=outTable:tag('tr'):css('line-height','1.8em')
        local tCell= tRow:tag('td'):cssText("padding:0 1em 0 0;white-space:nowrap")

        if not available_list or wiki == 'lv' then temp=v['flags']..' ' else temp='' end
        tCell:wikitext(temp..v['riderName']):attr('data-sort-value',v['sortkey'])

        if v['riderStart']~=nil or v['riderEnd']~=nil then
            tCell:tag('span'):cssText("font-size:80%; color:#686868")
            local note=''
            if v['riderReason'] ~= nil then
                note = ', [[#tr_'..i..frame.args[1]..'|'..translate("getSquadTableColumn",4,womenrace_bool)..']]'
                if wiki == "ar" then note = '، [[#tr_'..i..frame.args[1]..'|'..translate("getSquadTableColumn",4,womenrace_bool)..']]' end
            end
            tCell:wikitext(' ('..(v['riderStart'] or '')..'–'..(v['riderEnd'] or '')
                .. (v['riderPosition'] or '')..note..')')
        elseif v['riderReason'] then
            tCell:tag('span'):cssText("font-size:80%; color:#686868")
            :wikitext('([[#tr_'..i..frame.args[1]..'|'..translate("getSquadTableColumn",4,womenrace_bool)..']]'.. ')')
        end
        tCell=tRow:tag('td'):cssText("text-align:right;white-space:nowrap")
        if wiki == 'lv' then
            local _, _, beginYear, beginMonth, beginDay = string.find(startOfSeason,"(%d+)-(%d+)-0*(%d+)")
            local _, _, endYear, endMonth, endDay = string.find(v['riderBirthday'] or '',"(%d+)-(%d+)-0*(%d+)")
            tCell:wikitext(frame:expandTemplate{ title = 'Template:Birth date and age2', args = { beginYear, beginMonth, beginDay, endYear, endMonth, endDay } })
        else
            tCell:wikitext(funcDate(v['riderBirthday'] or '', 'long'))
            if available_list then
                tRow:tag('td'):wikitext(v['flags'].. ' '..v['pays'])
            end
        end

        if wiki =='he' then
            local isRtl = (mw.ustring.find(v['riderTeam'], '|.*[א-ת]') or (not mw.ustring.find(v['riderTeam'], '|') and mw.ustring.find(riderTeam, '[א-ת]')))
            if isRtl then
                tCell=tRow:tag('td'):cssText("padding:0 0.5em; text-align:right")
            else
                labelMissing = true -- FIXME: labelMissing is not functional in most languages. once we have infra support for it, move it there
                tCell=tRow:tag('td'):cssText("padding:0 0.5em; text-align:left")
            end
        else
            if wiki == "ar" then
               tCell=tRow:tag('td'):cssText("padding:0 0.5em")
            else
                tCell=tRow:tag('td'):cssText("padding:0 0.5em; text-align:left")
            end
        end
        if v['riderTeam'] then
            if wiki == "ar" then
                tCell:wikitext( v['riderTeam'] )
                tCell=tRow:tag('td'):cssText("padding:0 0.5em")
                tCell:wikitext( v['timeTeam']..v['errortext'] )
            else
                tCell:wikitext(v['riderTeam'].. v['timeTeam']..v['errortext'])
            end
        end
        --tableEndText is not a table
        if v['riderReason'] ~= nil or v['errortext'] ~= '' then
            local temp=(v['riderReason'] or '')..(v['errortext'] or '')
            if iii == 1 then
                tableEndText = tableEndText.. translate("getSquadTableColumn",5,womenrace_bool)..': '.. v['riderName'].. temp
            else
                tableEndText = tableEndText.. '<span style="color:white">'.. translate("getSquadTableColumn",5,womenrace_bool)..': </span>'.. riderName.. temp
            end
            iii = iii + 1
            if riderRef ~= nil then tableEndText = tableEndText..
                frame:extensionTag{name='ref', content=v['riderRef'], args = {name='tr_'..iii..frame.args[1]}} end
            tableEndText = tableEndText.. '<br>'
        end
    end
    if labelMissing then outTable:wikitext(getMissingLabelTrackingCategory()) end
    
    local UCIlink
    if wiki=="fr" then
        UCIlink="https://www.uci.org/fr/route/%C3%A9quipe"
    else
        UCIlink="https://www.uci.org/road/teams"
    end
    
    outTable:tag('tr'):tag('td'):addClass("navigation-only")
    :attr('data-sort-value','zz')
    :attr('colspan',th_colspan)
    :cssText("border-top: 2px "..backgroundColor.." solid; font-size: 80%;")
    :tag('tr')
    :tag('td'):attr('colspan',th_colspan)
    :attr('data-sort-value','zzz')
    :cssText("text-align:right")
    :tag('small'):wikitext(translate("race_reference", 1,womenrace_bool).."["..UCIlink..' UCI]')
    
    return tostring(outTable)..tableEndText
end

--== J) List of winners ==
function p.listofwinners(frame)
    local winnersProperty = {'Q20882667','Q20882668','Q20882669'}
    local display_team = false -- display of a rider without a team
    if tonumber(frame.args[5]) ==1 then display_team = true end -- display of the rider with the team
    local s = {
        countryflag=true,
        beginyear=tonumber(frame.args[2]),           
        endyear=tonumber(frame.args[3]),
        shapka=tonumber(frame.args[4]),
        display_team=display_team,       
        winnersProperty=winnersProperty,
        custom=false
    }
    return listofwinners_main(frame, s)
end

function p.listofwinnersyoung(frame)
    local winnersProperty = {'Q20883139','Q72099969','Q72099972'}
    local display_team = false -- display of a rider without a team
    if tonumber(frame.args[4]) ==1 then display_team = true end -- display of the rider with the team
    local s = {
        countryflag=true,
        beginyear=tonumber(frame.args[2]),           
        endyear=tonumber(frame.args[3]),
        shapka=tonumber(frame.args[4]),
        display_team=tonumber(frame.args[5]), -- since the answer is "args[4]"       
        winnersProperty=winnersProperty,
        custom=false
        }
    return listofwinners_main(frame, s)
end

function p.listofwinnersChamp(frame)
    local winnersProperty = {'Q20882667','Q20882668','Q20882669'}
    local s = {
        countryflag=false,
        beginyear=tonumber(frame.args[2]),           
        endyear=tonumber(frame.args[3]),
        shapka=tonumber(frame.args[4]),
        winnersProperty=winnersProperty,
        display_team = false,
        custom=false
        }
    return listofwinners_main(frame, s)
end

--listofwinnerssecondpart and so on can be coded with p.listofwinners
function p.listofwinnersnowiki(frame)
    local winnersProperty = {'Q20882667','Q20882668','Q20882669'}
    local display_team = false -- display of a rider without a team
    if tonumber(frame.args[5]) ==1 then display_team = true    end -- display of the rider with the team
    local s = {
        countryflag=true,
        beginyear=tonumber(frame.args[2]),           
        endyear=tonumber(frame.args[3]),
        shapka=tonumber(frame.args[4]),
        display_team=display_team, -- since the answer is "args[4]"       
        winnersProperty=winnersProperty,
        custom=false
    }
    return frame:extensionTag{ name = 'nowiki', content = listofwinners_main(frame, s)}
end

function p.listofwinnersteamofpoint(frame)
    local winnersProperty = {'Q27104269','Q72065970','Q72065977'}
    local display_team = false -- display of a rider without a team
    if tonumber(frame.args[5]) ==1 then display_team = true end -- display of the rider with the team
    local s = {
        countryflag=true,
        beginyear=tonumber(frame.args[2]),           
        endyear=tonumber(frame.args[3]),
        shapka=tonumber(frame.args[4]),
        display_team=display_team, -- since the answer is "args[4]"       
        winnersProperty=winnersProperty,
        custom=false
        }
    return listofwinners_main(frame, s)
end

function p.listofwinnersGSI(frame)
    local winnersProperty = {'Q98959152','Q98959192','Q98959196'}
    local display_team = false -- display of a rider without a team
    if tonumber(frame.args[5]) ==1 then display_team = true end -- display of the rider with the team
    local s = {
        countryflag=true,
        beginyear=tonumber(frame.args[2]),           
        endyear=tonumber(frame.args[3]),
        shapka=tonumber(frame.args[4]),
        display_team=display_team, -- since the answer is "args[4]"       
        winnersProperty=winnersProperty,
        custom=false
        }
    return listofwinners_main(frame, s)
end

function p.listofwinnersGSII(frame)
    local winnersProperty = {'Q98959153','Q98959194','Q98959197'}
    local display_team = false -- display of a rider without a team
    if tonumber(frame.args[5]) ==1 then display_team = true end -- display of the rider with the team
    local s = {
        countryflag=true,
        beginyear=tonumber(frame.args[2]),           
        endyear=tonumber(frame.args[3]),
        shapka=tonumber(frame.args[4]),
        display_team=display_team, -- since the answer is "args[4]"       
        winnersProperty=winnersProperty,
        custom=false
        }
    return listofwinners_main(frame, s)
end

function p.listofwinnersGSIII(frame)
    local winnersProperty = {'Q98959155','Q98959195','Q98959198'}
    local display_team = false -- display of a rider without a team
    if tonumber(frame.args[5]) ==1 then display_team = true end -- display of the rider with the team
    local s = {
        countryflag=true,
        beginyear=tonumber(frame.args[2]),           
        endyear=tonumber(frame.args[3]),
        shapka=tonumber(frame.args[4]),
        display_team=display_team, -- since the answer is "args[4]"       
        winnersProperty=winnersProperty,
        custom=false
        }
    return listofwinners_main(frame, s)
end

function p.listofwinnerscountry(frame)
    local winnersProperty = {'Q72068715','Q72068718','Q72068721'}
    local display_team = false -- display of a rider without a team
    if tonumber(frame.args[5]) ==1 then display_team = true end -- display of the rider with the team
    local s = {
        countryflag=true,
        beginyear=tonumber(frame.args[2]),           
        endyear=tonumber(frame.args[3]),
        shapka=tonumber(frame.args[4]),
        display_team=display_team, -- since the answer is "args[4]"       
        winnersProperty=winnersProperty,
        custom=false
        }
    return listofwinners_main(frame, s)
end

function p.listofwinnerscountryU23(frame)
    local winnersProperty = {'Q72068724','Q72068725','Q72068729'}
    local display_team = false -- display of a rider without a team
    if tonumber(frame.args[5]) ==1 then display_team = true end -- display of the rider with the team
    local s = {
        countryflag=true,
        beginyear=tonumber(frame.args[2]),           
        endyear=tonumber(frame.args[3]),
        shapka=tonumber(frame.args[4]),
        display_team=display_team, -- since the answer is "args[4]"       
        winnersProperty=winnersProperty,
        custom=false
        }
    return listofwinners_main(frame, s)
end

function p.listofwinnerscustom(frame)
    local winnersProperty ={}
    --general
    if frame.args[5] ~= nil and tonumber(frame.args[5]) ==1 then table.insert( winnersProperty,'Q20882667') end
    --podium
    if frame.args[6] ~= nil and tonumber(frame.args[6]) ==1 then
        table.insert( winnersProperty,'Q20882668')
        table.insert( winnersProperty,'Q20882669')
    end
    --points
    if frame.args[7] ~= nil and tonumber(frame.args[7]) ==1 then table.insert( winnersProperty, 'Q20883007' ) end
    --mounstain
    if frame.args[8] ~= nil and tonumber(frame.args[8]) ==1 then table.insert( winnersProperty, 'Q20883212' ) end
    -- sprints
    if frame.args[9] ~= nil and tonumber(frame.args[9]) ==1 then table.insert( winnersProperty, 'Q20883328' ) end
    -- youth
    if frame.args[10] ~= nil and tonumber(frame.args[10]) ==1 then table.insert( winnersProperty, 'Q20883139' ) end
    -- supercombativity
    if frame.args[11] ~= nil and tonumber(frame.args[11]) ==1 then table.insert( winnersProperty, 'Q101246973' ) end
    -- combativity
    if frame.args[11] ~= nil and tonumber(frame.args[11]) ==1 then table.insert( winnersProperty, 'Q20893983' ) end
    -- volante
    if frame.args[12] ~= nil and tonumber(frame.args[12]) ==1 then table.insert( winnersProperty, 'Q27067359' ) end
    -- regularity
    if frame.args[13] ~= nil and tonumber(frame.args[13]) ==1 then table.insert( winnersProperty, 'Q27067170' ) end
    -- combination
    if frame.args[14] ~= nil and tonumber(frame.args[14]) ==1 then table.insert( winnersProperty, 'Q20893979' ) end
    -- breakaway
    if frame.args[15] ~= nil and tonumber(frame.args[15]) ==1 then table.insert( winnersProperty, 'Q27907715' ) end
    -- azzurri
    if frame.args[16] ~= nil and tonumber(frame.args[16]) ==1 then table.insert( winnersProperty, 'Q27907747' ) end
    -- rookie
    if frame.args[17] ~= nil and tonumber(frame.args[17]) ==1 then table.insert( winnersProperty, 'Q28092831' )    end
    -- teams
    if frame.args[18] ~= nil and tonumber(frame.args[18]) ==1 then table.insert( winnersProperty, 'Q20882921' )    end
     -- teamspoints
    if frame.args[19] ~= nil and tonumber(frame.args[19]) ==1 then table.insert( winnersProperty, 'Q27104269' ) end   
    -- amateur
    if frame.args[20] ~= nil and tonumber(frame.args[20]) ==1 then table.insert( winnersProperty, 'Q61976850' ) end   
     --nationality
    if frame.args[21] ~= nil and tonumber(frame.args[21]) ==1 then table.insert( winnersProperty, 'Q61976872' ) end   
    -- country
    if frame.args[22] ~= nil and tonumber(frame.args[22]) ==1 then table.insert( winnersProperty, 'Q72068715' ) end   
    -- country U-23
    if frame.args[23] ~= nil and tonumber(frame.args[23]) ==1 then table.insert( winnersProperty, 'Q72068724' ) end   
    local s = {
        countryflag=true,
        beginyear=tonumber(frame.args[2]),           
        endyear=tonumber(frame.args[3]),
        shapka=tonumber(frame.args[4]),
        display_team = false,
        winnersProperty=winnersProperty,
        custom=true
        }
    return listofwinners_main(frame, s)
end

function listofwinners_main(frame, s)
    local rows = {}
    frame.args[1] = string.gsub(frame.args[1], "%c", "")
    local raceID = frame.args[1]
    local WDlink_on = (wiki == "mk") or (wiki == "ja") or (wiki == "ru")
        -- WDlink_on is used to decide if a Wikidata logo will be shown
    local WPcontent = {
        row ={},
        code = {}
    }
    local beginyear=s.beginyear or 0
    local endyear=s.endyear or 0
    local shapka=s.shapka or 0
    local titletable
    
    local womenrace_bool=isWomenrace(raceID)
    
    if s.custom then
        titletable={
        [ 'Q20882667' ]=translate("listofwinners",2, womenrace_bool), -- winner
        [ 'Q20882668' ]=translate("listofwinners",3, womenrace_bool), -- second       
        [ 'Q20882669' ]=translate("listofwinners",4, womenrace_bool), -- third       
        [ 'Q20883007' ]=translate("listofwinners",5, womenrace_bool), -- points 
        [ 'Q20883212' ]=translate("listofwinners",6, womenrace_bool), -- mountains
        [ 'Q20883328' ]=translate("listofwinners",7, womenrace_bool), -- sprints
        [ 'Q20883139' ]=translate("listofwinners",8, womenrace_bool), -- youth
        [ 'Q101246973' ]=translate("listofwinners",9, womenrace_bool), -- supercombativity       
        [ 'Q20893983' ]=translate("listofwinners",9, womenrace_bool), -- combativity
        [ 'Q20893979' ]=translate("listofwinners",10, womenrace_bool), -- combination
        [ 'Q20882921' ]=translate("listofwinners",11, womenrace_bool), -- teams
        [ 'Q27067359' ]=translate("listofwinners",12, womenrace_bool), -- volantes
        [ 'Q27067170' ]=translate("listofwinners",13, womenrace_bool), -- regularity
        [ 'Q27104269' ]=translate("listofwinners",14, womenrace_bool), -- teamspoints
        [ 'Q27907715' ]=translate("listofwinners",15, womenrace_bool), -- breakaway
        [ 'Q27907747' ]=translate("listofwinners",16, womenrace_bool), -- azzurri
        [ 'Q28092831' ]=translate("listofwinners",17, womenrace_bool), -- rookie
        [ 'Q61976850' ]=translate("listofwinners",18, womenrace_bool), -- amateur
        [ 'Q61976872' ]=translate("listofwinners",19, womenrace_bool), -- nationality
        [ 'Q72068715' ]=translate("listofwinners",23, womenrace_bool), -- winner country
        [ 'Q72068724' ]=translate("listofwinners",24, womenrace_bool), -- winner countryU23
    }
    else --main
         titletable={
-- winner:
        [ 'Q20882667' ]=translate("listofwinners",2, womenrace_bool), -- winner
        [ 'Q20883007' ]=translate("listofwinners",2, womenrace_bool), -- points 
        [ 'Q20883212' ]=translate("listofwinners",2, womenrace_bool), -- mountains
        [ 'Q20883328' ]=translate("listofwinners",2, womenrace_bool), -- sprints
        [ 'Q20883139' ]=translate("listofwinners",2, womenrace_bool), -- youth (time or point)
        [ 'Q101246973' ]=translate("listofwinners",2, womenrace_bool), -- supercombativity       
        [ 'Q20893983' ]=translate("listofwinners",2, womenrace_bool), -- combativity
        [ 'Q20893979' ]=translate("listofwinners",2, womenrace_bool), -- combination
        [ 'Q20882921' ]=translate("listofwinners",2, womenrace_bool), -- team (time)
        [ 'Q27067359' ]=translate("listofwinners",2, womenrace_bool), -- volantes
        [ 'Q27067170' ]=translate("listofwinners",2, womenrace_bool), -- regularity
        [ 'Q27104269' ]=translate("listofwinners",2, womenrace_bool), -- teampoints
        [ 'Q27907715' ]=translate("listofwinners",2, womenrace_bool), -- breakaway
        [ 'Q27907747' ]=translate("listofwinners",2, womenrace_bool), -- azzurri
        [ 'Q28092831' ]=translate("listofwinners",2, womenrace_bool), -- rookie
        [ 'Q61976850' ]=translate("listofwinners",2, womenrace_bool), -- amateur
        [ 'Q61976872' ]=translate("listofwinners",2, womenrace_bool), -- nationality
        [ 'Q72068715' ]=translate("listofwinners",2, womenrace_bool), -- winner country
        [ 'Q72068724' ]=translate("listofwinners",2, womenrace_bool), -- winner countryU23
        [ 'Q98959152' ]=translate("listofwinners",2, womenrace_bool), -- winner team GS-I
        [ 'Q98959153' ]=translate("listofwinners",2, womenrace_bool), -- winner team GS-II
        [ 'Q98959155' ]=translate("listofwinners",2, womenrace_bool), -- winner team GS-III       
-- 2 place:
        [ 'Q20882668' ]=translate("listofwinners",3, womenrace_bool), -- second       
        [ 'Q72065970' ]=translate("listofwinners",3, womenrace_bool), -- second teampoints
        [ 'Q72099969' ]=translate("listofwinners",3, womenrace_bool), -- youth (time or point)       
        [ 'Q72068718' ]=translate("listofwinners",3, womenrace_bool), -- second country
        [ 'Q72068725' ]=translate("listofwinners",3, womenrace_bool), -- second countryU23
        [ 'Q98959192' ]=translate("listofwinners",3, womenrace_bool), -- second team GS-I
        [ 'Q98959194' ]=translate("listofwinners",3, womenrace_bool), -- second team GS-II
        [ 'Q98959195' ]=translate("listofwinners",3, womenrace_bool), -- second team GS-III       
-- 3 place:
        [ 'Q20882669' ]=translate("listofwinners",4, womenrace_bool), -- third   
        [ 'Q72065977' ]=translate("listofwinners",4, womenrace_bool), -- third teampoints   
        [ 'Q72099972' ]=translate("listofwinners",4, womenrace_bool), -- youth (time or point)       
        [ 'Q72068721' ]=translate("listofwinners",4, womenrace_bool), -- third country
        [ 'Q72068729' ]=translate("listofwinners",4, womenrace_bool), -- third countryU23
        [ 'Q98959196' ]=translate("listofwinners",4, womenrace_bool), -- third team GS-I
        [ 'Q98959197' ]=translate("listofwinners",4, womenrace_bool), -- third team GS-II
        [ 'Q98959198' ]=translate("listofwinners",4, womenrace_bool), -- third team GS-III       
    }
    end

    --localframe defined as global for references
    if string.match(frame:getParent():getTitle(), '%P+') == mw.site.namespaces.Template.name then
        localframe = frame:getParent()
    else
        localframe = frame
    end
    if localframe.args[1] then
        localframe.args[1] = string.gsub(localframe.args[1], "%c", "")
    end
--[=[
It is possible to give the table listofwinners in the article commands. It could look like this:
{{Cycling race/listofwinners|Q18574623
| above row 1: '''[[aaa bbb ccc]]''' xxx
}}
"above row x" inserts a new row above row x into the table. Content is what is behind the ":".
]=]
    if localframe.args[2] then
        for num, _ in pairs(localframe.args) do
            if num > 1 and mw.ustring.find(mw.ustring.lower(localframe.args[num]), 'row') then
                local _, _, kebeginYear, val = mw.ustring.find(localframe.args[num], "([^:]+)%s*:%s*(%C+)")
                local _, _, key01, kebeginYear1, kebeginYear2 = mw.ustring.find(kebeginYear, "(%a+)%s*(%a+)%s*(%d+)")
                kebeginYear2 = tonumber(kebeginYear2) kebeginYear1 = mw.ustring.lower(key01..kebeginYear1)
                if kebeginYear1 == 'aboverow' then WPcontent.row[kebeginYear2] = val WPcontent.code[kebeginYear2] = 0  end --0 is above
                if kebeginYear1 == 'belowrow' then WPcontent.row[kebeginYear2] = val WPcontent.code[kebeginYear2] = 1  end --0 is above   
            end
        end
    end
    
    local firstyeartodisplay=2100
    local parts = mw.wikibase.getAllStatements(raceID, 'P527') -- P527 is 'has part'
    for _, part in ipairs(parts) do
        if part.rank ~= 'deprecated' and part.mainsnak.snaktype == 'value' then
            local partID = part.mainsnak.datavalue.value.id
            local timeOfRace=getTimeOfRace(partID) --original P585 and P580 inverted here
            local year = timeOfRace and string.sub(timeOfRace, 2, 5) or '?'
            local month = timeOfRace and string.sub(timeOfRace, 7, 8) or '01'   
            if year == "?" then mw.log("no year at " .. partID ) end
            if endyear==0 or (tonumber(year) or 0)<=endyear then
                    if (tonumber(year) or 0) >= beginyear then
                        local thereisawinner=false
                       
                        local sitelink = mw.wikibase.getSitelink(partID)
                        if sitelink then
                            sitelink = '[[' .. sitelink .. '|' .. year .. ']]'
                        else
                            sitelink = year
                        end
                        if WDlink_on then
                            sitelink = sitelink .. ' ' .. wdLink(partID)
                        end
                        local winners = {}
                        for _, property in ipairs(s.winnersProperty) do winners[property]='' end
                        local tCell
                        local tCellstr=''
                       
                         local temp=firstValue(partID, 'P1346','id')
                        if temp and temp=='Q30108381' then --race cancelled
                            local cancelledlabel = getLabelFallback('Q30108381', {wikilang, 'en', 'fr', 'de'})
                            tCell=mw.html.create('td'):attr('colspan','4')
                            :cssText('text-align:center; font-style: italic')
                            :wikitext(cancelledlabel)
                            tCellstr=tostring(tCell)
                        else
                            winner(partID, winners, timeOfRace, not s.countryflag, WDlink_on,s.display_team,true)
                            for _, property in ipairs(s.winnersProperty) do
                                tCell=mw.html.create('td'):wikitext(winners[property])
                                if winners[property]~='' then
                                    thereisawinner=true
                                    if tonumber(year)<firstyeartodisplay then firstyeartodisplay=tonumber(year) end
                                end
                                tCellstr= tCellstr..tostring(tCell)
                            end
                        end
                        if firstyeartodisplay<=tonumber(year) then
                            rows[#rows+1]={year..month, sitelink, tCellstr}
                        end
                    end
                end
            end
        end
    table.sort(rows, function(a, b) return a[1] < b[1] end) -- Sort by year
    
    local clear = "left"
    if wiki == "ar" then clear = "right" end
    
    --do not use hw.html here otherwise the begin and end year won't work
    local table_first = "<table cellpadding='4' cellspacing='0' style='"..standardtablecss.."'>"

    local tTitleRow=mw.html.create('tr')
    :css('text-align','center')
    :css('background-color',backgroundColor)
    local tCell=tTitleRow:tag('th')
    local wd_link = mw.html.create('span'):css('float','left'):wikitext(wdLink(raceID .. "#P527"))
    if arwiki_totemplate then wd_link = wdLink(tostring(raceID) .. "#P527") end
    if WDlink_on == false then
        tCell:wikitext(tostring(wd_link))
    end
    tCell:wikitext(translate("listofwinners",1,womenrace_bool)) --year
    for _, pp in ipairs(s.winnersProperty) do
        tTitleRow:tag('th'):wikitext(titletable[pp])
    end
    
    local table_center=''
    local nb_year_inrow=1
    local lastyear
    
    for i, row in ipairs(rows) do
        sitelink=row[2]

        local tRowWD=mw.html.create('tr')
        local tCell=tRowWD:tag('td'):css('text-align','left')
       
        if lastyear and mw.ustring.sub(row[1],1,4)==lastyear then
                nb_year_inrow=nb_year_inrow+1
                tCell:wikitext(sitelink..' ('..tostring(nb_year_inrow)..')')
        else
            tCell:wikitext(sitelink)
            nb_year_inrow=1
        end
        lastyear=mw.ustring.sub(row[1],1,4)
        tRowWD:node(row[3]) --add the end of the row
       
        if WPcontent.row[i] then
            tRow=mw.html.create('tr'):tag('td'):attr('colspan','4')
            :css('text-align','center')
            tRow:wikitext(WPcontent.row[i])

            if WPcontent.code[i]==0 then --above
                table_center=table_center..tostring(tRow)
                table_center=table_center..tostring(tRowWD)
            else --below
                table_center=table_center..tostring(tRowWD)
                table_center=table_center..tostring(tRow)
            end
        else
            table_center=table_center..tostring(tRowWD)
        end
    end
    --firstpart with header no foot
    if shapka == 1 then -- standard header
        return table_center .. "</table>"   
    elseif shapka == 2 then    -- you need to add a title and you can add text at the beginning
        return table_center
    else -- you need to add a title and you can add anything and anywhere
        return table_first .. tostring(tTitleRow) .. table_center .. "</table>"
    end
end

--== K) List of stages
function p.listofstages(frame)
    -- WDlink_on is used to decide if a Wikidata logo will be shown
    local WDlink_on = wiki == "mk" or wiki == "ja"
    local WPcontent = {}
    local raceID = frame.args[1]
    local thereiselevation=false
    local result, tableBody

    local localframe
    if string.match(frame:getParent():getTitle(), '%P+') == mw.site.namespaces.Template.name then
        localframe = frame:getParent()
    else
        localframe = frame
    end
    if localframe.args[1] then
        localframe.args[1] = string.gsub(localframe.args[1], "%c", "")
    end
    local womenrace_bool=isWomenrace(raceID)
--[=[ It is possible to give the table listofstages in the article commands which overwrites data from Wikidata.
It could look like this:
{{Cycling race/listofstages|Q18574623
| RoW 1: locaTION Ab : [[1a1b]]
| after row 1 : date : 99 août
| after row 1 : icon : [[File:Stage rest day.svg|vbght frthzt fdgtr]]
| after row 1: text : rest day at [[aaa bbb ccc]]
| row 4:  location A : [[4a4a]]abc
| row 3 : winner a : <sup>tzhgt</sup>
| row 4 : winner b : kjuzhgt<br />bbjje
| row 4 : icon : [[File:Mediummountainstage.svg|xcvbbgf fgtr]]
| row 4 : distance : <s>141.8</s> 122<ref>test</ref>
}}
The first paramer is "row x" or "after row x". "after row" adds a new row after row x into the table to print e.g. a rest day.
The second parameters are "location [a/b/ab]", "date", "icon", "text", "winner [a/b]" and "distance".
"a" and "b" means the first and the second location or winner. "ab" could be used if start location and
end location are the same. The file data for the icon looks this way: [[File:Stage rest day.svg|any text]]
]=]
    if localframe.args[2] then
        local WProw, WPnew_row, WPcourse, WPtext, WPdate, WPwinner, WPicon, WPdistance
            = 'row', 'afterrow', 'location', 'text', 'date', 'winner', 'icon', 'distance'
        local _, kebeginYear, key2, val
        local key01, kebeginYear1, kebeginYear2
        local key21, key22
        for num, var in pairs(localframe.args) do
            if num > 1 and mw.ustring.find(mw.ustring.lower(var), WProw) then
                _, _, kebeginYear, key2, val = mw.ustring.find(var, "([^:]+)%s*:?%s*([^:]*)%s*:%s*(%C+)")
                _, _, key01, kebeginYear1, kebeginYear2 = mw.ustring.find(kebeginYear, "(%a+)%s*(%a+)%s*(%d+)")
                kebeginYear2 = tonumber(kebeginYear2)
                kebeginYear1 = mw.ustring.lower(key01 .. kebeginYear1)
                key2 = mw.ustring.lower(mw.text.trim(key2))
                _, _, key21, key22 = mw.ustring.find(key2, "(%a+)%s*(%a*)")

                if not WPcontent[kebeginYear2] then WPcontent[kebeginYear2] = {} end
                if kebeginYear1 == WProw and key21 == WPcourse then WPcontent[kebeginYear2][key22] = val end
                if kebeginYear1 == WPnew_row and key2 == WPdate then
                    WPcontent[kebeginYear2]['date'] = val
                    WPcontent[kebeginYear2]['text'] = WPcontent[kebeginYear2]['text'] or ''
                    WPcontent[kebeginYear2]['icon (new row)'] = WPcontent[kebeginYear2]['icon (new row)'] or ''
                end
                if kebeginYear1 == WPnew_row and key2 == WPtext then
                    WPcontent[kebeginYear2]['text'] = val
                    WPcontent[kebeginYear2]['date'] = WPcontent[kebeginYear2]['date'] or ''
                    WPcontent[kebeginYear2]['icon (new row)'] = WPcontent[kebeginYear2]['icon (new row)'] or ''
                end
                if kebeginYear1 == WPnew_row and key2 == WPicon then
                    val = string.gsub(val, "|", "|border|right|20px|", 1)
                    WPcontent[kebeginYear2]['icon (new row)'] = val
                    WPcontent[kebeginYear2]['date'] = WPcontent[kebeginYear2]['date'] or ''
                    WPcontent[kebeginYear2]['text'] = WPcontent[kebeginYear2]['text'] or ''
                end
                if kebeginYear1 == WProw and key21 == WPwinner and key22 == 'a' then WPcontent[kebeginYear2]['stage winner'] = val end
                if kebeginYear1 == WProw and key21 == WPwinner and key22 == 'b' then WPcontent[kebeginYear2]['general winner'] = val end
                if kebeginYear1 == WProw and key21 == WPicon then
                    val = string.gsub(val, "|", "|border|right|20px|", 1)
                    WPcontent[kebeginYear2]['icon'] = val end
                if kebeginYear1 == WProw and key21 == WPdistance then WPcontent[kebeginYear2]['distance'] = val end
            end
        end
    end
    local countries = wikibase.getAllStatements(raceID, 'P17')
    local onecountry, firstcountryID
    if countries and #countries>1 then
        onecountry=false
        if countries[1] then
            firstcountryID=countries[1].mainsnak.datavalue.value.id
        end
    else
        onecountry=true
    end

    local rows = {}
    local stages = mw.wikibase.getBestStatements(raceID, 'P527') -- P527 is 'has part'
    for _, v in pairs(stages) do
        if v.mainsnak.snaktype == 'value' then
            local stageID = v.mainsnak.datavalue.value.id
            local p = mw.wikibase.getBestStatements(stageID, 'P1545') -- P1545 is 'series ordinal'
            local sOrdinal = p[1] and p[1].mainsnak.snaktype == 'value' and p[1].mainsnak.datavalue.value
                or ''
            local _, _, sNumber, sLetter = string.find(sOrdinal, '(%d+)(.*)')
            if not sNumber then sNumber = '' end
            if not sLetter then sLetter = '' end
            local WDLink = WDlink_on and wdLink(stageID) or ''
            local sitelink = mw.wikibase.getSitelink(stageID)
            local timeOfRace =getTimeOfRace(stageID) or ''
           
            local sPointID = firstValue(stageID, 'P1427', 'id')
            local sPoint = (sPointID and getPlaceLink(sPointID, timeOfRace)) or ''

            if sPointID and not onecountry and timeOfRace then
                local startcountry= getStatementForTime(sPointID, 'P17',timeOfRace)
                if startcountry then
                    local startcountryID = startcountry.mainsnak.datavalue.value.id
                    if firstcountryID ~= startcountryID then
                        local sflag = flag(startcountryID, timeOfRace)
                        sPoint = sflag.." "..sPoint
                    end
                end
            end

            local dPointID = firstValue(stageID, 'P1444', 'id')
            local dPoint = (dPointID and getPlaceLink(dPointID, timeOfRace)) or ''

            if dPointID and not onecountry and timeOfRace then
                local dcountry= getStatementForTime(dPointID, 'P17',timeOfRace)
                if dcountry then
                    local dcountryID = dcountry.mainsnak.datavalue.value.id
                    if firstcountryID ~= dcountryID then
                        local dflag = flag(dcountryID, timeOfRace)
                        dPoint = dflag.." "..dPoint
                    end
                end
            end

            local sDistance = getDistance(stageID, false) or ''
            local sElevation = getElevation(stageID)
            if sElevation then thereiselevation=true end
           
            local winners = {
                Q20882747 = '', -- Q20882747 is 'stage winner'
                Q20882763 = '', -- Q20882763 is 'overall leader at the end of the stage'
                Q20882667 = '', -- Q20882667 is 'overall winner' not supposed to be used
            }
            winner(stageID, winners, timeOfRace, false, WDlink_on)

            -- find the type of stage
            local sType = typeofstagelogo(stageID)
            local label, section_title
            if sOrdinal == "0" then
                label, section_title = translate("func_prologue"), "#" .. translate("func_prologue")
            else
                label, section_title = stageLink(sOrdinal, sNumber, sLetter)
            end
            -- if there is a Wikipedia article of that stage show it or show the section
            local sLink = sitelink and ("[[" .. sitelink .. "|" .. label .. "]]") or
                ("[[" .. section_title .. "|" .. label .. "]]")

            local sDate = funcDate(timeOfRace, 'small')
            local tempoverall
            if  winners['Q20882763']~='' then tempoverall=winners['Q20882763'] else    tempoverall=winners['Q20882667'] end
            rows[#rows + 1] = {
                tonumber(sNumber) or 0, sLetter,  -- Sort keys
                sLink, sDate, WDLink, sPoint, dPoint, sType, sDistance, sElevation, winners['Q20882747'], tempoverall -- Content
            }
        end
    end

    table.sort(rows, function(a, b)
        if a[1] ~= b[1] then return a[1] < b[1] end
        return a[2] < b[2]
    end)
    local Id = ((not WDlink_on and wdLink(string.gsub(raceID, '%s', '') .. "#P527")) or "")
    
    tab=mw.html.create('table')
    :attr('cellpadding','4' )
    :attr('cellspacing','0')
    :cssText(standardtablecss)

    local tRow=tab:tag('tr'):css('background-color',backgroundColor)
    :css('text-align','center')
    tRow:tag('th'):css('white-space','nowrap')
    :wikitext(Id..translate("headoftable",1,womenrace_bool))
    tRow:tag('th'):wikitext(translate("headoftable",2,womenrace_bool))
    tRow:tag('th'):wikitext(translate("headoftable",3,womenrace_bool))
    tRow:tag('th'):css('color',backgroundColor):wikitext("type")
    tRow:tag('th'):wikitext(translate("headoftable",4,womenrace_bool))
    if thereiselevation then
        tRow:tag('th'):wikitext(translate("headoftable",7,womenrace_bool))
    end
    tRow:tag('th'):wikitext(translate("headoftable",5,womenrace_bool))
    tRow:tag('th'):wikitext(translate("headoftable",6,womenrace_bool))
    local header = tostring(tRow)
    for num, row in pairs(rows) do
        local sLink, sDate, WDLink, sPoint, dPoint, sType, sDistance, sElevation, sSWin, sGWin
            = row[3], row[4], row[5], row[6], row[7], row[8], row[9], row[10], row[11], row[12]

        local WPc = WPcontent[num]
        if WPc then
            if WPc['a'] then sPoint = WPc['a'] end
            if WPc['b'] then dPoint = WPc['b'] end
            if WPc['ab'] then sPoint, dPoint = WPc['ab'], '' end
            if WPc['icon'] then sType = WPc['icon'] end
            if WPc['distance'] then sDistance = WPc['distance'] end
        end

        local tRow = tab:tag('tr')
        local tCell= tRow:tag('td'):cssText('text-align:center; white-space:nowrap'):wikitext(sLink)
        tCell:tag('span'):css('white-space','nowrap'):wikitext("&nbsp;".. WDLink )
        tRow:tag('td'):css('white-space','nowrap'):cssText("text-align:right; padding-right:0px")
        :wikitext(sDate)
        tCell=tRow:tag('td'):cssText("padding-right:0px"):wikitext( sPoint)
        if dPoint ~= '' then
            tCell:wikitext(" – " .. dPoint)
        end
        tRow:tag('td'):cssText("padding-right:0px"):wikitext(sType)
        tRow:tag('td'):css('text-align','center'):wikitext( sDistance)
        if thereiselevation then
            tRow:tag('td'):css('text-align','center'):wikitext(sElevation)
        end

        if WPc and WPc['stage winner'] then
            tRow:tag('td'):css('text-align',textalign):wikitext( WPc['stage winner'])
        else
            tRow:tag('td'):wikitext(sSWin)
        end
        if WPc and WPc['general winner'] then
            tRow:tag('td'):css('text-align',textalign):wikitext( WPc['general winner'])
        else
            tRow:tag('td'):wikitext(sGWin)
        end
        if WPc and (WPc['date'] or WPc['text'] or WPc['icon (new row)']) then
            tRow = tab:tag('tr')
            tRow:tag('td') --empty

            if WPc['icon (new row)'] == '' then
                tRow:tag('td'):cssText('text-align:right; padding:3px 0px 10px 0px;white-space:nowrap')
                :wikitext(WPc['date'])
                tRow:tag('td'):cssText("text-align:" .. textalign .. "; padding:3px 4px 10px")
                :wikitext(WPc['text'])
            else
                tRow:tag('td'):cssText('text-align:right; padding-right:0px')
                :wikitext(WPc['date'])
                tRow:tag('td'):cssText("text-align:" .. textalign)
                :wikitext(WPc['text'])
            end
            tRow:tag('td'):css('padding-top','10px'):wikitext(WPc['icon (new row)'])
            tRow:tag('td'):attr('colspan','3')
        end
    end
    if arwiki_totemplate then
        tab = change_listofstages(tab, raceID, header, Id)
    end
    return tab
end

function p.stagetitle(frame)
    -- WDlink_on is used to decide if a Wikidata logo will be shown
    local stageID = frame.args[1]
    -- from to
    local p = mw.wikibase.getBestStatements(stageID, 'P1427') -- P1427 is 'start point'
    local sPointID = p[1] and p[1].mainsnak.snaktype == 'value' and p[1].mainsnak.datavalue.value.id
    local sPoint = sPointID and getPlaceLink(sPointID) or ''
    p = mw.wikibase.getBestStatements(stageID, 'P1444') -- P1444 is 'destination point'
    local dPointID = p[1] and p[1].mainsnak.snaktype == 'value' and p[1].mainsnak.datavalue.value.id
    local dPoint = dPointID and getPlaceLink(dPointID) or ''

    local sDistance = getDistance(stageID, true) or ''
    -- find the type of stage
    local sType = typeofstagelogo(stageID)
    
    tab=mw.html.create('table')
    tab:tag('th'):wikitext(sPoint.." - "..dPoint)
    tab:tag('td'):wikitext(sType)
    tab:tag('td'):css('font-weight','bold'):wikitext("("..sDistance..")")
    return tab
end

local function champtitle(h) --!h is h.jersey
    local road, ITT, result
    local hcountry, hnotcountry = {},{}
    local womenrace_bool=nil --to be defined if needed
    
    --the jersey for a stage race and the jersey from national championship should be differentiated
    --to avoid to look every time, below is a list of all national championships

    if type(h) == 'table' and h[1] then
        for _, v in ipairs(h) do
            roadtemp=false
            ITTtemp=false
            if womenNcRoadtable[v] or menNcRoadtable[v] then
                 road = true
                 roadtemp=true
            elseif womenNcITTtable[v] or menNcITTtable[v] then
                 ITT = true
                 ITTtemp=true
            else
                local raceLabel = mw.wikibase.getLabelByLang(v,"fr")
                if raceLabel then
                    local testMenRoadrace, testMenITT, testWomenRoadrace, testWomenITT
                    local raceLabelmod = string.gsub(raceLabel, '-', 'x')
                    testMenRoadrace = string.find( raceLabel, 'Course en ligne masculine aux' )
                    testMenITT = string.find( raceLabelmod, 'Contrexlaxmontre masculin aux' )
                    testWomenRoadrace = string.find( raceLabel, 'Course en ligne féminine aux' )
                    testWomenITT = string.find( raceLabelmod, 'Contrexlaxmontre féminin aux' )
                    if testWomenRoadrace or testMenRoadrace then road = true roadtemp=true end
                    if testWomenITT or testMenITT then ITT = true ITTtemp=true end
                end
            end
            if roadtemp or ITTtemp then
                table.insert(hcountry,v)
            else
                table.insert(hnotcountry,v)   
            end
        end
    end
    if road and ITT then
        local image = {}
        for ii, v in ipairs(hcountry) do
            local p18 = mw.wikibase.getBestStatements(v, 'P18')
            if p18[1] and p18[1].mainsnak.snaktype == 'value' then
                local temp = p18[1].mainsnak.datavalue.value
                local alreadythere = 0
                for _, vv in ipairs(image) do
                    if vv==temp then alreadythere = 1 end
                end
                if alreadythere==0 then
                    table.insert(image,temp)
                else hcountry[ii] = nil
                end
            end
        end
        --avoid double display of jersey
        result = "<small>("..translate("startlist",10,womenrace_bool).." "..translate("startlist",12,womenrace_bool).." "..translate("startlist",11,womenrace_bool)..")</small>"
    elseif road then
        result = "<small>("..translate("startlist",10,womenrace_bool)..")</small>"
    elseif ITT then
        result = "<small>("..translate("startlist",11,womenrace_bool)..")</small>"
    else
        result = ""
    end
    return jersey(hcountry)..result..jersey(hnotcountry)
end

-- L) List of stages classification
local function winnerjersey(raceID, winners)
    local jerseytable, bgcolortable={}, {}
    local p1346 = wikibase.getAllStatements(raceID, 'P1346') -- P1346 is 'winner'
    for _, winner in pairs(p1346) do
        local wOf, thisjersey, bg_color
        local q = winner.qualifiers
        if q then
            if q.P642 and q.P642[1].snaktype == 'value' then
                wOf = q.P642[1].datavalue.value.id -- P642 is 'of'
            end
            if q.P2912 and q.P2912[1].snaktype == 'value' then
                thisjersey=q.P2912[1].datavalue.value.id
                if bg_color_table[thisjersey] then
                    bg_color = bg_color_table[thisjersey]
                end
            end
        end
        if winners[wOf] and thisjersey then
            jerseytable={}
            table.insert(jerseytable,thisjersey)
            winners[wOf] = jersey(jerseytable)
            bgcolortable[wOf] = bg_color
        end
    end
    return winners, bgcolortable
end

function p.listofstagesclassification(frame)
    -- WDlink_on is used to decide if a Wikidata logo will be shown
    local WDlink_on = wiki == "mk" or wiki == "ja"
    local displaytypeofstage = true
    local stageinfotable = {}
    local raceID = frame.args[1]
    local womenrace_bool=isWomenrace(raceID)
    local sType
    local localframe
    if string.match(frame:getParent():getTitle(), '%P+') == mw.site.namespaces.Template.name then
        localframe = frame:getParent()
    else
        localframe = frame
    end
    if localframe.args[1] then
        localframe.args[1] = string.gsub(localframe.args[1], "%c", "")
    end

    --link for Grand Tour
    local GTid={['Q33881']=true,['Q33861']=true,['Q33937']=true}
    local thisGT

    for _, p31 in statements(raceID, 'P31') do
        if GTid[p31.mainsnak.datavalue.value.id]==true then thisGT=p31.mainsnak.datavalue.value.id break end
    end

    local Sitelink,overallname, pointsname, mountainname, youngname, teamname, combativityname, supercombativityname, combinedname
    if thisGT then
        if thisGT=='Q33881' then
            Sitelink = wikibase.getSitelink('Q2267539')
            if Sitelink then overallname="[["..Sitelink .."|"..translate("headoftableII",9,womenrace_bool).."]]" end
            Sitelink = wikibase.getSitelink('Q175399')
            if Sitelink then pointsname="[["..Sitelink .."|"..translate("infobox",22,womenrace_bool).."]]" end
            Sitelink = wikibase.getSitelink('Q927157')
            if Sitelink then mountainname="[["..Sitelink .."|"..translate("infobox",23,womenrace_bool).."]]" end
            Sitelink = wikibase.getSitelink('Q641662')
            if Sitelink then youngname="[["..Sitelink .."|"..translate("infobox",25,womenrace_bool).."]]" end
            Sitelink = wikibase.getSitelink('Q1436680')
            if Sitelink then teamname="[["..Sitelink .."|"..translate("infobox",28,womenrace_bool).."]]" end

            Sitelink = wikibase.getSitelink('Q2094179')
            if Sitelink then combativityname="[["..Sitelink .."|"..translate("infobox",26,womenrace_bool).."]]" end

            Sitelink = wikibase.getSitelink('Q2094179')
            if Sitelink then supercombativityname="[["..Sitelink .."|"..translate("infobox",26,womenrace_bool).."]]" end


            Sitelink = wikibase.getSitelink('Q1835362')
            if Sitelink then combinedname="[["..Sitelink .."|"..translate("infobox",27,womenrace_bool).."]]" end
        elseif thisGT=='Q33861' then
            Sitelink = wikibase.getSitelink('Q1164275')
            if Sitelink then overallname="[["..Sitelink .."|"..translate("headoftableII",9,womenrace_bool).."]]" end
            Sitelink = wikibase.getSitelink('Q641083')
            if Sitelink then pointsname="[["..Sitelink .."|"..translate("infobox",22,womenrace_bool).."]]" end
            Sitelink = wikibase.getSitelink('Q641060')
            if Sitelink then mountainname="[["..Sitelink .."|"..translate("infobox",23,womenrace_bool).."]]" end
            Sitelink = wikibase.getSitelink('Q641662')
            if Sitelink then youngname="[["..Sitelink .."|"..translate("infobox",25,womenrace_bool).."]]" end
        else
            Sitelink = wikibase.getSitelink('Q2532554')
            if Sitelink then overallname="[["..Sitelink .."|"..translate("headoftableII",9,womenrace_bool).."]]" end
            Sitelink = wikibase.getSitelink('Q2241695')
            if Sitelink then pointsname="[["..Sitelink .."|"..translate("infobox",22,womenrace_bool).."]]" end
            Sitelink = wikibase.getSitelink('Q1118296')
            if Sitelink then mountainname="[["..Sitelink .."|"..translate("infobox",23,womenrace_bool).."]]" end
            Sitelink = wikibase.getSitelink('Q2330008')
            if Sitelink then combinedname="[["..Sitelink .."|"..translate("infobox",27,womenrace_bool).."]]" end
        end
    end

    local winners = {
        { name = translate("infobox",19,womenrace_bool), QID = 'Q20882747'}, -- stage
        { name = overallname or translate("headoftableII",9,womenrace_bool), QID = 'Q20882763' }, -- overall
        { name = pointsname or translate("infobox",22,womenrace_bool), QID = 'Q20883008' }, -- points
        { name = mountainname or translate("infobox",23,womenrace_bool), QID = 'Q20883213' }, -- mountains
        { name = translate("infobox",24,womenrace_bool), QID= 'Q20883329' }, -- sprints
        { name = youngname or translate("infobox",25,womenrace_bool), QID='Q20883140' }, -- youth
        { name = combativityname or translate("infobox",26,womenrace_bool), QID= 'Q21686770' }, -- combativity
        { name = supercombativityname or translate("infobox",26,womenrace_bool), QID= 'Q20893984' }, -- combativity
        { name = translate("infobox",35,womenrace_bool), QID= 'Q27104688' }, -- volantes
        { name = translate("infobox",36,womenrace_bool), QID= 'Q27104684' }, -- regularity
        { name = combinedname or translate("infobox",27,womenrace_bool), QID='Q20965880' }, -- combination
        { name = translate("infobox",38,womenrace_bool), QID='Q27907714' }, -- breakaway
        { name = translate("infobox",39,womenrace_bool), QID='Q27907748' }, -- azzurri
        { name = translate("infobox",40,womenrace_bool), QID='Q28096780'}, -- rookie
        { name = teamname or translate("infobox",28,womenrace_bool), QID='Q20882922' }, -- teams
        { name = translate("infobox",37,womenrace_bool), QID ='Q27104271' }, -- teamspoints
        { name = translate("infobox",41,womenrace_bool), QID ='Q61976847' },-- amateur
        { name = translate("infobox",42,womenrace_bool), QID ='Q61976871' } --nationality
    }

    local winnersgen = {
        { QID = 'Q20882667' }, -- overall
        { QID = 'Q20883007' }, -- points
        { QID = 'Q20883212' }, -- mountains
        { QID = 'Q20883328' }, -- sprints
        { QID = 'Q20883139' }, -- youth
        { QID = 'Q101246973' }, -- supercombativity       
        { QID = 'Q20893983' }, -- combativity
        { QID = 'Q27067359' }, -- volantes
        { QID = 'Q27067170' }, -- regularity
        { QID = 'Q20893979' }, -- combination
        { QID = 'Q27907715' }, -- breakaway
        { QID = 'Q27907747' }, -- azzurri
        { QID = 'Q28092831' }, -- rookie
        { QID = 'Q20882921' },  -- teams
        { QID = 'Q27104269' }, -- teamspoints
        { QID = 'Q61976850' },  -- amateur
        { QID = 'Q61976872' } --nationality
    }

    local generaltoleader = {
        ['Q20882747']= nil,
        ['Q20882667']= 'Q20882763', -- overall
        ['Q20883007']= 'Q20883008', -- points
        ['Q20883212']= 'Q20883213', -- mountains
        ['Q20883328']= 'Q20883329', -- sprints
        ['Q20883139']= 'Q20883140', -- youth
        ['Q20893983']= 'Q20893984', -- combativity
        ['Q101246973']= 'Q21686770', -- supercombativity           
        ['Q27067359']= 'Q27104688', -- volantes
        ['Q27067170']= 'Q27104684', -- regularity
        ['Q20893979']= 'Q20965880', -- combination
        ['Q27907715']= 'Q27907714', -- breakaway
        ['Q27907747']= 'Q27907748', -- azzurri
        ['Q28092831']= 'Q28096780', -- rookie
        ['Q20882921']= 'Q20882922', -- teams
        ['Q27104269']= 'Q27104271', -- teamspoints
        ['Q61976850']= 'Q61976847', -- amateur
        ['Q61976872']= 'Q61976871'  --nationality
    }

    --read stages
    local stages = mw.wikibase.getBestStatements(raceID, 'P527') -- P527 is 'has part'
    local columntable, jerseytable, bgcolortable={}, {}, {}
    for ii, v in ipairs(winners) do
        if v.QID then
            local t = {key=ii, name=v.name, jersey='', bg_color='', used=false}
            for jj = 1, #stages+1 do
                t[jj] = { {}, {}, {} }  -- leader, first stage, number of stages consecutive (for rowspan)
            end
            columntable[v.QID] = t
        end
    end
    --to have the columns in the same order as defined, otherwise they would be sorted according to the order in wikidata
    --make "Q123", "Q456" --> 1, 2
    local function itercolumns(columntable)
        local keys = {}
        for k, v in pairs(columntable) do
            keys[v.key] = k --v.key is just the order of the columns
        end
        local upto = 1
        return function ()
            while keys[upto] do
                upto = upto + 1
                return columntable[keys[upto-1]]
            end
        end
    end

    local timeOfRace
    for ii, v in pairs(stages) do
        if v.mainsnak.snaktype == 'value' then
            local somewinner = false --show the stage
            local stageID = v.mainsnak.datavalue.value.id
            local sitelink = mw.wikibase.getSitelink(stageID)
            if displaytypeofstage==true then
                sType = typeofstagelogo(stageID)
            end
            local p = mw.wikibase.getBestStatements(stageID, 'P1545') -- P1545 is 'series ordinal'
            local sOrdinal = p[1] and p[1].mainsnak.snaktype == 'value' and p[1].mainsnak.datavalue.value
                or ''
            local _, _, sNumber, sLetter = string.find(sOrdinal, '(%d+)(.*)')
            sNumber=sNumber or ''
            sLetter=sLetter or ''

            local label, section_title
            if sOrdinal == "0" then
                label, section_title = translate("func_prologue"), "#" .. translate("func_prologue")
            else
                label, section_title = stageLink(sOrdinal, sNumber, sLetter)
            end
            -- If there is a Wikipedia article of that stage show it or show the section.
            local sLink = sitelink and ("[[" .. sitelink .. "|" .. label .. "]]") or
                ("[[" .. section_title .. "|" .. label .. "]]")

            timeOfRace =getTimeOfRace(stageID)

            local win= {}
            for _, v in pairs(winners) do
                win[v.QID] = ''
                if ii==1 then jerseytable[v.QID]='' end
            end
            winner(stageID, win, timeOfRace, false, WDlink_on, false, false) --fill win table
            if ii==1 then --only first stage
                jerseytable, bgcolortable=winnerjersey(stageID, jerseytable)
            end
            for _, v in pairs(winners) do
                if v.QID and win[v.QID] ~= '' then
                    --column info
                    somewinner=true
                    columntable[v.QID][ii]["leader"]=win[v.QID]
                    if ii==1 then --first stage
                        columntable[v.QID][ii]["start"]=1  --start at row 1
                        columntable[v.QID][ii]["rowspan"]=1  --1 consecutive stage
                    elseif columntable[v.QID][ii-1]["leader"]==win[v.QID] then --same winner as past stage ,make previous longer and delete this one
                        local initialstage=columntable[v.QID][ii-1]["start"]
                        columntable[v.QID][ii]["start"]=initialstage --need because of the row above
                        columntable[v.QID][initialstage]["rowspan"]=columntable[v.QID][initialstage]["rowspan"]+1 --one more consecutive stage
                        columntable[v.QID][ii]["rowspan"]=0
                    else --new winner
                        columntable[v.QID][ii]["start"]=ii --start at this row/stage
                        columntable[v.QID][ii]["rowspan"]=1  --1 consecutive stage
                    end
                    columntable[v.QID].used=true
                    if ii==1 then --read the jersey in the first stage of a race
                        columntable[v.QID].jersey=jerseytable[v.QID]
                        columntable[v.QID].bg_color=bgcolortable[v.QID]
                    end
                end
            end
            table.insert(stageinfotable,{sLink=sLink, sType=sType, somewinner=somewinner})
        end
    end

    --read parent
    local win= {}
    for _, v in pairs(winnersgen) do
        if v.QID then
            win[v.QID] = ''
            jerseytable[v.QID]=''
        end
    end
    local thiskey
    somewinner = false
    jerseytable, bgcolortable=winnerjersey(raceID, jerseytable)
    winner(raceID, win, timeOfRace, false, WDlink_on, false, false)
    for _, v in pairs(winnersgen) do
        if win[v.QID] and win[v.QID] ~= '' then
            somewinner=true
            thiskey=generaltoleader[v.QID]
            --fill the final classification
            columntable[thiskey][#stages+1]["leader"]=win[v.QID]
            columntable[thiskey][#stages+1]["start"]=#stages+1
            columntable[thiskey][#stages+1]["rowspan"]=1
            --#stages is the last stage
            if (type(columntable[thiskey][#stages]["leader"])~="string"  --combativity is not extrapolated
                and thiskey~='Q20893984') then --check nil actually, but it is a table..
               
                columntable[thiskey][#stages]["leader"]= win[v.QID] --extrapolate the winner
                if (type(columntable[thiskey][#stages-1]["leader"])=="string" and
                    win[v.QID]==columntable[thiskey][#stages-1]["leader"]) then --if there is a leader at forelast stage
               
                    local initialstage=columntable[thiskey][#stages-1]["start"]
                    columntable[thiskey][#stages]["start"]=initialstage --needed because of row above
                    columntable[thiskey][initialstage]["rowspan"]=columntable[thiskey][initialstage]["rowspan"]+1
                    columntable[thiskey][#stages]["rowspan"]=0
                else
                    columntable[thiskey][#stages]["start"]=#stages
                    columntable[thiskey][#stages]["rowspan"]=1
                end
            end
            if jerseytable[v.QID] and jerseytable[v.QID]~='' then
                columntable[thiskey].jersey=jerseytable[v.QID]
                columntable[thiskey].bg_color=bgcolortable[v.QID]
            end
        end
    end
    table.insert(stageinfotable,{sLink=translate("listofstagesclassification",2,womenrace_bool), sType=nil, somewinner=somewinner})

    --build the table
    local    tab=mw.html.create('table')
    :attr('cellpadding','4' )
    :attr('cellspacing','0')
    :cssText(standardtablecss)
    local tRow=tab:tag('tr'):css('background-color',backgroundColor)
    :css('text-align','center')
    tRow:tag('th'):css('white-space','nowrap')
    :wikitext(((not WDlink_on and wdLink(string.gsub(raceID, '%s', '') .. "#P527")) or "")..
    translate("headoftable",1,womenrace_bool))
    
    if displaytypeofstage==true then tRow:tag('th') end

    for v in itercolumns(columntable) do
        if v.used == true then
            if v.jersey == '' then v.jersey = "_" end
            tRow:tag('th'):wikitext(v.name.."<br />"..v.jersey)
        end
    end

    local style
    --then fill the table
    for ii, v in pairs(stageinfotable) do --one stage=one row
        --stages link
        tRow=tab:tag('tr')
        local tCell=tRow:tag('td')
        if ii==#stageinfotable then
            tCell:attr('colspan','2'):cssText('font-weight:bold; border-top: 2px black solid;')
        end
        tCell:wikitext(v.sLink)
       
        if displaytypeofstage == true then
            tCell=tRow:tag('td')
            if ii==#stageinfotable then --general row
                tCell:cssText('font-weight:bold; border-top: 2px black solid;')
            end
            if v.sType then
                tCell:wikitext(v.sType) --picture type of stage
            end
        end

        --add winners
        for y in itercolumns(columntable) do
            if y.used==true and not (ii==#stageinfotable and columntable['Q20882747']==y) then --only display used QID
                if type(y[ii]["leader"])=="string" and type(y[ii]["rowspan"])=="number" then --actually check nil but it is a table
                    style=""
                    if y[ii]["rowspan"]~=0 and (columntable['Q20882747']==y)==false then
                        if ii~=1 and ii~=#stageinfotable then style=style.." border-top:1px gray solid;" end
                        if y.bg_color then style=style.." background-color:"..y.bg_color..";" end
                        if ii==#stageinfotable then style=style.."font-weight:bold; border-top: 2px black solid;" end
                        tRow:tag('td'):attr('rowspan',tostring(y[ii]["rowspan"])):cssText(style):wikitext(y[ii]["leader"])
                    elseif (columntable['Q20882747']==y) then --no rowspan for stages
                        tRow:tag('td'):wikitext(y[ii]["leader"])
                    end
                else
                    tCell=tRow:tag('td')
                    if ii~=#stageinfotable and v.somewinner==true then
                        tCell:wikitext(translate("listofstagesclassification",1,womenrace_bool)) --not attributed
                    elseif ii~=#stageinfotable then
                         --empty
                    elseif v.somewinner==true then  --general row
                        tCell:cssText('border-top: 2px black solid')
                        :wikitext(translate("listofstagesclassification",1,womenrace_bool)) --not attributed
                    else
                        tCell:cssText('border-top: 2px black solid') --empty
                    end
                end
            end
        end
    end
    return tab
end

--M) Start list
function p.startlist(frame)
    local IDtemp
    if frame.args[1] ~= nil then
        IDtemp=string.gsub(frame.args[1], "%c", "")
    end
    local womenrace_bool=isWomenrace(IDtemp)
    local s = {
        header_function = "startlist",
        header_1 = 1, -- translation 1 in function victories is printed in the upper part of the table header
        header_2 = {2, 3,4,5},
        item=IDtemp,
        title="Start list",
        data_sort_type={'unsortable', 'unsortable', 'unsortable'},
        property ='P710',
        no_roll_startlist=no_roll_startlist,
        womenrace_bool=womenrace_bool
    }

    local resultTable, tag = tableB(s)
    return startlist_main(s, resultTable, tag) 
end

function p.startlisttable(frame)
    local IDtemp
    if frame.args[1] ~= nil then
        IDtemp=string.gsub(frame.args[1], "%c", "")
    end
    local womenrace_bool=isWomenrace(IDtemp)

    local s = {
        header_function = "startlisttable",
        header_1 = 1, -- translation 1 in function victories is printed in the upper part of the table header
        header_2 = {2, 3,4,5},-- translations 2, 3, 4, 5, 6 in function victories are printed in this order
        item=IDtemp,
        title="Start list", -- in the lower part of the table header. The second value 3 in {4, 3} tells where the icon will go.
        no_country ={'fr'},
        data_sort_type={'', '', ''},
        property ='P710',
        no_roll_startlist=no_roll_startlist,
        womenrace_bool=womenrace_bool
    }
    return startlisttable_main(s, tableA(s))
end

local function startlist_sub(p710, timeOfRace,  WDlink_on, istable,womenrace_bool)
    local h, resultTable= {}, {}
    local tBody = '' --row in our case
    local riderID, riderTeamLink, riderTeamID, riderDossard, riderLink, riderRank
    local q, gender, riderTeamCode, riderDNF, DSQ, catID, countryID, national_team_boolean

    riderID = p710.mainsnak.datavalue.value.id
    q= p710.qualifiers
    riderLink= getRiderLink(riderID, timeOfRace)
    if WDlink_on then riderLink=riderLink..wdLink(riderID) end
    if q and q.P1618 and q.P1618[1].snaktype == 'value' then
        riderDossard = q.P1618[1].datavalue.value or ''
    else
        riderDossard = ''
    end
    riderDNF='' riderRank = '' DSQ=''
    if q and q.P1352 and q.P1352[1].snaktype == 'value' then -- P1352 is ranking
        riderRank = tonumber(q.P1352[1].datavalue.value.amount)
        --look for DSQ--
        DSQ=isdisqualified(p710, q)
    else
        --look for DNF...
        if q and q.P1534 and q.P1534[1].snaktype == 'value' then
            local dnf=q.P1534[1].datavalue.value.id
            if dnf=='Q1210380' then riderDNF =translate("startlist",6,womenrace_bool)--"HD","NP","DQ"
            elseif dnf=='Q54881674' or dnf=='Q7113430' then riderDNF =translate("startlist",7,womenrace_bool)
            elseif dnf=='Q1210382' then riderDNF =translate("startlist",8,womenrace_bool)
            elseif dnf=='Q1229261' then riderDNF =translate("startlist",9,womenrace_bool)
            else riderDNF=''
            end
            if q.P1545 and q.P1545[1].snaktype == 'value' then
                local stageofdnf=q.P1545[1].datavalue.value
                if stageofdnf and string.len(stageofdnf)>1 then
                    riderDNF='<small>'..riderDNF.."-"..stageofdnf..'</small>'
                else
                    riderDNF=riderDNF.."-"..stageofdnf
                end
            end
        end
    end

    h = {
        jersey = {}, -- lots of jerseyID
        value = {'', '', '', ''} -- points, time, time_gap, speed
    }

    if q and q.P2912 then -- P2912 is distinctive jersey
        for _, v in pairs(q.P2912) do
            if v.snaktype == 'value' then
                table.insert(h.jersey, v.datavalue.value.id)
            end
        end
    end

    if wiki == 'es' or wiki == 'fr' or wiki == 'ast' then
        --[[ These wikis need the gender to display the rank correct. Other wikis can skip this. ]]
        gender = getGenderCode(riderID, 'n')
    end
    local countryID = getNationality(riderID, timeOfRace,q)
    local uciCode=''
    local jerseytemp=''
    if countryID then
        if wiki ~= "ar" then
            uciCode=uciCodeCountry(countryID)
        end
        riderLink = flag(countryID, timeOfRace) ..' '.. riderLink
    end

    if h.jersey[1] then
        jerseytemp=champtitle(h.jersey) -- champtitle manages also the jersey
    end
    
    riderTeamLink, riderTeamID, catID, countryID, national_team_boolean = getTeam(riderID, timeOfRace, q)
    riderTeamID=seasonToTeamID(riderTeamID)
    riderTeamCode= getTeamCode(riderID, timeOfRace, q)
    
    --Custom display for national selection
    if national_team_boolean and countryID then
        if riderTeamCode and wikibase.getSitelink(countryID) then --for the refugee case
            riderTeamCode='[['..wikibase.getSitelink(countryID)..'|'..riderTeamCode..']]'
        end
        riderTeamLink=flag(countryID, timeOfRace)..' '..riderTeamLink
    else --for non national selection display "ridername (FRA)""
        riderLink =riderLink..uciCode
    end
    riderLink =riderLink..jerseytemp

    if riderTeamLink == nil then riderTeamLink ="" end
    local sortkey = riderDossard == "" and 0 or tonumber(riderDossard)

    tBody =   mw.html.create('tr'):cssText("line-height: 1.8em; padding: 5px;")
    tBody:tag('td'):cssText("text-align:right;padding:0 0.5em"):wikitext(riderDossard)
    tBody:tag('td'):cssText('text-align:'..textalign.. ';padding:0 0.5em;'..DSQ):wikitext(riderLink)

    local td_css = "text-align:left;padding:0 0.5em"
    if wiki == "ar" then td_css = "text-align:right;padding:0 0.5em" end
    if istable then
        tBody:tag('td'):cssText(td_css):wikitext(riderTeamLink)
    end
    tBody:tag('td'):cssText('text-align:'..textalign.. ';padding:0 0.5em;'..DSQ):wikitext(number(gender,riderRank,wiki)..riderDNF)

    table.insert(resultTable, {sortkey=sortkey, riderTeamLink=riderTeamLink,riderTeamID=riderTeamID,riderTeamCode=riderTeamCode, tBody=tBody})
    return resultTable
end

function startlist_main(s, resultTable, tag)
    local ridertable, DStable, subtable     = {}, {}, {}
    local DSID, DSLink, DSteamID, DSteam
    local WDlink_on = (wiki == "mk" or wiki == "ja" or wiki == "ru")

    local timeOfRace=getTimeOfRace(s.item)
    local womenrace_bool=isWomenrace(s.item)

    for _,p286 in statements(s.item, 'P286') do--look for DS
        DSID = p286.mainsnak.datavalue.value.id
        DSLink= getRiderLink(DSID, timeOfRace)
        q= p286.qualifiers
        if q.P642 and q.P642[1].snaktype == 'value' then
            DSteamID=q.P642[1].datavalue.value.id
            DSteamID=seasonToTeamID(DSteamID)
        end
        table.insert(DStable, {DSLink=DSLink, DSteamID=DSteamID})
    end

    for _, p710 in statements(s.item, 'P710') do -- P710 is participants
        subtable=startlist_sub(p710, timeOfRace, WDlink_on, false,womenrace_bool)
        ridertable[#ridertable + 1] = {
            subtable[1].sortkey,
            riderTeamLink=subtable[1].riderTeamLink,
            riderTeamID=subtable[1].riderTeamID,
            riderTeamCode=subtable[1].riderTeamCode,
            tBody=subtable[1].tBody
        }
    end
    
    --sort
    table.sort(ridertable, function(a, b) return a[1] < b[1] end)

    local thisTableRow, thisTeamTable, thisDS, insideTable, test
    local tSubtitle, tTitle
    
    if wiki == "ar" then
        tSubtitle=mw.html.create('tr')
        tSubtitle:tag('td'):attr('width','30px')
        :css("align:right;text-align:right")
        :wikitext(translate("startlist",2,womenrace_bool))
        tSubtitle:tag('td'):attr('width','200px')
        :css("align:right;text-align:right")
        :wikitext(translate("startlist",3,womenrace_bool))
        tSubtitle:tag('td'):attr('width','85px')
        :css("align:right;text-align:right")
        :wikitext(translate("startlist",4,womenrace_bool))
    else
        tSubtitle=mw.html.create('tr')
        tSubtitle:tag('td'):attr('width','30px'):wikitext(translate("startlist",2,womenrace_bool))
        tSubtitle:tag('td'):attr('width','250px'):wikitext(translate("startlist",3,womenrace_bool))
        tSubtitle:tag('td'):attr('width','35px'):wikitext(translate("startlist",4,womenrace_bool))
    end

    --look for transition between teams
    local numberofteam=0
    local tDS

    if #ridertable==0 then--empty table
        return nil
    else
        for ii=1,#ridertable  do
            if ridertable[ii].riderTeamLink==nil then ridertable[ii].riderTeamLink=translate("startlist",13,womenrace_bool) end
            if ii~=1 and ridertable[ii].riderTeamID and ridertable[ii].riderTeamID==ridertable[ii-1].riderTeamID then test=0 else test=1 end--team change
                --new team
            if test==1 or ii==1 then
                if thisDS and ii~=1 then
                    tDS=insideTable:tag('tr')
                    tDS:tag('td'):attr('colspan','3'):attr('align','center')
                    :wikitext(translate("startlist",5,womenrace_bool).." "..thisDS)
                    thisDS=nil
                end
               
                numberofteam=numberofteam+1
                if math.fmod(numberofteam, 3 )==1 then
                    if ii~=1 then
                        tag:node(thisTableRow) --a row with 3 tables inside, save and re-init
                    end
                    thisTableRow=mw.html.create('tr')
                end
                thisTeamTable= thisTableRow:tag('td'):cssText("width:33%;"):attr('valign','top')
                insideTable=thisTeamTable:tag('table') --reinit
                :attr('cellpadding','4') --solid rgb(200,200,200)
                :attr('background-color','rgb(255, 255, 255)')
                :attr('margin', '0 0 0.5em 0')
                :attr('padding','5px')
                :attr('float','left')
                :attr('text-align',textalign)
                :attr('line-height','1.8em')
                :attr('clear',floattable)

                tTitle =  mw.html.create('tr')
                :css("background-color",backgroundColor)
                :attr('align','center')
                local tCell=tTitle:tag('th'):attr('colspan','3')
                tCell:tag('big'):wikitext(ridertable[ii].riderTeamLink.."<br/>"..(ridertable[ii].riderTeamCode or "___"))
               
                insideTable:node(tTitle)
                insideTable:node(tSubtitle)
               
                tDS=nil
                --look for the DS of this team
                for _,v in pairs(DStable) do
                    if v.DSteamID==ridertable[ii].riderTeamID then
                        if not thisDS then
                            thisDS=v.DSLink
                        else
                            thisDS=thisDS..", "..v.DSLink
                        end
                    end
                end
            end
            insideTable:node(ridertable[ii].tBody)
        end
        --last DS
        if thisDS then
            tDS=insideTable:tag('tr')
            tDS:tag('td'):attr('colspan','3'):attr('align','center')
            :wikitext(translate("startlist",5,womenrace_bool).." "..thisDS)
        end
        tag:node(thisTableRow)

        return resultTable
    end
end

function startlisttable_main(s, resultTable)
    local t_Body = {}
    local WDlink_on = (wiki == "mk" or wiki == "ja" or wiki == "ru")

    local timeOfRace=getTimeOfRace(s.item)

    for _, p710 in statements(s.item, 'P710') do -- P710 is participants
        local subtable=startlist_sub(p710, timeOfRace, WDlink_on, true)
        t_Body[#t_Body + 1] = {subtable[1].sortkey, tostring(subtable[1].tBody)}
    end
    return sortAndConcat(t_Body, resultTable)
end

-- N) Rider ranking
local function checkminmaxyear(minmaxyear,thisyear)
    if minmaxyear.minimum ==0 or thisyear<minmaxyear.minimum then
        minmaxyear.minimum=thisyear
    end
    if minmaxyear.maximum==0 or thisyear>minmaxyear.maximum then
        minmaxyear.maximum=thisyear
    end
   return minmaxyear
end

function p.riderranking(frame)
    local s = {
        item = string.gsub(frame.args[1] or frame:getParent().args[1], "%c", ""),
    }
    return riderranking_main(frame, s)
end


function riderranking_main(frame,s)
    local thisCompetition, rank, thisyear, sitelink, q, gender, DSQ
    local resultTable, listofcalendar, UCImaster, UCImasterlimist={},{},{},{}
    local minmaxyear= {
        minimum = 0, -- lots of jerseyID
        maximum = 0 -- points, time, time_gap, speed
    }
    local calendarlistpresent={
        ["UCIwomen"]=false,
        ["UCImen"]=false
    }
    
    local UCI = {}
    --inverse the table
    for k,v in pairs(data.UCIYearToQ) do
        UCI[k]={}
        for kk, vv in pairs(v) do
            UCI[k][vv]=kk
        end
    end
    
    local UCImaster=data.UCImaster
    local gender=getGenderCode(s.item, 'm')
    local womenrace_bool=false
    if gender=="f" then womenrace_bool=true end

    UCImastername={
        ["women"]=    translate("riderranking",2,womenrace_bool),
        ['WWT']=    translate("riderranking",3,womenrace_bool),
        ['WWC']=    translate("riderranking",4,womenrace_bool),   
        ["UWT"]=    translate("riderranking",5,womenrace_bool),
        ["europe"]=    translate("riderranking",6,womenrace_bool),
        ["asia"]=    translate("riderranking",7,womenrace_bool),
        ["oceania"]=translate("riderranking",8,womenrace_bool),
        ["america"]=translate("riderranking",9,womenrace_bool) ,
        ["africa"]=    translate("riderranking",10,womenrace_bool),
        ["WR"]=        translate("riderranking",11,womenrace_bool),
        ["WC"]=        translate("riderranking",12,womenrace_bool),
        ["UPT"]=    translate("riderranking",13,womenrace_bool), --WC is world calendar here
        ["UCImen"]=    translate("riderranking",14,womenrace_bool),
        ["WCmen"]=    translate("riderranking",15,womenrace_bool), --UCImen = UCI ranking 1984-2004, WC= World cup men
        ["Pernod"]=    translate("riderranking",16,womenrace_bool),
        ["Desgrange"]=translate("riderranking",17,womenrace_bool),
    }

    UCImasterlimit={
        ["women"]=    {b=1989, e=0}, --women=Calendrier international féminin UCI, begin/end 0 = no end
        ["WWT"]=    {b=2016, e=0},
        ["WWC"]=    {b=1998, e=2015},
        ["UWT"]=    {b=2011, e=2018},
        ["europe"]= {b=2005, e=0},
        ["asia"]=    {b=2005, e=0},
        ["oceania"]={b=2005, e=0},
        ["america"]={b=2005, e=0},
        ["africa"]= {b=2005, e=0},
        ["WR"]=     {b=2016, e=0},
        ["WC"]=     {b=2009, e=2010},
        ["UPT"]=    {b=2005, e=2008},
        ["UCImen"]= {b=1984, e=2004},
        ["WCmen"]=  {b=1989, e=2004},
        ["Pernod"]= {b=1959, e=1987},
        ["Desgrange"]=  {b=1948, e=1958},
    }
    
    local listofwomencalendar={"women","WWC",  "WWT"} --"women" is in fact UCIwomen
    local listofmencalendar={"Desgrange","Pernod","UCImen","WCmen","UPT",
                            "WC","UWT","WR","europe","asia","america","oceania","africa","Pro"}
    
    if gender=="f" then
        listofcalendar=listofwomencalendar
    else
        listofcalendar=listofmencalendar
    end
    
    --init table
    for ii=1900,2100,1 do
        resultTable[tostring(ii)]={}
        for _, calendar  in pairs(listofcalendar) do
            resultTable[tostring(ii)][calendar]={
                rank=nil,
                sitelink=nil
            }
        end
    end

    --build the table
    for _, p1344 in statements(s.item, 'P1344') do
        thisCompetition = p1344.mainsnak.datavalue.value.id
    
        for _, calendar  in pairs(listofcalendar) do
            if UCI[calendar][thisCompetition] then
                thisyear=UCI[calendar][thisCompetition]
                minmaxyear=checkminmaxyear(minmaxyear,thisyear)
                q = p1344.qualifiers
                if q and q.P1352 and q.P1352[1].snaktype == 'value' then --rank
                    rank = tonumber(q.P1352[1].datavalue.value.amount)
                    DSQ = isdisqualified(p1344, q)
                 else
                    rank= nil
                end
                if rank then
                    resultTable[thisyear][calendar]["rank"]=tostring(rank)
                    resultTable[thisyear][calendar]["DSQ"]=DSQ or ""
                    calendarlistpresent[calendar]=true
                    sitelink=mw.wikibase.getSitelink(thisCompetition)
                    resultTable[thisyear][calendar]["sitelink"]=sitelink
                end
            end
        end
    end

    --display result
    if minmaxyear.minimum~=0 then
        local finalTable =mw.html.create('table'):attr('cellspacing','0')
        :attr("align","center"):cssText("text-align:center; border: 1px solid #999;  line-height: 1.8em;")
       
        local wdLin = wdLink(string.gsub(s.item, '%s', '') .. "#P1344")
        local tRow= finalTable:tag('tr'):tag('th')
        :css("background-color",backgroundColor)
        :wikitext(wdLin..' '..translate("riderranking",1,womenrace_bool))

        for ii=minmaxyear.minimum,minmaxyear.maximum,1 do
            tRow:tag('th'):attr("width","50px")
            :css('background-color',backgroundColor)
            :css("text-align","center")
            :css("padding","1px 1px")
            :wikitext(tostring(ii))
        end

        for _, calendar  in pairs(listofcalendar) do
            if calendarlistpresent[calendar] then
                sitelink=mw.wikibase.getSitelink(UCImaster[calendar])
                local tRow=finalTable:tag('tr')
                local tCell = tRow:tag('th'):cssText("text-align:" .. textalign .. ";") -- left
                if sitelink then
                    tCell:wikitext('[['..sitelink..'|'..UCImastername[calendar]..']]')
                else
                    tCell:wikitext(UCImastername[calendar])
                end
               
                for yy=minmaxyear.minimum,minmaxyear.maximum,1 do
                    thisyear=tostring(yy)
                    color="white"
                    if resultTable[    thisyear][calendar]["rank"] then
                        if resultTable[thisyear][calendar]["rank"]=="1" then
                            color="gold"
                        elseif (2<=tonumber(resultTable[thisyear][calendar]["rank"])) and (tonumber(resultTable[thisyear][calendar]["rank"])<=3) then
                            color="YellowGreen"
                        elseif (4<=tonumber(resultTable[thisyear][calendar]["rank"])) and (tonumber(resultTable[thisyear][calendar]["rank"])<=10) then
                            color="silver"
                        end

                        tCell=tRow:tag('td'):attr("bgcolor",color):cssText(resultTable[thisyear][calendar]["DSQ"])
                        local rank=tonumber(resultTable[thisyear][calendar]["rank"])
                        rank=number(gender,rank,wiki)
                        if resultTable[thisyear][calendar]["sitelink"] then
                            tCell:wikitext('[['..resultTable[thisyear][calendar]["sitelink"]..'|'..rank..']]')
                        else
                            tCell:wikitext(rank)
                        end
                    --this ranking exist for this year, but the rider is not ranked
                    elseif yy>=UCImasterlimit[calendar].b and
                        (UCImasterlimit[calendar].e==0 or yy<=UCImasterlimit[calendar].e) then
                        if wiki=="fr" then
                            tRow:tag('td'):wikitext(' nc ')
                        else
                            tRow:tag('td'):wikitext(' - ')
                        end
                    --this ranking does not exist for this year
                    else
                        tRow:tag('td'):css('background-color',backgroundColorLight)
                    end
                end
            end
        end

        local UCIlink, legend
        if wiki=="fr" then
            UCIlink="https://www.uci.org/fr/route/classements"
            legend= "  Légende : nc = non classé"
        else
            UCIlink="https://www.uci.org/road/rankings"
            legend=""
        end
       
        local tableyearsize=minmaxyear.maximum-minmaxyear.minimum+2
       
        finalTable:tag('tr'):tag('td'):addClass("navigation-only")
        :attr('colspan',tostring(tableyearsize))
        :cssText("border-top: 2px "..backgroundColor.." solid; font-size: 80%;")
       
        tCell=finalTable:tag('tr'):tag('td'):attr('colspan',tostring(tableyearsize))
        :tag('small')
       
        tCell:tag('span'):css("float","left")
        :wikitext(legend)
        tCell:tag('span'):css("float","right")
        :wikitext(translate("race_reference", 1,womenrace_bool).."["..UCIlink..' UCI]')

        return  finalTable
    end
end   


local function toboolean(str)
    if str=="true" then
        return true
    elseif str=="false" then
        return false
    else
        return str
    end
end

--=== O) Rider infobox
local function convertDate(date1, beginOrEnd, initialYear, finalYear)
    if not date1 then
        if beginOrEnd==0 then --begin
            y1=tostring(initialYear)
            m1="01"
            d1="01"
        else
            y1=tostring(finalYear)
            m1="12"
            d1="31"
        end
    else
        _, _, y1,m1,d1 = string.find(date1, "(%d+)-(%d+)-(%d+)")
        if m1 ==nil or m1=="00" then
            if beginOrEnd==0 then --begin
                m1="01"
                d1="01"
            else--end
                m1="12"
                d1="31"
            end
        end
    end
    return '+'..y1.."-"..m1.."-"..d1.."T00:00:00Z"
end

local function listofTeam(itemID, initialYear, finalYear, PID)
    --first we have to read P54 of the rider
    --alternative P6087 for managed team
    local riderteam={}
    local stagiaire

    for ii, p54 in statements(itemID, PID) do --itemID loaded in presentTeam
        if p54 then
            teamId=p54.mainsnak.datavalue.value.id
        else
            teamId=nil
        end
        local q = p54.qualifiers
        if q then
            local sTime, eTime=getStartEndfromQuali(q)
            sTime=convertDate(sTime, 0, initialYear, finalYear)
            eTime=convertDate(eTime, 1, initialYear, finalYear)
            if q.P39 and q.P39[1] and    q.P39[1].snaktype == 'value' then
                stagiaire = q.P39[1].datavalue.value
            else
                stagiaire = nil
            end
            dis=checkDis(q)
            table.insert(riderteam,{teamId=teamId, startTime=sTime, endTime=eTime, stagiaire=stagiaire, dis=dis})
        end
    end
    return riderteam
end

--format the date for display of the team
local function riderFormatDate(thisDate)
    if thisDate=='' then
        return ''
    else
        local month=math.ceil(thisDate['month']/2)
        if month==12 or month==1 then
            return thisDate['year']
        else
            local date1='+'..thisDate['year'].."-"..month.."-".."01".."T00:00:00Z"
        --    local newobj = Complexedate.splitDate(date1) 
            if month == 0 or month==nil then
                return thisDate['year']
            else
                return month..'.'..thisDate['year']
            end
        end
    end
end

local function getTeamInfo(teamId,mm,yy,dd,managedTeam)
    --get the nature and name of the team for the date mm,yy
    mm=tostring(mm)
    yy=tostring(yy)
    dd=tostring(dd)
    if mw.ustring.len(mm)==1 then mm='0'..mm end
    if mw.ustring.len(dd)==1 then dd='0'..dd end
    
    thistime='+'..yy.."-"..mm.."-"..dd.."T00:00:00Z"
    local sitelink, teamNature=getTeamLinkCat(teamId, thistime, false)
    local cat, boolean

    if managedTeam then
       cat=nationalcat
    else
       cat=amateurcat
    end

    if cat[teamNature] then --club
        boolean=true--amateur / national selection
    else
        boolean=false--pro  / not national selection
    end
    return boolean, sitelink
end

--for managed team, the table should be splat, as we can be national trainer and team trainer at the same time
local function analyzeManagedTeam(teamRider, initialYear,finalYear)
    local natTeamOut, managedTeamOut={},{}
    local dis="road"
    local managedTeam=true
    
    for i=1,24 do --init table
        natTeamOut[i]={}
        managedTeamOut[i]={}
        for j=initialYear,finalYear do
            natTeamOut[i][j]={ amateurTeam, link, stagiaire=nil}
            managedTeamOut[i][j]={amateurTeam,link, stagiaire=nil}
        end
    end

    local teamId, natTeam, sitelink
    local sYear, sMonth,eYear, eMonth, sDay, eDay

    if teamRider==nil then return nil end
    
    for _, v in pairs(teamRider) do --for each team where was the rider
        if v['dis']==dis then
            --exception managed at the reading
            _, _, sYear,sMonth,sDay = string.find(v['startTime'], "(%d+)-(%d+)-(%d+)")
            _, _, eYear,eMonth,eDay = string.find(v['endTime'], "(%d+)-(%d+)-(%d+)")
    
            sYear=tonumber(sYear)
            sMonth=tonumber(sMonth)
            eYear=tonumber(eYear)
            eMonth=tonumber(eMonth)
    
            if sYear<=eYear then --test of congruence
                for yy=sYear,eYear do
                    for mm=1,12 do
                        local mmindex=(mm-1)*2+1
                        --avoid reading info where the team is not the one of the rider
                        getinfo=true
                        if (yy==sYear and mm<sMonth) or (yy==eYear and mm>eMonth) then
                            getinfo=false
                        end
    
                        if getinfo then
                            if (yy==sYear) and (mm==sMonth) and (sDay~='01' and sDay~='00' and sDay~=nil)then
                                natTeam, sitelink=getTeamInfo(v['teamId'],mm,yy,sDay, managedTeam)
                                if natTeam then
                                    natTeamOut[mmindex+1][yy]['amateurTeam']=true
                                    natTeamOut[mmindex+1][yy]['link']=sitelink
                                else
                                    managedTeamOut[mmindex+1][yy]['amateurTeam']=false
                                    managedTeamOut[mmindex+1][yy]['link']=sitelink
                                end   
                            else
                                natTeam, sitelink=getTeamInfo(v['teamId'],mm,yy,'01', managedTeam)
                                if natTeam then
                                    natTeamOut[mmindex][yy]['amateurTeam']=true
                                    natTeamOut[mmindex][yy]['link']=sitelink
                                    if natTeamOut[mmindex+1][yy]['amateurTeam']==nil or v['stagiaire'] then --to avoid problem with team name change during the month
                                        natTeam, sitelink=getTeamInfo(v['teamId'],mm,yy,'28',managedTeam)
                                        natTeamOut[mmindex+1][yy]['amateurTeam']=true --a nat team stays a nat team
                                        natTeamOut[mmindex+1][yy]['link']=sitelink
                                    end
                                else
                                    managedTeamOut[mmindex][yy]['amateurTeam']=false
                                    managedTeamOut[mmindex][yy]['link']=sitelink
                                    if managedTeamOut[mmindex+1][yy]['amateurTeam']==nil or v['stagiaire'] then --to avoid problem with team name change during the month
                                        natTeam, sitelink=getTeamInfo(v['teamId'],mm,yy,'28',managedTeam)
                                        managedTeamOut[mmindex+1][yy]['amateurTeam']=false --a nat team stays a nat team
                                        managedTeamOut[mmindex+1][yy]['link']=sitelink
                                    end
                                end
                            end
                        end
                    end
                end
            end
        end
    end
    return natTeamOut, managedTeamOut --a filled matrix with the link and nature of the teams
end

local function analyzeTeam(teamRider, initialYear,finalYear, dis)
    local teamOut={}
    local managedTeam=false
    
    for i=1,24 do --init table
        teamOut[i]={}
        for j=initialYear,finalYear do
            teamOut[i][j]={ amateurTeam, link, stagiaire}
        end
    end

    local teamId, amateurTeam, sitelink
    local sYear, sMonth,eYear, eMonth, sDay, eDay

    if teamRider==nil then return nil end
    
    for _, v in pairs(teamRider) do --for each team where was the rider
        if v['dis']==dis then
            --exception managed at the reading
            _, _, sYear,sMonth,sDay = string.find(v['startTime'], "(%d+)-(%d+)-(%d+)")
            _, _, eYear,eMonth,eDay = string.find(v['endTime'], "(%d+)-(%d+)-(%d+)")
    
            sYear=tonumber(sYear)
            sMonth=tonumber(sMonth)
            eYear=tonumber(eYear)
            eMonth=tonumber(eMonth)
    
            if sYear<=eYear then --test of congruence
                for yy=sYear,eYear do
                    for mm=1,12 do
                        local mmindex=(mm-1)*2+1
                        --avoid reading info where the team is not the one of the rider
                        getinfo=true
                        if (yy==sYear and mm<sMonth) or (yy==eYear and mm>eMonth) then
                            getinfo=false
                        end
    
                        if getinfo then
                            if (yy==sYear) and (mm==sMonth) and (sDay~='01' and sDay~='00' and sDay~=nil)then
                                amateurTeam, sitelink=getTeamInfo(v['teamId'],mm,yy,sDay, managedTeam)
                                teamOut[mmindex+1][yy]['amateurTeam']=amateurTeam
                                teamOut[mmindex+1][yy]['link']=sitelink
                                teamOut[mmindex+1][yy]['stagiaire']=v['stagiaire']
                            else
                                amateurTeam, sitelink=getTeamInfo(v['teamId'],mm,yy,'01', managedTeam)
                                teamOut[mmindex][yy]['amateurTeam']=amateurTeam
                                teamOut[mmindex][yy]['link']=sitelink
                                teamOut[mmindex][yy]['stagiaire']=v['stagiaire']
                                if teamOut[mmindex+1][yy]['amateurTeam']==nil or v['stagiaire'] then --to avoid problem with team name change during the month
                                    amateurTeam, sitelink=getTeamInfo(v['teamId'],mm,yy,'28',managedTeam)
                                    teamOut[mmindex+1][yy]['amateurTeam']=amateurTeam
                                    teamOut[mmindex+1][yy]['link']=sitelink
                                    teamOut[mmindex+1][yy]['stagiaire']=v['stagiaire']
                                end
                            end
                        end
                    end
                end
            end
        end
    end
    return teamOut --a filled matrix with the link and nature of the teams
end

local function insertTeam(teamAmateur,teamPro,sDate,eDate,v)
    local sDate2=riderFormatDate(sDate)
    local eDate2=riderFormatDate(eDate)
    local ins = {link=v['link'], sDate=sDate2,eDate=eDate2,stagiaire=v['stagiaire']}
    
    if v['amateurTeam'] then
        table.insert(teamAmateur,ins)
    else
        table.insert(teamPro,ins)
    end
    return teamAmateur,teamPro
end

local function synthetizeTable(analyzedTeam, initialYear,finalYear)
    local teamPro, teamAmateur, tempTeam, tempsDate, tempeDate={}, {},{},{},{}
    local empty=true
    local active=false
    --bring together successive month with identical content
    for yy=initialYear,finalYear do
        for mm=1,24 do
            local v=analyzedTeam[mm][yy]
            if v['amateurTeam']~=nil then
                if empty then --first line
                    active=true
                    empty=false
                    tempTeam=v
                    tempsDate['month']=mm
                    tempsDate['year']=yy
                else
                    if tempTeam['amateurTeam']==v['amateurTeam'] and tempTeam['link']==v['link']
                    and tempTeam['stagiaire']==v['stagiaire'] then --no change
                        if yy==finalYear and mm==24 then--present team
                            teamAmateur,teamPro=insertTeam(teamAmateur,teamPro,tempsDate,'',tempTeam)
                        end
                    else--change
                        --save the old
                        if active then --if active false then it was already saved
                            if mm==1 then
                                tempeDate['year']=yy-1
                                tempeDate['month']=24
                            else
                                tempeDate['year']=yy
                                tempeDate['month']=mm-1
                            end
                            teamAmateur,teamPro=insertTeam(teamAmateur,teamPro,tempsDate,tempeDate,tempTeam)
                        end
                        --save the new
                        active=true
                        tempTeam=v
                        tempsDate['month']=mm
                        tempsDate['year']=yy
                    end --change
                end--first line
            elseif active then --there was a team and now there is an empty period
                active=false
                --save the old
                if mm==1 then
                    tempeDate['year']=yy-1
                    tempeDate['month']=24
                else
                    tempeDate['year']=yy
                    tempeDate['month']=mm-1
                end
                teamAmateur,teamPro=insertTeam(teamAmateur,teamPro,tempsDate,tempeDate,tempTeam)
                tempTeam['link']=nil --avoid problem if the rider comes back in the same team
            end 
        end-- for mm
    end--for yy
    return teamAmateur,teamPro
end

local function listOfManagedTeamTable(itemID, initialYear,finalYear)
    local managedTeamRider = listofTeam(itemID, initialYear,finalYear,'P6087')--raw list of team
    if not managedTeamRider then
        return nil, nil
    end
    
    local natTeamOut, managedTeamOut=analyzeManagedTeam(managedTeamRider, initialYear,finalYear) --table with links and nature of teams
    local nationalTeam,_=synthetizeTable(natTeamOut, initialYear,finalYear)
    local _,managedTeam=synthetizeTable(managedTeamOut, initialYear,finalYear)
    
    return nationalTeam,managedTeam
end

local function listOfTeamTable(itemID, initialYear,finalYear)
    local teamRider = listofTeam(itemID, initialYear,finalYear,'P54')--raw list of team
    if not teamRider then
        return nil, nil
    end
    local analyzedTeam1=analyzeTeam(teamRider, initialYear,finalYear, "road") --table with links and nature of teams
    local teamAmateur,teamPro=synthetizeTable(analyzedTeam1, initialYear,finalYear) --table formated, global
    local analyzedTeam2=analyzeTeam(teamRider, initialYear,finalYear, "mountainBike")
    local _, teamMountainBike=synthetizeTable(analyzedTeam2, initialYear,finalYear)
    local analyzedTeam3=analyzeTeam(teamRider, initialYear,finalYear, "cycloCross")
    local _, teamCycloCross=synthetizeTable(analyzedTeam3, initialYear,finalYear)
    local analyzedTeam4=analyzeTeam(teamRider, initialYear,finalYear, "track")
    local _, teamTrack=synthetizeTable(analyzedTeam4, initialYear,finalYear)   
    
    return teamAmateur,teamPro, teamMountainBike, teamCycloCross, teamTrack
end   

function getBirthDeathDate(entityID, display_age)
    local birthDate=firstValue(entityID, 'P569', 'time')
    local deathDate=firstValue(entityID, 'P570', 'time')
    local temp2, temp3, birth, death, initialYear, finalYear, age
    
    local gender=getGenderCode(entityID, 'm')
    local womenrace_bool=false
    if gender=="f" then womenrace_bool=true end

    if birthDate then
        local birthDateFormatted= funcDate(birthDate, 'long')
        age, initialYear, finalYear=calculateAge(birthDate)
        local birthPlace = firstValue(entityID, 'P19', 'id')
        local birthPlaceLink=''
        if birthPlace then birthPlaceLink=getPlaceLink(birthPlace, birthDate) end

        local plural, gen_singular, gen_plural = plural(age)
        local ans
        if gen_singular then
            ans=translate("riderinfobox",48,womenrace_bool)
        elseif gen_plural then
            ans=translate("riderinfobox",49,womenrace_bool)
        else
            ans=translate("riderinfobox",50,womenrace_bool)
        end

        if not deathDate and display_age~=false then
            temp2=' ('..tostring(age)..' '..ans..')<br/>'
        else
            temp2='<br/>'
        end
        birth=birthDateFormatted..temp2..birthPlaceLink
    else
        birth=nil
    end
    
    if deathDate then
        local deathDateFormatted= funcDate(deathDate, 'long')
        local deathPlace= firstValue(entityID, 'P20', 'id')
        local deathPlaceLink=''
        if deathPlace then deathPlaceLink=getPlaceLink(deathPlace, deathDate) end
       
        if birthDate then
            local age=calculateAge(birthDate, deathDate)
            local plural, gen_singular, gen_plural = plural(age)
            local ans
            if gen_singular then
                ans=translate("riderinfobox",48,womenrace_bool)
            elseif gen_plural then
                ans=translate("riderinfobox",49,womenrace_bool)
            else
                ans=translate("riderinfobox",50,womenrace_bool)
            end
            if display_age==false then
                temp2='<br/>'
            else
                temp2=' ('..tostring(age)..' '..ans..')<br/>'
            end
        else
            temp2='<br/>'
        end

        death=deathDateFormatted..temp2..deathPlaceLink
    else
        death=nil
    end

    return birth, death, initialYear, finalYear
end

local function presentTeam(itemID)
    local tToday=os.date("*t") 
    if mw.ustring.len(tToday["month"])==1 then tToday["month"]='0'..tToday["month"] end
    if mw.ustring.len(tToday["day"])==1 then tToday["day"]='0'..tToday["day"] end   
    local today='+'..tToday["year"].."-"..tToday["month"].."-"..tToday["day"].."T00:00:00Z"
    local plural=false
    local teamId, result, teamLink, teamLinkRoad, row

    for _, s in statements(itemID, 'P54') do
        p54 =checktime(s, s.qualifiers, today) --present Team
        if p54 then
            teamId= p54.mainsnak.datavalue.value.id
            teamLink=getTeamLinkCat(teamId, today)
            dis=checkDis(p54.qualifiers)
            row=nil
            if dis=='road' then
                teamLinkRoad=teamLink
            elseif dis=='mountainBike' then
                row=teamLink..' (VTT)'
            elseif dis=='cycloCross' then
                row=teamLink..' (cyclo-cross)'
            else
                row=teamLink..' (piste)'
            end
            if row then
                if not result then
                    result = row
                else
                    result= result..'<br/>'..row
                    plural = true
                end
            end
        end
    end
    if teamLinkRoad and result then --put road first
        result = teamLinkRoad..' (route)<br/>'..result
        plural= true
    elseif teamLinkRoad then
        result = teamLinkRoad
    end
    return result, plural
end

local function getSomeNames(details,entityID, PID, index, display_language)
    local rows={}
    if not details[index].content then
        local listOfNames=getFormerNames(entityID, PID)
        if listOfNames then
            for _, v in pairs(listOfNames) do
                rows[#rows + 1]=v[3]
                if v[2] and v[2]~='' then
                    rows[#rows]=rows[#rows]..' <small>('..v[2]..')</small>'
                end
                if display_language then
                    rows[#rows]=rows[#rows]..' <b><small>('..v[4]..')</small></b>'
                end
            end
            if #rows>0 then
                details[index].content = table.concat(rows, '<br/>')
            end
        end
    end
end

--for wikidata input
function teamTable(tab, teamAmateur, title_singular, title_plural)
    if teamAmateur and #teamAmateur>0 then
        if #teamAmateur==1 then
            tab:node(addATitle(title_singular))
        else
            tab:node(addATitle(title_plural))
        end
        for _, v in pairs(teamAmateur) do
            local nametemp=v['link']
            if v['sDate']==v['eDate'] then
                periodtemp=v['sDate']
            else
                periodtemp=v['sDate']..'-'..v['eDate']
            end
            if v['stagiaire'] then
                local stagiaire = string.gsub(wikibase.label('Q2328847'), "%b()", "") or getLabelFallback('Q2328847',{'en', 'fr', 'de'})
                nametemp=nametemp..' ('..stagiaire..')'
            end
            tab:node(addARow(periodtemp or '',nametemp)) --period, name
        end
    end
end

--for local data
function localTeamTable(tab, names, periods, title_singular, title_plural)
    if names then
        names =  mw.text.split(names, '<br />')
        periods = mw.text.split(periods or '', '<br />')
    
        if #names==1 then
            tab:node(addATitle(title_singular))
        else
            tab:node(addATitle(title_plural))
        end
        for i, name in pairs(names) do
            tab:node(addARow(periods[i] or '', name))
        end
    end
end   

function p.riderinfobox(frame)
    local frame = frame
    local lang = contentLanguage
    -- If true, winners will have Wikidata logos with link to Wikidata
    local WDlink_on = (wiki == "mk" or wiki == "ja")

    local localframe
    if string.match(frame:getParent():getTitle(), '%P+') == mw.site.namespaces.Template.name then
        localframe = frame:getParent()
    else
        localframe = frame
    end
    
    local entityID = mw.text.trim(frame.args[1])
    if type(entityID) ~= 'string' then error('parameter must be a string') end
    if not entityID:match('Q%d+') then error('parameter must be a valid Wikidata item (ex: Q42)') end
    
    local gender=getGenderCode(entityID, 'm')
    local womenrace_bool=false
    if gender=="f" then womenrace_bool=true end

    local details = {
        { name = translate("riderinfobox",1,womenrace_bool), name_plural =translate("riderinfobox",2,womenrace_bool)}, -- birth name
        { name = translate("riderinfobox",3,womenrace_bool), name_plural =translate("riderinfobox",4,womenrace_bool)}, -- nick name
        { name = translate("riderinfobox",5,womenrace_bool), name_plural =translate("riderinfobox",6,womenrace_bool)}, -- official name
        { name = translate("riderinfobox",7,womenrace_bool), name_plural =translate("riderinfobox",8,womenrace_bool)}, -- official name
        { name = translate("riderinfobox",9,womenrace_bool) }, -- birth translate("riderinfobox",9)
        { name = translate("riderinfobox",10,womenrace_bool)}, -- death
        { name = translate("riderinfobox",11,womenrace_bool), name_plural =translate("riderinfobox",12,womenrace_bool)}, -- country
        { name = translate("riderinfobox",13,womenrace_bool), name_plural =translate("riderinfobox",14,womenrace_bool)}, -- present team
        { name = translate("riderinfobox",15,womenrace_bool), name_plural =translate("riderinfobox",16,womenrace_bool)}, -- speciality
        { name = translate("riderinfobox",17,womenrace_bool) }, -- lateralisation
        { name = translate("riderinfobox",18,womenrace_bool) }, -- blood group
        { name = translate("riderinfobox",19,womenrace_bool) },  -- height
        { name = translate("riderinfobox",20,womenrace_bool) }, -- weight
        { name = translate("riderinfobox",21,womenrace_bool), name_plural =translate("riderinfobox",22,womenrace_bool)}, -- awards
    }
    
    local teams = {
        { name = translate("riderinfobox",23,womenrace_bool), name_plural =translate("riderinfobox",24,womenrace_bool)}, -- directed teams
        { name = translate("riderinfobox",25,womenrace_bool)}, -- directed years
        { name =  translate("riderinfobox",26,womenrace_bool), name_plural =translate("riderinfobox",27,womenrace_bool)}, -- amateur names 
        { name = translate("riderinfobox",28,womenrace_bool)}, -- amateur periods
        { name = translate("riderinfobox",29,womenrace_bool), name_plural =translate("riderinfobox",30,womenrace_bool)}, -- nonUCI names
        { name = translate("riderinfobox",31,womenrace_bool)}, -- nonUCI periods
        { name = translate("riderinfobox",32,womenrace_bool), name_plural =translate("riderinfobox",33,womenrace_bool)}, -- pro names
        { name = translate("riderinfobox",34,womenrace_bool)}, -- pro periods
        { name = translate("riderinfobox",35,womenrace_bool), name_plural =translate("riderinfobox",36,womenrace_bool)}, -- UCI names
        { name = translate("riderinfobox",37,womenrace_bool)}, -- UCI periods
        { name = translate("riderinfobox",52,womenrace_bool), name_plural =translate("riderinfobox",53,womenrace_bool)}, -- national selections
        { name = translate("riderinfobox",54,womenrace_bool)}, -- national selection years
        }

    --separated to have a title
    local subtitle = {
        { name = translate("riderinfobox",51,womenrace_bool)}, -- insertion of a sub-title
    }
    
    --separated to have a title
    local victories = {
        { name = translate("riderinfobox",38,womenrace_bool)}, -- main victories
    }
    
    --separated to have a title
    local medals = {
        { name = translate("riderinfobox",47,womenrace_bool)}, -- main victories
    }

    local others = {
        { name = translate("infobox",29,womenrace_bool)}, -- picture
        { name = translate("infobox",30,womenrace_bool)}, -- caption
        { name = translate("infobox",31,womenrace_bool)}, -- map
        { name = 'sectional'},             -- sectional
        { name = translate("infobox",30,womenrace_bool)}, -- caption map
        { name = translate("infobox",30,womenrace_bool)}, -- caption sectional
    }

    local name =  getLabelFallback(entityID, {wikilang, 'en', 'fr', 'de'}) or ''
    

    local display_birthnameastitle=false
    for _, value in pairs(display_birthnameastitle_in_riderinfobox) do -- get data if country should be printed in this wiki
        if value == wiki then display_birthnameastitle=true end
    end
    getLocalContent(subtitle, localframe.args)
    
    if not subtitle[1].content and display_birthnameastitle then
        local p1477 = mw.wikibase.getBestStatements(entityID, "P1477")
        if p1477[1] and p1477[1].mainsnak.snaktype == 'value' then
            subtitle[1].content = p1477[1].mainsnak.datavalue.value.text..' <b><small>('..
            p1477[1].mainsnak.datavalue.value.language..')</small></b>'
        end
        if not subtitle[1].content then
            local p1559 = mw.wikibase.getBestStatements(entityID, "P1559") -- P580 is start time
            if p1559[1] and p1559[1].mainsnak.snaktype == 'value' then
                subtitle[1].content = p1559[1].mainsnak.datavalue.value.text..' <b><small>('..
                p1559[1].mainsnak.datavalue.value.language..')</small></b>'
            end
        end
    end

    infoGetOthers(others, entityID)   

    getLocalContent(details, localframe.args)
    getLocalContent(teams, localframe.args)
    getLocalContent(others, localframe.args)
    getLocalContent(victories, localframe.args)
    getLocalContent(medals, localframe.args)

    local listOfBirthNames, listOfNickNames, listOfOfficialNames, listOfShortNames
    
    local icon = ' [[File:Cycling (road) pictogram.svg|35px]]'

    local display_language=false
    for _, value in pairs(display_language_in_riderinfobox) do -- get data if country should be printed in this wiki
        if value == wiki then display_language=true end
    end
    
    local display_age=true
    for _, value in pairs(display_noage_in_riderinfobox) do -- get data if country should be printed in this wiki
        if value == wiki then display_age=false end
    end

--    getSomeNames(details, entityID, 'P1477', 1, display_language) --birthname
    --less prio than P1477
    getSomeNames(details, entityID, 'P1559', 1, display_language) --birthname, bis
    getSomeNames(details, entityID, 'P1449', 2, display_language) --nick name
    getSomeNames(details, entityID, 'P1448', 3, display_language) --official name
    getSomeNames(details, entityID, 'P1813', 4, display_language) --short name

    local birth, death, initialYear, finalYear=getBirthDeathDate(entityID, display_age)
    
    if not details[5].content then
        details[5].content=birth
    end
    if not details[6].content then
        details[6].content= death
    end   

    local display_flag=false
    for _, value in pairs(display_flag_in_riderinfobox) do -- get data if country should be printed in this wiki
        if value == wiki then display_flag=true end
    end

    listWPlinkChrono(details, 7, entityID, {'P1532','P27'}, 'country', initialYear, display_flag)

    if not details[8].content then
        local plural
        details[8].content, plural=presentTeam(entityID)
        if plural then
            details[8].name = details[8].name_plural
        end
    end

    --speciality
    listWPlink(details, 9, entityID, 'P413',false)

    --lateralisation, for cycling not very interesting
    --listWPlink(details, 10, entityID, 'P552',false)

    --blood group, idem
    --listWPlink(details, 11, entityID, 'P1853',false)

    --height
    if not details[12].content then
        details[12].content=getHeight(entityID)
    end

    local display_weight=true
    for _, value in pairs(display_noweight_in_riderinfobox) do -- get data if country should be printed in this wiki
        if value == wiki then display_weight=false end
    end
    --weight
    if not details[13].content and  display_weight then
        details[13].content=getWeight(entityID)
    end   

    --award, should be table
    --awards look weird
    --if not details[14].content then
    --    listWPlink(details, 14, entityID, 'P166',false)
    --end

    local amateurTeam, nonUCITeam, proTeam, UCITeam --local data
    local amateurWD=true
    local proWD=true
    local managedWD=true
    local teamAmateur,teamPro, teamMountainBike, teamCycloCross, teamTrack=listOfTeamTable(entityID, initialYear, finalYear)
    local nationalTeam, managedTeam=listOfManagedTeamTable(entityID, initialYear, finalYear)
    local managedTeam_names, managedTeam_periods, amateurTeam_names, amateurTeam_periods
    local nonUCITeam_names, nonUCITeam_periods, proTeam_names, proTeam_periods
    local nationalTeam_names, nationalTeam_periods
    local UCITeam_names, UCITeam_periods

    if teams[1].content then
        managedTeam_names=teams[1].content
        managedTeam_periods=teams[2].content
        managedWD=false
    end

    if teams[3].content then
        amateurWD=false
        amateurTeam_names=teams[3].content
        amateurTeam_periods=teams[4].content
    end
           
    if teams[5].content then
        amateurWD=false
        nonUCITeam_names=teams[5].content
        nonUCITeam_periods=teams[6].content
    end

    if teams[7].content then
        proWD=false
        proTeam_names=teams[7].content
        proTeam_periods=teams[8].content
    end
    
    if teams[9].content then
        proWD=false
        UCITeam_names=teams[9].content
        UCITeam_periods=teams[10].content
    end   
    
    if teams[11].content then
        nationalTeam_names=teams[11].content
        nationalTeam_periods=teams[12].content
        managedWD=false
    end

    --plate and grab
    tab = infoInitTab("300px", name, icon, 2)
    if subtitle[1].content then
        tCell=tab:tag('tr'):tag('td'):attr('colspan','2')
        :cssText('solid white; text-align:center')
        :wikitext(subtitle[1].content)
    end

    infoFillOthersDetails(tab, others, details,translate("riderinfobox",55,womenrace_bool),"260px")
    if amateurWD then
        teamTable(tab, teamAmateur, translate("riderinfobox",26,womenrace_bool), translate("riderinfobox",27,womenrace_bool))
    else
         localTeamTable(tab,amateurTeam_names, amateurTeam_periods, translate("riderinfobox",26,womenrace_bool), translate("riderinfobox",27,womenrace_bool))
        localTeamTable(tab,nonUCITeam_names, nonUCITeam_periods, translate("riderinfobox",29,womenrace_bool), translate("riderinfobox",30,womenrace_bool))
    end
    
    if proWD then
        teamTable(tab, teamPro, translate("riderinfobox",45,womenrace_bool),translate("riderinfobox",46,womenrace_bool))
        teamTable(tab, teamMountainBike, translate("riderinfobox",39,womenrace_bool), translate("riderinfobox",40,womenrace_bool))
        teamTable(tab, teamCycloCross, translate("riderinfobox",41,womenrace_bool), translate("riderinfobox",42,womenrace_bool))
        teamTable(tab, teamTrack, translate("riderinfobox",43,womenrace_bool), translate("riderinfobox",44,womenrace_bool))
    else
        localTeamTable(tab,proTeam_names, proTeam_periods,translate("riderinfobox",45,womenrace_bool), translate("riderinfobox",46,womenrace_bool))
        localTeamTable(tab,UCITeam_names, UCITeam_periods, translate("riderinfobox",35,womenrace_bool), translate("riderinfobox",36,womenrace_bool))
    end

    --managed teams
    if managedWD then
        teamTable(tab, nationalTeam, translate("riderinfobox",52,womenrace_bool), translate("riderinfobox",53,womenrace_bool))
        teamTable(tab, managedTeam, translate("riderinfobox",23,womenrace_bool), translate("riderinfobox",24,womenrace_bool))
    else
        localTeamTable(tab,managedTeam_names, managedTeam_periods,translate("riderinfobox",23,womenrace_bool), translate("riderinfobox",24,womenrace_bool))
    end
    
    if victories[1].content then
        tab:node(addATitle(translate("riderinfobox",38,womenrace_bool)))
        tab:tag('tr'):tag('td')
        :css('vertical-align','top'):attr('colspan','2')
        :wikitext(victories[1].content)
    end
    
    if medals[1].content then
        tab:node(addATitle(translate("riderinfobox",47,womenrace_bool)))
        tab:tag('tr'):tag('td')
        :css('vertical-align','top'):attr('colspan','2')
        :wikitext(medals[1].content)
    end
    wdDoc(tab, "d:Wikidata:WikiProject Cycling/Documentation/riderinfobox", translate("raceinfobox",26,womenrace_bool), entityID)
    return tab
end

--=== P) Team infobox
function p.teaminfobox(frame)
    localframe = frame
    local lang = contentLanguage
    -- If true, winners will have Wikidata logos with link to Wikidata
    local WDlink_on = (wiki == "mk" or wiki == "ja")
    
    local entityID = mw.text.trim(frame.args[1])
    if type(entityID) ~= 'string' then error('parameter must be a string') end
    if not entityID:match('Q%d+') then error('parameter must be a valid Wikidata item (ex: Q42)') end   
    local womenrace_bool=isWomenrace(entityID)
    
    local tRace = {race={
            raceId,
            raceDate,
            future,
            },
          lastEditionMonth,
          lastEditionYear,
          numberOfEditions,
          lastLink,
          nextLink,
          }
    
    local details = {
        { name = translate("teaminfobox",2,womenrace_bool)}, -- sport
        { name = translate("teaminfobox",3,womenrace_bool), name_plural = translate("teaminfobox",4,womenrace_bool)}, -- type    
        { name = translate("teaminfobox",5,womenrace_bool), name_plural = translate("teaminfobox",6,womenrace_bool)}, -- UCI-cod
        { name = translate("teaminfobox",7,womenrace_bool), name_plural = translate("teaminfobox",8,womenrace_bool)}, -- сountry
        { name = translate("teaminfobox",9,womenrace_bool)}, -- creation date
        { name = translate("teaminfobox",10,womenrace_bool)}, -- disparition date
        { name = translate("teaminfobox",11,womenrace_bool)}, -- number of season
        { name = translate("teaminfobox",13,womenrace_bool)}, -- official web site
        { name = translate("teaminfobox",24,womenrace_bool), name_plural = translate("teaminfobox",25,womenrace_bool) }, -- bike
        { name = translate("teaminfobox",26,womenrace_bool)}, -- budget
    }
    
    local others = {
        { name = translate("infobox",29,womenrace_bool)}, -- picture
        { name = translate("infobox",30,womenrace_bool)}, -- caption
        { name = translate("infobox",31,womenrace_bool)}, -- map
        { name = 'sectional'},             -- sectional
        { name = translate("infobox",30,womenrace_bool)}, -- caption map
        { name = translate("infobox",30,womenrace_bool)}, -- caption sectional
    }
    
    local managers ={
        { name = translate("teaminfobox",14,womenrace_bool), name_plural = translate("teaminfobox",15,womenrace_bool)}, -- manager                                                    --country
        { name = translate("teaminfobox",16,womenrace_bool), name_plural = translate("teaminfobox",17,womenrace_bool)}, -- sports director
    }

    local name =  getLabelFallback(entityID, {wikilang, 'en', 'fr', 'de'}) or ''
    infoGetOthers(others, entityID)   
       
    local localframe
    if string.match(frame:getParent():getTitle(), '%P+') == mw.site.namespaces.Template.name then
        localframe = frame:getParent()
    else
        localframe = frame
    end
    getLocalContent(details, localframe.args)
    getLocalContent(others, localframe.args)
    getLocalContent(managers, localframe.args)
    
    local listOfNames=getFormerNames(entityID, 'P1448')
    
    local sport_id=firstValue(entityID, 'P641', 'id')
    local icon = (sport_id == "Q3609") and -- P641 is 'sport', Q3609 is 'road bicycle racing'
        ' [[File:Cycling (road) pictogram.svg|35px]]' or ''

    --1st ist sport
    if not details[1].content and sport_id then
        details[1].content = WPlinkpure(sport_id)
    end
    
    local creation=firstValue(entityID, 'P571', 'time')
    local initialYear=string.sub(creation,2,5)
    
    -- type
    listWPlinkChrono(details, 2, entityID, {'P31'}, 'rider', initialYear)--it is not a rider, but we need link + official name
    
    --UCI code
    listWPlinkChrono(details, 3, entityID, {'P1998'}, 'UCIcode', initialYear, nil, true)

    -- сountry
    local display_flag=true                   
    listWPlinkChrono(details, 4, entityID, {'P1532','P17'}, 'country', initialYear, display_flag)

    --creation date
    if not details[5].content and creation then
        details[5].content = funcDate(creation, "Y" )
    end

    -- disparition date
    local disparition=firstValue(entityID, 'P576', 'time')
    if not details[6].content and disparition then
        details[6].content =  funcDate(disparition,"Y")
    end

    --populate tRace
    listOfWinners(entityID, tRace,true)
    
    -- number of season
    if not details[7].content and tRace.numberOfEditions and tRace.lastEditionYear then
        details[7].content = tostring(tRace.numberOfEditions).." (" .. translate("teaminfobox",12,womenrace_bool) .. " "..tostring(tRace.lastEditionYear)..")"
    end

    -- official site
    if not details[8].content then
        details[8].content = officialSite(entityID)
    end
    
    --9 is bike (no Wikidata input)
    --10 budget
    listWPlinkChrono(details, 10, entityID, {'P2769'}, 'money', initialYear)

    -- manager
    listWPlinkChrono(managers, 1, entityID, {'P505'}, 'rider', initialYear)

    -- sports director
    listWPlinkChrono(managers, 2, entityID, {'P286'}, 'rider', initialYear)

    --Build the table
    tab = infoInitTab("300px", name, icon, 2)
    --former names
    wiki_listOfNamesAtBottom={'ru'}
    
    local listOfNamesAtBottom = false
    for _, value in pairs(wiki_listOfNamesAtBottom) do --
        if value == wiki then listOfNamesAtBottom = true end
    end
    --picture at the top
    infoFillOthersDetails(tab, others, details, translate("teaminfobox",1,womenrace_bool),"260px")

    if managers[1].content or managers[2].content then
        tab:node(addATitle(translate("teaminfobox",18,womenrace_bool)))
        for _, row in ipairs(managers) do
            tab:node(addARow(row.name, row.content)) --node check itself if nil
        end
    end
    
    if listOfNames and #listOfNames>0 then --Always display a list of names
        tab:node(addATitle(translate("teaminfobox",19,womenrace_bool)))
        for _, v in pairs(listOfNames) do
            tab:node(addARow(v[2],v[3])) --period, name
        end
    end

-- an empty line with a title under the form in the form of an image or third-party template
    if frame.args[2] then -- if the jersey is not specified, then the JERSEY header is not displayed
        tab:node(addATitle(translate("teaminfobox",20,womenrace_bool)))
        local outTable = mw.html.create('tr')
        local tCell=outTable:tag('td'):attr('colspan','3'):css('text-align','center')           
        tCell:wikitext(frame.args[2]) -- adding a form via "argument 2" by an image or an extraneous template
        tab:node(outTable)
    end       

-- adding a link to articles about the last and current seasons (the same as for the race)    
    if tRace.nextLink or tRace.lastLink then
        tab:node(addATitle(translate("teaminfobox",21,womenrace_bool)))
        local outTable

        if tRace.lastLink then
            outTable = mw.html.create('tr')
            local tCell=outTable:tag('td'):attr('colspan','2'):css('text-align','center')
            local lastText="[[File:Crystal Clear app kworldclock.png|left|37px]]"..
            translate("teaminfobox",22,womenrace_bool)..
            ":<br>'''"..
            tRace.lastLink.."'''"
            tCell:wikitext(lastText)
            tab:node(outTable)
        end   
       
        if tRace.nextLink then
            outTable = mw.html.create('tr')
            local tCell=outTable:tag('td'):attr('colspan','2'):css('text-align','center')
            local nextText = "[[File:Crystal Clear app kworldclock.png|left|37px]]"..
            translate("teaminfobox",23,womenrace_bool)..
            ":<br>'''"..
            tRace.nextLink.."'''"
            tCell:cssText("text-align:center"):wikitext(nextText)
            tab:node(outTable)
        end
    end   

    wdDoc(tab, "d:Wikidata:WikiProject Cycling/Documentation/raceinfobox", translate("raceinfobox",26,womenrace_bool), entityID)
    return tab
end

--=== Z) Miscellaneous / Other / Tests
--[[ Give access to a local variable. Used by other modules. ]]
function p.getLocal(name)
    if name == 'getTeamLinkCat' then return getTeamLinkCat end
    if name == 'getStatementForTime' then return getStatementForTime end
end

function p.testlocal(frame) --function to test local functions
    local function_name=frame.args[1]
    local argu=frame.args
    local temp, temp2

    if function_name=='firstValue' then
        return firstValue(argu[2],argu[3],argu[4])
    elseif function_name=='getOfficialName' then
        temp, temp2 =getOfficialName(argu[2],argu[3],argu[4])
        return temp
    elseif function_name=='getRiderLink' then
        if argu[3]=="nil" then arg3=nil else arg3=argu[3] end
        temp=getRiderLink(argu[2],arg3) --only first arg returned
        return temp
    elseif function_name=='funcDate' then
        return funcDate(argu[2],argu[3])
    elseif function_name=='funcDateFigure' then
        return funcDateFigure(argu[2],argu[3])       
    elseif function_name=='getStartEndTime1' then   
        temp, temp2=getStartEndTime(argu[2],argu[3],argu[4])
        return temp
    elseif function_name=='getStartEndTime2' then   
        temp, temp2=getStartEndTime(argu[2],argu[3],argu[4])
        return temp2
    elseif function_name=='getPeriodSub' then
        temp, temp2=getPeriodSub(argu[2],argu[3],toboolean(argu[4]))
        return temp
    elseif function_name=='getTeam' then
        temp=getTeam(argu[2],argu[3],argu[4])
        if temp then return temp else return 'nil' end
    elseif function_name=='getStatementForTime' then
        temp=getStatementForTime(argu[2],argu[3],argu[4])   
        if temp then
            return temp.mainsnak.datavalue.value.id
        else
            return 'nil'
        end
    elseif function_name=='getTeamLinkCat' then
        temp=getTeamLinkCat(argu[2],argu[3],toboolean(argu[4]),toboolean(argu[5]))   
        if temp then return temp else return 'nil' end
    elseif function_name=='getPlaceLink' then
        if argu[3]=="nil" then arg3=nil else arg3=argu[3] end
        return getPlaceLink(argu[2],arg3)
    elseif function_name=='getPlaceLink2' then        
        return getPlaceLink(argu[2],argu[3],nil,true)
    elseif function_name=='seasonToTeamID' then
        if argu[2]=="nil" then arg2=nil else arg2=argu[2] end
        return tostring(seasonToTeamID(arg2))
    elseif function_name=='translate' then
        return translate(argu[2],tonumber(argu[3]),toboolean(argu[4]))
    elseif function_name=="classLinkFn" then
        return classLinkFn(argu[2])
    elseif function_name=='raceLink' then
        return tostring(raceLink(argu[2]))
    elseif function_name=='getMainRaceLink' then
        if argu[5]=="nil" then arg5=nil else arg5=argu[5] end
        if argu[3]=='stage' then arg3='stage' else arg3=tonumber(argu[3]) end
        return tostring(getMainRaceLink(argu[2],arg3,argu[4], arg5,argu[6]))
    elseif  function_name=='getYear' then
        return getYear(argu[2])
    elseif function_name=='getCountryName' then
        return tostring(getCountryName(argu[2]))
    elseif function_name=='getTeamCodeCat' then
        return tostring(getTeamCodeCat(argu[2],argu[3]))
    elseif function_name=='getTeamCode' then
        return tostring(getTeamCode(argu[2],argu[3],argu[4]))
    elseif function_name=='getCountryBool' then
        return tostring(getCountryBool({argu[2],argu[3]}))
    elseif function_name=='WPlinkpure' then
        return WPlinkpure(argu[2])
    elseif function_name=='uciCodeCountry' then
        return uciCodeCountry(argu[2])
    elseif function_name=='isHuman' then
        return tostring(isHuman(argu[2]))
    elseif function_name=='isCountry' then
        return tostring(isCountry(argu[2]))
    elseif function_name=='isWomenrace' then
        return tostring(isWomenrace(argu[2]))
    elseif function_name=='commaStage' then
        temp =commaStage(argu[2],argu[3])
        return temp["prefix"]
    elseif function_name=='number' then
        return number(argu[2],tonumber(argu[3]), argu[4])
    elseif function_name=='classToCircuit' then
        return classToCircuit(argu[2], argu[3], toboolean(argu[5]), nil)
    elseif function_name=='getGenderCode' then
        return tostring(getGenderCode(argu[2], argu[3]))
    elseif function_name=='calculateTime' then
        return calculateTime(argu[2])
    elseif function_name=='getClass1' then
        temp, temp2 = getClass(argu[2])
        return temp
    elseif function_name=='getClass2' then
        temp, temp2 = getClass(argu[2])
        return temp2    
    elseif function_name=='infoGetPlace' then
        local details = {{ name = "test", name_plural="tests"}} -- course / not used
        infoGetPlace(details,1, argu[2], argu[3], argu[4])
        return details[1].content
    elseif function_name=='getFormerNames1' then
        temp=getFormerNames(argu[2],'P1448')
        if temp[1] then
            return temp[1][2]  --period
        else
            return ""
        end
    elseif function_name=='getFormerNames2' then
        temp=getFormerNames(argu[2],'P1448')
        if temp[1] then
            return temp[1][3]  --name
        else
            return ""
        end
    elseif function_name=='getType' then
        return getType(argu[2])
    elseif function_name=='compareDate' then
        return tostring(compareDate(argu[2]))
    elseif function_name=='officialSite' then
        return officialSite(argu[2])
    elseif function_name=='trans' then
        return tostring(trans(argu[2], argu[3], argu[4]))
    elseif function_name=='parseDate1' then   
        temp1, temp2, temp3, temp4, temp5= parseDate(argu[2], argu[3], argu[4], argu[5], "", "error text")
        return temp1
    elseif function_name=='parseDate2' then   
        temp1, temp2, temp3, temp4, temp5= parseDate(argu[2], argu[3], argu[4], argu[5], "", "error text")
        return temp2
    elseif function_name=='parseDate5' then           
        temp1, temp2, temp3, temp4, temp5= parseDate(argu[2], argu[3], argu[4], argu[5], "", "error text")
        return temp5
    elseif function_name=='findLastName' then
        return findLastName(argu[2],wiki)
    elseif function_name=='findSortKey' then
        if wiki=="ru" or wiki=="mk" then
            return findSortKey(argu[2],false, true)
        else
            return findSortKey(argu[2],true, false)
        end
    elseif function_name=='calculateAge' then
        temp1, _, _ =calculateAge(argu[2])
        return temp1
    elseif function_name=='getBirthDeathDate1' then
        temp1, temp2 =    getBirthDeathDate(argu[2])
        return temp1
    elseif function_name=='getBirthDeathDate2' then
        temp1, temp2 =    getBirthDeathDate(argu[2])
        return temp2   
    elseif function_name=='getLocalContent' then
        local details = {
                    { name = argu[2], name_plural= argu[3]}
                }
        local arguments = {}
        arguments[argu[4]]="test"

        getLocalContent(details, arguments)
        return details[1].content
    elseif function_name=='plural1' then   
        _, temp1, temp2=plural(tonumber(argu[2]))
        return temp1
    elseif function_name=='plural2' then   
        _, temp1, temp2=plural(tonumber(argu[2]))
        return temp2
    elseif function_name=='getNationality' then
        return getNationality(argu[2], argu[3])
    elseif function_name=='getClassCalendar_sub' then
        return getClassCalendar_sub(argu[2])
    end
    
end

function p.test_import(frame)
    local function_name=frame.args[1]
    local argu=frame.args

    if function_name=='class_dic' then
        return tostring(class_dic[argu[2]])
    elseif function_name=="class_sort" then
        return tostring(class_sort[argu[2]])
    elseif function_name=='bg_color_table' then   
        local temp = bg_color_table[argu[2]]
        temp=string.gsub(temp,'#',"")
        return temp
    end
end

return p