help3/xhpeditor/.gitignore | 1 help3/xhpeditor/css/xhpeditor.css | 36 +++++++++++- help3/xhpeditor/index.php | 114 +++++++++++++++++++++++++++++--------- help3/xhpeditor/js/snippets.js | 99 +++++++++++++++++++++++++++++++++ help3/xhpeditor/js/xhp2html.js | 37 ++++++++---- help3/xhpeditor/renderer.php | 19 ------ help3/xhpeditor/views/menu.php | 6 +- 7 files changed, 249 insertions(+), 63 deletions(-)
New commits: commit dd303175ab0882d9e4f41773a1342f51250cb295 Author: Juan José González <juanjose...@libreoffice.org> AuthorDate: Sun Jan 14 11:15:12 2024 -0600 Commit: Olivier Hallot <olivier.hal...@libreoffice.org> CommitDate: Tue Jan 16 20:37:14 2024 +0100 Use the new endpoint to check document, render error messages with JS Change-Id: I8e0a9ccd45270cd10fcd10fdd1d9a6b4745f8f3d Reviewed-on: https://gerrit.libreoffice.org/c/dev-tools/+/162051 Reviewed-by: Olivier Hallot <olivier.hal...@libreoffice.org> Tested-by: Olivier Hallot <olivier.hal...@libreoffice.org> diff --git a/help3/xhpeditor/.gitignore b/help3/xhpeditor/.gitignore new file mode 100644 index 00000000..279f9b57 --- /dev/null +++ b/help3/xhpeditor/.gitignore @@ -0,0 +1 @@ +xmlhelp.dtd diff --git a/help3/xhpeditor/DisplayArea.css b/help3/xhpeditor/css/DisplayArea.css similarity index 100% rename from help3/xhpeditor/DisplayArea.css rename to help3/xhpeditor/css/DisplayArea.css diff --git a/help3/xhpeditor/bootstrap-icons.min.css b/help3/xhpeditor/css/bootstrap-icons.min.css similarity index 100% rename from help3/xhpeditor/bootstrap-icons.min.css rename to help3/xhpeditor/css/bootstrap-icons.min.css diff --git a/help3/xhpeditor/fonts/bootstrap-icons.woff b/help3/xhpeditor/css/fonts/bootstrap-icons.woff similarity index 100% rename from help3/xhpeditor/fonts/bootstrap-icons.woff rename to help3/xhpeditor/css/fonts/bootstrap-icons.woff diff --git a/help3/xhpeditor/fonts/bootstrap-icons.woff2 b/help3/xhpeditor/css/fonts/bootstrap-icons.woff2 similarity index 100% rename from help3/xhpeditor/fonts/bootstrap-icons.woff2 rename to help3/xhpeditor/css/fonts/bootstrap-icons.woff2 diff --git a/help3/xhpeditor/xhpeditor.css b/help3/xhpeditor/css/xhpeditor.css similarity index 89% rename from help3/xhpeditor/xhpeditor.css rename to help3/xhpeditor/css/xhpeditor.css index c4b91e49..e14ea532 100644 --- a/help3/xhpeditor/xhpeditor.css +++ b/help3/xhpeditor/css/xhpeditor.css @@ -133,7 +133,6 @@ ol, ul { position: sticky; top:0px; margin: 0px; - background: AliceBlue; } #renderedpage { @@ -141,7 +140,6 @@ ol, ul { padding: 3px; margin: 0px; overflow-y:auto; - background: AliceBlue; display:inline-block; } .buttonrow{ @@ -253,3 +251,37 @@ div.card div.card-body { border-radius: 0 0 6px 6px; background-color: #fefefe; } + +/* Check mode results */ +div#renderedpage { + padding: 12px; +} + +div#renderedpage div.no-error-message { + padding: 6px; + border-left: 4px solid green; +} + +div#renderedpage div.error-message { + padding: 6px; + border-left: 4px solid gray; + margin-bottom: 12px; +} + +div#renderedpage div.severity-fatal { + border-left-color: red; + background-color: rgba(255,0,0,0.1); +} + +div#renderedpage div.severity-error { + border-left-color: red; +} + +div#renderedpage div.severity-warning { + border-left-color: red; +} + +div#renderedpage span.goto { + cursor: pointer; + color: blue; +} diff --git a/help3/xhpeditor/index.php b/help3/xhpeditor/index.php index c3ac4a97..b72b3dbc 100644 --- a/help3/xhpeditor/index.php +++ b/help3/xhpeditor/index.php @@ -25,16 +25,16 @@ $escaped_xhp_source = htmlspecialchars($xhp, ENT_NOQUOTES); <meta name="description" content="Online editor to create and modify XHP help files for LibreOffice Suite. The XHP editor includes a syntax validator and preview side by side." /> <link rel="icon" href="favicon.ico"/> - <link type="text/css" rel="stylesheet" href="cm/lib/codemirror.css"> - <link type="text/css" rel="stylesheet" href="cm/addon/hint/show-hint.css"> <link type="text/css" rel="stylesheet" href="cm/addon/dialog/dialog.css"> <link type="text/css" rel="stylesheet" href="cm/addon/display/fullscreen.css"> - <link type="text/css" rel="stylesheet" href="xhpeditor.css"> + <link type="text/css" rel="stylesheet" href="cm/addon/hint/show-hint.css"> + <link type="text/css" rel="stylesheet" href="cm/addon/search/matchesonscrollbar.css"> + <link type="text/css" rel="stylesheet" href="cm/lib/codemirror.css"> + <link type="text/css" rel="stylesheet" href="css/DisplayArea.css"> + <link type="text/css" rel="stylesheet" href="css/bootstrap-icons.min.css"> + <link type="text/css" rel="stylesheet" href="css/xhpeditor.css"> <link type="text/css" rel="stylesheet" href="helpcontent2/help3xsl/normalize.css"> <link type="text/css" rel="stylesheet" href="helpcontent2/help3xsl/prism.css"> - <link type="text/css" rel="stylesheet" href="DisplayArea.css"> - <link type="text/css" rel="stylesheet" href="cm/addon/search/matchesonscrollbar.css"> - <link rel="stylesheet" href="bootstrap-icons.min.css"> <script type="application/javascript" src="cm/lib/codemirror.js"></script> <script type="application/javascript" src="cm/mode/xml/xml.js"></script> @@ -51,6 +51,7 @@ $escaped_xhp_source = htmlspecialchars($xhp, ENT_NOQUOTES); <script type="application/javascript" src="cm/addon/search/matchesonscrollbar.js"></script> <script type="application/javascript" src="cm/addon/display/fullscreen.js"></script> <script type="application/javascript" src="cm/addon/scroll/annotatescrollbar.js"></script> + <script type="application/javascript" src="cm/addon/selection/active-line.js"></script> <script type="application/javascript" src="helpcontent2/help3xsl/prism.js"></script> <script type="application/javascript" src="js/autocomplete.js"></script> @@ -59,24 +60,44 @@ $escaped_xhp_source = htmlspecialchars($xhp, ENT_NOQUOTES); <script type="application/javascript" src="js/xhp2html.js" defer></script> <script type="text/javascript"> - const is_render_mode = '<?=isset($_POST["render_page"])?1:0?>'; - const xhpdoc = `<?=$_POST["xhpdoc"]?>`; - - // Trigger this snippet without reloading page - if (is_render_mode == 1) { - fetch('api/xhp_to_xml.php', { - method: 'POST', - headers: { - 'X-Requested-With': 'XMLHttpRequest', - }, - body: JSON.stringify({ - xhpdoc - }) - }).then(response => response.json()) - .then(data => { - document.getElementById('renderedpage').innerHTML = data.preview; - }); + function show_render() { + const is_render_mode = '<?=isset($_POST["render_page"])?1:0?>'; + const xhpdoc = `<?=$_POST["xhpdoc"]?>`; + + // Trigger this snippet without reloading page + if (is_render_mode == 1) { + document.getElementById("render_mode").style.display = "block"; + fetch('api/xhp_to_xml.php', { + method: 'POST', + headers: { + 'X-Requested-With': 'XMLHttpRequest', + }, + body: JSON.stringify({ + xhpdoc + }) + }).then(response => response.json()) + .then(data => { + document.getElementById('renderedpage').innerHTML = data.preview; + }); + } } + + window.EDITOR_MODE = { + EMPTY: 'empty', + RENDER: 'render', + CHECK: 'check', + }; + + function showSection(current_mode) { + document.querySelectorAll('section.preview-section').forEach(s=>s.style.display = 'none'); + document.querySelector(`#${current_mode}-mode`).style.display = 'block'; + document.getElementById('renderedpage').innerHTML = ""; + } + + document.addEventListener("DOMContentLoaded", (event) => { + const current_mode = window.mode || EDITOR_MODE.EMPTY + showSection(current_mode); + }); </script> </head> @@ -87,7 +108,7 @@ $escaped_xhp_source = htmlspecialchars($xhp, ENT_NOQUOTES); <header> <img class="logo" alt="LibreOffice" src="logo.png" /> <h1>LibreOffice Help XHP Editor</h1> - <p> + <p id="editing_filename"> <?=$label_editing?> </p> </header> @@ -102,8 +123,49 @@ $escaped_xhp_source = htmlspecialchars($xhp, ENT_NOQUOTES); </form> </div> - <?php include './renderer.php' ?> + <div id="renderedpageheader"> + <section id="empty-mode" class="preview-section"> + <?php include "views/empty_preview.php" ?> + </section> + <section id="render-mode" class="preview-section"> + <div class="buttonrow"> + <div class="systembuttons"> + System: + <input type="radio" name="sys" id="MAC" onclick="setSystemSpan('MAC')"> + <label for="MAC">MAC</label> + <input type="radio" name="sys" id="WIN" onclick="setSystemSpan('WIN')"> + <label for="WIN">WIN</label> + <input type="radio" name="sys" id="UNIX" onclick="setSystemSpan('UNIX')"> + <label for="UNIX">UNIX</label> + </div> + + <div class="applbuttons"> + Module: + <input type="radio" name="app" onclick="setApplSpan('WRITER')" id="WRITER"> + <label for="WRITER">WRITER</label> + <input type="radio" name="app" onclick="setApplSpan('CALC')" id="CALC"> + <label for="CALC">CALC</label> + <input type="radio" name="app" onclick="setApplSpan('IMPRESS')" id="IMPRESS"> + <label for="IMPRESS">IMPRESS</label> + <input type="radio" name="app" onclick="setApplSpan('DRAW')" id="DRAW"> + <label for="DRAW">DRAW</label> + <input type="radio" name="app" onclick="setApplSpan('BASE')" id="BASE"> + <label for="BASE">BASE</label> + <input type="radio" name="app" onclick="setApplSpan('MATH')" id="MATH"> + <label for="MATH">MATH</label> + <input type="radio" name="app" onclick="setApplSpan('CHART')" id="CHART"> + <label for="CHART">CHART</label> + </div> + </div> + </section> - </body> + <section id="check-mode" class="preview-section"> + <h2>Help File Verification: <?=$xhp_filename?></h2> + </section> + </div> + + <div id="renderedpage"> + </div> +</body> </html> diff --git a/help3/xhpeditor/js/snippets.js b/help3/xhpeditor/js/snippets.js index fd1564b8..f2d4303e 100644 --- a/help3/xhpeditor/js/snippets.js +++ b/help3/xhpeditor/js/snippets.js @@ -246,4 +246,103 @@ function lower(x) { return x.toLowerCase(); } function random(x) {var d = new Date(); return x +'_id'+(Math.floor(Math.random() * 100) + 1) + d.getTime(); } function helpFile() {var d = document.url(); var t = d.search("text/"); return d.substr(t); } +function renderXHP() { + const xhpdoc = window.editor.getValue(); + + window.mode = window.EDITOR_MODE.RENDER; + showSection(window.EDITOR_MODE.RENDER); + + fetch('api/xhp_to_xml.php', { + method: 'POST', + headers: { + 'X-Requested-With': 'XMLHttpRequest', + }, + body: JSON.stringify({ + xhpdoc + }) + }).then( + response => response.json() + ).then(data => { + document.getElementById('renderedpage').innerHTML = data.preview; + }); +} + +function renderCheckErrors(errors) { + const renderedPage = document.getElementById('renderedpage'); + renderedPage.innerHTML = ""; + + const noErrors = (errors.xhp_errors.length + errors.duplicated_ids.length) === 0; + if (noErrors) { + const noErrorsMessage = ` + <div class="no-error-message"> + <p><strong>No errors found</strong></p> + <p>No XML, DTD or duplicated id errors.</p> + </div> + `; + + renderedPage.innerHTML += noErrorsMessage; + return; + } + + const SEVERITY = { + 1: 'warning', + 2: 'error', + 3: 'fatal', + } + if (errors.xhp_errors.length > 0) { + for (const xhpError of errors.xhp_errors) { + console.log(xhpError); + const errorMessage = ` + <div class="error-message severity-${SEVERITY[xhpError.level]}"> + <p><strong>${xhpError.message}</strong></p> + <span>Line: ${xhpError.line}. Column: ${xhpError.column}.</span> + <span class="goto" onclick="goToLine(${xhpError.line}, ${xhpError.column})">Go to line</span> + </div> + `.trim(); + renderedPage.innerHTML += errorMessage; + } + } + if (errors.duplicated_ids.length > 0) { + // render duplicated ids + const duplicatedTitle = document.createElement('p'); + duplicatedTitle.appendChild( + document.createTextNode('Found duplicated id attributes') + ); + renderedPage.appendChild(duplicatedTitle); + + const dupList = document.createElement('ol'); + let dupItem = null; + for (const dupError of errors.duplicated_ids) { + dupItem = document.createElement('li'); + dupItem.appendChild( + document.createTextNode(dupError[0]) + ) + dupList.appendChild(dupItem); + } + + renderedPage.appendChild(dupList); + } +} + +function checkXHP() { + const xhpdoc = window.editor.getValue(); + + window.mode = window.EDITOR_MODE.CHECK; + showSection(window.mode); + + fetch('api/check_xhp.php', { + method: 'POST', + headers: { + 'X-Requested-With': 'XMLHttpRequest', + }, + body: JSON.stringify({ + xhpdoc + }) + }).then( + response => response.json() + ).then(data => { + renderCheckErrors(data.errors); + }); + +} /* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ diff --git a/help3/xhpeditor/js/xhp2html.js b/help3/xhpeditor/js/xhp2html.js index 4ce2886f..27d5076f 100644 --- a/help3/xhpeditor/js/xhp2html.js +++ b/help3/xhpeditor/js/xhp2html.js @@ -12,6 +12,7 @@ // Codemirror configuration var editor = CodeMirror.fromTextArea(document.getElementById("xhpeditor"), { + styleActiveLine: { nonEmpty: true }, lineNumbers: true, theme: "default", mode: "xml", @@ -41,21 +42,23 @@ const height = window.innerHeight - document.getElementById('editorpageheader'). editor.setSize(null, height); function readSingleFile(e) { - var file = e.target.files[0]; + const file = e.target.files[0]; - if (!file) { - return; - } - var reader = new FileReader(); - reader.onload = function(e) { - var contents = e.target.result; - editor.doc.setValue(contents); - }; + if (!file) { + return; + } + + const reader = new FileReader(); + reader.onload = function(e) { + var contents = e.target.result; + document.getElementById('editing_filename').textContent = `Editing ${file.name}`; + editor.doc.setValue(contents); + }; - // Refresh preview when load hasd ended - reader.onloadend = function(e) { - document.getElementById("render_page").click(); - } + // Refresh preview when load hasd ended + reader.onloadend = function(e) { + document.getElementById("render_page").click(); + } reader.readAsText(file); } @@ -90,3 +93,11 @@ function downloadFile(data, filename, type) { }, 0); } } + +function goToLine(line, ch) { + var t = editor.charCoords({line: line-1, ch: 0}, "local").top; + editor.setCursor({line: line-1, ch}); + editor.focus(); + var middleHeight = editor.getScrollerElement().offsetHeight / 2; + editor.scrollTo(null, t - middleHeight - 5); +} diff --git a/help3/xhpeditor/renderer.php b/help3/xhpeditor/renderer.php index 45a63c24..a576d70c 100644 --- a/help3/xhpeditor/renderer.php +++ b/help3/xhpeditor/renderer.php @@ -28,19 +28,6 @@ function display_xml_error($error, $xml) } if (isset($_POST["render_page"])) { - echo '<div id="renderedpageheader">'; - echo '<div class="buttonrow"><div class="systembuttons">System: '; - $opSys = array("MAC", "WIN", "UNIX"); - foreach ($opSys as $value) { - echo '<input type="radio" name="sys" id="' . $value . '" onclick="setSystemSpan(\''.$value.'\')" /><label for="' . $value . '">'.$value.'</label> '; - } - echo '</div><div class="applbuttons">Module: '; - $appModule = array("WRITER", "CALC", "IMPRESS", "DRAW", "BASE", "MATH", "CHART"); - foreach ($appModule as $value){ - echo '<input type="radio" name="app" onclick="setApplSpan(\''.$value.'\')" id="'. $value .'"><label for="'. $value .'">'.$value.'</label> '; - } - echo '</div></div></div><div id="renderedpage">'; - echo "</div>"; }elseif (isset($_POST["check_xhp"])) { libxml_use_internal_errors(true); libxml_clear_errors(); @@ -100,12 +87,6 @@ if (isset($_POST["render_page"])) { } } }elseif (isset($_POST["get_patch"])) { - echo '<div id="renderedpageheader">'; - echo '<h2>Get Patch:</h2>'; - echo "</div>"; } else { - echo '<div id="renderedpageheader">'; - include 'views/empty_preview.php'; - echo "</div>"; }; ?> diff --git a/help3/xhpeditor/views/menu.php b/help3/xhpeditor/views/menu.php index 8725dd19..36e9da15 100644 --- a/help3/xhpeditor/views/menu.php +++ b/help3/xhpeditor/views/menu.php @@ -118,9 +118,9 @@ <div class="dropdown"> <button class="dropbtn">Tools</button> <div class="dropdown-content"> - <input id="render_page" type="submit" form="CMtextarea" name="render_page" value="Render page"/> - <input type="submit" form="CMtextarea" name="get_patch" value="Generate patch"/> - <input id="check_xhp" type="submit" form="CMtextarea" name="check_xhp" value="Check XHP"/> + <a href="#" id="render_page" onclick="renderXHP()">Render Page</a> + <a href="#" id="check_xhp" onclick="checkXHP()" name="check_xhp">Check XHP</a> + <input type="submit" form="CMtextarea" name="get_patch" value="Generate patch" /> <input type="submit" form="CMtextarea" name="open_master" value="Open Master"/> </div> </div>