1/*! 2 * Copyright 2016-2024 GrammarSoft ApS <[email protected]> at https://grammarsoft.com/ 3 * All Rights Reserved 4 * Linguistic backend by Eckhard Bick <[email protected]> 5 * Frontend by Tino Didriksen <[email protected]> 6 */ 7'use strict'; 8 9function onResizeHandler() { 10 let ibr = $('#img-blue-rounded'); 11 if (ibr.length) { 12 ibr.outerHeight('auto'); 13 ibr.parent().outerHeight('auto'); 14 ibr.outerHeight(ibr.parent().outerHeight()); 15 } 16 17 let iframe = $('.tox-edit-area').find('iframe').first(); 18 if (!iframe.length) { 19 return; 20 } 21 22 $('.tox-tinymce').css('border', '0'); 23 $('.tox-tinymce').css('height', 'auto'); 24 $('.tox-edit-area').height(300); 25 iframe.height(300); 26 27 let height = $('#container').innerHeight(); 28 height -= $('#headbar').outerHeight(); 29 height -= $('#footbar').outerHeight(); 30 height -= $('#ed-head').outerHeight(); 31 height -= $('#ed-foot').outerHeight(); 32 height -= 40; 33 //height -= $('.mce-menubar.mce-toolbar').outerHeight(); 34 if ($('.tox-editor-header:visible').length) { 35 height -= $('.tox-editor-header').outerHeight(); 36 } 37 //height -= $('.mce-statusbar').outerHeight(); 38 height -= 1; 39 $('.tox-edit-area').height(height); 40 41 iframe.height(height); 42 // iOS cannot resize iframes, so handle this case by making the container bigger instead 43 if (iframe.height() != height) { 44 // console.log('Height was not set'); 45 let height = iframe.height(); 46 $('.tox-edit-area').height(height); 47 iframe.parent().height(height); 48 } 49 50 if (tinyMCE && tinyMCE.activeEditor) { 51 $(tinyMCE.activeEditor.getBody()).css('min-height', height-20); 52 } 53 54 editorHasText(); 55} 56 57function onResize() { 58 setTimeout(onResizeHandler, 500); 59 setTimeout(onResizeHandler, 1000); 60} 61 62function showInput() { 63 $('#do_input_text').val(ccomma.text()); 64 let m = bootstrap.Modal.getOrCreateInstance(document.getElementById('dg_input')); 65 m.show(); 66} 67 68$(function() { 69 shared_init(); 70 71 tnjq = Object.keys(text_nodes).join(','); 72 73 tinymce.init({ 74 selector: 'textarea', 75 menubar: false, 76 statusbar: false, 77 plugins: ['searchreplace'], 78 browser_spellcheck: true, 79 language: 'en', 80 entity_encoding: 'raw', 81 language_url: 'static/'+UILANG2+'.js', 82 content_css: 'static/inline.css?v=4e397eab', 83 valid_elements: '*[*]', 84 extended_valid_elements: '*[*]', 85 allow_script_urls: true, 86 //toolbar: ['undo redo | bold italic underline strikethrough removeformat | searchreplace | alignleft aligncenter alignright al
86ignjustify'], 87 setup: function(editor) { 88 editor.on('keyup paste change', function() { 89 if (eht_prevent) { 90 return; 91 } 92 //// console.log(eht_timer); 93 if (eht_timer) { 94 clearTimeout(eht_timer); 95 } 96 eht_timer = setTimeout(editorHasText, 250); 97 }); 98 editor.on('copy', function() { commaIgnoreAll(); matomo_event('ui', 'copy'); }); 99 editor.on('paste', function() { commaIgnoreAll(); setTimeout(cleanEditorContent, 500); matomo_event('ui', 'paste'); }); 100 editor.on('undo', function() { commaIgnoreAll(); setTimeout(cleanEditorContent, 500); /* matomo_event('ui', 'undo'); */ }); 101 editor.on('postRender', function() { 102 let iframe = $('iframe'); 103 $('#working').show().offset(iframe.offset()).width(iframe.width()).height(iframe.height()).hide(); 104 $('.tox-editor-header').hide(); 105 onResize(); 106 107 // Selective translations 108 if (tinymce && tinymce.i18n && tinymce.i18n.data && tinymce.i18n.data.da) { 109 tinymce.i18n.data.da['Blocks'] = 'Afsnit'; 110 tinymce.i18n.data.da['Inline'] = 'Skrifttype'; 111 } 112 }); 113 editor.on('click', function(e) { 114 e.preventDefault(); 115 let p = $(tinyMCE.activeEditor.getBody()); 116 p.find('.popover').remove(); 117 p.find('span.comma').popover('dispose').removeClass('comma-selected'); 118 119 ccomma = null; 120 return false; 121 }); 122 } 123 }); 124 125 checkDone(); 126 127 $('#btn-check,#btn-correct-all,#btn-wrong-all,#btn-accept-yellows,#btn-copy,#btn-erase,#btn-close').addClass('disabled'); 128 $('#btn-check').click(function() { 129 checkAll(); 130 matomo_event('ui', 'check'); 131 }); 132 $('#btn-correct-all').click(function () { 133 if ($(this).hasClass('disabled')) { 134 return; 135 } 136 matomo_event('ui', 'correct-all'); 137 log_click('correct-all'); 138 $(tinyMCE.activeEditor.getBody()).find('span.comma-green,span.comma-yellow,span.comma-info').each(function() { 139 ccomma = $(this); 140 commaAccept(); 141 }); 142 }); 143 $('#btn-wrong-all').click(function () { 144 if ($(this).hasClass('disabled')) { 145 return; 146 } 147 matomo_event('ui', 'wrong-all'); 148 log_click('wrong-all'); 149 $(tinyMCE.activeEditor.getBody()).find('span.comma-red').each(function() { 150 ccomma = $(this); 151 commaDiscard(); 152 }); 153 }); 154 $('#btn-accept-yellows').click(function () { 155 if ($(this).hasClass('disabled')) { 156 return; 157 } 158 matomo_event('ui', 'correct-all-yellow'); 159 log_click('correct-all-yellow'); 160 $(tinyMCE.activeEditor.getBody()).find('span.comma-yellow').each(function() { 161 ccomma = $(this); 162 commaYellow(); 163 }); 164 }); 165 166 $('#btn-menu').popover({ 167 container: document.body, 168 sanitize: false, 169 animation: false, 170 html: true, 171 placement: 'bottom', 172 fallbackPlacements: ['left', 'top', 'right', 'bottom'], 173 trigger: 'click', 174 }); 175 176 $('#btn-erase').click(function() { 177 $(tinyMCE.activeEditor.getBody()).text(''); 178 editorHasText(); 179 matomo_event('ui', 'erase'); 180 }); 181 $('#btn-close').click(function() { 182 commaIgnoreAll(); 183 matomo_event('ui', 'close'); 184 }); 185 $('#btn-copy').click(function() { 186 commaIgnoreAll(); 187 $(tinyMCE.activeEditor.getBody()).focus(); 188 tinyMCE.activeEditor.execCommand('selectAll'); 189 if (true || !iOS) { // ToDo: Test on newer iOS 190 tinyMCE.activeEditor.execCommand('copy'); 191 } 192 matomo_event('ui', 'copy'); 193 }); 194 $('#btn-transfer').click(transfer_document); 195 196 $('[data-bs-toggle="popover"]').popover(); 197 198 let opt = $('#options'); 199 opt.on('hide.bs.modal', function() { opt.find('.modal-body').html(''); }); 200 opt.on('hidden.bs.modal', function() { setTimeout(impl_loadUserdata, 2000); }); 201 $('#btn-options').click(function() { 202 opt.find('.modal-body').text(''); 203 opt.find('.modal-body').html('<iframe src="'+ROOT_URL_SELF+'/html/options.html?tool='+g_tool+'&singletool=1&host=web"></iframe>'); 204 let m = bootstrap.Modal.getOrCreateInstance(opt.get(0)); 205 m.show(); 206 }); 207 208 $('#dg_input').on('shown.bs.modal', function() { 209 $('#do_input_text').focus(); 210 }); 211 $('#do_input_one').click(function() { 212 let txt = $('#do_input_text').val(); 213 matomo_event('comma', 'input', txt); 214 log_marking_action({'a': 'input', 'm': ccomma.attr('data-types'), 'w': ccomma.text(), 'r': txt, 't': ccomma.attr('data-tid')}); 215 commaDo(txt); 216 }); 217 $('#do_input_all').click(function() { 218 let txt = $('#do_input_text').val(); 219 matomo_event('comma', 'input', txt); 220 log_marking_action({'a': 'input', 'm': ccomma.attr('data-types'), 'w': ccomma.text(), 'r': txt, 't': ccomma.attr('data-tid')}); 221 commaDo(txt); 222 }); 223 224 $('#btn-undo').click(undo_restore).addClass('disabled'); 225 226 $(window).on('resize', onResize); 227 $(window).on('beforeunload', function(e) { 228 // console.log(e); 229 if (tinyMCE && tinyMCE.activeEditor && $(tinyMCE.activeEditor.getBody()).find('span.comma').length) { 230 let msg = l10n_translate('MSG_WARN_CLOSE'); 231 e.returnValue = msg; 232 return msg; 233 } 234 }); 235 236 $('.warnTitle').click(function() { 237 alert($(this).attr('title')); 238 return true; 239 }); 240 241 $('#working').hide(); 242 $('#warning').hide();
243 $('#info').hide(); 244 iOS = window.navigator.userAgent.indexOf('Apple') !== -1 245 && (window.navigator.userAgent.indexOf('iPad') !== -1 246 || window.navigator.userAgent.indexOf('iPhone') !== -1 247 || window.navigator.userAgent.indexOf('iPod') !== -1) 248 ; 249 android = (window.navigator.userAgent.indexOf('Android') !== -1); 250 251 if (iOS) { 252 $('#btn-copy').find('.text').text(l10n_translate('BTN_SELECT_ALL')); 253 } 254 255 onResizeHandler(); 256 shared_hook(); 257 // ToDo: Remember focus, scroll to focus 258 259 g_impl.showWarning = function (msg) { 260 $('#warning').html(l10n_translate(msg)); 261 $('#warning').show(); 262 }; 263 g_impl.showError = function(msg) { 264 checkDone(); 265 alert(l10n_translate(msg)); 266 }; 267 268 g_impl.parseCheckStart = function() { 269 if (ts_slow) { 270 clearTimeout(ts_slow); 271 } 272 ts_slow = setTimeout(check_slow, 5000); 273 }; 274 g_impl.parseProgress = function() { 275 ts_fail = 0; 276 if (ts_slow) { 277 clearTimeout(ts_slow); 278 } 279 ts_xhr = null; 280 ts_slow = null; 281 }; 282 g_impl.parseNoResult = function() { 283 let html = $(tinyMCE.activeEditor.getBody()).html(); 284 if (ts_fail < 2) { 285 ++ts_fail; 286 --to_send_i; 287 log_warning({'e': 'empty', 'd': to_send_last, 'l': html.length, 'b': html.substr(0, Defs.MAX_RQ_SIZE)}); 288 sendTexts(); 289 return 0; 290 } 291 log_error({'e': 'empty', 'd': to_send_last, 'l': html.length, 'b': html.substr(0, Defs.MAX_RQ_SIZE)}); 292 alert(l10n_translate('ERR_EMPTY_RESPONSE')); 293 }; 294 g_impl.parseChunkDone = function() { 295 for ( ; segments_i<segments.length ; ++segments_i) { 296 let id = segment_ids[segments_i]; 297 let txt = ''; 298 for (let j=0 ; j<segments[segments_i].length ; ++j) { 299 let w = segments[segments_i][j]; 300 if (w.mark.length > 1) { 301 commaCreateInline(id, txt, w); 302 } 303 if (w.word.search(/^[-,.:;?!$*½§£$%&()={}+]$/) === -1) { 304 txt += ' '; 305 } 306 txt += w.word; 307 } 308 } 309 310 $('#btn-check,#btn-correct-all,#btn-wrong-all,#btn-accept-yellows,#btn-options,#btn-close').removeClass('disabled'); 311 $('#working').hide(); 312 let editor = $(tinyMCE.activeEditor.getBody()); 313 if (editor.find('span.comma').length) { 314 editor.removeAttr('contenteditable'); 315 } 316 if (!ccomma) { 317 let first = editor.find('span.comma').first(); 318 if (first.length) { 319 first.focus().click(); 320 } 321 } 322 323 if (to_send_i < to_send.length) { 324 sendTexts(); 325 } 326 else { 327 if (!ccomma) { 328 doneOpen('#btn-check'); 329 } 330 } 331 }; 332 g_impl.parseError = function(e) { 333 // console.log(e); 334 let html = $(tinyMCE.activeEditor.getBody()).html(); 335 log_error({'e': 'parse', 'd': to_send_last, 'l': html.length, 'b': html.substr(0, Defs.MAX_RQ_SIZE)}); 336 $('#warning').html(l10n_translate('ERR_CRITICAL') + '</p><p>' + e); 337 $('#warning').show(); 338 commaIgnoreAll(); 339 }; 340 g_impl.parseSendStart = function() {}; 341 g_impl.parseSendEnd = function() { 342 setTimeout(function() { 343 parseResult({c:'', v:VERSION_PROTOCOL, t:0}); 344 }, 100); 345 }; 346 347 if (!g_impl.hasOwnProperty('beforeSendTexts')) { 348 g_impl.beforeSendTexts = function(t) { return t; }; 349 } 350 if (!g_impl.hasOwnProperty('beforeParseResult')) { 351 g_impl.beforeParseResult = function(t) { return t; }; 352 } 353}); 354 355function editorHasText() { 356 //// console.log(eht_timer); 357 eht_timer = null; 358 if (!tinyMCE || !tinyMCE.activeEditor || eht_prevent) { 359 return; 360 } 361 let ed = tinyMCE.activeEditor.getBody(); 362 let editor = $(ed); 363 364 if (editor.find('span.comma-green,span.comma-yellow').length) { 365 $('#btn-correct-all').removeClass('disabled'); 366 } 367 else { 368 $('#btn-correct-all').addClass('disabled'); 369 } 370 371 if (editor.find('span.comma-red').length) { 372 $('#btn-wrong-all').removeClass('disabled'); 373 } 374 else { 375 $('#btn-wrong-all').addClass('disabled'); 376 } 377 378 if (editor.find('span.comma').length) { 379 editor.removeAttr('contenteditable'); 380 } 381 else { 382 editor.attr('contenteditable', true); 383 editor.css('overflow-y', ''); 384 last_result = null; 385 } 386 387 if (ed.childNodes.length && ed.textContent.length) { 388 $('#btn-check,#btn-copy,#btn-erase').removeClass('disabled'); 389 $('#placeholder').hide(); 390 } 391 else { 392 $('#btn-check,#btn-copy,#btn-erase').addClass('disabled'); 393 $('#placeholder').show(); 394 } 395 396 $(ed).find('span.comma').popover('dispose'); 397 ccomma = null; 398} 399 400let g_undos = []; 401 402function undo_save() { 403 g_undos.push($(tinyMCE.activeEditor.getBody()).contents().clone(true, true)); 404 while (g_undos.length > 10) { 405 g_undos.shift(); 406 } 407 $('#btn-undo').removeClass('disabled'); 408 // console.log(g_undos); 409} 410
411function undo_restore() { 412 let u = g_undos.pop(); 413 if (u) { 414 let b = tinyMCE.activeEditor.getBody(); 415 $(b).contents().remove(); 416 $(b).append(u); 417 } 418 if (!g_undos.length) { 419 $('#btn-undo').addClass('disabled'); 420 } 421 // console.log(g_undos); 422} 423 424function doneOpen(which) { 425 $(which).popover('show'); 426 setTimeout(function() { $(which).popover('hide'); }, 4000); 427} 428 429function commaAddToDict() { 430 let word = ccomma.text(); 431 addToDictionary(word); 432 433 let p = $(tinyMCE.activeEditor.getBody()); 434 let cs = p.find('span.comma-yellow').each(function() { 435 ccomma = $(this); 436 if (ccomma.text() !== word && ccomma.text().toUpperCase() !== word.toUpperCase()) { 437 return; 438 } 439 commaYellow(); 440 }); 441 matomo_event('ui', 'dict-add', word); 442 log_marking_action({'a': 'dict-add', 'w': word, 't': ccomma.attr('data-tid')}); 443} 444 445function commaPopup(c, exp) { 446 let b = tinyMCE.activeEditor.getBody(); 447 let p = $(b); 448 p.find('.popover').remove(); 449 p.find('span.comma').popover('dispose').removeClass('comma-selected'); 450 451 // If this comma's popup is the open one, just close it 452 if (!exp && ccomma && ccomma.get(0) === c) { 453 ccomma = null; 454 return; 455 } 456 ccomma = $(c); 457 ccomma.focus().addClass('comma-selected'); 458 //// console.log([c.offset(), c.width(), p.width()]); 459 460 let text = ccomma.text(); 461 let all_upper = is_upper(text); 462 let first_upper = all_upper || is_upper(text.charAt(0)); 463 464 let types = ccomma.attr('data-types'); 465 if (g_marks.to_lower.test(types)) { 466 all_upper = first_upper = false;
467 } 468 469 let html = '<div id="popup">'; 470 if (!ccomma.attr('data-sugs') && (text === ',' || text === '.')) { 471 let tgreen = l10n_translate('BTN_COMMA_INSERT'); 472 let tred = l10n_translate('BTN_IGNORE'); 473 if (g_marks.rx_del.test(types) || ccomma.hasClass('comma-red') || ccomma.hasClass('comma-info')) { 474 tgreen = l10n_translate('BTN_COMMA_KEEP'); 475 tred = l10n_translate('BTN_COMMA_REMOVE'); 476 } 477 if (ccomma.attr('data-types').indexOf('%k-stop') != -1) { 478 tgreen = l10n_translate('BTN_COMMA_INSERT_STOP'); 479 } 480 481 if (ccomma.hasClass('comma-red')) { 482 tred = '<b>'+tred+'</b>'; 483 } 484 else { 485 tgreen = '<b>'+tgreen+'</b>'; 486 } 487 if (g_marks.rx_del.test(types)) { 488 html += '<div class="action"><a class="commaDiscard"><span class="icon icon-accept"></span><span>'+tgreen+'</span></a></div>'; 489 html += '<div class="action"><a class="commaAccept"><span class="icon icon-discard"></span><span>'+tred+'</span></a></div>'; 490 } 491 else { 492 html += '<div class="action"><a class="commaAccept"><span class="icon icon-accept"></span><span>'+tgreen+'</span></a></div>'; 493 html += '<div class="action"><a class="commaDiscard"><span class="icon icon-discard"></span><span>'+tred+'</span></a></div>'; 494 } 495 } 496 else { 497 let crs = ccomma.attr('data-sugs').split('\t'); 498 for (let c=0 ; c<crs.length ; ++c) { 499 if (crs[c].length === 0) { 500 continue; 501 } 502 let txt = crs[c]; 503 if (all_upper) { 504 txt = txt.toUpperCase(); 505 } 506 else if (first_upper) { 507 txt = uc_first(txt); 508 } 509 html += '<div class="action"><a class="commaAccept"><span class="icon icon-accept"></span><span>'+escHTML(txt)+'</span></a></div>'; 510 } 511 if (g_marks.rx_del.test(types)) { 512 html += '<div class="action"><a class="commaAccept"><span class="icon icon-discard"></span><span>'+l10n_translate('BTN_GRAMMAR_REMOVE')+'</span></a></div>'; 513 } 514 if (g_marks.rx_ins.test(types)) { 515 html += '<div class="action"><a class="commaAccept"><span class="icon icon-accept"></span><span>'+l10n_translate('BTN_GRAMMAR_INSERT')+'</span></a></div>'; 516 } 517 if (g_marks.dict.test(types)) { 518 html += '<div class="action"><a class="commaAddToDict"><span class="icon icon-accept"></span><span>'+l10n_translate('BTN_DICT_ADD')+'</span></a></div>'; 519 } 520 if (types.indexOf('£question') !== -1) { 521 html += '<div class="action"><a class="commaDiscard"><span class="icon icon-accept"></span><span>Ok</span></a></div>'; 522 } 523 else { 524 html += '<div class="action"><a class="showInput"><span class="icon icon-accept"></span><span>'+l10n_translate('BTN_EDIT')+'</span></a></div>'; 525 html += '<div class="action"><a class="commaDiscard"><span class="icon icon-discard"></span><span>'+l10n_translate('BTN_IGNORE')+'</span></a></div>'; 526 } 527 } 528 if (android || iOS) { 529 html += '<div class="action"><a class="commaIgnoreAll"><span class="icon icon-edit"></span><span>'+l10n_translate('BTN_EDIT_TEXT')+'</span></a></div>'; 530 } 531 if (!exp) { 532 html += '<div class="action"><a class="commaExplain"><span class="icon icon-explain"></span><span>'+l10n_translate('BTN_EXPL_SEE')+'</span></a></div>'; 533 } 534 html += '</div>'; 535 if (exp) { 536 html += '<div id="explanation">'; 537 let ts = ccomma.attr('data-types').split(/ /g); 538 for (let i=0 ; i<ts.length ; ++i) { 539 if (!g_marks.types.hasOwnProperty(ts[i])) { 540 // console.log("No such error type: "+ts[i]); 541 continue; 542 } 543 let ctype = '<h3>'+g_marks.types[ts[i]][0]+'</h3><p>'+g_marks.types[ts[i]][1].replace(/(<br>\s*)+<br>\s*/g, '</p><p>')+'</p>'; 544 html += ctype.replace(/<p>\s*<\/p>/g, ''); 545 } 546 html += '</div>'; 547 } 548 else { 549 html += '<div id="explanation">'; 550 let ts = ccomma.attr('data-types').split(/ /g); 551 for (let i=0 ; i<ts.length ; ++i) { 552 if (!g_marks.types.hasOwnProperty(ts[i])) { 553 // console.log("No such error type: "+ts[i]); 554 continue; 555 } 556 let ctype = g_marks.types[ts[i]][0]+'<br>'; 557 html += ctype.replace(/<p>\s*<\/p>/g, ''); 558 } 559 html += '</div>'; 560 } 561 562 // If the comma is too close to the right edge, don't set the viewport as it messes up alignment 563 let vp = null; 564 if (ccomma.offset().left + ccomma.width() < p.width() - 100) { 565 vp = b; 566 } 567 568 // If the comma is too close to the top, force it to open below 569 let pl = 'top'; 570 if (ccomma.offset().top < 300) { 571 pl = 'bottom'; 572 } 573 574 ccomma.popover({ 575 container: b, 576 sanitize: false, 577 animation: false, 578 content: html, 579 html: true, 580 placement: pl, 581 fallbackPlacements: ['bottom', 'top', 'right', 'left'], 582 trigger: 'manual', 583 viewport: vp, 584 }).on('shown.bs.popover', function() { 585 let pop = p.find('div.popover'); 586 pop.click(function(e) { 587 e.preventDefault(); 588 return false;
589 }); 590 pop.find('.commaAccept').click(function(e) { 591 e.preventDefault(); 592 commaAccept($(this)); 593 return false; 594 }); 595 pop.find('.commaDiscard').click(function(e) { 596 e.preventDefault(); 597 commaDiscard(); 598 return false; 599 }); 600 pop.find('.commaAddToDict').click(function(e) { 601 e.preventDefault(); 602 commaAddToDict(); 603 return false; 604 }); 605 pop.find('.commaIgnoreAll').click(function(e) { 606 e.preventDefault(); 607 commaIgnoreAll(); 608 return false; 609 }); 610 pop.find('.commaExplain').click(function(e) { 611 e.preventDefault(); 612 commaExplain(); 613 return false; 614 }); 615 pop.find('.showInput').click(function(e) { 616 e.preventDefault(); 617 showInput(); 618 return false; 619 }); 620 621 //pop.scrollintoview(); 622 pop.find('a[target="_blank"]').off().on('click', function() { 623 window.open($(this).attr('href')); 624 }); 625 }); 626 ccomma.popover('show'); 627} 628 629function commaInlineClick(e) { 630 e.preventDefault(); 631 commaPopup(this, false); 632 matomo_event('comma', 'click'); 633 return false; 634} 635 636function commaExplain() { 637 commaPopup(ccomma.get(0), true); 638 matomo_event('comma', 'explain', ccomma.attr('data-types')); 639 log_click({'explain': ccomma.attr('data-types'), 'w': ccomma.attr('data-word')}); 640} 641 642function commaCreateInline(id, txt, comma) { 643 let p = $(tinyMCE.activeEditor.getBody()).find('#s'+id); 644 if (p.length == 0) { 645 renumerate_editor(); 646 p = $(tinyMCE.activeEditor.getBody()).find('#s'+id); 647 } 648 let ml = p.html(); 649 let nonl = /[^\d\wa-zA-ZéÃö̾̿Ãåà .,]/ig; 650 let lg = /[<>]/; 651 652 let types = comma.mark.split(/ /g); 653 let col = markingColor(types); 654 655 txt = $.trim(txt.replace(/\s+/g, '').replace(nonl, '\ue000')); 656 // console.log([txt.length, txt, comma.word]); 657 let insert = g_marks.rx_ins.test(comma.mark); 658 659 // Find the spot in the editor where the comma is to be changed 660 let ti = 0; 661 let i = 0; 662 for ( ; i<ml.length ; ++i) { 663 i = skipNonText(ml, i); 664 for (let tn=ti ; tn<txt.length && tn<ti+10 ; ++tn) { 665 if (txt.charAt(tn) === ml.charAt(i) || (txt.charAt(tn) === '\ue000' && !lg.test(ml.charAt(i)) && nonl.test(ml.charAt(i)))) { 666 ti = tn; 667 // Find identical sequential letters, e.g. 1977 668 while (ti < txt.length-1 && txt.charAt(ti) === ml.charAt(i)) { 669 //// console.log([ti, i, txt.charAt(ti), ml.charAt(i)]); 670 ++ti; 671 i = skipNonText(ml, i+1); 672 } 673 break; 674 } 675 } 676 //// console.log([ti, i, txt.charAt(ti), ml.charAt(i)]); 677 if (ti >= txt.length-1) { 678 // Check after text as well to find correct insert position 679 let nc = comma.word; 680 //// console.log([i, nc]); 681 if (!insert && nc && nc.length) { 682 let ni = skipNonText(ml, i+1); 683 //// console.log([ni, ml.charAt(ni), nc.charAt(0)]); 684 if (ml.charAt(ni) === nc.charAt(0)) { 685 // Skip tags backwards as well 686 if (ml.charAt(ni-1) === '>') { 687 while (ni > 0 && ml.charAt(ni) !== '<') { 688 --ni; 689 } 690 } 691 //// console.log([nc, i, ni]); 692 i = ni - 2; 693 } 694 } 695 break; 696 } 697 } 698 if (ml.charAt(i) !== ' ' && ml.charAt(i+1) === ' ' && (insert || comma.word === ',' || comma.word === '.')) { 699 ++i; 700 } 701 // console.log([ti, i]); 702 703 let ins = comma.word; 704 if (ins === STR_NULLISH) { 705 ins = comma.oword; 706 } 707 if (ml.indexOf(comma.word, i) === -1 && ml.indexOf(escHTML(comma.word), i) !== -1) { 708 comma.word = escHTML(comma.word); 709 } 710 711 if (insert) { 712 if (!/^[,.]$/.test(ins)) { 713 ins = ' ' + ins; 714 } 715 } 716 else { 717 let ni = ml.indexOf(comma.word, i); 718 let ot = ml.lastIndexOf('<', ni); 719 let et = ml.indexOf('>', ot); 720 while (ni !== -1 && ni > ot && ni < et) { 721 ni = ml.indexOf(comma.word, et+1); 722 ot = ml.lastIndexOf('<', ni); 723 et = ml.indexOf('>', ot); 724 } 725 if (ni !== -1) { 726 i = ni; 727 } 728 } 729 if (ml.length >= i+2 && ml.charAt(i+1) == '<' && ml.charAt(i+2) == '/') { 730 i = skipNonText(ml, i+1); 731 } 732 // console.log([ti, i, ml.substr(i)]); 733 734 if (!insert && /\S/.test(ml.charAt(i + comma.word.length - 1))) { 735 comma.word = $.trim(comma.word); 736 //// console.log(comma); 737 } 738 739 let alt = ''; 740 if (_live_options.config.opt_color) { 741 alt = ' alt'; 742 } 743 744 let rpl = '<span class="comma comma-'+col+alt+'" data-types="'+escHTML(comma.mark)+'" data-sugs="'+escHTML(comma.suggs)+'" data-tid="'+comma.tid+'">'+escHTML(ins)+'</span>'; 745 746 if (insert) { 747 ml = ml.substr(0, i) + rpl + ' ' + ml.substr(i); 748 } 749 else { 750 ml = ml.substr(0, i) + rpl + ml.substr(i+comma.word.length); 751 } 752 ml = ml.replace(' ', ' ').replace(/ +/g, ' '); 753 p.html(ml); 754 $(p).find('span.comma').off().click(commaInlineClick); 755} 756 757function commaDo(rpl) { 758 undo_save(); 759 760 let p = $(tinyMCE.activeEditor.getBody()); 761 p.find('.popover').remove(); 762 let commas = p.find('span.comma'); 763 commas.popover('dispose'); 764 bootstrap.Modal.getOrCreateInstance(document.getElementById('dg_input')).hide(); 765 if (!ccomma) { 766 return; 767 } 768 769 let c = 0; 770 for (let i=0 ; i<commas.length ; ++i) { 771 if (commas[i] === ccomma.get(0)) { 772 c = i; 773 } 774 } 775 776 ccomma.replaceWith(rpl); 777 ccomma = null; 778 779 if (p.find('span.comma').length == 0) { 780 $('#btn-correct-all,#btn-wrong-all,#btn-accept-yellows,#btn-close').addClass('disabled'); 781 doneOpen('#btn-correct-all'); 782 p.attr('contenteditable', true); 783 } 784 else { 785 p.removeAttr('contenteditable'); 786 commas = p.find('span.comma'); 787 if (c >= commas.length) { 788 --c; 789 } 790 commas.eq(c).click(); 791 } 792} 793 794function commaAccept(e) { 795 if (!ccomma) { 796 return; 797 } 798 799 let rpl = ''; 800 let types = ccomma.attr('data-types'); 801 let click = {'a': 'accept', 'm': types, 'w': ccomma.text(), 't': ccomma.attr('data-tid')}; 802 if (g_marks.rx_del.test(types)) { 803 click.a += '-remove'; 804 rpl = ''; 805 } 806 else if (g_marks.rx_ins.test(types)) { 807 click.a += '-insert'; 808 rpl = ccomma.text(); 809 } 810 else { 811 if (typeof e === 'undefined' || !e) { 812 return; 813 } 814 rpl = $(e).text(); 815 click.r = rpl; 816 } 817 818 matomo_event('comma', 'accept', ccomma.attr('data-types')); 819 log_marking_action(click); 820 commaDo(rpl); 821} 822 823function commaDiscard() { 824 matomo_event('comma', 'discard', ccomma.attr('data-types')); 825 log_marking_action({'a': 'discard', 'm': ccomma.attr('data-types'), 'w': ccomma.text(), 't': ccomma.attr('data-tid')}); 826 827 let types = ccomma.attr('data-types'); 828 if (g_marks.rx_ins.test(types)) { 829 commaDo(''); 830 } 831 else { 832 commaDo(ccomma.text()); 833 } 834} 835 836function commaYellow() { 837 let click = {'a': 'yellow', 'm': ccomma.attr('data-types'), 'w': ccomma.text(), 't': ccomma.attr('data-tid')}; 838 if (ccomma.attr('data-sugs')) { 839 let s = ccomma.attr('data-sugs').split('\t')[0]; 840 if (s === ccomma.text() || s.toUpperCase() === ccomma.text().toUpperCase()) { 841 click.r = s; 842 commaDo(s); 843 } 844 } 845 else { 846 commaDo(ccomma.text()); 847 } 848 matomo_event('comma', 'yellow', ccomma.attr('data-types')); 849 log_marking_action(click); 850} 851 852function commaIgnoreAll() { 853 if (ts_slow) { 854 clearTimeout(ts_slow); 855 } 856 ts_xhr = null; 857 ts_slow = null; 858 g_undos = []; 859 $('#btn-undo').addClass('disabled'); 860 861 let ed = $(tinyMCE.activeEditor.getBody()); 862 ed.find('.popover').remove(); 863 ed.find('span.comma').popover('dispose'); 864 ed.find('span.comma').each(function() { 865 let c = $(this); 866 if (g_marks.rx_ins.test(c.attr('data-types'))) { 867 c.replaceWith(''); 868 } 869 else { 870 c.replaceWith(c.text()); 871 } 872 }); 873 ed.attr('contenteditable', true); 874 $('#btn-correct-all,#btn-wrong-all,#btn-accept-yellows,#btn-close').addClass('disabled'); 875 ccomma = null; 876 $('#working').hide(); 877} 878 879function checkDone() { 880 if (ts_slow) { 881 clearTimeout(ts_slow); 882 } 883 ts_slow = null; 884 885 if (tinyMCE && tinyMCE.activeEditor) { 886 let ed = $(tinyMCE.activeEditor.getBody()); 887 commaIgnoreAll(); 888 ed.find(tnjq).removeAttr('id'); 889 } 890} 891 892function check(which, sel) { 893 if ($('#btn-check').hasClass('disabled')) { 894 return; 895 } 896 897 $('#warning').hide();
898 $('#info').hide(); 899 checkDone(); 900 $('#btn-check').addClass('disabled'); 901 let iframe = $('iframe'); 902 $('#working').show().offset(iframe.offset()).width(iframe.width()).height(iframe.height()).focus().find('span').text(l10n_translate('MSG_WORKING')); 903 904 let editor = $(tinyMCE.activeEditor.getBody()); 905 prepEditorContent(editor); 906 907 last_result = ''; 908 to_send = []; 909 to_send_i = 0; 910 ts_fail = 0; 911 ccomma = null; 912 g_undos = []; 913 $('#btn-undo').addClass('disabled'); 914 915 let nodes = $(findTextNodes(editor.get(0))).closest(tnjq).get(); 916 for (let i=0 ; i<nodes.length ; ++i) { 917 let need = false; 918 let run = []; 919 let wrap = false; 920 for (let j=0 ; j<nodes[i].childNodes.length ; ++j) { 921 if (text_nodes.hasOwnProperty(nodes[i].childNodes[j].nodeName)) { 922 if (run.length && wrap) { 923 //// console.log(run); 924 $(run).wrapAll('<div class="unwrap"></div>'); 925 } 926 need = true; 927 run = []; 928 wrap = false; 929 continue; 930 } 931 if (nodes[i].childNodes[j].nodeType === 1 || (nodes[i].childNodes[j].nodeType === 3 && /\S/.test(nodes[i].childNodes[j].nodeValue))) { 932 //// console.log(nodes[i].childNodes[j]); 933 wrap = true; 934 } 935 run.push(nodes[i].childNodes[j]); 936 } 937 if (run.length && need && wrap) { 938 //// console.log(run); 939 $(run).wrapAll('<div class="unwrap"></div>'); 940 } 941 } 942 943 let ps = []; 944 if (sel) { 945 let tns = findTextNodes(editor.get(0)); 946 let s = findTextNodes(sel.getStart())[0]; 947 let e = findTextNodes(sel.getEnd())[0]; 948 //// console.log([s, e]); 949 let in_s = false; 950 for (let i=0 ; i<tns.length ; ++i) { 951 if (tns[i] == s) { 952 in_s = true; 953 } 954 if (in_s) { 955 ps.push(tns[i]); 956 //// console.log([i, nodes[i]]); 957 } 958 if (tns[i] == e) { 959 break; 960 } 961 } 962 ps = $(ps).closest(tnjq).get(); 963 } 964 else { 965 ps = $(findTextNodes(editor.get(0))).closest(tnjq).get(); 966 } 967 // console.log(ps); 968 969 let doc = []; 970 for (let i=0 ; i<ps.length ; ++i) { 971 let p = $(ps[i]); 972 let ptxt = $.trim(p.html().replace(/<br\/?\s*>/g, '\n').replace(/<[^>]+>/g, '')); 973 if (!ptxt) { 974 continue; 975 } 976 977 let id = i+1; 978 p.attr('id', 's'+id); 979 let par = { 980 i: id, 981 t: decHTML(ptxt), 982 }; 983 doc.push(par); 984 } 985 986 if (!doc.length) { 987 return; 988 } 989 990 checkParagraphs(doc); 991}
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.