Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/techsneeze/dmarcts-report-viewer.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTechSneeze <dave@techsneeze.com>2021-05-07 08:36:13 +0300
committerGitHub <noreply@github.com>2021-05-07 08:36:13 +0300
commit90dd8f2d23616ade097358341774bfec829d0ffd (patch)
tree26fe6cb53f88832476db3dd88d079a08c489b93e
parentb4032a70557551f221d46ae9e3fa618b436ebe54 (diff)
parent4351444d29dd0d0310f3406fa9a9437c8bfd991d (diff)
Merge pull request #66 from jnew-gh/xml_highlight
XML Highlighting
-rw-r--r--default.css39
-rw-r--r--dmarcts-report-viewer-common.php37
-rw-r--r--dmarcts-report-viewer-report-data.php75
-rw-r--r--dmarcts-report-viewer.js93
-rw-r--r--dmarcts-report-viewer.php8
-rw-r--r--dusk2dawn.css2
-rw-r--r--highlight.js/LICENSE29
-rw-r--r--highlight.js/highlight.pack.js341
-rw-r--r--highlight.js/styles/default.css110
-rw-r--r--highlight.js/styles/dusk2dawn.css16
10 files changed, 717 insertions, 33 deletions
diff --git a/default.css b/default.css
index d6e1ac3..4627e0e 100644
--- a/default.css
+++ b/default.css
@@ -35,6 +35,8 @@ this program. If not, see <http://www.gnu.org/licenses/>.
--yellow: #FFFF00;
--orange: #FFA500;
--red: #FF0000;
+ --xml_highlighted: lightcyan;
+ --xml_pinned: #99ffff;
}
body {
@@ -45,6 +47,11 @@ body {
h1 {
}
+pre {
+ margin: 0px;
+ cursor: pointer;
+}
+
a {
color: var(--link);
}
@@ -200,15 +207,22 @@ table.reportlist td.hidden, table.reportlist th.hidden {
table.reportdata {
margin: 0 auto;
+}
+
+table.reportdata thead {
cursor:pointer;
}
-table.reportdata tr td {
+table.reportdata tr {
color: var(--text_over_colorbg);
text-align: center;
padding: 3px;
}
+table.reportdata th {
+ color: var(--text);
+}
+
table.reportdata tr th {
text-align: center;
padding: 3px;
@@ -218,7 +232,8 @@ table.reportdata tr th {
background-color: var(--header);
}
-table.reportdata td.sum {
+table.reportdata tr.sum {
+ cursor: default;
color: var(--text);
}
@@ -258,6 +273,26 @@ table.reportdata td.yellow {
background-color: var(--yellow);
}
+table.reportdata tr.highlight {
+ background-color: var(--xml_highlighted);
+ color: var(--text)
+}
+
+table.reportdata tr.pinned {
+ background-color: var(--xml_pinned);
+ color: var(--text)
+}
+
+.highlight {
+ background-color: var(--xml_highlighted);
+ color: var(--text)
+}
+
+.pinned {
+ background-color: var(--xml_pinned);
+ color: var(--text)
+}
+
.footer {
font-size: 70%;
text-align: center;
diff --git a/dmarcts-report-viewer-common.php b/dmarcts-report-viewer-common.php
index 47146c3..f554b68 100644
--- a/dmarcts-report-viewer-common.php
+++ b/dmarcts-report-viewer-common.php
@@ -107,22 +107,6 @@ $options = array(
"option_value" => "default.css",
"option_description" => "Name of the css file to be used.<br>The dropdown list is automatically generated from any css files in the main dmarcts-report-viewer directory. The css is immediately applied to this page when selected.",
),
- // This option will be implemented in a future version of dmarcts-reports-viewer.
- // "xml_data_highlight" => array(
- // "option_type" => "radio",
- // "option_label" => "Use Report Data to Raw XML Highlighting",
- // "option_values" => array(1,"On",0,"Off"),
- // "option_value" => "1",
- // "option_description" => "When the raw XML view is open, and when the mouse hovers over, or clicks on, a line of the Report Data table or the Report Data description, highlight the section in the raw XML that corresponds to that row or description. Also works in the opposite direction (i.e. hover/click on a XML record to highlight the corresponding Report Data table line or description). Facilitates determining which XML record corresponds to which line of the table.",
- // ),
- // This option will be implemented in a future version of dmarcts-reports-viewer.
- // "xml_data_hljs" => array(
- // "option_type" => "radio",
- // "option_label" => "Use XML Syntax Highlighting",
- // "option_values" => array(1,"On",0,"Off"),
- // "option_value" => "1",
- // "option_description" => "Use syntax highlighting on the Raw XML. This uses a small external javascript file which may or may not slow down the program.",
- // ),
"option_group_2_heading" => array(
"option_type" => "heading",
"option_label" => "Filters",
@@ -207,6 +191,13 @@ $options = array(
"option_value" => 0,
"option_description" => "Default sort order of Report List column chosen above.",
),
+ "option_group_4_heading" => array(
+ "option_type" => "heading",
+ "option_label" => "XML Data",
+ "option_values" => "",
+ "option_value" => "",
+ "option_description" => "Startup Defaults",
+ ),
"xml_data_open" => array(
"option_type" => "radio",
"option_label" => "Show Report Data XML",
@@ -220,6 +211,20 @@ $options = array(
"option_values" => array("units"=>"percent","min"=>"0","max"=>"100"),
"option_value" => 25,
"option_description" => "Initial width of the Report Data XML window when it is opened, a percentage of the width of the main browser window.",
+ ),
+ "xml_data_highlight" => array(
+ "option_type" => "radio",
+ "option_label" => "Use Report Data to Raw XML Highlighting",
+ "option_values" => array(1,"On",0,"Off"),
+ "option_value" => "1",
+ "option_description" => "When the raw XML view is open, and when the mouse hovers over, or clicks on, a line of the Report Data table or the Report Data description, highlight the section in the raw XML that corresponds to that row or description. Also works in the opposite direction (i.e. hover/click on a XML record to highlight the corresponding Report Data table line or description). Facilitates determining which XML record corresponds to which line of the table.",
+ ),
+ "xml_data_hljs" => array(
+ "option_type" => "radio",
+ "option_label" => "Use XML Syntax Highlighting",
+ "option_values" => array(1,"On",0,"Off"),
+ "option_value" => "1",
+ "option_description" => "Use syntax highlighting on the Raw XML. This uses a small external javascript file which may or may not slow down the program.",
)
// This option will be implemented in a future version of dmarcts-reports-viewer.
// ),
diff --git a/dmarcts-report-viewer-report-data.php b/dmarcts-report-viewer-report-data.php
index 1d5e4da..5c862cd 100644
--- a/dmarcts-report-viewer-report-data.php
+++ b/dmarcts-report-viewer-report-data.php
@@ -55,11 +55,11 @@ function tmpl_reportData($reportnumber, $reports, $host_lookup = 1) {
if (isset($reports[$reportnumber])) {
$row = $reports[$reportnumber];
- $row['raw_xml'] = formatXML($row['raw_xml']);
- $row = array_map('htmlspecialchars', $row);
+
+ $row['raw_xml'] = formatXML($row['raw_xml'], $reportnumber);
$reportdata[] = "<div id='report_desc_container' class='center reportdesc_container'>";
- $reportdata[] = "<div id='report_desc' class='center reportdesc'>Report from ".$row['org']." for ".$row['domain']."<br>(". format_date($row['mindate'], "r" ). " - ".format_date($row['maxdate'], "r" ).")<br> Policies: adkim=" . $row['policy_adkim'] . ", aspf=" . $row['policy_aspf'] . ", p=" . $row['policy_p'] . ", sp=" . $row['policy_sp'] . ", pct=" . $row['policy_pct'] . "</div>";
+ $reportdata[] = "<div id='report_desc' class='center reportdesc' class='hilighted' onmouseover='highlight(this);' onmouseout='unhighlight(this);' onclick='pin(this)'>Report from ".$row['org']." for ".$row['domain']."<br>(". format_date($row['mindate'], "r" ). " - ".format_date($row['maxdate'], "r" ).")<br> Policies: adkim=" . $row['policy_adkim'] . ", aspf=" . $row['policy_aspf'] . ", p=" . $row['policy_p'] . ", sp=" . $row['policy_sp'] . ", pct=" . $row['policy_pct'] . "</div>";
$reportdata[] = "<div style='display:inline-block;margin-left:20px;'><img src='xml.png' id='xml_html_img' width='30px' alt='Show Raw Report XML' title='Show Raw Report XML' onclick='report_data_xml_display_toggle()'></div>";
@@ -70,12 +70,12 @@ function tmpl_reportData($reportnumber, $reports, $host_lookup = 1) {
}
$reportdata[] = "<div id='report_data_xml' style='display:none; float:right; overflow-y:auto; border-left: 2px solid var(--shadow); text-align:left;padding-left: 7px;'>";
- $reportdata[] = "<pre lang=\"xml\">" . $row['raw_xml'] . "</pre>";
+ $reportdata[] = $row['raw_xml'];
$reportdata[] = "</div>";
$reportdata[] = "<div id='report_data_table_div' style='overflow-y:auto;'>";
if ( $cookie_options['dmarc_results_matching_only'] ) {
- $reportdata[] = "\"Show Only Matching Report Data records\" option is \"On\".<br>Report records may not be all displayed.";
+ $reportdata[] = "\"Show Only Matching Report Data records\" option is \"On\".<br><span style='color: var(--red);'>Some report records may not be displayed.</span>";
}
$reportdata[] = "<table id='report_data_table' class='reportdata'>";
$reportdata[] = " <thead>";
@@ -146,7 +146,7 @@ ORDER BY
/* escape html characters after exploring binary values, which will be messed up */
$row = array_map('htmlspecialchars', $row);
- $reportdata[] = " <tr class='" . get_dmarc_result($row)['color'] . "' title='DMARC Result: " . get_dmarc_result($row)['result'] . "'>";
+ $reportdata[] = " <tr id='line" . $row['id'] . "' class='" . get_dmarc_result($row)['color'] . "' title='DMARC Result: " . get_dmarc_result($row)['result'] . "' onmouseover='highlight(this);' onmouseout='unhighlight(this);' onclick='pin(this);'>";
$reportdata[] = " <td>". $ip. "</td>";
if ( $host_lookup ) {
$reportdata[] = " <td>". gethostbyaddr($ip). "</td>";
@@ -168,7 +168,7 @@ ORDER BY
$reportsum += $row['rcount'];
}
$reportdata[] = " </tbody>";
- $reportdata[] = "<tr><td></td><td class='right sum'>Sum:</td><td class='sum'>$reportsum</td><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr>";
+ $reportdata[] = "<tr class='sum'><td></td><td class='right'>Sum:</td><td class='sum'>$reportsum</td><td colspan='9'></td></tr>";
$reportdata[] = "</table>";
$reportdata[] = "</div>";
@@ -179,19 +179,66 @@ ORDER BY
return implode("\n ",$reportdata);
}
-function formatXML($xml) {
+function formatXML($raw_xml, $reportnumber) {
- $dom = new DOMDocument();
+ global $mysqli;
- // Initial block (must before load xml string)
+ $out = "";
+ $html = "";
+
+ $sql = "
+ SELECT
+ MIN(id) AS id_min,
+ MAX(id) AS id_max
+ FROM
+ rptrecord
+ WHERE
+ serial = $reportnumber;
+ ";
+
+ $query = $mysqli->query($sql) or die("Query failed: ".$mysqli->error." (Error #" .$mysqli->errno.")");
+
+ while($row = $query->fetch_assoc()) {
+ $id_min = $row['id_min'];
+ $id_max = $row['id_max'];
+ }
+
+ $dom = new DOMDocument();
$dom->preserveWhiteSpace = false;
$dom->formatOutput = true;
- // End initial block
+ $dom->loadXML($raw_xml);
+
+ // These next few lines adding <?xml version=\"1.0\" encoding=\"UTF-8\" > and <feedback> (as well as the lines adding the closing </feedback> tag) are are very risky because they assume that the first two lines and the last line of the raw_xml are weel-formed
+ // Hopefully not too risky as the raw_xml has already gone through the dmarcts-parser routine that looks for bad XML.
+ // If someone can code a proper way to get those lines, it would be appreciated.
+ $xml_arr = explode(PHP_EOL,$raw_xml);
+ $out = $xml_arr[0] . "\n" . $xml_arr[1];
+ // Should return first 2 lines of xml: <?xml version=\"1.0\" encoding=\"UTF-8\"> and <feedback>
+ $html = "<pre><code class='xml'>" . htmlspecialchars($out) . "</code></pre>";
+
+ $out = $dom->saveXML($dom->getElementsByTagName("report_metadata")[0]);
+ $out = htmlspecialchars($out);
+
+ $html .= "<div id='report_metadata' onmouseover='highlight(this);' onmouseout='unhighlight(this);' onclick='pin(this)'><pre><code class='xml'>" . $out . "</code></pre></div>";
+
+ $records = $dom->getElementsByTagName("record");
+ $i = 0;
+ // $i++;
+ foreach ( $records as $record) {
+ $out = $dom->saveXML($dom->getElementsByTagName("record")[$i]);
+ $out = htmlspecialchars($out);
+ $html .= "<div id='record$id_min' onmouseover='highlight(this);' onmouseout='unhighlight(this);' onclick='pin(this)'><pre><code class='xml'>";
+ $html .= $out;
+ $html .= "</code></pre></div>";
+ $id_min++;
+ $i++;
+ }
- $dom->loadXML($xml);
- $out = $dom->saveXML();
+ $out = $xml_arr[sizeof($xml_arr)-2];
+ $out = htmlspecialchars($out);
+ $html .= "<pre><code class='xml'>" . $out . "</code></pre>";
- return $out;
+ return $html;
}
//####################################################################
diff --git a/dmarcts-report-viewer.js b/dmarcts-report-viewer.js
index 046479f..6c1d52e 100644
--- a/dmarcts-report-viewer.js
+++ b/dmarcts-report-viewer.js
@@ -218,13 +218,22 @@ function report_data_xml_display_toggle() {
if (xml_data_open == 0) {
xml_data_open = 1;
+ cursor('pointer')
set_report_data_widths();
} else {
+ unpin_all();
xml_data_open = 0;
+ cursor('default')
set_report_data_widths();
}
}
+function cursor(type) {
+
+ document.getElementById('report_desc').style.cursor = type;
+ document.getElementById('report_data_table').getElementsByTagName('tbody')[0].style.cursor = type
+}
+
function set_report_data_widths () {
// An allowance to accomodate Report Data table width expanding/contracting when sorting arrow is added to/removed from the column title
@@ -350,6 +359,10 @@ function showReport(str) {
if ( xml_data_open == 1 ) {
set_report_data_widths();
+ cursor('pointer')
+ }
+ if ( xml_data_hljs ) {
+ hljs.highlightAll();
}
}
};
@@ -595,7 +608,9 @@ function build_cookie() {
"sort_column" : sort_column ,
"sort" : sort ,
// "alignment_unknown" : 0 ,
- "dmarc_results_matching_only" : 0
+ "dmarc_results_matching_only" : 0 ,
+ "xml_data_highlight" : xml_data_highlight,
+ "xml_data_hljs" : xml_data_hljs
};
cookie_value = JSON.stringify(cookie_value);
@@ -655,3 +670,79 @@ function cancelOptions() {
}
window.location.href = 'dmarcts-report-viewer.php';
}
+
+// Functions to highlight XML data
+
+function highlight(element) {
+
+ if ( xml_data_open == 1 && xml_data_highlight == 1 ) {
+ element.classList.add('highlight');
+ document.getElementById(other_element(element.id)).classList.add('highlight')
+ }
+}
+
+function unhighlight(element) {
+
+ if ( xml_data_open == 1 && xml_data_highlight == 1 ) {
+ element.classList.remove("highlight");
+ document.getElementById(other_element(element.id)).classList.remove('highlight')
+ }
+}
+
+function pin(element) {
+
+ if ( xml_data_open == 1 && xml_data_highlight == 1 ) {
+ if ( element.className.indexOf('pinned') != -1 ){
+ // Unpins element
+ element.classList.remove('pinned');
+ document.getElementById(other_element(element.id)).classList.remove('pinned');
+ } else {
+ // Pins element
+ unpin_all();
+ element.classList.add('pinned');
+ document.getElementById(other_element(element.id)).classList.add('pinned');
+ if ( element.id.indexOf('record') == 0 ) {
+ document.getElementById(other_element(element.id)).scrollIntoView({ behavior: 'smooth', block: 'center' });
+ } else {
+ document.getElementById(other_element(element.id)).scrollIntoView({ behavior: 'smooth', block: 'start' });
+ }
+ }
+ }
+}
+
+function other_element(str) {
+
+ num = str.replace(/[a-zA-Z]+/g,"");
+ name = str.replace(/[0-9]+/g,"");
+
+ // Special case of first section of xml (report_metadata) and Report Description (report_desc)
+ if ( str == "report_desc" ) {
+ return "report_metadata";
+ }
+ if( str == "report_metadata") {
+ return "report_desc";
+ }
+
+ switch (name) {
+ case "record":
+ other_name = "line";
+ break;
+ case "line":
+ other_name = "record";
+ break;
+ default:
+ other_name = "";
+ }
+
+ return other_name + num;
+}
+
+function unpin_all() {
+
+ pinned = document.getElementsByClassName('pinned')
+ if ( pinned.length != 0 ) {
+ for ( i = 0; i <= pinned.length; i++ ) {
+ pinned[0].classList.remove('pinned');
+ }
+ }
+}
diff --git a/dmarcts-report-viewer.php b/dmarcts-report-viewer.php
index 2f7c6da..2599b0f 100644
--- a/dmarcts-report-viewer.php
+++ b/dmarcts-report-viewer.php
@@ -47,10 +47,18 @@ function html ($domains = array(), $orgs = array(), $periods = array() ) {
$html[] = " <title>DMARC Report Viewer</title>";
$html[] = " <link rel='stylesheet' href='" . $cookie_options['cssfile'] . "'>";
$html[] = " <script src='dmarcts-report-viewer.js'></script>";
+
+ if ( $cookie_options['xml_data_hljs'] ) {
+ $html[] = " <link rel='stylesheet' href='highlight.js/styles/" . $cookie_options['cssfile'] . "'>";
+ $html[] = " <script src='highlight.js/highlight.pack.js'></script>";
+ }
+
$html[] = " <script>";
$html[] = " var report_list_height_percent = " . $cookie_options["report_list_height_percent"] . ";";
$html[] = " var report_data_xml_width_percent = " . $cookie_options["report_data_xml_width_percent"] . ";";
$html[] = " var xml_data_open = " . $cookie_options['xml_data_open'] . ";";
+ $html[] = " var xml_data_highlight = " . $cookie_options['xml_data_highlight'] . ";";
+ $html[] = " var xml_data_hljs = " . $cookie_options['xml_data_hljs'] . ";";
$html[] = " </script>";
$html[] = " <meta charset=\"UTF-8\" />";
$html[] = " <meta name='google' content='notranslate' />";
diff --git a/dusk2dawn.css b/dusk2dawn.css
index 8b20409..87afb77 100644
--- a/dusk2dawn.css
+++ b/dusk2dawn.css
@@ -38,4 +38,6 @@ this program. If not, see <http://www.gnu.org/licenses/>.
--yellow: #ffff64;
--orange: #ffb826;
--red: #ff4444;
+ --xml_highlighted: var(--hover);
+ --xml_pinned: var(--selected);
}
diff --git a/highlight.js/LICENSE b/highlight.js/LICENSE
new file mode 100644
index 0000000..2250cc7
--- /dev/null
+++ b/highlight.js/LICENSE
@@ -0,0 +1,29 @@
+BSD 3-Clause License
+
+Copyright (c) 2006, Ivan Sagalaev.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+* Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+* Neither the name of the copyright holder nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/highlight.js/highlight.pack.js b/highlight.js/highlight.pack.js
new file mode 100644
index 0000000..01a951a
--- /dev/null
+++ b/highlight.js/highlight.pack.js
@@ -0,0 +1,341 @@
+/*
+ Highlight.js 10.7.2 (00233d63)
+ License: BSD-3-Clause
+ Copyright (c) 2006-2021, Ivan Sagalaev
+ See https://github.com/highlightjs/highlight.js
+*/
+var hljs=function(){"use strict";function e(t){
+return t instanceof Map?t.clear=t.delete=t.set=()=>{
+throw Error("map is read-only")}:t instanceof Set&&(t.add=t.clear=t.delete=()=>{
+throw Error("set is read-only")
+}),Object.freeze(t),Object.getOwnPropertyNames(t).forEach((n=>{var i=t[n]
+;"object"!=typeof i||Object.isFrozen(i)||e(i)})),t}var t=e,n=e;t.default=n
+;class i{constructor(e){
+void 0===e.data&&(e.data={}),this.data=e.data,this.isMatchIgnored=!1}
+ignoreMatch(){this.isMatchIgnored=!0}}function s(e){
+return e.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&#x27;")
+}function a(e,...t){const n=Object.create(null);for(const t in e)n[t]=e[t]
+;return t.forEach((e=>{for(const t in e)n[t]=e[t]})),n}const r=e=>!!e.kind
+;class l{constructor(e,t){
+this.buffer="",this.classPrefix=t.classPrefix,e.walk(this)}addText(e){
+this.buffer+=s(e)}openNode(e){if(!r(e))return;let t=e.kind
+;e.sublanguage||(t=`${this.classPrefix}${t}`),this.span(t)}closeNode(e){
+r(e)&&(this.buffer+="</span>")}value(){return this.buffer}span(e){
+this.buffer+=`<span class="${e}">`}}class o{constructor(){this.rootNode={
+children:[]},this.stack=[this.rootNode]}get top(){
+return this.stack[this.stack.length-1]}get root(){return this.rootNode}add(e){
+this.top.children.push(e)}openNode(e){const t={kind:e,children:[]}
+;this.add(t),this.stack.push(t)}closeNode(){
+if(this.stack.length>1)return this.stack.pop()}closeAllNodes(){
+for(;this.closeNode(););}toJSON(){return JSON.stringify(this.rootNode,null,4)}
+walk(e){return this.constructor._walk(e,this.rootNode)}static _walk(e,t){
+return"string"==typeof t?e.addText(t):t.children&&(e.openNode(t),
+t.children.forEach((t=>this._walk(e,t))),e.closeNode(t)),e}static _collapse(e){
+"string"!=typeof e&&e.children&&(e.children.every((e=>"string"==typeof e))?e.children=[e.children.join("")]:e.children.forEach((e=>{
+o._collapse(e)})))}}class c extends o{constructor(e){super(),this.options=e}
+addKeyword(e,t){""!==e&&(this.openNode(t),this.addText(e),this.closeNode())}
+addText(e){""!==e&&this.add(e)}addSublanguage(e,t){const n=e.root
+;n.kind=t,n.sublanguage=!0,this.add(n)}toHTML(){
+return new l(this,this.options).value()}finalize(){return!0}}function g(e){
+return e?"string"==typeof e?e:e.source:null}
+const u=/\[(?:[^\\\]]|\\.)*\]|\(\??|\\([1-9][0-9]*)|\\./,h="[a-zA-Z]\\w*",d="[a-zA-Z_]\\w*",f="\\b\\d+(\\.\\d+)?",p="(-?)(\\b0[xX][a-fA-F0-9]+|(\\b\\d+(\\.\\d*)?|\\.\\d+)([eE][-+]?\\d+)?)",m="\\b(0b[01]+)",b={
+begin:"\\\\[\\s\\S]",relevance:0},E={className:"string",begin:"'",end:"'",
+illegal:"\\n",contains:[b]},x={className:"string",begin:'"',end:'"',
+illegal:"\\n",contains:[b]},v={
+begin:/\b(a|an|the|are|I'm|isn't|don't|doesn't|won't|but|just|should|pretty|simply|enough|gonna|going|wtf|so|such|will|you|your|they|like|more)\b/
+},w=(e,t,n={})=>{const i=a({className:"comment",begin:e,end:t,contains:[]},n)
+;return i.contains.push(v),i.contains.push({className:"doctag",
+begin:"(?:TODO|FIXME|NOTE|BUG|OPTIMIZE|HACK|XXX):",relevance:0}),i
+},y=w("//","$"),N=w("/\\*","\\*/"),R=w("#","$");var _=Object.freeze({
+__proto__:null,MATCH_NOTHING_RE:/\b\B/,IDENT_RE:h,UNDERSCORE_IDENT_RE:d,
+NUMBER_RE:f,C_NUMBER_RE:p,BINARY_NUMBER_RE:m,
+RE_STARTERS_RE:"!|!=|!==|%|%=|&|&&|&=|\\*|\\*=|\\+|\\+=|,|-|-=|/=|/|:|;|<<|<<=|<=|<|===|==|=|>>>=|>>=|>=|>>>|>>|>|\\?|\\[|\\{|\\(|\\^|\\^=|\\||\\|=|\\|\\||~",
+SHEBANG:(e={})=>{const t=/^#![ ]*\//
+;return e.binary&&(e.begin=((...e)=>e.map((e=>g(e))).join(""))(t,/.*\b/,e.binary,/\b.*/)),
+a({className:"meta",begin:t,end:/$/,relevance:0,"on:begin":(e,t)=>{
+0!==e.index&&t.ignoreMatch()}},e)},BACKSLASH_ESCAPE:b,APOS_STRING_MODE:E,
+QUOTE_STRING_MODE:x,PHRASAL_WORDS_MODE:v,COMMENT:w,C_LINE_COMMENT_MODE:y,
+C_BLOCK_COMMENT_MODE:N,HASH_COMMENT_MODE:R,NUMBER_MODE:{className:"number",
+begin:f,relevance:0},C_NUMBER_MODE:{className:"number",begin:p,relevance:0},
+BINARY_NUMBER_MODE:{className:"number",begin:m,relevance:0},CSS_NUMBER_MODE:{
+className:"number",
+begin:f+"(%|em|ex|ch|rem|vw|vh|vmin|vmax|cm|mm|in|pt|pc|px|deg|grad|rad|turn|s|ms|Hz|kHz|dpi|dpcm|dppx)?",
+relevance:0},REGEXP_MODE:{begin:/(?=\/[^/\n]*\/)/,contains:[{className:"regexp",
+begin:/\//,end:/\/[gimuy]*/,illegal:/\n/,contains:[b,{begin:/\[/,end:/\]/,
+relevance:0,contains:[b]}]}]},TITLE_MODE:{className:"title",begin:h,relevance:0
+},UNDERSCORE_TITLE_MODE:{className:"title",begin:d,relevance:0},METHOD_GUARD:{
+begin:"\\.\\s*[a-zA-Z_]\\w*",relevance:0},END_SAME_AS_BEGIN:e=>Object.assign(e,{
+"on:begin":(e,t)=>{t.data._beginMatch=e[1]},"on:end":(e,t)=>{
+t.data._beginMatch!==e[1]&&t.ignoreMatch()}})});function k(e,t){
+"."===e.input[e.index-1]&&t.ignoreMatch()}function M(e,t){
+t&&e.beginKeywords&&(e.begin="\\b("+e.beginKeywords.split(" ").join("|")+")(?!\\.)(?=\\b|\\s)",
+e.__beforeBegin=k,e.keywords=e.keywords||e.beginKeywords,delete e.beginKeywords,
+void 0===e.relevance&&(e.relevance=0))}function O(e,t){
+Array.isArray(e.illegal)&&(e.illegal=((...e)=>"("+e.map((e=>g(e))).join("|")+")")(...e.illegal))
+}function A(e,t){if(e.match){
+if(e.begin||e.end)throw Error("begin & end are not supported with match")
+;e.begin=e.match,delete e.match}}function L(e,t){
+void 0===e.relevance&&(e.relevance=1)}
+const I=["of","and","for","in","not","or","if","then","parent","list","value"]
+;function j(e,t,n="keyword"){const i={}
+;return"string"==typeof e?s(n,e.split(" ")):Array.isArray(e)?s(n,e):Object.keys(e).forEach((n=>{
+Object.assign(i,j(e[n],t,n))})),i;function s(e,n){
+t&&(n=n.map((e=>e.toLowerCase()))),n.forEach((t=>{const n=t.split("|")
+;i[n[0]]=[e,B(n[0],n[1])]}))}}function B(e,t){
+return t?Number(t):(e=>I.includes(e.toLowerCase()))(e)?0:1}
+function T(e,{plugins:t}){function n(t,n){
+return RegExp(g(t),"m"+(e.case_insensitive?"i":"")+(n?"g":""))}class i{
+constructor(){
+this.matchIndexes={},this.regexes=[],this.matchAt=1,this.position=0}
+addRule(e,t){
+t.position=this.position++,this.matchIndexes[this.matchAt]=t,this.regexes.push([t,e]),
+this.matchAt+=(e=>RegExp(e.toString()+"|").exec("").length-1)(e)+1}compile(){
+0===this.regexes.length&&(this.exec=()=>null)
+;const e=this.regexes.map((e=>e[1]));this.matcherRe=n(((e,t="|")=>{let n=0
+;return e.map((e=>{n+=1;const t=n;let i=g(e),s="";for(;i.length>0;){
+const e=u.exec(i);if(!e){s+=i;break}
+s+=i.substring(0,e.index),i=i.substring(e.index+e[0].length),
+"\\"===e[0][0]&&e[1]?s+="\\"+(Number(e[1])+t):(s+=e[0],"("===e[0]&&n++)}return s
+})).map((e=>`(${e})`)).join(t)})(e),!0),this.lastIndex=0}exec(e){
+this.matcherRe.lastIndex=this.lastIndex;const t=this.matcherRe.exec(e)
+;if(!t)return null
+;const n=t.findIndex(((e,t)=>t>0&&void 0!==e)),i=this.matchIndexes[n]
+;return t.splice(0,n),Object.assign(t,i)}}class s{constructor(){
+this.rules=[],this.multiRegexes=[],
+this.count=0,this.lastIndex=0,this.regexIndex=0}getMatcher(e){
+if(this.multiRegexes[e])return this.multiRegexes[e];const t=new i
+;return this.rules.slice(e).forEach((([e,n])=>t.addRule(e,n))),
+t.compile(),this.multiRegexes[e]=t,t}resumingScanAtSamePosition(){
+return 0!==this.regexIndex}considerAll(){this.regexIndex=0}addRule(e,t){
+this.rules.push([e,t]),"begin"===t.type&&this.count++}exec(e){
+const t=this.getMatcher(this.regexIndex);t.lastIndex=this.lastIndex
+;let n=t.exec(e)
+;if(this.resumingScanAtSamePosition())if(n&&n.index===this.lastIndex);else{
+const t=this.getMatcher(0);t.lastIndex=this.lastIndex+1,n=t.exec(e)}
+return n&&(this.regexIndex+=n.position+1,
+this.regexIndex===this.count&&this.considerAll()),n}}
+if(e.compilerExtensions||(e.compilerExtensions=[]),
+e.contains&&e.contains.includes("self"))throw Error("ERR: contains `self` is not supported at the top-level of a language. See documentation.")
+;return e.classNameAliases=a(e.classNameAliases||{}),function t(i,r){const l=i
+;if(i.isCompiled)return l
+;[A].forEach((e=>e(i,r))),e.compilerExtensions.forEach((e=>e(i,r))),
+i.__beforeBegin=null,[M,O,L].forEach((e=>e(i,r))),i.isCompiled=!0;let o=null
+;if("object"==typeof i.keywords&&(o=i.keywords.$pattern,
+delete i.keywords.$pattern),
+i.keywords&&(i.keywords=j(i.keywords,e.case_insensitive)),
+i.lexemes&&o)throw Error("ERR: Prefer `keywords.$pattern` to `mode.lexemes`, BOTH are not allowed. (see mode reference) ")
+;return o=o||i.lexemes||/\w+/,
+l.keywordPatternRe=n(o,!0),r&&(i.begin||(i.begin=/\B|\b/),
+l.beginRe=n(i.begin),i.endSameAsBegin&&(i.end=i.begin),
+i.end||i.endsWithParent||(i.end=/\B|\b/),
+i.end&&(l.endRe=n(i.end)),l.terminatorEnd=g(i.end)||"",
+i.endsWithParent&&r.terminatorEnd&&(l.terminatorEnd+=(i.end?"|":"")+r.terminatorEnd)),
+i.illegal&&(l.illegalRe=n(i.illegal)),
+i.contains||(i.contains=[]),i.contains=[].concat(...i.contains.map((e=>(e=>(e.variants&&!e.cachedVariants&&(e.cachedVariants=e.variants.map((t=>a(e,{
+variants:null},t)))),e.cachedVariants?e.cachedVariants:S(e)?a(e,{
+starts:e.starts?a(e.starts):null
+}):Object.isFrozen(e)?a(e):e))("self"===e?i:e)))),i.contains.forEach((e=>{t(e,l)
+})),i.starts&&t(i.starts,r),l.matcher=(e=>{const t=new s
+;return e.contains.forEach((e=>t.addRule(e.begin,{rule:e,type:"begin"
+}))),e.terminatorEnd&&t.addRule(e.terminatorEnd,{type:"end"
+}),e.illegal&&t.addRule(e.illegal,{type:"illegal"}),t})(l),l}(e)}function S(e){
+return!!e&&(e.endsWithParent||S(e.starts))}function P(e){const t={
+props:["language","code","autodetect"],data:()=>({detectedLanguage:"",
+unknownLanguage:!1}),computed:{className(){
+return this.unknownLanguage?"":"hljs "+this.detectedLanguage},highlighted(){
+if(!this.autoDetect&&!e.getLanguage(this.language))return console.warn(`The language "${this.language}" you specified could not be found.`),
+this.unknownLanguage=!0,s(this.code);let t={}
+;return this.autoDetect?(t=e.highlightAuto(this.code),
+this.detectedLanguage=t.language):(t=e.highlight(this.language,this.code,this.ignoreIllegals),
+this.detectedLanguage=this.language),t.value},autoDetect(){
+return!(this.language&&(e=this.autodetect,!e&&""!==e));var e},
+ignoreIllegals:()=>!0},render(e){return e("pre",{},[e("code",{
+class:this.className,domProps:{innerHTML:this.highlighted}})])}};return{
+Component:t,VuePlugin:{install(e){e.component("highlightjs",t)}}}}const D={
+"after:highlightElement":({el:e,result:t,text:n})=>{const i=H(e)
+;if(!i.length)return;const a=document.createElement("div")
+;a.innerHTML=t.value,t.value=((e,t,n)=>{let i=0,a="";const r=[];function l(){
+return e.length&&t.length?e[0].offset!==t[0].offset?e[0].offset<t[0].offset?e:t:"start"===t[0].event?e:t:e.length?e:t
+}function o(e){a+="<"+C(e)+[].map.call(e.attributes,(function(e){
+return" "+e.nodeName+'="'+s(e.value)+'"'})).join("")+">"}function c(e){
+a+="</"+C(e)+">"}function g(e){("start"===e.event?o:c)(e.node)}
+for(;e.length||t.length;){let t=l()
+;if(a+=s(n.substring(i,t[0].offset)),i=t[0].offset,t===e){r.reverse().forEach(c)
+;do{g(t.splice(0,1)[0]),t=l()}while(t===e&&t.length&&t[0].offset===i)
+;r.reverse().forEach(o)
+}else"start"===t[0].event?r.push(t[0].node):r.pop(),g(t.splice(0,1)[0])}
+return a+s(n.substr(i))})(i,H(a),n)}};function C(e){
+return e.nodeName.toLowerCase()}function H(e){const t=[];return function e(n,i){
+for(let s=n.firstChild;s;s=s.nextSibling)3===s.nodeType?i+=s.nodeValue.length:1===s.nodeType&&(t.push({
+event:"start",offset:i,node:s}),i=e(s,i),C(s).match(/br|hr|img|input/)||t.push({
+event:"stop",offset:i,node:s}));return i}(e,0),t}const $={},U=e=>{
+console.error(e)},z=(e,...t)=>{console.log("WARN: "+e,...t)},K=(e,t)=>{
+$[`${e}/${t}`]||(console.log(`Deprecated as of ${e}. ${t}`),$[`${e}/${t}`]=!0)
+},G=s,V=a,W=Symbol("nomatch");return(e=>{
+const n=Object.create(null),s=Object.create(null),a=[];let r=!0
+;const l=/(^(<[^>]+>|\t|)+|\n)/gm,o="Could not find the language '{}', did you forget to load/include a language module?",g={
+disableAutodetect:!0,name:"Plain text",contains:[]};let u={
+noHighlightRe:/^(no-?highlight)$/i,
+languageDetectRe:/\blang(?:uage)?-([\w-]+)\b/i,classPrefix:"hljs-",
+tabReplace:null,useBR:!1,languages:null,__emitter:c};function h(e){
+return u.noHighlightRe.test(e)}function d(e,t,n,i){let s="",a=""
+;"object"==typeof t?(s=e,
+n=t.ignoreIllegals,a=t.language,i=void 0):(K("10.7.0","highlight(lang, code, ...args) has been deprecated."),
+K("10.7.0","Please use highlight(code, options) instead.\nhttps://github.com/highlightjs/highlight.js/issues/2277"),
+a=e,s=t);const r={code:s,language:a};M("before:highlight",r)
+;const l=r.result?r.result:f(r.language,r.code,n,i)
+;return l.code=r.code,M("after:highlight",l),l}function f(e,t,s,l){
+function c(e,t){const n=v.case_insensitive?t[0].toLowerCase():t[0]
+;return Object.prototype.hasOwnProperty.call(e.keywords,n)&&e.keywords[n]}
+function g(){null!=R.subLanguage?(()=>{if(""===M)return;let e=null
+;if("string"==typeof R.subLanguage){
+if(!n[R.subLanguage])return void k.addText(M)
+;e=f(R.subLanguage,M,!0,_[R.subLanguage]),_[R.subLanguage]=e.top
+}else e=p(M,R.subLanguage.length?R.subLanguage:null)
+;R.relevance>0&&(O+=e.relevance),k.addSublanguage(e.emitter,e.language)
+})():(()=>{if(!R.keywords)return void k.addText(M);let e=0
+;R.keywordPatternRe.lastIndex=0;let t=R.keywordPatternRe.exec(M),n="";for(;t;){
+n+=M.substring(e,t.index);const i=c(R,t);if(i){const[e,s]=i
+;if(k.addText(n),n="",O+=s,e.startsWith("_"))n+=t[0];else{
+const n=v.classNameAliases[e]||e;k.addKeyword(t[0],n)}}else n+=t[0]
+;e=R.keywordPatternRe.lastIndex,t=R.keywordPatternRe.exec(M)}
+n+=M.substr(e),k.addText(n)})(),M=""}function h(e){
+return e.className&&k.openNode(v.classNameAliases[e.className]||e.className),
+R=Object.create(e,{parent:{value:R}}),R}function d(e,t,n){let s=((e,t)=>{
+const n=e&&e.exec(t);return n&&0===n.index})(e.endRe,n);if(s){if(e["on:end"]){
+const n=new i(e);e["on:end"](t,n),n.isMatchIgnored&&(s=!1)}if(s){
+for(;e.endsParent&&e.parent;)e=e.parent;return e}}
+if(e.endsWithParent)return d(e.parent,t,n)}function m(e){
+return 0===R.matcher.regexIndex?(M+=e[0],1):(I=!0,0)}function b(e){
+const n=e[0],i=t.substr(e.index),s=d(R,e,i);if(!s)return W;const a=R
+;a.skip?M+=n:(a.returnEnd||a.excludeEnd||(M+=n),g(),a.excludeEnd&&(M=n));do{
+R.className&&k.closeNode(),R.skip||R.subLanguage||(O+=R.relevance),R=R.parent
+}while(R!==s.parent)
+;return s.starts&&(s.endSameAsBegin&&(s.starts.endRe=s.endRe),
+h(s.starts)),a.returnEnd?0:n.length}let E={};function x(n,a){const l=a&&a[0]
+;if(M+=n,null==l)return g(),0
+;if("begin"===E.type&&"end"===a.type&&E.index===a.index&&""===l){
+if(M+=t.slice(a.index,a.index+1),!r){const t=Error("0 width match regex")
+;throw t.languageName=e,t.badRule=E.rule,t}return 1}
+if(E=a,"begin"===a.type)return function(e){
+const t=e[0],n=e.rule,s=new i(n),a=[n.__beforeBegin,n["on:begin"]]
+;for(const n of a)if(n&&(n(e,s),s.isMatchIgnored))return m(t)
+;return n&&n.endSameAsBegin&&(n.endRe=RegExp(t.replace(/[-/\\^$*+?.()|[\]{}]/g,"\\$&"),"m")),
+n.skip?M+=t:(n.excludeBegin&&(M+=t),
+g(),n.returnBegin||n.excludeBegin||(M=t)),h(n),n.returnBegin?0:t.length}(a)
+;if("illegal"===a.type&&!s){
+const e=Error('Illegal lexeme "'+l+'" for mode "'+(R.className||"<unnamed>")+'"')
+;throw e.mode=R,e}if("end"===a.type){const e=b(a);if(e!==W)return e}
+if("illegal"===a.type&&""===l)return 1
+;if(L>1e5&&L>3*a.index)throw Error("potential infinite loop, way more iterations than matches")
+;return M+=l,l.length}const v=N(e)
+;if(!v)throw U(o.replace("{}",e)),Error('Unknown language: "'+e+'"')
+;const w=T(v,{plugins:a});let y="",R=l||w;const _={},k=new u.__emitter(u);(()=>{
+const e=[];for(let t=R;t!==v;t=t.parent)t.className&&e.unshift(t.className)
+;e.forEach((e=>k.openNode(e)))})();let M="",O=0,A=0,L=0,I=!1;try{
+for(R.matcher.considerAll();;){
+L++,I?I=!1:R.matcher.considerAll(),R.matcher.lastIndex=A
+;const e=R.matcher.exec(t);if(!e)break;const n=x(t.substring(A,e.index),e)
+;A=e.index+n}return x(t.substr(A)),k.closeAllNodes(),k.finalize(),y=k.toHTML(),{
+relevance:Math.floor(O),value:y,language:e,illegal:!1,emitter:k,top:R}}catch(n){
+if(n.message&&n.message.includes("Illegal"))return{illegal:!0,illegalBy:{
+msg:n.message,context:t.slice(A-100,A+100),mode:n.mode},sofar:y,relevance:0,
+value:G(t),emitter:k};if(r)return{illegal:!1,relevance:0,value:G(t),emitter:k,
+language:e,top:R,errorRaised:n};throw n}}function p(e,t){
+t=t||u.languages||Object.keys(n);const i=(e=>{const t={relevance:0,
+emitter:new u.__emitter(u),value:G(e),illegal:!1,top:g}
+;return t.emitter.addText(e),t})(e),s=t.filter(N).filter(k).map((t=>f(t,e,!1)))
+;s.unshift(i);const a=s.sort(((e,t)=>{
+if(e.relevance!==t.relevance)return t.relevance-e.relevance
+;if(e.language&&t.language){if(N(e.language).supersetOf===t.language)return 1
+;if(N(t.language).supersetOf===e.language)return-1}return 0})),[r,l]=a,o=r
+;return o.second_best=l,o}const m={"before:highlightElement":({el:e})=>{
+u.useBR&&(e.innerHTML=e.innerHTML.replace(/\n/g,"").replace(/<br[ /]*>/g,"\n"))
+},"after:highlightElement":({result:e})=>{
+u.useBR&&(e.value=e.value.replace(/\n/g,"<br>"))}},b=/^(<[^>]+>|\t)+/gm,E={
+"after:highlightElement":({result:e})=>{
+u.tabReplace&&(e.value=e.value.replace(b,(e=>e.replace(/\t/g,u.tabReplace))))}}
+;function x(e){let t=null;const n=(e=>{let t=e.className+" "
+;t+=e.parentNode?e.parentNode.className:"";const n=u.languageDetectRe.exec(t)
+;if(n){const t=N(n[1])
+;return t||(z(o.replace("{}",n[1])),z("Falling back to no-highlight mode for this block.",e)),
+t?n[1]:"no-highlight"}return t.split(/\s+/).find((e=>h(e)||N(e)))})(e)
+;if(h(n))return;M("before:highlightElement",{el:e,language:n}),t=e
+;const i=t.textContent,a=n?d(i,{language:n,ignoreIllegals:!0}):p(i)
+;M("after:highlightElement",{el:e,result:a,text:i
+}),e.innerHTML=a.value,((e,t,n)=>{const i=t?s[t]:n
+;e.classList.add("hljs"),i&&e.classList.add(i)})(e,n,a.language),e.result={
+language:a.language,re:a.relevance,relavance:a.relevance
+},a.second_best&&(e.second_best={language:a.second_best.language,
+re:a.second_best.relevance,relavance:a.second_best.relevance})}const v=()=>{
+v.called||(v.called=!0,
+K("10.6.0","initHighlighting() is deprecated. Use highlightAll() instead."),
+document.querySelectorAll("pre code").forEach(x))};let w=!1;function y(){
+"loading"!==document.readyState?document.querySelectorAll("pre code").forEach(x):w=!0
+}function N(e){return e=(e||"").toLowerCase(),n[e]||n[s[e]]}
+function R(e,{languageName:t}){"string"==typeof e&&(e=[e]),e.forEach((e=>{
+s[e.toLowerCase()]=t}))}function k(e){const t=N(e)
+;return t&&!t.disableAutodetect}function M(e,t){const n=e;a.forEach((e=>{
+e[n]&&e[n](t)}))}
+"undefined"!=typeof window&&window.addEventListener&&window.addEventListener("DOMContentLoaded",(()=>{
+w&&y()}),!1),Object.assign(e,{highlight:d,highlightAuto:p,highlightAll:y,
+fixMarkup:e=>{
+return K("10.2.0","fixMarkup will be removed entirely in v11.0"),K("10.2.0","Please see https://github.com/highlightjs/highlight.js/issues/2534"),
+t=e,
+u.tabReplace||u.useBR?t.replace(l,(e=>"\n"===e?u.useBR?"<br>":e:u.tabReplace?e.replace(/\t/g,u.tabReplace):e)):t
+;var t},highlightElement:x,
+highlightBlock:e=>(K("10.7.0","highlightBlock will be removed entirely in v12.0"),
+K("10.7.0","Please use highlightElement now."),x(e)),configure:e=>{
+e.useBR&&(K("10.3.0","'useBR' will be removed entirely in v11.0"),
+K("10.3.0","Please see https://github.com/highlightjs/highlight.js/issues/2559")),
+u=V(u,e)},initHighlighting:v,initHighlightingOnLoad:()=>{
+K("10.6.0","initHighlightingOnLoad() is deprecated. Use highlightAll() instead."),
+w=!0},registerLanguage:(t,i)=>{let s=null;try{s=i(e)}catch(e){
+if(U("Language definition for '{}' could not be registered.".replace("{}",t)),
+!r)throw e;U(e),s=g}
+s.name||(s.name=t),n[t]=s,s.rawDefinition=i.bind(null,e),s.aliases&&R(s.aliases,{
+languageName:t})},unregisterLanguage:e=>{delete n[e]
+;for(const t of Object.keys(s))s[t]===e&&delete s[t]},
+listLanguages:()=>Object.keys(n),getLanguage:N,registerAliases:R,
+requireLanguage:e=>{
+K("10.4.0","requireLanguage will be removed entirely in v11."),
+K("10.4.0","Please see https://github.com/highlightjs/highlight.js/pull/2844")
+;const t=N(e);if(t)return t
+;throw Error("The '{}' language is required, but not loaded.".replace("{}",e))},
+autoDetection:k,inherit:V,addPlugin:e=>{(e=>{
+e["before:highlightBlock"]&&!e["before:highlightElement"]&&(e["before:highlightElement"]=t=>{
+e["before:highlightBlock"](Object.assign({block:t.el},t))
+}),e["after:highlightBlock"]&&!e["after:highlightElement"]&&(e["after:highlightElement"]=t=>{
+e["after:highlightBlock"](Object.assign({block:t.el},t))})})(e),a.push(e)},
+vuePlugin:P(e).VuePlugin}),e.debugMode=()=>{r=!1},e.safeMode=()=>{r=!0
+},e.versionString="10.7.2";for(const e in _)"object"==typeof _[e]&&t(_[e])
+;return Object.assign(e,_),e.addPlugin(m),e.addPlugin(D),e.addPlugin(E),e})({})
+}();"object"==typeof exports&&"undefined"!=typeof module&&(module.exports=hljs);hljs.registerLanguage("xml",(()=>{"use strict";function e(e){
+return e?"string"==typeof e?e:e.source:null}function n(e){return a("(?=",e,")")}
+function a(...n){return n.map((n=>e(n))).join("")}function s(...n){
+return"("+n.map((n=>e(n))).join("|")+")"}return e=>{
+const t=a(/[A-Z_]/,a("(",/[A-Z0-9_.-]*:/,")?"),/[A-Z0-9_.-]*/),i={
+className:"symbol",begin:/&[a-z]+;|&#[0-9]+;|&#x[a-f0-9]+;/},r={begin:/\s/,
+contains:[{className:"meta-keyword",begin:/#?[a-z_][a-z1-9_-]+/,illegal:/\n/}]
+},c=e.inherit(r,{begin:/\(/,end:/\)/}),l=e.inherit(e.APOS_STRING_MODE,{
+className:"meta-string"}),g=e.inherit(e.QUOTE_STRING_MODE,{
+className:"meta-string"}),m={endsWithParent:!0,illegal:/</,relevance:0,
+contains:[{className:"attr",begin:/[A-Za-z0-9._:-]+/,relevance:0},{begin:/=\s*/,
+relevance:0,contains:[{className:"string",endsParent:!0,variants:[{begin:/"/,
+end:/"/,contains:[i]},{begin:/'/,end:/'/,contains:[i]},{begin:/[^\s"'=<>`]+/}]}]
+}]};return{name:"HTML, XML",
+aliases:["html","xhtml","rss","atom","xjb","xsd","xsl","plist","wsf","svg"],
+case_insensitive:!0,contains:[{className:"meta",begin:/<![a-z]/,end:/>/,
+relevance:10,contains:[r,g,l,c,{begin:/\[/,end:/\]/,contains:[{className:"meta",
+begin:/<![a-z]/,end:/>/,contains:[r,c,g,l]}]}]},e.COMMENT(/<!--/,/-->/,{
+relevance:10}),{begin:/<!\[CDATA\[/,end:/\]\]>/,relevance:10},i,{
+className:"meta",begin:/<\?xml/,end:/\?>/,relevance:10},{className:"tag",
+begin:/<style(?=\s|>)/,end:/>/,keywords:{name:"style"},contains:[m],starts:{
+end:/<\/style>/,returnEnd:!0,subLanguage:["css","xml"]}},{className:"tag",
+begin:/<script(?=\s|>)/,end:/>/,keywords:{name:"script"},contains:[m],starts:{
+end:/<\/script>/,returnEnd:!0,subLanguage:["javascript","handlebars","xml"]}},{
+className:"tag",begin:/<>|<\/>/},{className:"tag",
+begin:a(/</,n(a(t,s(/\/>/,/>/,/\s/)))),end:/\/?>/,contains:[{className:"name",
+begin:t,relevance:0,starts:m}]},{className:"tag",begin:a(/<\//,n(a(t,/>/))),
+contains:[{className:"name",begin:t,relevance:0},{begin:/>/,relevance:0,
+endsParent:!0}]}]}}})());
diff --git a/highlight.js/styles/default.css b/highlight.js/styles/default.css
new file mode 100644
index 0000000..b3f5ba7
--- /dev/null
+++ b/highlight.js/styles/default.css
@@ -0,0 +1,110 @@
+/*
+
+Original highlight.js style (c) Ivan Sagalaev <maniac@softwaremaniacs.org>
+
+*/
+:root {
+ --xml_text: #cb1212;
+ --xml_meta: #1f7199;
+ --xml_name_weight: normal;
+ --xml_name_color: #000000;
+}
+
+
+.hljs {
+ display: block;
+}
+
+.hljs-tag {
+ font-weight: bolder;
+ color: var(--text);
+}
+/* Base color: saturation 0; */
+
+.hljs,
+.hljs-subst {
+ /* color: #444; */
+ color: var(--xml_text);
+ font-weight: bold;
+}
+
+.hljs-comment {
+ color: var(--text);
+}
+
+.hljs-keyword,
+.hljs-attribute,
+.hljs-selector-tag,
+.hljs-meta-keyword,
+.hljs-doctag,
+.hljs-name {
+ color: var(--xml_name_color);
+ font-weight: var(--xml_name_weight);
+}
+
+
+/* User color: hue: 0 */
+
+.hljs-type,
+.hljs-string,
+.hljs-number,
+.hljs-selector-id,
+.hljs-selector-class,
+.hljs-quote,
+.hljs-template-tag,
+.hljs-deletion {
+ color: var(--xml_text);
+}
+
+.hljs-title,
+.hljs-section {
+ color: var(--xml_text);
+ font-weight: bold;
+}
+
+.hljs-regexp,
+.hljs-symbol,
+.hljs-variable,
+.hljs-template-variable,
+.hljs-link,
+.hljs-selector-attr,
+.hljs-selector-pseudo {
+ color: var(--xml_text);
+}
+
+
+/* Language color: hue: 90; */
+
+.hljs-literal {
+ color: var(--green);
+}
+
+.hljs-built_in,
+.hljs-bullet,
+.hljs-code,
+.hljs-addition {
+ color: var(--green);
+}
+
+
+/* Meta color: hue: 200 */
+
+.hljs-meta {
+ color: var(--xml_meta);
+ font-weight: bold;
+}
+
+.hljs-meta-string {
+ color: var(--xml_meta);
+}
+
+
+/* Misc effects */
+
+.hljs-emphasis {
+ font-style: italic;
+}
+
+.hljs-strong {
+ font-weight: bold;
+}
diff --git a/highlight.js/styles/dusk2dawn.css b/highlight.js/styles/dusk2dawn.css
new file mode 100644
index 0000000..a3da99c
--- /dev/null
+++ b/highlight.js/styles/dusk2dawn.css
@@ -0,0 +1,16 @@
+/*
+
+Original highlight.js style (c) Ivan Sagalaev <maniac@softwaremaniacs.org>
+
+*/
+
+/* Include default.css */
+@import "default.css";
+
+/* All colors are controlled by the following section */
+:root {
+ --xml_text: #6cff7c;
+ --xml_meta: #1f7199;
+ --xml_name_weight: normal;
+ --xml_name_color: var(--text);
+}