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>&nbsp;';
-    }
-    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>&nbsp;';
-    }
-    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>

Reply via email to