diff --git a/.gitignore b/.gitignore index e14fc0f6..a545a9e9 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,4 @@ mail.* /vendor/ +/.vs diff --git a/bup.html b/bup.html index a07c86fa..e9a62327 100644 --- a/bup.html +++ b/bup.html @@ -221,7 +221,7 @@

-
+

@@ -236,6 +236,7 @@

+
@@ -322,12 +323,12 @@

-
+
-

+
@@ -349,6 +350,11 @@

+
+
+
+
+
@@ -371,21 +377,22 @@

- +
- - - + + +
diff --git a/css/displaymode.css b/css/displaymode.css index 1cdaaa02..8215d148 100644 --- a/css/displaymode.css +++ b/css/displaymode.css @@ -1,4 +1,5 @@ .d_international_score, +.d_tournament_score, .d_clean_score, .d_score_default { font-family: 'Droid Sans', 'droid_sansregular', 'Noto Mono', Arial, sans-serif; @@ -131,12 +132,15 @@ } .d_international_team, +.d_tournament, .d_clean_team { font-stretch: condensed; position: relative; height: 50%; + text-wrap: nowrap; } .d_international_player_container, +.d_tournament_player_container .d_clean_player_container, .d_bwfonlyplayers_player_container { position: relative; @@ -153,6 +157,12 @@ display: flex; align-items: center; } +.d_tournament_player{ + width: 100%; + font-size: 1vh; + display: flex; + align-items: center; +} .d_bwf_player { width: 100%; font-size: 13vh; @@ -160,6 +170,7 @@ align-items: center; } .d_international_score, +.d_tournament_score, .d_clean_score { font-size: 50vh; line-height: 50vh; @@ -172,6 +183,7 @@ overflow: hidden; } .d_international_score.d_international_score_dd, +.d_tournament_score.d_tournament_score_dd, .d_clean_score.d_clean_score_dd { letter-spacing: -4vh; text-align: left; @@ -191,6 +203,19 @@ justify-content: center; } +.d_tournament_gscore { + font-size: 25vh; + line-height: 25vh; + position: absolute; + top: 0; + bottom: 0; + right: 52vh; + width: 20vh; + overflow: hidden; + display: flex; + align-items: center; + justify-content: center; +} .d_2court_side0, .d_2court_side1 { @@ -254,7 +279,9 @@ bottom: 0; display: flex; align-items: center; - justify-content: center; + justify-content: right; + text-wrap: nowrap; + width: calc(50vw - 44vh - 20px); } .d_2court_side0 .d_2court_info, .d_2court_side0 .d_2court_gscore { @@ -471,7 +498,7 @@ bottom: 0; display: flex; flex-direction: column; - justify-content: center; + justify-content: space-evenly; align-items: center; } diff --git a/css/network.css b/css/network.css index 38bdd68d..1d90e52e 100644 --- a/css/network.css +++ b/css/network.css @@ -124,6 +124,11 @@ button > .loading-icon { color: #444; } +.setup_network_tabletoperator { + color: #c14949; + font-weight: bold; +} + .setup_network_heading::after { /* Set line-height to height, so that we can vertically center with vertical-align: middle; */ content: ''; diff --git a/css/score.css b/css/score.css index c8f95248..dabbc0b4 100644 --- a/css/score.css +++ b/css/score.css @@ -89,10 +89,10 @@ table[data-game-count="5"] .score_future-game { padding-right: 0; } .score > input { - max-width: 1.9em; + width: 1.9em; vertical-align: top; - font-size: 2vh; - max-height: 3vh; + font-size: 7.5vh; + height: 8vh; } .score_current-game > .score > input { font-size: 7.5vh; diff --git a/doc/URLs.txt b/doc/URLs.txt index 04490229..75abc8ee 100644 --- a/doc/URLs.txt +++ b/doc/URLs.txt @@ -12,26 +12,26 @@ import_url=https://... Import from the specified match URL (e.g. https://www.tu There are also many demos (which only run locally) available: -demo A one-match demo (will start right in the match) -baydemo Bayernliga demo (also applies to lower divisions) -bldemo Demo of a Bundesliga match -bldemo_incomplete Demo of a Bundesliga match where not all players have been configured yet -bldemo_inprogress Demo of a Bundesliga match already running -e_bldemo Demo of a Bundesliga match where nothing has been configured yet -edemo Demo of an event (empty) -intdemo International team match demo -tdemo International tournament demo -nlademo Demo of Swiss National League -nrwdemo Demo of a match in the lower divisions in NRW -obldemo Austrian Bundesliga demo -rlmdemo German Regionalliga Mitte demo -rlndemo German Regionalliga Nord demo -rlsodemo German Regionalliga SüdOst demo -rlwdemo German Regionalliga West demo -tdemo Tournament demo -txdemo Demo for transmission to turnier.de -vdemo Demo of an event (in progress) -wdmudemo Youth tournament demo (with lots of finals on Sunday) +demo A one-match demo (will start right in the match) +baydemo Bayernliga demo (also applies to lower divisions) +bldemo Demo of a Bundesliga match +bldemo_incomplete Demo of a Bundesliga match where not all players have been configured yet +bldemo_inprogress Demo of a Bundesliga match already running +e_bldemo Demo of a Bundesliga match where nothing has been configured yet +edemo Demo of an event (empty) +intdemo International team match demo +tdemo International tournament demo +nlademo Demo of Swiss National League +nrwdemo Demo of a match in the lower divisions in NRW +obldemo Austrian Bundesliga demo +rlmdemo German Regionalliga Mitte demo +rlndemo German Regionalliga Nord demo +rlsodemo German Regionalliga SüdOst demo +rlwdemo German Regionalliga West demo +tdemo Tournament demo +txdemo Demo for transmission to turnier.de +vdemo Demo of an event (in progress) +wdmudemo Youth tournament demo (with lots of finals on Sunday) There is also: @@ -44,19 +44,19 @@ Initial UI These can be combined with any other options (but not each other), as in #bldemo&display . -settings Show settings UI (currently the default) -nosettings Hide settings by default (click to see) -neversettings Completely hide settings in displaymode -display Start in display mode -referee_mode Start in referee mode -eventsheet=foo Show the dialog for generating the eventsheet named foo -order Show the match order dialog -mo Start with the manual order dialog (allows easy match creation and import) +settings Show settings UI (currently the default) +nosettings Hide settings by default (click to see) +neversettings Completely hide settings in displaymode +display Start in display mode +referee_mode Start in referee mode +eventsheet=foo Show the dialog for generating the eventsheet named foo +order Show the match order dialog +mo Start with the manual order dialog (allows easy match creation and import) Settings ======== -lang=LANGCODE Set language to the specified one. +lang=LANGCODE Set language to the specified one. LANGCODE can be one of: en English de German (Germany) @@ -67,39 +67,57 @@ lang=LANGCODE Set language to the specified one. court=COURTCODE Run on the specified court. COURTCODE depends on the network. Normally, a number like 1, 2, or the string "referee". dm_style=STYLECODE In display mode, start with the specified style. Available styles: - teamcourt Best for team matches. Supports interval timer. - international Best for individual international tournaments - bwf Like international, but geared towards the BWF understanding of names ("LASTNAME, Firstname") - clean Classic style for (national) individual tournaments - 2court Large scores for 2 courts. Supports interval timer. - andre Simple 1-court display, shows game count to the left - castall Greenscreen (all courts) - stream Transparent video overlay - streamcourt Transparent video overlay, one court only (to be scaled in streaming software) - streamteam Transparent video overlay, only team names & match result (scales by width) - greyish Overview for team competitions with muted gray colors - oncourt court score with teams, not colored, including points in all games - onlyplayers players only - bwfonlyplayers players only, in BWF definition of names ("LASTNAME, Firstname" and fixed font size) - onlyscore score (all games) only - giantscore score (current game + game count) only, for very small screens - clubplayers team + players - clubplayerslr team + players (home left / away right) - stripes stripes (vertically: team names, clubs, all scores, for one court) - teamscore Just team names and number of won matches - tim Colored overview of a team competition - top+list Current matches on top, list of all matches below - tournament_overview Shows all courts at once -show_pause=BOOLEAN Show or hide interval timers. - BOOLEAN can be either "true" or "false". - At the moment only supported for some styles, see above. -team_colors=BOOLEAN Use team colors (if applicable in the given style). - BOOLEAN can be either "true" or "false". -d_c0=COLOR, This group of options can be used to set displaymode colors. -d_c1=COLOR, COLOR can be an RGB color value, for instance "0f0" or "00ff00" for lime. -d_cfg=COLOR, d_c0 and d_c1 are the colors for the home and away team, -d_cbg=COLOR, d_cfg the foreground and d_bg the background color, -d_cbg2=COLOR, ... and there are a lot more color names available. - Different styles use different color names. - Hover over a color input in the displaymode or referee UI to get the color names. -hub_url=WSURL WebSocket URL (starting with ws:// or wss://) of the referee mode hub. + teamcourt Best for team matches. Supports interval timer. + international Best for individual international tournaments + bwf Like international, but geared towards the BWF understanding of names ("LASTNAME, Firstname") + tournamentcourt Best for (national) individual tournaments. Supports interval timer, court number, competition, round, remove middle names and underline the receiving player in doubles. + tournamentplayers Best for (national) individual tournaments. Supports interval timer, court number, competition, round, remove middle names and underline the receiving player in doubles. Show only the Players. Use it in combination with onlyscore. + clean Classic style for (national) individual tournaments + 2court Large scores for 2 courts. Supports interval timer. + andre Simple 1-court display, shows game count to the left + castall Greenscreen (all courts) + stream Transparent video overlay + streamcourt Transparent video overlay, one court only (to be scaled in streaming software) + streamcourt_dm Transparent video overlay, one court only with additional information and ci of dm 2025 in Cloppenburg + streamteam Transparent video overlay, only team names & match result (scales by width) + greyish Overview for team competitions with muted gray colors + oncourt court score with teams, not colored, including points in all games + onlyplayers players only + bwfonlyplayers players only, in BWF definition of names ("LASTNAME, Firstname" and fixed font size) + onlyscore score (all games) only + giantscore score (current game + game count) only, for very small screens + clubplayers team + players + clubplayerslr team + players (home left / away right) + stripes stripes (vertically: team names, clubs, all scores, for one court) + teamscore Just team names and number of won matches + tim Colored overview of a team competition + top+list Current matches on top, list of all matches below + tournament_overview Shows all courts at once +show_pause=BOOLEAN Show or hide interval timers. + BOOLEAN can be either "true" or "false". + At the moment only supported for some styles, see above. +show_court_number=BOOLEAN Show or hide court number. + BOOLEAN can be either "true" or "false". + At the moment only supported for some styles, see above. +show_competition=BOOLEAN Show or hide competition. + BOOLEAN can be either "true" or "false". + At the moment only supported for some styles, see above. +show_round=BOOLEAN Show or hide round. + BOOLEAN can be either "true" or "false". + At the moment only supported for some styles, see above. +show_middle_name=BOOLEAN Show or hide the middle name of the players. + BOOLEAN can be either "true" or "false". + At the moment only supported for some styles, see above. +show_doubles_receiving=BOOLEAN Underline the receiving player in doubles. + BOOLEAN can be either "true" or "false". + At the moment only supported for some styles, see above. +team_colors=BOOLEAN Use team colors (if applicable in the given style). + BOOLEAN can be either "true" or "false". +d_c0=COLOR, This group of options can be used to set displaymode colors. +d_c1=COLOR, COLOR can be an RGB color value, for instance "0f0" or "00ff00" for lime. +d_cfg=COLOR, d_c0 and d_c1 are the colors for the home and away team, +d_cbg=COLOR, d_cfg the foreground and d_bg the background color, +d_cbg2=COLOR, ... and there are a lot more color names available. + Different styles use different color names. + Hover over a color input in the displaymode or referee UI to get the color names. +hub_url=WSURL WebSocket URL (starting with ws:// or wss://) of the referee mode hub. diff --git a/icons/Ball_DM_Cloppenburg.svg b/icons/Ball_DM_Cloppenburg.svg new file mode 100644 index 00000000..4be2d3c7 --- /dev/null +++ b/icons/Ball_DM_Cloppenburg.svg @@ -0,0 +1,220 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + diff --git a/icons/Ball_DM_Cloppenburg_nur_Hintergund.svg b/icons/Ball_DM_Cloppenburg_nur_Hintergund.svg new file mode 100644 index 00000000..0770562c --- /dev/null +++ b/icons/Ball_DM_Cloppenburg_nur_Hintergund.svg @@ -0,0 +1,208 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + diff --git a/icons/Ball_DM_Cloppenburg_ohne_Hintergund.svg b/icons/Ball_DM_Cloppenburg_ohne_Hintergund.svg new file mode 100644 index 00000000..bcba6774 --- /dev/null +++ b/icons/Ball_DM_Cloppenburg_ohne_Hintergund.svg @@ -0,0 +1,214 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + diff --git a/icons/Ball_DM_Cloppenburg_schwarz.svg b/icons/Ball_DM_Cloppenburg_schwarz.svg new file mode 100644 index 00000000..478a8c5b --- /dev/null +++ b/icons/Ball_DM_Cloppenburg_schwarz.svg @@ -0,0 +1,134 @@ + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + diff --git a/icons/DBM_Schriftzug_mit_73_wiess.svg b/icons/DBM_Schriftzug_mit_73_wiess.svg new file mode 100644 index 00000000..8ed59413 --- /dev/null +++ b/icons/DBM_Schriftzug_mit_73_wiess.svg @@ -0,0 +1,103 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + diff --git a/js/btsh.js b/js/btsh.js index bfdc60c7..444b69ea 100644 --- a/js/btsh.js +++ b/js/btsh.js @@ -2,203 +2,381 @@ // BTS support (https://github.com/phihag/bts/) via HTTP function btsh(baseurl, tournament_key) { -var battery; -if (!battery && (typeof navigator != 'undefined') && navigator.getBattery) { - navigator.getBattery().then(function(bat) { - battery = bat; - }); -} + var ws = null; + var WS_PATH = '/ws/bup'; + var reconnect_timeout = 1000; + var bts_update_callback = null; + var bts_update_courts_callback = null; + var display_initialized = false; + var battery; + -function _bat_status() { - if (!battery) { - return undefined; + if (!battery && (typeof navigator != 'undefined') && navigator.getBattery) { + navigator.getBattery().then(function(bat) { + battery = bat; + }); } - return { - charging: battery.charging, - level: battery.level, - chargingTime: battery.chargingTime, - dischargingTime: battery.dischargingTime, - }; -} -function _device_data() { - return { - id: refmode_client_ui.get_node_id(), - battery: _bat_status(), - court: state.settings.court_id, - }; -} - -function _request_json(s, component, options, cb) { - options.dataType = 'text'; - options.timeout = s.settings.network_timeout; - network.$request(component, options).done(function(res_json) { - try { - var res = JSON.parse(res_json); - } catch (e) { - return cb(e); + function _bat_status() { + if (!battery) { + return undefined; } + return { + charging: battery.charging, + level: battery.level, + chargingTime: battery.chargingTime, + dischargingTime: battery.dischargingTime, + }; + } - if (res.status !== 'ok') { - return cb({msg: res.message + ' ' + s._('network:error:status', {status: res.status})}); - } + function _device_data() { + return { + id: refmode_client_ui.get_node_id(), + battery: _bat_status(), + court: state.settings.court_id, + }; + } - return cb(null, res); - }).fail(function (xhr) { - var msg = ((xhr.status === 0) ? - s._('network:error:bts') : - s._('network:error:http', {code: xhr.status}) - ); - return cb({ - type: 'network-error', - status: xhr.status, - msg: msg, - }); - }); -} + function _request_json(s, component, options, cb) { + options.dataType = 'text'; + options.timeout = s.settings.network_timeout; + network.$request(component, options).done(function(res_json) { + try { + var res = JSON.parse(res_json); + } catch (e) { + return cb(e); + } -function send_score(s) { - if (s.settings.court_id === 'referee') { - network.errstate('btsh.score', null); - return; - } - if (! /^bts_/.test(s.setup.match_id)) { - return; - } - var req_match_id = s.setup.match_id; - var match_id = req_match_id.substring('bts_'.length); - - var netscore = calc.netscore(s, true); - var duration_ms = (s.metadata.start && s.metadata.end) ? (s.metadata.end - s.metadata.start) : null; - var end_ts = s.metadata.end ? s.metadata.end : null; - var post_data = { - court_id: s.settings.court_id, - network_team1_serving: s.game.team1_serving, - network_teams_player1_even: s.game.teams_player1_even, - network_score: netscore, - team1_won: s.match.team1_won, - presses: s.presses, - duration_ms: duration_ms, - end_ts: end_ts, - marks: s.match.marks, - shuttle_count: s.match.shuttle_count, - device: _device_data(s, post_data), - }; + if (res.status !== 'ok') { + return cb({msg: res.message + ' ' + s._('network:error:status', {status: res.status})}); + } - var url = baseurl + 'h/' + encodeURIComponent(tournament_key) + '/m/' + encodeURIComponent(match_id) + '/score'; + return cb(null, res); + }).fail(function (xhr) { + var msg = ((xhr.status === 0) ? + s._('network:error:bts') : + s._('network:error:http', {code: xhr.status}) + ); + return cb({ + type: 'network-error', + status: xhr.status, + msg: msg, + }); + }); + } - _request_json(s, 'btsh.score', { - method: 'POST', - url: url, - data: JSON.stringify(post_data), - contentType: 'application/json; charset=utf-8', - }, function(err) { - if (s.setup.match_id !== req_match_id) { // Match changed while the request was underway + function send_score(s) { + if (s.settings.court_id === 'referee') { + network.errstate('btsh.score', null); + return; + } + if (! /^bts_/.test(s.setup.match_id)) { return; } + const req_match_id = s.setup.match_id; + const match_id = req_match_id.substring('bts_'.length); - network.errstate('btsh.score', err); - }); -} + const netscore = calc.netscore(s, true); + const duration_ms = (s.metadata.start && s.metadata.end) ? (s.metadata.end - s.metadata.start) : null; + const end_ts = s.metadata.end ? s.metadata.end : null; + const score_data = { + court_id: s.settings.court_id, + match_id: match_id, + network_team1_serving: s.game.team1_serving, + network_teams_player1_even: s.game.teams_player1_even, + network_score: netscore, + team1_won: s.match.team1_won, + finish_confirmed: s.match.finish_confirmed, + presses: s.presses, + duration_ms: duration_ms, + end_ts: end_ts, + marks: s.match.marks, + shuttle_count: s.match.shuttle_count, + device: _device_data(), + }; + send_score_changed(score_data); + } -function sync(s) { - send_score(s); -} + function sync(s) { + send_score(s); + } -/* s, press */ -function send_press(s) { - sync(s); -} + /* s, press */ + function send_press(s) { + sync(s); + } -function list_matches(s, cb) { - var court_id = ''; - if ((s.ui && s.ui.displaymode_visible)) { - var style = s.settings.displaymode_style; - if (displaymode.option_applies(style, 'court_id') && (style != '2court')) { - court_id = s.settings.displaymode_court_id; + function fetch_courts(s, callback) { + bts_update_courts_callback = callback; + connect(); + if (s.btsh_courts && s.btsh_courts != null){ + callback(null, s.btsh_courts); } - } else { - court_id = s.settings.court_id; } - var filter = court_id ? ('court=' + encodeURIComponent(court_id)) : ''; - var device_url = '&device=' + encodeURIComponent(btoa(JSON.stringify(_device_data(s)))); - _request_json(s, 'btsh.list', { - url: baseurl + 'h/' + encodeURIComponent(tournament_key) + '/matches?' + filter + device_url, - }, function(err, answer) { - if (err) { - return cb(err); + function ui_init() { + if (!baseurl) { + baseurl = '../'; + } + var m = window.location.pathname.match(/^(.*\/)bup\/(?:bup\.html|index\.html)?$/); + if (m) { + baseurl = m[1]; } - var ev = answer.event; - eventutils.annotate(s, ev); + click.qs('.settings_send_export', function (e) { + e.preventDefault(); + persist_display_settings(); + }); + click.qs('.settings_reset_export', function (e) { + e.preventDefault(); + reset_display_settings(); + }); + } - return cb(null, ev); - }); -} + async function persist_display_settings() { + ws_send({ type: 'persist_display_settings', tournament_key: tournament_key, panel_settings: state.settings }); + } + + async function reset_display_settings() { + ws_send({ type: 'reset_display_settings', tournament_key: tournament_key, panel_settings: state.settings }); + } + + async function send_device_info() { + ws_send({ type: 'device_info', tournament_key: tournament_key, device: _device_data() }); + setTimeout(send_device_info, 1000*60*5); + } + async function send_score_changed(score) { + network.errstate('btsh.score', null); + ws_send({ type: 'score_update', tournament_key: tournament_key, score: score }); + } -function fetch_courts(s, callback) { - var device_url = '?device=' + encodeURIComponent(btoa(JSON.stringify(_device_data(s)))); - _request_json(s, 'btsh.courts', { - url: baseurl + 'h/' + encodeURIComponent(tournament_key) + '/courts' + device_url, - }, function(err, response) { - if (err) { - return callback(err); + async function send_command_done(command) { + ws_send({ type: 'command_done', tournament_key: tournament_key, wait_for_command: command}) + } + + function confirm_match_finished() { + if (state.match && (state.match.team1_won != null) && state.metadata.end && state.metadata.end != null){ + control.post_match_confirm(state); + } + } + + async function ws_send(json) { + if (ws == null) { + connect(); } + ws.sendmsg(json); + } + + function service_name() { + return 'BTSh'; + } + + function editable(/*s*/) { + return false; + } + + function courts(s) { + return s.btsh_courts; + } - var courts = response.courts.map(function(rc) { - var res = { - id: rc._id, - label: rc.num, - }; - if (rc.match_id) { - res.match_id = 'bts_' + rc.match_id; + function connect() { + try { + if (ws == null) { + ws = new WebSocket(construct_url(WS_PATH), 'bts-bup'); + ws.sendmsg = ws_sendmsg; + ws.onopen = function () { + reload_match_information(); + send_device_info(); + match_storage.remove_all(12); + }; + ws.onmessage = handle_message; + ws.onclose = function () { + ws = null; + send_bts_not_reachable(); + setTimeout(connect, reconnect_timeout); + }; } - return res; - }); - courts.push({ - id: 'referee', - description: s._('court:referee'), + } catch (e) { + ws = null; + send_bts_not_reachable(); + setTimeout(connect, reconnect_timeout); + } + } + function construct_url(abspath) { + var l = window.location; + return ( + ((l.protocol === 'https:') ? 'wss://' : 'ws://') + + l.hostname + + (((l.port !== 80) && (l.port !== 443)) ? ':' + l.port : '') + + abspath + ); + } + + async function ws_sendmsg(msg) { + + waitForSocketConnection(ws, () => { + const msg_json = JSON.stringify(msg); + ws.send(msg_json); }); - s.btsh_courts = courts; - return callback(err, courts); - }); -} -function ui_init() { - if (!baseurl) { - baseurl = '../'; } - var m = window.location.pathname.match(/^(.*\/)bup\/(?:bup\.html|index\.html)?$/); - if (m) { - baseurl = m[1]; + + // Make the function wait until the connection is made... + function waitForSocketConnection(socket, callback){ + setTimeout( + function () { + if (socket.readyState === 1) { + if (callback != null){ + callback(); + } + } else { + waitForSocketConnection(socket, callback); + } + + }, 5); // wait 5 milisecond for the connection... + } + + function handle_message(ws_msg) { + var msg_json = ws_msg.data; + var msg = JSON.parse(msg_json); + if (!msg) { + send({ + type: 'error', + message: 'Could not parse message', + }); + } + switch (msg.type) { + case 'change': + default_change_handler(msg); + break; + case 'error': + network.errstate('btsh.score', msg); + break; + default: + send({ + type: 'error', + rid: msg.rid, + message: 'Unsupported message ' + msg.type, + }); + } } -} -function service_name() { - return 'BTSh'; -} + function default_change_handler(c) { + switch (c.ctype) { + case 'score-update': + if (bts_update_callback != null) { + bts_update_callback(null, state, c.val.event); + if (state.settings.court_id != '' && c.val.event.matches[0] && c.val.event.matches[0].end_ts != null) { + setTimeout(reload_match_information, 60000); + } + } else { + if (state && state != null) { + state.bts_event = c.val.event; + } + } + break; + case 'settings-update': + state.settings = c.val; + state.dads = c.val.advertisements; + settings.update(state); + settings.on_mode_change(state); + break; + case 'confirm-match-finished': + confirm_match_finished(); + break; + case 'advertisement_add': + state.dads.push(c.val) + break; + case 'advertisement_remove': + if (state.dads) { + const changed_t = utils.find(state.dads, m => m._id === c.val.advertisement_id); + if (changed_t) { + state.dads.splice(state.dads.indexOf(changed_t), 1); + } + } + break; + case 'courts-update': + var courts = c.val.map(function (rc) { + var res = { + id: rc._id, + label: rc.num, + }; + if (rc.match_id) { + res.match_id = 'bts_' + rc.match_id; + } + send_command_done(c); + return res; + }); + courts.push({ + id: 'referee', + description: state._('court:referee'), + }); -function editable(/*s*/) { - return false; -} + state.btsh_courts = courts; + if (bts_update_courts_callback && bts_update_courts_callback != null) { + bts_update_courts_callback(null, state.btsh_courts); + } + if(state.settings.devicemode == "umpire") { + settings.show(); + settings.on_mode_change(state); + } else { + settings.hide_displaymode(); + } + break; + default: + break; + } + send_command_done(c); + } -function courts(s) { - return s.btsh_courts; -} + function reload_match_information() { + if (state.ui.displaymode_visible) { + var style = state.settings.displaymode_style; + if (displaymode.option_applies(style, 'court_id') && (style != '2court')) { + state.settings.court_id = state.settings.displaymode_court_id; + } else { + state.settings.court_id = '' + } + + } else { + state.settings.court_id = state.settings.displaymode_court_id; + } + ws.sendmsg({ type: 'init', initialize_display: !display_initialized, tournament_key: tournament_key, panel_settings: state.settings }); + display_initialized = true; + } + + function subscribe(s, cb, calc_timeout) { + bts_update_callback = cb; + if (state && state.bts_event && state.bts_event != null) { + bts_update_callback(null, state, state.bts_event); + state.bts_event = null; + } + connect(); + } -return { - ui_init: ui_init, - send_press: send_press, - list_matches: list_matches, - sync: sync, - courts: courts, - fetch_courts: fetch_courts, - service_name: service_name, - editable: editable, - limited_ui: true, -}; + function send_bts_not_reachable() { + if (bts_update_callback && bts_update_callback != null) { + var msg = state._('network:error:bts'); + bts_update_callback({ + type: 'network-error', + msg: msg, + }, state, null); + } + } + return { + ui_init: ui_init, + send_press: send_press, + sync: sync, + courts: courts, + fetch_courts: fetch_courts, + service_name: service_name, + editable: editable, + limited_ui: true, + push_service: true, + subscribe: subscribe, + reload_match_information: reload_match_information, + }; } /*@DEV*/ @@ -208,6 +386,7 @@ if ((typeof module !== 'undefined') && (typeof require !== 'undefined')) { var eventutils = require('./eventutils'); var network = require('./network'); var refmode_client_ui = require('./refmode_client_ui'); + var click = require('./click'); module.exports = btsh; } diff --git a/js/calc.js b/js/calc.js index db6b1be9..aee83fea 100644 --- a/js/calc.js +++ b/js/calc.js @@ -70,11 +70,38 @@ function warmup_timer(s, cointoss_ts) { switch (s.setup.warmup) { case 'none': return false; + case 'call-down': + // This case covers a feature of BTS in which the warm-up countdown starts the moment + // the game is called. The time can be set in BTS. + return { + start: s.setup.called_timestamp, + duration: s.setup.warmup_start * 1000, + exigent: Math.max(0, s.setup.warmup_start-s.setup.warmup_ready) * 1000 + 499, + restart: false, + }; + case 'call-up': + // This case covers a feature of BTS in which the warm-up timer starts the moment + // the game is called. The timer runs upward until the Game starts. + return { + start: s.setup.called_timestamp, + upwards: true, + restart: false, + }; + case 'choise': + // This case covers a feature of BTS in which the warm-up timer starts the moment + // of the cointoss (first tablet interaction of the umpire). The time can be set in BTS. + return { + start: cointoss_ts, + duration: s.setup.warmup_start * 1000, + exigent: Math.max(0, s.setup.warmup_start-s.setup.warmup_ready) * 1000 + 499, + restart: true, + }; case 'bwf-2016': return { start: cointoss_ts, duration: 120000, exigent: 30499, + restart: true, }; case 'legacy': default: @@ -82,6 +109,7 @@ function warmup_timer(s, cointoss_ts) { start: cointoss_ts, duration: 120000, exigent: 5499, + restart: true, }; } } @@ -318,6 +346,24 @@ function init_state(s, setup, presses, keep_metadata) { s.initialized = true; s.presses = presses ? presses : []; s.timer = false; + + // These calls ensure that the timers are started before the first tablet interaction. + // This is only necessary for warm-up timers that are not started by the referee. + if (s.setup.warmup === 'call-down') { + s.timer = { + start: s.setup.called_timestamp, + duration: s.setup.warmup_start * 1000, + exigent: Math.max(0, s.setup.warmup_start-s.setup.warmup_ready) * 1000 + 499, + restart: false, + }; + } else if (s.setup.warmup === 'call-up') { + s.timer = { + start: s.setup.called_timestamp, + upwards: true, + restart: false, + }; + } + s.remote = {}; delete s.match; @@ -447,6 +493,7 @@ function recalc_after_score(s, team_id, press) { start: press.timestamp, duration: (counting === '5x11_15^90' ? 90000 : 60000), exigent: 25000, + restart: true, }; } if ((press.type != 'red-card') || is_interval) { @@ -481,6 +528,7 @@ function recalc_after_score(s, team_id, press) { start: press.timestamp, duration: rest_duration, exigent: 25000, + restart: true, }; } else if (!s.game.interval && !s.match.suspended && !s.match.injuries) { if (press.type !== 'red-card') { @@ -810,6 +858,7 @@ function calc_press(s, press) { start: press.timestamp, duration: s.timer.duration, exigent: s.timer.exigent, + restart: s.timer.restart, }; } break; @@ -1072,22 +1121,9 @@ function netscore(s, always_zero) { if (s.match.finished && !s.match.won_by_score) { if (scores.length > 0) { var last_score = scores[scores.length - 1]; - if (game_winner(counting, scores.length - 1, last_score[0], last_score[1]) === 'inprogress') { - _finish_score(scores.length - 1, last_score, s.match.team1_won); - } } var max_games = max_game_count(counting); - while (scores.length < max_games) { - var mwinner = match_winner(counting, scores); - if ((mwinner == 'left') || (mwinner == 'right')) { - break; - } - - var new_score = [0, 0]; - _finish_score(scores.length, new_score, s.match.team1_won); - scores.push(new_score); - } } return scores; diff --git a/js/control.js b/js/control.js index 0ec98e0d..90fb865e 100644 --- a/js/control.js +++ b/js/control.js @@ -117,7 +117,7 @@ function ask_leave_match(s) { return; } - if (network.score_transmitted()) { + if (s.match.finish_confirmed || network.score_transmitted()) { leave_match(s); return; } @@ -261,14 +261,7 @@ function ui_init() { type: 'postinterval-confirm', }); }); - click.qs('#postmatch-confirm', function() { - if (! state.match.finish_confirmed) { - on_press({ - type: 'postmatch-confirm', - }); - } - ask_leave_match(state); - }); + click.qs('#postmatch-confirm', post_match_confirm); click.qs('#postmatch-leave', function() { ask_leave_match(state); }); @@ -394,6 +387,14 @@ function ui_init() { } +function post_match_confirm(){ + if (!state.match.finish_confirmed) { + on_press({ + type: 'postmatch-confirm', + }); + } + ask_leave_match(state); +} function set_current(s) { buphistory.record(s); @@ -443,6 +444,7 @@ return { hide_exception_dialog: hide_exception_dialog, install_destructor: install_destructor, on_press: on_press, + post_match_confirm: post_match_confirm, resume_match: resume_match, set_current: set_current, start_match: start_match, diff --git a/js/court.js b/js/court.js index 5025ca96..7e5c5743 100644 --- a/js/court.js +++ b/js/court.js @@ -105,6 +105,23 @@ function render(s, cui) { if (umpire_name && s.setup.service_judge_name) { umpire_name += ' / ' + s.setup.service_judge_name; } + + if (s.setup.tabletoperators && s.setup.tabletoperators.length > 0) { + var _tabletoperator_str = function (tabletoperators) { + if (tabletoperators.length === 0) { + return 'N.N'; + } else if (tabletoperators.length == 1) { + return tabletoperators[0].name; + } else { + return tabletoperators[0].name + ' / ' + tabletoperators[1].name; + } + }; + if (umpire_name.length > 0) { + umpire_name += ' / '; + } + umpire_name += _tabletoperator_str(s.setup.tabletoperators); + } + uiu.text(cui.umpire_name, umpire_name); uiu.visible(cui.umpire_name, (cdata.left_serving === null)); diff --git a/js/displaymode.js b/js/displaymode.js index b8b424a4..31f60177 100644 --- a/js/displaymode.js +++ b/js/displaymode.js @@ -7,6 +7,8 @@ var ALL_STYLES = [ 'bwf', 'clean', 'teamcourt', + 'tournamentcourt', + 'tournamentplayers', 'stripes', '2court', 'greyish', @@ -22,14 +24,18 @@ var ALL_STYLES = [ 'castall', 'stream', 'streamcourt', + 'streamcourt_dm', 'streamteam', 'tournament_overview', + 'tournament_overview_dm', + 'tournament_overview_dm_finals', 'andre', ]; var ALL_COLORS = [ 'c0', 'c1', 'cb0', 'cb1', 'cbg', 'cbg2', 'cbg3', 'cbg4', - 'cfg', 'cfg2', 'cfg3', 'cfg3', 'cfgdark', + 'cfg', 'cfg2', 'cfg3', 'cfg4', 'cfgdark', + 'cexp', 'ct', // transparent 'cborder', 'cserv', 'cserv2', 'crecv', @@ -65,7 +71,10 @@ function _setup_autosize(el, right_node, determine_height) { if (right_node) { var prect = parent_node.getBoundingClientRect(); var rrect = right_node.getBoundingClientRect(); - w = Math.max(10, Math.min(w, rrect.left - prect.left)); + + // The -20 at the end of the formula is a fixed value. Without the fix, the calculation + // sometimes resulted in the strings going just beyond the end of the planned range. + w = Math.max(10, Math.min(w, rrect.left - prect.left-20)); } var h; @@ -196,6 +205,11 @@ function hash(settings, event) { court_id: settings.displaymode_court_id, reverse_order: settings.displaymode_reverse_order, show_pause: settings.d_show_pause, + show_court_number: settings.d_show_court_number, + show_competition: settings.d_show_competition, + show_round: settings.d_show_round, + show_middle_name: settings.d_show_middle_name, + show_doubles_receiving: settings.d_show_doubles_receiving, team_colors: settings.d_team_colors, courts: utils.deep_copy(event.courts), matches: utils.deep_copy(event.matches), @@ -246,6 +260,50 @@ function determine_server(match, current_score) { }; } +function determine_receiver(match, current_score) { + var team_id; + if (typeof match.network_team1_serving === 'boolean') { + team_id = match.network_team1_serving ? 1 : 0; // welches Team hat den ersten Aufschlag angenommen? + } + if (team_id === undefined) return {}; + if (!match.network_teams_player1_even) { + return { + team_id: team_id, + }; // This ensures that server.player_id is undefined + } + + var player_id = 0; + if (match.setup.is_doubles) { + var p0even = match.network_teams_player1_even[team_id]; //Welcher spieler des Teams stand bei der Annahme bei 0 Rechts? + if (p0even === null) { + // only team known + return { + team_id: team_id, + }; + } + player_id = (p0even == (current_score[(team_id + 1) % 2] % 2 === 0)) ? 0 : 1; + } + + // Network score only, but at end of game? + // (the positions of players may be relayed, but should not be shown) + var netscore = match.network_score; + if (netscore && netscore.length > 0) { + var game_idx = netscore.length - 1; + var last_game = netscore[game_idx]; + var gwinner = calc.game_winner(match.setup.counting, game_idx - 1, last_game[0], last_game[1]); + if (gwinner !== 'inprogress') { + return { + team_id: team_id, + }; + } + } + + return { + team_id: team_id, + player_id: player_id, + }; +} + function _match_by_court(event, court) { return court.match_id ? utils.find(event.matches, function(m) { return court.match_id === m.setup.match_id; @@ -427,9 +485,6 @@ function render_tournament_overview(s, container, event) { var max_game_count = _calc_max_games(event); var colors = calc_colors(s.settings, event); - var table = uiu.el(container, 'table', 'd_to_table'); - var tbody = uiu.el(table, 'tbody'); - event.courts.forEach(function(court, idx) { var match = _match_by_court(event, court); var nscore = (match ? match.network_score : 0) || []; @@ -479,6 +534,659 @@ function render_tournament_overview(s, container, event) { }); } +function _tournament_overview_dm_render_players(tr, players) { + var td = uiu.el(tr, 'td', 'd_to_team'); + uiu.el(td, 'span', {}, namestr(players)); +} + +function render_tournament_overview_dm(s, container, event) { + var max_game_count = _calc_max_games(event); + var colors = calc_colors(s.settings, event); + + var background = uiu.el(container, 'div', { + style: ( + 'position:absolute;top:0vh;left:0vh;' + + 'height:100vh;width:100vw;' + + 'background-color: #000000;' + + 'z-index:10;' + ), + }); + + event.courts.forEach(function(court, idx) { + var match = _match_by_court(event, court); + var nscore = (match ? match.network_score : 0) || []; + + var counting = match ? match.setup.counting : eventutils.default_counting(event.league_key); + var max_games = counting ? calc.max_game_count(counting) : 0; + var current_score = (nscore.length > 0) ? nscore[nscore.length - 1] : ['', '']; + var server = match ? determine_server(match, current_score) : {}; + + var court_el = uiu.el(background, 'div', { + style: ( + 'position:absolute;top:'+idx*20+'vh;left:0vh;' + + 'height:20vh;width:100vw;' + ), + }); + +/* + var logo = uiu.el(court_el, 'div', { + style: ( + 'position:absolute;top:1vh;left:2vh;' + + 'height:18vh;width:17.6vh;' + + 'z-index:10;'+ + 'font-size: 15vh;'+ + 'color: #000;'+ + 'display: flex;'+ + 'justify-content: center;'+ + 'align-items: center;'+ + 'font-weight: bold;'+ + 'font-style: oblique;' + ), + }, idx+1);*/ + var top_bar = uiu.el(court_el, 'div', { + style: ( + 'position:absolute;top:2vh;left:2vw;' + + 'height:16vh;' + + 'z-index:-1;' + + 'display: flex;' + + 'flex-direction: row;' + ), + }); + + + var top_bar_court = uiu.el(top_bar, 'div', { + style: ( + 'position:static;' + + 'height:16vh;width:10vw;' + + 'display: flex;' + + 'flex-direction: column;' + + 'justify-content: space-between;' + ), + }); + + uiu.el(top_bar_court, 'div', { + style: ( + 'position:static;' + + 'height:5%;width:100%;' + + 'background-color: #ffffff;' + + 'border-top-right-radius: 1vh;'+ + 'border-top-left-radius: 1vh;' + ), + }); + + var court_number = uiu.el(top_bar_court, 'div', { + style: ( + 'position:static;' + + 'height:90%;width:100%;' + + 'background-color: #ffffffbb;' + + 'text-align: center;' + ), + }); + + uiu.el(court_number, 'div', { + style: ( + 'font-size: 12.5vh;'+ + 'height: 100%;' + + 'font-weight: bold;' + + 'font-style: oblique;' + ), + },idx+1); + + uiu.el(top_bar_court, 'div', { + style: ( + 'position:static;' + + 'height:5%;width:100%;' + + 'background-color: #ffffff;' + + 'border-bottom-right-radius: 1vh;'+ + 'border-bottom-left-radius: 1vh;' + ), + }); + + + + var top_bar_left = uiu.el(top_bar, 'div', { + style: ( + 'position:static;' + + 'height:16vh;width:70vw;' + + 'display: flex;' + + 'flex-direction: column;' + + 'justify-content: space-between;' + + 'margin-left: 0.5vh;' + ), + }); + + var border_top = uiu.el(top_bar_left, 'div', { + style: ( + 'position:static;' + + 'height:5%;width:100%;' + + 'background-color: #ffffff;' + + 'border-top-right-radius: 1vh;'+ + 'border-top-left-radius: 1vh;' + ), + }); + + var teams = []; + + teams.push(uiu.el(top_bar_left, 'div', { + style: ( + 'position:static;' + + 'height:43%;width:100%;' + + 'background-color: #ffffffbb;' + + 'display: flex;' + + 'justify-content: space-between;' + ), + })); + + var border_middle = uiu.el(top_bar_left, 'div', { + style: ( + 'position:static;' + + 'height:4%;width:100%;' + ), + }); + + + teams.push(uiu.el(top_bar_left, 'div', { + style: ( + 'position:static;' + + 'height:43%;width:100%;' + + 'background-color: #ffffffbb;' + + 'display: flex;' + + 'justify-content: space-between;' + ), + })); + + var border_bottom = uiu.el(top_bar_left, 'div', { + style: ( + 'position:static;' + + 'height:5%;width:100%;' + + 'background-color: #ffffff;' + + 'border-bottom-left-radius: 1vh;'+ + 'border-bottom-right-radius: 1vh;' + ), + }); + + var team_service = []; + for (var team_idx = 0;team_idx < 2;team_idx++) { + var team_name = uiu.el(teams[team_idx], 'div', { + style: ( + 'margin-left:1vh;' + + 'font-size:6.0vh;' + + 'height: 100%;' + + 'align-content: center;' + + 'width: fit-content;' + + 'font-weight: bold;' + ) + }, + match ? match.setup.teams[team_idx].players[0].name +(match.setup.teams[team_idx].players.length > 1 ? ' / ' + match.setup.teams[team_idx].players[1].name : '') : ''); + + let service = uiu.el(teams[team_idx], 'div', { + style: ( + 'height: 100%;' + + 'align-content: center;' + + 'width: 6.5vh;' + + 'background-repeat: no-repeat;' + + 'background-position:center;' + + 'background-size:contain;' + + 'background-image:url("icons/Ball_DM_Cloppenburg_schwarz.svg");' + )}); + + service.style.visibility = "hidden"; + + team_service.push(service); + + } + + var sets = []; + + var team_serving = -1; + + // for (var game_idx = 0;game_idx < max_games;game_idx++) { + for (var game_idx = 0;game_idx < 3;game_idx++) { + if (nscore.length > game_idx) { + for (var team_idx = 0;team_idx < 2;team_idx++) { + var gwinner = calc.game_winner( + match.setup.counting, game_idx, nscore[game_idx][0], nscore[game_idx][1]); + var is_team_serving = ( + (gwinner === 'left') ? (team_idx === 0) : ( + (gwinner === 'right') ? (team_idx === 1) : ( + (server.team_id === team_idx)))); + + if(is_team_serving) { + team_serving = team_idx; + } + } + } + + var top_bar_set = uiu.el(top_bar, 'div', { + style: ( + 'position:static;' + + 'height:16vh;width:9vh;' + + 'display: flex;' + + 'flex-direction: column;' + + 'justify-content: space-between;' + + 'margin-left: 0.5vh;' + ), + }); + + uiu.el(top_bar_set, 'div', { + style: ( + 'position:static;' + + 'height:5%;width:100%;' + + 'background-color: #ffffff;' + + 'border-top-left-radius: 1vh;' + + 'border-top-right-radius: 1vh;' + ), + }); + + let set = []; + set.push(uiu.el(top_bar_set, 'div', { + style: ( + 'position:static;' + + 'height:43%;width:100%;' + + 'background-color: #ffffffbb;' + + 'display: flex;' + + 'justify-content: center;' + + 'font-size:7vh;' + + 'align-items: center;'+ + 'font-weight: bold;' + ), + }, game_idx < nscore.length ? nscore[game_idx][0] : '')); + + uiu.el(top_bar_set, 'div', { + style: ( + 'position:static;' + + 'height:4%;width:100%;' + ), + }); + + set.push(uiu.el(top_bar_set, 'div', { + style: ( + 'position:static;' + + 'height:43%;width:100%;' + + 'background-color: #ffffffbb;' + + 'display: flex;' + + 'justify-content: center;' + + 'font-size:7vh;' + + 'align-items: center;'+ + 'font-weight: bold;' + ), + }, game_idx < nscore.length ? nscore[game_idx][1] : '')); + + uiu.el(top_bar_set, 'div', { + style: ( + 'position:static;' + + 'height:5%;width:100%;' + + 'background-color: #ffffff;' + + 'border-bottom-left-radius: 1vh;' + + 'border-bottom-right-radius: 1vh;' + ), + }); + + sets.push(set); + } + + if(team_serving >= 0) { + team_service[team_serving].style.visibility = 'visible'; + } + + }); +} + + function render_tournament_overview_dm_finals(s, container, event) { + var max_game_count = _calc_max_games(event); + var colors = calc_colors(s.settings, event); + + var background = uiu.el(container, 'div', { + style: ( + 'position:absolute;top:0vh;left:0vh;' + + 'height:100vh;width:100vw;' + + 'background-color: #000000;' + + 'z-index:10;' + ), + }); + + var courts = [1, 0, 2]; + var duration = -1; + courts.forEach(function (id , idx) { + var match = _match_by_court(event, event.courts[id]); + if (match != null) { + if (match.presses_json && match.presses_json != null) { + + + var presses = JSON.parse(match.presses_json); + const foundpress = presses.find(press => press.type === "love-all"); + if (foundpress && foundpress != null) { + var start = foundpress.timestamp; + duration = Math.floor((Date.now() - start) / 1000/60); + } + + + } + } + var nscore = (match ? match.network_score : 0) || []; + + var counting = match ? match.setup.counting : eventutils.default_counting(event.league_key); + var max_games = counting ? calc.max_game_count(counting) : 0; + var current_score = (nscore.length > 0) ? nscore[nscore.length - 1] : ['', '']; + var server = match ? determine_server(match, current_score) : {}; + + var court_el = uiu.el(background, 'div', { + style: ( + 'position:absolute;top:' + idx * 33 + 'vh;left:0vh;' + + 'height:20vh;width:100vw;' + ), + }); + + /* + var logo = uiu.el(court_el, 'div', { + style: ( + 'position:absolute;top:1vh;left:2vh;' + + 'height:18vh;width:17.6vh;' + + 'z-index:10;'+ + 'font-size: 15vh;'+ + 'color: #000;'+ + 'display: flex;'+ + 'justify-content: center;'+ + 'align-items: center;'+ + 'font-weight: bold;'+ + 'font-style: oblique;' + ), + }, idx+1);*/ + var top_bar = uiu.el(court_el, 'div', { + style: ( + 'position:absolute;top:2vh;left:2vw;' + + 'height:16vh;' + + 'z-index:-1;' + + 'display: flex;' + + 'flex-direction: row;' + ), + }); + + + var top_bar_court = uiu.el(top_bar, 'div', { + style: ( + 'position:static;' + + 'height:30vh;width:18vw;' + + 'display: flex;' + + 'flex-direction: column;' + + 'justify-content: space-between;' + ), + }); + + uiu.el(top_bar_court, 'div', { + style: ( + 'position:static;' + + 'height:3%;width:100%;' + + 'background-color: #ffffff;' + + 'border-top-right-radius: 1vh;' + + 'border-top-left-radius: 1vh;' + ), + }); + + var court_number = uiu.el(top_bar_court, 'div', { + style: ( + 'position:static;' + + 'height:94%;width:100%;' + + 'background-color: #ffffffbb;' + + 'text-align: center;' + ), + }); + + uiu.el(court_number, 'div', { + style: ( + 'font-size: 12.5vh;' + + 'font-weight: bold;' + + 'font-style: oblique;' + ), + }, id + 1); + + uiu.el(court_number, 'div', { + style: ( + 'font-size: 4vh;' + + 'font-weight: bold;' + + 'font-style: oblique;' + ), + }, (match && match.setup) ? match.setup.event_name+' - '+match.setup.match_name : ""); + + + uiu.el(court_number, 'div', { + style: ( + 'font-size: 6vh;' + + 'font-weight: bold;' + + 'font-style: oblique;' + ), + }, (duration == -1 ) ? "" : duration+"'"); + + uiu.el(top_bar_court, 'div', { + style: ( + 'position:static;' + + 'height:3%;width:100%;' + + 'background-color: #ffffff;' + + 'border-bottom-right-radius: 1vh;' + + 'border-bottom-left-radius: 1vh;' + ), + }); + + + + var top_bar_left = uiu.el(top_bar, 'div', { + style: ( + 'position:static;' + + 'height:30vh;width:55vw;' + + 'display: flex;' + + 'flex-direction: column;' + + 'justify-content: space-between;' + + 'margin-left: 0.5vh;' + ), + }); + + var border_top = uiu.el(top_bar_left, 'div', { + style: ( + 'position:static;' + + 'height:3%;width:100%;' + + 'background-color: #ffffff;' + + 'border-top-right-radius: 1vh;' + + 'border-top-left-radius: 1vh;' + ), + }); + + var teams = []; + + teams.push(uiu.el(top_bar_left, 'div', { + style: ( + 'position:static;' + + 'height:46%;width:100%;' + + 'background-color: #ffffffbb;' + + 'display: flex;' + + 'justify-content: space-between;' + ), + })); + + var border_middle = uiu.el(top_bar_left, 'div', { + style: ( + 'position:static;' + + 'height:2%;width:100%;' + ), + }); + + + teams.push(uiu.el(top_bar_left, 'div', { + style: ( + 'position:static;' + + 'height:46%;width:100%;' + + 'background-color: #ffffffbb;' + + 'display: flex;' + + 'justify-content: space-between;' + ), + })); + + var border_bottom = uiu.el(top_bar_left, 'div', { + style: ( + 'position:static;' + + 'height:3%;width:100%;' + + 'background-color: #ffffff;' + + 'border-bottom-left-radius: 1vh;' + + 'border-bottom-right-radius: 1vh;' + ), + }); + + var team_service = []; + for (var team_idx = 0; team_idx < 2; team_idx++) { + + var team_name = uiu.el(teams[team_idx], 'div', { + style: ( + 'margin-left:1vh;' + + 'height: 100%;' + + 'display: flex;' + + 'justify-content: center;' + + 'flex-direction: column;' + ) + }); + + uiu.el(team_name, 'div', { + style: ( + 'margin-left:1vh;' + + 'font-size:6.0vh;' + + 'height: 100%;' + + 'align-content: center;' + + 'width: fit-content;' + + 'font-weight: bold;' + ) + }, + match ? match.setup.teams[team_idx].players[0].name : ''); + + if (match && match.setup.teams[team_idx].players.length > 1) { + uiu.el(team_name, 'div', { + style: ( + 'margin-left:1vh;' + + 'font-size:6.0vh;' + + 'height: 100%;' + + 'align-content: center;' + + 'width: fit-content;' + + 'font-weight: bold;' + ) + }, + match.setup.teams[team_idx].players[1].name ); + } + + let service = uiu.el(teams[team_idx], 'div', { + style: ( + 'height: 100%;' + + 'align-content: center;' + + 'width: 6.5vh;' + + 'background-repeat: no-repeat;' + + 'background-position:center;' + + 'background-size:contain;' + + 'background-image:url("icons/Ball_DM_Cloppenburg_schwarz.svg");' + ) + }); + + service.style.visibility = "hidden"; + + team_service.push(service); + + } + + var sets = []; + + var team_serving = -1; + + // for (var game_idx = 0;game_idx < max_games;game_idx++) { + for (var game_idx = 0; game_idx < 3; game_idx++) { + if (nscore.length > game_idx) { + for (var team_idx = 0; team_idx < 2; team_idx++) { + var gwinner = calc.game_winner( + match.setup.counting, game_idx, nscore[game_idx][0], nscore[game_idx][1]); + var is_team_serving = ( + (gwinner === 'left') ? (team_idx === 0) : ( + (gwinner === 'right') ? (team_idx === 1) : ( + (server.team_id === team_idx)))); + + if (is_team_serving) { + team_serving = team_idx; + } + } + } + + var top_bar_set = uiu.el(top_bar, 'div', { + style: ( + 'position:static;' + + 'height:30vh;width:8vw;' + + 'display: flex;' + + 'flex-direction: column;' + + 'justify-content: space-between;' + + 'margin-left: 0.5vh;' + ), + }); + + uiu.el(top_bar_set, 'div', { + style: ( + 'position:static;' + + 'height:3%;width:100%;' + + 'background-color: #ffffff;' + + 'border-top-left-radius: 1vh;' + + 'border-top-right-radius: 1vh;' + ), + }); + + let set = []; + set.push(uiu.el(top_bar_set, 'div', { + style: ( + 'position:static;' + + 'height:46%;width:100%;' + + 'background-color: #ffffffbb;' + + 'display: flex;' + + 'justify-content: center;' + + 'font-size:10vh;' + + 'align-items: center;' + + 'font-weight: bold;' + ), + }, game_idx < nscore.length ? nscore[game_idx][0] : '')); + + uiu.el(top_bar_set, 'div', { + style: ( + 'position:static;' + + 'height:2%;width:100%;' + ), + }); + + set.push(uiu.el(top_bar_set, 'div', { + style: ( + 'position:static;' + + 'height:46%;width:100%;' + + 'background-color: #ffffffbb;' + + 'display: flex;' + + 'justify-content: center;' + + 'font-size:10vh;' + + 'align-items: center;' + + 'font-weight: bold;' + ), + }, game_idx < nscore.length ? nscore[game_idx][1] : '')); + + uiu.el(top_bar_set, 'div', { + style: ( + 'position:static;' + + 'height:3%;width:100%;' + + 'background-color: #ffffff;' + + 'border-bottom-left-radius: 1vh;' + + 'border-bottom-right-radius: 1vh;' + ), + }); + + sets.push(set); + } + + if (team_serving >= 0) { + team_service[team_serving].style.visibility = 'visible'; + } + + }); + } + + + + + function render_castall(s, container, event, colors) { if (!event.courts) { uiu.el(container, 'div', 'error', 'Court information missing'); @@ -960,19 +1668,384 @@ function render_streamcourt(s, container, event/*, colors*/) { } } - uiu.el(tr, 'td', { - style: ( - 'width:1.2em;border-left:0.1vw solid #888;font-family:Arial Black;' + - 'padding:0 0.2em;text-align:center;background:#555;' + - 'font-size:90%;' + - 'color:' + (team_serving ? '#ee0' : '#fff') + ';' + - extra_style), - }, (game_idx < nscore.length) ? nscore[game_idx][team_idx] : ''); - } - } + uiu.el(tr, 'td', { + style: ( + 'width:1.2em;border-left:0.1vw solid #888;font-family:Arial Black;' + + 'padding:0 0.2em;text-align:center;background:#555;' + + 'font-size:90%;' + + 'color:' + (team_serving ? '#ee0' : '#fff') + ';' + + extra_style), + }, (game_idx < nscore.length) ? nscore[game_idx][team_idx] : ''); + } + } +} + + +function render_streamcourt_dm(s, container, event/*, colors*/) { + if (!event.courts) { + uiu.el(container, 'div', 'error', 'Court information missing'); + return; + } + + var court = event.courts.find(function(c) { + return c.court_id == s.settings.displaymode_court_id; + }) || event.courts[0]; + var match = _match_by_court(event, court); + var counting = match ? match.setup.counting : eventutils.default_counting(event.league_key); + var max_games = counting ? calc.max_game_count(counting) : 0; + var nscore = (match ? match.network_score : 0) || []; + var current_score = (nscore.length > 0) ? nscore[nscore.length - 1] : ['', '']; + var server = match ? determine_server(match, current_score) : {}; + + + var logo = uiu.el(container, 'div', { + style: ( + 'position:absolute;top:3vh;left:4vh;' + + 'height:10vh;width:9vh;' + + 'background-repeat: no-repeat;' + + 'background-position:center;' + + 'background-size:contain;' + + 'background-image:url("icons/Ball_DM_Cloppenburg.svg");'+ + 'z-index:10;' + ), + }); + var top_bar = uiu.el(container, 'div', { + style: ( + 'position:absolute;top:3.6vh;left:6.6vh;' + + 'height:8.8vh;' + + 'z-index:-1;' + + 'display: flex;' + + 'flex-direction: row;' + + ), + }); + + var top_bar_left = uiu.el(top_bar, 'div', { + style: ( + 'position:static;' + + 'height:8.8vh;width:fit-content;' + + 'display: flex;' + + 'flex-direction: column;' + + 'justify-content: space-between;' + ), + }); + + + + + + var border_top = uiu.el(top_bar_left, 'div', { + style: ( + 'position:static;' + + 'height:5%;width:100%;' + + 'background-color: #ffffff;' + + 'border-top-right-radius: 1vh;' + ), + }); + + var teams = []; + + teams.push(uiu.el(top_bar_left, 'div', { + style: ( + 'position:static;' + + 'height:43%;width:100%;' + + 'background-color: #ffffffbb;' + + 'display: flex;' + + 'justify-content: space-between;' + ), + })); + + var border_middle = uiu.el(top_bar_left, 'div', { + style: ( + 'position:static;' + + 'height:4%;width:100%;' + ), + }); + + + teams.push(uiu.el(top_bar_left, 'div', { + style: ( + 'position:static;' + + 'height:43%;width:100%;' + + 'background-color: #ffffffbb;' + + 'display: flex;' + + 'justify-content: space-between;' + ), + })); + + var border_bottom = uiu.el(top_bar_left, 'div', { + style: ( + 'position:static;' + + 'height:5%;width:100%;' + + 'background-color: #ffffff;' + + 'border-bottom-right-radius: 1vh;' + ), + }); + + var team_service = []; + for (var team_idx = 0;team_idx < 2;team_idx++) { + var team_name = uiu.el(teams[team_idx], 'div', { + style: ( + 'margin-left:6.7vh;' + + 'font-size:3vh;' + + 'height: 100%;' + + 'align-content: center;' + + 'width: fit-content;' + ) + }, + match ? namestr_short(match.setup.teams[team_idx].players) : ''); + + let service = uiu.el(teams[team_idx], 'div', { + style: ( + 'height: 100%;' + + 'align-content: center;' + + 'width: 4vh;' + + 'background-repeat: no-repeat;' + + 'background-position:center;' + + 'background-size:contain;' + + 'background-image:url("icons/Ball_DM_Cloppenburg_schwarz.svg");' + )}); + + service.style.visibility = "hidden"; + + team_service.push(service); + + } + + var sets = []; + + var team_serving = -1; + + for (var game_idx = 0;game_idx < max_games;game_idx++) { + + if (game_idx < nscore.length) { + + for (var team_idx = 0;team_idx < 2;team_idx++) { + var gwinner = calc.game_winner( + match.setup.counting, game_idx, nscore[game_idx][0], nscore[game_idx][1]); + var is_team_serving = ( + (gwinner === 'left') ? (team_idx === 0) : ( + (gwinner === 'right') ? (team_idx === 1) : ( + (server.team_id === team_idx)))); + + if(is_team_serving) { + team_serving = team_idx; + } + } + + var top_bar_set = uiu.el(top_bar, 'div', { + style: ( + 'position:static;' + + 'height:8.8vh;width:4vh;' + + 'display: flex;' + + 'flex-direction: column;' + + 'justify-content: space-between;' + + 'margin-left: 0.3vh;' + ), + }); + + uiu.el(top_bar_set, 'div', { + style: ( + 'position:static;' + + 'height:5%;width:100%;' + + 'background-color: #ffffff;' + + 'border-top-left-radius: 1vh;' + + 'border-top-right-radius: 1vh;' + ), + }); + + let set = []; + set.push(uiu.el(top_bar_set, 'div', { + style: ( + 'position:static;' + + 'height:43%;width:100%;' + + 'background-color: #ffffffbb;' + + 'display: flex;' + + 'justify-content: center;' + + 'font-size:3vh;' + + 'align-items: center;' + ), + }, nscore[game_idx][0])); + + uiu.el(top_bar_set, 'div', { + style: ( + 'position:static;' + + 'height:4%;width:100%;' + ), + }); + + set.push(uiu.el(top_bar_set, 'div', { + style: ( + 'position:static;' + + 'height:43%;width:100%;' + + 'background-color: #ffffffbb;' + + 'display: flex;' + + 'justify-content: center;' + + 'font-size:3vh;' + + 'align-items: center;' + ), + }, nscore[game_idx][1])); + + uiu.el(top_bar_set, 'div', { + style: ( + 'position:static;' + + 'height:5%;width:100%;' + + 'background-color: #ffffff;' + + 'border-bottom-left-radius: 1vh;' + + 'border-bottom-right-radius: 1vh;' + ), + }); + + sets.push(set); + } + } + + if(team_serving >= 0) { + team_service[team_serving].style.visibility = 'visible'; + } + + var logo_dm = uiu.el(container, 'div', { + style: ( + 'position:absolute;bottom:1vh;right:2vh;' + + 'height:17.008vh;width:28.346vh;' + + 'background-repeat: no-repeat;' + + 'background-position:center;' + + 'background-size:contain;' + + 'background-image:url("icons/DBM_Schriftzug_mit_73_wiess.svg");'+ + 'z-index:10;' + ), + }); + + + var top_bar_right = uiu.el(container, 'div', { + style: ( + 'position:absolute; top: 3.6vh;left: calc(100% - 33.2vh);' + + 'height:8.8vh;' + + 'z-index:-1;' + + 'display: flex;' + + 'flex-direction: column;' + + 'color: #ffffff' + ), + }); + + uiu.el(top_bar_right, 'div', { + style: ( + 'position:static;' + + 'text-align: center;' + + 'height: 2.5vh;' + + 'width: 100%;' + + 'font-size: 2.1vh;' + + 'font-weight: bold;' + ), + }, s._('Court') + ' ' + (court.label || court.num || court.court_id)); + + uiu.el(top_bar_right, 'div', { + style: ( + 'position:static;' + + 'text-align: center;' + + 'height: 2.5vh;' + + 'width: 100%;' + + 'font-size: 2.1vh;' + + 'font-weight: bold;' + ), + }, createEventAnnouncement(s, match.setup)); + + uiu.el(top_bar_right, 'div', { + style: ( + 'position:static;' + + 'text-align: center;' + + 'height: 2.5vh;' + + 'width: 100%;' + + 'font-size: 2.1vh;' + + 'font-weight: bold;' + ), + }, createRoundAnnouncement(s, match.setup)); +} + +function createRoundAnnouncement(s, matchSetup) { + var round = matchSetup.match_name; + if (round == "R64") { + round = s._('announcements:round_64'); + } else if (round == "R32") { + round = s._('announcements:round_32'); + } else if (round == "R16") { + round = s._('announcements:round_16'); + } else if (round == "VF") { + round = s._('announcements:quaterfinal'); + } else if (round == "HF") { + round = s._('announcements:semifinal'); + } else if (round == "Finale") { + round = s._('announcements:final'); + } else if (round.indexOf('/') !== -1) { + var roundParts = round.split("/") + var diff = roundParts[1] - roundParts[0]; + if (diff > 1) { + round = s._('announcements:intermediate_round'); + } else { + round = s._('announcements:game_for_place') + roundParts[0] + s._('announcements:and') + roundParts[1]; + } + } else if (round.indexOf('-') !== -1) { + round = s._('announcements:intermediate_round'); + } else { + round = ""; + } + return round; +} +function createEventAnnouncement(s, matchSetup) { + var eventParts = matchSetup.event_name.replaceAll("-", " ").split(" "); + var eventName = ""; + if (eventParts[0] == 'JE') { + eventName = s._('announcements:boys_singles'); + } else if (eventParts[0] == 'JD') { + eventName = s._('announcements:boys_doubles'); + } else if (eventParts[0] == 'ME') { + eventName = s._('announcements:girls_singles'); + } else if (eventParts[0] == 'MD') { + eventName = s._('announcements:girls_doubles') + } else if (eventParts[0] == 'GD' || eventParts[0] == 'MX') { + eventName = s._('announcements:mixed_doubles') + } else if (eventParts[0] == 'HE') { + eventName = s._('announcements:men_singles'); + } else if (eventParts[0] == 'HD') { + eventName = s._('announcements:men_doubles'); + } else if (eventParts[0] == 'DE') { + eventName = s._('announcements:women_singles'); + } else if (eventParts[0] == 'DD') { + eventName = s._('announcements:women_doubles'); + } + if (eventName == "") { + if (eventParts[1] == 'JE') { + eventName = s._('announcements:boys_singles'); + } else if (eventParts[1] == 'JD') { + eventName = s._('announcements:boys_doubles'); + } else if (eventParts[1] == 'ME') { + eventName = s._('announcements:girls_singles'); + } else if (eventParts[1] == 'MD') { + eventName = s._('announcements:girls_doubles') + } else if (eventParts[1] == 'GD' || eventParts[1] == 'MX') { + eventName = s._('announcements:mixed_doubles') + } else if (eventParts[1] == 'HE') { + eventName = s._('announcements:men_singles'); + } else if (eventParts[1] == 'HD') { + eventName = s._('announcements:men_doubles'); + } else if (eventParts[1] == 'DE') { + eventName = s._('announcements:women_singles'); + } else if (eventParts[1] == 'DD') { + eventName = s._('announcements:women_doubles'); + } + if (eventParts[0]) { + eventName = eventName + " " + eventParts[0]; + } + } else { + if (eventParts[1]) { + eventName = eventName + " " + eventParts[1]; + } + } + return eventName; } + function render_list(container, event) { render_html_list(container, event); // TODO switch to svg } @@ -1888,6 +2961,309 @@ function render_teamscore(s, container, event, colors) { }); } +function sleepSync(ms) { + const end = Date.now() + ms; + while (Date.now() < end) { + // tut nichts – blockiert einfach alles + } + } + + +var timer_alternative_text = []; + +function render_tournamentcourt(s, container, event, court, match, colors) { + + //sleepSync(3000); // blockiert synchron für 3 Sekunden + + + var nscore = extract_netscore(match); + var gscore = _gamescore_from_netscore(nscore, match.setup); + var is_doubles = match.setup.is_doubles; + var pcount = is_doubles ? 2 : 1; + var current_score = nscore[nscore.length - 1] || []; + var server = determine_server(match, current_score); + var receiver = determine_receiver(match, current_score); + var first_game = (nscore.length < 2); + var mwinner = calc.match_winner(match.setup.counting, nscore); + var match_over = (mwinner === 'left') || (mwinner === 'right'); + + var match_meta_container = uiu.el(container, 'div', { + style: ( + 'z-index:1;' + + 'position:absolute;' + + 'right: 53vh;' + + 'top:42vh;' + + 'bottom:42vh;' + + 'display:flex;' + + 'align-items:center;' + + 'font-size:10vh;' + + 'justify-content: space-between;' + + 'width: calc(99vw - 53vh);' + + 'text-wrap: nowrap;' + + 'color:' + colors.fg + ), + }); + + var meta_fields = []; + + if (option_applies(s.settings.displaymode_style, 'show_court_number') && s.settings.d_show_court_number) { + meta_fields.push(s._('Court') + ' ' + (court.label || court.num || court.court_id)); + } + + if (option_applies(s.settings.displaymode_style, 'show_competition') && s.settings.d_show_competition) { + if (meta_fields.length) + { + meta_fields.push('\xa0•\xa0'); + } + + meta_fields.push(match.setup.event_name); + } + + if (option_applies(s.settings.displaymode_style, 'show_round') && s.settings.d_show_round) { + if (meta_fields.length) + { + meta_fields.push('\xa0•\xa0'); + } + + meta_fields.push(match.setup.match_name); + } + + show_match_meta(_extract_timer_state(s, match), + match_meta_container, + colors.fg2, + colors.exp, + meta_fields); + + match.setup.teams.forEach(function(team, team_id) { + var col = colors[team_id]; + var bg_col = colors['b' + team_id] || '#000'; + + var gwinner = calc.game_winner(match.setup.counting, nscore.length - 1, current_score[0], current_score[1]); + var team_serving = ( + (gwinner === 'left') ? (team_id === 0) : ( + (gwinner === 'right') ? (team_id === 1) : ( + (server.team_id === team_id)))); + + var team_receiving = ( + (gwinner === 'left') ? (team_id === 1) : ( + (gwinner === 'right') ? (team_id === 0) : ( + (receiver.team_id === team_id)))); + + var player_names = team.players.map(function(player) { + if (!option_applies(s.settings.displaymode_style, 'show_middle_name')) { + return player.name; + } + + if(!s.settings.d_show_middle_name) { + var first_names = player.firstname.split(" "); + return first_names[0] + ' ' + player.lastname; + } + return player.name; + }); + while (player_names.length < pcount) { + player_names.push(''); + } + + var team_container = uiu.el(container, 'div', { + 'class': 'd_tournament', + style: ( + 'color:' + col + ';' + + 'background:' + bg_col + ';' + )}); + + var team_name_container = uiu.el(team_container, 'div', { + style: ( + ((team_id === 0) ? 'position:absolute; bottom: 0;' : '') + + 'width:100%;height:20%;' + + 'font-size: 10vh;' + + 'display: flex;align-items: center;' + ), + }); + + var player_spans = player_names.map(function(pname, player_id) { + var is_server = (!match_over) && team_serving && (server.player_id === player_id); + var is_receiver = (!match_over) && team_receiving && (receiver.player_id === player_id); + var player_container = uiu.el(team_container, 'div', { + 'style': 'height: ' + (is_doubles ? '40%' : '80%') + ';', + 'class': 'd_tournament_player_container', + }); + var pel = uiu.el(player_container, 'div', { + style: ( + 'background: ' + (is_server ? col : bg_col) + ';' + + 'color: ' + (is_server ? bg_col : col) + ';' + + 'height: ' + (is_doubles ? '100%' : '100%') + ';' + ), + 'class': 'd_tournament_player', + }); + return uiu.el(pel, 'div', (s.settings.d_show_doubles_receiving && is_doubles && is_receiver ? {style: ('text-decoration: underline;')} : {}), pname); + }); + + var right_border; + if (! first_game) { + right_border = uiu.el(team_container, 'div', { + 'class': 'd_tournament_gscore', + style: 'background: ' + bg_col + ';' + + 'color: ' + colors.fg + ';' + + 'height: 80%;' + + 'top: ' + (team_id ? '10vh' : '0vh') + ';', + }, gscore[team_id]); + } + + var points = current_score[team_id]; + var points_el = uiu.el(team_container, 'div', { + 'class': 'd_tournament_score' + ((points >= 10) ? ' d_tournament_score_dd' : ''), + style: 'background: ' + (team_serving ? col : bg_col) + '; color: ' + (team_serving ? bg_col : col), + }, points); + if (!right_border) { + right_border = points_el; + } + + player_spans.forEach(function(ps) { + _setup_autosize(ps, right_border, function(parent_node) { + return parent_node.offsetHeight * 0.94; + }); + }); + }); +} + +function render_tournamentplayers(s, container, event, court, match, colors) { + var nscore = extract_netscore(match); + var gscore = _gamescore_from_netscore(nscore, match.setup); + var is_doubles = match.setup.is_doubles; + var pcount = is_doubles ? 2 : 1; + var current_score = nscore[nscore.length - 1] || []; + var server = determine_server(match, current_score); + var receiver = determine_receiver(match, current_score); + var first_game = (nscore.length < 2); + var mwinner = calc.match_winner(match.setup.counting, nscore); + var match_over = (mwinner === 'left') || (mwinner === 'right'); + + var match_meta_container = uiu.el(container, 'div', { + style: ( + 'z-index:1;' + + 'position:absolute;' + + 'right: 1vw;' + + 'top:42vh;' + + 'bottom:42vh;' + + 'display:flex;' + + 'align-items:center;' + + 'font-size:10vh;' + + 'justify-content: space-between;' + + 'width: calc(98vw);' + + 'text-wrap: nowrap;' + + 'color:' + colors.fg + ), + }); + + var meta_fields = []; + + if (option_applies(s.settings.displaymode_style, 'show_court_number') && s.settings.d_show_court_number) { + meta_fields.push(s._('Court') + ' ' + (court.label || court.num || court.court_id)); + } + + if (option_applies(s.settings.displaymode_style, 'show_competition') && s.settings.d_show_competition) { + if (meta_fields.length) + { + meta_fields.push('\xa0•\xa0'); + } + + meta_fields.push(match.setup.event_name); + } + + if (option_applies(s.settings.displaymode_style, 'show_round') && s.settings.d_show_round) { + if (meta_fields.length) + { + meta_fields.push('\xa0•\xa0'); + } + + meta_fields.push(match.setup.match_name); + } + + show_match_meta(_extract_timer_state(s, match), + match_meta_container, + colors.fg2, + colors.exp, + meta_fields); + + match.setup.teams.forEach(function(team, team_id) { + var col = colors[team_id]; + var bg_col = colors['b' + team_id] || '#000'; + + var gwinner = calc.game_winner(match.setup.counting, nscore.length - 1, current_score[0], current_score[1]); + var team_serving = ( + (gwinner === 'left') ? (team_id === 0) : ( + (gwinner === 'right') ? (team_id === 1) : ( + (server.team_id === team_id)))); + + var team_receiving = ( + (gwinner === 'left') ? (team_id === 1) : ( + (gwinner === 'right') ? (team_id === 0) : ( + (receiver.team_id === team_id)))); + + var player_names = team.players.map(function(player) { + if (!option_applies(s.settings.displaymode_style, 'show_middle_name')) { + return player.name; + } + + if(!s.settings.d_show_middle_name) { + var first_names = player.firstname.split(" "); + return first_names[0] + ' ' + player.lastname; + } + return player.name; + }); + while (player_names.length < pcount) { + player_names.push(''); + } + + var team_container = uiu.el(container, 'div', { + 'class': 'd_tournament', + style: ( + 'color:' + col + ';' + + 'background:' + bg_col + ';' + )}); + + var team_name_container = uiu.el(team_container, 'div', { + style: ( + ((team_id === 0) ? 'position:absolute; bottom: 0;' : '') + + 'width:100%;height:20%;' + + 'font-size: 10vh;' + + 'display: flex;align-items: center;' + ), + }); + + var player_spans = player_names.map(function(pname, player_id) { + var is_server = (!match_over) && team_serving && (server.player_id === player_id); + var is_receiver = (!match_over) && team_receiving && (receiver.player_id === player_id); + var player_container = uiu.el(team_container, 'div', { + 'style': 'height: ' + (is_doubles ? '40%' : '80%') + ';', + 'class': 'd_tournament_player_container', + }); + var pel = uiu.el(player_container, 'div', { + style: ( + 'background: ' + (is_server ? col : bg_col) + ';' + + 'color: ' + (is_server ? bg_col : col) + ';' + + 'height: ' + (is_doubles ? '100%' : '100%') + ';' + ), + 'class': 'd_tournament_player', + }); + return uiu.el(pel, 'div', (s.settings.d_show_doubles_receiving && is_doubles && is_receiver ? {style: ('text-decoration: underline;')} : {}), pname); + }); + var right_border; + + right_border = uiu.el(team_container, 'div', { + style: 'position: absolute;' + + 'right: 1vw;', + }, ''); + + player_spans.forEach(function(ps) { + _setup_autosize(ps, right_border, function(parent_node) { + return parent_node.offsetHeight * 0.94; + }); + }); + }); +} + + function render_teamcourt(s, container, event, court, match, colors) { var nscore = extract_netscore(match); var gscore = _gamescore_from_netscore(nscore, match.setup); @@ -1912,12 +3288,16 @@ function render_teamcourt(s, container, event, court, match, colors) { ), }); var timer_state = _extract_timer_state(s, match); + + // First Field is empty because the timer didn't overide the first field + var meta_fields = ["",match.setup.match_name]; if (timer_state) { - create_timer(timer_state, match_name_container, { - style: 'margin-right:1ch', - }); + show_match_meta(timer_state, + match_name_container, + colors.fg2, + colors.fg2, + meta_fields); } - uiu.el(match_name_container, 'div', {}, match.setup.match_name); match.setup.teams.forEach(function(team, team_id) { var col = colors[team_id]; @@ -1998,6 +3378,7 @@ function render_teamcourt(s, container, event, court, match, colors) { }); } + function render_stripes(s, container, event, court, match, colors) { var nscore = extract_netscore(match); var setup = match.setup; @@ -2539,27 +3920,86 @@ function _extract_timer_state(s, match) { } var active_timers = []; -function create_timer(timer_state, parent, props) { - var tv = timer.calc(timer_state); - if (!tv.visible || tv.upwards) { + +function show_match_meta(timer_state, parent, default_color, exigent_color, match_meta) { + if(!match_meta){ + match_meta = [""]; + } + + let timer_alternative_text = []; + + var create_text_element = function(parent, element, color) { + let fontSize = '13vh'; + timer_alternative_text.push([uiu.el(parent, 'div', {style: ('font-size:' + fontSize + '; color:' + color +'; width: fit-content;')}, element), fontSize]); + }; + + var auto_size_alternative_strings = function(parrent_el) { + var parrent_width = parrent_el.offsetWidth; + var child_width = 0; + timer_alternative_text.forEach(function(item) { + let [el, origFontSize] = item; + el.style.fontSize = origFontSize; + child_width += el.offsetWidth; + }); + + if(parrent_width < child_width) { + timer_alternative_text.forEach(function(item) { + + let [el, origFontSize] = item; + var match = origFontSize.match(/^(\d*\.?\d+)\s*([a-zA-Z%]+)$/); + var numeric_value = match[1] ? parseFloat(match[1]) : 10; + var unit = match[2] ? match[2] : 'vh'; + + el.style.fontSize = numeric_value * (parrent_width/child_width) + unit; + }); + } + } + + window.addEventListener('resize', () => {auto_size_alternative_strings(parent);} , true); + + if(timer_state) { + var tv = timer.calc(timer_state); + } + + if (!tv || !tv.visible) { + match_meta.forEach(function(element){create_text_element(parent, element, default_color);}); + auto_size_alternative_strings(parent); return; } - var el = uiu.el(parent, 'div', props, tv.str); + + create_text_element(parent, match_meta[0], default_color); + + let timerFontSize = '25vh'; + var el = uiu.el(parent, 'div', {style: ('font-size:' + timerFontSize + '; color:' + default_color +';')}, '\xa0'+tv.str); + timer_alternative_text.push([el, timerFontSize]); var tobj = {}; active_timers.push(tobj); var update = function() { var tv = timer.calc(timer_state); - var visible = tv.visible && !tv.upwards; - uiu.text(el, tv.str); + var visible = tv.visible; + uiu.text(el, '\xa0'+tv.str); + + if(tv.exigent && exigent_color) { + //uiu.attr(el, exigent_color); + el.style.color = exigent_color; + } + if (visible && tv.next) { tobj.timeout = setTimeout(update, tv.next); } else { tobj.timeout = null; } + if (!visible) { - uiu.remove(el); + timer_alternative_text.forEach(function(item) { + let [element, origFontSize] = item; + uiu.remove(element); + }) + timer_alternative_text = []; + match_meta.forEach(function(element){create_text_element(parent, element, default_color);}); } + auto_size_alternative_strings(parent); }; update(); } @@ -2664,17 +4104,19 @@ function render_2court(s, container, event, colors) { match.setup.match_name : (match.setup.event_name || '').replace(/(?:\s*-)?\s*Qualification/, 'Q')); var d_2court_info_container = uiu.el(court_container, 'div', 'd_2court_info'); - uiu.el(d_2court_info_container, 'div', { - style: 'color:' + colors.fg + ';', - }, match_name); + + var meta_container = uiu.el(d_2court_info_container, 'div', { + style: 'color:' + colors.fg + '; width:100%; display:flex; flex-wrap: nowrap; justify-content: space-evenly;' , + }); + + var meta_fields = ['', match_name, '']; var timer_state = _extract_timer_state(s, match); - if (timer_state) { - create_timer(timer_state, court_container, { - 'class': 'd_2court_timer', - style: 'background: ' + colors.bg + '; color: ' + colors.fg + ';', - }); - } + show_match_meta(timer_state, + meta_container, + colors.fg, + colors.fg, + meta_fields); } } @@ -2734,6 +4176,11 @@ function update(err, s, event) { uiu.visible_qs('.settings_display_court_id', option_applies(style, 'court_id')); uiu.visible_qs('.settings_display_reverse_order', option_applies(style, 'reverse_order')); uiu.visible_qs('.settings_d_show_pause', option_applies(style, 'show_pause')); + uiu.visible_qs('.settings_d_show_court_number', option_applies(style, 'show_court_number')); + uiu.visible_qs('.settings_d_show_competition', option_applies(style, 'show_competition')); + uiu.visible_qs('.settings_d_show_round', option_applies(style, 'show_round')); + uiu.visible_qs('.settings_d_show_middle_name', option_applies(style, 'show_middle_name')); + uiu.visible_qs('.settings_d_show_doubles_receiving', option_applies(style, 'show_doubles_receiving')); uiu.visible_qs('.settings_d_scale', option_applies(style, 'scale')); uiu.visible_qs('.settings_d_team_colors', option_applies(style, 'team_colors')); @@ -2764,7 +4211,7 @@ function update(err, s, event) { title: uc, value: s.settings['d_' + uc], }); - color_input.addEventListener('change', on_color_select); + color_input.addEventListener('change', on_style_change); }); color_inputs.setAttribute('data-json', JSON.stringify(used_colors)); } @@ -2796,6 +4243,8 @@ function update(err, s, event) { onlyscore: render_onlyscore, stripes: render_stripes, teamcourt: render_teamcourt, + tournamentcourt: render_tournamentcourt, + tournamentplayers: render_tournamentplayers, }[style]; if (xfunc) { var court = _render_court(s, container, event); @@ -2818,6 +4267,7 @@ function update(err, s, event) { ), }); + /* // background for colors for (var team_id = 0;team_id < 2;team_id++) { uiu.el(nomatch_el, 'div', { @@ -2827,6 +4277,7 @@ function update(err, s, event) { ), }); } + */ var _render_team_name = function(team_id) { uiu.el(nomatch_el, 'div', { @@ -2844,9 +4295,10 @@ function update(err, s, event) { } else if (event.tournament_logo_url) { uiu.el(nomatch_el, 'img', { src: event.tournament_logo_url, - style: 'height: 70vh;', + style: 'max-height: 70vh; max-width: 90vw; height:70vh;', alt: (event.tournament_name || ''), }); + } else { var tname = event.tournament_name; if (tname) { @@ -2859,7 +4311,7 @@ function update(err, s, event) { } uiu.el(nomatch_el, 'div', { style: ( - 'font-size:18vmin;' + 'font-size:22vmin;' ), }, s._('Court') + ' ' + (court.label || court.num || court.court_id)); if (is_team) { @@ -2879,10 +4331,13 @@ function update(err, s, event) { castall: render_castall, greyish: render_greyish, tournament_overview: render_tournament_overview, + tournament_overview_dm: render_tournament_overview_dm, + tournament_overview_dm_finals: render_tournament_overview_dm_finals, tim: render_tim, teamscore: render_teamscore, stream: render_stream, streamcourt: render_streamcourt, + streamcourt_dm: render_streamcourt_dm, streamteam: render_streamteam, }[style]; if (ofunc) { @@ -2894,7 +4349,6 @@ function update(err, s, event) { // Default: top+list render_top_list(s, container, event); } - function on_style_change(s) { if (s.ui && s.ui.displaymode_visible) { update(_last_err, s, s.event); @@ -2907,6 +4361,7 @@ function on_style_change(s) { input.value = col; } }); + network.reload_match_information(); } var _cancel_updates = null; @@ -2986,6 +4441,21 @@ function ui_init(s, hash_query) { if (hash_query.show_pause) { settings.change(s, 'd_show_pause', (hash_query.show_pause === 'true')); } + if (hash_query.show_court_number) { + settings.change(s, 'd_show_court_number', (hash_query.show_court_number === 'true')); + } + if (hash_query.show_competition) { + settings.change(s, 'd_show_competition', (hash_query.show_competition === 'true')); + } + if (hash_query.show_round) { + settings.change(s, 'd_show_round', (hash_query.show_round === 'true')); + } + if (hash_query.show_middle_name) { + settings.change(s, 'd_show_middle_name', (hash_query.show_middle_name === 'true')); + } + if (hash_query.show_doubles_receiving) { + settings.change(s, 'd_show_doubles_receiving', (hash_query.show_doubles_receiving === 'true')); + } if (hash_query.team_colors) { settings.change(s, 'd_team_colors', (hash_query.team_colors === 'true')); } @@ -3072,12 +4542,18 @@ function option_applies(style_id, option_name) { onlyscore: ['court_id', 'team_colors', 'c0', 'cb0', 'c1', 'cb1'], stream: ['reverse_order'], streamcourt: ['court_id'], + streamcourt_dm: ['court_id'], streamteam: ['team_colors', 'c0', 'cb0', 'c1', 'cb1', 'cfg', 'cbg'], teamcourt: ['court_id', 'team_colors', 'c0', 'cb0', 'c1', 'cb1', 'cfg', 'cfg2', 'show_pause'], + tournamentcourt: ['court_id', 'team_colors', 'c0', 'cb0', 'c1', 'cb1', 'cfg', 'cfg2', 'cexp', 'show_pause', 'show_court_number', 'show_competition', 'show_round', 'show_middle_name', 'show_doubles_receiving'], + tournamentplayers: ['court_id', 'team_colors', 'c0', 'cb0', 'c1', 'cb1', 'cfg', 'cfg2', 'cexp', 'show_pause', 'show_court_number', 'show_competition', 'show_round', 'show_middle_name', 'show_doubles_receiving'], teamscore: ['team_colors', 'c0', 'c1', 'cfg', 'cbg'], tim: ['cbg', 'cfg', 'ctim_blue', 'ctim_active'], tournament_overview: ['cfg', 'cbg', 'cbg3', 'cborder', 'cfg2'], + tournament_overview_dm: ['cfg', 'cbg', 'cbg3', 'cborder', 'cfg2'], + tournament_overview_dm_finals: ['cfg', 'cbg', 'cbg3', 'cborder', 'cfg2'], stripes: ['court_id', 'cbg', 'team_colors', 'c0', 'c1', 'cfg', 'cfgdark', 'cbg4', 'cserv'], + umpire: ['fullscreen_ask', 'shuttle_counter', 'show_announcements', 'negative_timers', 'editmode_doubleclick', 'click_mode', 'button_block_timeout', 'network_timeout', 'network_update_interval', 'style'], }; var bs = BY_STYLE[style_id]; if (bs) { diff --git a/js/i18n_de.js b/js/i18n_de.js index aac6f36e..b93f8944 100644 --- a/js/i18n_de.js +++ b/js/i18n_de.js @@ -17,7 +17,8 @@ var i18n_de = { 'Call referee': 'Referee rufen', 'Are you retiring?': 'Werden Sie aufgeben?', 'import link': 'Aus Datei importieren ...', -'send export': 'Export senden', +'send export': 'Speichern', +'reset export': 'Zurücksetzen', 'select pick': 'Auswählen', 'back': 'Zurück', @@ -49,11 +50,12 @@ var i18n_de = { 'network:error': 'Fehler bei der Netzwerk-Übertragung', 'network:error:unconfigured': 'Keine Netzwerk-Anbindung konfiguriert! (Einzel-Demo-Modus statt #bldemo?)', -'network:error:bts': 'BTS nicht via HTTP erreichbar', +'network:error:bts': 'Verbindung zum BTS unterbrochen', 'network:error:http': 'Netzwerk-Fehler (Code {code})', 'network:error:status': '(Status ist {status})', 'network:Matches': 'Spiele', 'network:Matches on court': 'Spiele auf Feld {court}', +'network:Tabletoperator': 'Tabletbedienung:', 'network:restart match': 'Spiel bei 0-0 neu starten', 'network:resume match': 'Spiel bei {score} fortsetzen', 'network:in progress': 'Das Spiel {match} wurde bereits angefangen', @@ -142,7 +144,7 @@ var i18n_de = { 'wonby|2': 'Der zweite Satz', 'wonby|3': 'Der dritte Satz', 'wonby|4': 'Der vierte Satz', -'wonby.match': 'Das Spiel wurde gewonnen von {winner_name} {score_str}', +'wonby.match': 'Das Spiel wurde gewonnen von {winner_name} {score_str}\n\nBestätigen des Ergebnisses durch Klick.', 'wonby.walkover': '(Walkover zugunsten von {winner_name}.\n{loser_name} waren nicht anwesend.)', 'wonby.and': ' und ', 'game(won)': 'Satz', @@ -195,6 +197,7 @@ var i18n_de = { 'settings:Network statistics': 'Netzwerkstatistiken', 'settings:Order link': 'Reihenfolge', 'settings:Export link': 'Exportieren', +'settings:Clear Match Archive': 'Alte Spiele löschen', 'settings:New match': 'Neues Spiel', 'settings:Singles': 'Einzel', 'settings:Doubles': 'Doppel', @@ -229,6 +232,7 @@ var i18n_de = { 'settings:style:complete': 'Vollständig', 'settings:style:clean': 'Nur Feld und Vollbild', 'settings:style:focus': 'Fokus', +'settings:style:hidden': 'Verborgen (zentrale Verwaltung)', 'settings:warmup': 'Spielfeld\u00ADgewöhnungszeit', 'settings:warmup:bwf-2016': 'BWF ab 2016 (90s)', @@ -520,6 +524,8 @@ var i18n_de = { 'displaymode|bwf': 'BWF', 'displaymode|clean': 'Clean', 'displaymode|teamcourt': 'Mannschaftskampf', +'displaymode|tournamentcourt': 'Individual-Turnier', +'displaymode|tournamentplayers': 'Individual-Turnier: Nur Spieler', 'displaymode|2court': '2 Felder', 'displaymode|andre': 'André', 'displaymode|tim': 'Tim', @@ -536,6 +542,9 @@ var i18n_de = { 'displaymode|stripes': 'Streifen', 'displaymode|stream': 'Stream', 'displaymode|streamcourt': 'Court-Stream', +'displaymode|streamcourt_dm': 'Court-Stream für DM 2025', +'displaymode|tournament_overview_dm': 'Aktuelle Spielstände für DM 2025', +'displaymode|tournament_overview_dm_finals': 'Aktuelle Spielstände für DM 2025 - Finals', 'displaymode|streamteam': 'Stream-Team', 'displaymode:court_id': 'Court:', 'displaymode:court_id:loading': 'lade ...', @@ -545,6 +554,11 @@ var i18n_de = { 'displaymode:colors': 'Farben:', 'displaymode:use team colors': 'Verwende Team-Farben', 'displaymode:show_pause': 'Zeige verbleibende Pausenzeit', +'displaymode:show_court_number': 'Zeige Spielfeldnummer', +'displaymode:show_competition': 'Zeige die Konkurrenz', +'displaymode:show_round': 'Zeige die Runde', +'displaymode:show_middle_name': 'Zeige zweiten Vornamen der Spieler', +'displaymode:show_doubles_receiving': 'Unterstreiche den annehmenden Spieler im Doppel', 'displaymode:hide settings': 'Verbergen', 'liveaw:lost connection': 'Verbindung wird wiederhergestellt ...', @@ -656,6 +670,38 @@ var i18n_de = { 'login:button': 'Einloggen', 'login:link': 'Login als ...', 'login:close': 'Schließen', + +'announcements:begin_to_play': 'Bitte mit dem Spielen beginnen!', +'announcements:second_call': '"Zweiter Aufruf fuer:"', +'announcements:vs': ' gegen ', +'announcements:counting_board_service': 'Klapptafelbedienung:', +'announcements:table_service': 'Tabletbedienung:', +'announcements:umpire': 'Schiedsrichter:', +'announcements:service_judge': 'Aufschlagrichter:', +'announcements:and': ' und ', +'announcements:preparation': 'In Vorbereitung:', +'announcements:meetingpoint': 'Treffen am Meetingpoint!', +'announcements:on_court': 'Auf Spielfeld ', +'announcements:match_number': 'Spiel Nummer ', +'announcements:boys_singles': 'Jungeneinzel', +'announcements:boys_doubles': 'Jungendoppel', +'announcements:girls_singles': 'Maedchenneinzel', +'announcements:girls_doubles': 'Maedchendoppel', +'announcements:mixed_doubles': 'Gemischtes Doppel', +'announcements:men_singles': 'Herreneinzel', +'announcements:men_doubles': 'Herrendoppel', +'announcements:women_singles': 'Dameneinzel', +'announcements:women_doubles': 'Damendoppel', +'announcements:round_64': '1. Runde', +'announcements:round_32': '2. Runde', +'announcements:round_16': '3. Runde', +'announcements:quaterfinal': 'Viertelfinale', +'announcements:semifinal': 'Halbfinale', +'announcements:final': 'Finale', +'announcements:intermediate_round': 'Zwischenrunde', +'announcements:game_for_place': 'Spiel um Platz', +'announcements:voice': 'Google Deutsch', +'announcements:lang': 'de-DE', }; /*@DEV*/ diff --git a/js/i18n_dech.js b/js/i18n_dech.js index 2354c458..ecf51710 100644 --- a/js/i18n_dech.js +++ b/js/i18n_dech.js @@ -14,7 +14,7 @@ var i18n_dech = { 'Event Sheet': 'Resultatblatt', 'match suspended': 'Das Spiel ist suspendiert', 'ready to play': 'Bereit zum Spielen.', -'wonby.match': 'Spiel gewonnen von {winner_name} {score_str}', +'wonby.match': 'Spiel gewonnen von {winner_name} {score_str}\n\nBestätigen des Ergebnisses durch Klick.', 'wonby.winner': ' gewonnen von {winner_name} {winner_score}-{loser_score}', 'wonby|1': 'Erster Satz', 'wonby|2': 'Zweiter Satz', diff --git a/js/i18n_en.js b/js/i18n_en.js index abebf186..384f39f0 100644 --- a/js/i18n_en.js +++ b/js/i18n_en.js @@ -17,7 +17,8 @@ var i18n_en = { 'Call referee': 'Call referee', 'Are you retiring?': 'Are you retiring?', 'import link': 'Import event from backup file ...', -'send export': 'send export file', +'send export': 'Save setting', +'reset export': 'Reset setting', 'select pick': 'Select', 'back': 'Back', @@ -54,6 +55,7 @@ var i18n_en = { 'network:error:status': '(State is {status})', 'network:Matches': 'Matches', 'network:Matches on court': 'Matches on court {court}', +'network:Tabletoperator': 'Tablet service:', 'network:restart match': 'Restart match at 0-0', 'network:resume match': 'Resume match at {score}', 'network:in progress': 'Match {match} has already been started', @@ -142,7 +144,7 @@ var i18n_en = { 'wonby|2': 'Second game', 'wonby|3': 'Third game', 'wonby|4': 'Fourth game', -'wonby.match': 'Match won by {winner_name} {score_str}', +'wonby.match': 'Match won by {winner_name} {score_str}\n\nConfirming the result by clicking.', 'wonby.walkover': '(Walkover in favor of {winner_name}.\n{loser_name} did not show up.)', 'wonby.and': ' and ', 'game(won)': 'Game', @@ -228,6 +230,7 @@ var i18n_en = { 'settings:style:complete': 'Complete', 'settings:style:clean': 'Clean', 'settings:style:focus': 'Focus Mode', +'settings:style:hidden': 'Hidden Mode (central administration)', 'settings:warmup': 'Warmup:', 'settings:warmup:bwf-2016': 'BWF 2016+ (120s, ready after 90s)', @@ -519,6 +522,8 @@ var i18n_en = { 'displaymode|bwf': 'BWF', 'displaymode|clean': 'clean', 'displaymode|teamcourt': 'team display', +'displaymode|tournamentcourt': 'individual tournament', +'displaymode|tournamentplayers': 'individual tournament: players only', 'displaymode|2court': '2 courts', 'displaymode|castall': 'greenscreen (all courts)', 'displaymode|andre': 'André', @@ -535,6 +540,8 @@ var i18n_en = { 'displaymode|stripes': 'stripes', 'displaymode|stream': 'stream', 'displaymode|streamcourt': 'court stream', +'displaymode|streamcourt_dm': 'Court-Stream for DM 2025', +'displaymode|tournament_overview_dm': 'Current Scoresfor DM 2025', 'displaymode|streamteam': 'stream team', 'displaymode:court_id': 'court:', 'displaymode:court_id:loading': 'loading ...', @@ -544,6 +551,11 @@ var i18n_en = { 'displaymode:colors': 'Colors:', 'displaymode:use team colors': 'use team colors', 'displaymode:show_pause': 'Show interval timer', +'displaymode:show_court_number': 'Show court number', +'displaymode:show_competition': 'Show the competition', +'displaymode:show_round': 'Show the round', +'displaymode:show_middle_name': 'Show middle names of players', +'displaymode:show_doubles_receiving': 'Underline the receiving player in doubles', 'displaymode:hide settings': 'Hide', 'liveaw:lost connection': 'Reconnecting ...', @@ -656,6 +668,38 @@ var i18n_en = { 'login:link': 'Log in as ...', 'login:close': 'Close', +'announcements:begin_to_play': 'Start to play!', +'announcements:second_call': '"Second call for:"', +'announcements:vs': ' vs ', +'announcements:counting_board_service': 'Countingboard service:', +'announcements:table_service': 'Tablet service:', +'announcements:umpire': 'Umpire:', +'announcements:service_judge': 'Servicejudge:', +'announcements:and': ' and ', +'announcements:preparation': 'In preparation:', +'announcements:meetingpoint': 'Come to the meetingpoint!', +'announcements:on_court': 'On Court ', +'announcements:match_number': 'Match number ', +'announcements:boys_singles': 'Boys singles', +'announcements:boys_doubles': 'Boys double', +'announcements:girls_singles': 'Girls singles', +'announcements:girls_doubles': 'Girls doubles', +'announcements:mixed_doubles': 'Mixed doubles', +'announcements:men_singles': 'Men singles', +'announcements:men_doubles': 'Men doubles', +'announcements:women_singles': 'Women singles', +'announcements:women_doubles': 'Women doubles', +'announcements:round_64': 'Round of 64', +'announcements:round_32': 'Round of 32', +'announcements:round_16': 'Round of 16', +'announcements:quaterfinal': 'Quarterfinal', +'announcements:semifinal': 'Semifinal', +'announcements:final': 'Final', +'announcements:intermediate_round': 'Intermediate round', +'announcements:game_for_place': 'Game for place ', +'announcements:voice': 'Google UK English Male', +'announcements:lang': 'en-EN', + }; /*@DEV*/ diff --git a/js/i18n_frch.js b/js/i18n_frch.js index 38383241..f560519f 100644 --- a/js/i18n_frch.js +++ b/js/i18n_frch.js @@ -18,7 +18,8 @@ var i18n_frch = { 'Call referee': 'Appelle juge-arbitre', 'Are you retiring?': 'Abandonnez-vous?', 'import link': 'Importer un événement à partir du fichier de sauvegarde ...', -'send export': 'Envoyer un fichier d\'exportation', +'send export': 'Enregistrer les paramètres', +'reset export': 'Réinitialiser les paramètres', 'select pick': 'Choisir', 'back': 'Retourner', @@ -52,6 +53,7 @@ var i18n_frch = { 'network:error:unconfigured': 'Connexion réseau non configurée (mode de démonstration? Mettre #nlademo dans l\'URL)', 'network:Matches': 'Jeux', 'network:Matches on court': 'Jeux sur {court}', +'network:Tabletoperator': 'Commande de la tablette:', 'network:restart match': 'Redémarrer le jeu à 0-0', 'network:resume match': 'Reprendre le jeu à {score}', 'network:in progress': 'Le jeu {match} a déjà été lancé', @@ -139,7 +141,7 @@ var i18n_frch = { 'wonby|2': 'Deuxième set', 'wonby|3': 'Troisième set', 'wonby|4': 'Quatrième set', -'wonby.match': 'Match gagné par {winner_name} {score_str}', +'wonby.match': 'Match gagné par {winner_name} {score_str}\n\nConfirmer le résultat en cliquant.', 'wonby.walkover': '(Walkover pour {winner_name}.\n{loser_name} ne s\'est pas présenté.)', 'wonby.and': ' et ', 'game(won)': 'Set', @@ -269,6 +271,7 @@ var i18n_frch = { 'settings:style:complete': 'Achevée', 'settings:style:clean': 'Nettoyer', 'settings:style:focus': 'Concentré', +'settings:style:hidden': 'Caché', 'settings:mode:label': 'Mode:', 'settings:mode:umpire': 'Arbitre', @@ -514,6 +517,8 @@ var i18n_frch = { 'displaymode|bwf': 'BWF', 'displaymode|clean': 'Clean', 'displaymode|teamcourt': 'Affichage de l\'équipe', +'displaymode|tournamentcourt': 'Tournoi individuel', +'displaymode|tournamentplayers': 'Tournoi individuel: Joueurs seulement', 'displaymode|2court': '2 courts', 'displaymode|castall': 'Greenscreen (tous les terrains)', 'displaymode|andre': 'André', @@ -530,6 +535,7 @@ var i18n_frch = { 'displaymode|stripes': 'Bandes', 'displaymode|stream': 'Stream', 'displaymode|streamcourt': 'court stream', +'displaymode|streamcourt_dm': 'Court-Stream für DM 2025', 'displaymode|streamteam': 'stream team', 'displaymode:court_id': 'court:', 'displaymode:court_id:loading': 'chargement ...', @@ -539,6 +545,11 @@ var i18n_frch = { 'displaymode:colors': 'Couleurs:', 'displaymode:use team colors': 'Utiliser les couleurs de l\'équipe', 'displaymode:show_pause': 'Afficher le temps de repos', +'displaymode:show_court_number': 'Afficher le numéro de court', +'displaymode:show_competition': 'Afficher la concurrence', +'displaymode:show_round': 'Afficher le tour', +'displaymode:show_middle_name': 'Afficher le deuxième prénom des joueurs', +'displaymode:show_doubles_receiving': 'Souligner le joueur receveur en double', 'displaymode:hide settings': 'Cacher', 'liveaw:lost connection': 'Reconnecter ...', diff --git a/js/i18n_nlbe.js b/js/i18n_nlbe.js index 29ce2af6..ae3147e1 100644 --- a/js/i18n_nlbe.js +++ b/js/i18n_nlbe.js @@ -56,7 +56,7 @@ var i18n_nlbe = { 'wonby|2': 'Tweede game', 'wonby|3': 'Derde game', 'wonby|4': 'Vierde game', -'wonby.match': 'Wedstrijd gewonnen door {winner_name} {score_str}', +'wonby.match': 'Wedstrijd gewonnen door {winner_name} {score_str}\n\nHet resultaat bevestigen door te klikken.', 'wonby.walkover': '(Walkover in favor of {winner_name}.\n{loser_name} did not show up.) <---', 'wonby.and': ' en ', 'game(won)': 'Game', diff --git a/js/importexport.js b/js/importexport.js index 5fa096e5..c52ad8f4 100644 --- a/js/importexport.js +++ b/js/importexport.js @@ -136,14 +136,8 @@ function ui_init() { ui_import_json(state); }); - click.qs('.settings_send_export', function(e) { - e.preventDefault(); - var status = uiu.qs('.settings_footer_status'); - send_export(state); - uiu.text(status, state._('importexport:export sent')); - window.setTimeout(function() { - uiu.text(status, ''); - }, 10000); + click.qs('.delete_matches_link', function () { + match_storage.remove_all(0); }); } diff --git a/js/match_storage.js b/js/match_storage.js index 834c454f..7c410484 100644 --- a/js/match_storage.js +++ b/js/match_storage.js @@ -57,6 +57,23 @@ function remove(match_id) { window.localStorage.removeItem('bup_match_' + match_id); } +function remove_all(hours_ago) { + var matches = load(); + if (matches && matches.length > 0) { + const now = new Date(); + const hours_ago_ms = hours_ago * 60 * 60 * 1000; + matches.forEach(function (m) { + if (m.metadata && m.metadata.end && m.metadata.end != null) { + const end_time = new Date(m.metadata.end); + const match_diff_ms = now - end_time; + if (match_diff_ms > hours_ago_ms) { + window.localStorage.removeItem('bup_match_' + m.metadata.id); + } + } + }); + } +} + function ui_init() { var matches = load(); matches = matches.filter(function(m) { @@ -102,6 +119,7 @@ return { store: store, remove: remove, load_match: load_match, + remove_all: remove_all, }; })(); diff --git a/js/network.js b/js/network.js index 7f2e5d59..d6a831ed 100644 --- a/js/network.js +++ b/js/network.js @@ -304,6 +304,27 @@ function ui_render_matchlist(s, event) { 'class': 'setup_network_umpire_name', }, umpire_name); } + + var _tabletoperator_str = function (tabletoperators) { + if (tabletoperators.length === 0) { + return 'N.N'; + } else if (tabletoperators.length == 1) { + return tabletoperators[0].name; + } else { + return tabletoperators[0].name + ' / ' + tabletoperators[1].name; + } + + }; + + if (match.setup.tabletoperators && match.setup.tabletoperators.length > 0) { + uiu.el(btn, 'span', { + 'class': 'setup_network_tabletoperator', + }, s._('network:Tabletoperator')); + uiu.el(btn, 'span', { + 'class': 'setup_network_tabletoperator', + }, _tabletoperator_str(match.setup.tabletoperators)); + } + var score_text = _score_text(match.network_score); uiu.el(btn, 'span', { 'class': 'setup_network_match_score', @@ -351,57 +372,76 @@ function ui_list_matches(s, silent, no_timer) { return; } - update_event(s, event); + if(event){ + update_event(s, event); + } + eventsheet.render_links(s, uiu.qs('.setup_eventsheets')); urlexport.render_links(s, uiu.qs('.urlexport_links')); var editable = netw.editable(s); - var use_setupsheet = event.team_competition; + var use_setupsheet = event ? event.team_competition : false; uiu.visible_qs('.setupsheet_link', editable && use_setupsheet); uiu.visible_qs('.editevent_link', editable && !use_setupsheet); - ui_render_matchlist(s, event); + if(event){ + ui_render_matchlist(s, event); + } }, function(s) { return no_timer ? 'abort' : s.settings.network_update_interval; }); } +function reload_match_information() { + var netw = get_netw(); + if (netw && netw.push_service) { + netw.reload_match_information(); + } +} + // Returns a callback to be called when the updates are no longer required. // cb gets called with (err, s, event); s is NOT updated implicitly // calc_timeout is called with s and must return immediately the timeout or the string 'abort' function subscribe(s, cb, calc_timeout) { - var cancelled = false; - var timeout = null; - function query() { - if (cancelled) { - return; - } - var netw = get_netw(); - if (!netw) { - cb({ - msg: s._('network:error:unconfigured'), - }, s); - return; + var netw = get_netw(); + if (netw.push_service) { + netw.subscribe(s, cb, calc_timeout); + } else { + + var cancelled = false; + var timeout = null; + + function query() { + if (cancelled) { + return; + } + var netw = get_netw(); + if (!netw) { + cb({ + msg: s._('network:error:unconfigured'), + }, s); + return; + } + list_matches(s, function(err, event) { + cb(err, s, event); + }); + var new_timeout = calc_timeout(s); + if (new_timeout === 'abort') { + cancelled = true; + return; + } + timeout = setTimeout(query, new_timeout); } - list_matches(s, function(err, event) { - cb(err, s, event); - }); - var new_timeout = calc_timeout(s); - if (new_timeout === 'abort') { + query(); + + return function() { cancelled = true; - return; - } - timeout = setTimeout(query, new_timeout); + if (timeout) { + clearTimeout(timeout); + timeout = null; + } + }; } - query(); - - return function() { - cancelled = true; - if (timeout) { - clearTimeout(timeout); - timeout = null; - } - }; } @@ -509,9 +549,9 @@ function _court_by_id(all_courts, court_id) { } function _court_pick_dialog(s, all_courts, on_cancel) { - bupui.make_pick(s, s._('Select Court'), all_courts, function(c) { - _set_court(s, c); - }, on_cancel, uiu.qs('body'), 5); + //bupui.make_pick(s, s._('Select Court'), all_courts, function(c) { + // _set_court(s, c); + //}, on_cancel, uiu.qs('body'), 5); } function ui_init_court(s, hash_query) { @@ -853,6 +893,7 @@ return { ui_uninstall_staticnet: ui_uninstall_staticnet, uninstall_refmode_push: uninstall_refmode_push, update_event: update_event, + reload_match_information: reload_match_information, }; diff --git a/js/render.js b/js/render.js index c25f4831..4f7c9bed 100644 --- a/js/render.js +++ b/js/render.js @@ -105,14 +105,14 @@ function _score_display_set_game(s, game, game_index, is_current) { var left_input = left.children('input'); left.attr('class', 'score score_left'); if (game) { - if (! game.started && !game.finished) { + if (!game.started && !game.finished) { left.addClass('score_empty'); } if (game.finished) { if (game.team1_won == s.game.team1_left) { left.addClass('score_won'); } - } else if ((game.team1_serving !== null) && (game.team1_serving == s.game.team1_left)) { + } else if ((game.team1_serving !== null) && (game.team1_serving == s.game.team1_left)) { left.addClass('score_serving'); } @@ -123,13 +123,23 @@ function _score_display_set_game(s, game, game_index, is_current) { if (editmode_active) { uiu.$visible(left_input, editmode_score_active); } + } else { + left.addClass('score_empty'); + var left_text = left.children('span'); + var left_points = 0; + _val(left_input, left_points); + left_text.text(left_points); + if (editmode_active) { + uiu.$visible(left_input, editmode_score_active); + } + } var right = $(tr.querySelector('.score_right')); var right_input = right.children('input'); right.attr('class', 'score score_right'); if (game) { - if (! game.started && !game.finished) { + if (!game.started && !game.finished) { right.addClass('score_empty'); } if (game.finished) { @@ -147,6 +157,15 @@ function _score_display_set_game(s, game, game_index, is_current) { if (editmode_active) { uiu.$visible(right_input, editmode_score_active); } + } else { + right.addClass('score_empty'); + var right_text = right.children('span'); + var right_points = 0; + _val(right_input, right_points); + right_text.text(right_points); + if (editmode_active) { + uiu.$visible(right_input, editmode_score_active); + } } if (is_current) { diff --git a/js/scoresheet.js b/js/scoresheet.js index fa2026c7..04744d05 100644 --- a/js/scoresheet.js +++ b/js/scoresheet.js @@ -699,9 +699,12 @@ function sheet_render(s, svg) { _text('.scoresheet_scheduled_time_value', s.setup.scheduled_time_str); } + + console.log(s.match); + _text('.scoresheet_court_id', compat.courtnum(s.match.court_id ? s.match.court_id : s.setup.court_id)); - _text('.scoresheet_umpire_name', s.match.umpire_name ? s.match.umpire_name : s.setup.umpire_name); - _text('.scoresheet_service_judge_name', s.match.service_judge_name ? s.match.service_judge_name : s.setup.service_judge_name); + _text('.scoresheet_umpire_name', s.match.umpire_name ? s.match.umpire_name : (s.setup.umpire && s.setup.umpire.name ? s.setup.umpire.name : "")); + _text('.scoresheet_service_judge_name', s.match.service_judge_name ? s.match.service_judge_name : (s.setup.service_judge && s.setup.service_judge.name ? s.setup.service_judge.name : "")); _text('.scoresheet_begin_value', ((s.metadata.start && !s.match.walkover) ? utils.time_str(s.metadata.start) : '')); if (s.match.finished) { diff --git a/js/settings.js b/js/settings.js index 782b8588..08ea1ced 100644 --- a/js/settings.js +++ b/js/settings.js @@ -23,6 +23,7 @@ var default_settings = { d_cbg4: '#404040', d_cfg2: '#aaaaaa', d_cfg3: '#cccccc', + d_cexp: '#ff0000', d_cborder: '#444444', d_ct: '#80ff00', d_ctim_blue: '#0070c0', @@ -32,7 +33,12 @@ var default_settings = { d_crecv: '#707676', d_scale: 100, d_team_colors: false, - d_show_pause: false, + d_show_pause: true, + d_show_court_number: true, + d_show_competition: true, + d_show_round: true, + d_show_middle_name: false, + d_show_doubles_receiving: false, settings_autohide: 30000, dads_interval: 20000, dads_wait: 60000, @@ -55,7 +61,7 @@ var default_settings = { refmode_referee_ws_url: 'wss://live.aufschlagwechsel.de/refmode_hub/', refmode_client_node_name: '', referee_service_judges: false, - settings_style: 'default', + style: 'default', }; function load() { @@ -186,15 +192,16 @@ function hide(force, skip_state) { function update_court(s) { var court_select = $('.settings [name="court_select"]'); court_select.val(s.settings.court_id); + network.reload_match_information(); } function update_refclient(s) { - var settings_style = get_settings_style(s); + var style = get_settings_style(s); var ref_ui_visible = ( (get_mode(s) !== 'referee') && s.settings.refmode_client_enabled - && settings_style === 'complete'); + && style === 'complete'); uiu.$visible_qs( '.settings_refmode_client_container', ref_ui_visible); refmode_client_ui.on_settings_change(s); @@ -207,6 +214,11 @@ var _settings_checkboxes = [ 'refmode_client_enabled', 'displaymode_reverse_order', 'd_show_pause', + 'd_show_court_number', + 'd_show_competition', + 'd_show_round', + 'd_show_middle_name', + 'd_show_doubles_receiving', 'd_team_colors', 'referee_service_judges', ]; @@ -258,14 +270,14 @@ var _settings_selects = [ 'language', 'wakelock', 'dads_mode', - 'settings_style', + 'style', ]; function update_court_settings(s) { var automatic = false; var manual = false; if (get_mode(s) === 'umpire') { - automatic = uiu.qs('.settings select[name="court_select"]').getAttribute('data-auto-available') === 'true'; + automatic = true; //uiu.qs('.settings select[name="court_select"]').getAttribute('data-auto-available') === 'true'; manual = ! automatic; } uiu.$visible_qs('.settings_court_manual', manual); @@ -291,6 +303,11 @@ function update(s) { _settings_selects.forEach(function(name) { var $select = $('.settings [name="' + name + '"]'); $select.val(s.settings[name]); + if(name === 'style' && s.settings[name] == 'hidden') { + $select[0].disabled = true; + } else { + $select[0].disabled = false; + } }); update_court(s); @@ -380,7 +397,7 @@ function on_change(s, name) { case 'fullscreen_ask': fullscreen.update_fullscreen_button(); break; - case 'settings_style': + case 'style': on_mode_change(s); break; } @@ -488,7 +505,7 @@ function get_mode(s) { } function get_settings_style(s) { - var res = s.settings.settings_style; + var res = s.settings.style; if (res === 'default') { var netw = network.get_netw(); res = (netw && netw.limited_ui) ? 'clean' : 'complete'; @@ -517,7 +534,7 @@ function on_mode_change(s) { } else { if (styles) { visible = false; - } else if ((settings_style === 'clean') || (settings_style === 'focus')) { + } else if ((settings_style === 'clean') || (settings_style === 'focus')|| (settings_style === 'hidden')) { visible = false; } // else: complete, everything visible } diff --git a/js/timer.js b/js/timer.js index 90c289d2..0b9237c9 100644 --- a/js/timer.js +++ b/js/timer.js @@ -3,7 +3,7 @@ var timer = (function() { var ui_timer = null; function set() { - uiu.visible_qs('.timer_restart', !state.timer.upwards); + uiu.visible_qs('.timer_restart', !state.timer.upwards && state.timer.restart); if (ui_timer) { window.clearTimeout(ui_timer); diff --git a/js/utils.js b/js/utils.js index da8a91df..e9eb2e10 100644 --- a/js/utils.js +++ b/js/utils.js @@ -505,6 +505,9 @@ function cmp(a, b) { function cmp_key(key) { return function(x, y) { + if(!isNaN(Number(x[key])) && !isNaN(Number(y[key]))) { + return cmp(Number(x[key]), Number(y[key])); + } return cmp(x[key], y[key]); }; }