diff --git a/userguide/edit.php b/userguide/edit.php index f92eec5..4219ef7 100644 --- a/userguide/edit.php +++ b/userguide/edit.php @@ -26,6 +26,7 @@ const base_dir = '$base_dir/'; //]]> + EOD; diff --git a/userguide/inc/blocks.php b/userguide/inc/blocks.php index ea318ce..9317162 100644 --- a/userguide/inc/blocks.php +++ b/userguide/inc/blocks.php @@ -132,6 +132,8 @@ function load_doc_with_blocks($doc_id, $lang = null) { // Pass the script path to JavaScript append_js_code($head, $js); + append_js_file($head, $base_url . '/shared/common.js'); + append_js_file($head, $base_url . '/shared/blocks.js'); append_css_file($head, $base_url . '/shared/blocks.css'); if ($lang) { diff --git a/userguide/shared/block_edit_tool.js b/userguide/shared/block_edit_tool.js index aabcc8b..ae7c6a8 100644 --- a/userguide/shared/block_edit_tool.js +++ b/userguide/shared/block_edit_tool.js @@ -1,162 +1,58 @@ -const ping_delay = 60; // in seconds - -const attr_trans_id = '_translation_id'; -const attr_state = '_edit_state'; +const edit_tool = '/shared/edit_tool.html'; +const edit_tool_ctx = 'EditBlock'; var edited_node = null; -var linked_nodes = new Array(); var original_text; -function endEditionEvent(clickOK) { - if (window.edited_node == null) - return; - - var id = window.edited_node.getAttribute(attr_trans_id); - var next_node = null; - - var new_text = edit_window.document.getElementById('modified').value; - - if (clickOK && new_text) { - var not_mark = edit_window.document.getElementById('not_mark').checked; - - source_strings[id] = new_text; - - var xml_http = new XMLHttpRequest(); - - var encoded_text = encodeURI(new_text).replace(/&/g, '%26'); - - xml_http.open('POST', base_url + '/block_edit.php', true); - xml_http.setRequestHeader('Content-Type', - 'application/x-www-form-urlencoded'); - xml_http.addEventListener("load", editSaveFinished); - xml_http.send('edit_doc=' + doc_id + '&edit_string=' + id + - '&edit_text=' + encoded_text + '&dont_mark_fuzzy=' + (not_mark ? '1' : '0')); - - xml_http.userguide_string_id = id; - xml_http.userguide_new_text = new_text; - return; - } else { - window.edited_node.innerHTML = formatText(source_strings[id]); - } +function sendEdition(node, id, new_text, not_mark) { + var xml_http = HTTPRequest('POST', 'block_edit.php', + { + edit_doc: doc_id, + edit_string: id, + edit_text: new_text, + dont_mark_fuzzy: (not_mark ? '1' : '0'), + } , { + load: serverRequestListener, + }); + + xml_http.userguide_string_id = id; + xml_http.userguide_new_text = new_text; + xml_http.userguide_fuzzy = !not_mark; +} - edit_window.close(); - edit_window = null; - window.edited_node = null; +function cancelEdition(node, id) { + node.innerHTML = formatText(source_strings[id]); + node.setAttribute(attr_state, getBlockState(id)); + closeEditWindow(); } -function editSaveFinished() { - edit_window.focus(); +var removeBlock = cancelEdition; - var resp = this.responseText, - id = this.userguide_string_id, - new_text = this.userguide_new_text; - - var send_ok; - if (resp.substring(0, 7) == 'badxml ') - edit_window.alert('The server rejected the translation because of XML ' + - "parsing errors :\n" + this.responseText.substring(3) + - "\n" + 'Check the XML tags used in your translation.'); - else if (resp.substring(0, 6) == 'interr') - edit_window.alert('The original XML code seems corrupt. Please contact ' + - 'an administrator.' + "\n"); - else if (resp.substring(0, 2) != 'ok') - edit_window.alert('There was an error sending the translation. Please ' + - 'retry.' + "\n" + this.responseText); - else - send_ok = true; - - for (var i = 0 ; i < linked_nodes[id].length ; i++) { - linked_nodes[id][i].innerHTML = formatText(new_text); - if (send_ok) { - linked_nodes[id][i].removeAttribute(attr_state); - } else { - linked_nodes[id][i].setAttribute(attr_state, 'error'); - } - } +function editSaveFinished(id, new_text, fuzzy, send_ok) { + edit_window.focus(); if (!send_ok) { - edit_window.focus(); + window.edited_node.setAttribute(attr_state, 'error'); return; } - edit_window.close(); - edit_window = null; - window.edited_node = null; - - // Refresh the statistics - xml_http = new XMLHttpRequest(); - xml_http.open('GET', base_url + '/update_stats.php?doc_id=' + doc_id, true); - xml_http.send(null); -} - -function pingServer() { - var xml_http = new XMLHttpRequest(); - - xml_http.open('GET', base_url + '/lock.php?doc_id=' + doc_id); - xml_http.send(null); - - window.setTimeout(pingServer, ping_delay * 1000); -} - -function mouseClickEvent(e) { - if (window.edited_node != null) { - edit_window.focus(); - return false; - } - - window.edited_node = this; + source_strings[id] = new_text; + new_text = formatText(new_text); + const state = getBlockState(id); - var id = this.getAttribute(attr_trans_id); - - edit_window = window.open(base_url + '/shared/edit_tool.html', - 'Edit Block', 'width=600,height=300,toolbar=0,status=0'); - window.original_text = source_strings[id]; - - return true; -} - -function imgMouseClickEvent(e) { - var src = this.getAttribute("src"); - if (base_local != '.') { - src = base_local + '/' + src; + for (const node of getBlockNodes(id)) { + node.innerHTML = new_text; + node.setAttribute(attr_state, state); } - window.open(base_url + '/res_upload.php?path=' + encodeURIComponent(src), - src, 'width=800,height=600,status=0,toolbar=0,location=0,menubar=0,directories=0,resizable=1,scrollbars=1'); - return true; -} + closeEditWindow(); -function setProperties(node) { - if (node == null) - return; - - if (node.getAttribute) { // Avoid special nodes - if (node.getAttribute(attr_trans_id) != null) { - var id = node.getAttribute(attr_trans_id); - - if (source_strings[id]) { - node.onclick = mouseClickEvent; - - if (linked_nodes[id] == null) { - linked_nodes[id] = [ node ]; - } else { - linked_nodes[id].push(node); - } - } - - return; - } else if (node.tagName.toLowerCase() == "img") { - node.onclick = imgMouseClickEvent; - } - } - - for (var i = 0 ; i < node.childNodes.length ; i++) { - setProperties(node.childNodes[i]); - } + // Refresh the statistics + HTTPRequest('GET', 'update_stats.php', {doc_id: doc_id}); } -function formatText(s) { - return s.replace(/\{LANG_CODE\}/g, 'en'); +function getBlockState(id) { + return ''; } window.onload = function() { @@ -165,17 +61,19 @@ window.onload = function() { if (window.XMLHttpRequest) functions_ok++; - if (encodeURI) + if (Object.entries) functions_ok++; if (functions_ok != 2) { window.alert('Your browser does not support some JavaScript ' + 'functions which are needed for this page to work correctly. ' + - "\nBrowser known to work : Safari 4, Firefox/BeZillaBrowser 2.x, " + "3.x."); + "\nTry again with an updated modern browser."); return; } else { - window.setTimeout(pingServer, ping_delay * 1000); + lockDocument(doc_id); } - setProperties(document.getElementsByTagName('body')[0]); + insertUnreachableBlocks(); + + document.addEventListener('click', clickHandler); } diff --git a/userguide/shared/blocks.js b/userguide/shared/blocks.js new file mode 100644 index 0000000..f8e361f --- /dev/null +++ b/userguide/shared/blocks.js @@ -0,0 +1,128 @@ +const attr_trans_id = '_translation_id'; +const attr_state = '_edit_state'; + +function clickHandler(e) { + var node = e.target; + if (node.nodeName.toLowerCase() == 'img') { + imgClickHandler(node); + } else { + node = node.closest('[' + attr_trans_id + ']'); + if (node != null) { + blockClickHandler(node); + } + } +} + +function imgClickHandler(img) { + var src = img.getAttribute("src"); + if (base_local != '.') { + src = base_local + '/' + src; + } + var lang_param = (typeof lang === 'undefined') ? '' : ('&lang=' + lang); + window.open(base_url + '/res_upload.php?path=' + encodeURIComponent(src) + lang_param, + src, 'width=800,height=600,status=0,toolbar=0,location=0,menubar=0,personalbar=0,resizable=1,scrollbars=1'); +} + +function blockClickHandler(node) { + if (window.edited_node != null) { + edit_window.focus(); + return; + } + + window.edited_node = node; + + var id = node.getAttribute(attr_trans_id); + + edit_window = window.open(base_url + edit_tool, + edit_tool_ctx, 'width=650,height=400,status=0,toolbar=0,location=0,menubar=0,personalbar=0,resizable=1,scrollbars=0'); + window.original_text = source_strings[id]; + if (typeof translated_strings !== 'undefined') { + window.translated_text = translated_strings[id]; + } +} + +function endEditionEvent(clickOK) { + if (window.edited_node == null) + return; + + const node = window.edited_node; + const id = node.getAttribute(attr_trans_id); + + if (clickOK) { + const new_text = edit_window.document.getElementById('modified').value; + if (new_text.trim() == '') { + removeBlock(node, id); + } else { + const fuzzy = + edit_window.document.getElementById('fuzzy_check').checked; + sendEdition(node, id, new_text, fuzzy); + } + } else { + cancelEdition(node, id); + } +} + +function closeEditWindow() { + edit_window.close(); + edit_window = null; + window.edited_node = null; +} + +function serverRequestListener() { + const resp = this.responseText; + var send_ok = false; + + if (resp.substring(0, 7) == 'badxml ') + edit_window.alert('The server rejected the change because of XML ' + + "parsing errors :\n" + resp.substring(3) + + "\n" + 'Check the XML tags.'); + else if (resp.substring(0, 7) == 'diffxml') + edit_window.alert('The server rejected the change because the ' + + 'XML code used in it differs from the original string.' + "\n" + + 'Check the XML tags.'); + else if (resp.substring(0, 6) == 'interr') + edit_window.alert('The original XML code seems corrupt. Please contact ' + + 'an administrator.' + "\n"); + else if (resp.substring(0, 2) != 'ok') + edit_window.alert('There was an error sending the change. Please ' + + 'retry.' + "\n" + resp); + else + send_ok = true; + + editSaveFinished(this.userguide_string_id, this.userguide_new_text, + this.userguide_fuzzy, send_ok); +} + +function getBlockNodes(id, root = document) { + return root.querySelectorAll('[' + attr_trans_id + '="' + id + '"]'); +} + +function getAllBlockNodes(root = document) { + return root.querySelectorAll('[' + attr_trans_id + ']'); +} + +function insertUnreachableBlocks() { + // We can't click some translatable blocks (namely title), so we insert + // them in the document if there is no other copy. + var insertPoint = null; + + const blocks = document.head.querySelectorAll('[' + attr_trans_id + ']'); + for (const block of blocks) { + const id = block.getAttribute(attr_trans_id); + + if (getBlockNodes(id, document.body).length == 0) { + if (insertPoint === null) { + insertPoint = document.querySelector('h1, h2'); + if (insertPoint === null) { + insertPoint = document.body.firstChild; + } + } + + var new_node = document.createElement('h1'); + new_node.innerHTML = block.innerHTML; + new_node.setAttribute(attr_trans_id, id); + new_node.setAttribute('title', 'This is a fake header for content that the tool cannot reach. It won\'t be here in the final document.'); + insertPoint.parentNode.insertBefore(new_node, insertPoint); + } + } +} diff --git a/userguide/shared/common.js b/userguide/shared/common.js new file mode 100644 index 0000000..9c63c77 --- /dev/null +++ b/userguide/shared/common.js @@ -0,0 +1,49 @@ +const ping_delay = 60; // in seconds + +function HTTPRequest(method, endpoint, params = null, listeners = null) { + var xml_http = new XMLHttpRequest(); + var url = base_url + '/' + endpoint; + var body = null; + var paramString = null; + + if (params != null) { + var paramArray = []; + for (const [k,v] of Object.entries(params)) { + paramArray.push(encodeURIComponent(k) + '=' + encodeURIComponent(v)); + } + paramString = paramArray.join('&'); + + if (method == 'GET') { + url = url + '?' + paramString; + } else if (method == 'POST') { + body = paramString; + } + } + + if (listeners != null) { + for (const [k,v] of Object.entries(listeners)) { + xml_http.addEventListener(k, v); + } + } + + xml_http.open(method, url, true); + if (method == 'POST') { + xml_http.setRequestHeader('Content-Type', + 'application/x-www-form-urlencoded'); + } + xml_http.send(body); + + return xml_http; +} + +function lockDocument(doc_id) { + function pingServer() { + HTTPRequest('GET', 'lock.php', {doc_id: doc_id}); + } + + setInterval(pingServer, ping_delay * 1000); +} + +function formatText(s, lang = null) { + return s.replace(/\{LANG_CODE\}/g, lang || window.lang || 'en'); +} diff --git a/userguide/shared/edit_tool.html b/userguide/shared/edit_tool.html index 5e7b8bb..2aa9092 100644 --- a/userguide/shared/edit_tool.html +++ b/userguide/shared/edit_tool.html @@ -40,7 +40,7 @@
diff --git a/userguide/shared/edit_tool.js b/userguide/shared/edit_tool.js index 0c72a2e..cb899f3 100644 --- a/userguide/shared/edit_tool.js +++ b/userguide/shared/edit_tool.js @@ -1,4 +1,3 @@ -const ping_delay = 60; // in seconds const update_delay = 500; // in ms var preview_window = null; @@ -9,15 +8,6 @@ var translated_text; var count = 0; -function pingServer() { - var xml_http = new XMLHttpRequest(); - - xml_http.open('GET', base_url + '/lock.php?doc_id=' + doc_id); - xml_http.send(null); - - window.setTimeout(pingServer, ping_delay * 1000); -} - function keyDownAction() { var e = event; @@ -44,9 +34,8 @@ function updateStatus() { if (!preview_window) return; - preview_window.document.write(editor.value - .replace('', '') - .replace(/\{LANG_CODE\}/g, 'en')); + preview_window.document.write(formatText(editor.value + .replace('', ''))); preview_window.document.close(); } @@ -140,7 +129,7 @@ window.onload = function() { window.alert('Your browser does not support XML HTTP requests. ' + 'You will not be protected against concurrent edits!'); } else { - window.setTimeout(pingServer, ping_delay * 1000); + lockDocument(doc_id); } preview_button = document.getElementById('preview'); diff --git a/userguide/shared/tool.css b/userguide/shared/tool.css index ffa9bbf..2fd0082 100644 --- a/userguide/shared/tool.css +++ b/userguide/shared/tool.css @@ -73,7 +73,7 @@ textarea, input, button { margin-left: 5px; } -#original, #translated, #modified { +#original, #modified { width: 99%; height: 99%; } diff --git a/userguide/shared/translate_tool.html b/userguide/shared/translate_tool.html index 46164bd..f33efe3 100644 --- a/userguide/shared/translate_tool.html +++ b/userguide/shared/translate_tool.html @@ -36,7 +36,7 @@ var last_update_text = ''; function updateEvent() { - var text = document.getElementById('translated').value + var text = document.getElementById('modified').value if (text == last_update_text) { window.setTimeout(5000); @@ -58,7 +58,7 @@ window.opener.original_text); document.getElementById('original').value = window.opener.original_text; - document.getElementById('translated').value = text; + document.getElementById('modified').value = text; document.getElementById('valid_button').value = 'Send ' + window.opener.lang_name + ' Translation'; @@ -69,10 +69,10 @@ pos = selectText(text); if (pos) - document.getElementById('translated').setSelectionRange(pos[0], pos[1]); + document.getElementById('modified').setSelectionRange(pos[0], pos[1]); else { - document.getElementById('translated').focus(); - document.getElementById('translated').select(); + document.getElementById('modified').focus(); + document.getElementById('modified').select(); } window.setTimeout(updateEvent, 500); @@ -106,13 +106,13 @@
Translated text
- +
diff --git a/userguide/shared/translate_tool.js b/userguide/shared/translate_tool.js index d041e28..3f36b61 100644 --- a/userguide/shared/translate_tool.js +++ b/userguide/shared/translate_tool.js @@ -1,100 +1,61 @@ -const attr_trans_id = '_translation_id'; -const attr_state = '_edit_state'; +const edit_tool = '/shared/translate_tool.html'; +const edit_tool_ctx = 'TranslateBlock'; var edit_window = null; var edited_node = null; var original_text; var translated_text; -var linked_nodes = new Array(); var all_nodes = new Array(); -var title_id = 0; -var title_insert = false; -var first_title = null; - -function endEditionEvent(clickOK) { - if (window.edited_node == null) - return; - - var id = window.edited_node.getAttribute(attr_trans_id); - var next_node = null; - - if (clickOK) { - var trans = edit_window.document.getElementById('translated').value; - var mark_fuzzy = edit_window.document.getElementById('mark_fuzzy').checked; - - translated_strings[id] = trans; +function sendEdition(node, id, trans, mark_fuzzy) { + var xml_http = HTTPRequest('POST', 'translate.php', + { + translate_lang: lang, + translate_doc: doc_id, + translate_string: id, + translate_text: trans, + translate_source: source_strings[id], + is_fuzzy: (mark_fuzzy ? '1' : '0'), + } , { + load: serverRequestListener, + }); + + xml_http.userguide_string_id = id; + xml_http.userguide_new_text = trans; + xml_http.userguide_fuzzy = mark_fuzzy; +} - var xml_http = new XMLHttpRequest(); +function cancelEdition(node, id) { + var text = translated_strings[id] == '' ? source_strings[id] : translated_strings[id]; + node.innerHTML = formatText(text); + node.setAttribute(attr_state, getBlockState(id)); + closeEditWindow(); +} - var encoded_source = encodeURI(source_strings[id]).replace(/&/g, '%26').replace(/\+/g, '%2B'); - var encoded_translation = encodeURI(trans).replace(/&/g, '%26').replace(/\+/g, '%2B'); +function removeBlock(node, id) { + // TODO: currently gives an error due to empty text, but could be used + // to remove a translation + sendEdition(node, id, '', false); +} - xml_http.open('POST', base_url + '/translate.php', true); - xml_http.addEventListener("load", translateSaveFinished); - xml_http.setRequestHeader('Content-Type', - 'application/x-www-form-urlencoded'); - xml_http.send('translate_lang=' + lang + '&translate_doc=' + doc_id + - '&translate_string=' + id + '&translate_text=' + encoded_translation + - '&translate_source=' + encoded_source + '&is_fuzzy=' + (mark_fuzzy ? '1' : '0')); +function editSaveFinished(id, trans, fuzzy, send_ok) { + edit_window.focus(); - xml_http.userguide_string_id = id; - xml_http.userguide_trans = trans; - xml_http.userguide_mark_fuzzy = mark_fuzzy; + if (!send_ok) { + window.edited_node.setAttribute(attr_state, 'error'); return; - } else { - window.edited_node.innerHTML = formatText(translated_strings[id]); - if (window.edited_node.innerHTML == '' - || window.edited_node.innerText == '') - window.edited_node.innerHTML = formatText(source_strings[id]); - translateBlockDone(next_node); } -} -function translateSaveFinished() { - edit_window.focus(); - - var resp = this.responseText; - var id = this.userguide_string_id; var next_node; - var send_ok; - if (resp.substring(0, 7) == 'badxml ') - edit_window.alert('The server rejected the translation because of XML ' + - "parsing errors :\n" + this.responseText.substring(3) + - "\n" + 'Check the XML tags used in your translation.'); - else if (resp.substring(0, 7) == 'diffxml') - edit_window.alert('The server rejected the translation because the ' + - 'XML code used in it differs from the original string.' + "\n" + - 'Check the XML tags used in your translation.'); - else if (resp.substring(0, 6) == 'interr') - edit_window.alert('The original XML code seems corrupt. Please contact ' + - 'an administrator.' + "\n"); - else if (resp.substring(0, 2) != 'ok') - edit_window.alert('There was an error sending the translation. Please ' + - 'retry.' + "\n" + this.responseText); - else - send_ok = true; - - is_fuzzy[id] = this.userguide_mark_fuzzy; - - for (var i = 0 ; i < linked_nodes[id].length ; i++) { - linked_nodes[id][i].innerHTML = formatText(this.userguide_trans); - - if (send_ok) { - if (this.userguide_mark_fuzzy) { - linked_nodes[id][i].setAttribute(attr_state, 'fuzzy'); - } else { - linked_nodes[id][i].removeAttribute(attr_state); - } - } else { - linked_nodes[id][i].setAttribute(attr_state, 'error'); - } - } + is_fuzzy[id] = fuzzy; + translated_strings[id] = trans; + trans = formatText(trans); + const state = getBlockState(id); - if (!send_ok) { - edit_window.focus(); - return; + for (const node of getBlockNodes(id)) { + node.innerHTML = trans; + node.setAttribute(attr_state, state); } if (edit_window.document.getElementById('auto_cont').checked) { @@ -120,98 +81,29 @@ function translateBlockDone(next_node) { window.translated_text = translated_strings[id]; window.setTimeout(edit_window.refreshAll, 0); } else { - edit_window.close(); - edit_window = null; - window.edited_node = null; + closeEditWindow(); } } -function mouseClickEvent(e) { - if (window.edited_node != null) { - edit_window.focus(); - return false; +function getBlockState(id) { + if (translated_strings[id] == '') { + return 'untranslated'; + } else if (is_fuzzy[id]) { + return 'fuzzy'; } - - window.edited_node = this; - - var id = this.getAttribute(attr_trans_id); - - edit_window = window.open(base_url + '/shared/translate_tool.html', - 'Edit Translation', 'width=650,height=400,status=0,toolbar=0,location=0,menubar=0,directories=0,resizable=1,scrollbars=0'); - window.original_text = source_strings[id]; - window.translated_text = translated_strings[id]; - - return true; -} - -function imgMouseClickEvent(e) { - var src = this.getAttribute("src"); - if (src != '.') { - src = base_local + '/' + src; - } - window.open(base_url + '/res_upload.php?path=' + encodeURIComponent(src) + '&lang=' + lang, - src, 'width=800,height=600,status=0,toolbar=0,location=0,menubar=0,directories=0,resizable=1,scrollbars=1'); - - return true; + return ''; } -function setProperties(node) { - if (node == null) - return; +function initializeNode(node) { + const id = node.getAttribute(attr_trans_id); + const state = getBlockState(id); - if (node.getAttribute) { // Avoid special nodes - if (node.getAttribute(attr_trans_id) != null) { - var id = node.getAttribute(attr_trans_id); - - if (source_strings[id]) { - var node_name = node.tagName.toLowerCase(); - - if (node_name != "title") { // We can't touch it - if (translated_strings[id] == '') { - node.setAttribute(attr_state, 'untranslated'); - } else if (is_fuzzy[id]) { - node.setAttribute(attr_state, 'fuzzy'); - node.innerHTML = formatText(translated_strings[id]); - } else { - node.innerHTML = formatText(translated_strings[id]); - } - - node.onclick = mouseClickEvent; - - node.setAttribute('_internal_id', all_nodes.length); - all_nodes.push(node); - - if (linked_nodes[id] == null) { - linked_nodes[id] = [ node ]; - } else { - linked_nodes[id].push(node); - } - } - - if (title_id == 0 && node_name == "title") { - title_id = id; - title_insert = true; - } else if (id == title_id) { - title_insert = false; - } - - if (first_title == null && (node_name == "h1" || node_name == "h2")) { - first_title = node; - } - } - return; - } else if (node.tagName.toLowerCase() == "img") { - node.onclick = imgMouseClickEvent; - } - } - - for (var i = 0 ; i < node.childNodes.length ; i++) { - setProperties(node.childNodes[i]); + if (translated_strings[id] != '') { + node.innerHTML = formatText(translated_strings[id]); } -} - -function formatText(s) { - return s.replace(/\{LANG_CODE\}/g, lang); + node.setAttribute(attr_state, state); + node.setAttribute('_internal_id', all_nodes.length); + all_nodes.push(node); } window.onload = function() { @@ -220,32 +112,20 @@ window.onload = function() { if (window.XMLHttpRequest) functions_ok++; - if (encodeURI) + if (Object.entries) functions_ok++; if (functions_ok != 2) { window.alert('Your browser does not support some JavaScript ' + 'functions which are needed for this page to work correctly. ' + - "\nBrowser known to work : Safari 4, Firefox/BeZillaBrowser 2.x, " + "3.x."); + "\nTry again with an updated modern browser."); return; } - setProperties(document.getElementsByTagName('html')[0]); - - if (title_insert) { - // The translatable text used in the tag is not used anywhere else. - // Since the translate tool does not allow translating <title>, we must insert - // this text somewhere. - var new_title = document.createElement("h1"); - new_title.setAttribute(attr_trans_id, title_id); - new_title.appendChild(document.createTextNode(source_strings[title_id])); - - if (first_title != null && first_title.parentNode != null) { - first_title.parentNode.insertBefore(new_title, first_title); - } else { - document.body.insertBefore(new_title, document.body.firstChild); - } - - setProperties(new_title); + insertUnreachableBlocks(); + for (const node of getAllBlockNodes()) { + initializeNode(node); } + + document.addEventListener('click', clickHandler); }