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 @@