2022-11-23 04:15:30 +00:00
< html >
< head >
< script src = "https://code.jquery.com/jquery-3.6.1.min.js" integrity = "sha256-o88AwQnZB+VDvE9tvIXrMQaPlFFSUTR+nldQm1LuPXQ=" crossorigin = "anonymous" > < / script >
2023-01-01 20:54:44 +00:00
< link rel = "stylesheet" href = "https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/css/bootstrap.min.css" integrity = "sha384-rbsA2VBKQhggwzxH7pPCaAqO46MgnOM80zW1RWuH61DGLwZJEdK2Kadq2F9CUG65" crossorigin = "anonymous" >
2023-08-29 08:09:49 +00:00
< link rel = "stylesheet" href = "https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.5/font/bootstrap-icons.css" >
< script src = "https://cdn.jsdelivr.net/npm/bootstrap@5.3.1/dist/js/bootstrap.bundle.min.js" integrity = "sha384-HwwvtgBNo3bZJJLYd8oVXjrBZt8cqVSpeBNS5n7C8IVInixGAoxmnlMuBnhbgrkm" crossorigin = "anonymous" > < / script >
2023-08-28 01:18:47 +00:00
< style type = "text/css" >
2023-08-29 08:09:49 +00:00
.badge {
--bs-badge-font-size: 0.9em;
--bs-badge-padding-y: 0.15em;
}
2023-09-01 00:52:45 +00:00
/*div.output .input-group { width: 55em; }*/
2023-08-29 08:09:49 +00:00
div.output {
2023-09-01 00:52:45 +00:00
width: 70%;
2023-08-29 08:09:49 +00:00
margin-bottom: 1em;
padding-bottom: 1em;
border-bottom: 1px dashed;
}
.btn.btn-xs {
--bs-btn-padding-y: .25rem;
--bs-btn-padding-x: .5rem;
--bs-btn-font-size: .5rem;
}
.tranString.badge {
height: 2.5em;
2023-09-01 00:52:45 +00:00
margin: 0 0.1em 0.2em;
2023-08-29 08:09:49 +00:00
}
.tranString input[type='text'] {
2023-08-28 01:18:47 +00:00
border-collapse: collapse;
border: none;
}
2023-08-29 08:09:49 +00:00
.tranString input[type='text'][readonly] {
background: transparent;
color: white;
}
2023-09-01 00:52:45 +00:00
.buttonwrapper {
display: inline-block;
width: 2em;
}
2023-08-29 08:09:49 +00:00
.wide { width: 18em; }
.narrow { width: 7em; }
.text-bg-warning { background-color: RGBA(183,138,2,var(--bs-bg-opacity,1)) !important }
2023-09-03 01:58:16 +00:00
.text-bg-light { background-color: RGBA(196,196,196,var(--bs-bg-opacity,1)) !important }
2023-09-01 20:57:17 +00:00
.distance {
font-weight: bold;
padding: 0.2em;
color: white;
background-color: rgba(255,255,255,0.2);
margin-right: 1em;
}
2023-08-28 01:18:47 +00:00
< / style >
2023-08-29 08:09:49 +00:00
< / head >
< body style = "padding: 1em;" >
< div style = "position: fixed; top: 0; right: 0; width: 30%; z-index: 999; background: white; border: 1px solid gray; padding: 1em;" >
2023-08-29 20:14:29 +00:00
< h3 style = "font-size: 1.5em;" > Organic Maps Locale Viewer / Editor< / h3 >
2023-09-01 00:52:45 +00:00
< p > Don't worry about adding or removing periods like < span class = "badge bg-secondary" > .< / span > < span class = "badge bg-secondary" > 。< / span > (Japanese) or < span class = "badge bg-secondary" > ।< / span > (Hindi) at the end of strings. They are automatically removed as appropriate. < br / > You may hover over phrases to see their English version in a popup. Right-to-left languages may not be displayed correctly. Click the edit button next to a string and then click the save button to make changes. < br / > < b > Your changes are not saved anywhere,< / b > so copy the change request that appears below when you're ready.< / p >
2023-08-29 20:14:29 +00:00
< b > Locale language:< / b >
2023-08-29 08:09:49 +00:00
< select id = "localeSelect" class = "form-control" >
< option value = "ar" > ﺎﻠﻋﺮﺒﻳﺓ< / option >
< option value = "be" > Беларусь< / option >
< option value = "ca" > Català< / option >
< option value = "cs" > Čeština< / option >
< option value = "da" > Dansk< / option >
< option value = "de" > Deutsch< / option >
< option value = "el" > Ελληνικά< / option >
< option value = "en" > English< / option >
< option value = "es" > Español< / option >
< option value = "es-MX" > Español (MX)< / option >
< option value = "eu" > Euskara< / option >
< option value = "fa" > ﻑﺍﺮﺴﯾ< / option >
< option value = "fi" > Suomi< / option >
< option value = "fr" > Français< / option >
< option value = "hi" > हिंदी< / option >
< option value = "hr" > Hrvatski< / option >
< option value = "hu" > Magyar< / option >
< option value = "id" > Indonesia< / option >
< option value = "it" > Italiano< / option >
< option value = "ja" > 日本語< / option >
< option value = "ko" > 한국어< / option >
< option value = "mr" > मराठी< / option >
< option value = "nb" > Norsk< / option >
< option value = "nl" > Nederlands< / option >
< option value = "pl" > Polski< / option >
< option value = "pt" > Português< / option >
< option value = "pt-BR" > Português (BR)< / option >
< option value = "ro" > Română< / option >
< option value = "ru" > Русский< / option >
< option value = "sk" > Slovenčina< / option >
< option value = "sv" > Svenska< / option >
< option value = "sw" > Kiswahili< / option >
< option value = "th" > ภาษาไทย< / option >
< option value = "tr" > Türkçe< / option >
< option value = "uk" > Українська< / option >
< option value = "vi" > Tiếng Việt< / option >
< option value = "zh-Hans" > 中文简体< / option >
< option value = "zh-Hant" > 中文繁體< / option >
< / select > < br / >
<!-- TTS Voice: <select id="voiceSelect" class="form - control"></select><br/><br/> -->
<!--
< a id = "sourceUrl" href = "#" target = "_blank" > json< / a > < br / >
< a href = "https://github.com/organicmaps/organicmaps/blob/ada410b5825e2dfd7c1ed95d539292dcef7d09dc/data/strings/sound.txt" target = "_blank" > sound.txt< / a > < br / >
< em > Please propose any fixes by editing sound.txt or by adding a comment to the < a href = "https://github.com/organicmaps/organicmaps/pull/3130" > pull request< / a > < / em >
-->
<!-- position: absolute; z - index: 999; padding: 1em; box - shadow: 2px 2px 6px #999; top: 20%; left: 20%; width: 60%; background: white; border: 1px solid black;" -->
< div id = "submitpopup" style = "display: none;" >
2023-08-29 20:14:29 +00:00
< b > Copy this text: < / b > < button address = "any text" class = "btn btn-sm btn-primary copyToClipboard" > < span > Copy< / span > < / button >
2023-09-01 00:52:45 +00:00
< textarea class = "form-control" style = "height: 20em; font-family: monospace; font-size: 0.7em" > < / textarea >
2023-08-29 20:14:29 +00:00
< p > < b > Then,< / b > paste it into a new comment on < a href = "https://github.com/organicmaps/organicmaps/pull/3130" target = "_blank" > this Github pull request< / a > < / p >
2023-08-29 08:09:49 +00:00
<!--
< p style = "text-align: center; margin-top: 2em;" >
< button id = "closesubmitpopup" class = "btn btn-danger" > Done< / button >
< / p >
-->
< / div >
< / div >
< div class = "output" id = "out-e" > < / div >
< div class = "output" id = "out-f" > < / div >
< div class = "output" id = "out-g" > < / div >
< div class = "output" id = "out-d" > < / div >
< div class = "output" id = "out-c" > < / div >
< div class = "output" id = "out-b" > < / div >
< div class = "output" id = "out-a" > < / div >
< script type = "text/javascript" >
const synth = window.speechSynthesis;
var voices;
function loadTts(){
voices = synth.getVoices();
$.each(voices, function(i, voice){
$("#voiceSelect").append('< option value = "'+i+'" lang = "'+voice.lang+'" > '+voice.name+'< / option > ');
});
if (voices.length == 0) {
$("#voiceSelect").attr("disabled", "disabled").after('< em > Try another browser to use TTS, or install TTS support for this browser.< / em > ');
window.hideTts = true;
}
let params = new URLSearchParams(document.location.search);
let lang = params.get("lang");
let tts = params.get("tts");
const userLocale =
navigator.languages & & navigator.languages.length
? navigator.languages[0]
: navigator.language;
if (lang) {
$("#localeSelect").val(lang);
} else {
$("#localeSelect option").each(function(i, o){
if (userLocale.match($(o).attr('value'))) {
$("#localeSelect").val($(o).attr('value'));
}
});
}
if (tts) {
$("#voiceSelect").val(tts);
} else {
if (lang) {
$("#voiceSelect option").each(function(i, o){
// console.log(lang,$(o).attr('lang').substr(0,2));
if (lang.match($(o).attr('lang').substr(0,2))) {
// console.log("got it");
$("#voiceSelect").val($(o).attr('value'));
return false;
}
});
} else {
$("#voiceSelect option").each(function(i, o){
// console.log(userLocale,$(o).attr('lang'));
if (userLocale.match($(o).attr('lang'))) {
$("#voiceSelect").val($(o).attr('value'));
}
});
}
}
$("#localeSelect").change(function(){
showData();
params.set('lang', $("#localeSelect").val());
document.location.search = params.toString();
});
$("#voiceSelect").change(function(){
loadTts();
// params.set('tts', $("#voiceSelect").val());
// document.location.search = params.toString();
});
loadLocale();
}
$(document).ready(function(){ loadTts(); });
< / script >
< div class = "modal fade" id = "fmtModal" tabindex = "-1" aria-labelledby = "exampleModalLabel" aria-hidden = "true" >
< div class = "modal-dialog" >
< div class = "modal-content" >
< form id = "fmtModalForm" >
< div class = "modal-header" >
< h5 class = "modal-title" id = "exampleModalLabel" > Edit Complex TTS String< / h5 >
< button type = "button" class = "btn-close" data-bs-dismiss = "modal" aria-label = "Close" > < / button >
< / div >
< div class = "modal-body" >
< label for = "exampleInputEmail1" class = "form-label" > TTS String< / label >
< input type = "text" class = "form-control" id = "fmtModalText" aria-describedby = "fmtModalDesc" >
< div id = "fmtModalDesc" class = "form-text" >
Formatter strings use numbered variables like < span class = "badge bg-secondary" > %1$s< / span > to indicate parts that should be replaced by other text.
< ul >
2023-09-01 01:29:44 +00:00
< li > < span class = "badge text-bg-warning" > %1$s< / span > will be replaced by the distance translation string, like "in 500 meters."< / li >
< li > < span class = "badge text-bg-primary" > %2$s< / span > will be replaced by the direction translation string, like "make a right turn." < i > For languages that use different grammar for sentences like "in 100 feet, make a right turn onto Main Street" versus "in 100 feet, make a right turn" an optional string like < code > make_a_right_turn_street< / code > can replace the default < code > make_a_right_turn< / code > string.< / i > < / li >
< li > < span class = "badge text-bg-secondary" > %3$s< / span > will be dynamically replaced by the name or number of the street or exit, like "Main Street" or "Exit 123."< / li >
< li > < span class = "badge text-bg-danger" > %4$s< / span > is optional and is used for languages that split their grammar for sentences like "in 100 feet, make a right turn onto Main Street" into two parts, like "make in 100 feet a right turn onto Main Street". Optional strings like < code > make_a_right_turn_street_verb< / code > may be defined for this purpose.< / li >
2023-08-29 08:09:49 +00:00
< / ul >
2023-08-29 20:14:29 +00:00
For example "< span class = "badge text-bg-warning" > %1$s< / span > < span class = "badge text-bg-primary" > %2$s< / span > onto < span class = "badge text-bg-secondary" > %3$s< / span > "< br / > becomes "< span class = "badge text-bg-warning" > In 500 meters< / span > < span class = "badge text-bg-primary" > make a right turn< / span > onto < span class = "badge text-bg-secondary" > Main Street< / span > "< br / > < br / >
2023-09-01 01:29:44 +00:00
whereas "< span class = "badge text-bg-danger" > %4$s< / span > onto < span class = "badge text-bg-secondary" > %3$s< / span > a < span class = "badge text-bg-primary" > %2$s< / span > when you reach it < span class = "badge text-bg-warning" > %1$s< / span > "< br / > can become "< span class = "badge text-bg-danger" > Make< / span > onto < span class = "badge text-bg-secondary" > Main Street< / span > a < span class = "badge text-bg-primary" > right turn< / span > when you reach it < span class = "badge text-bg-warning" > in 500 meters< / span > ." with the help of < code > _street< / code > and < code > _street_verb< / code > strings.
2023-08-29 08:09:49 +00:00
< / div >
< / div >
< div class = "modal-footer" >
< button type = "button" class = "btn btn-secondary" data-bs-dismiss = "modal" > Close< / button >
< input type = "submit" class = "btn btn-primary" value = "Save changes" / >
< / div >
< / form >
< / div >
< / div >
< / div >
2023-08-28 01:18:47 +00:00
2022-11-23 04:15:30 +00:00
< script type = "text/javascript" >
2023-08-28 01:18:47 +00:00
function parseSoundTxt(raw) {
var lines = raw.split("\n");
2023-09-03 01:58:16 +00:00
// TODO: temporary testing
lines.push(" [dist_direction_onto_exit]");
lines.push(" comment = [1: In 100m] [2: take exit %s / follow signs] [3: for the [%s] expressway] [4: towards %s]");
lines.push(" en = %1$s %2$s %3$s %4$s");
lines.push(" [take_exit]");
lines.push(" en = take exit %s");
lines.push(" [follow_signs]");
lines.push(" en = follow signs");
lines.push(" [for_the_expressway]");
lines.push(" en = for highway %s");
lines.push(" [towards]");
lines.push(" en = towards %s");
2023-08-28 01:18:47 +00:00
var thisSection = "";
var out = {};
for (var i=0;i< lines.length ; i + + ) {
var line = lines[i];
var sectionMatch = line.match(/\s+\[(\w+)\]/);
var stringMatch = line.match(/\s+([\w\-]+) = (.+)/);
if (sectionMatch & & sectionMatch.length > 1) {
thisSection = sectionMatch[1];
} else if (stringMatch & & stringMatch.length > 2) {
if (!out[stringMatch[1]]) {
out[stringMatch[1]] = {};
2022-11-23 04:15:30 +00:00
}
2023-08-28 01:18:47 +00:00
out[stringMatch[1]][thisSection] = stringMatch[2];
} else {
2023-08-29 08:09:49 +00:00
console.log("no text parser match:", line);
2023-08-28 01:18:47 +00:00
}
}
2022-11-23 04:15:30 +00:00
2023-08-28 01:18:47 +00:00
return out;
}
2022-11-23 04:15:30 +00:00
2023-09-03 01:58:16 +00:00
function buildTranStringInput(key, value, color="text-bg-primary", editable=true, nested="unnested") {
2023-08-29 08:09:49 +00:00
var classes = "stringTxt";
2023-09-01 00:52:45 +00:00
2023-09-01 01:29:44 +00:00
if (!value)
return "";
2023-09-01 20:57:17 +00:00
var size = value.length+6;
2023-08-29 08:28:14 +00:00
2023-09-01 01:29:44 +00:00
var english = "key: "+key;
2023-08-29 08:28:14 +00:00
var englishVersion = window.originalEnglishData[key];
if (englishVersion) {
2023-09-01 01:29:44 +00:00
english += "\nen: "+englishVersion;
2023-08-29 08:28:14 +00:00
}
2023-09-01 20:57:17 +00:00
var out = "< span class = 'tranString badge "+color+"' > ";
// display "then" icon
if (key == "then"){
out += "< span class = 'distance' > ...< / span > ";
}
// display distance icon
if (key.substring(0,3) == "in_"){
var dist = key.replace(/in_|_meters|_kilometers|_kilometer|_feet|_miles|_mile/g, '').replace(/_/g, '.');
var unit = "";
if (key.includes("kilo")){
unit = "km";
} else if (key.includes("meter")){
unit = "m";
} else if (key.includes("feet")){
unit = "ft";
} else if (key.includes("mile")){
unit = "mi";
}
2023-09-03 01:58:16 +00:00
var distEl = document.createElement("span");
distEl.setAttribute("class", "distance");
distEl.innerText = dist+unit;
out += distEl.outerHTML;
2023-09-01 20:57:17 +00:00
}
const imagesArr = {
"take_the_1_exit": "ic_turn_round_exit_1",
"take_the_2_exit": "ic_turn_round_exit_2",
"take_the_3_exit": "ic_turn_round_exit_3",
"take_the_4_exit": "ic_turn_round_exit_4",
"take_the_5_exit": "ic_turn_round_exit_5",
"take_the_6_exit": "ic_turn_round_exit_6",
"take_the_7_exit": "ic_turn_round_exit_7",
"take_the_8_exit": "ic_turn_round_exit_8",
"take_the_9_exit": "ic_turn_round_exit_9",
"take_the_10_exit": "ic_turn_round_exit_10",
"take_the_11_exit": "ic_turn_round_exit_11",
"make_a_slight_right_turn": "ic_turn_right_slight",
"make_a_right_turn": "ic_turn_right",
"make_a_sharp_right_turn": "ic_turn_right_sharp",
"enter_the_roundabout": "ic_turn_round_enter",
"leave_the_roundabout": "ic_turn_round_exit",
"make_a_slight_left_turn": "ic_turn_left_slight",
"make_a_left_turn": "ic_turn_left",
"make_a_sharp_left_turn": "ic_turn_left_sharp",
"make_a_u_turn": "ic_turn_uleft",
"go_straight": "ic_turn_straight",
"exit": "ic_exit_highway_to_right",
"destination": "ic_turn_finish",
"you_have_reached_the_destination": "ic_turn_finish",
"unknown_camera": "camera"
};
// _street_verb and _street use the same icons
var imageKey = key.replace(/_street_verb|_street/g, '');
if (img = imagesArr[imageKey]){
// poorly detect left/right driving based on user's browser timezone for iconography
// (the browser being used tells us about the user, versus the language being selected)
var browserTimezone = Intl.DateTimeFormat().resolvedOptions().timeZone;
if (
browserTimezone.includes("Nassau") || // Bahamas
browserTimezone.includes("Jamaica") ||
browserTimezone.includes("Guyana") ||
browserTimezone.includes("Paramaribo") || // Suriname
browserTimezone.includes("Stanley") || // Falkland
browserTimezone.includes("London") || // UK
browserTimezone.includes("GB") || // UK
browserTimezone.includes("Dublin") || // Ireland
browserTimezone.includes("Eire") ||
browserTimezone.includes("Windhoek") || // Namibia
browserTimezone.includes("Johannesburg") || // South Africa
browserTimezone.includes("Maputo") || // Mozambique, Botswana, Malawi, Zambia, Zimbabwe (NOT: Burundi, DRC, Rwanda)
browserTimezone.includes("Blantyre") || // Malawi
browserTimezone.includes("Nairobi") || // Kenya, Uganda, Tanzania (NOT: Somalia, Madagascar, Comoros, Ethiopia, Eritrea, Djibouti)
browserTimezone.includes("Mbabane") || // Swaziland
browserTimezone.includes("Maseru") || // Lesotho
browserTimezone.includes("Nicosia") || // Cyprus
browserTimezone.includes("Famagusta") || // Cyprus
browserTimezone.includes("Karachi") || // Pakistan
browserTimezone.includes("Kolkata") || // India
browserTimezone.includes("Katmandu") || // Nepal
browserTimezone.includes("Thimphu") || // Bhutan
browserTimezone.includes("Dhaka") || // Bangladesh
browserTimezone.includes("Colombo") || // Sri Lanka
browserTimezone.includes("Bangkok") || // Thailand
browserTimezone.includes("Tokyo") || // Japan
browserTimezone.includes("Japan") ||
browserTimezone.includes("Jakarta") || // Indonesia
browserTimezone.includes("Jayapura") ||
browserTimezone.includes("Makassar") ||
browserTimezone.includes("Pontianak") ||
browserTimezone.includes("Singapore") || // and Malaysia
browserTimezone.includes("Bougainville") || // Papua New Guinea
browserTimezone.includes("Port_Moresby") ||
browserTimezone.includes("Australia") ||
browserTimezone.includes("Auckland") || // New Zealand
browserTimezone.includes("Chatham") ||
browserTimezone.includes("Fiji") ||
browserTimezone.includes("Pago_Pago")
){
imagesArr["make_a_u_turn"] = "ic_turn_uright";
imagesArr["exit"] = "ic_exit_highway_to_left";
}
var imageAlt = img.replace(/ic_|turn_/g, '')
.replace(/round/g, 'roundabout').replace(/uleft|uright/g, 'u-turn')
.replace(/highway_to_left|highway_to_right/g, 'highway').replace(/_/g, ' ');
out += "< img alt = '"+imageAlt+"' title = '"+imageAlt+"' src = 'img/"+img+".webp' style = 'height: 1.5em; margin-right: 1em' / > ";
}
2023-09-03 01:58:16 +00:00
if (nested == "nested") {
var nestedEl = document.createElement("span");
nestedEl.setAttribute("class", classes);
nestedEl.setAttribute("data-key", key);
nestedEl.setAttribute("title", english);
nestedEl.innerHTML = value;
out += nestedEl.outerHTML;
} else {
var valueEl = document.createElement("input");
valueEl.setAttribute("type", "text");
valueEl.setAttribute("class", classes);
valueEl.setAttribute("size", size);
valueEl.setAttribute("readonly", true);
valueEl.setAttribute("data-key", key);
valueEl.setAttribute("title", english);
valueEl.setAttribute("value", value);
out += valueEl.outerHTML;
}
2023-08-29 08:09:49 +00:00
2023-09-01 20:57:17 +00:00
if (editable) {
out += "< span class = 'buttonwrapper' > < button class = 'editbutton btn btn-xs btn-light text-warning' type = 'button' style = 'display:none;' > < i class = 'bi bi-pencil-fill' > < / i > < / button > < button class = 'savebutton btn btn-xs btn-light text-success' style = 'display: none;' type = 'button' > < i class = 'bi bi-check-lg' > < / i > < / button > < / span > ";
}
2023-08-29 08:09:49 +00:00
2023-09-01 20:57:17 +00:00
out += "< / span > ";
2023-08-29 08:09:49 +00:00
return out;
}
function buildFmtStringInput(key, value, color="text-bg-primary", fmtKey, editable=true) {
var classes = "stringTxt";
2023-08-29 08:28:14 +00:00
// replace RTL chars with their HTML equivalent
value = value.replace("\\u202C", "‬");
2023-08-29 08:09:49 +00:00
var out = "< span class = 'tranString badge "+color+"' > < span type = 'text' class = '"+classes+"' readonly data-key = '"+
key +
"' data-fmtkey='" +
fmtKey +
"'>" +
value +
"< / span > ";
if (editable) {
2023-09-01 00:52:45 +00:00
out += "< span class = 'buttonwrapper' > < button class = 'fmteditbutton btn btn-xs btn-light text-warning' type = 'button' style = 'display:none;' > < i class = 'bi bi-pencil-fill' > < / i > < / button > < / span > ";
2023-08-29 08:09:49 +00:00
}
out += "< / span > ";
return out;
}
function loadEventHandlers(){
$(".tranString").hover(function(){
2023-09-01 00:52:45 +00:00
let editBtn = $(this).children(".buttonwrapper").find(".editbutton");
2023-08-29 08:09:49 +00:00
if (editBtn.prop("disabled") == false) {
editBtn.show(); // only show when not disabled
}
2023-09-01 00:52:45 +00:00
let ttsEditBtn = $(this).children(".buttonwrapper").find(".fmteditbutton");
2023-08-29 08:09:49 +00:00
if (ttsEditBtn.prop("disabled") == false) {
ttsEditBtn.show(); // only show when not disabled
}
}, function(){
2023-09-01 00:52:45 +00:00
$(this).children(".buttonwrapper").find(".editbutton").hide();
$(this).children(".buttonwrapper").find(".fmteditbutton").hide();
2023-08-29 08:09:49 +00:00
});
$(".stringTxt").on('keyup', function (e) {
2023-09-01 00:52:45 +00:00
// handle enter = save
2023-08-29 08:09:49 +00:00
if (e.key === 'Enter' || e.keyCode === 13) {
2023-09-01 00:52:45 +00:00
$(this).siblings().find(".savebutton").click();
$(this).blur();
2023-08-29 08:09:49 +00:00
}
2023-09-01 00:52:45 +00:00
// adjust input size by text content
var size = $(this).val().length;
2023-09-01 20:57:17 +00:00
$(this).attr('size', size+6);
2023-09-01 00:52:45 +00:00
2023-08-29 08:09:49 +00:00
}).dblclick(function(){
2023-09-01 00:52:45 +00:00
$(this).siblings().find(".editbutton").click();
2023-08-29 08:09:49 +00:00
});
$(".editbutton").click(function(){
2023-09-01 00:52:45 +00:00
$(this).parent().siblings(".stringTxt").prop("readonly",false).focus();
2023-08-29 08:09:49 +00:00
$(this).prop("disabled",true).hide();
$(this).siblings(".savebutton").show();
});
$(".fmteditbutton").click(function(){
// for dist_direction_onto_street only
$(document.getElementById('fmtModalText')).val(window.modifiedSoundData['dist_direction_onto_street']);
$("#fmtModal").modal('show');
});
$(".savebutton").click(function(){
var changes = [];
2023-09-01 00:52:45 +00:00
$(this).parent().siblings(".stringTxt").prop("readonly",true);
2023-08-29 08:09:49 +00:00
$(this).hide();
$(this).siblings(".editbutton").prop("disabled",false).show();
2023-09-01 00:52:45 +00:00
var stringTxt = $(this).parent().siblings(".stringTxt")[0];
2023-08-29 08:09:49 +00:00
window.modifiedSoundData[$(stringTxt).data("key")] = $(stringTxt).val();
var soundKeys = Object.keys(window.originalSoundData);
var out = "";
var locale = $("#localeSelect").val();
for (i=0;i< soundKeys.length ; i + + ) {
var soundKey = soundKeys[i];
var orig = window.originalSoundData[soundKey];
var mod = window.modifiedSoundData[soundKey];
if (mod != orig) {
out += " [" + soundKey + "]\n" +
"- " + locale + " = " + orig + "\n" +
"+ " + locale + " = " + mod + "\n";
$(".stringTxt[data-key='"+soundKey+"']").val(mod); // update all instances
}
}
$("#submitpopup textarea").text("Translation change request:\n```\n"+
"# data/strings/sound.txt\n" + out + "```"
);
$("#submitpopup").show();
});
$('.copyToClipboard').click(function () {
$('#submitpopup textarea').select();
document.execCommand('copy');
$(this).addClass("btn-success").text("Copied!");
});
$(".playtts").click(function(){
var myText = "";
var strings = $(this).siblings(".form-control").find(".stringTxt");
for (i=0;i< strings.length ; i + + ) {
var string = strings[i];
// remove preceding periods from TTS text
if (myText.endsWith(".") || myText.endsWith("。") || myText.endsWith("।")) {
myText = myText.slice(0, -1);
}
if (i > 0) {
// add comma and whitespace between TTS strings
myText += ", " + $(string).val();
} else {
myText += $(string).val();
}
}
console.log("Speaking", myText);
const msg = new SpeechSynthesisUtterance(
myText
);
msg.voice = voices[$("#voiceSelect").val()];
synth.speak(msg);
});
if (window.hideTts) {
$(".playtts").hide();
}
2023-08-28 01:18:47 +00:00
}
2022-11-23 04:15:30 +00:00
2023-09-03 01:58:16 +00:00
function parseNextStreet(nextStreetStr) {
var out = {
exitNum: null,
nextExpwy: null,
nextRoad: null,
destination: null
};
// "[I 10 Bypass] > Downtown",
// "[M4] > The West; London",
// try multiple rounds of parsing, from most complex to least
var nextStreetArr = Array.from(nextStreetStr.matchAll(/\[(.+)\]: \[(.+)\] (.+) > (.+)/g));
if (nextStreetArr.length) {
out.exitNum = nextStreetArr[0][1];
out.nextExpwy = nextStreetArr[0][2];
out.nextRoad = nextStreetArr[0][3];
out.destination = nextStreetArr[0][4];
return out;
}
if (!nextStreetArr.length) {
nextStreetArr = Array.from(nextStreetStr.matchAll(/\[(.+)\]: \[(.+)\] > (.+)/g));
}
if (nextStreetArr.length) {
out.exitNum = nextStreetArr[0][1];
out.nextExpwy = nextStreetArr[0][2];
out.destination = nextStreetArr[0][3];
return out;
}
if (!nextStreetArr.length) {
nextStreetArr = Array.from(nextStreetStr.matchAll(/\[(.+)\]: (.+) > (.+)/g));
}
if (nextStreetArr.length) {
out.exitNum = nextStreetArr[0][1];
out.nextRoad = nextStreetArr[0][2];
out.destination = nextStreetArr[0][3];
return out;
}
if (!nextStreetArr.length) {
nextStreetArr = Array.from(nextStreetStr.matchAll(/\[(.+)\] (.+) > (.+)/g));
}
if (nextStreetArr.length) {
out.nextExpwy = nextStreetArr[0][1];
out.nextRoad = nextStreetArr[0][2];
out.destination = nextStreetArr[0][3];
return out;
}
if (!nextStreetArr.length) {
nextStreetArr = Array.from(nextStreetStr.matchAll(/\[(.+)\] > (.+)/g));
}
if (nextStreetArr.length) {
out.nextExpwy = nextStreetArr[0][1];
out.destination = nextStreetArr[0][2];
return out;
}
if (!nextStreetArr.length) {
nextStreetArr = Array.from(nextStreetStr.matchAll(/(.+) > (.+)/g));
}
if (nextStreetArr.length) {
out.nextRoad = nextStreetArr[0][1];
out.destination = nextStreetArr[0][2];
return out;
}
if (!nextStreetArr.length) {
nextStreetArr = Array.from(nextStreetStr.matchAll(/\[(.+)\] (.+)/g));
}
if (nextStreetArr.length) {
out.nextExpwy = nextStreetArr[0][1];
out.nextRoad = nextStreetArr[0][2];
return out;
}
if (!nextStreetArr.length) {
nextStreetArr = Array.from(nextStreetStr.matchAll(/\[(.+)\]:/g));
}
if (nextStreetArr.length) {
out.exitNum = nextStreetArr[0][1];
return out;
}
if (!nextStreetArr.length) {
nextStreetArr = Array.from(nextStreetStr.matchAll(/\[(.+)\]/g));
}
if (nextStreetArr.length) {
out.nextExpwy = nextStreetArr[0][1];
return out;
}
if (!nextStreetArr.length) {
out.nextRoad = nextStreetStr; // give up, we assume it's a plain road
return out;
}
}
2023-08-28 01:18:47 +00:00
function showData(){
var locale = $("#localeSelect").val();
var data = parseSoundTxt(window.soundTxtRaw)[locale];
2023-08-29 08:28:14 +00:00
window.originalEnglishData = parseSoundTxt(window.soundTxtRaw)['en'];
2023-08-28 01:18:47 +00:00
window.originalSoundData = data;
2023-08-29 08:09:49 +00:00
window.modifiedSoundData = Object.assign({}, data); // clone
window.fmtSoundData = {};
2023-08-28 01:18:47 +00:00
const distance = [
"in_50_meters",
"in_100_meters",
"in_200_meters",
"in_250_meters",
"in_300_meters",
"in_400_meters",
"in_500_meters",
"in_600_meters",
"in_700_meters",
"in_750_meters",
"in_800_meters",
"in_900_meters",
"in_1_kilometer",
"in_1_5_kilometers",
"in_2_kilometers",
"in_2_5_kilometers",
"in_3_kilometers",
"in_50_feet",
"in_100_feet",
"in_200_feet",
"in_300_feet",
"in_400_feet",
"in_500_feet",
"in_600_feet",
"in_700_feet",
"in_800_feet",
"in_900_feet",
"in_1000_feet",
"in_1500_feet",
"in_2000_feet",
"in_2500_feet",
"in_3000_feet",
"in_3500_feet",
"in_4000_feet",
"in_4500_feet",
"in_5000_feet",
"in_1_mile",
"in_1_5_miles",
"in_2_miles"
];
const direction = [
"take_the_1_exit",
"take_the_2_exit",
"take_the_3_exit",
"take_the_4_exit",
"take_the_5_exit",
"take_the_6_exit",
"take_the_7_exit",
"take_the_8_exit",
"take_the_9_exit",
"take_the_10_exit",
"take_the_11_exit",
"make_a_slight_right_turn",
"make_a_right_turn",
"make_a_sharp_right_turn",
"enter_the_roundabout",
"leave_the_roundabout",
"make_a_slight_left_turn",
"make_a_left_turn",
"make_a_sharp_left_turn",
"make_a_u_turn",
"go_straight",
"exit",
"destination" // exception: shouldn't have a street afterwards
];
const nextStreets = [
"Main Street",
2023-09-03 01:58:16 +00:00
"3rd Avenue",
"[23B]:",
"[US 99]",
"[NY 53] Empire Parkway",
"[I 10 Bypass] > Downtown",
"[M4] > The West; London",
"[2]: [US 22]",
"[13A]: [CA 22] > City Center; Los Angeles",
"[243]: [CA 1] Pacific Coast Highway > Monterey Bay; San Francisco",
2023-08-28 01:18:47 +00:00
];
// dist_direction_onto_street
2023-09-03 01:58:16 +00:00
// dist_direction_onto_exit
2023-08-28 01:18:47 +00:00
// then
// you_have_reached_the_destination
// unknown_camera
$("#out-a").html("");
$("#out-b").html("");
$("#out-c").html("");
$("#out-d").html("");
$("#out-e").html("");
$("#out-f").html("");
$("#out-g").html("");
2023-08-29 08:09:49 +00:00
var inputPre = "< div class = 'input-group mb-3' > "; // < button type = 'button' class = 'playtts btn btn-outline-secondary' > 🔊< / button >
inputPre += "< div class = 'form-control' > ";
2023-08-28 01:18:47 +00:00
var inputPost = "< / div > < / div > ";
var tranStringTtsPre = "< span class = 'tranString badge text-bg-secondary' > ";
$.each(direction, function(i, dir){
// can't "you'll arrive" in the present tense
if (dir != "destination") {
$("#out-a").append(inputPre +
buildTranStringInput(dir, data[dir]) +
inputPost);
}
});
2022-11-23 04:15:30 +00:00
2023-08-28 01:18:47 +00:00
$.each(direction, function(i, dir){
// can't "then" past your destination
if (dir != "destination") {
2023-08-29 08:09:49 +00:00
var nextDir = direction[(i + 2) % direction.length];
2023-08-28 01:18:47 +00:00
$("#out-b").append(inputPre +
2023-08-29 08:09:49 +00:00
buildTranStringInput(dir, data[dir]) +
2023-08-29 20:14:29 +00:00
buildTranStringInput("then", data["then"], "text-bg-success") +
2023-08-29 08:09:49 +00:00
buildTranStringInput(nextDir, data[nextDir]) +
2023-08-28 01:18:47 +00:00
inputPost);
}
});
2022-11-23 04:15:30 +00:00
2023-08-28 01:18:47 +00:00
$.each(distance, function(i, dist){
var dir = direction[i % direction.length];
$("#out-c").append(inputPre +
2023-08-29 08:09:49 +00:00
buildTranStringInput(dist, data[dist], "text-bg-warning") +
buildTranStringInput(dir, data[dir]) +
2023-08-28 01:18:47 +00:00
inputPost);
});
2023-01-01 20:54:44 +00:00
2023-08-28 01:18:47 +00:00
$.each(distance, function(i, dist){
2023-08-29 08:09:49 +00:00
var dir = direction[direction.length - ((i+6) % direction.length+1)];
2023-08-28 01:18:47 +00:00
// can't "then" past your destination
if (dir != "destination") {
var nextDir = direction[(i + 1) % direction.length];
$("#out-d").append(inputPre +
2023-08-29 08:09:49 +00:00
buildTranStringInput(dist, data[dist], "text-bg-warning") +
buildTranStringInput(dir, data[dir]) +
2023-08-29 20:14:29 +00:00
buildTranStringInput("then", data["then"], "text-bg-success") +
2023-08-29 08:09:49 +00:00
buildTranStringInput(nextDir, data[nextDir]) +
2023-08-28 01:18:47 +00:00
inputPost);
}
});
2023-01-01 20:54:44 +00:00
2023-08-28 01:18:47 +00:00
$.each(distance, function(i, dist){
var dir = direction[i % direction.length];
2023-01-01 20:54:44 +00:00
2023-08-29 08:09:49 +00:00
if (dir != "destination") {
// can't "arrive onto" your destination
2023-09-01 01:29:44 +00:00
//
// TODO: this entire section gets largely duplicated in the fmtModalForm handler
//
2023-08-29 08:09:49 +00:00
var fmtObj = {
dir: dir,
dist: dist,
nextStr: ((i+1) % nextStreets.length)
};
var fmtKey = btoa(JSON.stringify(fmtObj));
window.fmtSoundData[fmtKey] = fmtObj;
var streetDir;
// overwrite the direction string if present for street TTS
if (data[dir+"_street"]) {
streetDir = buildTranStringInput(dir+"_street", data[dir+"_street"]);
} else {
streetDir = buildTranStringInput(dir, data[dir]);
}
2023-01-01 20:54:44 +00:00
2023-09-01 01:29:44 +00:00
var streetDirVerb;
if (data[dir+"_street_verb"])
streetDirVerb = data[dir+"_street_verb"];
2023-09-03 01:58:16 +00:00
var nextStreetStr = nextStreets[(i+1) % nextStreets.length];
var streetFmt;
// dir exit is a highway exit, otherwise presence of a [ means highway turn (entrance/merge)
if (dir == "exit" || nextStreetStr[0] == "["){
// long highway exit form, "[In 500 ft] take exit [12] for [Main Street] towards [London]" (no particle since it's already set)
// the text format from OM is: "[243]: [CA 1] Pacific Coast Highway > Monterey Bay; San Francisco"
// the format string is: [1: In 100m] [2: take exit %s / follow signs] [3: for the [%s] expressway / for [%s]] [4: towards %s]");
streetFmt = buildFmtStringInput('dist_direction_onto_exit', data['dist_direction_onto_exit'], "text-bg-light", fmtKey);
streetFmt = streetFmt.replace("%1$s", buildTranStringInput(dist, data[dist], "text-bg-warning"));
var nextStreetObj = parseNextStreet(nextStreetStr);
// determine if this is an exit or an entrance/merge
if (dir == "exit") {
window.fmtSoundData["take_exit"] = { nextStr: ((i+1) % nextStreets.length) };
var exitStr = buildFmtStringInput("take_exit", data["take_exit"], "text-bg-primary", "take_exit"); // editable
exitStr = exitStr.replace("%s", buildTranStringInput("", nextStreetObj.exitNum, "text-bg-secondary", false, "nested")); // non-editable, nested
streetFmt = streetFmt.replace("%2$s", exitStr); // shove it in (nesting!)
} else { // nextStreetStr[0] == "[", i.e. an entrance/merge
streetFmt = streetFmt.replace("%2$s", buildTranStringInput("follow_signs", data["follow_signs"])); //editable
}
// TODO: we should never get to a place where this else is all we do,
// i.e. "In 100m follow signs" and then nothing, since [ should indicate an exit or an expressway
if (nextStreetObj.nextExpwy) {
var expwyStr = buildTranStringInput("for_the_expressway", data["for_the_expressway"]); // editable
expwyStr = expwyStr.replace("%s", buildTranStringInput("", nextStreetObj.nextExpwy, "text-bg-secondary", false, "nested")); // non-editable, nested
streetFmt = streetFmt.replace("%3$s", expwyStr); // non-editable
} else if (nextStreetObj.nextRoad) {
var roadStr = buildTranStringInput("for_road", data["for_road"]); // editable
roadStr = roadStr.replace("%s", buildTranStringInput("", nextStreetObj.nextRoad, "text-bg-secondary", false, "nested")); // non-editable, nested
streetFmt = streetFmt.replace("%3$s", roadStr); // non-editable
} else {
streetFmt = streetFmt.replace("%4$s", ""); // nothing
}
if (nextStreetObj.destination) {
var towardsStr = buildTranStringInput("towards", data["towards"]); // editable
towardsStr = towardsStr.replace("%s", buildTranStringInput("", nextStreetObj.destination, "text-bg-secondary", false, "nested")); // non-editable, nested
streetFmt = streetFmt.replace("%4$s", towardsStr); // non-editable
} else {
streetFmt = streetFmt.replace("%4$s", ""); // nothing
}
} else {
// short turn form, "[In 500 ft] [turn right] onto [Main Street]" with optional 4th particle for "turn right"
streetFmt = buildFmtStringInput('dist_direction_onto_street', data['dist_direction_onto_street'], "text-bg-dark", fmtKey);
streetFmt = streetFmt.replace("%1$s", buildTranStringInput(dist, data[dist], "text-bg-warning"));
streetFmt = streetFmt.replace("%2$s", streetDir);
streetFmt = streetFmt.replace("%3$s", buildTranStringInput("", nextStreets[(i+1) % nextStreets.length], "text-bg-secondary", false)); // non-editable
streetFmt = streetFmt.replace("%4$s", buildTranStringInput(dir+"_street_verb", streetDirVerb, "text-bg-danger")); //optional
}
2023-08-29 08:09:49 +00:00
$("#out-e").append(inputPre +
2023-09-01 01:29:44 +00:00
streetFmt +
2023-08-29 08:09:49 +00:00
inputPost);
}
2023-08-28 01:18:47 +00:00
});
2023-08-29 08:09:49 +00:00
2023-08-28 01:18:47 +00:00
$.each(distance, function(i, dist){
2023-08-29 08:09:49 +00:00
var dir = direction[direction.length - (i % direction.length+1)];
2023-08-28 01:18:47 +00:00
2023-08-29 08:09:49 +00:00
if (dir != "destination") {
// can't "arrive onto" your destination
var nextDir = direction[(i + 1) % direction.length];
2023-08-28 01:18:47 +00:00
2023-09-01 01:29:44 +00:00
//
// TODO: this entire section gets largely duplicated in the fmtModalForm handler
//
2023-08-29 08:09:49 +00:00
var fmtObj = {
dir: dir,
dist: dist,
nextStr: ((i+1) % nextStreets.length)
};
var fmtKey = btoa(JSON.stringify(fmtObj));
window.fmtSoundData[fmtKey] = fmtObj;
2023-09-01 01:29:44 +00:00
var streetFmt = buildFmtStringInput('dist_direction_onto_street', data['dist_direction_onto_street'], "text-bg-dark", fmtKey);
2023-08-29 08:09:49 +00:00
var streetDir;
// overwrite the direction string if present for street TTS
if (data[dir+"_street"]) {
streetDir = buildTranStringInput(dir+"_street", data[dir+"_street"]);
} else {
streetDir = buildTranStringInput(dir, data[dir]);
}
2023-09-01 01:29:44 +00:00
var streetDirVerb;
if (data[dir+"_street_verb"])
streetDirVerb = data[dir+"_street_verb"];
streetFmt = streetFmt.replace("%1$s", buildTranStringInput(dist, data[dist], "text-bg-warning"));
streetFmt = streetFmt.replace("%2$s", streetDir);
streetFmt = streetFmt.replace("%3$s", buildTranStringInput("", nextStreets[(i+1) % nextStreets.length], "text-bg-secondary", false)); // non-editable
streetFmt = streetFmt.replace("%4$s", buildTranStringInput(dir+"_street_verb", streetDirVerb, "text-bg-danger")); //optional
2023-08-29 08:09:49 +00:00
$("#out-f").append(inputPre +
2023-09-01 01:29:44 +00:00
streetFmt +
2023-08-29 08:09:49 +00:00
buildTranStringInput('then', data['then'], "text-bg-success") +
buildTranStringInput(nextDir, data[nextDir], "text-bg-primary") +
inputPost);
}
2023-08-28 01:18:47 +00:00
});
$("#out-g").append(inputPre +
2023-08-29 08:09:49 +00:00
buildTranStringInput('you_have_reached_the_destination', data['you_have_reached_the_destination']) +
2023-08-28 01:18:47 +00:00
inputPost);
$("#out-g").append(inputPre +
2023-08-29 08:09:49 +00:00
buildTranStringInput('unknown_camera', data['unknown_camera']) +
2023-08-28 01:18:47 +00:00
inputPost);
2023-01-01 20:54:44 +00:00
2023-08-29 08:09:49 +00:00
$("#fmtModalForm").submit(function(){
// for dist_direction_onto_street only
2023-08-28 01:18:47 +00:00
var changes = [];
2023-08-29 08:09:49 +00:00
$("#fmtModal").modal('hide');
2023-08-28 01:18:47 +00:00
2023-08-29 08:09:49 +00:00
window.modifiedSoundData['dist_direction_onto_street'] = $(document.getElementById('fmtModalText')).val();
2023-08-28 01:18:47 +00:00
2023-08-29 08:09:49 +00:00
var soundKeys = Object.keys(window.originalSoundData);
var out = "";
var locale = $("#localeSelect").val();
2023-08-28 01:18:47 +00:00
2023-08-29 08:09:49 +00:00
for (i=0;i< soundKeys.length ; i + + ) {
var soundKey = soundKeys[i];
var orig = window.originalSoundData[soundKey];
var mod = window.modifiedSoundData[soundKey];
2023-08-28 01:18:47 +00:00
2023-08-29 08:09:49 +00:00
if (mod != orig) {
out += " [" + soundKey + "]\n" +
"- " + locale + " = " + orig + "\n" +
"+ " + locale + " = " + mod + "\n";
$(".stringTxt[data-key='"+soundKey+"']").val(mod); // update all instances
2023-08-28 01:18:47 +00:00
}
2023-08-29 08:09:49 +00:00
}
2023-08-28 01:18:47 +00:00
2023-08-29 08:09:49 +00:00
// also update all dist_direction_onto_street instances (rebuild from metadata)
$(".stringTxt[data-key='dist_direction_onto_street']").each(function(i,o){
var fmtKey = $(o).data("fmtkey");
var fmtObj = window.fmtSoundData[fmtKey];
2023-08-28 01:18:47 +00:00
2023-08-29 08:09:49 +00:00
var dir = fmtObj.dir;
var dist = fmtObj.dist;
var nextStr = fmtObj.nextStr;
2023-08-28 01:18:47 +00:00
2023-09-01 01:29:44 +00:00
//
// TODO: this entire section gets largely duplicated in the showData function
//
2023-08-29 08:09:49 +00:00
var streetFmt = buildFmtStringInput('dist_direction_onto_street', window.modifiedSoundData['dist_direction_onto_street'], "text-bg-dark", fmtKey);
2023-08-28 01:18:47 +00:00
2023-08-29 08:09:49 +00:00
var streetDir;
// overwrite the direction string if present for street TTS
if (window.modifiedSoundData[dir+"_street"]) {
streetDir = buildTranStringInput(dir+"_street", window.modifiedSoundData[dir+"_street"]);
} else {
streetDir = buildTranStringInput(dir, window.modifiedSoundData[dir]);
}
2023-08-28 01:18:47 +00:00
2023-09-01 01:29:44 +00:00
var streetDirVerb;
if (window.modifiedSoundData[dir+"_street_verb"])
streetDirVerb = window.modifiedSoundData[dir+"_street_verb"];
2023-08-29 08:09:49 +00:00
streetFmt = streetFmt.replace("%1$s", buildTranStringInput(dist, window.modifiedSoundData[dist], "text-bg-warning")); // no full stops
streetFmt = streetFmt.replace("%2$s", streetDir); // no full stops
2023-08-29 20:14:29 +00:00
streetFmt = streetFmt.replace("%3$s", buildTranStringInput("", nextStreets[nextStr], "text-bg-secondary", false)); // non-editable
2023-09-01 01:29:44 +00:00
streetFmt = streetFmt.replace("%4$s", buildTranStringInput(dir+"_street_verb", streetDirVerb, "text-bg-danger")); //optional
2023-08-29 08:09:49 +00:00
$(o).parent().parent().html(streetFmt);
});
$("#submitpopup textarea").text("Translation change request:\n```\n"+
"# data/strings/sound.txt\n" + out + "```"
2023-08-28 01:18:47 +00:00
);
2023-08-29 08:09:49 +00:00
$("#submitpopup").show();
loadEventHandlers(); // we've just redone a bunch of html, reload handlers
return false; // prevent navigation
2022-11-23 04:15:30 +00:00
});
2023-08-29 08:09:49 +00:00
loadEventHandlers();
2023-08-28 01:18:47 +00:00
}
function loadLocale(){
2023-09-02 02:52:42 +00:00
var commitHash = "f3ada222856b57ce75d5fc53be09d1312c59a64b";
2023-08-28 01:18:47 +00:00
var sourceUrl = "https://raw.githubusercontent.com/organicmaps/organicmaps/"+commitHash+"/data/strings/sound.txt";
$("#sourceUrl").attr("href", sourceUrl).text("sound.txt");
if (!window.soundTxtRaw) {
$.get(sourceUrl, function(rawData){
window.soundTxtRaw = rawData;
showData();
});
} else {
showData();
}
2022-11-23 04:15:30 +00:00
}
< / script >
< / body >
< / html >
2023-01-01 20:54:44 +00:00