Back to home page

EIC code displayed by LXR

 
 

    


Warning, file /image_browser/image_browser.html was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).

0001 <!DOCTYPE html>
0002 <html lang="en">
0003 
0004 <head>
0005     <meta charset="UTF-8">
0006     <meta name="viewport" content="width=device-width, initial-scale=1.0">
0007     <title>Image Gallery</title>
0008     <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
0009     <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css">
0010     <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
0011     <script src="https://cdnjs.cloudflare.com/ajax/libs/html2canvas/1.4.1/html2canvas.min.js"></script>
0012     <script src="https://polyfill.io/v3/polyfill.min.js?features=es6"></script>
0013     <script src="https://cdnjs.cloudflare.com/ajax/libs/lazysizes/5.3.2/lazysizes.min.js" async></script>
0014 
0015     <script id="MathJax-script" async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js"></script>
0016 
0017 </head>
0018 
0019 <style>
0020     body,
0021     html {
0022         margin: 0;
0023         padding: 0;
0024         font-family: Arial, sans-serif;
0025         height: 100%;
0026         overflow-y: auto;
0027         overflow-x: hidden;
0028     }
0029 
0030     .navbar {
0031         background-color: #f8f8f8;
0032         color: #333;
0033         display: flex;
0034         justify-content: space-between;
0035         padding: 10px 20px;
0036         box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
0037         margin-bottom: 5px;
0038         position: fixed;
0039         top: 0;
0040         left: 0;
0041         width: 100%;
0042         z-index: 1030;
0043     }
0044 
0045     .nav-logo a,
0046     .nav-links a {
0047         color: #000;
0048         text-decoration: none;
0049         margin: 0 15px;
0050         font-size: 18px;
0051     }
0052 
0053     .nav-links a:hover {
0054         color: #555;
0055     }
0056 
0057     .container {
0058         display: flex;
0059         height: calc(100vh - 50px);
0060         /* Adjust height based on navbar height */
0061     }
0062 
0063     #sidebar {
0064         position: relative;
0065         width: 100%;
0066         height: auto;
0067         overflow-y: auto;
0068         top: 100px;
0069         left: 0;
0070     }
0071 
0072     .selector {
0073         width: 100%;
0074         padding: 10px;
0075         margin-bottom: 20px;
0076     }
0077 
0078     .form-label {
0079         margin-top: 15px;
0080     }
0081 
0082     #imageGallery,
0083     #galleryInfo {
0084         position: relative;
0085         /* Ensure they flow normally within the document */
0086         top: 0;
0087         left: 0;
0088         width: 100%;
0089         /* Full width for smaller screens */
0090         padding: 10px;
0091         background-color: #ffffff;
0092         box-sizing: border-box;
0093         /* Padding included in width */
0094     }
0095 
0096     #imageGallery {
0097         height: auto;
0098         /* Flexible height */
0099         overflow-y: auto;
0100         /* Allow vertical scrolling */
0101         display: flex;
0102         flex-wrap: wrap;
0103         /* Responsive image wrapping */
0104     }
0105 
0106     /* Medium screens and larger */
0107     @media (min-width: 768px) {
0108         #sidebar {
0109             position: fixed;
0110             /* Fixed position on larger screens */
0111             top: 100px;
0112             /* Set the top offset */
0113             left: 0;
0114             /* Aligned to the left edge */
0115             width: 300px;
0116             /* Fixed width */
0117             height: calc(100% - 100px);
0118             /* Adjusted height based on the top offset */
0119         }
0120 
0121         #mainContent {
0122             margin-left: 300px;
0123             /* Offset main content to avoid overlapping with the sidebar */
0124         }
0125 
0126         #imageGallery {
0127             position: relative;
0128             /* Maintain relative positioning for flexibility */
0129             left: 0;
0130             /* Align left edge with the content area */
0131             width: calc(100% - 300px);
0132             /* Adjust width to account for sidebar */
0133             margin-top: 50px;
0134         }
0135 
0136         #galleryInfo {
0137             position: relative;
0138             /* Keep relative to flow with document layout */
0139             left: 0;
0140             /* Align with main content */
0141             top: 0;
0142             /* Reset top alignment */
0143         }
0144     }
0145 
0146     .d-flex {
0147         display: flex !important;
0148         flex-wrap: wrap;
0149     }
0150 
0151     .ms-2 {
0152         margin-left: 10px;
0153         margin-right: 3.0rem !important;
0154     }
0155 
0156     .gallery img {
0157         margin: 10px;
0158         width: 200px;
0159         height: auto;
0160         border-radius: 8px;
0161         transition: transform 0.2s ease-in-out;
0162 
0163     }
0164 
0165     .card-text-container {
0166         display: flex;
0167         align-items: flex-end;
0168         justify-content: space-between;
0169         /* or use flex-start/flex-end depending on your layout */
0170     }
0171 
0172     .card-text {
0173         margin: 0;
0174         padding-right: 10px;
0175         /* Adjust as necessary */
0176     }
0177 
0178 
0179     .card {
0180         box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
0181         transition: box-shadow 0.3s ease;
0182     }
0183 
0184     .card:hover {
0185         box-shadow: 0 8px 16px rgba(0, 0, 0, 0.2);
0186     }
0187 
0188 
0189     .gallery img:hover {
0190         transform: scale(1.05);
0191     }
0192 
0193     .footer {
0194         background: #f8f8f8;
0195         padding: 10px 0;
0196         position: fixed;
0197         left: 0;
0198         bottom: 0;
0199         width: 100%;
0200         text-align: center;
0201         box-shadow: 0 -2px 4px rgba(0, 0, 0, 0.1);
0202     }
0203 
0204     .footer .btn-link {
0205         color: #333;
0206         text-decoration: none;
0207     }
0208 
0209     .footer .card {
0210         width: auto;
0211     }
0212 
0213     .card-body {
0214         font-size: 0.9rem;
0215         padding: 8px;
0216     }
0217 
0218     nav ul {
0219         list-style: none;
0220         padding: 0;
0221         margin: 0;
0222         display: flex;
0223         align-items: center;
0224     }
0225 
0226     nav ul li {
0227         position: relative;
0228         margin-right: 20px;
0229     }
0230 
0231     nav ul li a {
0232         text-decoration: none;
0233         color: black;
0234         display: block;
0235         padding: 5px;
0236     }
0237 
0238     .submenu {
0239         display: none;
0240         position: absolute;
0241         background-color: white;
0242         border-radius: 5px;
0243         box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
0244         width: auto;
0245         z-index: 1000;
0246     }
0247 
0248     nav ul li:hover .submenu {
0249         display: block;
0250         /* Show submenu on hover */
0251     }
0252 
0253     .submenu a {
0254         display: block;
0255         padding: 10px;
0256         border-bottom: 1px solid #ccc;
0257         color: black;
0258         text-decoration: none;
0259     }
0260 
0261     .submenu a:hover {
0262         background-color: #f9f9f9;
0263     }
0264 
0265     .submenu a:last-child {
0266         border-bottom: none;
0267     }
0268 
0269     #imageGallery.single-plot {
0270         justify-content: flex-start;
0271     }
0272 
0273     .breadcrumb {
0274         padding: 8px 15px;
0275         margin-bottom: 0;
0276         background-color: #f8f9fa;
0277         /* Light grey background */
0278         border-radius: 0.25rem;
0279         list-style-type: none;
0280         display: flex;
0281     }
0282 
0283     .breadcrumb-item {
0284         display: inline;
0285     }
0286 
0287     .breadcrumb-item:first-child::before {
0288         content: none;
0289         /* No content for the first item */
0290     }
0291 
0292     .breadcrumb-item+.breadcrumb-item::after {
0293         content: "/";
0294         /* Custom separator */
0295         padding: 0 5px;
0296         color: #6c757d;
0297         /* Bootstrap's default secondary text color */
0298         display: inline;
0299     }
0300 
0301     .breadcrumb-item a {
0302         text-decoration: none;
0303     }
0304 
0305     .breadcrumb-item.active a {
0306         color: #6c757d;
0307         /* Bootstrap's default secondary text color */
0308         pointer-events: none;
0309         /* Makes the link non-clickable */
0310         cursor: default;
0311     }
0312 
0313     .modal {
0314         display: none;
0315         /* Hidden by default */
0316         position: fixed;
0317         /* Stay in place */
0318         z-index: 20;
0319         /* Sit on top */
0320         left: 0;
0321         width: 100%;
0322         /* Full width */
0323         height: 100%;
0324         /* Full height */
0325         overflow: auto;
0326         /* Enable scroll if needed */
0327         background-color: rgba(0, 0, 0, 0.4);
0328         /* Black w/ opacity */
0329     }
0330 
0331     .modal-content {
0332         position: relative;
0333         top: 30px;
0334         background-color: #fefefe;
0335         margin: 10% auto;
0336         /* 10% from the top and centered */
0337         padding: 50px;
0338         padding-top: 100px;
0339         padding-bottom: 100px;
0340         border: 1px solid #888;
0341         width: 100%;
0342         /* Could be more or less, depending on screen size */
0343     }
0344 
0345     .close {
0346         position: absolute;
0347         color: #aaa;
0348         right: 30px;
0349         font-size: 28px;
0350         cursor: pointer;
0351     }
0352 
0353     input[type="text"],
0354     input[type="email"],
0355     input[type="file"],
0356     textarea {
0357         width: 60%;
0358         padding: 12px;
0359         margin: 8px 0;
0360         display: inline-block;
0361         border: 1px solid #ccc;
0362         border-radius: 8px;
0363         box-sizing: border-box;
0364     }
0365 
0366     input[type="submit"] {
0367         width: 60%;
0368         background-color: black;
0369         color: white;
0370         padding: 14px 20px;
0371         margin: 8px 0;
0372         border: none;
0373         border-radius: 4px;
0374         cursor: pointer;
0375     }
0376 
0377     input[type="submit"]:hover {
0378         background-color: gray;
0379     }
0380 
0381     .modal-tabs {
0382         overflow: hidden;
0383         border-radius: 8px;
0384         background-color: rgb(247, 247, 247);
0385         padding: 10px;
0386         width: 260px;
0387     }
0388 
0389     .tablink {
0390         background-color: inherit;
0391         border-radius: 8px;
0392         float: left;
0393         border: none;
0394         outline: none;
0395         cursor: pointer;
0396         padding: 14px 16px;
0397         transition: 0.3s;
0398         font-size: 17px;
0399     }
0400 
0401     .tablink:hover {
0402         background-color: #ddd;
0403     }
0404 
0405     /* Active tab style */
0406     .tablink.active {
0407         background-color: white;
0408         /* Light grey background for the active tab */
0409     }
0410 
0411     #formHeaderContact {
0412         font-weight: 900;
0413         margin-top: 80px;
0414         padding: 10px;
0415     }
0416 
0417     #formHeaderAddPlot {
0418         font-weight: 900;
0419         margin-top: 80px;
0420         padding: 10px;
0421     }
0422 
0423     #searchBar {
0424         margin: 0;
0425     }
0426 
0427     .btn-custom {
0428         background-color: black;
0429         color: white;
0430         border: none;
0431         border-radius: 8px;
0432         padding: 10px 15px;
0433     }
0434 
0435     .about-section {
0436         display: flex;
0437         justify-content: space-between;
0438         align-items: center;
0439         margin-top: 20rem;
0440         padding-left: 10px;
0441     }
0442 
0443     .about-section img {
0444         max-width: 100%;
0445         height: auto;
0446         border-radius: 8px;
0447         margin-top: 35px;
0448         padding: 20px;
0449     }
0450 
0451     .about-section h1 {
0452         font-weight: bold;
0453         font-size: 4rem;
0454     }
0455 
0456     /*make first p section have gray font color*/
0457     .about-section p:first-of-type {
0458         color: gray;
0459     }
0460 
0461     .about-section p {
0462         font-size: 1.3rem;
0463     }
0464 
0465 
0466     .col-md-6 {
0467         padding-left: 40px;
0468     }
0469 
0470     .fa-download {
0471         margin-left: 30px;
0472         color: gray;
0473         vertical-align: bottom;
0474         /* Align with the middle of the text */
0475     }
0476 
0477     select[multiple] {
0478         font-size: 16px;
0479         /* Increase the font size */
0480         height: 400px;
0481         /* Automatically adjust the height */
0482         width: 250px;
0483         /* Set a custom width */
0484         padding: 10px;
0485         /* Add padding to increase the size */
0486     }
0487 </style>
0488 
0489 <body>
0490     <nav class="navbar navbar-expand-lg navbar-light bg-light">
0491         <div class="container-fluid">
0492             <a class="navbar-brand" href="#"><img src="./EPIC-logo_black_transparent.png" alt="epic logo" width="75px"
0493                     height="auto"></a>
0494             <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav"
0495                 aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
0496                 <span class="navbar-toggler-icon"></span>
0497             </button>
0498             <div class="collapse navbar-collapse justify-content-end" id="navbarNav">
0499                 <ul class="navbar-nav">
0500                     <li class="nav-item">
0501                         <a class="nav-link active" aria-current="page" id="homeLink" href="#home">Home</a>
0502                     </li>
0503                     <li class="nav-item">
0504                         <a class="nav-link" href="#" id="physicsLink">Physics</a>
0505                         <div id="PhysicsSubmenu" class="submenu"></div>
0506                     </li>
0507                     <li class="nav-item">
0508                         <a class="nav-link" href="#" id="detectorLink">Detector</a>
0509                         <div id="DetectorSubmenu" class="submenu"></div>
0510                     </li>
0511                     <li>
0512                         <a class="nav-link" href="#" id="ciLink">CI</a>
0513                     </li>
0514                     <li class="nav-item">
0515                         <a class=" btn-custom" href="mailto:roark@jlab.org" id="contactLink">Contact</a>
0516                     </li>
0517                 </ul>
0518             </div>
0519         </div>
0520     </nav>
0521 
0522     <div id="modal" class="modal">
0523         <div class="modal-content">
0524             <!--<span class="close">&times;</span>-->
0525             <div class="modal-tabs">
0526                 <button class="tablink" onclick="openForm(event, 'Contact')">Contact</button>
0527                 <button class="tablink" onclick="openForm(event, 'AddPlot')">Add a new plot</button>
0528             </div>
0529             <div id="Contact" class="tabcontent">
0530                 <div id="formHeaderContact">
0531                     <h2>Contact</h2>
0532                 </div>
0533                 <form id="contactForm" method="POST">
0534                     <input type="text" id="firstName" name="firstName" placeholder="First name" required><br>
0535                     <input type="text" id="lastName" name="lastName" placeholder="Last name" required><br>
0536                     <input type="email" id="email" name="email" placeholder="Email address" required><br>
0537                     <textarea id="message" name="message" placeholder="Your message" required></textarea><br>
0538                     <input type="submit" value="Submit">
0539                 </form>
0540             </div>
0541             <div id="AddPlot" class="tabcontent">
0542                 <div id="formHeaderAddPlot">
0543                     <h2>Add a new plot type</h2>
0544                 </div>
0545                 <div id="reminder">
0546                     <p>Macros/scripts should be submitted to the benchmarks repository in GitLab <a
0547                             href="https://eicweb.phy.anl.gov/EIC/benchmarks" target="_blank"><i
0548                                 class="fab fa-gitlab"></i></a>. Alternatively, you may use this form. </p>
0549                 </div>
0550                 <form id="addPlotForm">
0551                     <input type="text" id="name" name="name" placeholder="Your Name" required><br>
0552                     <input type="text" id="workingGroup" name="workingGroup" placeholder="Working Group" required><br>
0553                     <input type="text" id="plotName" name="plotName" placeholder="Name of Plot" required><br>
0554                     <textarea id="description" name="description" placeholder="Brief description of the plot"
0555                         required></textarea><br>
0556                     <textarea id="instructions" name="instructions" placeholder="Special instructions for running?"
0557                         required></textarea><br>
0558                     <input type="file" id="plotFile" name="plotFile[]" multiple required><br>
0559                     <input type="submit" value="Submit">
0560                 </form>
0561             </div>
0562         </div>
0563     </div>
0564 
0565 
0566     <div id="dynamicContent" class="container-fluid"></div>
0567     <footer class="footer">
0568         <p><i class="fa-solid fa-triangle-exclamation"></i> Plots on this page are automatically generated and are not
0569             approved for use in presentations or other documents. </p>
0570     </footer>
0571 
0572     <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
0573 </body>
0574 
0575 </html>
0576 
0577 <script>
0578 
0579     document.addEventListener('DOMContentLoaded', function () {
0580         populateSubmenus();
0581         loadHomePage();  // Load home page content by default
0582 
0583         document.body.addEventListener("click", function (e) {
0584             if (e.target.closest('#homeLink')) {
0585                 e.preventDefault(); // Prevent default link behavior
0586                 loadHomePage();
0587             } else if (e.target.closest('.submenu a')) {
0588                 var plotGroup = e.target.getAttribute("data-plotgroup-id");
0589 
0590                 if (plotGroup) {
0591                     populateSidebar(plotGroup);
0592                     loadOriginalContent(plotGroup);
0593                 }
0594             }
0595         });
0596     });
0597 
0598     function updateImageGallery() {
0599         console.log('updateImageGallery called');
0600 
0601         var campaignSelect = document.getElementById("CampaignSelect");
0602         var plotTypeSelect = document.getElementById("PlotTypesSelect");
0603         var geometrySelect = document.getElementById("GeometrySelect");
0604         var energySelect = document.getElementById("BeamEnergySelect");
0605         var minQ2Select = document.getElementById("MinQ2Select");
0606         var particleTypeSelect = document.getElementById("ParticleTypeSelect");
0607         var pipelineSelect = document.getElementById("PipelineSelect");
0608 
0609         var campaigns = campaignSelect ? Array.from(campaignSelect.selectedOptions).map(option => option.value) : [];
0610         var plotTypes = plotTypeSelect ? Array.from(plotTypeSelect.selectedOptions).map(option => option.value) : [];
0611         var energies = energySelect ? Array.from(energySelect.selectedOptions).map(option => option.value) : [];
0612         var geometries = geometrySelect ? Array.from(geometrySelect.selectedOptions).map(option => option.value) : [];
0613         var minQ2s = minQ2Select ? Array.from(minQ2Select.selectedOptions).map(option => option.value) : [];
0614         var particleTypes = particleTypeSelect ? Array.from(particleTypeSelect.selectedOptions).map(option => option.value) : [];
0615         var pipelines = pipelineSelect ? Array.from(pipelineSelect.selectedOptions).map(option => option.value) : [];
0616 
0617         // Clear current images
0618         var imageGallery = document.getElementById('imageGallery');
0619         imageGallery.innerHTML = '';
0620 
0621         if (plotTypes.length === 1) {
0622             imageGallery.classList.add("single-plot");
0623         } else {
0624             imageGallery.classList.remove("single-plot");
0625         }
0626 
0627         if (plotTypes.includes('all')) {
0628             plotTypes = Array.from(plotTypeSelect.options).map(option => option.value).filter(value => value !== 'all');
0629         }
0630 
0631         // Determine if IsChunked should be 1 or 0
0632         var IsChunked = pipelines.length > 0 ? 1 : 0;
0633 
0634         // Loop through each plot type and fetch images
0635         plotTypes.forEach(plotType => {
0636             var cardDeckDiv = document.createElement("div");
0637             cardDeckDiv.className = "d-flex flex-row mb-4 flex-wrap";
0638 
0639             // Variables to hold header and subheader
0640             var header = document.createElement("h2");
0641             header.className = "text-left";
0642             header.style.fontSize = "24px";
0643 
0644             var headerSet = false; // Flag to check if header text is set
0645             var headerAppended = false; // Flag to check if header has been appended to the DOM
0646 
0647             // Fetch image paths from the PHP script
0648             fetch(`/epic/php/images.php?plotName=${encodeURIComponent(plotType)}&IsChunked=${IsChunked}`)
0649                 .then(response => response.json())
0650                 .then(data => {
0651                     if (!data || data.length === 0) {
0652                         console.error('No data received from the server for plotType:', plotType);
0653                         return;
0654                     }
0655 
0656                     // Set header text using DisplayName from the first plot
0657                     if (!headerSet) {
0658                         let displayText = data[0].DisplayName || plotType.replace(".png", "");
0659                         header.textContent = displayText;
0660                         headerSet = true;
0661                     }
0662 
0663                     // Variable to hold subheader if needed
0664                     var subHeader = null;
0665                     // Check if Description is available and different from DisplayName
0666                     if (data[0].Description && data[0].Description !== data[0].DisplayName) {
0667                         // Create the subheader element
0668                         subHeader = document.createElement("h3");
0669                         subHeader.style.fontSize = "18px"; // Set font size for subheader
0670                         subHeader.style.fontWeight = "normal"; // Make subheader less bold than header
0671                         subHeader.style.marginTop = "5px"; // Optional: Add some spacing
0672                         subHeader.textContent = data[0].Description;
0673                     }
0674 
0675                     var results = data.map(plot => {
0676                         const pathParts = plot.ImagePath.split('/');
0677 
0678                         let pipeline = null;
0679                         let pipeline_id = null;
0680                         let campaign = null;
0681                         let geometry = null;
0682                         let energy = null;
0683                         let minQ2 = null;
0684 
0685                         // Check if the path contains 'images' directory and determine indices accordingly
0686                         const imagesIndex = pathParts.indexOf('images');
0687                         const pipelinesIndex = pathParts.indexOf('pipelines');
0688 
0689                         if (pipelinesIndex !== -1) {
0690                             pipeline = pathParts.length > pipelinesIndex + 1 ? pathParts[pipelinesIndex + 1] : null;
0691                             if (pipeline) {
0692                                 let pipelineParts = pipeline.split('-');
0693                                 pipeline_id = pipelineParts.length > 1 ? pipelineParts[1] : null;
0694                             }
0695                         }
0696 
0697                         if (imagesIndex !== -1 && IsChunked === 0) {
0698                             // For non-CI plots, extract campaign info
0699                             // If 'images' directory is found, the campaign (if any) should be the next directory
0700                             campaign = pathParts.length > imagesIndex + 1 ? pathParts[imagesIndex + 1] : null;
0701 
0702                             // Check if the campaign is actually a known campaign or just a directory like 'MaterialScan'
0703                             const knownCampaigns = campaigns.length > 0 ? campaigns : []; // List of known campaigns
0704                             if (!knownCampaigns.includes(campaign)) {
0705                                 // If not a known campaign, set campaign to null
0706                                 campaign = null;
0707                             }
0708 
0709                             geometry = pathParts.length > imagesIndex + 2 ? pathParts[imagesIndex + 2] : null;
0710                             energy = pathParts.length > imagesIndex + 5 ? pathParts[imagesIndex + 5] : null;
0711                             minQ2 = pathParts.length > imagesIndex + 6 ? (pathParts[imagesIndex + 6].split("=")[1] || null) : null;
0712                         }
0713 
0714                         const hasPipeline = pipelines.length === 0 || (pipeline_id && pipelines.includes(pipeline_id));
0715                         const hasCampaign = campaigns.length === 0 || (campaign && campaigns.includes(campaign));
0716                         const hasGeometry = geometries.length === 0 || (geometry && geometries.includes(geometry));
0717                         const hasEnergy = energies.length === 0 || (energy && energies.includes(energy));
0718                         const hasMinQ2 = minQ2s.length === 0 || (minQ2 && minQ2s.includes(minQ2));
0719                         const hasParticleType = particleTypes.length === 0 || particleTypes.some(pt => plot.PlotTypeName.includes(pt));
0720 
0721                         // Include hasPipeline in the condition
0722                         if (hasPipeline && hasCampaign && hasGeometry && hasEnergy && hasMinQ2 && hasParticleType) {
0723                             var colDiv = document.createElement("div");
0724                             colDiv.className = "col-md-4";
0725                             colDiv.setAttribute('data-campaign', campaign); // Add data attribute for sorting
0726 
0727                             var cardDiv = document.createElement("div");
0728                             cardDiv.className = "card mb-4 mx-2";
0729 
0730                             var link = document.createElement("a");
0731                             link.setAttribute('data-href', plot.ImagePath + plot.PlotTypeName + ".png");
0732                             link.target = "_blank";
0733                             link.rel = "noopener noreferrer"; // For security
0734 
0735                             // Set href on click to prevent prefetching
0736                             link.addEventListener('click', function (event) {
0737                                 event.preventDefault(); // Prevent default action
0738                                 var url = link.getAttribute('data-href');
0739                                 window.open(url, '_blank');
0740                             });
0741 
0742                             /*
0743                             var img = document.createElement("img");
0744                             img.style.paddingTop = "10px";
0745                             img.src = plot.ImagePath + plot.PlotTypeName + ".png";
0746                             img.className = "card-img-top image-item";
0747                             img.alt = plot.PlotTypeName;
0748                             */
0749                             var img = document.createElement("img");
0750                             img.style.paddingTop = "10px";
0751                             img.setAttribute('data-src', plot.ImagePath + plot.PlotTypeName + ".png");
0752                             img.className = "card-img-top image-item lazyload"; // Add 'lazyload' class
0753                             img.alt = plot.PlotTypeName;
0754 
0755                             var tooltipText = `Path: ${plot.ImagePath}`;
0756                             img.title = tooltipText;
0757 
0758                             var cardBodyDiv = document.createElement("div");
0759                             cardBodyDiv.className = "card-body";
0760 
0761                             var cardTextContainer = document.createElement("div");
0762                             cardTextContainer.className = "card-text-container";
0763 
0764                             var campaignInfo = document.createElement("p");
0765                             campaignInfo.className = "card-text";
0766                             let campaignTextItems = [];
0767 
0768                             if (IsChunked === 1) {
0769                                 // For CI plots, only add Pipeline ID
0770                                 if (pipeline_id) {
0771                                     campaignTextItems.push(`Pipeline ID: ${pipeline_id}`);
0772                                 }
0773                             } else {
0774                                 // For non-CI plots, include campaign info
0775                                 if (campaign) {
0776                                     campaignTextItems.push(`Campaign: ${campaign}`);
0777                                 }
0778 
0779                                 // Parse energy to a float and check if it's a valid number
0780                                 let sanitizedEnergy = energy ? energy.replace(/[^0-9.-]+/g, '') : null;
0781                                 let energyValue = sanitizedEnergy ? parseFloat(sanitizedEnergy) : null;
0782 
0783                                 if (!isNaN(energyValue)) {
0784                                     campaignTextItems.push(`Energy: ${energy}`);
0785                                 }
0786 
0787                                 if (minQ2) {
0788                                     campaignTextItems.push(`Min Q²: ${minQ2}`);
0789                                 }
0790                             }
0791 
0792                             // If there are any campaign info items, join them and set the innerHTML
0793                             if (campaignTextItems.length > 0) {
0794                                 campaignInfo.innerHTML = campaignTextItems.join('<br>');
0795                             }
0796 
0797                             var downloadIcon = document.createElement("i");
0798                             downloadIcon.className = "fas fa-download";
0799                             downloadIcon.title = "Download Image";
0800 
0801                             downloadIcon.addEventListener('click', function(event) {
0802                                 event.stopPropagation();
0803                                 event.preventDefault(); // Prevent default action
0804                             
0805                                 var img = cardDiv.querySelector('img');
0806                             
0807                                 // Check if the image is loaded
0808                                 if (img.complete && img.naturalHeight !== 0) {
0809                                     // Proceed with download
0810                                     downloadCardAsPNG(cardDiv, downloadIcon);
0811                                 } else {
0812                                     // Wait for the image to load
0813                                     img.addEventListener('load', function onImageLoad() {
0814                                         // Remove the event listener to prevent multiple triggers
0815                                         img.removeEventListener('load', onImageLoad);
0816                                         downloadCardAsPNG(cardDiv, downloadIcon);
0817                                     });
0818                                 }
0819                             }, { once: true });
0820 
0821                             // Create a link for the download icon
0822                             var downloadLink = document.createElement("a");
0823                             downloadLink.href = plot.ImagePath + plot.PlotTypeName + ".png";
0824                             downloadLink.download = plot.PlotTypeName + ".png"; // Set the download attribute with file name
0825                             downloadLink.appendChild(downloadIcon);
0826 
0827                             cardTextContainer.appendChild(campaignInfo);
0828                             cardTextContainer.appendChild(downloadLink);
0829 
0830                             cardDiv.appendChild(link);
0831                             link.appendChild(img);
0832                             cardBodyDiv.appendChild(cardTextContainer);
0833                             cardDiv.appendChild(cardBodyDiv);
0834                             colDiv.appendChild(cardDiv);
0835 
0836                             return { pipeline_id, minQ2: parseFloat(minQ2) || 0, element: colDiv }; // Return object with minQ2 and the DOM element
0837                         }
0838                         return null;
0839                     }).filter(item => item !== null);
0840 
0841                     // Now sort results and display them
0842                     results.sort((a, b) => a.pipeline_id !== null && b.pipeline_id !== null
0843                     ? a.pipeline_id - b.pipeline_id
0844                     : a.minQ2 - b.minQ2
0845                 );                    
0846 
0847                     if (results.length > 0) {
0848                         // Append the header and subheader if not already appended
0849                         if (!headerAppended) {
0850                             // Create a container for header and subheader
0851                             var headerContainer = document.createElement("div");
0852                             headerContainer.appendChild(header);
0853                             if (subHeader) {
0854                                 headerContainer.appendChild(subHeader);
0855                             }
0856                             imageGallery.appendChild(headerContainer);
0857                             headerAppended = true;
0858                         }
0859 
0860                         results.forEach(result => {
0861                             if (result && result.element) {
0862                                 cardDeckDiv.appendChild(result.element); // Append sorted elements
0863                             }
0864                         });
0865                         imageGallery.appendChild(cardDeckDiv);
0866                     }
0867                     updateImageCount();
0868                 })
0869                 .catch(error => {
0870                     console.error('Error fetching data for plotType:', plotType, error);
0871                 });
0872         });
0873     }
0874 
0875 
0876     function populateSubmenus() {
0877         var xmlhttp;
0878         if (window.XMLHttpRequest) {
0879             // code for modern browsers
0880             xmlhttp = new XMLHttpRequest();
0881         } else {
0882             // code for old IE browsers
0883             xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
0884         }
0885 
0886         xmlhttp.onreadystatechange = function () {
0887             if (this.readyState == 4 && this.status == 200) {
0888                 var response = JSON.parse(this.responseText);
0889 
0890                 // Clear existing menus if needed
0891                 document.querySelectorAll('.submenu').forEach(menu => menu.innerHTML = '');
0892 
0893                 // Loop over each supergroup in the response
0894                 Object.keys(response).forEach(function (superGroupName) {
0895                     //skip superGroupName that do not equal Physics or Detector
0896                     if (superGroupName !== "Physics" && superGroupName !== "Detector") {
0897                         return;
0898                     }
0899                     var menuId = superGroupName + 'Submenu'; // create an ID by removing spaces
0900                     var menu = document.getElementById(menuId);
0901 
0902                     if (menu) {
0903                         response[superGroupName].forEach(function (item) {
0904                             var link = document.createElement("a");
0905                             link.href = "#";
0906                             link.textContent = item.name;
0907                             link.className = "dropdown-item";
0908                             link.setAttribute("data-plotgroup-id", item.id);
0909                             link.setAttribute("data-toggle", "tooltip");
0910                             link.setAttribute("title", "Click to view plots for " + item.name);
0911 
0912                             // Disable items that are not "Jets and Heavy Flavor" for PhysicsSubmenu
0913                             if (superGroupName === "Physics" && item.name !== "Jets and Heavy Flavor") {
0914                                 link.classList.add("disabled");
0915                                 link.setAttribute("aria-disabled", "true");
0916                                 link.style.pointerEvents = "none"; // Disable clicking
0917                                 link.style.opacity = "0.5"; // Optional: make it look disabled
0918                             }
0919 
0920                             if (superGroupName === "Detector" && item.name !== "Material Scan" && item.name !== "Barrel Imaging Calorimeter") {
0921                                 link.classList.add("disabled");
0922                                 link.setAttribute("aria-disabled", "true");
0923                                 link.style.pointerEvents = "none"; // Disable clicking
0924                                 link.style.opacity = "0.5";
0925                             }
0926                             menu.appendChild(link);
0927                         });
0928                     } else {
0929                         console.error('Menu for ' + superGroupName + ' not found');
0930                     }
0931                 });
0932             }
0933         };
0934         xmlhttp.open("GET", "./php/get_groups.php", true);
0935         xmlhttp.send();
0936     }
0937 
0938     function updateImageCount() {
0939         const imageCount = document.querySelectorAll('#imageGallery .image-item').length;
0940         document.getElementById('imageCount').innerText = imageCount + " Images";
0941     }
0942 
0943     function sortImages() {
0944         const sortSelect = document.getElementById("sortSelect").value;
0945         const cardDecks = document.querySelectorAll('.d-flex.flex-row.mb-4'); // Get all card decks
0946 
0947         console.log(`Sorting ${cardDecks.length} card decks...`);
0948 
0949         cardDecks.forEach(deck => {
0950             let colDivs = Array.from(deck.querySelectorAll('.col-md-4')); // Get all card containers in this deck
0951 
0952             // Log the unsorted state for debugging
0953             console.log('Unsorted cards:', colDivs.map(div => div.getAttribute('data-campaign')));
0954 
0955             // Sort the containers based on the campaign data
0956             colDivs.sort((a, b) => {
0957                 const campaignA = a.getAttribute('data-campaign').split('.').map(Number);
0958                 const campaignB = b.getAttribute('data-campaign').split('.').map(Number);
0959                 return (sortSelect === 'Oldest' ? 1 : -1) * (campaignA[0] - campaignB[0] || campaignA[1] - campaignB[1]);
0960             });
0961 
0962             // Log the sorted state for debugging
0963             console.log('Sorted cards:', colDivs.map(div => div.getAttribute('data-campaign')));
0964 
0965             // Re-append each container in sorted order
0966             colDivs.forEach(colDiv => deck.appendChild(colDiv));
0967         });
0968 
0969         console.log("Sorting complete.");
0970     }
0971 
0972     function loadHomePage() {
0973         const content = `
0974                 <div class="container-fluid about-section" style="padding-left: 0; padding-right: 0;">
0975                     <div class="row">
0976                         <div class="col-md-6">
0977                             <h1>About</h1>
0978                             <p>Goal: Central location to view plots produced from ePIC simulations to assist in visualizing and understanding the integrity of the data and identify missing or unexpected results. These plots should be accessible to everyone in the collaboration.</p>
0979                             <p>Designs and prototypes are developed in Figma and deployed using Javascript, HTML, and CSS.</p>
0980                             <p>References to the images on this page are stored in a MySQL database. The database tracks the campaign and relevant meta data for each image automatically. Data is fetched from the database using PHP.</p>
0981                         </div>
0982                         <div class="col-md-6">
0983                             <img src="./epic-cutaway-labeled.png" alt="Detector Image">
0984                         </div>
0985                     </div>
0986                 </div>
0987 
0988                 <footer class="footer">
0989                     <p><i class="fa-solid fa-triangle-exclamation"></i> Plots on this page are automatically generated and are not approved for use in presentations or other documents. </p>
0990                 </footer>
0991             `;
0992         document.getElementById('dynamicContent').innerHTML = content;
0993     }
0994 
0995     function loadOriginalContent(plotGroup) {
0996         const content = `<div class="container-fluid">
0997     <div class="row">
0998             <div class="col-12 col-md-2 mb-3" id="sidebar"></div>
0999  
1000         <div class="col-12 col-md-10 mb-3" id="mainContent">
1001             <div class="row mb-3 align-items-center" id="galleryInfo">
1002                 <div class="col-12 col-md-4">
1003                     <span id="imageCount">Count Images</span>
1004                 </div>
1005                 <div class="col-12 col-md-8 d-flex justify-content-end">
1006                     <!--
1007                     <div class="input-group" id="searchBar" style="width: 200px;">
1008                         <span class="input-group-text"><i class="fas fa-search"></i></span>
1009                         <input type="text" id="searchBar" class="form-control" placeholder="Search..."
1010                             oninput="filterImages()">
1011                     </div>-->
1012                     <select class="form-select ms-2" style="width: 100px;" id="sortSelect">
1013                         <option value="default">Sort by</option>
1014                         <option value="Newest">Newest</option>
1015                         <option value="Oldest">Oldest</option>
1016                     </select>
1017                 </div>
1018             </div>
1019             <div class="row" id="imageGallery"></div>
1020         </div>
1021     </div>
1022 </div>`;
1023         document.getElementById('dynamicContent').innerHTML = content;
1024         document.getElementById("sortSelect").addEventListener("change", sortImages);
1025     }
1026 
1027     function populateSidebar(plotGroup) {
1028         var xmlhttp = new XMLHttpRequest();
1029         xmlhttp.onreadystatechange = function () {
1030             if (this.readyState == 4 && this.status == 200) {
1031                 var response = JSON.parse(this.responseText);
1032                 const sidebar = document.getElementById('sidebar');
1033                 sidebar.innerHTML = ''; // Clear existing content in the sidebar
1034     
1035                 // Process each key in the response to create dropdowns with data
1036                 Object.keys(response).forEach(function (key) {
1037                     if (response[key].length > 0) { // Only create dropdowns if there are options available
1038                         var label = document.createElement('label');
1039                         label.setAttribute('for', key + 'Select');
1040                         label.className = 'form-label';
1041                         label.textContent = key === 'PlotTypes' ? 'Plot Type' : key;
1042     
1043                         var select = document.createElement('select');
1044                         select.id = key + 'Select';
1045                         select.className = 'form-select';
1046                         select.multiple = true;
1047     
1048                         // Sort Pipeline options if the key is "Pipeline"
1049                         let options = response[key];
1050                         if (key === "Pipeline") {
1051                             options = options
1052                                 .map(item => parseInt(item)) // Convert Pipeline IDs to integers
1053                                 .sort((a, b) => a - b) // Sort in ascending order
1054                                 .map(item => item.toString()); // Convert back to strings
1055                         } else if (key === "PlotTypes") {
1056                             options = options.sort((a, b) => a.Name.localeCompare(b.Name)); // Sort PlotTypes alphabetically by Name
1057                         }
1058     
1059                         // Append options to the select element
1060                         options.forEach(function (item) {
1061                             var option = document.createElement('option');
1062     
1063                             if (key === 'PlotTypes') {
1064                                 option.value = item.Name;
1065                                 option.textContent = item.DisplayName || item.Description || item.Name;
1066                             } else {
1067                                 option.value = item;
1068                                 option.textContent = item;
1069                             }
1070     
1071                             select.appendChild(option);
1072                         });
1073     
1074                         if (select.id === "PlotTypesSelect") {
1075                             var allOption = document.createElement('option');
1076                             allOption.value = "all";
1077                             allOption.textContent = "All";
1078                             allOption.selected = true;
1079                             select.prepend(allOption);
1080                         }
1081     
1082                         var button = document.createElement('button');
1083                         button.className = 'btn btn-toggle w-100 text-start align-items-center justify-content-between d-flex';
1084                         button.type = 'button';
1085                         button.setAttribute('data-bs-toggle', 'collapse');
1086                         button.setAttribute('data-bs-target', '#filter' + key);
1087                         button.setAttribute('aria-expanded', 'false');
1088                         button.setAttribute('aria-controls', 'filter' + key);
1089                         button.innerHTML = `${key === 'PlotTypes' ? 'Plot Type' : key} <span id="filter${key}-icon">+</span>`;
1090     
1091                         var collapseDiv = document.createElement('div');
1092                         collapseDiv.className = 'collapse';
1093                         collapseDiv.id = 'filter' + key;
1094                         collapseDiv.appendChild(select);
1095     
1096                         button.addEventListener('click', function () {
1097                             var icon = button.querySelector('span');
1098                             icon.textContent = icon.textContent === '+' ? '-' : '+';
1099                         });
1100     
1101                         if (key === 'Campaign' || key === 'PlotTypes' || key === 'Pipeline') {
1102                             collapseDiv.classList.add('show');
1103                             button.setAttribute('aria-expanded', 'true');
1104                             var icon = button.querySelector('span');
1105                             icon.textContent = '-';
1106                         }
1107     
1108                         sidebar.appendChild(button);
1109                         sidebar.appendChild(collapseDiv);
1110     
1111                         if (select.id === "CampaignSelect") {
1112                             if (plotGroup === '28') {
1113                                 Array.from(select.options).forEach(option => {
1114                                     if (option.value === "24.04.0") {
1115                                         option.selected = true;
1116                                     }
1117                                 });
1118                             } else {
1119                                 select.lastElementChild.selected = true;
1120                             }
1121                         }
1122     
1123                         if (select.id === "PipelineSelect") {
1124                             // Select the latest (highest) Pipeline ID by default
1125                             select.lastElementChild.selected = true;
1126                         }
1127                     }
1128                 });
1129     
1130                 setOnChangeEvents();
1131                 updateImageGallery();
1132             }
1133         };
1134         xmlhttp.open("GET", "./php/generate_sidebar.php?superPlotGroup_ID=" + plotGroup, true);
1135         xmlhttp.send();
1136     }
1137     
1138     function setOnChangeEvents() {
1139         var campaignSelect = document.getElementById("CampaignSelect");
1140         var plotTypeSelect = document.getElementById("PlotTypesSelect");
1141         var geometrySelect = document.getElementById("GeometrySelect");
1142         var energySelect = document.getElementById("BeamEnergySelect");
1143         var minQ2Select = document.getElementById("MinQ2Select");
1144         var particleTypeSelect = document.getElementById("ParticleTypeSelect");
1145         var pipelineSelect = document.getElementById("PipelineSelect");
1146 
1147         if (campaignSelect) {
1148             campaignSelect.onchange = updateImageGallery;
1149         }
1150         if (plotTypeSelect) {
1151             plotTypeSelect.onchange = updateImageGallery;
1152         }
1153         if (geometrySelect) {
1154             geometrySelect.onchange = updateImageGallery;
1155         }
1156         if (energySelect) {
1157             energySelect.onchange = updateImageGallery;
1158         }
1159         if (minQ2Select) {
1160             minQ2Select.onchange = updateImageGallery;
1161         }
1162 
1163         if (particleTypeSelect) {
1164             particleTypeSelect.onchange = updateImageGallery;
1165         }
1166 
1167 
1168         if (pipelineSelect) {
1169             pipelineSelect.onchange = updateImageGallery;
1170         }
1171     }
1172 
1173     function downloadCardAsPNG(cardElement, buttonElement) {
1174         // Find the image within the card
1175         let imageElement = cardElement.querySelector('img.card-img-top');
1176         let imageName = imageElement && imageElement.alt ? imageElement.alt.trim().replace(/ /g, '_') : 'default_card_image';
1177     
1178         // Check if the image is loaded
1179         if (!imageElement.complete || imageElement.naturalHeight === 0) {
1180             // Wait for the image to load before proceeding
1181             imageElement.addEventListener('load', function onImageLoad() {
1182                 imageElement.removeEventListener('load', onImageLoad);
1183                 proceedWithDownload();
1184             });
1185         } else {
1186             proceedWithDownload();
1187         }
1188     
1189         function proceedWithDownload() {
1190             // Capture the card
1191             html2canvas(cardElement, {
1192                 scale: 3,
1193                 onclone: function(clonedDoc) {
1194                     let clonedButton = clonedDoc.querySelector('.fa-download');
1195                     if (clonedButton) {
1196                         clonedButton.style.visibility = 'hidden';
1197                     }
1198                 }
1199             }).then(function(canvas) {
1200                 var image = canvas.toDataURL("image/png");
1201                 var link = document.createElement('a');
1202                 link.href = image;
1203                 link.download = `${imageName}.png`;
1204                 document.body.appendChild(link);
1205                 link.click();
1206                 document.body.removeChild(link);
1207             });
1208         }
1209     }
1210 
1211     function filterPlotTypeOptions() {
1212         var input = document.getElementById("plotTypeSearch").value.toLowerCase();
1213         var plotTypeSelect = document.getElementById("plotTypeSelect");
1214         var options = plotTypeSelect.options;
1215 
1216         for (var i = 0; i < options.length; i++) {
1217             var option = options[i];
1218             var optionText = option.text.toLowerCase();
1219             if (optionText.indexOf(input) > -1) {
1220                 option.style.display = ""; // Option matches, show it
1221             } else {
1222                 option.style.display = "none"; // Option does not match, hide it
1223             }
1224         }
1225     }
1226 
1227     window.onclick = function (event) {
1228         if (event.target == document.getElementById('modal')) {
1229             document.getElementById('modal').style.display = 'none';
1230         }
1231     }
1232 
1233     function openForm(evt, formName) {
1234         var i, tabcontent, tablinks;
1235         tabcontent = document.getElementsByClassName("tabcontent");
1236         for (i = 0; i < tabcontent.length; i++) {
1237             tabcontent[i].style.display = "none";
1238         }
1239         tablinks = document.getElementsByClassName("tablink");
1240         for (i = 0; i < tablinks.length; i++) {
1241             tablinks[i].className = tablinks[i].className.replace(" active", "");
1242         }
1243         document.getElementById(formName).style.display = "block";
1244         evt.currentTarget.className += " active";
1245     }
1246 
1247     document.getElementById('ciLink').addEventListener('click', function (event) {
1248         event.preventDefault();
1249 
1250         populateSidebar('38');
1251         loadOriginalContent('38');
1252         updateImageGallery();
1253 
1254     });
1255 </script>