3rdUnknown / Spectre 5000

// ==UserScript==
// @name         Spectre 5000
// @namespace    https://spectre.5000
// @version      17.001
// @description  The feeling of not wanting to lose, everyone has it. If you find it unfair, you'll have to work harder yourself.
// @author       3rdUnknown
// @copyright    2025
// @license      MIT
// @match        *://62.210.192.50:3000/projects*
// @match        *://spectre.autotests-xbees.wildix.com/projects*
// @require      https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js
// @require      https://cdnjs.cloudflare.com/ajax/libs/jszip/3.7.1/jszip.min.js
// @require      https://cdnjs.cloudflare.com/ajax/libs/jszip-utils/0.1.0/jszip-utils.min.js
// @require      https://cdnjs.cloudflare.com/ajax/libs/FileSaver.js/2.0.0/FileSaver.min.js
// @require      https://unpkg.com/image-compare-viewer/dist/image-compare-viewer.min.js
// @resource     IMPORTED_CSS https://unpkg.com/image-compare-viewer/dist/image-compare-viewer.min.css
// @grant        GM_notification
// @grant        GM_xmlhttpRequest
// @grant        GM_openInTab
// @grant        GM_getResourceText
// @grant        GM_addStyle
// @run-at       document-idle
// @updateURL    https://openuserjs.org/meta/3rdUnknown/Spectre_5000.meta.js
// ==/UserScript==

(function() {
    'use strict';

    const my_css = GM_getResourceText('IMPORTED_CSS');
    GM_addStyle(my_css);
    GM_addStyle(`
/* ==== Modal Styling ==== */
.modal {
    display: none;
    position: fixed;
    z-index: 1;
    inset: 0;
    width: 100%;
    height: 100%;
    overflow: auto;
    background-color: rgba(0, 0, 0, 0.4);
}

.modal-content {
    background-color: #fefefe;
    margin: 3% auto;
    padding: 20px;
    border: 1px solid #888;
    width: 85%;
    height: 85%;
    text-align: center;
}

.imgModal1 img {
    height: 95%;
}

.imgModal2 img {
    max-width: 95%;
}

#image-compare {
    width: 100%;
    height: 95%;
}

/* ==== Modal Close Button ==== */
.close {
    color: #aaa;
    float: right;
    font-size: 28px;
    font-weight: bold;
    cursor: pointer;
}

.close:hover,
.close:focus {
    color: black;
    text-decoration: none;
}

/* ==== Image Compare Wrapper ==== */
.icv__wrapper,
div.icv__fluidwrapper {
    background-size: contain;
    background-repeat: no-repeat;
}

/* Image Compare Labels Styling */
.icv__label {
  font-size: 0.9rem !important;
  font-weight: 600 !important;
  padding: 4px 8px !important;
  border-radius: 4px !important;
  position: absolute !important;
  bottom: 10px !important;
  z-index: 1000 !important;
  text-shadow: 1px 1px 2px rgba(0,0,0,0.5) !important;
  box-shadow: 0 1px 3px rgba(0,0,0,0.2) !important;
}

/* Baseline label - зеленый, левый угол */
.icv__label-before,
.icv__label.icv__label-before {
  background-color: #28a745 !important;
  color: white !important;
  border: 1px solid #1e7e34 !important;
  left: 10px !important;
}

/* CSS переменные для динамической ширины линий */
:root {
  --before-line-width: 0px;
  --after-line-width: 0px;
}

.icv__label-before::before,
.icv__label.icv__label-before::before {
  content: '' !important;
  position: absolute !important;
  top: 50% !important;
  left: 100% !important;
  transform: translateY(-50%) !important;
  width: var(--before-line-width) !important;
  height: 2px !important;
  background-color: #28a745 !important;
  border-radius: 1px !important;
  z-index: 999 !important;
  transition: width 0.2s ease !important;
}

/* Comparison label - оранжевый, правый угол */
.icv__label-after,
.icv__label.icv__label-after {
  background-color: #ff8c00 !important;
  color: white !important;
  border: 1px solid #e07600 !important;
  right: 10px !important;
}

.icv__label-after::before,
.icv__label.icv__label-after::before {
  content: '' !important;
  position: absolute !important;
  top: 50% !important;
  right: 100% !important;
  transform: translateY(-50%) !important;
  width: var(--after-line-width) !important;
  height: 2px !important;
  background-color: #ff8c00 !important;
  border-radius: 1px !important;
  z-index: 999 !important;
  transition: width 0.2s ease !important;
}

/* ==== Metadata Styling ==== */
.date-time {
    color: black;
    font-size: 1.5rem;
}

.version {
    font-size: 0.8em;
    color: #909090;
    margin: -5px 0 -10px;
}



/* ==== ZIP Download Block ==== */
#download-block {
    display: flex;
    flex-direction: column;
    align-items: center;
}

/* ==== Lightbox Border ==== */
.lightbox {
    border: 2px dashed red;
}

/* ==== Tabs: Product ==== */
#product-tabs {
  margin: 20px 0 5px 0;
  padding-left: 0;
  padding-bottom: 10px;
  border-bottom: 2px solid #666;
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
}

.product-tab {
  padding: 12px 20px !important;
  margin-right: 8px !important;
  margin-bottom: 0 !important;
  border: 1px solid #ddd;
  border-bottom: none;
  background-color: #f8f8f8;
  border-radius: 5px 5px 0 0;
  font-size: 16px !important;
  font-weight: bold;
  min-width: 120px;
  text-align: center;
  cursor: pointer;
  transition: all 0.2s ease;
}

.product-tab:hover {
  background-color: #e9e9e9 !important;
}

.product-tab.x-bees-tab {
  color: #ff8c00 !important;
}

.product-tab.x-hoppers-tab {
  color: #006400 !important;
}

.product-tab.collaboration-tab {
  color: #00008b !important;
}

.product-tab.all-tab {
  color: #333 !important;
}

.product-tab.website-tab {
  color: #8b008b !important;
}

.active-tab {
  background-color: #f0f0f0 !important;
  border-bottom: 3px solid currentColor !important;
  font-weight: bold !important;
}

.hidden-project {
  display: none;
}

/* ==== Tabs: Environments ==== */
#environment-tabs {
  margin: 0;
  padding-left: 0;
  border-bottom: none;
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
}

.environment-tab {
  padding: 8px 15px;
  margin-right: 6px;
  margin-bottom: 0;
  border: 1px solid #ddd;
  border-bottom: none;
  background-color: #f8f8f8;
  border-radius: 5px 5px 0 0;
  font-size: 14px;
  color: #333;
  cursor: pointer;
  transition: all 0.2s ease;
}

.environment-tab:hover {
  background-color: #e9e9e9 !important;
}

.environment-tab.active-env-tab {
  background-color: #f0f0f0 !important;
  border-bottom: 3px solid currentColor !important;
  font-weight: bold !important;
  color: black !important;
}

.environment-tab.stage-tab {
  color: #9c27b0 !important;
}

.environment-tab.stable-tab {
  color: #ff9800 !important;
}

/* ==== Category Filter ==== */
#category-filters {
    margin: 10px 0;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    padding-left: 15px;
}

.category-label {
    font-weight: bold;
    margin-right: 10px;
    font-size: 14px;
}

.category-button {
    padding: 6px 12px !important;
    margin: 0 8px 5px 0 !important;
    border: 1px solid #ddd;
    background-color: #f1f1f1;
    border-radius: 4px;
    font-size: 13px !important;
    color: #333 !important;
    cursor: pointer;
    transition: all 0.2s ease;
}

.category-button:hover {
    background-color: #e0e0e0;
}

.category-button.active-category {
    background-color: #4caf50;
    color: white !important;
    font-weight: bold;
    box-shadow: 0 1px 3px rgba(0,0,0,0.2);
}

.category-kite {
    border-left: 4px solid #2196F3;
}
.category-web {
    border-left: 4px solid #FF5722;
}
.category-android {
    border-left: 4px solid #4CAF50;
}
.category-ios {
    border-left: 4px solid #9C27B0;
}
.category-mobile-kite {
    border-left: 4px solid #FFC107;
}

/* ==== Project Rows ==== */
.old-project-row {
    background-color: #ffe5b4 !important;
    font-weight: bold;
}

.very-old-project-row {
    background-color: #f7e6e6 !important;
    font-weight: bold;
    border-left: 4px solid red;
}

/* ==== Test Labels and Stats ==== */
.label--fail,
.label--pass,
.label--warning,
a.total-tests {
    font-size: 1.65rem !important;
    font-weight: bold !important;
    text-align: center !important;
    vertical-align: middle !important;
    border-radius: 6px !important;
    margin: 2px !important;
    display: inline-block !important;
    white-space: nowrap !important;
    overflow: hidden !important;
    text-overflow: ellipsis !important;
    height: 40px !important;
    line-height: 40px !important;
    padding: 0 8px !important;
    border: 1px solid transparent !important;
    box-sizing: border-box !important;
}

td.text-center {
    text-align: right !important;
    padding-left: 15px !important;
}

.label--fail,
.label--pass {
    width: 95px !important;
}

.label--warning {
    width: 70px !important;
}

a.total-tests {
    width: 105px !important;
    height: 40px !important;
    line-height: 40px !important;
}

/* ==== Custom Blue Fail Button ==== */
.fail-button-blue {
    width: 140px !important;
    height: 37px !important;
    line-height: 40px !important;
    background-color: #17a2b8 !important;
    color: white !important;
    border-color: #17a2b8 !important;
    font-size: 1.65rem !important;
    font-weight: bold !important;
    text-align: center !important;
    vertical-align: middle !important;
    border-radius: 6px !important;
    margin: 2px !important;
    display: inline-block !important;
    white-space: nowrap !important;
    overflow: hidden !important;
    text-overflow: ellipsis !important;
    border: 1px solid #17a2b8 !important;
    box-sizing: border-box !important;
}

.fail-button-blue:hover {
    background-color: #138496 !important;
    border-color: #117a8b !important;
}

/* ==== Row Dividers in Project Table ==== */
tr.project {
    border-bottom: 2px solid black !important;
}

tr.project td,
tr.project th {
    padding-top: 8px !important;
    padding-bottom: 8px !important;
}

/* ==== Count and Percentage Styling ==== */
#test_count > span > span,
.count-number,
.count-sup {
    font-size: 1.4rem !important;
}

.count-number {
    vertical-align: middle;
    font-size: 1.65rem;
}

.count-sup {
    vertical-align: super;
}

.icon-span {
    font-size: 2.2rem;
    margin-left: 5px;
}

.percent-mark {
    display: inline-block;
    background-color: #FF8C00;
    color: white;
    padding: 0.1rem 0.3rem;
    border-radius: 0.25rem;
    font-size: 0.8rem;
    min-width: 45px;
    text-align: center;
}

/* ==== Difference Count ==== */
.low-difference-count {
    font-size: 0.8em;
    color: #909090;
    font-weight: bold;
    margin-top: -20px;
}

/* ==== Adjust Suite Column Width ==== */
table#projects th:first-child,
table#projects td:first-child,
#suite th:first-child,
#suite td:first-child {
    max-width: 150px !important;
    width: 150px !important;
    white-space: nowrap !important;
    overflow: hidden !important;
    text-overflow: ellipsis !important;
}

table#projects th:first-child a,
#suite th:first-child a {
    display: block !important;
    max-width: 100% !important;
    overflow: hidden !important;
    text-overflow: ellipsis !important;
    white-space: nowrap !important;
}

table#projects th:first-child *,
table#projects td:first-child *,
#suite th:first-child *,
#suite td:first-child * {
    white-space: nowrap !important;
    overflow: hidden !important;
    text-overflow: ellipsis !important;
}

.download-button {
    width: 250px;
    height: 60px;
    background-color: #28a745;
    color: white;
    font-size: 1.75rem;
    font-weight: 600;
    border: none;
    border-radius: 6px;
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    margin: 4px;
    transition: background-color 0.2s ease-in-out;
}

.download-button:hover {
    background-color: #218838;
}

td#test_count {
    padding-left: 8px !important;
    padding-right: 8px !important;
}

#combined-filters {
    padding-left: 0;
    margin-bottom: 10px;
    border-bottom: 1px solid #ddd;
    display: flex;
    flex-wrap: wrap;
    align-items: flex-end;
    gap: 20px;
}

#environment-tabs {
    margin: 0;
    padding-left: 0;
    border-bottom: none !important;
    display: flex;
    flex-wrap: wrap;
    align-items: flex-end;
}

#category-filters {
    margin: 0 !important;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
}

.category-label {
    margin-left: 0;
    margin-right: 10px;
}

/* ==== Table Header Color Override ==== */
table#projects thead th,
table#projects tr:first-child th,
.table-header,
thead th {
    background-color: #008080 !important;
    color: white !important;
}

/* ==== Set as Baseline Button Styling ==== */
.baseline-button-success,
button.baseline-button-success,
.btn.baseline-button-success,
a.baseline-button-success {
    background-color: #28a745 !important;
    color: white !important;
    border-color: #28a745 !important;
    border: 2px solid #28a745 !important;
}

.baseline-button-success:hover,
button.baseline-button-success:hover,
.btn.baseline-button-success:hover,
a.baseline-button-success:hover {
    background-color: #218838 !important;
    border-color: #1e7e34 !important;
}

.baseline-notification {
    position: fixed;
    top: 20px;
    right: 20px;
    background-color: #28a745;
    color: white;
    padding: 15px 20px;
    border-radius: 5px;
    box-shadow: 0 2px 10px rgba(0,0,0,0.2);
    z-index: 10000;
    font-size: 14px;
    font-weight: bold;
    opacity: 0;
    transform: translateX(100%);
    transition: all 0.3s ease;
}

.baseline-notification.show {
    opacity: 1;
    transform: translateX(0);
}

/* ==== Difference Score Styling ==== */
.diff-score-low {
    color: #ff8c00 !important;
    font-size: 1.8rem !important;
    font-weight: bold !important;
}

.diff-score-high {
    color: #dc3545 !important;
    font-size: 2.0rem !important;
    font-weight: bold !important;
}

.diff-icon {
    width: 32px;
    height: 32px;
    vertical-align: middle;
    margin-right: 5px;
}

/* ==== Environment and Product Colors ==== */
.env-stage {
    color: #9932cc !important; /* darkviolet */
}

.env-stable {
    color: #ff8c00 !important; /* darkorange */
}

.product-x-bees {
    color: #ff8c00 !important; /* darkorange */
}

.product-x-hoppers {
    color: #006400 !important; /* darkgreen */
}

.product-collaboration {
    color: #00008b !important; /* darkblue */
}

/* ==== Test Count Colors ==== */
.count-warning {
    color: #ff8c00 !important;
    font-weight: bold !important;
}

.count-excess {
    color: #ff8c00 !important;
    font-weight: bold !important;
}

/* ==== Date and Project Age Styling ==== */
.date-old {
    color: red !important;
    font-weight: bold !important;
}

.project-old {
    background-color: #FDF5E6 !important;
}

.project-very-old {
    background-color: #D3D3D3 !important;
}

/* ==== New Baseline Styling ==== */
.new-baseline-link {
    color: red !important;
}

/* ==== Modal Display States ==== */
.modal-show {
    display: block !important;
}

.modal-hide {
    display: none !important;
}

/* ==== Utility Classes ==== */
.flex-container {
    display: flex !important;
    flex-wrap: wrap !important;
    align-items: center !important;
    margin-top: 10px !important;
    margin-bottom: 10px !important;
}

.hidden-element {
    display: none !important;
}

.column-name-width {
    max-width: 250px !important;
    width: 250px !important;
    white-space: nowrap !important;
    overflow: hidden !important;
    text-overflow: ellipsis !important;
}

.column-suite-width {
    max-width: 350px !important;
    width: 350px !important;
    white-space: normal !important;
    overflow: hidden !important;
    text-overflow: ellipsis !important;
}

.column-last-run-width {
    width: 600px !important;
    min-width: 550px !important;
}

/* ==== Split Cell Styling ==== */
.split-cell-container {
    display: flex !important;
    width: 100% !important;
    min-height: 50px !important;
}

.split-cell-container > div:first-child {
    flex: 0 0 200px !important;
    border-right: 1px solid #ddd !important;
    padding-right: 8px !important;
    display: flex !important;
    flex-direction: column !important;
    justify-content: center !important;
}

.split-cell-container > div:last-child {
    flex: 1 !important;
    padding-left: 8px !important;
    display: flex !important;
    flex-wrap: wrap !important;
    align-items: center !important;
    gap: 2px !important;
}

/* ==== Date Info Header Styling ==== */
.date-info-header {
    background-color: #008080 !important;
    color: white !important;
    padding: 8px !important;
    width: 600px !important;
    min-width: 550px !important;
}

.date-info-header > div {
    display: flex !important;
    width: 100% !important;
}

.label-minimal-padding {
    padding: 1px 1px !important;
}



/* ==== Compare Button Styling ==== */
.compare-button {
    background-color: #ffc107 !important;
    color: #212529 !important;
    border-color: #ffc107 !important;
    min-width: 140px !important;
    width: 140px !important;
    height: 37px !important;
    line-height: 37px !important;
    padding: 0 8px !important;
    margin: 2px !important;
    border-radius: 4px !important;
    font-size: 16px !important;
    font-weight: bold !important;
    text-align: center !important;
    cursor: pointer !important;
    transition: all 0.2s ease !important;
    display: inline-block !important;
    text-decoration: none !important;
    border: 1px solid #ffc107 !important;
    box-sizing: border-box !important;
}

.compare-button:hover {
    background-color: #e0a800 !important;
    border-color: #d39e00 !important;
    color: #212529 !important;
}



/* ==== Baseline Button Styling ==== */
button[value*="baseline"],
input[value*="baseline"],
.test__set-baseline {
    background-color: #6c757d !important;
    color: white !important;
    border-color: #6c757d !important;
    min-width: 140px !important;
    width: 140px !important;
    height: 37px !important;
    line-height: 37px !important;
    padding: 0 8px !important;
    margin: 2px !important;
    border-radius: 4px !important;
    font-size: 16px !important;
    font-weight: bold !important;
    text-align: center !important;
    cursor: pointer !important;
    transition: all 0.2s ease !important;
    display: inline-block !important;
    text-decoration: none !important;
    border: 1px solid #6c757d !important;
    box-sizing: border-box !important;
}

button[value*="baseline"]:hover,
input[value*="baseline"]:hover,
.test__set-baseline:hover {
    background-color: #5a6268 !important;
    border-color: #545b62 !important;
}

/* ==== Unified Button Classes for Test Results ==== */
.test-compare-button {
    background-color: #ffc107 !important;
    color: #212529 !important;
    border-color: #ffc107 !important;
    width: 120px !important;
    height: 37px !important;
    line-height: 37px !important;
    padding: 0 8px !important;
    margin: 2px !important;
    border-radius: 4px !important;
    font-size: 16px !important;
    font-weight: bold !important;
    text-align: center !important;
    cursor: pointer !important;
    transition: all 0.2s ease !important;
    display: inline-block !important;
    text-decoration: none !important;
    border: 1px solid #ffc107 !important;
    box-sizing: border-box !important;
}

.test-compare-button:hover {
    background-color: #e0a800 !important;
    border-color: #d39e00 !important;
}

.test-fail-button {
    background-color: #dc3545 !important;
    color: white !important;
    border-color: #dc3545 !important;
    width: 140px !important;
    height: 37px !important;
    line-height: 37px !important;
    padding: 0 8px !important;
    margin: 2px !important;
    border-radius: 4px !important;
    font-size: 16px !important;
    font-weight: bold !important;
    text-align: center !important;
    cursor: pointer !important;
    transition: all 0.2s ease !important;
    display: inline-block !important;
    text-decoration: none !important;
    border: 1px solid #dc3545 !important;
    box-sizing: border-box !important;
}

.test-fail-button:hover {
    background-color: #c82333 !important;
    border-color: #bd2130 !important;
}

.test-baseline-button {
    background-color: #6c757d !important;
    color: white !important;
    border-color: #6c757d !important;
    width: 140px !important;
    height: 37px !important;
    line-height: 37px !important;
    padding: 0 8px !important;
    margin: 2px !important;
    border-radius: 4px !important;
    font-size: 16px !important;
    font-weight: bold !important;
    text-align: center !important;
    cursor: pointer !important;
    transition: all 0.2s ease !important;
    display: inline-block !important;
    text-decoration: none !important;
    border: 1px solid #6c757d !important;
    box-sizing: border-box !important;
}

.test-baseline-button:hover {
    background-color: #5a6268 !important;
    border-color: #545b62 !important;
}

/* Low diff button styling */
.low-diff-button {
    background-color: #C8A2C8 !important;
    border: 1px solid #C8A2C8 !important;
    color: white !important;
    padding: 0 8px !important;
    border-radius: 6px !important;
    text-decoration: none !important;
    font-size: 1.65rem !important;
    font-weight: bold !important;
    display: inline-block !important;
    margin: 2px !important;
    transition: all 0.2s ease !important;
    white-space: nowrap !important;
    overflow: hidden !important;
    text-overflow: ellipsis !important;
    width: 50px !important;
    height: 40px !important;
    line-height: 40px !important;
    text-align: center !important;
    vertical-align: middle !important;
    box-sizing: border-box !important;
}

.low-diff-button:hover {
    background-color: #C8A2C8 !important;
    border-color: #C8A2C8 !important;
    color: white !important;
    text-decoration: none !important;
}

/* ==== Dynamic Styles for JavaScript Created Elements ==== */
.dynamic-split-container {
    display: flex !important;
    width: 100% !important;
    min-height: 50px !important;
}

.dynamic-date-section {
    flex: 0 0 200px !important;
    border-right: 1px solid #ddd !important;
    padding-right: 8px !important;
    display: flex !important;
    flex-direction: column !important;
    justify-content: center !important;
}

.dynamic-buttons-section {
    flex: 1 !important;
    padding-left: 8px !important;
    display: flex !important;
    flex-wrap: wrap !important;
    align-items: center !important;
    gap: 2px !important;
}

.dynamic-last-run-cell {
    width: 600px !important;
    min-width: 550px !important;
    padding: 4px !important;
}

.dynamic-header-style {
    background-color: #008080 !important;
    color: white !important;
    padding: 8px !important;
    width: 600px !important;
    min-width: 550px !important;
}

.dynamic-header-content {
    display: flex !important;
    width: 100% !important;
}

.dynamic-header-date {
    flex: 0 0 200px !important;
    text-align: center !important;
    border-right: 1px solid #ddd !important;
    padding-right: 8px !important;
}

.dynamic-header-results {
    flex: 1 !important;
    text-align: center !important;
    padding-left: 8px !important;
}

.dynamic-notification {
    position: fixed !important;
    top: 20px !important;
    right: 20px !important;
    background-color: #28a745 !important;
    color: white !important;
    padding: 15px 20px !important;
    border-radius: 5px !important;
    box-shadow: 0 2px 10px rgba(0,0,0,0.2) !important;
    z-index: 10000 !important;
    font-size: 14px !important;
    font-weight: bold !important;
    opacity: 0 !important;
    transform: translateX(100%) !important;
    transition: all 0.3s ease !important;
}

.dynamic-notification.show {
    opacity: 1 !important;
    transform: translateX(0) !important;
}

.dynamic-baseline-button {
    background-color: #28a745 !important;
    border-color: #28a745 !important;
    color: white !important;
}

.flex-display {
    display: flex !important;
}

.border-bottom-black {
    border-bottom: 2px solid black !important;
}

.progress-bar {
    width: 0% !important;
}

/* ==== Mass baseline editing (runs page) ==== */
.filters.mass-editing-filters {
    display: flex !important;
    flex-wrap: wrap !important;
    align-items: center !important;
}

.filters.mass-editing-filters form {
    margin-right: auto !important; /* форма слева */
}

.mass-editing-controls {
    display: inline-flex !important;
    align-items: center !important;
    gap: 8px !important;
    margin-left: auto !important; /* кнопки справа */
}

/* hide helper: beats existing display:... !important */
.mass-editing-controls [hidden] {
    display: none !important;
}

/* кнопки в фильтрах: не переносить текст и не фиксировать узкую ширину */
.mass-editing-controls button.test-compare-button,
.mass-editing-controls button.test-fail-button,
.mass-editing-controls button.test-baseline-button {
    white-space: nowrap !important;
    width: auto !important;
    padding: 0 12px !important;
}

.mass-editing-controls button.test-compare-button {
    min-width: 140px !important;
}

.mass-editing-controls button.test-fail-button {
    min-width: 160px !important;
}

.mass-editing-controls button.test-baseline-button {
    min-width: 120px !important;
}

.mass-baseline-label {
    display: inline-flex !important;
    align-items: center !important;
    gap: 6px !important;
    margin-left: 10px !important;
    font-size: 13px !important;
    font-weight: 600 !important;
    cursor: pointer !important;
    user-select: none !important;
}

.mass-baseline-label input[type="checkbox"] {
    transform: scale(1.05);
}

.mass-editing-hud {
    position: fixed !important;
    left: 16px !important;
    bottom: 16px !important;
    padding: 8px 10px !important;
    border-radius: 6px !important;
    background: rgba(0, 0, 0, 0.78) !important;
    color: #fff !important;
    font-size: 14px !important;
    font-weight: 700 !important;
    z-index: 10005 !important;
    pointer-events: none !important;
}
`);

    let totalTests = {
        //Updated 1.1.26
        '------XBS-----': 0,
        'monitoring chats list x-bees WEB': 7,
        'basic visual regression testing x-bees WEB': 453,
        'DMbasic visual regression testing x-bees WEB': 453,
        'pseudo localization basic visual regression testing x-bees WEB': 453,
        'basic visual regression testing x-bees iOS': 18,
        'DM basic visual regression testing x-bees iOS': 18,
        'basic visual regression testing simulator x-bees iOS': 185,
        'DM basic visual regression testing simulator x-bees iOS': 185,
        'big font size basic visual regression testing simulator x-bees iOS': 185,
        'pseudo localization basic visual regression testing x-bees iOS': 185,
        'basic visual regression testing x-bees Android': 245,
        'DM basic visual regression testing x-bees Android': 245,
        'big font size basic visual regression testing x-bees Android': 245,
        'pseudo localization basic visual regression testing x-bees Android': 245,
        'storybook visual regression testing x-bees Android': 77,
        'Kite basic visual regression testing (chrome) x-bees KITE': 73,
        'Kite basic visual regression testing (firefox) x-bees KITE': 72,
        'Kite basic visual regression testing (safari) x-bees KITE': 72,
        'chrome Kite mobile basic visual regression testing x-bees mobile KITE': 41,
        'safari Kite mobile basic visual regression testing x-bees mobile KITE': 27,
        'locales test suite x-bees WEB': 120,
        'locales test suite x-bees Android': 70,
        '------XHOPS------': 0,
        'monitoring chats list x-hoppers WEB': 7,
        'basic visual regression testing x-hoppers WEB': 453,
        'DMbasic visual regression testing x-hoppers WEB': 453,
        'basic visual regression testing x-hoppers iOS': 18,
        'basic visual regression testing simulator x-hoppers iOS': 186,
        'basic visual regression testing x-hoppers Android': 245,
        'Kite basic visual regression testing (chrome) x-hoppers KITE': 73,
        'chrome Kite mobile basic visual regression testing x-hoppers mobile KITE': 41,
        'safari Kite mobile basic visual regression testing x-hoppers mobile KITE': 27,
        '------C7------': 0,
        'basic visual regression testing collaboration WEB': 374,
        'DMbasic visual regression testing collaboration WEB': 374,
        'basic visual regression testing collaboration iOS': 18,
        'basic visual regression testing simulator collaboration iOS': 184,
        'basic visual regression testing collaboration Android': 240,
        'Kite basic visual regression testing (chrome) collaboration KITE': 60,
        'chrome Kite mobile basic visual regression testing collaboration mobile KITE': 37,
        'safari Kite mobile basic visual regression testing collaboration mobile KITE': 27,
        '------WEBSITE-----': 0,
        'widget: visual regression testing WEBSITE' : 24,
        'website: visual regression testing WEBSITE' : 64,
        'expand widget: visual regression testing WEBSITE': 11,
        'link-preview: visual regression testing WEBSITE': 15
    };

    // Global variables
    let text, score,
        lowDiffCount = 0;
    let lowDiffCountUniq = 0;
    let testLowDiffTitle = '';
    let testNamePrev = '';

    // Tab functionality
    function addProductTabs() {
        if (window.location.pathname === '/projects') {

            const tabContainer = document.createElement('div');
            tabContainer.id = 'product-tabs';

            const products = [
                { name: 'All', class: 'all-tab' },
                { name: 'x-bees', class: 'x-bees-tab' },
                { name: 'x-hoppers', class: 'x-hoppers-tab' },
                { name: 'collaboration', class: 'collaboration-tab' },
                { name: 'Website', class: 'website-tab' }
            ];

            products.forEach(product => {
                const tab = document.createElement('button');
                tab.innerText = product.name;
                tab.dataset.product = product.name.toLowerCase();
                tab.classList.add('product-tab', product.class);

                if (product.name === 'All') {
                    tab.classList.add('active-tab');
                }

                tab.addEventListener('click', function() {
                    const envTabs = document.getElementById('environment-tabs');
                    if (envTabs) {
                        envTabs.classList.add('flex-display');

                        const activeEnvTab = document.querySelector('.environment-tab.active-env-tab');
                        if (!activeEnvTab) {
                            const allEnvTab = document.querySelector('.environment-tab[data-env="all environments"]');
                            if (allEnvTab) allEnvTab.classList.add('active-env-tab');
                        }
                    }

                    filterProjectsByProduct(this.dataset.product);

                    document.querySelectorAll('.product-tab').forEach(t => {
                        t.classList.remove('active-tab');
                    });

                    this.classList.add('active-tab');
                });

                tabContainer.appendChild(tab);
            });

            const projectsHeader = document.querySelector('h1');
            if (projectsHeader) {
                projectsHeader.parentNode.insertBefore(tabContainer, projectsHeader.nextSibling);

                addEnvironmentTabs(projectsHeader.parentNode, tabContainer);
                addCategoryFilters(projectsHeader.parentNode);

                const envTabs = document.getElementById('environment-tabs');
                if (envTabs) {
                    envTabs.classList.add('flex-display');
                }
            }
        }
    }

    function addEnvironmentTabs(parentNode, tabContainer) {
        const envTabContainer = document.createElement('div');
        envTabContainer.id = 'environment-tabs';

        const environments = [
            { name: 'All Environments', class: '' },
            { name: '🏗️ Stage', class: 'stage-tab' },
            { name: '🏭 Stable', class: 'stable-tab' }
        ];

        environments.forEach(env => {
            const tab = document.createElement('button');
            tab.innerText = env.name;
            tab.dataset.env = env.name.toLowerCase();
            tab.classList.add('environment-tab');
            if (env.class) tab.classList.add(env.class);

            if (env.name === 'All Environments') {
                tab.classList.add('active-env-tab');
            }

            tab.addEventListener('click', function() {
                filterProjectsByEnvironment(this.dataset.env);

                document.querySelectorAll('.environment-tab').forEach(t => {
                    t.classList.remove('active-env-tab');
                });

                this.classList.add('active-env-tab');
            });

            envTabContainer.appendChild(tab);
        });

        document.body.appendChild(envTabContainer);
    }

    function addCategoryFilters(parentNode) {
        const categoryContainer = document.createElement('div');
        categoryContainer.id = 'category-filters';

        const labelSpan = document.createElement('span');
        labelSpan.classList.add('category-label');
        labelSpan.innerText = 'Filter by platform:';
        categoryContainer.appendChild(labelSpan);

        const categories = [
            { name: 'All', class: '' },
            { name: 'WEB', class: 'category-web' },
            { name: 'KITE', class: 'category-kite' },
            { name: 'Android', class: 'category-android' },
            { name: 'iOS', class: 'category-ios' },
            { name: 'Mobile KITE', class: 'category-mobile-kite' }
        ];

        categories.forEach(category => {
            const button = document.createElement('button');
            button.innerText = category.name;
            button.dataset.category = category.name.toLowerCase();
            button.classList.add('category-button');
            if (category.class) button.classList.add(category.class);

            if (category.name === 'All') {
                button.classList.add('active-category');
            }

            button.addEventListener('click', function() {
                filterProjectsByCategory(this.dataset.category);

                document.querySelectorAll('.category-button').forEach(b => {
                    b.classList.remove('active-category');
                });

                this.classList.add('active-category');
            });

            categoryContainer.appendChild(button);
        });

        document.body.appendChild(categoryContainer);
    }

    function filterProjectsByProduct(product) {
        const projects = document.querySelectorAll('tr.project');
        const activeEnvTab = document.querySelector('.environment-tab.active-env-tab');
        const activeCategoryTab = document.querySelector('.category-button.active-category');

        const envFilter = activeEnvTab ? activeEnvTab.dataset.env : 'all environments';
        const categoryFilter = activeCategoryTab ? activeCategoryTab.dataset.category : 'all';

        projects.forEach(project => {
            const projectNameElement = project.querySelector('th');
            if (!projectNameElement) return;

            const projectName = projectNameElement.innerText.toLowerCase();
            let showProject = true;

            if (product !== 'all' && !projectName.includes(product)) {
                showProject = false;
            }

            if (product !== 'all' && envFilter !== 'all environments') {
                if (!projectName.includes(envFilter.toLowerCase())) {
                    showProject = false;
                }
            }

            if (categoryFilter !== 'all') {
                if (!projectName.includes(categoryFilter.toLowerCase())) {
                    showProject = false;
                }
            }

            if (showProject) {
                project.classList.remove('hidden-project');
            } else {
                project.classList.add('hidden-project');
            }
        });
    }

    function filterProjectsByEnvironment(environment) {
        const activeProductTab = document.querySelector('.product-tab.active-tab');
        if (!activeProductTab) return;

        const activeCategoryTab = document.querySelector('.category-button.active-category');
        const product = activeProductTab.dataset.product?.toLowerCase() || 'all';
        const categoryFilter = activeCategoryTab?.dataset.category?.toLowerCase() || 'all';
        const env = environment.toLowerCase();

        document.querySelectorAll('tr.project').forEach(project => {
            const projectNameElement = project.querySelector('th');
            if (!projectNameElement) return;

            const projectName = projectNameElement.innerText.toLowerCase();
            const projectEnv = project.dataset.project?.toLowerCase() || '';

            if (product !== 'all' && !projectName.includes(product)) {
                project.classList.add('hidden-project');
                return;
            }

            if (env !== 'all environments') {
                const isStage = env.includes('stage');
                const isStable = env.includes('stable');

                if ((isStage && !projectEnv.includes('stage')) || (isStable && !projectEnv.includes('stable'))) {
                    project.classList.add('hidden-project');
                    return;
                }
            }

            if (categoryFilter !== 'all') {
                const isMobileKite = categoryFilter === 'mobile kite';
                const categoryMatch = isMobileKite
                ? projectName.includes('mobile') && projectName.includes('kite')
                : projectName.includes(categoryFilter);

                if (!categoryMatch) {
                    project.classList.add('hidden-project');
                    return;
                }
            }

            project.classList.remove('hidden-project');
        });
    }

    function filterProjectsByCategory(category) {
        const activeProductTab = document.querySelector('.product-tab.active-tab');
        const activeEnvTab = document.querySelector('.environment-tab.active-env-tab');

        if (!activeProductTab) return;

        const product = activeProductTab.dataset.product;
        const environment = activeEnvTab ? activeEnvTab.dataset.env : 'all environments';

        const projects = document.querySelectorAll('tr.project');

        projects.forEach(project => {
            const projectNameElement = project.querySelector('th');
            if (!projectNameElement) return;

            const projectName = projectNameElement.innerText.toLowerCase();
            let showProject = true;

            if (product !== 'all' && !projectName.includes(product)) {
                showProject = false;
            }

            if (environment !== 'all environments' && !projectName.includes(environment.toLowerCase())) {
                showProject = false;
            }

            if (category !== 'all') {
                if (category === 'mobile kite') {
                    if (!projectName.includes('mobile') || !projectName.includes('kite')) {
                        showProject = false;
                    }
                } else if (!projectName.includes(category)) {
                    showProject = false;
                }
            }

            if (showProject) {
                project.classList.remove('hidden-project');
            } else {
                project.classList.add('hidden-project');
            }
        });
    }

    function combineFilterElements() {
        const productTabs = document.getElementById('product-tabs');
        const envTabs = document.getElementById('environment-tabs');
        const categoryFilters = document.getElementById('category-filters');

        if (productTabs && envTabs && categoryFilters) {
            const combinedContainer = document.createElement('div');
            combinedContainer.id = 'combined-filters';

            if (productTabs.parentNode) {
                productTabs.parentNode.insertBefore(combinedContainer, productTabs.nextSibling);
            }
            combinedContainer.appendChild(envTabs);
            combinedContainer.appendChild(categoryFilters);
        }
    }


    function addRowDividers() {
        const projectRows = document.querySelectorAll('tr.project');
        projectRows.forEach(row => {
            row.classList.add('border-bottom-black');
        });
    }

    function hideProjectsHeader() {
        const projectsHeader = document.querySelector('h1');
        if (projectsHeader && projectsHeader.textContent.includes('Projects')) {
            projectsHeader.classList.add('hidden-element');
        }
    }

    function adjustColumnWidths() {
        const projectsTable = document.getElementById('projects');
        if (!projectsTable) return;

        const projectRows = projectsTable.querySelectorAll('tr.project');
        if (!projectRows.length) return;

        const headerRow = projectsTable.querySelector('thead tr');
        if (headerRow && !headerRow.querySelector('.date-info-header')) {
            const lastRunHeader = headerRow.querySelector('th:nth-child(3)');
            if (lastRunHeader) {
                lastRunHeader.innerHTML = `
                    <div class="dynamic-header-content">
                        <div class="dynamic-header-date">Дата запуска</div>
                        <div class="dynamic-header-results">Результаты тестов</div>
                    </div>
                `;
                lastRunHeader.classList.add('date-info-header', 'dynamic-header-style');
            }
        }

        projectRows.forEach(row => {
            const lastRunCell = row.querySelector('td:nth-child(3)');
            if (lastRunCell && !lastRunCell.querySelector('.split-cell-container')) {
                const dateInfo = lastRunCell.querySelector('span.text-muted');
                const dateTimeElement = lastRunCell.querySelector('.date-time');
                const buttons = lastRunCell.querySelectorAll('.label--fail, .label--pass, .label--warning, a.total-tests, .low-diff-button');
                const splitContainer = document.createElement('div');
                splitContainer.classList.add('split-cell-container', 'dynamic-split-container');

                const dateSection = document.createElement('div');
                dateSection.classList.add('dynamic-date-section');

                if (dateInfo) {
                    dateSection.appendChild(dateInfo.cloneNode(true));
                }
                if (dateTimeElement) {
                    dateSection.appendChild(dateTimeElement.cloneNode(true));
                }

                const buttonsSection = document.createElement('div');
                buttonsSection.classList.add('dynamic-buttons-section');

                buttons.forEach(button => {
                    button.classList.add('label-minimal-padding');
                    buttonsSection.appendChild(button);
                });

                splitContainer.appendChild(dateSection);
                splitContainer.appendChild(buttonsSection);

                lastRunCell.innerHTML = '';
                lastRunCell.appendChild(splitContainer);
                lastRunCell.classList.add('dynamic-last-run-cell');
            }
        });

        const nameHeaders = projectsTable.querySelectorAll('th:first-child, td:first-child');
        nameHeaders.forEach(header => {
            header.classList.add('column-name-width');
        });

        const suiteHeaders = projectsTable.querySelectorAll('th:nth-child(2), td:nth-child(2)');
        suiteHeaders.forEach(header => {
            header.classList.add('column-suite-width');
        });
    }

    function highlightProjectAge() {
        const projectRows = document.querySelectorAll('tr.project');

        projectRows.forEach(row => {
            const dateTimeSpan = row.querySelector('td span.text-muted');
            if (!dateTimeSpan) return;

            const dateText = dateTimeSpan.innerText;

            if ((dateText.includes('days ago') && parseInt(dateText, 10) > 1) || dateText.includes('month ago') || dateText.includes('months ago') || dateText.includes('year ago') || dateText.includes('years ago')) {
                row.classList.add('old-project-row');
            }

            if (dateText.includes('days ago') && parseInt(dateText, 10) > 3) {
                row.classList.add('very-old-project-row');
            }
        });
    }



    function addLowDiffButtons() {
        // Работаем на странице /projects и на страницах с таблицами Latest runs
        if (window.location.pathname === '/projects' ||
            window.location.pathname.includes('/suites/') ||
            document.querySelector('table') ||
            document.querySelector('tr.project')) {

            // Ищем строки проектов в разных таблицах
            const projectRows = document.querySelectorAll('tr.project, tr[data-project], tbody tr');

            projectRows.forEach((row, index) => {
                // Пропускаем строки заголовков
                if (row.querySelector('th') && !row.querySelector('td')) {
                    return;
                }

                let buttonsSection = row.querySelector('.split-cell-container > div:last-child');
                if (!buttonsSection) {
                    // Ищем последнюю ячейку с кнопками
                    const cells = row.querySelectorAll('td');
                    if (cells.length > 0) {
                        buttonsSection = cells[cells.length - 1];
                    }
                }

                if (!buttonsSection || buttonsSection.querySelector('.low-diff-button')) {
                    return;
                }

                let allLinks = buttonsSection.querySelectorAll('a');
                let failedLink = null;
                for (let link of allLinks) {
                    const text = link.textContent.toLowerCase().trim();
                    const computedStyle = window.getComputedStyle(link);
                    const backgroundColor = computedStyle.backgroundColor;

                    if (text.includes('failed') ||
                        backgroundColor.includes('rgb(220, 53, 69)') ||
                        backgroundColor.includes('rgb(217, 83, 79)') ||
                        link.classList.contains('label--fail')) {
                        failedLink = link;
                        break;
                    }
                }

                if (!failedLink && allLinks.length > 0) {
                    failedLink = allLinks[allLinks.length - 1];
                }

                if (failedLink && failedLink.href) {
                    const url = new URL(failedLink.href);
                    url.searchParams.set('status', 'pass');
                    url.searchParams.set('diff', 'low-diff');

                    const lowDiffButton = document.createElement('a');
                    lowDiffButton.href = url.toString();
                    lowDiffButton.textContent = 'LD';
                    lowDiffButton.className = 'low-diff-button label-minimal-padding';
                    lowDiffButton.title = 'Показать тесты с низкими различиями (Low diff)';

                    buttonsSection.appendChild(lowDiffButton);
                }
            });
        }
    }



    function setupBaselineButtons() {
        function isMassEditingControl(el) {
            if (!el) return false;
            try {
                return el.hasAttribute('data-mass-editing-control') || !!el.closest('.mass-editing-controls');
            } catch (e) {
                return false;
            }
        }

        function handleBaselineClick(event) {
            // Не трогаем элементы управления массовым режимом
            if (isMassEditingControl(this)) return;
            this.classList.add('baseline-button-success', 'dynamic-baseline-button');
            showBaselineNotification();
            event.stopPropagation();
        }

        function findAndSetupBaselineButtons() {
            const selectors = [
                'button',
                '.btn',
                'input[type="button"]',
                'input[type="submit"]',
                'a[role="button"]',
                'a.btn',
                '[onclick*="baseline"]',
                '[onclick*="Baseline"]'
            ];

            selectors.forEach(selector => {
                const buttons = document.querySelectorAll(selector);
                buttons.forEach(button => {
                    if (isMassEditingControl(button)) return;
                    const text = (button.textContent || button.value || button.title || button.getAttribute('onclick') || '').toLowerCase();
                    if ((text.includes('baseline') || text.includes('set as baseline')) && !button.hasAttribute('data-baseline-listener')) {
                        button.addEventListener('click', handleBaselineClick);
                        button.setAttribute('data-baseline-listener', 'true');
                    }
                });
            });
        }

        findAndSetupBaselineButtons();

        const observer = new MutationObserver(function(mutations) {
            mutations.forEach(function(mutation) {
                mutation.addedNodes.forEach(function(node) {
                    if (node.nodeType === 1) {
                        if (isMassEditingControl(node)) return;
                        const text = (node.textContent || node.value || node.title || '').toLowerCase();
                        if ((node.tagName === 'BUTTON' || node.classList.contains('btn') || node.tagName === 'A') &&
                            (text.includes('baseline') || text.includes('set as baseline')) && !node.hasAttribute('data-baseline-listener')) {
                            node.addEventListener('click', handleBaselineClick);
                            node.setAttribute('data-baseline-listener', 'true');
                        }

                        if (node.querySelectorAll) {
                            findAndSetupBaselineButtons();
                        }
                    }
                });
            });
        });

        observer.observe(document.body, {
            childList: true,
            subtree: true
        });

        setInterval(findAndSetupBaselineButtons, 2000);
    }

    function showBaselineNotification() {
        const existingNotifications = document.querySelectorAll('.baseline-notification');
        existingNotifications.forEach(notif => notif.remove());

        const notification = document.createElement('div');
        notification.classList.add('baseline-notification', 'dynamic-notification');
        notification.innerHTML = '✅ Baseline успешно применен!';
        document.body.appendChild(notification);

        setTimeout(() => notification.classList.add('show'), 100);
        setTimeout(() => {
            notification.classList.remove('show');
            setTimeout(() => notification.remove(), 300);
        }, 3000);
    }

    // Selectors
    const differenceMarkElements = document.querySelectorAll('span.test__diff.text-muted');
    const testimgs = document.querySelectorAll('a.test__image');
    const baselineimgs = document.querySelectorAll('a.baseline__image');

    function addDifferencesMarks() {
        let testRow, testName, testEnv;
        const positive = '<img src="https://github.githubassets.com/images/icons/emoji/unicode/2714.png" width="32" height="32">';

        for (let elem of differenceMarkElements) {
            text = elem.innerHTML;
            testRow = elem.parentElement.parentElement;
            if (elem.innerHTML === 'No difference') {
                elem.innerHTML = positive + text;
                testRow.classList.add('pass');
            } else {
                score = parseFloat(elem.innerHTML);
                if (score <= 0.05) {
                    elem.innerHTML = `<img src="https://github.githubassets.com/images/icons/emoji/unicode/2754.png" class="diff-icon"><span class="diff-score-low">${score}%</span><br />${text}`;
                } else if (score > 0.05) {
                    elem.innerHTML = `<img src="https://github.githubassets.com/images/icons/emoji/unicode/2716.png" class="diff-icon"><span class="diff-score-high">${score}%</span><br />${text}`;
                }
                if (score < 0.10) {
                    lowDiffCount += 1;
                    const testNameElement = testRow.querySelector('.test__name');
                    const testEnvElement = testRow.querySelector('th p');

                    if (testNameElement && testEnvElement) {
                        testName = testNameElement.innerText;
                        testEnv = testEnvElement.innerText;

                        if (testName !== testNamePrev) {
                            testLowDiffTitle = `${testLowDiffTitle} ${testName} - ${testEnv}\n`;
                            testNamePrev = testName;
                            lowDiffCountUniq += 1;
                        }
                    }
                }
                testRow.classList.add('diffed');
            }
        }
    }

    function footerAndHeader() {
        if (window.location.pathname.indexOf('/runs/') !== -1) {
            const h1 = document.querySelector('div h1');
            if (h1) {
                const lowDiffP = document.createElement('p');
                lowDiffP.classList.add('low-difference-count');
                lowDiffP.innerText = `Low diff tests: ${lowDiffCountUniq} / Low diff screenshots: ${lowDiffCount}`;
                lowDiffP.title = testLowDiffTitle;
                insertAfter(lowDiffP, h1);

                addDownloadBlock();
                const containerHeader = $('.container').find('h1');
                const archiveName = containerHeader.length ? containerHeader.text() : 'spectre-export';
                const imageExtension = '.png';

                $('#download-images').on('click', function () {
                    resetMessage();
                    const zip = new JSZip();
                    $('tr.test').each(function () {
                        const $this = $(this);
                        const testImgLink = $this.find('td#image-test a.test__image');
                        if (testImgLink.length) {
                            const url = testImgLink.attr('data-href');
                            const testNameElement = $this.find('span.test__name');
                            const testInfoElement = $this.find('small.text-muted');

                            if (url && testNameElement.length && testInfoElement.length) {
                                const testName = testNameElement.text()
                                .replace(/\s\s+/g, ' ')
                                .replace(/\s+/g, '-');
                                const testInfo = testInfoElement.text()
                                .replace(/\s\s+/g, ' ')
                                .replace(/,/g, '')
                                .replace(/\s+/g, '-');
                                const filename = testName + testInfo;
                                zip.file(filename + imageExtension, urlToPromise(url), { binary: true });
                            }
                        }
                    });

                    zip.generateAsync({ type: 'blob' }, function updateCallback(metadata) {
                        let msg = 'progression : ' + metadata.percent.toFixed(2) + ' %';
                        if (metadata.currentFile) {
                            msg += ', current file = ' + metadata.currentFile;
                        }
                        showMessage(msg);
                        updatePercent(Math.floor(metadata.percent));
                    })
                        .then(function callback(blob) {
                        saveAs(blob, archiveName + '.zip');
                        showMessage('Done! Archive created successfully.');
                    }, function (e) {
                        showError(e);
                    });
                    return false;
                });
            }
        }
    }

    function addModalsToImgs(imgs) {
        if (!imgs || imgs.length === 0) return;

        imgs.forEach(img => {
            if (img && img.href) {
                img.setAttribute('data-href', img.href);
                img.removeAttribute('href');
            }
        });

        let modalWindow = document.createElement('div');
        let imgClass = 'imgModal1';

        if (window.location.pathname.indexOf('projects/website/suites') !== -1) {
            imgClass = 'imgModal2';
        }

        modalWindow.classList.add('modal', imgClass);
        modalWindow.innerHTML = '<div class="modal-content"><span class="close">&times;</span><img class="lightbox" src=""/></div>';
        document.body.appendChild(modalWindow);

        imgs.forEach(element => {
            if (element) {
                element.addEventListener('click', function() {
                    const modal = document.querySelector(`div.${imgClass}`);
                    if (modal) {
                        modal.classList.add('modal-show');
                        modal.classList.remove('modal-hide');
                        const modalimg = modal.querySelector('img');
                        if (modalimg) {
                            modalimg.src = element.getAttribute('data-href');
                        }

                        const close = modal.querySelector('span.close');
                        if (close) {
                            close.onclick = function() {
                                modal.classList.add('modal-hide');
                                modal.classList.remove('modal-show');
                            };
                        }

                        window.onclick = function(event) {
                            if (event.target == modal) {
                                modal.classList.add('modal-hide');
                                modal.classList.remove('modal-show');
                            }
                        };
                    }
                });
            }
        });
    }

    function addVisualComparisonButtons() {
        let pics, picReference, picCurrent;
        let failedTests = document.querySelectorAll('.diffed');

        failedTests.forEach(test => {
            if (!test) return;

            pics = test.querySelectorAll('a.test__image');
            if (!pics || pics.length < 2) return;

            pics.forEach(pic => {
                if (pic) pic.classList.add('diffBtn');
            });

            picReference = pics[0].href;
            picCurrent = pics[1].href;

            if (test.id && picReference && picCurrent) {
                addModalWindows(test.id, picReference, picCurrent, test);
            }
        });

        const failedButtons = document.querySelectorAll('.diffed .test__diff');
        failedButtons.forEach(button => {
            if (!button || !button.parentElement || !button.parentElement.parentElement) return;

            let parentId = button.parentElement.parentElement.id;
            if (parentId) {
                addElementButton(parentId, button);
            }
        });

        setListener();
        setViewers();
    }

    function improveDateTime() {
        let dateElements = document.querySelectorAll('td.text-muted span');
        dateElements.forEach(el => {
            if (!el) return;

            let dateTime = el.getAttribute('Title');
            if (dateTime) {
                addElementPTime(el, dateTime);
            }
        });

        dateElements = document.querySelectorAll('#test_count span.text-muted span');
        let parentCells = document.querySelectorAll('td#test_count');
        let parentRows = document.querySelectorAll('tr.project');

        dateElements.forEach(function(el, index) {
            if (!el || !parentCells[index]) return;

            let dateTime = el.getAttribute('Title');
            let dateText = el.innerText;
            let row = parentRows[index];

            if (dateText) {
                if (dateText.includes('month ago') || dateText.includes('months ago') || dateText.includes('year ago')) {
                    el.classList.add('date-old');
                    if (row) row.classList.add('project-old');
                } else if (dateText.includes('days ago')) {
                    const daysMatch = dateText.match(/(\d+) days ago/);
                    if (daysMatch && daysMatch[1]) {
                        const days = parseInt(daysMatch[1], 10);
                        if (days > 3) {
                            el.classList.add('date-old');
                            if (row) row.classList.add('project-very-old');
                        }
                    }
                }
            }

            if (dateTime) {
                let newP = document.createElement('p');
                newP.classList.add('date-time');
                newP.innerHTML = '🕒 ' + dateTime;
                parentCells[index].appendChild(newP);
            }
        });
    }

    function envStyle() {
        let nameElements = document.querySelectorAll('.project a');
        nameElements.forEach(el => {
            if (!el) return;

            let content = el.outerText ? el.outerText.toLowerCase() : '';

            if (content.includes('stage')) {
                el.innerHTML = '🏗️ <span class="env-stage">' + el.innerHTML + '</span>';
            } else if (content.includes('prod')) {
                el.innerHTML = '🏢 ' + el.innerHTML;
            } else if (content.includes('stable')) {
                el.innerHTML = '🏭 <span class="env-stable">' + el.innerHTML + '</span>';
            }
        });
    }

    function productStyle() {
        let nameElements = document.querySelectorAll('.project th');
        nameElements.forEach(el => {
            if (!el) return;

            let text = el.innerText ? el.innerText.toLowerCase() : '';

            if (text.includes('x-bees')) {
                el.innerHTML = '<span class="product-x-bees">' + el.innerHTML + '</span>';
            } else if (text.includes('x-hoppers')) {
                el.innerHTML = '<span class="product-x-hoppers">' + el.innerHTML + '</span>';
            } else if (text.includes('collaboration')) {
                el.innerHTML = '<span class="product-collaboration">' + el.innerHTML + '</span>';
            }
        });
    }

    function lowDiffFilter() {
        if ((window.location.pathname.indexOf('/suites/') !== -1) && (window.location.pathname.indexOf('/runs/') !== -1)) {
            let filtersForm = document.querySelector('div.filters form');
            if (!filtersForm) return;

            let selectDiff = document.createElement('select');
            selectDiff.name = 'diff';
            selectDiff.id = 'diff';
            selectDiff.classList.add('form-control');

            let optionAll = document.createElement('option');
            optionAll.value = 'All';
            optionAll.innerText = 'All';

            let optionLowDiff = document.createElement('option');
            optionLowDiff.value = 'low-diff';
            optionLowDiff.innerText = 'Low diff';

            selectDiff.appendChild(optionAll);
            selectDiff.appendChild(optionLowDiff);

            // Проверяем URL-параметры при загрузке страницы
            const urlParams = new URLSearchParams(window.location.search);
            const diffParam = urlParams.get('diff');

            if (diffParam === 'low-diff') {
                selectDiff.value = 'low-diff';
                // Автоматически применяем фильтр
                setTimeout(() => {
                    let tests = document.querySelectorAll('.pass, .test--fail');
                    tests.forEach(test => {
                        if (test) test.remove();
                    });
                }, 1000); // Небольшая задержка для загрузки элементов
            }

            selectDiff.addEventListener('change', function() {
                let tests = document.querySelectorAll('.pass, .test--fail');

                if (this.value === 'low-diff') {
                    tests.forEach(test => {
                        if (test) test.remove();
                    });
                    // Обновляем URL с параметром
                    const newUrl = new URL(window.location);
                    newUrl.searchParams.set('diff', 'low-diff');
                    window.history.pushState({}, '', newUrl);
                } else {
                    // Удаляем параметр из URL и перезагружаем
                    const newUrl = new URL(window.location);
                    newUrl.searchParams.delete('diff');
                    window.location.href = newUrl.toString();
                }
            });

            filtersForm.appendChild(selectDiff);
        }
    }

    function initMassBaselineEditing() {
        const isRunsInSuite =
            window.location.pathname.includes('/suites/') &&
            window.location.pathname.includes('/runs/');
        if (!isRunsInSuite) return;

        const STATE = {
            injected: false,
            mode: 'idle', // idle | scanned
            baselineInputs: [],
            checkboxRecords: [], // { checkbox, label, onChange }
            hudEl: null,
            filtersEl: null,
            controlsEl: null,
            btnMass: null,
            btnSet: null,
            btnCancel: null,
            onMassClick: null,
            onSetClick: null,
            onCancelClick: null,
        };

        function ensureInjected() {
            if (STATE.injected) return true;

            const filters = document.querySelector('div.filters');
            const form = document.querySelector('div.filters form');
            if (!filters || !form) return false;

            STATE.filtersEl = filters;
            filters.classList.add('mass-editing-filters');

            const controls = document.createElement('div');
            controls.className = 'mass-editing-controls';

            const btnMass = document.createElement('button');
            btnMass.type = 'button';
            btnMass.className = 'test-compare-button';
            btnMass.textContent = 'Mass editing';
            btnMass.setAttribute('data-mass-editing-control', 'true');

            const btnSet = document.createElement('button');
            btnSet.type = 'button';
            btnSet.className = 'test-fail-button';
            btnSet.textContent = 'Set baselines';
            btnSet.hidden = true;
            btnSet.setAttribute('data-mass-editing-control', 'true');

            const btnCancel = document.createElement('button');
            btnCancel.type = 'button';
            btnCancel.className = 'test-baseline-button';
            btnCancel.textContent = 'Cancel';
            btnCancel.hidden = true;
            btnCancel.setAttribute('data-mass-editing-control', 'true');

            controls.appendChild(btnMass);
            controls.appendChild(btnSet);
            controls.appendChild(btnCancel);

            form.insertAdjacentElement('afterend', controls);

            STATE.controlsEl = controls;
            STATE.btnMass = btnMass;
            STATE.btnSet = btnSet;
            STATE.btnCancel = btnCancel;

            STATE.onMassClick = () => startMassEditing();
            STATE.onSetClick = () => handleSetBaselines();
            STATE.onCancelClick = () => cleanupToIdle();

            btnMass.addEventListener('click', STATE.onMassClick);
            btnSet.addEventListener('click', STATE.onSetClick);
            btnCancel.addEventListener('click', STATE.onCancelClick);

            STATE.injected = true;
            return true;
        }

        function startMassEditing() {
            if (!ensureInjected()) return;
            if (STATE.mode !== 'idle') return;

            // Standard browser confirm: OK = "Go fast", Cancel = "Cancel"
            const ok = confirm("We can do this fast or slow, it's your call.\nMass editing can permanently change multiple screenshots.");
            if (!ok) return;

            STATE.btnMass.hidden = true;
            STATE.btnSet.hidden = false;
            STATE.btnCancel.hidden = false;

            // По требованию: сканируем сразу при нажатии Mass editing
            scanAndRender();
        }

        function createHudIfNeeded() {
            if (STATE.hudEl) return;
            const hud = document.createElement('div');
            hud.className = 'mass-editing-hud';
            hud.textContent = 'SELECTED DIFFS: 0/0';
            document.body.appendChild(hud);
            STATE.hudEl = hud;
        }

        function updateHud() {
            if (!STATE.hudEl) return;
            const found = STATE.baselineInputs.length;
            const selected = STATE.checkboxRecords.reduce((acc, r) => acc + (r.checkbox.checked ? 1 : 0), 0);
            STATE.hudEl.textContent = `SELECTED DIFFS: ${selected}/${found}`;
        }

        function scanAndRender() {
            // Очищаем возможные хвосты предыдущего скана, если что-то пошло не так
            removeCheckboxesAndHud();

            const inputs = Array.from(document.querySelectorAll('input.test__set-baseline'));
            STATE.baselineInputs = inputs;

            if (!inputs.length) {
                // Standard browser confirm: OK = "I know what I am doing", Cancel = "Cancel"
                confirm("Have you tried turning it off and on again?\nNo changes were detected.");
                cleanupToIdle();
                return false;
            }

            createHudIfNeeded();

            inputs.forEach((baselineInput, idx) => {
                if (!baselineInput || !baselineInput.parentElement) return;
                if (baselineInput.hasAttribute('data-mass-baseline-processed')) return;

                baselineInput.setAttribute('data-mass-baseline-processed', 'true');

                const label = document.createElement('label');
                label.className = 'mass-baseline-label';

                const checkbox = document.createElement('input');
                checkbox.type = 'checkbox';
                checkbox.checked = true;
                checkbox.setAttribute('data-mass-baseline-index', String(idx));

                const textNode = document.createTextNode('Set as a baseline');
                label.appendChild(checkbox);
                label.appendChild(textNode);

                baselineInput.insertAdjacentElement('afterend', label);

                const onChange = () => updateHud();
                checkbox.addEventListener('change', onChange);

                STATE.checkboxRecords.push({ checkbox, label, onChange });
            });

            updateHud();
            STATE.mode = 'scanned';
            return true;
        }

        async function applySelectedBaselines() {
            const selectedIndices = STATE.checkboxRecords
                .filter(r => r.checkbox.checked)
                .map(r => parseInt(r.checkbox.getAttribute('data-mass-baseline-index') || '', 10))
                .filter(n => Number.isFinite(n));

            const selectedInputs = selectedIndices
                .map(i => STATE.baselineInputs[i])
                .filter(Boolean);

            const selectedCount = selectedInputs.length;

            // Standard browser confirm: OK/Cancel
            const ok = confirm("WARNING: That's one way to get your attention…\nProceed only if you’re confident.");
            if (!ok) return;

            // Standard browser input (prompt): require safety phrase
            const phrase = prompt('Type "THEBEAST" for confirmation:', '');
            if (phrase !== 'THEBEAST') {
                alert('Aborted.');
                return;
            }

            // Блокируем кнопки на время выполнения
            STATE.btnSet.disabled = true;
            STATE.btnCancel.disabled = true;

            for (let i = 0; i < selectedInputs.length; i++) {
                try {
                    selectedInputs[i].click();
                } catch (e) {
                    // игнорируем единичные ошибки клика
                }
                await new Promise(r => setTimeout(r, 250));
            }

            cleanupToIdle();

            // Standard browser confirm: OK = refresh, Cancel = stay
            const refresh = confirm(`UPDATED ${selectedCount} BASELINES\n\nREFRESH THE PAGE?`);
            if (refresh) {
                window.location.reload();
            }
        }

        function handleSetBaselines() {
            if (!ensureInjected()) return;

            if (STATE.mode !== 'scanned') {
                const ok = scanAndRender();
                if (!ok) return;
            }

            applySelectedBaselines();
        }

        function removeCheckboxesAndHud() {
            STATE.checkboxRecords.forEach(r => {
                try {
                    r.checkbox.removeEventListener('change', r.onChange);
                } catch (e) {}
                try {
                    if (r.label) r.label.remove();
                } catch (e) {}
            });
            STATE.checkboxRecords = [];

            // Снимаем метки обработанности, чтобы повторный вход работал корректно
            try {
                document.querySelectorAll('input.test__set-baseline[data-mass-baseline-processed="true"]').forEach(el => {
                    el.removeAttribute('data-mass-baseline-processed');
                });
            } catch (e) {}

            if (STATE.hudEl) {
                try { STATE.hudEl.remove(); } catch (e) {}
                STATE.hudEl = null;
            }

            STATE.baselineInputs = [];
        }

        function cleanupToIdle() {
            if (!ensureInjected()) return;

            removeCheckboxesAndHud();

            STATE.mode = 'idle';
            STATE.btnSet.disabled = false;
            STATE.btnCancel.disabled = false;

            // На всякий случай убираем "успешные" классы, если кто-то их навесил
            STATE.btnMass.classList.remove('baseline-button-success', 'dynamic-baseline-button');
            STATE.btnSet.classList.remove('baseline-button-success', 'dynamic-baseline-button');
            STATE.btnCancel.classList.remove('baseline-button-success', 'dynamic-baseline-button');

            STATE.btnMass.hidden = false;
            STATE.btnSet.hidden = true;
            STATE.btnCancel.hidden = true;
        }

        // Пытаемся сразу, и на случай поздней отрисовки фильтров — наблюдатель
        if (!ensureInjected()) {
            const obs = new MutationObserver(() => {
                if (ensureInjected()) obs.disconnect();
            });
            obs.observe(document.body, { childList: true, subtree: true });
        }
    }

    function totalTestCount() {
        let executedTestsElements = document.querySelectorAll('a.total-tests');

        if (executedTestsElements && executedTestsElements.length > 0) {
            executedTestsElements.forEach(el => {
                if (!el) return;

                let newCount;
                let count = parseInt(el.textContent, 10);
                let suitName = el.getAttribute('data-suite');

                if (!suitName) return;

                suitName = suitName.replace('stable: ', '').replace('stage: ', '');

                if (totalTests[suitName]) {
                    if (count === totalTests[suitName]) {
                        newCount = `<span class="count-number">${count}</span><sup class="count-sup">${totalTests[suitName]}</sup>`;
                    } else if (count < totalTests[suitName]) {
                        newCount = `<span class="count-warning count-number">${count}</span><sup class="count-sup">${totalTests[suitName]}</sup>`;
                    } else {
                        newCount = `<span class="count-number">${count}</span><sup class="count-sup"><span class="count-excess count-number">${totalTests[suitName]}</span></sup>`;
                    }

                    el.innerHTML = newCount;

                    const iconSpan = document.createElement('span');
                    iconSpan.classList.add('icon-span');

                    if (count < totalTests[suitName]) {
                        iconSpan.textContent = '⚠️';
                    } else if (count > totalTests[suitName]) {
                        iconSpan.textContent = '⬆️';
                    }

                    if (iconSpan.textContent) el.appendChild(iconSpan);

                    if (count < totalTests[suitName]) {
                        let percentMark = document.createElement('a');
                        percentMark.classList.add('label', 'label--warning', 'percent-mark');
                        percentMark.title = 'Test Pass Rate';
                        percentMark.innerText = `${((count / totalTests[suitName]) * 100).toFixed(1)}%`;
                        insertAfter(percentMark, el);
                    }
                }
            });
        }
    }

    function sortingProjects() {
        let projectsTable = document.querySelector('#projects');

        if (!projectsTable) return;

        let projects = projectsTable.querySelectorAll('.project');

        if (!projects || projects.length === 0) return;

        let sortedProjectsArr = [];
        let projectName, projectNameEnv, projectNameNormalized;

        projects.forEach(project => {
            if (!project) return;

            projectName = project.getAttribute('data-project');

            if (!projectName) return;

            projectNameEnv = projectName.split(':')[0];

            if (projectName.includes('monitoring chats list')) {
                projectNameNormalized = projectNameEnv + '9';
            } else if (projectName.includes('widget:') || projectName.includes('website:')) {
                projectNameNormalized = '1website';
            } else {
                projectNameNormalized = projectNameEnv;
            }

            sortedProjectsArr.push({ name: projectNameNormalized, val: project });
        });

        if (sortedProjectsArr.length > 0) {
            sortedProjectsArr.sort((a, b) => b.name.localeCompare(a.name));

            projectsTable.innerHTML = '';

            sortedProjectsArr.forEach(project => {
                projectsTable.appendChild(project.val);
            });
        }
    }

    function sortingTests() {
        if (window.location.pathname.indexOf('projects/website/suites/website-visual-regression-testing/runs') !== -1) {
            let testsBlockArr = document.querySelectorAll('tr.test');

            if (!testsBlockArr || testsBlockArr.length === 0) return;

            let parentTests = testsBlockArr[0].parentElement;

            if (!parentTests) return;

            let sortedTestsBlockArr = [];

            testsBlockArr.forEach(testsBlock => {
                if (!testsBlock) return;

                const test = testsBlock.querySelector('th a');

                if (!test) return;

                const testName = test.innerHTML.replace(/\s+/g, '-').toLowerCase();
                sortedTestsBlockArr.push({ name: testName, val: testsBlock });
            });

            if (sortedTestsBlockArr.length > 0) {
                sortedTestsBlockArr.sort((a, b) => a.name.localeCompare(b.name));

                parentTests.innerHTML = '';

                sortedTestsBlockArr.forEach(testsBlock => {
                    parentTests.appendChild(testsBlock.val);
                });
            }
        }
    }

    // Baseline highlight
    if ((window.location.pathname.includes('/projects/')) && (window.location.pathname.includes('/suites/')) && (window.location.pathname.indexOf('/runs/') === -1)) {
        let newBaselines = document.querySelectorAll('tr.new-baseline');
        let oldBaselines = document.querySelectorAll('tr.old-baseline');

        if (newBaselines && newBaselines.length > 0) {
            let breadcrumb = document.querySelector('ul.breadcrumb');
            if (breadcrumb) {
                let newButton = document.createElement('button');
                newButton.id = 'show-new-baselines';
                newButton.classList.add('label--all', 'label');
                newButton.innerText = `New baselines: ${newBaselines.length}`;
                breadcrumb.appendChild(newButton);

                newBaselines.forEach(item => {
                    const link = item.querySelector('th > a');
                    if (link) link.classList.add('new-baseline-link');
                });

                newButton.addEventListener('click', function() {
                    oldBaselines.forEach(item => {
                        if (item) item.remove();
                    });

                    const filters = document.querySelector('div.filters');
                    if (filters) {
                        filters.scrollIntoView({ behavior: 'instant', block: 'start', inline: 'start' });
                    }
                });
            }
        }
    }

    // Helper functions
    function addElementButton(parentId, test) {
        if (!parentId || !test) return;

        let newButton = document.createElement('button');
        newButton.id = parentId;
        newButton.classList.add('test-compare-button', 'diffBtn');
        newButton.innerText = 'Compare';
        insertAfter(newButton, test);
    }

    function addModalWindows(parentId, reference, current) {
        if (!parentId || !reference || !current) return;

        let modalWindow = document.createElement('div');
        modalWindow.id = parentId;
        modalWindow.classList.add('modal');
        modalWindow.innerHTML = '<div class="modal-content"><span class="close">&times;</span><div id="image-compare"><img src="' + reference + '" alt="reference" /><img src="' + current + '" alt="current" /></div></div>';
        document.body.appendChild(modalWindow);
    }

    function addElementPTime(parent, text) {
        if (!parent || !text) return;

        const time = text.split(' ');

        if (time.length < 4) return;

        const timeParts = time[3].split(':');

        if (timeParts.length < 2) return;

        let newP = document.createElement('p');
        newP.classList.add('date-time');
        newP.innerHTML = '🕒 ' + text;
        insertAfter(newP, parent);
    }

    function setViewers() {
        const options = {
            showLabels: true,
            labelOptions: {
                before: 'Baseline',
                after: 'Comparison',
                onHover: false
            },
            smoothing: false,
            startingPoint: 50,
            fluidMode: true,
            controlColor: '#FF0000',
            controlShadow: false,
            addCircle: false,
            addCircleBlur: false,
        };
        const viewers = document.querySelectorAll('#image-compare');
        viewers.forEach(element => {
            const viewer = new ImageCompare(element, options).mount();

            const observer = new MutationObserver(function() {
                updateHorizontalLines();
            });

            observer.observe(element, {
                attributes: true,
                childList: true,
                subtree: true
            });

            element.addEventListener('mousemove', updateHorizontalLines);
            element.addEventListener('click', updateHorizontalLines);
        });
    }

    function updateHorizontalLines() {
        const control = document.querySelector('.icv__control');
        const container = document.querySelector('#image-compare');
        const beforeLabel = document.querySelector('.icv__label-before');
        const afterLabel = document.querySelector('.icv__label-after');

        if (!control || !container || !beforeLabel || !afterLabel) return;

        const controlRect = control.getBoundingClientRect();
        const containerRect = container.getBoundingClientRect();
        const beforeLabelRect = beforeLabel.getBoundingClientRect();
        const afterLabelRect = afterLabel.getBoundingClientRect();

        const controlCenterX = controlRect.left + (controlRect.width / 2);
        const beforeLineWidth = controlCenterX - (beforeLabelRect.right);
        const afterLineWidth = (afterLabelRect.left) - controlCenterX;

        document.documentElement.style.setProperty('--before-line-width', `${Math.max(0, beforeLineWidth)}px`);
        document.documentElement.style.setProperty('--after-line-width', `${Math.max(0, afterLineWidth)}px`);
    }

    function setListener() {
        const btn = document.querySelectorAll('button.diffBtn');
        btn.forEach(element => {
            element.addEventListener('click', function(event) {
                let modal = document.querySelector(`div#${event.target.id}`);
                if (!modal) return;

                modal.classList.add('modal-show');
                modal.classList.remove('modal-hide');
                let close = modal.querySelector('span.close');
                if (close) {
                    close.onclick = function() {
                        modal.classList.add('modal-hide');
                        modal.classList.remove('modal-show');
                    };
                }

                window.onclick = function(event) {
                    if (event.target == modal) {
                        modal.classList.add('modal-hide');
                        modal.classList.remove('modal-show');
                    }
                };
            });
        });
    }

    function insertAfter(newNode, existingNode) {
        if (!newNode || !existingNode || !existingNode.parentNode) return;
        existingNode.parentNode.insertBefore(newNode, existingNode.nextSibling);
    }


    function addDownloadBlock() {
        let downloadBlock = document.createElement('div');
        downloadBlock.id = 'download-block';
        downloadBlock.classList.add('container');

        let downloadButton = document.createElement('button');
        downloadButton.id = 'download-images';
        downloadButton.classList.add('download-button');
        downloadButton.innerHTML = 'Download images';
        downloadBlock.appendChild(downloadButton);

        let progressBlock = document.createElement('div');
        progressBlock.id = 'progress_bar';
        progressBlock.classList.add('progress', 'hide');
        progressBlock.innerHTML = '<div class="progress-bar" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100"></div>';
        downloadBlock.appendChild(progressBlock);

        let progressStatus = document.createElement('p');
        progressStatus.id = 'result';
        progressStatus.classList.add('hide');
        progressStatus.innerHTML = '';
        downloadBlock.appendChild(progressStatus);

        let footer = document.createElement('p');
        footer.innerHTML = 'All images from the current run will be archived';
        downloadBlock.appendChild(footer);

        let versionInfo = document.createElement('p');
        versionInfo.classList.add('version');
        versionInfo.innerHTML = 'Spectre 5000 v.17.001';
        downloadBlock.appendChild(versionInfo);

        document.body.appendChild(downloadBlock);
    }

    function resetMessage() {
        $('#result')
            .removeClass()
            .text('');
    }

    function showMessage(text) {
        resetMessage();
        $('#result')
            .addClass('alert alert-success')
            .text(text);
    }

    function showError(text) {
        resetMessage();
        $('#result')
            .addClass('alert alert-danger')
            .text(text);
    }

    function updatePercent(percent) {
        $('#progress_bar').removeClass('hide')
            .find('.progress-bar')
            .attr('aria-valuenow', percent)
            .css({
            width: percent + '%'
        });
    }

    const Promise = window.Promise || JSZip.external.Promise;

    function urlToPromise(url) {
        return new Promise(function(resolve, reject) {
            JSZipUtils.getBinaryContent(url, function(err, data) {
                if (err) {
                    reject(err);
                } else {
                    resolve(data);
                }
            });
        });
    }


    // Initialize all functions
    addDifferencesMarks();
    footerAndHeader();
    addVisualComparisonButtons();
    improveDateTime();
    lowDiffFilter();
    totalTestCount();
    sortingProjects();
    sortingTests();
    envStyle();
    productStyle();
    addModalsToImgs(testimgs);
    addModalsToImgs(baselineimgs);
    addProductTabs();
    highlightProjectAge();
    addRowDividers();
    adjustColumnWidths();
    hideProjectsHeader();
    combineFilterElements();
    setupBaselineButtons();

    addLowDiffButtons();
    initMassBaselineEditing();

    setTimeout(() => {
        addLowDiffButtons();
    }, 2000);

})();