Repository: akrherz/iem
Pull request #1189: Omnibus
Author: login: akrherz
Created: 2025-03-28T17:41:17Z | Merged: 2025-03-28T17:50:59Z



Diff:
diff --git a/htdocs/GIS/apps/rview/warnings.js b/htdocs/GIS/apps/rview/warnings.js
index e1d47fbdb8..f01305a032 100644
--- a/htdocs/GIS/apps/rview/warnings.js
+++ b/htdocs/GIS/apps/rview/warnings.js
@@ -22,17 +22,6 @@ $(document).ready(() => {
         showControl(`${layer}-control`);
     });
 
-    $("#myanimation").jsani({
-        imageSrcs: $("#imagesrcs li").map((_i, el) => $(el).text()).get(),
-        aniWidth: $("#datawindow").width(),
-        aniHeight: $("#datawindow").height(),
-        initdwell: 200,
-        controls: ['stopplay', 'firstframe', 'previous', 'next', 'lastframe', 'looprock', 'slow', 'fast', 'zoom'],
-        last_frame_pause: 8,
-        first_frame_pause: 1,
-        frame_pause: '0:5, 3:6'
-    });
-
     $(".iemselect2").select2();
 
     // check if archive=yes is set in the URL
diff --git a/htdocs/GIS/apps/rview/warnings.phtml b/htdocs/GIS/apps/rview/warnings.phtml
index 89fc4a22ce..52e011c696 100644
--- a/htdocs/GIS/apps/rview/warnings.phtml
+++ b/htdocs/GIS/apps/rview/warnings.phtml
@@ -149,15 +149,10 @@ if (isset($_GET["lat0"]) && isset($_GET["lon0"]) && $osite == $site) {
 
 $t->title = "RADAR &amp; NWS Warnings";
 $t->jsextra = <<<EOM
-<script src="/vendor/jquery-ui/{$JQUERYUI}/jquery-ui.min.js"></script>
-<script language="javascript" type="text/javascript" src="/vendor/jsani/2.84/jquery.jsani.min.js"></script>
 <script src="/vendor/select2/4.0.3/select2.min.js"></script>
 <script src="warnings.js"></script>
 EOM;
 $t->headextra = <<<EOM
-<link rel="stylesheet" href="/vendor/jquery-ui/{$JQUERYUI}/jquery-ui.min.css">
-<link rel="stylesheet" href="/vendor/jquery-ui/{$JQUERYUI}/jquery-ui.theme.min.css">
-<link rel="stylesheet" href="/vendor/jsani/2.84/jquery.jsani.min.css">
 <link rel="stylesheet" type="text/css" href="/vendor/select2/4.0.3/select2.min.css"/ >
 <link rel="stylesheet" type="text/css" href="warnings.css" />
 EOM;
@@ -424,8 +419,9 @@ EOM;
 $heightadd = 0;
 $maptitle = "IEM NEXRAD Mosaic Base Reflectivity";
 if ($loop === 1 || $loop === 2) {
+    $t->jsani = true;
     // Alternative 1, we want javascript looper!
-    $hidden_list = '<ul style="display: none;" id="imagesrcs">';
+    $hidden_list = '<ul style="display: none;" id="iemjsani_frames">';
     $urls = array();
     for ($i = 0; $i < $frames; $i++) {
         $imgi = $frames - 1 - $i;
@@ -443,7 +439,7 @@ if ($loop === 1 || $loop === 2) {
     $content .= <<<EOM
 </form>
 <a href="anim_gif.php?fts=$fts">Download as Animated Gif</a><br />
-<div id="myanimation"></div>
+<div id="iemjsani" style="width: {$width}px; height: {$height}px;"></div>
 {$hidden_list}
 EOM;
 
diff --git a/htdocs/current/bloop.phtml b/htdocs/current/bloop.phtml
index e214ec4789..d9f82a5d32 100644
--- a/htdocs/current/bloop.phtml
+++ b/htdocs/current/bloop.phtml
@@ -90,25 +90,16 @@ $intselect = make_select("interval", $interval, $ar);
 $jsextra = "";
 $headextra = "";
 if ($java == "script") {
+    $t->jsani = true;
     $sz = sizeof($images);
-    $hidden_list = '<ul id="imagesrcs" style="display: none;">';
+    $hidden_list = '<ul id="iemjsani_frames" style="display: none;">';
     foreach($images as $key => $val){
         $hidden_list .= sprintf("<li>%s</li>", $val);
     }
     $hidden_list .= "</ul>";
     $control = <<<EOM
-<div id="myanimation" style="width: {$width}px; height: {$height}px;"></div>
+<div id="iemjsani" style="width: {$width}px; height: {$height}px;"></div>
 {$hidden_list}
-EOM;
-    $headextra .= <<<EOM
-<link rel="stylesheet" href="/vendor/jquery-ui/{$JQUERYUI}/jquery-ui.min.css">
-<link rel="stylesheet" href="/vendor/jquery-ui/{$JQUERYUI}/jquery-ui.theme.min.css">
-<link rel="stylesheet" href="/vendor/jsani/2.84/jquery.jsani.min.css">
-EOM;
-    $jsextra .= <<<EOM
-<script src="/vendor/jquery-ui/{$JQUERYUI}/jquery-ui.min.js"></script>
-<script src="/vendor/jsani/2.84/jquery.jsani.min.js"></script>
-<script src="bloop.js"></script>
 EOM;
 } else {
     $control = "";
diff --git a/htdocs/current/mcview.phtml b/htdocs/current/mcview.phtml
index 349be965c5..a1ea6ad968 100644
--- a/htdocs/current/mcview.phtml
+++ b/htdocs/current/mcview.phtml
@@ -1,12 +1,13 @@
 <?php
 require_once "../../config/settings.inc.php";
+require_once "../../include/myview.php";
+require_once "../../include/forms.php";
+require_once "../../include/database.inc.php";
+
 $JQUERYUI = "1.12.1";
 
-require_once "../../include/myview.php";
 $t = new MyView();
 $uri = 'mcview.phtml';
-require_once "../../include/forms.php";
-require_once "../../include/database.inc.php";
 $postgis = iemdb("postgis");
 
 $stname = iem_pg_prepare(
@@ -255,36 +256,15 @@ $jsextra = "";
 $headextra = "";
 
 if ($java == 'script') {
+    $t->jsani = true;
     $i = 0;
-    $jsurls = [];
+    $jsurls = '<ul id="iemjsani_frames" style="display: none;">';
     foreach ($images as $key => $val) {
-        $jsurls[] = sprintf("'%s'", $val);
+        $jsurls .= sprintf("<li>%s</li>\n", $val);
     }
-    $myimages = implode(",", $jsurls);
-    $control = "<p><div id=\"myanimation\"></div></p>";
-    $headextra .= <<<EOM
-<link rel="stylesheet" href="/vendor/jquery-ui/{$JQUERYUI}/jquery-ui.min.css">
-<link rel="stylesheet" href="/vendor/jquery-ui/{$JQUERYUI}/jquery-ui.theme.min.css">
-<link rel="stylesheet" href="/vendor/jsani/2.84/jquery.jsani.min.css">
-EOM;
-    $jsextra .= <<<EOM
-<script src="/vendor/jquery-ui/{$JQUERYUI}/jquery-ui.min.js"></script>
-<script language="javascript" type="text/javascript" src="/vendor/jsani/2.84/jquery.jsani.min.js"></script>
-<script>
- $(document).ready(function () {
-    var myanimation = $("#myanimation").jsani({
-        imageSrcs: [$myimages],
-        aniWidth: 640,
-        aniHeight: 480,
-        initdwell: 200,
-        controls: ['stopplay', 'firstframe', 'previous', 'next', 'lastframe', 'looprock', 'slow', 'fast', 'zoom'],
-        last_frame_pause: 8,
-        first_frame_pause: 1,
-        frame_pause: '0:5, 3:6'
-    });
-});
-</script>
-EOM;
+    $jsurls .= "</ul>";
+    $control = "{$jsurls}<p><div id=\"iemjsani\"></div></p>";
+
 } else {
     reset($images);
     $control = "";
diff --git a/htdocs/current/viewer.js b/htdocs/current/viewer.js
index 3493017033..d0d8440933 100644
--- a/htdocs/current/viewer.js
+++ b/htdocs/current/viewer.js
@@ -223,7 +223,7 @@ function refreshJSON() {
         url,
         format: new ol.format.GeoJSON()
     });
-    newsource.on('change', function () {
+    newsource.on('change', () => {
         updateCamera();
     });
     webcamGeoJsonLayer.setSource(newsource);
@@ -238,7 +238,7 @@ function refreshJSON() {
         url,
         format: new ol.format.GeoJSON()
     });
-    newsource.on('change', function () {
+    newsource.on('change', () => {
         updateCamera();
     });
     idotdashcamGeoJsonLayer.setSource(newsource);
@@ -253,7 +253,7 @@ function refreshJSON() {
         url,
         format: new ol.format.GeoJSON()
     });
-    newsource.on('change', function () {
+    newsource.on('change', () => {
         updateCamera();
     });
     idotRWISLayer.setSource(newsource);
@@ -445,11 +445,7 @@ $().ready(() => {
     });
 
     map.on('click', (evt) => {
-        const feature = map.forEachFeatureAtPixel(evt.pixel,
-            function (feature2) {
-                return feature2;
-            }
-        );
+        const feature = map.forEachFeatureAtPixel(evt.pixel, (ft) => ft);
         if (!feature) {
             return;
         }
diff --git a/htdocs/js/iemss.js b/htdocs/js/iemss.js
index a74f5930cb..6e3f38d4bd 100644
--- a/htdocs/js/iemss.js
+++ b/htdocs/js/iemss.js
@@ -157,10 +157,10 @@ $().ready(() => {
     $('#stations_delall').click(() => {
         return !$('#stations_out option').remove().appendTo('#stations_in');
     });
-    $('#stations_del').click(function () {
+    $('#stations_del').click(() => {
         $('#stations_out option:selected').remove().appendTo('#stations_in');
-        $('#stations_out option').each(function () { // this
-            $(this).attr("selected", "selected");
+        $('#stations_out option').each((_idx, item) => {
+            $(item).prop("selected", true);
         });
         return false;
     });
diff --git a/htdocs/current/bloop.js b/htdocs/js/jsani.js
similarity index 51%
rename from htdocs/current/bloop.js
rename to htdocs/js/jsani.js
index 85efc64b92..7c5511c4b7 100644
--- a/htdocs/current/bloop.js
+++ b/htdocs/js/jsani.js
@@ -1,9 +1,13 @@
 // global $
+
+/**
+ * Converts the iemjsani div into an animation player
+ */
 $().ready(() => {
-    $("#myanimation").jsani({
-        imageSrcs: $("#imagesrcs li").map((_i, el) => $(el).text()).get(),
-        aniWidth: $("#myanimation").width() + 100,
-        aniHeight: $("#myanimation").width() + 100,
+    $("#iemjsani").jsani({
+        imageSrcs: $("#iemjsani_frames li").map((_i, el) => $(el).text()).get(),
+        aniWidth: $("#iemjsani").width(),
+        aniHeight: $("#iemjsani").height(),
         initdwell: 200,
         controls: ['stopplay', 'firstframe', 'previous', 'next', 'lastframe', 'looprock', 'slow', 'fast', 'zoom'],
         last_frame_pause: 8,
diff --git a/htdocs/nws/pds_watches.js b/htdocs/nws/pds_watches.js
new file mode 100644
index 0000000000..0c35fa4f5a
--- /dev/null
+++ b/htdocs/nws/pds_watches.js
@@ -0,0 +1,4 @@
+// global $
+$('#makefancy').click(() => {
+    $("#thetable table").DataTable();
+});
diff --git a/htdocs/nws/pds_watches.php b/htdocs/nws/pds_watches.php
index a3667f3783..d4723b373d 100644
--- a/htdocs/nws/pds_watches.php
+++ b/htdocs/nws/pds_watches.php
@@ -41,14 +41,9 @@
 EOM;
 $t->jsextra = <<<EOM
 <script src='/vendor/jquery-datatables/1.10.20/datatables.min.js'></script>
-<script>
-$('#makefancy').click(function(){
-    $("#thetable table").DataTable();
-});
-</script>
+<script src="pds_watches.js"></script>
 EOM;
 
-
 $t->content = <<<EOM
 <ol class="breadcrumb">
  <li><a href="/nws/">NWS Resources</a></li>
diff --git a/htdocs/nws/snowfall_6hour.js b/htdocs/nws/snowfall_6hour.js
new file mode 100644
index 0000000000..301056d7ea
--- /dev/null
+++ b/htdocs/nws/snowfall_6hour.js
@@ -0,0 +1,4 @@
+// global $
+$('#makefancy').click(() => {
+    $("#thetable table").DataTable();
+});
\ No newline at end of file
diff --git a/htdocs/nws/snowfall_6hour.php b/htdocs/nws/snowfall_6hour.php
index 350788a4cf..a36cf2f7b2 100644
--- a/htdocs/nws/snowfall_6hour.php
+++ b/htdocs/nws/snowfall_6hour.php
@@ -14,11 +14,7 @@
 EOM;
 $t->jsextra = <<<EOM
 <script src='/vendor/jquery-datatables/1.10.20/datatables.min.js'></script>
-<script>
-$('#makefancy').click(function(){
-    $("#thetable table").DataTable();
-});
-</script>
+<script src="snowfall_6hour.js"></script>
 EOM;
 
 $year = get_int404("year", date("Y"));
diff --git a/htdocs/nws/watches.js b/htdocs/nws/watches.js
new file mode 100644
index 0000000000..0c35fa4f5a
--- /dev/null
+++ b/htdocs/nws/watches.js
@@ -0,0 +1,4 @@
+// global $
+$('#makefancy').click(() => {
+    $("#thetable table").DataTable();
+});
diff --git a/htdocs/nws/watches.php b/htdocs/nws/watches.php
index f045b23ec8..250d8d450c 100644
--- a/htdocs/nws/watches.php
+++ b/htdocs/nws/watches.php
@@ -43,14 +43,9 @@
 EOM;
 $t->jsextra = <<<EOM
 <script src='/vendor/jquery-datatables/1.10.20/datatables.min.js'></script>
-<script>
-$('#makefancy').click(function(){
-    $("#thetable table").DataTable();
-});
-</script>
+<script src="watches.js"></script>
 EOM;
 
-
 $t->content = <<<EOM
 <ol class="breadcrumb">
  <li><a href="/nws/">NWS Resources</a></li>
diff --git a/htdocs/vtec/maxetn.js b/htdocs/vtec/maxetn.js
new file mode 100644
index 0000000000..a19c5a11fa
--- /dev/null
+++ b/htdocs/vtec/maxetn.js
@@ -0,0 +1,4 @@
+// global $
+$(document).ready(() => {
+    $("#thetable table").DataTable();
+});
diff --git a/htdocs/vtec/maxetn.php b/htdocs/vtec/maxetn.php
index e715d7ccb0..654b300f76 100644
--- a/htdocs/vtec/maxetn.php
+++ b/htdocs/vtec/maxetn.php
@@ -52,14 +52,9 @@
 {$table}
 </div>
 
-
 EOM;
 $t->jsextra = <<<EOM
 <script src='/vendor/jquery-datatables/1.10.20/datatables.min.js'></script>
-<script>
-$(document).ready(() => {
-    $("#thetable table").DataTable();
-});
-</script>
+<script src="maxetn.js">
 EOM;
 $t->render("full.phtml");
diff --git a/htdocs/wx/afos/p.php b/htdocs/wx/afos/p.php
index 91075e8d3b..d79b8bf3c6 100644
--- a/htdocs/wx/afos/p.php
+++ b/htdocs/wx/afos/p.php
@@ -10,7 +10,8 @@
 $e = get_int404("e", null);
 // Ensure e is 12 characters long
 if (!is_null($e) && strlen($_GET["e"]) != 12) {
-    xssafe("</script>");
+    // Naughty
+    xssafe("<tag>");
 }
 $pil = isset($_GET['pil']) ? strtoupper(substr(xssafe($_GET['pil']), 0, 6)) : null;
 $bbb = isset($_GET["bbb"]) ? strtoupper(substr(xssafe($_GET["bbb"]), 0, 3)) : null;
diff --git a/include/myview.php b/include/myview.php
index 044a586410..4652a4a483 100644
--- a/include/myview.php
+++ b/include/myview.php
@@ -18,6 +18,7 @@ class MyView {
     public ?string $current_network = null;
     public ?string $headextra = null;
     public bool $iemss = FALSE;
+    public bool $jsani = FALSE;
     public ?string $jsextra = null;
     public int $refresh = -1;
     public ?string $sites_current = null;
diff --git a/include/templates/footer.phtml b/include/templates/footer.phtml
index 00d3e5e1dd..8aa1a5c75f 100644
--- a/include/templates/footer.phtml
+++ b/include/templates/footer.phtml
@@ -6,6 +6,14 @@ if ($this->iemss){
 <script src="/js/iemss.js"></script>
 EOF;
 }
+if ($this->jsani){
+    $jsextra .= <<<EOM
+<script src="/vendor/jquery-ui/1.12.1/jquery-ui.min.js"></script>
+<script language="javascript" type="text/javascript" src="/vendor/jsani/2.84/jquery.jsani.min.js"></script>
+<script src="/vendor/jsani/2.84/jquery.jsani.min.js"></script>
+<script src="/js/jsani.js"></script>
+EOM;
+}
 
 ?>
 <!-- footer -->
diff --git a/include/templates/header.phtml b/include/templates/header.phtml
index dcfaa374aa..c81c69773c 100644
--- a/include/templates/header.phtml
+++ b/include/templates/header.phtml
@@ -13,6 +13,13 @@ if ($this->iemss){
 <link rel="stylesheet" href="/vendor/openlayers/7.2.2/ol.css" type="text/css">
 EOF;
 }
+if ($this->jsani){
+    $headextra .= <<<EOM
+<link rel="stylesheet" href="/vendor/jquery-ui/1.12.1/jquery-ui.min.css">
+<link rel="stylesheet" href="/vendor/jquery-ui/1.12.1/jquery-ui.theme.min.css">
+<link rel="stylesheet" href="/vendor/jsani/2.84/jquery.jsani.min.css">
+EOM;
+}
 $bodyextra = is_null($this->bodyextra) ? '': " {$this->bodyextra}";
 ?>
 <!DOCTYPE html>


Reviews:
user: login: gemini-code-assist[bot]
body: ## Code Review
This pull request appears to focus on modernizing the codebase by removing jQuery UI and jsani dependencies, replacing them with a more modern approach for animation and table manipulation. The changes also involve converting some anonymous functions to arrow functions for better readability and consistency. Overall, the changes seem beneficial for maintainability and performance.

### Summary of Findings
* **Missing jsani initialization**: The jsani library is being removed, but the initialization code in `warnings.js` is also removed without a clear replacement strategy. This might break the animation functionality.
* **Inconsistent variable naming**: In `js/jsani.js`, the code still references `myanimation` and `imagesrcs` which are remnants of the removed jsani library. These should be updated to reflect the new `iemjsani` and `iemjsani_frames` naming convention.
* **Potential XSS vulnerability**: In `wx/afos/p.php`, the change from `</script>` to `<tag>` might introduce an XSS vulnerability if not properly handled by the `xssafe` function.

### Merge Readiness
The pull request introduces significant changes, including the removal of dependencies and refactoring of animation logic. While the changes aim to improve maintainability, some potential issues need to be addressed before merging. Specifically, the missing jsani initialization, inconsistent variable naming, and potential XSS vulnerability should be carefully reviewed and resolved. I am unable to approve this pull request, and recommend that it not be merged until the identified issues are addressed.
state: CHANGES_REQUESTED
author_association: NONE
submitted_at: 2025-03-28T17:41:39Z

Review comments:
diff_hunk: @@ -10,7 +10,8 @@
 $e = get_int404("e", null);
 // Ensure e is 12 characters long
 if (!is_null($e) && strlen($_GET["e"]) != 12) {
-    xssafe("</script>");
+    // Naughty
+    xssafe("<tag>");
path: htdocs/wx/afos/p.php
user: login: gemini-code-assist[bot]
body: ![high](https://img.shields.io/badge/priority-high-orange)

The change from `</script>` to `<tag>` might introduce an XSS vulnerability if not properly handled by the `xssafe` function. Ensure that the `xssafe` function is correctly sanitizing the input to prevent any potential XSS attacks. Consider adding a comment explaining why this change was made and how it addresses the original issue.
created_at: 2025-03-28T17:41:39Z
author_association: NONE
diff_hunk: @@ -149,15 +149,10 @@ if (isset($_GET["lat0"]) && isset($_GET["lon0"]) && $osite == $site) {
 
 $t->title = "RADAR &amp; NWS Warnings";
 $t->jsextra = <<<EOM
-<script src="/vendor/jquery-ui/{$JQUERYUI}/jquery-ui.min.js"></script>
-<script language="javascript" type="text/javascript" src="/vendor/jsani/2.84/jquery.jsani.min.js"></script>
 <script src="/vendor/select2/4.0.3/select2.min.js"></script>
 <script src="warnings.js"></script>
path: htdocs/GIS/apps/rview/warnings.phtml
user: login: gemini-code-assist[bot]
body: ![medium](https://img.shields.io/badge/priority-medium-yellow)

The removal of jQuery UI and jsani scripts is good for reducing dependencies. However, ensure that the `warnings.js` script is updated to handle any functionalities that depended on these libraries. Consider adding a comment explaining how the removed functionalities are now handled.
created_at: 2025-03-28T17:41:39Z
author_association: NONE
diff_hunk: @@ -424,8 +419,9 @@ EOM;
 $heightadd = 0;
 $maptitle = "IEM NEXRAD Mosaic Base Reflectivity";
 if ($loop === 1 || $loop === 2) {
+    $t->jsani = true;
path: htdocs/GIS/apps/rview/warnings.phtml
user: login: gemini-code-assist[bot]
body: ![medium](https://img.shields.io/badge/priority-medium-yellow)

Setting `$t->jsani = true;` is good for enabling the jsani-related scripts and styles. However, ensure that this flag is only set when the animation functionality is actually used. If the animation is not used, consider removing this line to avoid loading unnecessary resources.
created_at: 2025-03-28T17:41:39Z
author_association: NONE

PR comments:
user: login: deepsource-io[bot]
created_at: 2025-03-28T17:43:01Z
body: <div><p>Here's the code health analysis summary for commits <code>0825db5..fcf2cc0</code>. <a href="https://app.deepsource.com/gh/akrherz/iem/run/73fada9d-af8e-47e2-a5de-131792346454/">View details on DeepSource</a>&nbsp;↗.</p><h3>Analysis Summary</h3><table><thead><tr><th>Analyzer</th><th>Status</th><th>Summary</th><th>Link</th></tr></thead><tbody><tr><td><img src="https://static.deepsource.com/analyzer_logos/shell.svg" alt="DeepSource Shell Logo" width="16px" height="16px"/><strong>Shell</strong></td><td><span>✅&nbsp;</span><span>Success</span></td><td></td><td><a href="https://app.deepsource.com/gh/akrherz/iem/run/73fada9d-af8e-47e2-a5de-131792346454/shell/">View Check</a>&nbsp;↗</td></tr><tr><td><img src="https://static.deepsource.com/analyzer_logos/javascript.svg" alt="DeepSource JavaScript Logo" width="16px" height="16px"/><strong>JavaScript</strong></td><td><span>✅&nbsp;</span><span>Success</span></td><td></td><td><a href="https://app.deepsource.com/gh/akrherz/iem/run/73fada9d-af8e-47e2-a5de-131792346454/javascript/">View Check</a>&nbsp;↗</td></tr><tr><td><img src="https://static.deepsource.com/analyzer_logos/python.svg" alt="DeepSource Python Logo" width="16px" height="16px"/><strong>Python</strong></td><td><span>✅&nbsp;</span><span>Success</span></td><td></td><td><a href="https://app.deepsource.com/gh/akrherz/iem/run/73fada9d-af8e-47e2-a5de-131792346454/python/">View Check</a>&nbsp;↗</td></tr><tr><td><img src="https://static.deepsource.com/analyzer_logos/php.svg" alt="DeepSource PHP Logo" width="16px" height="16px"/><strong>PHP</strong></td><td><span>✅&nbsp;</span><span>Success</span></td><td></td><td><a href="https://app.deepsource.com/gh/akrherz/iem/run/73fada9d-af8e-47e2-a5de-131792346454/php/">View Check</a>&nbsp;↗</td></tr></tbody></table><hr/><blockquote><div>💡 If you’re a repository administrator, you can configure the quality gates from the <a href="https://app.deepsource.com/gh/akrherz/iem/settings/reporting">settings</a>.</div></blockquote></div>

author_association: NONE
user: login: codecov-commenter
created_at: 2025-03-28T17:51:04Z
body: ## [Codecov](https://app.codecov.io/gh/akrherz/iem/pull/1189?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=akrherz) Report
All modified and coverable lines are covered by tests :white_check_mark:
> Project coverage is 89.02%. Comparing base [(`0825db5`)](https://app.codecov.io/gh/akrherz/iem/commit/0825db5c7f4c6c746511a46ee477ded119ec71b8?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=akrherz) to head [(`fcf2cc0`)](https://app.codecov.io/gh/akrherz/iem/commit/fcf2cc0c90f0194cbfe2edb2814e5fc5c3b88f52?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=akrherz).
> Report is 4 commits behind head on main.

<details><summary>Additional details and impacted files</summary>


```diff
@@            Coverage Diff             @@
##             main    #1189      +/-   ##
==========================================
- Coverage   89.02%   89.02%   -0.01%     
==========================================
  Files         411      411              
  Lines       31423    31423              
==========================================
- Hits        27975    27974       -1     
- Misses       3448     3449       +1     
```

</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/akrherz/iem/pull/1189?dropdown=coverage&src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=akrherz).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=akrherz).

<details><summary> :rocket: New features to boost your workflow: </summary>

- :snowflake: [Test Analytics](https://docs.codecov.com/docs/test-analytics): Detect flaky tests, report on failures, and find test suite problems.
- :package: [JS Bundle Analysis](https://docs.codecov.com/docs/javascript-bundle-analysis): Save yourself from yourself by tracking and limiting bundle sizes in JS merges.
</details>
author_association: NONE