gebo007 / Intersteno_Internet_Results

// ==UserScript==
// @name        Intersteno_Internet_Results
// @namespace   https://openuserjs.org/scripts/gebo007/Intersteno_Internet_Results
// @updateURL   https://openuserjs.org/install/gebo007/Intersteno_Internet_Results.meta.js
// @version     0.6.9
// @date        2022-04-10
// @description View Intersteno Internet Keyboarding Competition classification lists on intersteno.it with added functionality, some fixes and better CSS
// @author      gebo007
// @copyright   2020+, Geert Bonte (https://openuserjs.org/users/gebo007)
// @license	    MIT
// @include     https://internet.intersteno.it/*
// @include     https://org.intersteno.it/*
// @include     https://www.intersteno.it/*
// @grant       none
// ==/UserScript==

// define a base64 encoded image to be used as an icon for escaping from the iframe to a full browser window
var imgFullScreen =
    'R0lGODlhHAAWAIABAICAgP///yH5BAEKAAEALAAAAAAcABYAAAJIhI+py+0Xopy0SmSzxpquyHWX' +
    'AkJixYVnaaonZLDANo91HLj2Xso9y0O1brifJTUzrXJD5RJZXDJxn2dTR4tKPc7t5AEOixkFADs';
// define a base64 encoded image to be used as an icon for a link to a different web page (inspired by Wikipedia)
var imgExternalLink =
    'R0lGODlhDAAMAOMIAP///zNmzJmZzMzM/2aZ/5nM/2aZzMzMzODgIODgIODgIODgIODgIODgIODg' +
    'IODgICH5BAEKAAcALAAAAAAMAAwAAAQf8MhJqwQ4V0yB5deVeRNplsdJploacmvsdts2glZ+RAA7';

// our own stylesheet, correcting/improving/complementing the original stylesheets
// at  https://org.intersteno.it/style.css.php (international contests)
// and https://www.intersteno.it/style.css.php (national contests)
var styleSheet =
    '* { font-family: Tahoma, sans-serif; font-size: 9pt; text-align: left; }' +
    ' :root { --langDisplay: table-row; }' +
    ' div, table { margin-left: 5px; margin-right: 5px; }' +
    ' a, a:active, a:visited, a:hover { color: #0099ff; text-decoration: none; }' +
    ' tr b font, div b   { font-size: 13pt; }' +
    ' div td b, div th b { font-size: 9pt; }' +
    ' form { margin-bottom: 12pt; margin-top: 2pt; }' +
    ' input { margin-left: 3pt; margin-top: 3pt; }' +
    ' table { color: #ffffff; margin: 0; margin-top: .5em; width: 100%; }' +
    ' tr         { background-color: #0099ff; font-size: 9pt; }' +
    ' tr.center  { background-color: #ffffff; color: #000000; }' +
    ' tr.evenline{ background-color: #d9edff; color: #000000; }' +
    ' tr.oddline { background-color: #f2faff; color: #000000; }' +
    ' tr.even1   { background-color: #ffffff; }' +
    ' tr.odd1    { background-color: #ffffff; }' +
    ' tr.lang    { display: var(--langDisplay); }' +
    ' div.schools table { color: #ffffff; margin: 0; margin-top: .5em; max-width: 900px; }' +
    ' div.schools tr.center  { background-color: #ffffff; color: #000000; }' +
    ' div.schools tr         { background-color: #9900ff; font-size: 9pt; }' +
    ' div.schools tr.evenline{ background-color: #edd9ff; color: #000000; }' +
    ' div.schools tr.oddline { background-color: #faf2ff; color: #000000; }' +
    ' td, th { padding: 2.5pt; vertical-align: top; }' +
    ' th.sticky { background-color: #0099ff; position: sticky; top: 0; }' +
    ' div.schools th.sticky { background-color: #9900ff; font-size: 8pt; text-align: left; }' +
    ' tr.evenline+tr.center td, tr.oddline+tr.center td, tr.hidden+tr.center td { padding-top: 10pt; }' +
    ' tr.evenline td, tr.oddline td { font-size: 9pt; }' +
    ' font { color: #ffffff; font-size: 8pt; }' +
    ' tr.oddline font, tr.evenline font { color: #000000; font-size: 9pt; }' +
    ' th.link { cursor: pointer; }' +
    ' span.small { font-size: 8.5pt; line-height: 100%; padding: 0; } ' +
    ' p.notice { border-top: .5pt solid #d9edff; font-size: 8pt; font-style: italic; margin-left: 5px; margin-top: 3em; padding-top: 4pt; }' +
    ' p.notice, p.notice a { color: #d9edff; text-decoration: none; }' +
    ' div.schools p.notice { border-color #edd9ff; }' +
    ' div.schools p.notice, div.schools p.notice a { color: #edd9ff; text-decoration: none; }' +
    ' p.switchDomain { margin-left: 5px; }' +
    ' p.switchDomain { color: #0099ff; font-style: italic; }' +
    ' span.changeContest { font-size: 13pt; }' +
    ' span.changeContest a { color: #000000; font-size: 13pt; text-decoration: none; }' +
    ' span.changeContest a:hover { color: #a0a0a0; }' +
    ' .center { text-align: left; }' + // I know, the class name suggests this should be centered, but it's better this is left aligned
    ' .c1, .c5, .c6, .c7, .c8, .c9 { padding-right: .5em; text-align: right; }' +
    ' .c10 { padding-left: .5em; white-space: nowrap; }' +
    ' div.schools td.c2 { font-style: italic; }' +
    ' div.schools td.c4 { padding-right: 3em; text-align: right; }' +
    ' div.schools td { text-align: left; }' +
    ' div.schools input, .hidden { display: none; }' +
    ' div.schools span.highlight { background-color: #edd9ff; color: #000000; font-weight: normal; padding: 4pt; } ' +
    ' #cnySelect, #lngSelect { display: none; }' +
    ' #cnyLabel, #lngLabel { display: none; padding-left: 2em; padding-right: .25em; }' +
    ' #escFrame { cursor: pointer; display: none; position: fixed; right: 0; top: 0; background-image: url("data:image/gif;base64,' + imgFullScreen + '"); }' +
    ' .extLink { background-image: url("data:image/gif;base64,' + imgExternalLink + '"); background-repeat: no-repeat; background-position: center; margin-left: 4px; padding-right: 12px; height: 12px; }';

// javascript function for escaping from the iframe
var fnEscape =
    'function escFrame() {' +
        ' var e = document.getElementById("escFrame");' +
        ' top.location = self.location.href;' +
        ' e.style.display = "none";' +
    '} ';

// javascript function for contest selection (on schools page only)
var fnContestSelect =
    'function contestSelect(sel) {' +
        ' var newURL = window.location.href;' +
        ' var newC = "contest_id=" + sel.value;' +
        ' if (newURL.search(/contest_id=[^&]+/) > -1) {' + // if the url already contains a contest_id parameter, replace it
            ' newURL = newURL.replace(/contest_id=[^&]+/, newC);' +
        ' }' +
        ' else {' + // if the url doesn't contain a contest_id parameter, add it
            ' if (newURL.search(/\\?/) > -1) {' +
                ' newURL += "&";' +
            ' }' +
            ' else {' +
                ' newURL += "?";' +
            ' }' +
            ' newURL += newC;' +
        ' }' +
        ' window.location.href = newURL;' +
    '} ';

// javascript function for language selection
var fnLangSelect =
    'function langSelect(sel) {' +
        ' var newURL = window.location.href;' +
        ' var newL = "language=" + sel.value;' +
        ' if (newURL.search(/language=[^&]+/) > -1) {' + // if the url already contains a language parameter, replace it
            ' newURL = newURL.replace(/language=[^&]+/, newL);' +
        ' }' +
        ' else {' + // if the url doesn't contain a language parameter, add it
            ' if (newURL.search(/\\?/) > -1) {' +
                ' newURL += "&";' +
            ' }' +
            ' else {' +
                ' newURL += "?";' +
            ' }' +
            ' newURL += newL;' +
        ' }' +
        ' window.location.href = newURL;' +
    '} ';

// javascript functions for applying country and language filters
var fnFilter =
    'function filterNation(sel) {' +
        ' var newURL = window.location.href;' +
        ' var lng = "";' +
        ' var pos = newURL.search(/&?custom_filter=[a-z]{2}/);' +
        ' if (pos > -1) {' +
            ' lng = newURL.substr(pos + 15, 2);' +
        ' }' +
        ' pos = newURL.search(/&?custom_filter=XX-[a-z]{2}/);' +
        ' if (pos > -1) {' +
            ' lng = newURL.substr(pos + 18, 2);' +
        ' }' +
        ' newURL = newURL.replace(/&?custom_filter=[^&]*/, "");' + // remove custom_filter from URL
        ' var cny = sel.value;' +
        ' var newFn = "filter_nation=";' +
        ' var newCf = "";' +
        ' if (cny == "XX") {' +
            ' newCf = "&custom_filter=XX"; ' +
            ' if (lng !== "") { newCf += "-" + lng; }' +
        ' }' +
        ' else {' +
            ' newFn += cny;' +
            ' if (lng !== "") { newCf = "&custom_filter=" + lng; }' +
        ' }' +
        ' var fn = newURL.search(/filter_nation=[^&]{0,2}/);' +
        ' if (fn > -1) {' + // if the url already contains a filter_nation parameter, replace it
            ' newURL = newURL.replace(/filter_nation=[^&]{0,2}/, newFn);' +
        ' }' +
        ' else {' + // if the url doesn't contain a filter_nation parameter, add it
            ' newURL += "&" + newFn;' +
        ' }' +
        ' newURL += newCf;' + // add the custom_filter parameter (if non-empty)

        ' window.location.href = newURL;' +
    '} ' +
    'function filterLanguage(sel) {' +
        ' var newURL = window.location.href;' +
        ' var cny = "";' +
        ' var pos = newURL.search(/&?custom_filter=XX/);' +
        ' if (pos > -1) {' +
            ' cny = "XX-";' +
        ' }' +
        ' newURL = newURL.replace(/&?custom_filter=[^&]*/, "");' + // remove custom_filter from URL
        ' var lng = sel.value;' +
        ' var newCf = "&custom_filter=" + cny + lng;' +
        ' if (lng !== "" || cny != "") { newURL += newCf; }' +
        ' window.location.href = newURL;' +
    '} ';

// javascript function for showing/hiding totals on multilanguage result pages
var fnToggleTotals =
    'function toggleTotals() {' +
        " var langDisplay = document.documentElement.style.getPropertyValue('--langDisplay');" +
        " if (langDisplay != 'none') {" +
            " langDisplay = 'none';" +
        " }" +
        " else {" +
            " langDisplay = 'table-row';" +
        " }" +
        " document.documentElement.style.setProperty('--langDisplay', langDisplay);" +
    '} ';

// set proportional column widths, resulting in equal widths across all tables
var colGroupResults = // results tables
    '<col width="2%"  />' + // column #1: Place
    '<col width="16%" />' + // column #2: Surname and name
    '<col width="16%" />' + // column #3: Town/nation
    '<col width="26%" />' + // column #4: School/association
    '<col width="8%"  />' + // column #5: Strokes per minute
    '<col width="7%"  />' + // column #6: Total strokes
    '<col width="4%"  />' + // column #7: Errors
    '<col width="4%"  />' + // column #8: Error %
    '<col width="4%"  />' + // column #9: Points
    '<col width="5%"  />' + // column #10: Language
    '<col width="4%"  />' + // column #11: Software
    '<col width="4%"  />' ; // column #12: Technology
var colGroupSchools = // schools tables
    '<col width="55%" />' + // column #1: School/Association
    '<col width="20%" />' + // column #2: City
    '<col width="15%" />' + // column #3: Country
    '<col width="10%" />' ; // column #4: Students (column is added by this script)

// define all contests to be included in the contest selection list, with canonized contest names, in alphabetical order and in reversed chronological order
// the first part of the list are national contests (with country code), the second part are the Intersteno contests (without country code)
var allContests = ['169:23:ABW Klaviervaardigheid 2016 (BE)', '147:23:ABW Klaviervaardigheid 2015 (BE)', '133:23:ABW Klaviervaardigheid 2014 (BE)', '118:23:ABW Klaviervaardigheid 2013 (BE)', '98:23:ABW Klaviervaardigheid 2012 (BE)', '83:23:ABW Klaviervaardigheid 2011 (BE)', '74:23:ABW Klaviervaardigheid 2010 (BE)', '67:23:ABW Klaviervaardigheid 2009 (BE)', '52:23:ABW Klaviervaardigheid 2008 (BE)', '40:23:ABW Klaviervaardigheid 2007 (BE)', '15:23:ABW Klaviervaardigheid 2006 (BE)', '11:3:ABW Klaviervaardigheid 2005 (BE)', '231:23:ABW Odisee 2021-2022 (BE)', '223:23:ABW Odisee 2020-2021 (BE)', '217:23:ABW Odisee 2019-2020 (BE)', '208:23:ABW Odisee 2018-2019 (BE)', '184:23:ABW Odisee 2016-2017 (BE)', '163:23:ABW Odisee 2015-2016 (BE)', '144:23:ABW Odisee 2014-2015 (BE)', '230:23:Adalet MYO 2021 (TR)', '213:123:Adalet MYO 2019 (TR)', '199:123:Adalet MYO 2018 (TR)', '189:123:Adalet MYO 2017 (TR)', '173:123:Adalet MYO 2016 (TR)', '207:123:Allenamenti 2019 Cagliari (IT)', '177:123:Allenamenti 2017 Berlino (IT)', '138:123:Allenamenti 2015 Budapest (IT)', '104:123:Allenamenti 2013 Ghent (IT)', '77:123:Allenamenti 2011 Parigi (IT)', '224:123:APSB 2021 (BE)', '214:123:APSB 2020 (BE)', '202:123:APSB 2019 (BE)', '191:123:APSB 2018 (BE)', '185:123:APSB 2017 (BE)', '165:123:APSB 2016 (BE)', '137:123:APSB 2015 (BE)', '127:123:APSB 2014 (BE)', '110:123:APSB 2013 (BE)', '93:123:APSB 2012 (BE)', '79:123:APSB 2011 (BE)', '70:123:APSB 2010 (BE)', '61:123:APSB 2009 (BE)', '45:123:APSB 2008 (BE)', '37:123:APSB 2007 (BE)', '12:123:APSB 2006 (BE)', '8:13:APSB 2005 (BE)', '196:23:Cagliari 2018 (IT)', '176:23:Cagliari 2016 (IT)', '183:23:Cagliari Finale (IT)', '200:23:Cagliari Maggio 2018 (IT)', '201:23:Cagliari Marrubio 2018 (IT)', '227:23:Certificazione Accademia 2021 (IT)', '221:23:Certificazione Accademia 2020 (IT)', '211:23:Certificazione Accademia 2019 (IT)', '195:23:Certificazione Accademia 2018 (IT)', '167:23:Certificazione Accademia 2016 (IT)', '129:23:Certificazione Accademia 2014 (IT)', '109:23:Certificazione Accademia 2013 (IT)', '91:23:Certificazione Accademia 2012 (IT)', '60:123:Certificazione Accademia 2009 (IT)', '76:23:Challenge Francophone 2010 (BE)', '216:23:Državno Natjecanje 2019 (HR)', '192:23:Državno Natjecanje 2017 (HR)', '178:23:Državno Natjecanje 2016 (HR)', '160:23:Državno Natjecanje 2015 (HR)', '119:23:Ecole Centre Sprimont 2014 (BE)', '234:123:Intersteno 2022', '229:123:Intersteno 2021', '215:123:Intersteno 2020', '204:123:Intersteno 2019', '198:123:Intersteno 2018', '188:123:Intersteno 2017', '168:123:Intersteno 2016', '152:123:Intersteno 2015', '130:123:Intersteno 2014', '111:123:Intersteno 2013', '96:123:Intersteno 2012', '82:123:Intersteno 2011', '72:123:Intersteno 2010', '64:123:Intersteno 2009', '54:123:Intersteno 2008', '41:123:Intersteno 2007', '14:123:Intersteno 2006', '10:23:Intersteno 2005', '2:3:Intersteno 2004', '1:3:Intersteno 2003', '107:23:KH Leuven 2012 (BE)', '171:123:LUX Open Tournament 2016 (LU)', '232:23:LUX Speed-Typing 2022 (LU)', '225:123:LUX-Speed-Typing 2021 (LU)', '219:123:LUX-Speed-Typing 2020 (LU)', '210:123:LUX-Speed-Typing 2019 (LU)', '194:123:LUX-Speed-Typing 2018 (LU)', '182:123:LUX-Speed-Typing 2017 (LU)', '161:123:LUX-Speed-Typing 2016 (LU)', '141:123:LUX-Speed-Typing 2015 (LU)', '203:23:PRO_Belgium 2019 (BE)', '186:23:PRO_Belgium 2017 (BE)', '145:123:PRO_Belgium 2015 (BE)', '134:123:PRO_Belgium 2014 (BE)', '113:123:PRO_Belgium 2013 (BE)', '95:123:PRO_Belgium 2012 (BE)', '233:23:Swiss 2022 (CH)', '228:23:Swiss 2021 (CH)', '212:123:Swiss 2019 (CH)', '197:23:Swiss 2018 (CH)', '187:23:Swiss 2017 (CH)', '166:123:Swiss 2016 (CH)', '140:123:Swiss 2015 (CH)', '128:123:Swiss 2014 (CH)', '114:23:Swiss 2013 (CH)', '97:23:Swiss 2012 (CH)', '81:23:Swiss 2011 (CH)', '71:23:Swiss 2010 (CH)', '65:23:Swiss 2009 (CH)', '56:23:Swiss 2008 (CH)', '42:23:Swiss 2007 (CH)', '16:23:Swiss 2006 (CH)', '9:3:Swiss 2005 (CH)', '226:123:Türkiye İhsan Yener Klâvye Şampiyonası 2021 (TR)', '220:123:Türkiye İhsan Yener Klâvye Şampiyonası 2020 (TR)', '209:123:Türkiye İhsan Yener Klâvye Şampiyonası 2019 (TR)', '193:123:Türkiye İhsan Yener Klâvye Şampiyonası 2018 (TR)', '180:123:Türkiye İhsan Yener Klâvye Şampiyonası 2017 (TR)', '162:123:Türkiye İnternet Şampiyonası 2016 (TR)', '139:123:Türkiye İnternet Şampiyonası 2015 (TR)', '125:123:Türkiye İnternet Şampiyonası 2014 (TR)', '108:123:Türkiye İnternet Şampiyonası 2013 (TR)', '89:123:Türkiye İnternet Şampiyonası 2012 (TR)', '78:123:Türkiye İnternet Şampiyonası 2011 (TR)', '69:123:Türkiye İnternet Şampiyonası 2010 (TR)', '63:123:Türkiye İnternet Şampiyonası 2009 (TR)', '46:123:Türkiye İnternet Şampiyonası 2008 (TR)', '38:123:Türkiye İnternet Şampiyonası 2007 (TR)', '13:3:Türkiye İnternet Şampiyonası 2006 (TR)'
];
var maxId = 0; // maximum contest id in this list (will be set later)

// define languages (for international contests)
var lng; // current country code (empty = all languages)
var lngs = ['All languages:', 'Český:cs', 'Deutsch:de', 'English:en', 'Español:es', 'Français:fr', 'Hrvatski:hr', 'Italiano:it', 'Magyar:hu', 'Nederlands:nl', 'Polski:pl', 'Purtuguês:pt', 'Română:ro', 'Slovenčina:sk', 'Suomi:fi', 'Türkçe:tr', 'Русский:ru', 'српски:sr', '日本語:ja']; // languages (language name:language code)
var lngCodes = []; // will be populated by language codes
var lngNames = []; // will be populated by language names

// define countries (for international contests)
var cny; // current country code (empty = all countries)
var cnys = ['All countries:', 'Algeria:DZ', 'Argentina:AR', 'Australia:AU', 'Austria:AT', 'Belgium:BE', 'Belgium + The Netherlands:XX', 'Brazil:BR', 'Cameroon:CM', 'Canada:CA', 'China:CN', 'Croatia:HR', 'Cyprus:CY', 'Czech Republic:CZ', 'Egypt:EG', 'France:FR', 'Germany:DE', 'Greece:GR', 'Hungary:HU', 'India:IN', 'Indonesia:ID', 'Italy:IT', 'Japan:JP', 'Luxembourg:LU', 'Malaysia:MY', 'Morocco:MA', 'The Netherlands:NL', 'Nigeria:NG', 'Norway:NO', 'Poland:PL', 'Romania:RO', 'Russia:RU', 'Serbia:RS', 'Serbia:YU', 'Senegal:SN', 'Slovakia:SK', 'Spain:ES', 'Switzerland:CH', 'Taiwan:TW', 'Turkey:TR', 'United Kingdom:UK', 'United States:US', 'Vietnam:VN', 'Yemen:YE']; // countries (country name:country code)
var cnysEN = cnys;
var cnysDE = ['Alle Länder:', 'Ägypten:EG', 'Algerien:DZ', 'Argentinien:AR', 'Australien:AU', 'Belgien:BE', 'Belgien + Niederlande:XX', 'Brasilien:BR', 'China:CN', 'Deutschland:DE', 'Frankreich:FR', 'Griechenland:GR', 'Indien:IN', 'Indonesien:ID', 'Italien:IT', 'Japan:JP', 'Jemen:YE', 'Kamerun:CM', 'Kanada:CA', 'Kroatien:HR', 'Luxemburg:LU', 'Malaysia:MY', 'Marokko:MA', 'Niederlande:NL', 'Nigeria:NG', 'Norwegen:NO', 'Österreich:AT', 'Polen:PL', 'Rumänien:RO', 'Russland:RU', 'Schweiz:CH', 'Senegal:SN', 'Serbien:RS', 'Serbien:YU', 'Slowakei:SK', 'Spanien:ES', 'Taiwan:TW', 'Tschechien:CZ', 'Türkei:TR', 'Ungarn:HU', 'Vereinigte Staaten:US', 'Vereinigtes Königreich:UK', 'Vietnam:VN', 'Zypern:CY']; // countries, German
var cnysES = ['Todos los países:', 'Alemania:DE', 'Argelia:DZ', 'Argentina:AR', 'Australia:AU', 'Austria:AT', 'Brasil:BR', 'Bélgica:BE', 'Bélgica + Países Bajos:XX', 'Camerún:CM', 'Canadá:CA', 'Chequia:CZ', 'China:CN', 'Chipre:CY', 'Croacia:HR', 'Egipto:EG', 'Eslovaquia:SK', 'España:ES', 'Estados Unidos:US', 'Francia:FR', 'Grecia:GR', 'Hungría:HU', 'India:IN', 'Indonesia:ID', 'Italia:IT', 'Japón:JP', 'Luxemburgo:LU', 'Malasia:MY', 'Marruecos:MA', 'Nigeria:NG', 'Noruega:NO', 'Países Bajos:NL', 'Polonia:PL', 'Reino Unido:UK', 'Rumania:RO', 'Rusia:RU', 'Senegal:SN', 'Serbia:RS', 'Serbia:YU', 'Suiza:CH', 'Taiwán:TW', 'Turquía:TR', 'Vietnam:VN', 'Yemen:YE']; // countries, Spanish
var cnysFR = ['Tous les pays:', 'Algérie:DZ', 'Allemagne:DE', 'Argentine:AR', 'Australie:AU', 'Autriche:AT', 'Belgique:BE', 'Belgique + Pays-Bas:XX', 'Brésil:BR', 'Cameroun:CM', 'Canada:CA', 'Chine:CN', 'Chypre:CY', 'Croatie:HR', 'Egypte:EG', 'Espagne:ES', 'Etats Unis:US', 'France:FR', 'Grèce:GR', 'Hongrie:HU', 'Inde:IN', 'Indonésie:ID', 'Italie:IT', 'Japon:JP', 'Luxembourg:LU', 'Malaisie:MY', 'Maroc:MA', 'Nigeria:NG', 'Norvège:NO', 'Pays-Bas:NL', 'Pologne:PL', 'Roumanie:RO', 'Royaume-Unie:UK', 'Russie:RU', 'Sénégal:SN', 'Serbes:RS', 'Serbes:YU', 'Slovaquie:SK', 'Suisse:CH', 'Taïwan:TW', 'Tchéquie:CZ', 'Turquie:TR', 'Vietnam:VN', 'Yémen:YE']; // countries, French
var cnysIT = ['Tutti i paesi:', 'Algeria:DZ', 'Argentina:AR', 'Australia:AU', 'Austria:AT', 'Belgio:BE', 'Belgio + Paesi Bassi:XX', 'Brasile:BR', 'Camerun:CM', 'Canada:CA', 'Cechia:CZ', 'Cina:CN', 'Cipro:CY', 'Croazia:HR', 'Egitto:EG', 'Francia:FR', 'Germania:DE', 'Giappone:JP', 'Grecia:GR', 'India:IN', 'Indonesia:ID', 'Italia:IT', 'Lussemburgo:LU', 'Malaysia:MY', 'Marocco:MA', 'Nigeria:NG', 'Norvegia:NO', 'Paesi Bassi:NL', 'Polonia:PL', 'Regno Unito:UK', 'Romania:RO', 'Russia:RU', 'Senegal:SN', 'Serbia:RS', 'Serbia:YU', 'Slovacchia:SK', 'Spagna:ES', 'Stati Uniti:US', 'Svizzera:CH', 'Taiwan:TW', 'Turchia:TR', 'Ungheria:HU', 'Vietnam:VN', 'Yemen:YE']; // countries, Italian
var cnysNL = ['Alle landen:', 'Algerije:DZ', 'Argentinië:AR', 'Australië:AU', 'België:BE', 'België + Nederland:XX', 'Brazilië:BR', 'Canada:CA', 'China:CN', 'Cyprus:CY', 'Duitsland:DE', 'Egypte:EG', 'Frankrijk:FR', 'Griekenland:GR', 'Hongarije:HU', 'India:IN', 'Indonesië:ID', 'Italië:IT', 'Japan:JP', 'Jemen:YE', 'Kameroen:CM', 'Kroatië:HR', 'Luxemburg:LU', 'Maleisië:MY', 'Marokko:MA', 'Nederland:NL', 'Nigeria:NG', 'Noorwegen:NO', 'Oostenrijk:AT', 'Polen:PL', 'Roemenië:RO', 'Rusland:RU', 'Senegal:SN', 'Servië:RS', 'Servië:YU', 'Slowakije:SK', 'Spanje:ES', 'Taiwan:TW', 'Tsjechië:CZ', 'Turkije:TR', 'Verenigd Koninkrijk:UK', 'Verenigde Staten:US', 'Vietnam:VN', 'Zwitzerland:CH']; // countries, Dutch
var cnyCodes = []; // will be populated by country codes
var cnyNames = []; // will be populated by country names
var cnyList = [ // array holding the proper country codes for each year (international contests only)
    'AT BE CZ DE IT SK', // 2003
    'AR AT BE DE IT NL TR', // 2004
    'AR AT BE CZ DE FR HU IT NL PL SK TR', // 2005
    'AT BE CH CZ DE FR IN IT NL PL SK TR', // 2006
    'AT BE CH CZ DE FR GR HR IN IT NL PL RU SK TR', // 2007
    'AT BE CA CH CM CZ DE FR HR HU IT PL RU SK TR US', // 2008
    'AT BE CA CM CZ DE FR HR HU IT PL RO RS RU SK TR US', // 2009
    'AT BE BR CA CH CZ DE FR HR HU IT PL RO RS RU SK TR US', // 2010
    'AT AU BE CA CH CZ DE FR HR HU IT PL RU SK TR US', // 2011
    'AT AU BE BR CA CH CZ DE FR HR HU IN IT NL PL RU SK TR US', // 2012
    'AT AU BE CA CH CZ DE ES FR HR HU ID IT LU NL PL RU SK TR US', // 2013
    'AT BE CA CH CZ DE ES FR HR HU ID IT JP LU NL PL RU SK TR US', // 2014
    'AT AU BE CA CH CZ DE FR HR HU IN IT JP LU NL NO PL RU SK TR US', // 2015
    'AT BE CA CH CZ DE ES FR HR HU IT JP LU NL PL RU SK TR US', // 2016
    'AT BE CA CH CZ DE ES FR HR HU IT JP LU NL PL RU SK TR US', // 2017
    'AT BE CA CH CZ DE ES FR HR HU IT JP LU NL PL RU SK TR US', // 2018
    'AR AT BE CH CZ DE FR HR HU IT JP LU MA NL PL RU SK TR TW', // 2019
    'AR AT AU BE CH CZ DE EG ES FR GB HR HU IT JP LU MA MY NG NL NO PL RO RU SK TR US VN', // 2020
    'AR AT AU BE CH CZ DE EG ES FR GB HR HU IT JP LU MA MY NG NL NO PL RO RS RU SK TR US VN', // 2021
    'AR AT AU BE CH CN CZ DE EG ES FR GB HR HU IT JP LU MA MY NG NL NO PL RO RS RU SK TR US VN', // 2022
    // 2023
];

// set new language selection drop-down list
// the following language values are clearly unsupported, so we don't include them in our list: portoghese, rumeno, giapponese_transliterated, finlandese, ceco, cinese
// language order is completely illogical, but we'll just go with the original order to avoid confusing legacy users
var langSelect =
'<select id="language_selector" onChange="langSelect(this)">' +
'	<option value="inglese">English</option>' +
'	<option value="italiano">Italiano</option>' +
'	<option value="tedesco">Deutsch</option>' +
'	<option value="francese">Français</option>' +
'	<option value="olandese">Nederlands</option>' +
'	<option value="spagnolo">Español</option>' +
'	<option value="turco">Türkçe</option>' +
'	<option value="giapponese">日本語</option>' +
'	<option value="croato">Hrvatski</option>' +
'</select>';

// set URL and text for returning/switching to the contest/language selection page
var titleOrg = 'International contests - Intersteno Internet Keyboarding Competitions';
var changeURLorg = 'https://org.intersteno.it/id_primario-47/id_secondario-211/?';
var switchDomOrg = 'Switch to national contests';
//
var titleWww = 'National contests - Intersteno Internet Keyboarding Competitions';
var changeURLwww = 'https://www.intersteno.it/id_primario-47/id_secondario-211/?noframe=true';
var switchDomWww = 'Switch to international contests';

// set URL for list of schools and associations
var schools = 'https://org.intersteno.it/page.php/id_primario-8/id_secondario-83/?';

// title text for escape from frame icon
var escFrame = 'View in full browser window';

// our own notice to be inserted at the bottom of the web page
var notice = 'Userscript / CSS by <a href="https://openuserjs.org/users/gebo007" target="_blank">gebo007</a>';

// other variables
var a; // attribute
var e; // element
var o; // iterator
var p; // parameter (in query string)
var shy = '\u00ad'; // soft hyphen (U+00AD)
//
var content = ''; // page content
var selected = ''; // selected option in drop-down list
var firstYear = 2003; // first year of the Intersteno Internet Keyboarding Contest
var listYear = 0; // year at the beginning of the item in the contest drop-down list (will be set later)
var year = 0; // current year of the Intersteno Internet Keyboarding Contest (will be set later)
var classCode = ''; // classification code (will be set later) / 1:  multilingual / 2: only mother tongue / 3: general
var contestId = ''; // current contest ID (will be set later)
var contestName = ''; // current contest name (will be set later)
var contestType = 0; // 0: national contests (will be set later) / 1: Intersteno Internet Keyboarding Contest

// main function
(function () {
    'use strict';

    // check which subdomain we're on:
    // org.intersteno.it hosts the international contests
    // www.intersteno.it hosts the national contests (alternate subdomain: internet.intersteno.it)
    var subdomain = 'www'; // set this as the default
    var changeURL = changeURLwww;
    var title = titleWww;
    var switchDom = '<a href="' + changeURLorg + '">' + switchDomWww + '</a>';
    // set English as the default language
    var lang = 'en';
    var queryLang = 'inglese';
    //
    if (window.location.href.search('org.intersteno.it') > -1) {
        subdomain = 'org';
        changeURL = changeURLorg;
        title = titleOrg;
        switchDom = '<a href="' + changeURLwww + '">' + switchDomOrg + '</a>';
    }
    else {
        schools = schools.replace(/org/, 'www') + 'noframe=true&';
    }

    // first, check if we're on a results page or on a schools page (and not on any other page on intersteno.it)
    // for results:
    //   the querystring should contain id_primario=47 and id_secondario=211 (in any order)
    //   or the url should contain /id_primario-47/id_secondario-211/
    //   for backwards compatibility, id's could also be 8 and 210 instead of 47 and 211
    // for schools:
    //   the querystring should contain id_primario=47 and id_secondario=213 (in any order)
    //   or the url should contain /id_primario-47/id_secondario-213/
    //   for backwards compatibility, id's could also be 8 and 83 instead of 47 and 213
    var location = window.location.href;
    if (((location.search(/id_primario(=|-)8/) > -1) && (location.search(/id_secondario(=|-)(83|210)/) > -1)) ||
        ((location.search(/id_primario(=|-)47/) > -1) && (location.search(/id_secondario(=|-)(211|213)/) > -1))) {

        // get the querystring
        var queryString = window.location.search;
        if (queryString === null) { queryString = ''; } // set to empty string if the current URL doesn't contain a querystring

        // check on what type of page we are
        var pageType = 0; // default page type: contest/language selection page
        if (queryString.search(/contest_id=\d/) > -1) {
            pageType = 1; // age level/classification type selection page
            // get contest ID
            contestId = queryString.match(/contest_id=\d+/)[0];
            contestId = contestId.replace(/contest_id=/, "");
        }
        var multiLang = 0;
        if (document.getElementsByTagName("table").length > 0) {
            pageType = 2; // classification list page
            if (queryString.search(/classification_type=mother_tongue+/) == -1) {
                multiLang = 1; // this is a multilinguage results pages
            }
        }
        if (window.location.href.search(/id_secondario(=|-)(83|213)/) > -1) {
            pageType = 4; // schools page
        }

        // get currently selected language
        if (queryString.search(/language=[^&]+/) > -1) {
            queryLang = queryString.match(/language=[^&]+/)[0];
            queryLang = queryLang.replace(/language=/, "");
        }
        //
        if (queryLang.search(/italiano/) > -1) {
            lang = 'it'; cnys = cnysIT;
        }
        if (queryLang.search(/tedesco(svizzero)?/) > -1) {
            queryLang = 'tedesco';
            lang = 'de'; cnys = cnysDE;
        }
        if (queryLang.search(/francese(belga|svizzero)?/) > -1) {
            queryLang = 'francese';
            lang = 'fr'; cnys = cnysFR;
        }
        if (queryLang.search(/(olandese|fiammingo)/) > -1) {
            queryLang = 'olandese';
            lang = 'nl'; cnys = cnysNL;
        }
        if (queryLang.search(/spagnolo/) > -1) {
            lang = 'es'; cnys = cnysES;
        }
        if (queryLang.search(/turco/) > -1) { lang = 'tr'; }
        if (queryLang.search(/giapponese/) > -1) { lang = 'ja'; }
        if (queryLang.search(/croato/) > -1) { lang = 'hr'; }
        //
        p = langSelect.indexOf('value="' + queryLang + '"');
        if (p > -1) {
            langSelect = langSelect.substring(0, p) + 'selected="" ' + langSelect.substring(p);
        }

        // on contest/language selection page, add link to national or international contests, replace or add language selection drop-down list
        if (pageType == 0) {
            // add link
            e = document.createElement('p');
            e.classList.add('switchDomain');
            switchDom = switchDom.replace(/">/, '&language=' + queryLang + '">');
            e.innerHTML = '► ' + switchDom;
            document.body.appendChild(e);
            // remove language selection drop-down list (international contests only)
            if (document.getElementsByName('language_selector').length > 0) {
                document.getElementsByName('language_selector')[0].outerHTML = '';
            }
            else {
                langSelect = '<br />' + langSelect + '<br /><br />';
            }
            // add new language selection drop-down list (national and international contests)
            var f = document.getElementsByTagName('form')[0];
            f.innerHTML = langSelect + f.innerHTML;
        }

        // get the contest name, replace it with the canonized contest name (+ link to language/contest selection), and set the contest family
        if (pageType > 0) {
            if (document.getElementsByTagName('b')[0] != null) {
                contestName = document.getElementsByTagName('b')[0].innerHTML;
                changeURL = changeURLwww;
                for (o = 0; o < allContests.length; o++) {
                    z = allContests[o].search(/:/);
                    if (contestId == allContests[o].substr(0, z)) {
                        var rest = allContests[o].substr(z + 1);
                        z = rest.search(/:/);
                        classCode = rest.substr(0, z);
                        contestName = rest.substr(z + 1);
                        if (contestName.search(/^Intersteno \d{4}$/) > -1) {
                            contestType = 1;
                            changeURL = changeURLorg;
                            year = contestName.match(/\d+/)[0]; // get the current year
                        }
                        else {
                            contestType = 0;
                            contestName = contestName.replace(/ \([A-Z]{2}\)$/, ''); // remove country code
                        }
                    }
                }
                e = document.createElement('span');
                e.classList.add('changeContest');
                e.innerHTML = '<a href="' + changeURL + '&language=' + queryLang + '">' + contestName + '</a>';
                document.getElementsByTagName('b')[0].innerHTML = e.outerHTML;
            }
        }

        // add the stylesheet
        e = document.createElement('style');
        e.type = 'text/css';
        e.innerHTML = styleSheet;
        document.head.appendChild(e);

        // add the scripts
        e = document.createElement('script');
        e.innerHTML = fnEscape + fnContestSelect + fnLangSelect + fnFilter + fnToggleTotals;
        document.head.appendChild(e);

        // populate lngCodes, lngNames arrays
        for (o = 0; o < lngs.length; o++) {
            var z = lngs[o].search(/:/);
            lngNames.push(lngs[o].substr(0, z));
            lngCodes.push(lngs[o].substr(z + 1));
        }

        // populate cnyCodes, cnyNames arrays
        for (o = 0; o < cnys.length; o++) {
            z = cnys[o].search(/:/);
            cnyNames.push(cnys[o].substr(0, z));
            cnyCodes.push(cnys[o].substr(z + 1));
        }

        // add escape icon, allowing the user to escape the iFrame and go to a full browser window
        e = document.createElement('div');
        a = document.createAttribute('id'); a.value = 'escFrame'; e.setAttributeNode(a);
        a = document.createAttribute('title'); a.value = escFrame; e.setAttributeNode(a);
        a = document.createAttribute('onclick'); a.value = 'escFrame()'; e.setAttributeNode(a);
        e.innerHTML = '<img src="data:image/gif;base64,' + imgFullScreen + '" />';
        if (window.self != window.top) { // show icon only if we're in a frame
            e.style.display = 'block';
        }
        document.body.appendChild(e);

        // add colgroup, setting the same proportional column widths across all tables
        e = document.createElement('colgroup');
        e.innerHTML = colGroupResults;
        if (pageType == 4) {
            e.innerHTML = colGroupSchools;
        }
        var t = document.getElementsByTagName('table')[0];
        if (t != null) {
            t.insertBefore(e, t.childNodes[0]);
        }

        // get the value of the filter_nation parameter from the query string, if present
        // e.g. filter_nation=IT for Italy
        cny = ''; // default: no filter
        p = queryString.search(/filter_nation=[^&]{2}/);
        if (p > -1) {
            cny = queryString.substr(p + 14, 2);
        }
        else {
            // get the value of the custom_filter parameter from the query string, if present
            // custom_filter=XX for Belgium + The Netherlands
            p = queryString.search(/custom_filter=[A-Z]{2}/);
            if (p > -1) {
                cny = queryString.substr(p + 14, 2);
            }
        }

        // get the value of the custom_filter parameter from the query string, if present
        // e.g. custom_filter=fr for French or custom_filter=XX-nl for Dutch (Belgium + The Netherlands)
        var lng = ''; // default: no filter
        p = queryString.search(/custom_filter=[a-z]{2}/);
        if (p > -1) {
            lng = queryString.substr(p + 14, 2);
        }
        else {
            p = queryString.search(/custom_filter=[A-Z]{2}-[a-z]{2}/);
            if (p > -1) {
                lng = queryString.substr(p + 17, 2);
            }
        }

        // add country selection
        for (o = 0; o < cnyList.length; o++) {
            cnyList[o] = cnyList[o].replace(/GB/, 'UK'); // replace ISO country code by hybrid Intersteno country code
            if (firstYear + o == 2009) {
                cnyList[o] = cnyList[o].replace(/RS/, 'YU'); // replace RS by YU in 2009 list
            }
            if ((cnyList[o].search('BE') > -1) && (cnyList[o].search('NL') > -1)) {
                cnyList[o] += ' XX'; // add custom code XX only if both BE and NL are in the list
            }
        }
        // add country and language selection drop-down lists (Intersteno contests only)
        if ((pageType == 2) && (contestType == 1)) {
            // language selection
            var s = document.getElementsByTagName('select')[0];
            e = document.createElement('select');
            a = document.createAttribute('id'); a.value = 'lngSelect'; e.setAttributeNode(a);
            a = document.createAttribute('size'); a.value = '1'; e.setAttributeNode(a);
            a = document.createAttribute('onchange'); a.value = 'filterLanguage(this)'; e.setAttributeNode(a);
            var options = '<option value="">' + lngNames[0] + '</option>';
            for (o = 1; o < lngCodes.length; o++) {
                options += '<option value="' + lngCodes[o] + '"';
                if (lng == lngCodes[o]) { // found the current language: select it
                    options += ' selected=""';
                }
                options += '>' + lngNames[o] + ' (' + lngCodes[o] + ')</option>';
            }
            e.innerHTML = options;
            s.parentNode.insertBefore(e, s.nextSibling);
            // add language selection label
            e = document.createElement('label');
            a = document.createAttribute('id'); a.value = 'lngLabel'; e.setAttributeNode(a);
            e.innerHTML = 'Filter results by language:';
            s.parentNode.insertBefore(e, s.nextSibling);
            // country selection
            // s = document.getElementsByTagName('select')[0];
            e = document.createElement('select');
            a = document.createAttribute('id'); a.value = 'cnySelect'; e.setAttributeNode(a);
            a = document.createAttribute('size'); a.value = '1'; e.setAttributeNode(a);
            a = document.createAttribute('onchange'); a.value = 'filterNation(this)'; e.setAttributeNode(a);
            options = '<option value="">' + cnyNames[0] + '</option>';
            var cnyFound = 0;
            var validCnyCodes = ('' + cnyCodes).replace(/\,/g, ' '); // if no cnyList defined for current year, just show all country codes
            if (year - firstYear < cnyList.length) {
                validCnyCodes = cnyList[year - firstYear]; // else show only the country list for the current year
            }
            for (o = 1; o < cnyCodes.length; o++) {
                if (validCnyCodes.search(cnyCodes[o]) > -1) {
                    options += '<option value="' + cnyCodes[o] + '"';
                    if (cny == cnyCodes[o]) { // found the current country: select it
                        options += ' selected=""';
                        cnyFound = 1;
                    }
                    options += '>' + cnyNames[o] + ' (';
                    if (cnyCodes[o] == 'XX') { // we've assigned the custom country code XX to the combination of Belgium + The Netherlands
                        options += 'BE|NL';
                    }
                    else if (cnyCodes[o] == 'UK') { // Intersteno uses 'UK' for United Kingdom instead of the correct ISO country code, which is 'GB'
                        options += 'GB';
                    }
                    else {
                        options += cnyCodes[o];
                    }
                    options += ')</option>';
                }
            }
            if ((cnyFound == 0) && (cny !== '')) { // current country is not in our list of countries, so add it as an extra option and select it
                options += '<option value="' + cny + '" selected="">' + cny + '</option>';
            }
            e.innerHTML = options;
            s.parentNode.insertBefore(e, s.nextSibling);
            // add country selection label
            e = document.createElement('label');
            a = document.createAttribute('id'); a.value = 'cnyLabel'; e.setAttributeNode(a);
            e.innerHTML = 'Filter results by country:';
            s.parentNode.insertBefore(e, s.nextSibling);
        }

        // use same stylesheet for national and international contests
        content = document.getElementsByTagName('head')[0].innerHTML;
        content = content.replace(/<link rel="stylesheet" [^>]+>/i, '<link rel="stylesheet" href="https://org.intersteno.it/style.css.php" type="text/css" />');
        // change document tiitle to something more useful
        content = content.replace(/<title>.+<\/title>/, '<title>' + title + '</title>');
        document.getElementsByTagName('head')[0].innerHTML = content;

        // get and fix page content
        content = document.getElementsByTagName('body')[0].innerHTML;
        if (pageType == 4) {
            if (lang.search(/^(de|en|es|fr|it|nl)$/) > -1) { // supported translations
                content = content.replace(/<div class="center"><span class="highlight">[^<]+<\/span><\/div><br><br>/i, '');
            }
            else {
                lang = 'en'; // set language to english for non-supported languages
            }
            content = content.replace(/<div class="center"/ig, '<div class="schools"');
            content = content.replace(/<td width="[^>]+"/g, '<td'); // remove column widths (we're using colgroup instead!)
        }
        content = content.replace(/<div class="center">[^<]+<b><span class="changeContest">/, '<div class="center"><b><span class="changeContest">');

        // determine the maximum contest id
        for (o = 0; o < allContests.length; o++) {
            contestType = 0;
            z = allContests[o].search(/:/);
            var cid = parseInt(allContests[o].substr(0, z));
            if (cid > maxId) { maxId = cid; }
        }

        // replace contest drop-down list
        if ((pageType == 0) || (pageType == 4)) {
            e = '<select name="contest_id" size="1">';
            if (pageType == 4) {
                e = e.replace(/>/, ' onChange="contestSelect(this)">');
            }
            // get newer contests from the original list
            var newContests = [];
            o = -1;
            if (content.search(/<select name="contest_id".+?<\/select>/) > -1) {
                var select = content.match(/<select name="contest_id".+?<\/select>/)[0];
                while (select.search(/<option [^<]+<\/option>/) > -1) {
                    var opts = select.match(/<option [^<]+<\/option>/)[0]; // get next option element
                    select = select.replace(/<option [^<]+<\/option>/, ''); // remove it
                    var val = opts.match(/ value="\d+"/)[0]; // get value (contest id)
                    val = val.replace(/\D+/g, ''); // remove non-digits
                    if (parseInt(val) > maxId) { // only add contests that are newer than our maximum contest id
                        var name = opts.match(/>.+</)[0]; // get contest name
                        name = name.replace(/[<>]/g, ''); // remove delimiters
                        name = name.replace(/^\d{4} - /, ''); // ignore year at the start (Intersteno added this in 2021-02)
                        if (name.search(/^\d+/) > -1) { // name begins with some digits
                            var yr = name.match(/^\d+/)[0]; // get the digits (probably the year)
                            name = name.replace(/^\d+ ?/, '') + ' ' + yr; // move digits to the end
                        }
                        name = name.replace(/Državno Natjecanje/i, 'Državno Natjecanje');
                        name = name.replace(/LUX_/i, 'LUX-');
                        name = name.replace(/Typing20/i, 'Typing 20');
                        name = name.replace(/PRO.Belgium./i, 'PRO_Belgium ');
                        name = name.replace(/T.rk.ye .hsan Yener Kl.vye .amp.yonas./i, 'Türkiye İhsan Yener Klâvye Şampiyonası');
                        if (name.search(/ \([A-Z]{2}\)/) > -1) { // name contains a country code
                            var cy = name.match(/ \([A-Z]{2}\)/)[0]; // get the country code
                            name = name.replace(/ \([A-Z]{2}\)/, '') + ' ' + cy; // move country code to the end
                        }
                        else {
                            if (name.search(/^(ABW|APSB|PRO_Belgium) /) > -1) { name += ' (BE)'; }
                            if (name.search(/^Swiss /) > -1) { name += ' (CH)'; }
                            if (name.search(/^Državno /) > -1) { name += ' (HR)'; }
                            if (name.search(/^(Allenamenti|Certificazione) /) > -1) { name += ' (IT)'; }
                            if (name.search(/^LUX/) > -1) { name += ' (LU)'; }
                            if (name.search(/^(Adalet|Türkiye) /) > -1) { name += ' (TR)'; }
                        }
                        var sortKey = name.toLowerCase();
                        sortKey = sortKey.replace(/ \(be\)/, '');
                        if (val != 222) { // Swiss 2020 is a non-contest
                            newContests.push(sortKey + ';' + name + ':' + val);
                        }
                    }
                }
                newContests = newContests.sort(); // sort new contests alphabetically
            }
            // compose the new contest selection list and the country selection list
            validCnyCodes = '';
            if (pageType == 4) {
                var currentId = contestId;
                if (queryString.search(/contest_id=[^&]+/) == -1) {
                    e += '<option value=""></option>';
                }
            }
            for (o = 0; o < allContests.length; o++) {
                contestType = 0;
                z = allContests[o].search(/:/);
                contestId = parseInt(allContests[o].substr(0, z));
                contestName = allContests[o].substr(z + 1);
                contestName = contestName.replace(/^\d+:/, ''); // remove classification code
                if (contestName.search(/^Intersteno/) > -1) {
                    contestType = 1;
                }
                if (((contestType == 0) && (subdomain == 'www')) || ((contestType == 1) && (subdomain == 'org'))) {
                    // check if there are one or more newer contests that should be inserted before this one
                    var contest = contestName.replace(/\d+/, ''); // ignore digits when comparing contest names
                    var n = '';
                    for (var oo = 0; oo < newContests.length; oo++) {
                        var zz = newContests[oo].search(/:/);
                        if (zz > -1) {
                            var newName = newContests[oo].substr(0, zz);
                            var zzz = newName.search(/;/);
                            sortKey = newName.substr(0, zzz);
                            newName = newName.substr(zzz + 1);
                            var newId = parseInt(newContests[oo].substr(zz + 1));
                            if (sortKey.replace(/\d+/, '') < contestName.toLowerCase().replace(/ \(..\)$/, '')) { // insert!
                                newContests[oo] = ''; // mark as processed
                                newName = newName.replace(/^[^;]+;/, ''); // remove sortKey
                                // get the country of the contest to be inserted
                                var newCny = '';
                                if (newName.search(/ \([A-Z]{2}\)$/) > -1) {
                                    newCny = newName.match(/ \([A-Z]{2}\)$/)[0];
                                    newCny = newCny.replace(/[ \(\)]/g, '');
                                }
                                // insert newer contest
                                if ((cny == '') || ((cny > '') && (cny == newCny))) {
                                    n += '<option value="' + newId + '">' + newName + '</option>';
                                }
                            }
                        }
                    }
                    e += n;
                    // get this contest's country
                    var contestCny = '';
                    if (contestName.search(/ \([A-Z]{2}\)$/) > -1) {
                        contestCny = contestName.match(/ \([A-Z]{2}\)$/)[0];
                        contestCny = contestCny.replace(/[ \(\)]/g, '');
                    }
                    // add this contest to the list
                    if ((cny == '') || ((cny > '') && (cny == contestCny))) {
                        e += '<option value="' + allContests[o].substr(0, z) + '">' + contestName + '</option>';
                    }
                    // get country code for this contest
                    zz = contestName.search(/ \([A-Z]{2}\)$/);
                    if (zz > -1) {
                        validCnyCodes += contestName.substr(zz + 2, 2) + ' ';
                    }
                }
            }
            options = '<option value="">' + cnyNames[0] + '</option>';
            cnyFound = 0;
            for (oo = 1; oo < cnyCodes.length; oo++) {
                if (validCnyCodes.search(cnyCodes[oo]) > -1) {
                    options += '<option value="' + cnyCodes[oo] + '"';
                    if (cny == cnyCodes[oo]) { // found the current country: select it
                        options += ' selected=""';
                        cnyFound = 1;
                    }
                    options += '>' + cnyNames[oo] + ' (';
                    if (cnyCodes[oo] == 'UK') { // Intersteno uses 'UK' for United Kingdom instead of the correct ISO country code, which is 'GB'
                        options += 'GB';
                    }
                    else {
                        options += cnyCodes[oo];
                    }
                    options += ')</option>';
                }
            }
            if ((cnyFound == 0) && (cny !== '')) { // current country is not in our list of countries, so add it as an extra option and select it
                options += '<option value="' + cny + '" selected="">' + cny + '</option>';
            }
            var cnySelect = '<br><br>Please choose a country: <select id="cnySelect" size="1" onchange="filterNation(this)" style="display: inline;">' + options + '</select><br>';
            //
            var lngFound = 0;
            for (oo = 1; oo < lngCodes.length; oo++) {
                if (validCnyCodes.search(lngCodes[oo]) > -1) {
                    options += '<option value="' + lngCodes[oo] + '"';
                    if (lng == lngCodes[oo]) { // found the current country: select it
                        options += ' selected=""';
                        lngFound = 1;
                    }
                    options += '>' + lngNames[oo] + ' (' + lngCodes[oo] + ')</option>';
                }
            }
            if ((cnyFound == 0) && (cny !== '')) { // current country is not in our list of countries, so add it as an extra option and select it
                options += '<option value="' + cny + '" selected="">' + cny + '</option>';
            }
            options = '<option value="">' + lngNames[0] + '</option>';
            var lngSelect = '<br><br>Please choose a language: <select id="lngSelect" size="1" onchange="filterLanguage(this)" style="display: inline;">' + options + '</select><br>';
            if (pageType != 2) { lngSelect = ''; }
            if (subdomain == 'www') {
                content = content.replace(/<br><br>/, cnySelect + lngSelect);
            }
            // check if there are still one or more newer contests left that should be added at the end of the list
            for (oo = 0; oo < newContests.length; oo++) {
                zz = newContests[oo].search(/:/);
                if (zz > -1) {
                    newId = parseInt(newContests[oo].substr(zz + 1));
                    newName = newContests[oo].substr(0, zz);
                    zzz = newName.search(/;/);
                    sortKey = newName.substr(0, zzz);
                    newName = newName.substr(zzz + 1);
                    // get the country of the contest to be inserted
                    newCny = '';
                    if (newName.search(/ \([A-Z]{2}\)$/) > -1) {
                        newCny = newName.match(/ \([A-Z]{2}\)$/)[0];
                        newCny = newCny.replace(/[ \(\)]/g, '');
                    }
                    // insert newer contest
                    if ((cny == '') || ((cny > '') && (cny == newCny))) {
                        e += '<option value="' + newId + '">' + newName + '</option>' + n;
                    }
                }
            }
            e += '</select>';
            var rgxp = new RegExp('value="' + currentId + '"');
            e = e.replace(rgxp, 'value="' + currentId + '" selected=""'); // select the current contest
            content = content.replace(/<select name="contest_id".+?<\/select>/, e);
            // get rid of filter_nation parameter
            content = content.replace(/name="filter_nation" value="[^"]+"/, 'name="filter_nation" value=""');
        }

        // replace existing or add missing classification type drop-down list
        if ((pageType > 0) && (pageType < 4)) {
            // remove drop-down list, if present (unless it's a newer contest)
            if (contestId <= maxId) {
                content = content.replace(/<BR>[^<]+<SELECT name="classification_type".+<\/SELECT><INPUT type="submit"/i, '<INPUT type="submit"');
                // determine the currently selected classification type
                var currentClassType = '';
                if (queryString.search(/classification_type=[^&]+/) > -1) {
                    currentClassType = queryString.match(/classification_type=[^&]+/)[0];
                    currentClassType = currentClassType.replace(/classification_type=/, '');
                }
                // add new drop-down list with the correct options for this contest and select the appropriate option
                if ((classCode.length > 1) || (pageType == 1)) {
                    var classSelection = '<BR>Classification type: <select name="classification_type" size="1">';
                    if (currentClassType == 'multulingual') { selected = ' selected=""'; } else { selected = ''; }
                    if (classCode.search(/1/) > -1) { classSelection += '<option value="multilingual"' + selected + '>multilingual</option>'; }
                    if (currentClassType == 'mother_tongue') { selected = ' selected=""'; } else { selected = ''; }
                    if (classCode.search(/2/) > -1) { classSelection += '<option value="mother_tongue"' + selected + '>only mother tongue</option>'; }
                    if (currentClassType == 'normal') { selected = ' selected=""'; } else { selected = ''; }
                    if (classCode.search(/3/) > -1) { classSelection += '<option value="normal"' + selected + '>general</option>'; }
                    classSelection += '</select>';
                    if (classCode.length == 1) {
                        classSelection = '<span class="hidden">' + classSelection + '</span>';
                    }
                    content = content.replace(/<INPUT type="submit"/i, classSelection + '<INPUT type="submit"');
                }
            }
        }

        if (pageType == 4) {
            // translate countries on schools pages and count the number of countries
            content = content.replace(/ \(Hrvatska\)/g, '');
            cnysEN.push('China:CN');
            cnysEN.push('Czech Republic:CZ');
            cnysEN.push('Macedonia, Former Yugoslav Republic of:MK');
            cnysEN.push('Netherlands Antilles:NL');
            cnysEN.push('Netherlands, The:NL');
            cnysEN.push('Senegal:SN');
            cnysEN.push('United Kingdom:GB');
            cnysEN.push('United States:US');
            cnysEN.push('Vietnam:VN');
            cnysEN.push('Yugoslavia:YU');
            // step 1: English country names to ISO country codes + count countries
            var totalCny = 0;
            for (o = 0; o < cnysEN.length; o++) {
                z = cnysEN[o].search(/:/);
                var cnyEN = cnysEN[o].substr(0, z);
                var cnyISO = cnysEN[o].substr(z + 1);
                rgxp = new RegExp('<b>' + cnyEN + '</b>', 'g');
                content = content.replace(rgxp, '<b>' + cnyISO + '</b>');
            }
            // step 2: ISO country codes to country names in the selected language
            for (o = 0; o < cnyCodes.length; o++) {
                var cnyName = cnyNames[o];
                cnyISO = cnyCodes[o];
                rgxp = new RegExp('<b>' + cnyISO + '</b>', 'g');
                if (cnyISO == 'UK') { cnyISO = 'GB'; } // correct for United Kingdom (ISO country code is GB, not UK!)
                content = content.replace(rgxp, cnyISO + ' – <b>' + cnyName + '</b>');
                // step 3: move ISO country code to the end of the previous column (town)
                rgxp = new RegExp('</td><td>' + cnyISO + ' – <b>', 'g');
                if (content.search(rgxp) > -1) { totalCny++; }
                content = content.replace(rgxp, ', ' + cnyISO + '</td><td><b>');
            }
            // add column heads on schools pages
            var colHeads = '<tr><th class="sticky c1">School/Association</th><th class="sticky c2">Town/Country</th><th class="sticky c3">Country</th><th class="sticky c4">Participants</th></tr>';
            content = content.replace(/<\/font><\/b><\/td><\/tr>/ig, '</font></b></td></tr><!-- colHeads -->');
            content = content.replace(/<!-- colHeads -->/, '');
            content = content.replace(/<!-- colHeads -->/, colHeads);
            // add extra column for number of participants
            content = content.replace(/ colspan="3"/g, ' colspan="4"');
            content = content.replace(/ colspan="2"/g, ' colspan="3"');
            content = content.replace(/ \(<b>(\d+)<\/b> students competed, <b>(\d+)<\/b> did not compete\)<\/td><\/tr>/gi, '</td><td>$1 (+$2)</td></tr>');
            content = content.replace(/ \(\+0\)<\/td><\/tr>/gi, '</td></tr>');
            // add total number of countries
            var totalRow = '<tr class="evenline"><td class="c1">Total countries</td><td colspan="3" align="right"><b>' + totalCny + '</b></td></tr>';
            content = content.replace(/<tr class="oddline">/, totalRow + '<tr class="oddline">');
        }

        // fix translations
        content = content.replace(/This information is updated in real time/, 'Contest <b>OPEN</b>'); // replace this message
        content = content.replace(/Contest to be displayed:/, 'Please choose an Internet contest:');
        if (lang == 'en') {
            // schools page
            // The terms 'schools', 'teachers' and 'students' were introduced in 2003, when only students participated in the internet contest.
            // Nowadays, everyone can participate and these terms must be updated!
            content = content.replace(/Total countries/, 'Number of countries');
            content = content.replace(/Total schools/, 'Number of schools and associations');
            content = content.replace(/Total teachers/, 'Number of representatives');
            content = content.replace(/Total students/, 'Number of participants');
            content = content.replace(/Schools part[^<]+ on the Internet/, 'KEYBOARDING COMPETITION VIA INTERNET'); // should be identical to title on results pages!
            content = content.replace(/List of the schools/, 'List of schools and associations');
            content = content.replace(/Schools registered to the Internet contests/, 'Registered schools and associations');
        }
        if (lang == 'it') {
            content = content.replace(/Switch to national contests/, 'Passa ai concorsi nazionali');
            content = content.replace(/Switch to international contests/, 'Passa ai concorsi internazionali');
            content = content.replace(/View in full browser window/, 'Visualizza nella finestra del browser completa');
            content = content.replace(/Please choose a country:/, 'Scegliete per favore un paese:');
            content = content.replace(/Filter results by country:/, 'Filtra i risultati per paese:');
            content = content.replace(/Filter results by language:/, 'Filtra i risultati per lingua:');
            content = content.replace(/Please choose an Internet contest:/, 'Scegliete per favore un Internet contest:');
            content = content.replace(/Classification type:/, 'Tipologia di classifica:');
            content = content.replace(/>multilingual</, '>multilingue<');
            content = content.replace(/>only mother tongue</, '>solo lingua madre<');
            content = content.replace(/>general</, '>generale<');
            content = content.replace(/Contest to be displayed/, 'Concorso');
            content = content.replace(/Contest /, 'Concorso ');
            content = content.replace(/CLOSED/, 'CHIUSO');
            content = content.replace(/OPEN/, 'APERTO');
            if (pageType == 4) {
                content = content.replace(/Total countries/, 'Numero di paesi');
                content = content.replace(/Total schools/, 'Numero di scuole e associazioni');
                content = content.replace(/Total teachers/, 'Numero di persone responsabili');
                content = content.replace(/Total students/, 'Numero di partecipanti');
                content = content.replace(/Schools part[^<]+ on the Internet/, 'CAMPIONATO DI SCRITTURA AL COMPUTER A MEZZO INTERNET');
                content = content.replace(/List of the schools/, 'Elenco di scuole e associazioni');
                content = content.replace(/School\/Association/, 'Scuola/Associazione');
                content = content.replace(/Schools registered to the Internet contests/, 'Scuole e associazioni registrate');
                content = content.replace(/Town\/Country/, 'Città/Nazione');
                content = content.replace(/Country/, 'Nazione');
                content = content.replace(/Participants/g, 'Partecipanti');
            }
        }
        if (lang == 'de') {
            content = content.replace(/Switch to national contests/, 'Zu nationalen Wettbewerben wechseln');
            content = content.replace(/Switch to international contests/, 'Zu internationalen Wettbewerben wechseln');
            content = content.replace(/View in full browser window/, 'Im vollständigen Browserfenster anzeigen');
            content = content.replace(/Please choose a country:/, 'Bitte Land wählen:');
            content = content.replace(/Filter results by country:/, 'Ergebnisse nach Land filtern:');
            content = content.replace(/Filter results by language:/, 'Ergebnisse nach Sprache filtern:');
            content = content.replace(/Please choose an Internet contest:/, 'Bitte Internetwettbewerb wählen:');
            content = content.replace(/Please choose the age level:/, 'Bitte Altersstufe wählen:');
            content = content.replace(/Participants of all ages/g, 'Teilnehmer jeden Alters');
            content = content.replace(/Classification type:/, 'Klassifizierungsart:');
            content = content.replace(/>multilingual</, '>Mehrsprachenwettbewerb<');
            content = content.replace(/>only mother tongue</, '>Muttersprache<');
            content = content.replace(/>general</, '>Allgemeines<');
            content = content.replace(/Contest to be displayed/, 'Wettbewerb');
            content = content.replace(/Contest /, 'Wettbewerb ');
            content = content.replace(/- length:/, '- Dauer:');
            content = content.replace(/CLOSED/, 'GESCHLOSSEN');
            content = content.replace(/OPEN/, 'GEÖFFNET');
            content = content.replace(/Rangliste Meisterschaft im PC/, 'RANGLISTE MEISTERSCHAFT IM PC');
            content = content.replace(/Schreiben via Internet/, 'SCHREIBEN VIA INTERNET');
            content = content.replace(/Technology/, 'Technologie');
//          content = content.replace(/Unsuccessful competitors/, 'Nicht Klassierte');
            content = content.replace(/Anschläge pro minute/, 'Anschläge pro Minute');
            content = content.replace(/All languages/, 'Alle Sprachen');
            if (pageType == 4) {
                content = content.replace(/Total countries/, 'Anzahl der Länder');
                content = content.replace(/Total schools/, 'Anzahl der Schulen und Vereine');
                content = content.replace(/Total teachers/, 'Anzahl der Verantwortlichen');
                content = content.replace(/Total students/, 'Anzahl der Teilnehmer');
                content = content.replace(/Schools part[^<]+ on the Internet/, 'SCHREIBEN VIA INTERNET');
                content = content.replace(/List of the schools/, 'Liste der Schulen und Vereine');
                content = content.replace(/Schools registered to the Internet contests/, 'Registrierte Schulen und Vereine');
                content = content.replace(/School\/Association/, 'Schule/Verein');
                content = content.replace(/Town\/Country/, 'Stadt/Land');
                content = content.replace(/Country/, 'Land');
                content = content.replace(/Participants/g, 'Teilnehmer');
            }
        }
        if (lang == 'fr') {
            content = content.replace(/Switch to national contests/, 'Passer aux concours nationaux');
            content = content.replace(/Switch to international contests/, 'Passer aux concours internationaux');
            content = content.replace(/View in full browser window/, 'Afficher dans la fenêtre complète du navigateur');
            content = content.replace(/Please choose a country:/, 'Choisissez un pays:');
            content = content.replace(/Filter results by country:/, 'Filtrer les résultats par pays:');
            content = content.replace(/Filter results by language:/, 'Filtrer les résultats par langue:');
            content = content.replace(/Please choose an Internet contest:/, 'Choisissez un championnat:');
            content = content.replace(/Please choose the age level:/, 'Choisissez la catégorie:');
            content = content.replace(/Participants of all ages/g, 'Participants de tous âges');
            content = content.replace(/Classification type:/, 'Classement:');
            content = content.replace(/>multilingual</, '>multilingue<');
            content = content.replace(/>only mother tongue</, '>langue maternelle<');
            content = content.replace(/>general</, '>général<');
            content = content.replace(/Contest to be displayed/, 'Championnat');
            content = content.replace(/Contest /, 'Championnat ');
            content = content.replace(/- length:/, '- durée:');
            content = content.replace(/CLOSED/, 'FERMÉ');
            content = content.replace(/OPEN/, 'OUVERT');
            content = content.replace(/Classement du Championnat d'écriture sur ordinateur par le biais d'Internet/, 'CLASSEMENT DU CHAMPIONNAT D’ÉCRITURE SUR ORDINATEUR PAR LE BIAIS D’INTERNET');
            content = content.replace(/Technology/, 'Technologie');
//          content = content.replace(/Unsuccessful competitors/, 'Non classés');
            content = content.replace(/% de ftes/, '% de fautes');
            content = content.replace(/All languages/, 'Toutes les langues');
            if (pageType == 4) {
                content = content.replace(/Total countries/, 'Nombre de pays');
                content = content.replace(/Total schools/, 'Nombre d’écoles et d’associations');
                content = content.replace(/Total teachers/, 'Nombre de responsables');
                content = content.replace(/Total students/, 'Nombre de participants');
                content = content.replace(/Schools part[^<]+ on the Internet/, 'CHAMPIONNAT D’ÉCRITURE SUR ORDINATEUR PAR LE BIAIS D’INTERNET');
                content = content.replace(/List of the schools/, 'Liste des écoles et associations');
                content = content.replace(/Schools registered to the Internet contests/, 'Écoles et associations inscrites');
                content = content.replace(/School\/Association/, 'École/Association');
                content = content.replace(/Town\/Country/, 'Ville/Pays');
                content = content.replace(/Country/, 'Pays');
//              content = content.replace(/Participants/g, 'Participants');
            }
        }
        if (lang == 'nl') {
            content = content.replace(/Switch to national contests/, 'Schakel over naar nationale wedstrijden');
            content = content.replace(/Switch to international contests/, 'Schakel over naar internationale wedstrijden');
            content = content.replace(/View in full browser window/, 'Bekijk in volledig browservenster');
            content = content.replace(/Please choose a country:/, 'Kies een land:');
            content = content.replace(/Filter results by country:/, 'Filter resultaten op land:');
            content = content.replace(/Filter results by language:/, 'Filter resultaten op taal:');
            content = content.replace(/Please choose an Internet contest:/, 'Kies een test:');
            content = content.replace(/Please choose the age level:/, 'Kies de leeftijdscategorie:');
            content = content.replace(/Participants of all ages/g, 'Deelnemers van alle leeftijden');
            content = content.replace(/Classification type:/, 'Klassement:');
            content = content.replace(/>multilingual</, '>meertalig<');
            content = content.replace(/>only mother tongue</, '>moedertaal<');
            content = content.replace(/>general</, '>algemeen<');
            content = content.replace(/Contest to be displayed/, 'Wedstrijd');
            content = content.replace(/Contest /, 'Wedstrijd ');
            content = content.replace(/- length:/, '- duur:');
            content = content.replace(/CLOSED/, 'GESLOTEN');
//          content = content.replace(/OPEN/, 'OPEN');
            content = content.replace(/Technology/, 'Technologie');
            content = content.replace(/Unsuccessful competitors/, 'Niet-geslaagde deelnemers');
            content = content.replace(/All languages/, 'Alle talen');
            if (pageType == 4) {
                content = content.replace(/Total countries/, 'Aantal landen');
                content = content.replace(/Total schools/, 'Aantal scholen en verenigingen');
                content = content.replace(/Total teachers/, 'Aantal verantwoordelijken');
                content = content.replace(/Total students/, 'Aantal deelnemers');
                content = content.replace(/Schools part[^<]+ on the Internet/, 'INTERNETTEST KLAVIERVAARDIGHEID');
                content = content.replace(/List of the schools/, 'Lijst van scholen en verenigingen');
                content = content.replace(/Schools registered to the Internet contests/, 'Geregistreerde scholen en verenigingen');
                content = content.replace(/School\/Association/, 'School/Vereniging');
                content = content.replace(/Town\/Country/, 'Gemeente/Land');
                content = content.replace(/Country/, 'Land');
                content = content.replace(/Participants/g, 'Deelnemers');
            }
            // wrong translation:
            content = content.replace(/De uiteindelijke ranglijst zal enkel de resultaten bevatten van de deelnemers die aan de normen voldaan hebben/,
                                      'Deze ranglijst wordt in real time geüpdatet en bevat de resultaten van alle deelnemers');
        }
        if (lang == 'es') {
            content = content.replace(/Switch to national contests/, 'Cambiar a concursos nacionales');
            content = content.replace(/Switch to international contests/, 'Cambiar a concursos internacionales');
            content = content.replace(/View in full browser window/, 'Ver en la ventana completa del navegador');
            content = content.replace(/Please choose a country:/, 'Seleccionar un país:');
            content = content.replace(/Filter results by country:/, 'Filtrar resultados por país:');
            content = content.replace(/Filter results by language:/, 'Filtrar los resultados por idioma:');
//          content = content.replace(/Please choose an Internet contest:/, 'Seleccionar el concurso Internet:');
            content = content.replace(/Please choose the age level:/, 'Seleccionar el nivel de edad:');
            content = content.replace(/Participants of all ages/g, 'Competidores de todas las edades');
            content = content.replace(/Classification type:/, 'Tipo de clasificación:');
            content = content.replace(/>multilingual</, '>plurilingüe<');
            content = content.replace(/>only mother tongue</, '>solo lengua materna<');
            content = content.replace(/>general</, '>general<');
            content = content.replace(/Contest to be displayed/, 'Concurso');
            content = content.replace(/Contest /, 'Concurso ');
            content = content.replace(/- length:/, '- duración:');
            content = content.replace(/CLOSED/, 'CERRADO');
            content = content.replace(/OPEN/, 'ABIERTO');
            content = content.replace(/Technology/, 'Tecnología');
            content = content.replace(/Unsuccessful competitors/, 'No clasificado');
            content = content.replace(/All languages/, 'Todos los idiomas');
            if (pageType == 4) {
                content = content.replace(/Total countries/, 'Número de países');
                content = content.replace(/Total schools/, 'Número de escuelas y asociaciones');
                content = content.replace(/Total teachers/, 'Número de personas responsables');
                content = content.replace(/Total students/, 'Número de participantes');
                content = content.replace(/Schools part[^<]+ on the Internet/, 'CAMPEONATO DE ESCRITURA EN TECLADO DE COMPUTADORA VÍA INTERNET');
                content = content.replace(/List of the schools/, 'Lista de escuelas y asociaciones');
                content = content.replace(/Schools registered to the Internet contests/, 'Escuelas y asociaciones registradas');
                content = content.replace(/School\/Association/, 'Escuela/Asociación');
                content = content.replace(/Town\/Country/, 'Ciudad/Nación');
                content = content.replace(/Country/, 'Nación');
                content = content.replace(/Participants/g, 'Participantes');
            }
            // wrong translation:
            content = content.replace(/Seleccionar el contexto Internet:/, 'Seleccionar el concurso Internet:');
        }
        if (lang == 'tr') {
            content = content.replace(/Classification type:/, 'Sıralama türü:');
            content = content.replace(/>multilingual</, '>çokdilli<');
            content = content.replace(/>only mother tongue</, '>sadece Anadili<');
            content = content.replace(/>general</, '>general<');
        }
        if (lang == 'ja') {
            content = content.replace(/Classification type:/, '分類:');
            content = content.replace(/>multilingual</, '>複数の言語<');
            content = content.replace(/>only mother tongue</, '>母国語のみ<');
            content = content.replace(/>general</, '>全参加者(失格者を含む)<');
        }
        if (lang == 'hr') {
            content = content.replace(/Classification type:/, 'Vrsta klasifikacije:');
            content = content.replace(/>multilingual</, '>višejezična<');
            content = content.replace(/>only mother tongue</, '>samo materinski jezik<');
            content = content.replace(/>general</, '>općetina<');
        }

        // insert some soft hyphens in the column heads
        content = content.replace(/anslagen/g, 'an' + shy + 'sla' + shy + 'gen');
        content = content.replace(/Errores/g, 'Er' + shy + 'ro' + shy + 'res');
        content = content.replace(/errores/g, 'er' + shy + 'ro' + shy + 'res');
        content = content.replace(/Fautes/g, 'Fau' + shy + 'tes');
        content = content.replace(/Fehler/g, 'Feh' + shy + 'ler');
        content = content.replace(/Fouten/g, 'Fou' + shy + 'ten');
        content = content.replace(/Frappes/g, 'Frap' + shy + 'pes');
        content = content.replace(/\/minut/, shy + '/mi' + shy + 'nut');
        content = content.replace(/Porcentaje/, 'Por' + shy + 'cen' + shy + 'taje');
        content = content.replace(/Posición/, 'Posi' + shy + 'ción');
        content = content.replace(/Posizione/, 'Posi' + shy + 'zione');
        content = content.replace(/Pulsaciones/, 'Pul' + shy + 'sa' + shy + 'cio' + shy + 'nes');
        content = content.replace(/pulsaciones/, 'pul' + shy + 'sa' + shy + 'cio' + shy + 'nes');
        content = content.replace(/Software/, 'Soft' + shy + 'ware');
        content = content.replace(/Technolo/, 'Tech' + shy + 'nolo');
        content = content.replace(/Tecnolo/, 'Tecno' + shy + 'lo');

        // fix encoding errors
        content = content.replace(/\ufeff/g, ''); // remove byte order mark (U+feff) right after the <body> start tag
        content = content.replace(/\x6f\x3b\x3f/g, ''); // this appears in lieu of the byte order mark on Croatian pages (notice this is the byte order mark with the high bit stripped on each of the 3 bytes that together form the Unicode character)!
        content = content.replace(/\u008a/g, 'Š'); // fix encoding errors in Intersteno 2003 list: U+008A -> U+0160
        content = content.replace(/\u009a/g, 'š'); // fix encoding errors in Intersteno 2003 list: U+009A -> U+0161
        content = content.replace(/\u009e/g, 'ž'); // fix encoding errors in Intersteno 2003 list: U+009E -> U+017e
        content = content.replace(/\u00f2/g, 'ň'); // fix encoding errors in Intersteno 2003 list: U+00F2 -> U+0148
        content = content.replace(/¹/g, 'š'); // fix encoding errors in Intersteno 2003 list
        content = content.replace(/³/g, 'ł'); // fix encoding errors in Intersteno 2003 list

        // minor fixes
        content = content.replace(/ - /g, ' – '); // space hyphen space to space en-dash space
        content = content.replace(/(\d)min/, "$1 min"); // insert a space between last digit and 'min' in contest duration

        // replace fixed content, remove attributes from body tag
        document.getElementsByTagName('body')[0].innerHTML = content;
        document.getElementsByTagName('body')[0].removeAttribute('bottommargin');
        document.getElementsByTagName('body')[0].removeAttribute('topmargin');

        // show country filter only for yearly Intersteno contests, not for national contests
        contestType = 0;
        if (contestName.search(/^Intersteno/) > -1) {
            contestType = 1;
        }
        if (contestType == 1) {
            if (document.getElementById("cnyLabel") != null) {
                document.getElementById("cnyLabel").style.display = 'inline';
                document.getElementById("cnySelect").style.display = 'inline';
            }
            if (document.getElementById("lngLabel") != null) {
                document.getElementById("lngLabel").style.display = 'inline';
                document.getElementById("lngSelect").style.display = 'inline';
            }
        }

        // as long as browsers don't fully support col[@style], we'll have to apply a class to each cell
        // but while we're iterating all rows and cells anyway, we can filter on the combination Belgium + The Netherlands!
        // and we can correct ISO country and language codes!
        // also, we must change cells from td to th in the table head to make them sticky
        // also adding some extra styles:
        //   even1/odd1 for rows containing failed or missing results
        //   lang       for rows containing invidual language results (in multilanguage result lists)
        var r, c, cells, col;
        var rows = document.getElementsByTagName('tr');
        var pCny = ''; // participant's country
        var pLng = ''; // participant's language
        var oddOrEven = 'evenline';
        var oddOrEven1 = 'evenline';
        var thead = true;
        var totalsRow = -1;
        for (r = 0; r < rows.length; r++) {
            var spanRow = 1;
            // add class attribute to each cell
            col = 1;
            cells = rows[r].getElementsByTagName('td');
            for (c = 0; c < cells.length; c++) {
                if (cells[c].colSpan == 1) {
                    cells[c].classList.add('c' + col);
                }
                if (col == 3) { // get participant's country from third column
                    spanRow = 0;
                    var tn = cells[c].innerHTML;
                    n = tn.search(/, [A-Z]{2}</);
                    if (n > -1) {
                        pCny = tn.substr(n + 2, 2); // get the participant's country code
                        if (pCny == 'UK') { // correct for United Kingdom (ISO country code is GB, not UK!)
                            cells[c].innerHTML = cells[c].innerHTML.substr(0, n + 2) + 'GB' + cells[c].innerHTML.substr(n + 4);
                        }
                    }
                }
                if ((col == 4) && (r == 4)) { // school/association: make column head a clickable link to schools page for this contest
                    cells[c].innerHTML = cells[c].innerHTML.replace(/<\//, '<span class="extLink"></');
                    cells[c].innerHTML = '<a href="' + schools + 'contest_id=' + contestId + '&language=' + queryLang + '">' + cells[c].innerHTML + '</a>';
                }
                if (col == 10) { // ISO language codes to lowercase; remember participant's language
                    var firstChar = cells[c].innerHTML.substr(0, 1);
                    lang = cells[c].innerHTML.substr(0, 2);
                    lang = lang.replace(/cz/i, 'cs'); // correct for czech (ISO language code is cs, not cz!)
                    lang = lang.replace(/jp/i, 'ja'); // correct for japanese (ISO language code is ja, not jp!)
                    pLng = lang.toLowerCase(); // language code to lowercase
                    cells[c].innerHTML = pLng + cells[c].innerHTML.substr(2); // keep country code uppercase
                    cells[c].innerHTML = cells[c].innerHTML.replace(/LATIN/i, 'Lat.'); // 'LATIN' (for SR (LATIN)) is not a country code, so put it in lowercase with initial cap and abbreviate
                    cells[c].innerHTML = cells[c].innerHTML.replace(/TRANSL/i, 'Transl.'); // 'TRANSLITERATED' (for JA (TRANSLITERATED)) is not a country code, so put it in lowercase with initial cap and abbreviate
                    if (firstChar == '-') {
                        totalsRow = r; // remember totals row for multilingual results
                        cells[c].innerHtml += ' [' + r + ']';
                    }
                }
                col += cells[c].colSpan;
            }
            // change row class if a custom filter is in effect
            if ((rows[r].className == 'oddline') || (rows[r].className == 'evenline')) {
                thead = false;
                var rowStatus = '';
                // apply custom country filter
                if (cny == 'XX') {
                    a = document.createAttribute('class');
                    a.value = 'hidden';
                    if ((pCny == 'BE') || (pCny == 'NL')) { // this participant is from Belgium or The Netherlands!
                        if (spanRow == 0) {
                            if (oddOrEven == 'oddline') {
                                oddOrEven = 'evenline';
                            }
                            else {
                                oddOrEven = 'oddline';
                            }
                        }
                        a.value = oddOrEven;
                    }
                    rows[r].setAttributeNode(a);
                    rowStatus = a.value;
                }
                // apply custom language filter
                if ((lng !== '') && (rowStatus !== 'hidden')) {
                    a = document.createAttribute('class');
                    a.value = 'hidden';
                    if (lng == pLng) { // this result has the right language code!
                        if (spanRow == 0) {
                            if (oddOrEven1 == 'oddline') {
                                oddOrEven1 = 'evenline';
                            }
                            else {
                                oddOrEven1 = 'oddline';
                            }
                        }
                        a.value = oddOrEven1;
                    }
                    if (totalsRow >= 0) {
                        if (a.value !== 'hidden') {
                            rows[totalsRow].className = oddOrEven1;
                            totalsRow = -1;
                        }
                    }
                    rows[r].setAttributeNode(a);
                }
                // change background color for unsuccessful participants
                if ((rows[r].innerHTML.search(/<font color="red">0<\/font>/i) > -1) || ((rows[r].innerHTML.search(/<td class="c9">/i) == -1)) || ((rows[r].innerHTML.search(/<td class="c9"><b>0<\/b>/i) > -1))) {
                    a = rows[r].className;
                    var b = a.replace(/line/, '1');
                    rows[r].className = a + ' ' + b;
                }
                // for multilanguage results, add a class for rows containing multilanguage results
                if (queryString.search(/classification_type=mother_tongue+/) == -1) { // on multilinguage results pages only
                    if (rows[r].innerHTML.search(/<b>\d+<\/b>/) == -1) {
                        a = rows[r].className;
                        if (a.search(/hidden/) == -1) { // do this only for rows that are not hidden!
                            rows[r].className = a + ' lang';
                        }
                    }
                }
            }
            else {
                if (thead == true) {
                    var rowContent = rows[r].innerHTML;
                    if (rowContent.search(/<td colspan/) == -1) {
                        rowContent = rowContent.replace(/<td class="/g, '<th class="sticky ');
                        rowContent = rowContent.replace(/<\/td/g, '</th');
                        if (multiLang == 1) {
                            rowContent = rowContent.replace(/<th class="sticky c9">/, '<th class="sticky c9 link" onclick="toggleTotals()" title="Click to show totals only (hiding multilanguage results)">');
                        }
                        rows[r].innerHTML = rowContent;
                    }
                }
            }
        }

        // add notice
        e = document.createElement('p');
        e.classList.add('notice');
        e.innerHTML = notice;
        document.body.appendChild(e);

    }

})();