825 lines
26 KiB
JavaScript
825 lines
26 KiB
JavaScript
// termicDeployedVersion should only be changed on major releases.
|
|
// We don't want to annoy users with the info banner on every minor release!
|
|
const termicDeployedVersion = 1.5;
|
|
const currentYearFooter = new Date().getFullYear();
|
|
// Version shown in the footer and the update banner
|
|
const currentVersionHTML = "1.5.0";
|
|
const urlParams = new URLSearchParams(window.location.search);
|
|
const searchOptions = document.getElementsByClassName("search-option");
|
|
const periodButtons = document.getElementsByName("period-button");
|
|
var termicStoredVersion = localStorage.getItem("termicStoredVersion");
|
|
var isLoading = false;
|
|
var lengthGlossary = 0;
|
|
var lengthTm = 0;
|
|
var isTwoColumnLayout = false;
|
|
|
|
$(function () {
|
|
const q = urlParams.get("q"); // Query (search term)
|
|
const sl = urlParams.get("sl"); // Source language
|
|
const tl = urlParams.get("tl"); // Target language
|
|
const o = urlParams.get("o"); // Search option
|
|
const rc_gl = urlParams.get("rc_gl"); // Glossary result count
|
|
const rc_tm = urlParams.get("rc_tm"); // TM result count
|
|
const cs = urlParams.get("cs"); // Case sensitive?
|
|
const p = urlParams.get("p"); // Data period
|
|
|
|
var sourceLang = sl || $("#source-lang").val();
|
|
var targetLang = tl || $("#target-lang").val();
|
|
var searchOption = o || "unexact_match"; // default value
|
|
var resultCountGl = rc_gl || parseInt($("#result-count-glossary").val());
|
|
var resultCountTm = rc_tm || parseInt($("#result-count-tm").val());
|
|
var caseSensitive = cs || 0; // default value
|
|
var modes = ["glossary", "tm"]; // default values
|
|
var dataPeriod =
|
|
p || $("button[name='period-button'][data-active='active']").val();
|
|
|
|
// Focus search input on page load
|
|
$("#term").focus();
|
|
|
|
if (!isTouchDevice()) {
|
|
// Do not use select2 dropdown on mobile devices
|
|
$("#source-lang, #target-lang").select2({
|
|
templateResult: appendIcons,
|
|
templateSelection: appendIcons,
|
|
});
|
|
}
|
|
|
|
// --- Begining of Local Storage ---
|
|
|
|
// Show update banner if termic version is different from stored version
|
|
if (termicStoredVersion != termicDeployedVersion) {
|
|
$("#info-banner").css("display", "flex");
|
|
}
|
|
|
|
if (localStorage.getItem("source-lang") && !sl) {
|
|
$("#source-lang")
|
|
.val(localStorage.getItem("source-lang"))
|
|
.trigger("change.select2");
|
|
}
|
|
|
|
if (localStorage.getItem("target-lang") && !tl) {
|
|
$("#target-lang")
|
|
.val(localStorage.getItem("target-lang"))
|
|
.trigger("change.select2");
|
|
}
|
|
|
|
if (localStorage.getItem("result-count-glossary") && !rc_gl) {
|
|
$("#result-count-glossary")
|
|
.val(localStorage.getItem("result-count-glossary"))
|
|
.trigger("change.select2");
|
|
}
|
|
|
|
if (localStorage.getItem("result-count-tm") && !rc_tm) {
|
|
$("#result-count-tm")
|
|
.val(localStorage.getItem("result-count-tm"))
|
|
.trigger("change.select2");
|
|
}
|
|
// --- End of Local Storage ---
|
|
|
|
// --- Beginning of Event Listeners ---
|
|
$("#hamburger-btn").on("click", function () {
|
|
if ($("#hamburger").hasClass("inactive")) {
|
|
$("#hamburger").addClass("active").removeClass("inactive");
|
|
} else {
|
|
$("#hamburger").addClass("inactive").removeClass("active");
|
|
}
|
|
});
|
|
|
|
$(document).on("select2:open", () => {
|
|
document.querySelector(".select2-search__field").focus();
|
|
});
|
|
|
|
$(".toggle").on("click", function () {
|
|
// Get the name of the associated dropdown to toggle
|
|
// and item to add/remove from modes list (glossary or TM)
|
|
let mode = this.id.split("-")[1];
|
|
let resultCountDropdown = "#result-count-" + mode;
|
|
let periodButton = "button[name='period-button']";
|
|
|
|
if (this.checked) {
|
|
$($(this).data("target")).css("opacity", "1");
|
|
$(resultCountDropdown).prop("disabled", false);
|
|
if (mode == "tm") $(periodButton).prop("disabled", false);
|
|
if (modes.indexOf(mode) === -1) modes.push(mode);
|
|
} else {
|
|
$($(this).data("target")).css("opacity", "0.5");
|
|
$(resultCountDropdown).prop("disabled", true);
|
|
if (mode == "tm") $(periodButton).prop("disabled", true);
|
|
modes.splice(modes.indexOf(mode), 1);
|
|
}
|
|
|
|
if (
|
|
!$("#toggle-glossary").is(":checked") &&
|
|
!$("#toggle-tm").is(":checked")
|
|
) {
|
|
$("#search-btn").prop("disabled", true);
|
|
} else {
|
|
$("#search-btn").prop("disabled", false);
|
|
}
|
|
});
|
|
|
|
// Search options handling
|
|
handleButtonsState(searchOptions, (option) => {
|
|
searchOption = option.value;
|
|
});
|
|
|
|
// Period buttons handling
|
|
handleButtonsState(periodButtons, (button) => {
|
|
dataPeriod = button.value;
|
|
periodDesc = $(".period-description");
|
|
dataPeriod == "2020+"
|
|
? periodDesc.text(periodDesc.attr("desc-2020"))
|
|
: periodDesc.text(periodDesc.attr("desc-2017"));
|
|
});
|
|
|
|
$("#target-lang, #source-lang").on("change", function () {
|
|
checkDataPeriodCompatibility();
|
|
});
|
|
|
|
// Result count select handling
|
|
$("select#result-count-glossary, select#result-count-tm").on(
|
|
"change",
|
|
function () {
|
|
if ($(this).is("#result-count-glossary")) {
|
|
resultCountGl = parseInt($("#result-count-glossary").val());
|
|
} else if ($(this).is("#result-count-tm")) {
|
|
resultCountTm = parseInt($("#result-count-tm").val());
|
|
}
|
|
}
|
|
);
|
|
|
|
$("#highlight-btn").on("click", function () {
|
|
highlightResults(this);
|
|
});
|
|
|
|
$("#two-col-btn").on("click", function () {
|
|
if (!isTouchDevice()) {
|
|
switchResultsLayout();
|
|
} else {
|
|
showToast("Two-column layout not available on mobile devices");
|
|
}
|
|
});
|
|
|
|
$("#close-error").on("click", function () {
|
|
$("#error-banner").css("display", "none");
|
|
});
|
|
|
|
$("#close-info, #changelog-link").on("click", function () {
|
|
localStorage.setItem("termicStoredVersion", termicDeployedVersion);
|
|
$("#info-banner").css("display", "none");
|
|
});
|
|
|
|
$(".additional-match-option").on("click", function () {
|
|
if (this.dataset.active == "inactive" && this.value == "case_sensitivity") {
|
|
this.dataset.active = "active";
|
|
caseSensitive = 1;
|
|
} else {
|
|
this.dataset.active = "inactive";
|
|
caseSensitive = 0;
|
|
}
|
|
});
|
|
|
|
$(".search-option[value='regex']").on("click", function () {
|
|
$(".additional-match-option[value='case_sensitivity']").prop(
|
|
"disabled",
|
|
true
|
|
);
|
|
$(".additional-match-option[value='case_sensitivity']").prop(
|
|
"title",
|
|
"Case Sensitivity is not available for RegExp searches"
|
|
);
|
|
});
|
|
|
|
$(".search-option:not([value='regex'])").on("click", function () {
|
|
$(".additional-match-option[value='case_sensitivity']").prop(
|
|
"disabled",
|
|
false
|
|
);
|
|
$(".additional-match-option[value='case_sensitivity']").prop(
|
|
"title",
|
|
"Case Sensitivity"
|
|
);
|
|
});
|
|
|
|
$("#swap-btn").on("click", function () {
|
|
swapLanguages();
|
|
});
|
|
// --- End of Event Listeners ---
|
|
|
|
if (q) {
|
|
// Update term input box with the value of the q parameter and launch search
|
|
$("#term").val(q);
|
|
var term = $("#term").val();
|
|
if (!modes || modes.length <= 0) {
|
|
showBanner("error", "Please select at least one search mode.");
|
|
} else if (sourceLang == undefined || targetLang == undefined) {
|
|
showBanner("error", "Please select a source and target language.");
|
|
} else if (sourceLang == targetLang) {
|
|
showBanner("error", "Source and target languages cannot be identical.");
|
|
} else {
|
|
request(
|
|
term,
|
|
sourceLang,
|
|
targetLang,
|
|
resultCountGl,
|
|
resultCountTm,
|
|
searchOption,
|
|
caseSensitive,
|
|
modes,
|
|
dataPeriod
|
|
);
|
|
}
|
|
}
|
|
|
|
if (sl) {
|
|
// Update target language dropdown with the value of the sl parameter
|
|
$("#source-lang").val(sl).trigger("change.select2");
|
|
}
|
|
|
|
if (tl) {
|
|
// Update target language dropdown with the value of the tl parameter
|
|
$("#target-lang").val(tl).trigger("change.select2");
|
|
}
|
|
|
|
if (rc_gl) {
|
|
// Update glossary result count dropdown with the value of the rc_gl parameter
|
|
$("#result-count-glossary").val(rc_gl).trigger("change.select2");
|
|
}
|
|
|
|
if (rc_tm) {
|
|
// Update TM result count dropdown with the value of the rc_tm parameter
|
|
$("#result-count-tm").val(rc_tm).trigger("change.select2");
|
|
}
|
|
|
|
if (o && o != "unexact_match") {
|
|
// Update match type buttons to active
|
|
$("button[value='unexact_match']").attr("data-active", "inactive");
|
|
$(`button[value='${o}']`).attr("data-active", "active");
|
|
}
|
|
|
|
if (cs == 1) {
|
|
// Update case sensitivity button to active
|
|
$("button[value='case_sensitivity']").attr("data-active", "active");
|
|
}
|
|
|
|
if (p) {
|
|
// Update period buttons to active
|
|
periodButtons.forEach((b) => $(b).attr("data-active", "inactive"));
|
|
$(`button[value='${p}']`).attr("data-active", "active");
|
|
}
|
|
|
|
$("#search-form").on("submit", function (e) {
|
|
e.preventDefault();
|
|
var term = $("#term").val();
|
|
var sourceLang = $("#source-lang").val();
|
|
var targetLang = $("#target-lang").val();
|
|
if (!modes || modes.length <= 0) {
|
|
showBanner("error", "Please select at least one search mode.");
|
|
} else if (sourceLang == undefined || targetLang == undefined) {
|
|
showBanner("error", "Please select a source and target language.");
|
|
} else if (sourceLang == targetLang) {
|
|
showBanner("error", "Source and target languages cannot be identical.");
|
|
} else {
|
|
if (!isLoading) {
|
|
isLoading = true;
|
|
$("#warning-banner").css("display", "none");
|
|
$("#error-banner").css("display", "none");
|
|
request(
|
|
term,
|
|
sourceLang,
|
|
targetLang,
|
|
resultCountGl,
|
|
resultCountTm,
|
|
searchOption,
|
|
caseSensitive,
|
|
modes,
|
|
dataPeriod
|
|
);
|
|
}
|
|
}
|
|
});
|
|
});
|
|
|
|
/**
|
|
* Request search results from the server
|
|
* @param {string} term - Term to search for
|
|
* @param {string} sourceLang - Source language
|
|
* @param {string} targetLang - Target language
|
|
* @param {number} resultCountGl - Number of glossary results to return
|
|
* @param {number} resultCountTm - Number of TM results to return
|
|
* @param {string} searchOption - Search option (exact_match, unexact_match, regex)
|
|
* @param {number} caseSensitive - Case sensitivity (0 or 1)
|
|
* @param {Array} modes - Array of modes to search (glossary, tm)
|
|
* @param {string} dataPeriod - Data period (2017, 2020+)
|
|
*/
|
|
function request(
|
|
term,
|
|
sourceLang,
|
|
targetLang,
|
|
resultCountGl,
|
|
resultCountTm,
|
|
searchOption,
|
|
caseSensitive,
|
|
modes,
|
|
dataPeriod
|
|
) {
|
|
$("#target-lang").prop("disabled", true);
|
|
$("#search-btn").prop("disabled", true);
|
|
$("#no-results").css("display", "none");
|
|
$("#loader").css("display", "flex");
|
|
$.ajax({
|
|
headers: {
|
|
Accept: "application/json",
|
|
"Content-Type": "application/json",
|
|
},
|
|
url: "/",
|
|
type: "POST",
|
|
dataType: "json",
|
|
data: JSON.stringify({
|
|
term: term,
|
|
source_lang: sourceLang,
|
|
target_lang: targetLang,
|
|
result_count_gl: resultCountGl,
|
|
result_count_tm: resultCountTm,
|
|
search_option: searchOption,
|
|
case_sensitive: caseSensitive,
|
|
modes: modes,
|
|
data_period: dataPeriod,
|
|
}),
|
|
success: function (response) {
|
|
lengthGlossary = Object.values(response)[0].length;
|
|
// select last array for length of TM results
|
|
lengthTm = Object.values(response).slice(-1)[0].length;
|
|
|
|
// Update URL
|
|
let params = {
|
|
q: term,
|
|
sl: sourceLang,
|
|
tl: targetLang,
|
|
o: searchOption,
|
|
rc_gl: resultCountGl,
|
|
rc_tm: resultCountTm,
|
|
cs: caseSensitive,
|
|
p: dataPeriod,
|
|
};
|
|
let newParams = new URLSearchParams(params);
|
|
const newUrl = window.location.pathname + "?" + newParams.toString();
|
|
window.history.pushState({ path: newUrl }, "", newUrl);
|
|
|
|
// Get results
|
|
console.log(Object.values(response));
|
|
|
|
switch (true) {
|
|
case $("#toggle-glossary").is(":checked") &&
|
|
!$("#toggle-tm").is(":checked"):
|
|
$("#tm-results").css("display", "none");
|
|
getGlossary(response, lengthGlossary);
|
|
break;
|
|
case !$("#toggle-glossary").is(":checked") &&
|
|
$("#toggle-tm").is(":checked"):
|
|
$("#glossary-results").css("display", "none");
|
|
getExcerpts(response, lengthTm, dataPeriod);
|
|
break;
|
|
default:
|
|
getGlossary(response, lengthGlossary);
|
|
getExcerpts(response, lengthTm, dataPeriod);
|
|
}
|
|
|
|
let sourceLangText = $("#source-lang option:selected").text();
|
|
let targetLangText = $("#target-lang option:selected").text();
|
|
|
|
// Update table headers with source and target languages
|
|
$(".source-lang-label").html("[" + sourceLangText + "]");
|
|
$(".target-lang-label").html("[" + targetLangText + "]");
|
|
|
|
$("#results").removeClass("hidden");
|
|
$("#results").addClass("block");
|
|
|
|
$("#error-banner").css("display", "none");
|
|
$("#loader").css("display", "none");
|
|
|
|
document.title = `termic :: ${term} (${sourceLangText} → ${targetLangText})`;
|
|
|
|
// Local storage set
|
|
localStorage.setItem("source-lang", sourceLang);
|
|
localStorage.setItem("target-lang", targetLang);
|
|
localStorage.setItem("result-count-glossary", resultCountGl);
|
|
localStorage.setItem("result-count-tm", resultCountTm);
|
|
},
|
|
error: function (jqXHR, textStatus, errorThrown) {
|
|
let errMsg = JSON.parse(jqXHR.responseText).msg;
|
|
|
|
$("#error-banner").css("display", "flex");
|
|
$("#error-banner #error-msg").html(
|
|
textStatus + ": " + errorThrown + " — " + errMsg
|
|
);
|
|
$("#loader").css("display", "none");
|
|
},
|
|
complete: function () {
|
|
// fix bug that prevents new search
|
|
isLoading = false;
|
|
$("#target-lang").prop("disabled", false);
|
|
$("#search-btn").prop("disabled", false);
|
|
$("#highlight-btn").prop("disabled", false);
|
|
$("#two-col-btn").prop("disabled", false);
|
|
if (lengthGlossary > 0 || lengthTm > 0) {
|
|
activateSortRows();
|
|
|
|
// On mobile devices, activate copy button
|
|
// and disable two-column display button
|
|
//
|
|
// We're fake-disabling the button by adding some "disabled" style
|
|
// instead of using prop because we still want the button to be clickable
|
|
// for the toast to appear
|
|
return isTouchDevice()
|
|
? (activateCopyWithTap(), $("#two-col-btn").addClass("disabled"))
|
|
: activateCopyWithBtn();
|
|
} else if (lengthGlossary == 0 && lengthTm == 0) {
|
|
$("#no-results").css("display", "flex");
|
|
$("#glossary-results").css("display", "none");
|
|
$("#tm-results").css("display", "none");
|
|
}
|
|
},
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Get glossary results and display them
|
|
* @param {Response} response - Response from server
|
|
* @param {number} length - Number of glossary results
|
|
*/
|
|
function getGlossary(response, length) {
|
|
if (length > 0) {
|
|
//console.log("Found " + length + " glossary entries");
|
|
$("#glossary-results").css("display", "block");
|
|
let resultsGl = "";
|
|
for (let i = 0; i < length; i++) {
|
|
resultsGl += `
|
|
<tr>
|
|
<td data-attribute="source">${response.gl_source[i]}<br>
|
|
<i>(${response.gl_source_pos[i]
|
|
.substring(response.gl_source_pos[i].indexOf(":") + 1)
|
|
.trim()
|
|
.toLowerCase()})</i>
|
|
</td>
|
|
|
|
<td>${response.gl_translation[i]}<br>
|
|
<i>(${response.gl_target_pos[i]
|
|
.substring(response.gl_target_pos[i].indexOf(":") + 1)
|
|
.trim()
|
|
.toLowerCase()})</i>
|
|
</td>
|
|
|
|
<td>${response.gl_source_def[i]
|
|
.substring(response.gl_source_def[i].indexOf(":") + 1)
|
|
.trim()}
|
|
</td>
|
|
|
|
<td><i class="fa-solid fa-copy"></i></td>
|
|
</tr>
|
|
`;
|
|
}
|
|
$("#glossary-results-table").html(resultsGl);
|
|
$("#glossary-nb").html(` (${length} results)`);
|
|
$("#search-filters-container").css("display", "flex");
|
|
} else {
|
|
$("#glossary-results").css("display", "none");
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get TM results and display them
|
|
* @param {Response} response - Response from server
|
|
* @param {number} length - Number of TM results
|
|
*/
|
|
function getExcerpts(response, length, dataPeriod) {
|
|
if (length > 0) {
|
|
//console.log("Found " + length + " TM entries");
|
|
$("#tm-results").css("display", "block");
|
|
let resultsTm = "";
|
|
for (let i = 0; i < length; i++) {
|
|
resultsTm += `
|
|
<tr>
|
|
<td data-attribute="source">${response.tm_source[i]}</td>
|
|
<td>${response.tm_translation[i]}</td>
|
|
<td>${response.tm_cat[i]}</td>
|
|
<td>${response.tm_platform[i]}</td>
|
|
<td>${response.tm_product[i]}</td>
|
|
<td><i class="fa-solid fa-copy"></i></td>
|
|
</tr>
|
|
`;
|
|
}
|
|
$("#tm-results-table").html(resultsTm);
|
|
$("#tm-nb").html(` (${dataPeriod} | ${length} results)`);
|
|
$("#search-filters-container").css("display", "flex");
|
|
highlightVSCodeStrings();
|
|
} else {
|
|
$("#tm-results").css("display", "none");
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Highlight results with mark.js
|
|
* @param {HTMLButtonElement} e - Highlight button
|
|
*/
|
|
function highlightResults(e) {
|
|
if (e.dataset.active == "inactive") {
|
|
e.dataset.active = "active";
|
|
let keyword = $("#term").val();
|
|
|
|
$("td[data-attribute='source']").unmark({
|
|
done: function () {
|
|
$("td[data-attribute='source']").mark(keyword);
|
|
},
|
|
});
|
|
} else {
|
|
$("td[data-attribute='source']").unmark();
|
|
e.dataset.active = "inactive";
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get row content
|
|
* @param {HTMLTableCellElement} row - Row to get content from
|
|
* @returns {string} - Row content
|
|
*/
|
|
function getRowContent(row) {
|
|
const cells = Array.from(row.cells);
|
|
const rowContent = cells
|
|
.map((cell) => cell.textContent.trim())
|
|
.join(" | ")
|
|
.replace(/\s*\n\s*/g, " ")
|
|
.slice(0, -3);
|
|
return rowContent;
|
|
}
|
|
|
|
/**
|
|
* Activate copy fezture with button on non-touch devices
|
|
* and devices which width > 480
|
|
*/
|
|
function activateCopyWithBtn() {
|
|
Array.from(document.getElementsByClassName("fa-copy")).forEach(
|
|
(copyButton) => {
|
|
$(copyButton).on("click", function () {
|
|
const rowContent = getRowContent(copyButton.closest("tr"));
|
|
copyToClipboard(rowContent);
|
|
$(copyButton).addClass("copied");
|
|
setTimeout(() => {
|
|
$(copyButton).removeClass("copied");
|
|
}, 2000);
|
|
});
|
|
}
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Activate copy feature with two-finger tap on touch devices
|
|
* and devices which width <= 480
|
|
*/
|
|
function activateCopyWithTap() {
|
|
$(".fa-copy").css("display", "none");
|
|
$("#tip-tap").css("display", "block");
|
|
Array.from(document.querySelectorAll("tbody")).forEach((tbody) => {
|
|
$(tbody).on("touchstart", function (e) {
|
|
e.stopPropagation();
|
|
if (e.touches.length === 2) {
|
|
const rowContent = getRowContent(e.target.closest("tr"));
|
|
showToast("<p>Row copied</p>");
|
|
copyToClipboard(rowContent);
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Copy row content to clipboard
|
|
* @param {string} text - Row content to copy
|
|
*/
|
|
function copyToClipboard(text) {
|
|
// Reminder: navigator.clipboard requires secure origin!
|
|
navigator.clipboard
|
|
.writeText(text)
|
|
.then(() => console.log("Row content copied to clipboard"))
|
|
.catch((error) => console.error("Failed to copy row content: ", error));
|
|
}
|
|
|
|
/**
|
|
* Show toast to mobile users
|
|
* @param {string} text - Text to display in toast
|
|
*/
|
|
function showToast(text) {
|
|
let toastTimeout = 0;
|
|
$("#toast").html(text);
|
|
$("#toast-container").css("display", "block");
|
|
$("#toast-container").animate({ opacity: 1 }, 500);
|
|
if (toastTimeout) clearTimeout(toastTimeout);
|
|
toastTimeout = setTimeout(() => {
|
|
$("#toast-container").animate({ opacity: 0 }, 500, () => {
|
|
$("#toast-container").css("display", "none");
|
|
});
|
|
}, 2000);
|
|
}
|
|
|
|
/**
|
|
* Activate sort rows feature
|
|
* Modified from https://stackoverflow.com/questions/3160277/jquery-table-sort
|
|
*/
|
|
function activateSortRows() {
|
|
$("th").click(function () {
|
|
var table = $(this).parents("table").eq(0);
|
|
var ths = table.find("th");
|
|
var rows = table
|
|
.find("tr:gt(0)")
|
|
.toArray()
|
|
.sort(comparer($(this).index()));
|
|
this.asc = !this.asc;
|
|
|
|
// Clear existing carets
|
|
Array.from(ths).forEach((th) => {
|
|
$(th).find(".fa-caret-down").removeClass("caret-force-visible");
|
|
$(th).find(".fa-caret-up").removeClass("caret-force-visible");
|
|
});
|
|
|
|
if (!this.asc) {
|
|
rows = rows.reverse();
|
|
$(this).find(".fa-caret-down").addClass("caret-force-visible");
|
|
} else {
|
|
$(this).find(".fa-caret-up").addClass("caret-force-visible");
|
|
}
|
|
for (var i = 0; i < rows.length; i++) {
|
|
{
|
|
table.append(rows[i]);
|
|
}
|
|
}
|
|
});
|
|
|
|
/**
|
|
* Compare rows
|
|
* @param {number} index - Index of the column to sort by
|
|
* @returns {function} - Comparator function to sort rows by index column
|
|
*/
|
|
function comparer(index) {
|
|
return function (a, b) {
|
|
var valA = getCellValue(a, index);
|
|
var valB = getCellValue(b, index);
|
|
return $.isNumeric(valA) && $.isNumeric(valB)
|
|
? valA - valB
|
|
: valA.toString().localeCompare(valB);
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Get cell value
|
|
* @param {HTMLTableCellElement} row - Row to get value from
|
|
* @param {number} index - Index of the column to get value from
|
|
* @returns {string}
|
|
*/
|
|
function getCellValue(row, index) {
|
|
return $(row).children("td").eq(index).text();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Check if device is touch-enabled
|
|
* https://stackoverflow.com/questions/4817029/whats-the-best-way-to-detect-a-touch-screen-device-using-javascript
|
|
* @returns {boolean} - true if the device is touch, false otherwise
|
|
*/
|
|
function isTouchDevice() {
|
|
return (
|
|
("ontouchstart" in window ||
|
|
// temp fix for https://stackoverflow.com/questions/69125308/navigator-maxtouchpoints-256-on-desktop
|
|
(navigator.maxTouchPoints > 0 && navigator.maxTouchPoints != 256) ||
|
|
// if the screen is >480, there's enough space for the copy button; no need to activate touch mode
|
|
navigator.msMaxTouchPoints > 0) &&
|
|
window.screen.width <= 480
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Show banner with text
|
|
* @param {string} type - Type of banner to show (error, info)
|
|
* @param {string} text - Text to display in error banner
|
|
*/
|
|
function showBanner(type, text) {
|
|
$(`#${type}-banner`).css("display", "flex");
|
|
$(`#${type}-banner #${type}-msg`).html(text);
|
|
}
|
|
|
|
/**
|
|
* Hide banner
|
|
* @param {string} type - Type of banner to hide (error, info)
|
|
*/
|
|
function hideBanner(type) {
|
|
$(`#${type}-banner`).css("display", "none");
|
|
}
|
|
|
|
/**
|
|
* Handle collection of buttons so that only one button can be active at a time
|
|
* @param {HTMLCollectionOf<Element>} buttonsCollection - Collection of buttons to handle
|
|
* @param {function} callback - Callback function to execute on button click
|
|
*/
|
|
function handleButtonsState(buttonsCollection, callback) {
|
|
Array.from(buttonsCollection).forEach((button) => {
|
|
$(button).on("click", function () {
|
|
Array.from(buttonsCollection).forEach((otherButton) => {
|
|
otherButton.dataset.active = "inactive";
|
|
});
|
|
this.dataset.active =
|
|
this.dataset.active == "inactive" ? "active" : "inactive";
|
|
|
|
callback(this);
|
|
});
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Swap source and target language dropdown selections.
|
|
*/
|
|
function swapLanguages() {
|
|
let oldSourceLang = $("#source-lang").val();
|
|
$("#source-lang").val($("#target-lang").val()).trigger("change.select2");
|
|
$("#target-lang").val(oldSourceLang).trigger("change.select2");
|
|
}
|
|
|
|
/**
|
|
* Switch between horizontal and vertical (two-column) layout
|
|
*/
|
|
function switchResultsLayout() {
|
|
if (!isTwoColumnLayout) {
|
|
isTwoColumnLayout = true;
|
|
$("#results").removeClass("block");
|
|
$(
|
|
"#results, #glossary-results, #tm-results, #tm-title, #glossary-title, #tm, #glossary, #glossary thead, #tm thead"
|
|
).addClass("two-col");
|
|
$("button[value='two-col']").attr("data-active", "active");
|
|
} else {
|
|
isTwoColumnLayout = false;
|
|
$("#results").addClass("block");
|
|
$(
|
|
"#results, #glossary-results, #tm-results, #tm-title, #glossary-title, #tm, #glossary, #glossary thead, #tm thead"
|
|
).removeClass("two-col");
|
|
$("button[value='two-col']").attr("data-active", "inactive");
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Append VSCode icon to VSCode strings in search results
|
|
*/
|
|
function highlightVSCodeStrings() {
|
|
if ($("#tm-results-table").find("td:contains('VSCode')")) {
|
|
$("td:contains('VSCode')").html(
|
|
"<img src='static/images/svgs/vscode-icon.svg' class='vscode-icon'><td>VSCode</td>"
|
|
);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Check if the selected languages support the selected data period
|
|
*/
|
|
function checkDataPeriodCompatibility() {
|
|
let compatibleLangs = $(
|
|
"#source-lang option[data-2017='true'], #target-lang option[data-2017='true']"
|
|
)
|
|
.map(function () {
|
|
return $(this).val();
|
|
})
|
|
.get();
|
|
|
|
if (
|
|
!compatibleLangs.includes($("#source-lang").val()) ||
|
|
!compatibleLangs.includes($("#target-lang").val())
|
|
) {
|
|
$("button.period-2017").prop("disabled", true);
|
|
$("button.period-2017").prop(
|
|
"title",
|
|
"One of the languages does not support this data period"
|
|
);
|
|
$("button.period-2017").attr("data-active", "inactive");
|
|
$("button.period-2020").attr("data-active", "active");
|
|
$(".period-description").text($(".period-description").attr("desc-2020"));
|
|
} else {
|
|
$("button.period-2017").prop("disabled", false);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Append icons to select2.js dropdown options
|
|
* @param {Object} e - select2.js event data object (https://select2.org/programmatic-control/events#event-data)
|
|
* @returns {string}
|
|
*/
|
|
function appendIcons(e) {
|
|
let polygonAndVSCodeIcons = `<img src="static/images/svgs/vscode-icon.svg" height="15" width="15" class="polygon" style="position: relative; bottom: 2px;"><img src="static/images/svgs/polygon.svg" height="15" width="15" class="polygon" style="position: relative; bottom: 2px;"><span>${e.text}</span>`;
|
|
let polygonIcon = `<img src="static/images/svgs/polygon.svg" height="15" width="15" class="polygon" style="position: relative; bottom: 2px;"><span>${e.text}</span>`;
|
|
let VSCodeIcon = `<img src="static/images/svgs/vscode-icon.svg" height="15" width="15" class="polygon" style="position: relative; bottom: 2px;"><span>${e.text}</span>`;
|
|
|
|
if ($(e.element).attr("data-2017") && $(e.element).attr("vscode")) {
|
|
return $(polygonAndVSCodeIcons);
|
|
} else if ($(e.element).attr("data-2017")) {
|
|
return $(polygonIcon);
|
|
} else if ($(e.element).attr("vscode")) {
|
|
return $(VSCodeIcon);
|
|
}
|
|
|
|
return e.text;
|
|
}
|