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
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>