/**
* Interaction between LWT and jQuery
*
* @license unlicense
* @author andreask7 <andreasks7@users.noreply.github.com>
* @since 1.6.16-fork
*/
/**************************************************************
Global variables used in LWT jQuery functions
***************************************************************/
TEXTPOS = -1;
OPENED = 0;
/** @var {int} WID - Word ID */
WID = 0;
/** Text ID (int) */
TID = 0;
/** First dictionary URL */
WBLINK1 = '';
/** Second dictionary URL */
WBLINK2 = '';
/** Google Translate */
WBLINK3 = '';
SOLUTION = '';
ADDFILTER = '';
/** Right-to-left indicator */
RTL = 0;
ANN_ARRAY = {};
DELIMITER = '';
JQ_TOOLTIP = 0;
/**************************************************************
LWT jQuery functions
***************************************************************/
/**
* Set translation and romanization in a form when possible.
*
* Marj the form as edited if something was changed.
*
* @param {string} tra Translation
* @param {string} rom Romanization
*/
function setTransRoman(tra, rom) {
let form_changed = false;
if ($('textarea[name="WoTranslation"]').length == 1) {
$('textarea[name="WoTranslation"]').val(tra);
form_changed |= true;
}
if ($('input[name="WoRomanization"]').length == 1) {
$('input[name="WoRomanization"]').val(rom);
form_changed |= true;
}
if (form_changed)
makeDirty();
}
/**
* Return whether characters are outside the multilingual plane.
*
* @param {string} s Input string
* @returns {boolean} true is some characters are outside the plane
*/
function containsCharacterOutsideBasicMultilingualPlane (s) {
return /[\uD800-\uDFFF]/.test(s);
}
/**
* Alert if characters are outside the multilingual plane.
*
* @param {string} s Input string
* @returns {boolean} true is some characters are outside the plane
*/
function alertFirstCharacterOutsideBasicMultilingualPlane (s, info) {
if (!containsCharacterOutsideBasicMultilingualPlane(s)) {
return 0;
}
const match = /[\uD800-\uDFFF]/.exec(s);
alert(
'ERROR\n\nText "' + info + '" contains invalid character(s) ' +
'(in the Unicode Supplementary Multilingual Planes, > U+FFFF) like emojis ' +
'or very rare characters.\n\nFirst invalid character: "' +
s.substring(match.index, match.index + 2) + '" at position ' +
(match.index + 1) + '.\n\n' +
'More info: https://en.wikipedia.org/wiki/Plane_(Unicode)\n\n' +
'Please remove this/these character(s) and try again.'
);
return 1;
}
/**
* Return the memory size of an UTF8 string.
*
* @param {string} s String to evaluate
* @returns {number} Size in bytes
*/
function getUTF8Length (s) {
return (new Blob([String(s)])).size;
}
/**
* Force the user scrolling to an anchor.
*
* @param {string} aid Anchor ID
*/
function scrollToAnchor (aid) {
document.location.href = '#' + aid;
}
/**
* Set an existing translation as annotation for a term.
*
* @param {int} textid Text ID
* @param {string} elem_name Name of the element of which to change annotation (e. g.: "rg1")
* @param {Object} form_data All the data from the form
* (e. g. {"rg0": "foo", "rg1": "bar"})
*/
function do_ajax_save_impr_text(textid, elem_name, form_data) {
const idwait = '#wait' + elem_name.substring(2);
$(idwait).html('<img src="icn/waiting2.gif" />');
// elem: "rg2", form_data: {"rg2": "translation"}
$.post(
'api.php/v1/texts/' + textid + '/annotation',
{
elem: elem_name,
data: form_data
},
function (data) {
$(idwait).html('<img src="icn/empty.gif" />');
if ("error" in data)
alert(
'Saving your changes failed, please reload the page and try again! ' +
'Error message: "' + data.error + '".'
);
},
"json"
);
}
/**
* Change the annotation for a term by setting its text.
*/
function changeImprAnnText () {
$(this).prev('input:radio').attr('checked', 'checked');
const textid = $('#editimprtextdata').attr('data_id');
const elem_name = $(this).attr('name');
const form_data = JSON.stringify($('form').serializeObject());
do_ajax_save_impr_text(textid, elem_name, form_data);
}
/**
* Change the annotation for a term by setting its text.
*/
function changeImprAnnRadio () {
const textid = $('#editimprtextdata').attr('data_id');
const elem_name = $(this).attr('name');
const form_data = JSON.stringify($('form').serializeObject());
do_ajax_save_impr_text(textid, elem_name, form_data);
}
/**
* Update a word translation.
*
* @param {int} wordid Word ID
* @param {string} txid Text HTML ID or unique HTML selector
*/
function updateTermTranslation(wordid, txid) {
const translation = $(txid).val().trim();
const pagepos = $(document).scrollTop();
if (translation == '' || translation == '*') {
alert('Text Field is empty or = \'*\'!');
return;
}
let request = {
translation: translation,
};
const failure = "Updating translation of term failed!" +
"Please reload page and try again.";
$.post(
'api.php/v1/terms/' + wordid + '/translations',
request,
function (d) {
if (d == '') {
alert(failure);
return;
}
if ("error" in d) {
alert(failure + "\n" + d.error);
return;
}
do_ajax_edit_impr_text(pagepos, d.update, wordid);
},
"json"
);
}
/**
* Add (new word) a word translation.
*
* @param {string} txid Text HTML ID or unique HTML selector
* @param {string} word Word text
* @param {int} lang Language ID
*/
function addTermTranslation(txid, word, lang) {
const translation = $(txid).val().trim();
const pagepos = $(document).scrollTop();
if (translation == '' || translation == '*') {
alert('Text Field is empty or = \'*\'!');
return;
}
const failure = "Adding translation to term failed!" +
"Please reload page and try again."
$.post(
'api.php/v1/terms/new',
{
translation: translation,
term_text: word,
lg_id: lang
},
function (d) {
if (d == '') {
alert(failure);
return;
}
if ("error" in d) {
alert(failure + "\n" + d.error);
return;
}
do_ajax_edit_impr_text(pagepos, d.add, d.term_id);
},
"json"
);
}
/**
* Set a new status for a word in the test table.
*
* @param {string} wordid Word ID
* @param {bool} up true if status sould be increased, false otherwise
*/
function changeTableTestStatus (wordid, up) {
const status_change = up ? 'up' : 'down';
const wid = parseInt(wordid, 10);
$.post(
'api.php/v1/terms/' + wid + '/status/' + status_change,
{},
function (data) {
if (data == "" || "error" in data) {
return;
}
$('#STAT' + wordid).html(data.increment);
},
"json"
);
}
/**
* Check if there is no problem with the text.
*
* @returns {boolean} true if all checks were successfull
*/
function check () {
let count = 0;
$('.notempty').each(function (_n) {
if ($(this).val().trim() == '') count++;
});
if (count > 0) {
alert('ERROR\n\n' + count + ' field(s) - marked with * - must not be empty!');
return false;
}
count = 0;
$('input.checkurl').each(function (_n) {
if ($(this).val().trim().length > 0) {
if (($(this).val().trim().indexOf('http://') != 0) &&
($(this).val().trim().indexOf('https://') != 0) &&
($(this).val().trim().indexOf('#') != 0)) {
alert(
'ERROR\n\nField "' + $(this).attr('data_info') +
'" must start with "http://" or "https://" if not empty.'
);
count++;
}
}
});
// Note: as of LWT 2.9.0, no field with "checkregexp" property is found in the code base
$('input.checkregexp').each(function (_n) {
const regexp = $(this).val().trim();
if (regexp.length > 0) {
$.ajax({
type: 'POST',
url: 'inc/ajax.php',
data: {
action: "",
action_type: "check_regexp",
regex: regexp
},
async: false
}
).always(function (data) {
if (data != '') {
alert(data);
count++;
}
});
}
});
// To enable limits of custom feed texts/articl.
// change the following «input[class*="max_int_"]» into «input[class*="maxint_"]»
$('input[class*="max_int_"]').each(function (_n) {
const maxvalue = parseInt($(this).attr('class')
.replace(/.*maxint_([0-9]+).*/, '$1'));
if ($(this).val().trim().length > 0) {
if ($(this).val() > maxvalue) {
alert(
'ERROR\n\n Max Value of Field "' + $(this).attr('data_info') +
'" is ' + maxvalue
);
count++;
}
}
});
// Check that the Google Translate field is of good type
$('input.checkdicturl').each(function (_n) {
const translate_input = $(this).val().trim();
if (translate_input.length > 0) {
let refinned = translate_input;
if (translate_input.startsWith('*')) {
refinned = translate_input.substring(1);
}
if (!/^https?:\/\//.test(refinned)) {
refinned = 'http://' + refinned;
}
try {
new URL(refinned);
} catch (err) {
if (err instanceof TypeError) {
alert(
'ERROR\n\nField "' + $(this).attr('data_info') +
'" should be an URL if not empty.'
);
count++;
}
}
}
});
$('input.posintnumber').each(function (_n) {
if ($(this).val().trim().length > 0) {
if (!(isInt($(this).val().trim()) && (parseInt($(this).val().trim(), 10) > 0))) {
alert(
'ERROR\n\nField "' + $(this).attr('data_info') +
'" must be an integer number > 0.'
);
count++;
}
}
});
$('input.zeroposintnumber').each(function (_n) {
if ($(this).val().trim().length > 0) {
if (!(isInt($(this).val().trim()) && (parseInt($(this).val().trim(), 10) >= 0))) {
alert(
'ERROR\n\nField "' + $(this).attr('data_info') +
'" must be an integer number >= 0.'
);
count++;
}
}
});
$('input.checkoutsidebmp').each(function (_n) {
if ($(this).val().trim().length > 0) {
if (containsCharacterOutsideBasicMultilingualPlane($(this).val())) {
count += alertFirstCharacterOutsideBasicMultilingualPlane(
$(this).val(), $(this).attr('data_info')
);
}
}
});
$('textarea.checklength').each(function (_n) {
if ($(this).val().trim().length > (0 + $(this).attr('data_maxlength'))) {
alert(
'ERROR\n\nText is too long in field "' + $(this).attr('data_info') +
'", please make it shorter! (Maximum length: ' +
$(this).attr('data_maxlength') + ' char.)'
);
count++;
}
});
$('textarea.checkoutsidebmp').each(function (_n) {
if (containsCharacterOutsideBasicMultilingualPlane($(this).val())) {
count += alertFirstCharacterOutsideBasicMultilingualPlane(
$(this).val(), $(this).attr('data_info')
);
}
});
$('textarea.checkbytes').each(function (_n) {
if (getUTF8Length($(this).val().trim()) > (0 + $(this).attr('data_maxlength'))) {
alert(
'ERROR\n\nText is too long in field "' + $(this).attr('data_info') +
'", please make it shorter! (Maximum length: ' +
$(this).attr('data_maxlength') + ' bytes.)'
);
count++;
}
});
$('input.noblanksnocomma').each(function (_n) {
if ($(this).val().indexOf(' ') > 0 || $(this).val().indexOf(',') > 0) {
alert(
'ERROR\n\nNo spaces or commas allowed in field "' +
$(this).attr('data_info') + '", please remove!'
);
count++;
}
});
return (count == 0);
}
function isInt (value) {
for (let i = 0; i < value.length; i++) {
if ((value.charAt(i) < '0') || (value.charAt(i) > '9')) {
return false;
}
}
return true;
}
function markClick () {
if ($('input.markcheck:checked').length > 0) {
$('#markaction').removeAttr('disabled');
} else {
$('#markaction').attr('disabled', 'disabled');
}
}
function confirmDelete () {
return confirm('CONFIRM\n\nAre you sure you want to delete?');
}
/**
* Enable/disable words hint.
* Function called when clicking on "Show All".
*/
function showAllwordsClick () {
const showAll = $('#showallwords').prop('checked') ? '1' : '0';
const showLeaning = $('#showlearningtranslations').prop('checked') ? '1' : '0';
const text = $('#thetextid').text();
// Timeout necessary because the button is clicked on the left (would hide frames)
setTimeout(function () {
showRightFrames(
'set_text_mode.php?mode=' + showAll + '&showLearning=' + showLeaning +
'&text=' + text
);}, 500);
setTimeout(function () {window.location.reload();}, 4000);
}
function textareaKeydown (event) {
if (event.keyCode && event.keyCode == '13') {
if (check())
$('input:submit').last().trigger('click');
return false;
} else {
return true;
}
}
function noShowAfter3Secs () {
$('#hide3').slideUp();
}
/**
* Set the focus on an element with the "focus" class.
*/
function setTheFocus () {
$('.setfocus')
.trigger('focus')
.trigger('select');
}
/**
* Prepare a dialog when the user clicks a word during a test.
*
* @returns {false}
*/
function word_click_event_do_test_test () {
run_overlib_test(
WBLINK1, WBLINK2, WBLINK3,
$(this).attr('data_wid'),
$(this).attr('data_text'),
$(this).attr('data_trans'),
$(this).attr('data_rom'),
$(this).attr('data_status'),
$(this).attr('data_sent'),
$(this).attr('data_todo')
);
$('.todo').text(SOLUTION);
return false;
}
/**
* Handle keyboard interaction when testing a word.
*
* @param {object} e A keystroke object
* @returns {bool} true if nothing was done, false otherwise
*/
function keydown_event_do_test_test (e) {
if (e.key == 'Space' && OPENED == 0) {
// space : show sol.
$('.word').trigger('click');
cleanupRightFrames();
showRightFrames('show_word.php?wid=' + $('.word').attr('data_wid') + '&ann=');
OPENED = 1;
return false;
}
if (e.which == 38) {
// up : status+1
showRightFrames('set_test_status.php?wid=' + WID + '&stchange=1');
return false;
}
if (e.which == 27) {
// esc : dont change status
showRightFrames(
'set_test_status.php?wid=' + WID + '&status=' + $('.word').attr('data_status')
);
return false;
}
if (e.which == 73) {
// I : status=98
showRightFrames('set_test_status.php?wid=' + WID + '&status=98');
return false;
}
if (e.which == 87) {
// W : status=99
showRightFrames('set_test_status.php?wid=' + WID + '&status=99');
return false;
}
if (e.which == 69) {
// E : EDIT
showRightFrames('edit_tword.php?wid=' + WID);
return false;
}
if (OPENED == 0) return true;
if (e.which == 40) {
// down : status-1
showRightFrames('set_test_status.php?wid=' + WID + '&stchange=-1');
return false;
}
for (let i = 1; i <= 5; i++) {
if (e.which == (48 + i) || e.which == (96 + i)) {
// 1,.. : status=i
showRightFrames('set_test_status.php?wid=' + WID + '&status=' + i);
return false;
}
}
return true;
}
/**
* Add annotations to a word.
*
* @param {*} _ Unused, usually word number
*/
function word_each_do_text_text(_) {
const wid = $(this).attr('data_wid');
if (wid != '') {
const order = $(this).attr('data_order');
if (order in ANN_ARRAY) {
if (wid == ANN_ARRAY[order][1]) {
const ann = ANN_ARRAY[order][2];
const re = new RegExp(
'([' + DELIMITER + '][ ]{0,1}|^)(' +
ann.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&') + ')($|[ ]{0,1}[' +
DELIMITER + '])',
''
);
if (!re.test($(this).attr('data_trans').replace(/ \[.*$/, ''))) {
const trans = ann + ' / ' + $(this).attr('data_trans');
$(this).attr('data_trans', trans.replace(' / *', ''));
}
$(this).attr('data_ann', ann);
}
}
}
if (!JQ_TOOLTIP) {
this.title = make_tooltip(
$(this).text(),
$(this).attr('data_trans'),
$(this).attr('data_rom'),
$(this).attr('data_status')
);
}
}
function mword_each_do_text_text(_) {
if ($(this).attr('data_status') != '') {
const wid = $(this).attr('data_wid');
if (wid != '') {
const order = parseInt($(this).attr('data_order'));
for (let j = 2; j <= 16; j = j + 2) {
const index = (order + j).toString();
if (index in ANN_ARRAY) {
if (wid == ANN_ARRAY[index][1]) {
const ann = ANN_ARRAY[index][2];
const re = new RegExp(
'([' + DELIMITER + '][ ]{0,1}|^)(' +
ann.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&') + ')($|[ ]{0,1}[' +
DELIMITER + '])',
''
);
if (!re.test($(this).attr('data_trans').replace(/ \[.*$/, ''))) {
const trans = ann + ' / ' + $(this).attr('data_trans');
$(this).attr('data_trans', trans.replace(' / *', ''));
}
$(this).attr('data_ann', ann);
break;
}
}
}
}
if (!JQ_TOOLTIP) {
this.title = make_tooltip(
$(this).attr('data_text'),
$(this).attr('data_trans'), $(this).attr('data_rom'),
$(this).attr('data_status')
);
}
}
}
function word_dblclick_event_do_text_text () {
const t = parseInt($('#totalcharcount').text(), 10);
if (t == 0) return;
let p = 100 * ($(this).attr('data_pos') - 5) / t;
if (p < 0) p = 0;
if (typeof (window.parent.frames.h.new_pos) === 'function') {
window.parent.frames.h.new_pos(p);
}
}
/**
* Do a word edition window. Usually called when the user clicks on a word.
*
* @returns {bool} false
*/
function word_click_event_do_text_text () {
const status = $(this).attr('data_status');
let ann = '';
if ($(this).attr('data_ann') !== undefined) {
ann = $(this).attr('data_ann');
}
let hints;
if (JQ_TOOLTIP) {
hints = make_tooltip(
$(this).text(), $(this).attr('data_trans'), $(this).attr('data_rom'), status
);
} else {
hints = $(this).attr('title');
}
// Get multi-words containing word
const multi_words = Array(7);
for (let i = 0; i < 7; i++) {
// Start from 2 as multi-words have at least two elements
multi_words[i] = $(this).attr('data_mw' + (i + 2));
}
if (status < 1) {
run_overlib_status_unknown(
WBLINK1, WBLINK2, WBLINK3, hints,
TID, $(this).attr('data_order'), $(this).text(), multi_words, RTL
);
showRightFrames(
'edit_word.php?tid=' + TID + '&ord=' + $(this).attr('data_order') + '&wid='
);
} else if (status == 99) {
run_overlib_status_99(
WBLINK1, WBLINK2, WBLINK3, hints,
TID, $(this).attr('data_order'),
$(this).text(), $(this).attr('data_wid'), multi_words, RTL, ann
);
} else if (status == 98) {
run_overlib_status_98(
WBLINK1, WBLINK2, WBLINK3, hints,
TID, $(this).attr('data_order'),
$(this).text(), $(this).attr('data_wid'), multi_words, RTL, ann
);
} else {
run_overlib_status_1_to_5(
WBLINK1, WBLINK2, WBLINK3, hints,
TID, $(this).attr('data_order'),
$(this).text(), $(this).attr('data_wid'), status, multi_words, RTL, ann
);
}
return false;
}
function mword_click_event_do_text_text () {
const status = $(this).attr('data_status');
if (status != '') {
let ann = '';
if ((typeof $(this).attr('data_ann')) !== 'undefined') {
ann = $(this).attr('data_ann');
}
run_overlib_multiword(
WBLINK1, WBLINK2, WBLINK3,
JQ_TOOLTIP ? make_tooltip(
$(this).text(),
$(this).attr('data_trans'),
$(this).attr('data_rom'),
status
) : $(this).attr('title'),
TID, $(this).attr('data_order'), $(this).attr('data_text'),
$(this).attr('data_wid'), status, $(this).attr('data_code'), ann
);
}
return false;
}
function mword_drag_n_drop_select (event) {
if (JQ_TOOLTIP)$('.ui-tooltip').remove();
const context = $(this).parent();
context.one('mouseup mouseout', $(this), function () {
clearTimeout(to);
$('.nword').removeClass('nword');
$('.tword').removeClass('tword');
$('.lword').removeClass('lword');
$('.wsty', context).css('background-color', '').css('border-bottom-color', '');
$('#pe').remove();
});
to = setTimeout(function () {
let pos;
context.off('mouseout');
$('.wsty', context).css('background-color', 'inherit')
.css('border-bottom-color', 'rgba(0,0,0,0)').not('.hide,.word')
.each(function () {
f = parseInt($(this).attr('data_code')) * 2 +
parseInt($(this).attr('data_order')) - 1;
h = '';
$(this).nextUntil($('[id^="ID-' + f + '-"]', context), '[id$="-1"]')
.each(function () {
l = $(this).attr('data_order');
if (typeof l !== 'undefined') {
h += '<span class="tword" data_order="' + l + '">' + $(this).text() +
'</span>';
} else {
h += '<span class="nword" data_order="' +
$(this).attr('id').split('-')[1] + '">' + $(this).text() + '</span>';
}
});
$(this).html(h);
});
$('#pe').remove();
$('body')
.append(
'<style id="pe">#' + context.attr('id') + ' .wsty:after,#' +
context.attr('id') + ' .wsty:before{opacity:0}</style>'
);
$('[id$="-1"]', context).not('.hide,.wsty').addClass('nword').each(function () {
$(this).attr('data_order', $(this).attr('id').split('-')[1]);
});
$('.word', context).not('.hide').each(function () {
$(this).html(
'<span class="tword" data_order="' + $(this).attr('data_order') + '">' +
$(this).text() + '</span>'
);
});
if (event.data.annotation == 1) {
$('.wsty', context)
.not('.hide')
.each(function () {
$(this).children('.tword').last()
.attr('data_ann', $(this).attr('data_ann'))
.attr('data_trans', $(this).attr('data_trans'))
.addClass(
'content' + $(this)
.removeClass('status1 status2 status3 status4 status5 status98 status99')
.attr('data_status')
);
});
} else if (event.data.annotation == 3) {
$('.wsty', context)
.not('.hide')
.each(function () {
$(this).children('.tword').first()
.attr('data_ann', $(this).attr('data_ann'))
.attr('data_trans', $(this).attr('data_trans'))
.addClass(
'content' + $(this)
.removeClass('status1 status2 status3 status4 status5 status98 status99')
.attr('data_status')
);
});
}
$(context).one('mouseover', '.tword', function () {
$('html').one('mouseup', function () {
$('.wsty', context).each(function () {
$(this).addClass('status' + $(this).attr('data_status'));
});
if (!$(this).hasClass('tword')) {
$('span', context).removeClass('nword tword lword');
$('.wsty', context).css('background-color', '')
.css('border-bottom-color', '');
$('#pe').remove();
}
});
pos = parseInt($(this).attr('data_order'));
$('.lword', context).removeClass('lword');
$(this).addClass('lword');
$(context).on('mouseleave', function () {
$('.lword', context).removeClass('lword');
});
$(context).one('mouseup', '.nword,.tword', function (ev) {
if (ev.handled !== true) {
const len = $('.lword.tword', context).length;
if (len > 0) {
g = $('.lword', context).first().attr('data_order');
if (len > 1) {
const text = $('.lword', context)
.map(function () { return $(this).text(); }).get().join('');
if (text.length > 250) {
alert('selected text is too long!!!');
} else {
showRightFrames(
'edit_mword.php?tid=' + TID + '&len=' + len + '&ord=' + g +
'&txt=' + text
);
}
} else {
showRightFrames(
'edit_word.php?tid=' + TID + '&ord=' + g + '&txt=' +
$('#ID-' + g + '-1').text()
);
}
}
$('span', context).removeClass('tword nword');
ev.handled = true;
}
});
});
$(context).hoverIntent({
over: function () {
$('.lword', context).removeClass('lword');
const lpos = parseInt($(this).attr('data_order'));
$(this).addClass('lword');
if (lpos > pos) {
for (var i = pos; i < lpos; i++) {
$(
'.tword[data_order="' + i + '"],.nword[data_order="' + i + '"]',
context
).addClass('lword');
}
} else {
for (var i = pos; i > lpos; i--) {
$(
'.tword[data_order="' + i + '"],.nword[data_order="' + i + '"]',
context
).addClass('lword');
}
}
},
out: function () {},
sensitivity: 18,
selector: '.tword'
});
}, 300);
}
function word_hover_over () {
if (!$('.tword')[0]) {
const v = $(this).attr('class').replace(/.*(TERM[^ ]*)( .*)*/, '$1');
$('.' + v).addClass('hword');
if (JQ_TOOLTIP) {
$(this).trigger('mouseover');
}
}
}
function word_hover_out () {
$('.hword').removeClass('hword');
if (JQ_TOOLTIP)$('.ui-helper-hidden-accessible>div[style]').remove();
}
jQuery.fn.extend({
tooltip_wsty_content: function () {
var re = new RegExp('([' + DELIMITER + '])(?! )', 'g');
let title = '';
if ($(this).hasClass('mwsty')) {
title = "<p><b style='font-size:120%'>" + $(this).attr('data_text') +
'</b></p>';
} else {
title = "<p><b style='font-size:120%'>" + $(this).text() + '</b></p>';
}
const roman = $(this).attr('data_rom');
let trans = $(this).attr('data_trans').replace(re, '$1 ');
let statname = '';
const status = parseInt($(this).attr('data_status'));
if (status == 0)statname = 'Unknown [?]';
else if (status < 5)statname = 'Learning [' + status + ']';
if (status == 5)statname = 'Learned [5]';
if (status == 98)statname = 'Ignored [Ign]';
if (status == 99)statname = 'Well Known [WKn]';
if (roman != '') {
title += '<p><b>Roman.</b>: ' + roman + '</p>';
}
if (trans != '' && trans != '*') {
if ($(this).attr('data_ann')) {
const ann = $(this).attr('data_ann');
if (ann != '' && ann != '*') {
var re = new RegExp(
'(.*[' + DELIMITER + '][ ]{0,1}|^)(' +
ann.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&') + ')($|[ ]{0,1}[' +
DELIMITER + '].*$| \\[.*$)',
''
);
trans = trans.replace(re, '$1<span style="color:red">$2</span>$3');
}
}
title += '<p><b>Transl.</b>: ' + trans + '</p>';
}
title += '<p><b>Status</b>: <span class="status' + status + '">' + statname +
'</span></p>';
return title;
}
});
jQuery.fn.extend({
tooltip_wsty_init: function () {
$(this).tooltip({
position: { my: 'left top+10', at: 'left bottom', collision: 'flipfit' },
items: '.hword',
show: { easing: 'easeOutCirc' },
content: function () { return $(this).tooltip_wsty_content(); }
});
}
});
function get_position_from_id (id_string) {
if ((typeof id_string) === 'undefined') return -1;
const arr = id_string.split('-');
return parseInt(arr[1]) * 10 + 10 - parseInt(arr[2]);
}
function keydown_event_do_text_text (e) {
if (e.which == 27) { // esc = reset all
TEXTPOS = -1;
$('span.uwordmarked').removeClass('uwordmarked');
$('span.kwordmarked').removeClass('kwordmarked');
cClick();
return false;
}
if (e.which == 13) { // return = edit next unknown word
$('span.uwordmarked').removeClass('uwordmarked');
const unknownwordlist = $('span.status0.word:not(.hide):first');
if (unknownwordlist.size() == 0) return false;
$(window).scrollTo(unknownwordlist, { axis: 'y', offset: -150 });
unknownwordlist.addClass('uwordmarked').trigger('click');
cClick();
return false;
}
const knownwordlist = $(
'span.word:not(.hide):not(.status0)' + ADDFILTER +
',span.mword:not(.hide)' + ADDFILTER
);
const l_knownwordlist = knownwordlist.size();
// console.log(knownwordlist);
if (l_knownwordlist == 0) return true;
// the following only for a non-zero known words list
if (e.which == 36) { // home : known word navigation -> first
$('span.kwordmarked').removeClass('kwordmarked');
TEXTPOS = 0;
curr = knownwordlist.eq(TEXTPOS);
curr.addClass('kwordmarked');
$(window).scrollTo(curr, { axis: 'y', offset: -150 });
var ann = '';
if ((typeof curr.attr('data_ann')) !== 'undefined') {
ann = curr.attr('data_ann');
}
showRightFrames(
'show_word.php?wid=' + curr.attr('data_wid') + '&ann=' +
encodeURIComponent(ann)
);
return false;
}
if (e.which == 35) { // end : known word navigation -> last
$('span.kwordmarked').removeClass('kwordmarked');
TEXTPOS = l_knownwordlist - 1;
curr = knownwordlist.eq(TEXTPOS);
curr.addClass('kwordmarked');
$(window).scrollTo(curr, { axis: 'y', offset: -150 });
var ann = '';
if ((typeof curr.attr('data_ann')) !== 'undefined') {
ann = curr.attr('data_ann');
}
showRightFrames(
'show_word.php?wid=' + curr.attr('data_wid') + '&ann=' +
encodeURIComponent(ann)
);
return false;
}
if (e.which == 37) { // left : known word navigation
var marked = $('span.kwordmarked');
var currid = (marked.length == 0)
? (100000000)
: get_position_from_id(marked.attr('id'));
$('span.kwordmarked').removeClass('kwordmarked');
// console.log(currid);
TEXTPOS = l_knownwordlist - 1;
for (var i = l_knownwordlist - 1; i >= 0; i--) {
var iid = get_position_from_id(knownwordlist.eq(i).attr('id'));
// console.log(iid);
if (iid < currid) {
TEXTPOS = i;
break;
}
}
// TEXTPOS--;
// if (TEXTPOS < 0) TEXTPOS = l_knownwordlist - 1;
curr = knownwordlist.eq(TEXTPOS);
curr.addClass('kwordmarked');
$(window).scrollTo(curr, { axis: 'y', offset: -150 });
var ann = '';
if ((typeof curr.attr('data_ann')) !== 'undefined') {
ann = curr.attr('data_ann');
}
showRightFrames(
'show_word.php?wid=' + curr.attr('data_wid') + '&ann=' +
encodeURIComponent(ann)
);
return false;
}
if (e.which == 39 || e.which == 32) { // space /right : known word navigation
var marked = $('span.kwordmarked');
var currid = (marked.length == 0)
? (-1)
: get_position_from_id(marked.attr('id'));
$('span.kwordmarked').removeClass('kwordmarked');
// console.log(currid);
TEXTPOS = 0;
for (var i = 0; i < l_knownwordlist; i++) {
var iid = get_position_from_id(knownwordlist.eq(i).attr('id'));
// console.log(iid);
if (iid > currid) {
TEXTPOS = i;
break;
}
}
// TEXTPOS++;
// if (TEXTPOS >= l_knownwordlist) TEXTPOS = 0;
curr = knownwordlist.eq(TEXTPOS);
curr.addClass('kwordmarked');
$(window).scrollTo(curr, { axis: 'y', offset: -150 });
var ann = '';
if ((typeof curr.attr('data_ann')) !== 'undefined') {
ann = curr.attr('data_ann');
}
showRightFrames(
'show_word.php?wid=' + curr.attr('data_wid') + '&ann=' +
encodeURIComponent(ann)
);
return false;
}
if ((!$('.kwordmarked, .uwordmarked')[0]) && $('.hword:hover')[0]) {
curr = $('.hword:hover');
} else {
if (TEXTPOS < 0 || TEXTPOS >= l_knownwordlist) return true;
curr = knownwordlist.eq(TEXTPOS);
}
const wid = curr.attr('data_wid');
const ord = curr.attr('data_order');
const stat = curr.attr('data_status');
const txt = (curr.hasClass('mwsty')) ? curr.attr('data_text') : curr.text();
let dict = '';
for (var i = 1; i <= 5; i++) {
if (e.which == (48 + i) || e.which == (96 + i)) { // 1,.. : status=i
if (stat == '0') {
if (i == 1) {
/** @var {string} sl Source language */
const sl = getLangFromDict(WBLINK3);
const tl = WBLINK3.replace(/.*[?&]tl=([a-zA-Z\-]*)(&.*)*$/, '$1');
if (sl != WBLINK3 && tl != WBLINK3)
i = i + '&sl=' + sl + '&tl=' + tl;
}
showRightFrames(
'set_word_on_hover.php?text=' + txt + '&tid=' + TID + '&status=' + i
);
} else {
showRightFrames(
'set_word_status.php?wid=' + wid + '&tid=' + TID + '&ord=' + ord +
'&status=' + i
);
return false;
}
}
}
if (e.which == 73) { // I : status=98
if (stat == '0') {
showRightFrames(
'set_word_on_hover.php?text=' + txt + '&tid=' + TID +
'&status=98'
);
} else {
showRightFrames(
'set_word_status.php?wid=' + wid + '&tid=' + TID +
'&ord=' + ord + '&status=98'
);
return false;
}
}
if (e.which == 87) { // W : status=99
if (stat == '0') {
showRightFrames(
'set_word_on_hover.php?text=' + txt + '&tid=' + TID + '&status=99'
);
} else {
showRightFrames(
'set_word_status.php?wid=' + wid + '&tid=' + TID + '&ord=' + ord +
'&status=99'
);
}
return false;
}
if (e.which == 80) { // P : pronounce term
const lg = getLangFromDict(WBLINK3);
readTextAloud(txt, lg);
return false;
}
if (e.which == 84) { // T : translate sentence
let popup = false;
let dict_link = WBLINK3;
if (WBLINK3.startsWith('*')) {
popup = true;
dict_link = substring(dict_link, 1);
}
if (dict_link.startsWith('ggl.php')) {
dict_link = "http://" + dict_link;
}
let open_url = true;
let final_url;
try {
final_url = new URL(dict_link);
popup |= final_url.searchParams.has("lwt_popup");
} catch (err) {
if (err instanceof TypeError) {
open_url = false;
}
}
if (popup) {
owin('trans.php?x=1&i=' + ord + '&t=' + TID);
} else if (open_url) {
showRightFrames(undefined, 'trans.php?x=1&i=' + ord + '&t=' + TID);
}
return false;
}
if (e.which == 65) { // A : set audio pos.
let p = curr.attr('data_pos');
const t = parseInt($('#totalcharcount').text(), 10);
if (t == 0) return true;
p = 100 * (p - 5) / t;
if (p < 0) p = 0;
if (typeof (window.parent.frames.h.new_pos) === 'function') {
window.parent.frames.h.new_pos(p);
} else {
return true;
}
return false;
}
if (e.which == 71) { // G : edit term and open GTr
dict = '&nodict';
setTimeout(function () {
let target_url = WBLINK3;
let popup = false;
popup = target_url.startsWith('*');
try {
const final_url = new URL(target_url);
popup |= final_url.searchParams.has('lwt_popup');
} catch (err) {
if (!(err instanceof TypeError)) {
throw err;
}
}
if (popup) {
owin(createTheDictUrl(target_url, txt));
} else {
showRightFrames(undefined, createTheDictUrl(target_url, txt));
}
}, 10);
}
if (e.which == 69 || e.which == 71) { // E / G: edit term
let url = '';
if (curr.hasClass('mword')) {
url = 'edit_mword.php?wid=' + wid + '&len=' + curr.attr('data_code') +
'&tid=' + TID + '&ord=' + ord + dict;
} else if (stat == '0') {
url = 'edit_word.php?wid=&tid=' + TID + '&ord=' + ord + dict;
} else {
url = 'edit_word.php?wid=' + wid + '&tid=' + TID + '&ord=' + ord + dict;
}
showRightFrames(url);
return false;
}
return true;
}
/**
* Save a setting to the database.
*
* @param {string} k Setting name as a key
* @param {string} v Setting value
*/
function do_ajax_save_setting (k, v) {
$.post(
'api.php/v1/settings',
{
key: k,
value: v
}
);
}
/**
* Assign the display value of a select element to the value element of another input.
*
* @param {elem} select_elem
* @param {elem} input_elem
*/
function quick_select_to_input(select_elem, input_elem)
{
let val = select_elem.options[select_elem.selectedIndex].value;
if (val != '')
input_elem.value = val;
select_elem.value = '';
}
/**
* Return an HTML group of options to add to a select field.
*
* @param {string[]} paths All paths (files and folders)
* @param {string[]} folders Folders paths, should be a subset of paths
* @param {string} base_path Base path for LWT to append
*
* @returns {HTMLOptionElement[]} List of options to append to the select.
*/
function select_media_path(paths, folders, base_path)
{
let options = [], temp_option = document.createElement('option');
temp_option.value = "";
temp_option.text = "[Choose...]";
options.push(temp_option);
for (let i = 0; i < paths.length; i++) {
temp_option = document.createElement('option')
if (folders.includes(paths[i])) {
temp_option.setAttribute("disabled", "disabled");
temp_option.text = '-- Directory: ' + paths[i] + '--';
} else {
temp_option.value = base_path + "/" + paths[i];
temp_option.text = paths[i];
}
options.push(temp_option);
}
return options;
}
/**
* Process the received data from media selection query
*
* @param {Object} data Received data as a JSON object
*/
function media_select_receive_data(data) {
$('#mediaSelectLoadingImg').css("display", "none");
if (data["error"] !== undefined) {
let msg;
if (data["error"] == "not_a_directory") {
msg = '[Error: "../' + data["base_path"] + '/media" exists, but it is not a directory.]';
} else if (data["error"] == "does_not_exist") {
msg = '[Directory "../' + data["base_path"] + '/media" does not yet exist.]';
} else {
msg = "[Unknown error!]";
}
$('#mediaSelectErrorMessage').text(msg);
$('#mediaSelectErrorMessage').css("display", "inherit");
} else {
const options = select_media_path(data["paths"], data["folders"], data["base_path"]);
$('#mediaselect select').empty();
for (let i = 0; i < options.length; i++) {
$('#mediaselect select').append(options[i]);
}
$('#mediaselect select').css("display", "inherit");
}
}
/**
* Perform an AJAX query to retrieve and display the media files path.
*/
function do_ajax_update_media_select () {
$('#mediaSelectErrorMessage').css("display", "none");
$('#mediaselect select').css("display", "none");
$('#mediaSelectLoadingImg').css("display", "inherit");
$.getJSON(
'api.php/v1/media-files',
{},
media_select_receive_data
);
}
/**
* Prepare am HTML element that formats the sentences
*
* @param {JSON} sentences A list of sentences to display.
* @param {string} click_target The selector for the element that should change value on click
* @returns {HTMLElement} A formatted group of sentences
*/
function display_example_sentences(sentences, click_target)
{
let img, clickable, parentDiv;
const outElement = document.createElement("div");
for (let i = 0; i < sentences.length; i++) {
// Add the checbox
img = document.createElement("img");
img.src = "icn/tick-button.png";
img.title = "Choose";
// Clickable element
clickable = document.createElement('span');
clickable.classList.add("click");
// Doesn't feel the right way to do it
clickable.setAttribute(
"onclick",
"{" +
click_target + ".value = '" + sentences[i][1].replaceAll("'", "\\'") +"';makeDirty();}"
);
clickable.appendChild(img);
// Create parent
parentDiv = document.createElement("div");
parentDiv.appendChild(clickable);
parentDiv.innerHTML += " " + sentences[i][0];
// Add to the output
outElement.appendChild(parentDiv);
}
return outElement;
}
/**
* Prepare am HTML element that formats the sentences
*
* @param {JSON} sentences A list of sentences to display.
* @param {string} click_target The selector for the element that should change value on click
* @returns {HTMLElement} A formatted group of sentences
*/
function change_example_sentences_zone(sentences, ctl) {
$('#exsent-waiting').css("display", "none");
$('#exsent-sentences').css("display", "inherit");
const new_element = display_example_sentences(sentences, ctl);
$('#exsent-sentences').append(new_element);
}
/**
* Get and display the sentences containing specific word.
*
* @param {int} lang Language ID
* @param {string} word Term text (the looked for term)
* @param {string} ctl Selector for the element to edit on click
* @param {int} woid Term id (word or multi-word)
* @returns {undefined}
*/
function do_ajax_show_sentences (lang, word, ctl, woid) {
$('#exsent-interactable').css("display", "none");
$('#exsent-waiting').css("display", "inherit");
if (isInt(woid) && woid != -1) {
$.getJSON(
'api.php/v1/sentences-with-term/' + woid,
{
lg_id: lang,
word_lc: word
},
(data) => change_example_sentences_zone(data, ctl)
);
} else {
let query = {
lg_id: lang,
word_lc: word
};
if (parseInt(woid, 10) == -1) {
query["advanced_search"] = true;
}
$.getJSON(
'api.php/v1/sentences-with-term',
query,
(data) => change_example_sentences_zone(data, ctl)
);
}
}
/**
* Send an AJAX request to get similar terms to a term.
*
* @param {number} lg_id Language ID
* @param {string} word_text Text to match
* @returns {JSON} Request used
*/
function do_ajax_req_sim_terms(lg_id, word_text) {
return $.getJSON(
'api.php/v1/similar-terms',
{
"lg_id": lg_id,
"term": word_text
}
);
}
/**
* Display the terms similar to a specific term with AJAX.
*/
function do_ajax_show_similar_terms () {
$('#simwords').html('<img src="icn/waiting2.gif" />');
do_ajax_req_sim_terms(
parseInt($('#langfield').val(), 10), $('#wordfield').val()
)
.done(
function (data) {
$('#simwords').html(data.similar_terms);
}
).fail(
function (data) {
console.log(data);
}
);
}
/**
* Update WORDCOUNTS in with an AJAX request.
*
* @returns {undefined}
*/
function do_ajax_word_counts () {
const t = $('.markcheck').map(function () {
return $(this).val();
})
.get().join(',');
$.getJSON(
'api.php/v1/texts/statistics',
{
texts_id: t
},
function (data) {
WORDCOUNTS = data;
word_count_click();
$('.barchart').removeClass('hide');
}
);
}
/**
* Set a unique item in barchart to reflect how many words are known.
*
* @returns {undefined}
*/
function set_barchart_item() {
const id = $(this).find('span').first().attr('id').split('_')[2];
/** @var {int} v Number of terms in the text */
let v;
if (SUW & 16) {
v = parseInt(WORDCOUNTS.expru[id] || 0, 10) +
parseInt(WORDCOUNTS.totalu[id], 10);
} else {
v = parseInt(WORDCOUNTS.expr[id] || 0, 10) +
parseInt(WORDCOUNTS.total[id], 10);
}
$(this).children('li').each(function () {
/** {number} Word count in the category */
let cat_word_count = parseInt($(this).children('span').text(), 10);
/*
Linear version
const h = (v - $(this).children('span').text()) * 25 / v;
*/
/*
Logarithmic version
(25 / v) is vocab per pixel
log scale so the size scaled becomes Math.log(($(this).children('span').text()))
so the total height corresponding to text vocab after scaling should be
Math.log(v) the proportion of column height to box height is thus
(Math.log(($(this).children('span').text())) / Math.log(v))
putting this back in pixel, we get
(Math.log(($(this).children('span').text())) / Math.log(v)) * 25
should be the column height
so (25 - (Math.log(($(this).children('span').text())) / Math.log(v)) * 25)
is the intended border top size.
*/
// Avoid to put 0 in logarithm
cat_word_count += 1;
v += 1;
const h = 25 - Math.log(cat_word_count) / Math.log(v) * 25;
$(this).css('border-top-width', h + 'px');
});
}
/**
* Set the number of words known in a text (in edit_texts.php main page).
*
* @returns {undefined}
*/
function set_word_counts () {
$.each(WORDCOUNTS.totalu, function (key, value) {
let knownu, known, todo, stat0;
knownu = known = todo = stat0 = 0;
const expr = WORDCOUNTS.expru[key] ? parseInt((SUW & 2) ? WORDCOUNTS.expru[key] : WORDCOUNTS.expr[key]) : 0;
if (!WORDCOUNTS.stat[key]) {
WORDCOUNTS.statu[key] = WORDCOUNTS.stat[key] = [];
}
$('#total_' + key).html((SUW & 1 ? value : WORDCOUNTS.total[key]));
$.each(WORDCOUNTS.statu[key], function (k, v) {
if (SUW & 8)
$('#stat_' + k + '_' + key).html(v);
knownu += parseInt(v);
});
$.each(WORDCOUNTS.stat[key], function (k, v) {
if (!(SUW & 8))
$('#stat_' + k + '_' + key).html(v);
known += parseInt(v);
});
$('#saved_' + key).html(known ? ((SUW & 2 ? knownu : known) - expr + '+' + expr) : 0);
if (SUW & 4) {
todo = parseInt(value) + parseInt(WORDCOUNTS.expru[key] || 0) - parseInt(knownu);
} else {
todo = parseInt(WORDCOUNTS.total[key]) + parseInt(WORDCOUNTS.expr[key] || 0) - parseInt(known);
}
$('#todo_' + key).html(todo);
// added unknown percent
// console.log(SUW);
if (SUW & 8) {
unknowncount = parseInt(value) + parseInt(WORDCOUNTS.expru[key] || 0) - parseInt(knownu);
unknownpercent = Math.round(unknowncount * 10000 / (knownu + unknowncount)) / 100;
} else {
unknowncount = parseInt(WORDCOUNTS.total[key]) + parseInt(WORDCOUNTS.expr[key] || 0) - parseInt(known);
unknownpercent = Math.round(unknowncount * 10000 / (known + unknowncount)) / 100;
}
$('#unknownpercent_' + key).html(unknownpercent == 0 ? 0 : unknownpercent.toFixed(2));
// end here
if (SUW & 16) {
stat0 = parseInt(value) + parseInt(WORDCOUNTS.expru[key] || 0) - parseInt(knownu);
} else {
stat0 = parseInt(WORDCOUNTS.total[key]) + parseInt(WORDCOUNTS.expr[key] || 0) - parseInt(known);
}
$('#stat_0_' + key).html(stat0);
});
$('.barchart').each(set_barchart_item);
}
/**
* Handle the click event to switch between total and
* unique words count in edit_texts.php.
*
* @returns {undefined}
*/
function word_count_click () {
$('.wc_cont').children().each(function () {
if (parseInt($(this).attr('data_wo_cnt')) == 1) {
$(this).html('u');
} else {
$(this).html('t');
}
SUW = (parseInt($('#chart').attr('data_wo_cnt')) << 4) +
(parseInt($('#unknownpercent').attr('data_wo_cnt')) << 3) +
(parseInt($('#unknown').attr('data_wo_cnt')) << 2) +
(parseInt($('#saved').attr('data_wo_cnt')) << 1) +
(parseInt($('#total').attr('data_wo_cnt')));
set_word_counts();
});
}
/**
* Create a radio button with a candidate choice for a term annotation.
*
* @param {string} curr_trans Current anotation (translation) set for the term
* @param {string} trans_data All the useful data for the term
* @returns {string} An HTML-formatted option
*/
function translation_radio(curr_trans, trans_data)
{
if (trans_data.wid === null) {
return "";
}
const trim_trans = curr_trans.trim();
if (trim_trans == '*' || trim_trans == '') {
return "";
}
const set = trim_trans == trans_data.trans;
const option = `<span class="nowrap">
<input class="impr-ann-radio" ` +
(set ? 'checked="checked" ' : '') + 'type="radio" name="rg' +
trans_data.ann_index + `" value="` + escape_html_chars(trim_trans) + `" />
` + escape_html_chars(trim_trans) + `
</span>
<br />`;
return option;
}
/**
* When a term translation is edited, recreate it's annotations.
*
* @param {Object} trans_data Useful data for this term
* @param {int} text_id Text ID
*/
function edit_term_ann_translations(trans_data, text_id)
{
const widset = trans_data.wid !== null;
// First create a link to edit the word in a new window
let edit_word_link;
if (widset) {
const req_arg = $.param({
fromAnn: "$(document).scrollTop()",
wid: trans_data.wid,
ord: trans_data.term_ord,
tid: text_id
})
edit_word_link = `<a name="rec${trans_data.ann_index}"></a>
<span class="click"
onclick="oewin('edit_word.php?` + escape_html_chars(req_arg) + `');">
<img src="icn/sticky-note--pencil.png" title="Edit Term" alt="Edit Term" />
</span>`;
} else {
edit_word_link = ' ';
}
$(`#editlink${trans_data.ann_index}`).html(edit_word_link);
// Now edit translations (if necessary)
let translations_list = "";
trans_data.translations.forEach(
function (candidate_trans) {
translations_list += translation_radio(candidate_trans, trans_data);
}
);
const select_last = trans_data.translations.length == 0;
// Empty radio button and text field after the list of translations
translations_list += `<span class="nowrap">
<input class="impr-ann-radio" type="radio" name="rg${trans_data.ann_index}" ` +
(select_last ? 'checked="checked" ' : '') + `value="" />
<input class="impr-ann-text" type="text" name="tx${trans_data.ann_index}` +
`" id="tx${trans_data.ann_index}" value="` +
(select_last ? escape_html_chars(curr_trans) : '') +
`" maxlength="50" size="40" />
<img class="click" src="icn/eraser.png" title="Erase Text Field"
alt="Erase Text Field"
onclick="$('#tx${trans_data.ann_index}').val('').trigger('change');" />
<img class="click" src="icn/star.png" title="* (Set to Term)"
alt="* (Set to Term)"
onclick="$('#tx${trans_data.ann_index}').val('*').trigger('change');" />
`;
// Add the "plus button" to add a translation
if (widset) {
translations_list +=
`<img class="click" src="icn/plus-button.png"
title="Save another translation to existent term"
alt="Save another translation to existent term"
onclick="updateTermTranslation(${trans_data.wid}, ` +
`'#tx${trans_data.ann_index}');" />`;
} else {
translations_list +=
`<img class="click" src="icn/plus-button.png"
title="Save translation to new term"
alt="Save translation to new term"
onclick="addTermTranslation('#tx${trans_data.ann_index}',` +
`${trans_data.term_lc},${trans_data.lang_id});" />`;
}
translations_list += `
<span id="wait${trans_data.ann_index}">
<img src="icn/empty.gif" />
</span>
</span>`;
$(`#transsel${trans_data.ann_index}`).html(translations_list);
}
/**
* Load the possible translations for a word.
*
* @param {int} pagepos Position to scroll to
* @param {string} word Word in lowercase to get annotations
* @param {int} term_id Term ID
*
* @since 2.9.0 The new parameter $wid is now necessary
*/
function do_ajax_edit_impr_text(pagepos, word, term_id)
{
// Special case, on empty word reload the main annotations form
if (word == '') {
$('#editimprtextdata').html('<img src="icn/waiting2.gif" />');
location.reload();
return;
}
// Load the possible translations for a word
const textid = $('#editimprtextdata').attr('data_id');
$.getJSON(
'api.php/v1/terms/' + term_id + '/translations',
{
text_id: textid,
term_lc: word
},
function (data) {
if ("error" in data) {
alert(data["error"]);
} else {
edit_term_ann_translations(data, textid);
$.scrollTo(pagepos);
$('input.impr-ann-text').on('change', changeImprAnnText);
$('input.impr-ann-radio').on('change', changeImprAnnRadio);
}
}
);
}
/**
* Show the right frames if found, and can load an URL in those frames
*
* @param {string|undefined} roUrl Upper-right frame URL to laod
* @param {string|undefined} ruUrl Lower-right frame URL to load
* @returns {boolean} true if frames were found, false otherwise
*/
function showRightFrames(roUrl, ruUrl) {
if (roUrl !== undefined) {
top.frames.ro.location.href = roUrl;
}
if (ruUrl !== undefined) {
top.frames.ru.location.href = ruUrl;
}
if ($('#frames-r').length) {
$('#frames-r').animate({right: '5px'});
return true;
}
return false;
}
/**
* Hide the right frames if found.
*
* @returns {boolean} true if frames were found, false otherwise
*/
function hideRightFrames() {
if ($('#frames-r').length) {
$('#frames-r').animate({right: '-100%'});
return true;
}
return false;
}
/**
* Hide the right frame and any popups.
*
* Called from several places: insert_word_ignore.php,
* set_word_status.php, delete_word.php, etc.
*
* @returns {undefined}
*/
function cleanupRightFrames() {
// A very annoying hack to get right frames to hide correctly.
// Calling hideRightFrames directly in window.parent.setTimeout
// does //not work* for some reason ... when called that way,
// in hideRightFrames $('#frames-r').length is always 0. I'm not
// sure why. Using the mytimeout method lets the js find the
// element at runtime, and then it's clicked, invoking the function
// hideRightFrames, which then works.
//
// We have to use an anon function to ensure that the frames-r
// gets resolved when the timeout fires.
const mytimeout = function() {
const rf = window.parent.document.getElementById('frames-r');
rf.click();
}
window.parent.setTimeout(mytimeout, 800);
window.parent.document.getElementById('frame-l').focus();
window.parent.setTimeout(window.parent.cClick, 100);
}
/**
* Play the success sound.
*
* @returns {object} Promise on the status of sound
*/
function successSound() {
document.getElementById('success_sound').pause();
document.getElementById('failure_sound').pause();
return document.getElementById('success_sound').play();
}
/**
* Play the failure sound.
*
* @returns {object} Promise on the status of sound
*/
function failureSound() {
document.getElementById('success_sound').pause();
document.getElementById('failure_sound').pause();
return document.getElementById('failure_sound').play();
}
const lwt = {
/**
* Prepare the action so that a click switches between
* unique word count and total word count.
*
* @returns {undefined}
*/
prepare_word_count_click: function () {
$('#total,#saved,#unknown,#chart,#unknownpercent')
.on('click', function( event ) {
$(this).attr('data_wo_cnt',parseInt($(this).attr('data_wo_cnt'))^1);
word_count_click();
event.stopImmediatePropagation();
}).attr('title',"u: Unique Word Counts\nt: Total Word Counts");
do_ajax_word_counts();
},
/**
* Save the settings about unique/total words count.
*
* @returns {undefined}
*/
save_text_word_count_settings: function () {
if (SUW == SHOWUNIQUE) {
return;
}
const a = $('#total').attr('data_wo_cnt') +
$('#saved').attr('data_wo_cnt') +
$('#unknown').attr('data_wo_cnt') +
$('#unknownpercent').attr('data_wo_cnt') +
$('#chart').attr('data_wo_cnt');
do_ajax_save_setting('set-show-text-word-counts', a);
}
}
// Present data in a handy way, for instance in a form
$.fn.serializeObject = function () {
const o = {};
const a = this.serializeArray();
$.each(a, function () {
if (o[this.name] !== undefined) {
if (!o[this.name].push) {
o[this.name] = [o[this.name]];
}
o[this.name].push(this.value || '');
} else {
o[this.name] = this.value || '';
}
});
return o;
};
/**
* Wrap the radio buttons into stylised elements.
*/
function wrapRadioButtons() {
$(
':input,.wrap_checkbox span,.wrap_radio span,a:not([name^=rec]),select,' +
'#mediaselect span.click,#forwbutt,#backbutt'
).each(function (i) { $(this).attr('tabindex', i + 1); });
$('.wrap_radio span').on('keydown', function (e) {
if (e.keyCode == 32) {
$(this).parent().parent().find('input[type=radio]').trigger('click');
return false;
}
});
}
/**
* Do a lot of different DOM manipulations
*/
function prepareMainAreas() {
$('.edit_area').editable('inline_edit.php',
{
type: 'textarea',
indicator: '<img src="icn/indicator.gif">',
tooltip: 'Click to edit...',
submit: 'Save',
cancel: 'Cancel',
rows: 3,
cols: 35
}
);
$('select').wrap("<label class='wrap_select'></label>");
$('form').attr('autocomplete', 'off');
$('input[type="file"]').each(function () {
if (!$(this).is(':visible')) {
$(this).before('<button class="button-file">Choose File</button>')
.after('<span style="position:relative" class="fakefile"></span>')
.on('change', function () {
let txt = this.value.replace('C:\\fakepath\\', '');
if (txt.length > 85)txt = txt.replace(/.*(.{80})$/, ' ... $1');
$(this).next().text(txt);
})
.on('onmouseout', function () {
let txt = this.value.replace('C:\\fakepath\\', '');
if (txt.length > 85)txt = txt.replace(/.*(.{80})$/, ' ... $1');
$(this).next().text(txt);
});
}
});
$('input[type="checkbox"]').each(function (z) {
if (typeof z === 'undefined')z = 1;
if (typeof $(this).attr('id') === 'undefined') {
$(this).attr('id', 'cb_' + z++);
}
$(this).after(
'<label class="wrap_checkbox" for="' + $(this).attr('id') +
'"><span></span></label>'
);
});
$('span[class*="tts_"]').on('click', function () {
const lg = $(this).attr('class').replace(/.*tts_([a-zA-Z-]+).*/, '$1');
const txt = $(this).text();
readRawTextAloud(txt, lg);
});
$(document).on('mouseup', function () {
$('button,input[type=button],.wrap_radio span,.wrap_checkbox span')
.trigger('blur');
});
$('.wrap_checkbox span').on('keydown', function (e) {
if (e.keyCode == 32) {
$(this).parent().parent().find('input[type=checkbox]').trigger('click');
return false;
}
});
$('input[type="radio"]').each(function (z) {
if (z === undefined) {
z = 1;
}
if (typeof $(this).attr('id') === 'undefined') {
$(this).attr('id', 'rb_' + z++);
}
$(this).after(
'<label class="wrap_radio" for="' + $(this).attr('id') +
'"><span></span></label>'
);
});
$('.button-file').on('click', function () {
$(this).next('input[type="file"]').trigger('click');
return false;
});
$('input.impr-ann-text').on('change', changeImprAnnText);
$('input.impr-ann-radio').on('change', changeImprAnnRadio);
$('form.validate').on('submit', check);
$('input.markcheck').on('click', markClick);
$('.confirmdelete').on('click', confirmDelete);
$('textarea.textarea-noreturn').on('keydown', textareaKeydown);
// Resizable from right frames
$('#frames-r').resizable({
handles: "w",
stop: function (_event, ui) {
// Resize left frames
$('#frames-l').css('width', ui.position.left - 20);
// Save settings
do_ajax_save_setting(
'set-text-l-framewidth-percent',
Math.round($('#frames-l').width() / $(window).width() * 100)
);
}
});
$('#termtags').tagit(
{
beforeTagAdded: function (_event, ui) {
return !containsCharacterOutsideBasicMultilingualPlane(ui.tag.text());
},
availableTags: TAGS,
fieldName: 'TermTags[TagList][]'
}
);
$('#texttags').tagit(
{
beforeTagAdded: function (_event, ui) {
return !containsCharacterOutsideBasicMultilingualPlane(ui.tag.text());
},
availableTags: TEXTTAGS,
fieldName: 'TextTags[TagList][]'
}
);
markClick();
setTheFocus();
if (
$('#simwords').length > 0 && $('#langfield').length > 0 &&
$('#wordfield').length > 0
) {
$('#wordfield').on('blur', do_ajax_show_similar_terms);
do_ajax_show_similar_terms();
}
window.setTimeout(noShowAfter3Secs, 3000);
}
$(window).on('load', wrapRadioButtons);
$(document).ready(prepareMainAreas);