mirror of
https://github.com/Frooodle/Stirling-PDF.git
synced 2025-04-12 01:17:15 +02:00
Fix: Thymeleaf syntax (/*[[...]]*/)
(#2659)
# Description Please provide a summary of the changes, including relevant motivation and context. Closes #(issue_number) ## Checklist - [x] I have read the [Contribution Guidelines](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/CONTRIBUTING.md) - [x] I have performed a self-review of my own code - [ ] I have attached images of the change if it is UI based - [ ] I have commented my code, particularly in hard-to-understand areas - [ ] If my code has heavily changed functionality I have updated relevant docs on [Stirling-PDFs doc repo](https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/) - [x] My changes generate no new warnings - [ ] I have read the section [Add New Translation Tags](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/HowToAddNewLanguage.md#add-new-translation-tags) (for new translation tags only)
This commit is contained in:
parent
b2da426cc1
commit
76cbf94fdc
@ -1,10 +1,10 @@
|
||||
document.addEventListener("DOMContentLoaded", function() {
|
||||
|
||||
|
||||
var cacheInputs = localStorage.getItem("cacheInputs") || "disabled";
|
||||
if (cacheInputs !== "enabled") {
|
||||
return; // Stop execution if caching is not enabled
|
||||
}
|
||||
|
||||
|
||||
// Function to generate a key based on the form's action attribute
|
||||
function generateStorageKey(form) {
|
||||
const action = form.getAttribute('action');
|
||||
@ -24,11 +24,11 @@ document.addEventListener("DOMContentLoaded", function() {
|
||||
for (let i = 0; i < elements.length; i++) {
|
||||
const element = elements[i];
|
||||
// Skip elements without names, passwords, files, hidden fields, and submit/reset buttons
|
||||
if (!element.name ||
|
||||
element.type === 'password' ||
|
||||
element.type === 'file' ||
|
||||
//element.type === 'hidden' ||
|
||||
element.type === 'submit' ||
|
||||
if (!element.name ||
|
||||
element.type === 'password' ||
|
||||
element.type === 'file' ||
|
||||
//element.type === 'hidden' ||
|
||||
element.type === 'submit' ||
|
||||
element.type === 'reset') {
|
||||
continue;
|
||||
}
|
||||
|
@ -18,7 +18,7 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
// Find all forms and add CSRF token
|
||||
const forms = document.querySelectorAll('form');
|
||||
const csrfToken = decodeCsrfToken(getCsrfToken());
|
||||
|
||||
|
||||
// Only proceed if we have a cookie-based token
|
||||
if (csrfToken) {
|
||||
forms.forEach(form => {
|
||||
@ -34,4 +34,4 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
form.appendChild(csrfInput);
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
@ -102,4 +102,4 @@ document.addEventListener("DOMContentLoaded", function () {
|
||||
toggleDarkMode();
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
@ -4,7 +4,7 @@ window.fetchWithCsrf = async function(url, options = {}) {
|
||||
.split('; ')
|
||||
.find(row => row.startsWith('XSRF-TOKEN='))
|
||||
?.split('=')[1];
|
||||
|
||||
|
||||
if (cookieValue) {
|
||||
return cookieValue;
|
||||
}
|
||||
@ -14,10 +14,10 @@ window.fetchWithCsrf = async function(url, options = {}) {
|
||||
|
||||
// Create a new options object to avoid modifying the passed object
|
||||
const fetchOptions = { ...options };
|
||||
|
||||
|
||||
// Ensure headers object exists
|
||||
fetchOptions.headers = { ...options.headers };
|
||||
|
||||
|
||||
// Add CSRF token if available
|
||||
const csrfToken = getCsrfToken();
|
||||
if (csrfToken) {
|
||||
@ -25,4 +25,4 @@ window.fetchWithCsrf = async function(url, options = {}) {
|
||||
}
|
||||
|
||||
return fetch(url, fetchOptions);
|
||||
}
|
||||
}
|
||||
|
@ -32,8 +32,8 @@ function initializeGame() {
|
||||
const BASE_SPAWN_INTERVAL_MS = 1250; // milliseconds before a new enemy spawns
|
||||
const LEVEL_INCREASE_FACTOR_MS = 25; // milliseconds to decrease the spawn interval per level
|
||||
const MAX_SPAWN_RATE_REDUCTION_MS = 800; // Max milliseconds from the base spawn interval
|
||||
|
||||
|
||||
|
||||
|
||||
let keysPressed = {};
|
||||
const pdfs = [];
|
||||
const projectiles = [];
|
||||
@ -42,7 +42,7 @@ function initializeGame() {
|
||||
let pdfSpeed = BASE_PDF_SPEED;
|
||||
let gameOver = false;
|
||||
|
||||
|
||||
|
||||
function handleKeys() {
|
||||
if (keysPressed["ArrowLeft"]) {
|
||||
playerX -= PLAYER_MOVE_SPEED;
|
||||
@ -72,7 +72,7 @@ function initializeGame() {
|
||||
function onKeyUp(event) {
|
||||
keysPressed[event.key] = false;
|
||||
}
|
||||
|
||||
|
||||
document.removeEventListener("keydown", onKeydown);
|
||||
document.removeEventListener("keyup", onKeyUp);
|
||||
document.addEventListener("keydown", onKeydown);
|
||||
@ -243,7 +243,7 @@ function initializeGame() {
|
||||
|
||||
let spawnPdfTimeout;
|
||||
|
||||
|
||||
|
||||
|
||||
function spawnPdfInterval() {
|
||||
if (gameOver || paused) {
|
||||
|
@ -39,4 +39,3 @@ document.getElementById("cacheInputs").addEventListener("change", function () {
|
||||
cacheInputs = this.checked ? "enabled" : "disabled";
|
||||
localStorage.setItem("cacheInputs", cacheInputs);
|
||||
});
|
||||
|
||||
|
@ -308,37 +308,36 @@
|
||||
|
||||
document.getElementById('syncToAccount').addEventListener('click', async function() {
|
||||
/*<![CDATA[*/
|
||||
const urlUpdateUserSettings = /*[[@{/api/v1/user/updateUserSettings}]]*/ "/api/v1/user/updateUserSettings";
|
||||
/*]]>*/
|
||||
|
||||
let settings = {};
|
||||
for (let i = 0; i < localStorage.length; i++) {
|
||||
const key = localStorage.key(i);
|
||||
if(key !== 'debug' && key !== '0' && key !== '1' && !key.includes('pdfjs') && !key.includes('posthog') && !key.includes('pageViews')) {
|
||||
settings[key] = localStorage.getItem(key);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await window.fetchWithCsrf(urlUpdateUserSettings, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(settings)
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
location.reload();
|
||||
} else {
|
||||
alert('Error syncing settings to account');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error:', error);
|
||||
alert('Error syncing settings to account');
|
||||
}
|
||||
});
|
||||
const urlUpdateUserSettings = /*[[@{/api/v1/user/updateUserSettings}]]*/ "/api/v1/user/updateUserSettings";
|
||||
/*]]>*/
|
||||
|
||||
let settings = {};
|
||||
for (let i = 0; i < localStorage.length; i++) {
|
||||
const key = localStorage.key(i);
|
||||
if(key !== 'debug' && key !== '0' && key !== '1' && !key.includes('pdfjs') && !key.includes('posthog') && !key.includes('pageViews')) {
|
||||
settings[key] = localStorage.getItem(key);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await window.fetchWithCsrf(urlUpdateUserSettings, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(settings)
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
location.reload();
|
||||
} else {
|
||||
alert('Error syncing settings to account');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error:', error);
|
||||
alert('Error syncing settings to account');
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
<div class="mb-3 mt-4 text-center">
|
||||
|
@ -16,8 +16,8 @@
|
||||
|
||||
<!-- powered by section -->
|
||||
<div class="footer-powered-by">
|
||||
<span th:text="#{poweredBy}"></span>
|
||||
<a href="https://stirlingpdf.com" class="stirling-link">Stirling PDF</a>
|
||||
</div>
|
||||
<span th:text="#{poweredBy}"></span>
|
||||
<a href="https://stirlingpdf.com" class="stirling-link">Stirling PDF</a>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
@ -12,87 +12,87 @@
|
||||
<div class="container-flex">
|
||||
<main class="form-signin">
|
||||
<script th:inline="javascript">
|
||||
const redirectAttempts = parseInt(localStorage.getItem('ssoRedirectAttempts') || '0');
|
||||
const urlParams = new URLSearchParams(window.location.search);
|
||||
const hasRedirectError = urlParams.has('error');
|
||||
const hasLogout = urlParams.has('logout');
|
||||
const hasMessage = urlParams.has('message');
|
||||
const MAX_REDIRECT_ATTEMPTS = 3;
|
||||
|
||||
document.addEventListener('modeChanged', function(e) {
|
||||
var mode = e.detail;
|
||||
|
||||
document.body.classList.remove("light-mode", "dark-mode", "rainbow-mode"); // remove all mode classes first
|
||||
|
||||
switch (mode) {
|
||||
case "on":
|
||||
document.body.classList.add("dark-mode");
|
||||
break;
|
||||
case "off":
|
||||
document.body.classList.add("light-mode");
|
||||
break;
|
||||
case "rainbow":
|
||||
document.body.classList.add("rainbow-mode");
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
|
||||
const runningEE = [[${@runningEE}]];
|
||||
const SSOAutoLogin = [[${@SSOAutoLogin}]];
|
||||
const loginMethod = [[${loginMethod}]];
|
||||
const providerList = [[${providerlist}]];
|
||||
const shouldAutoRedirect = !hasRedirectError &&
|
||||
!hasLogout &&
|
||||
!hasMessage &&
|
||||
redirectAttempts < MAX_REDIRECT_ATTEMPTS &&
|
||||
loginMethod !== 'normal' && runningEE && SSOAutoLogin;
|
||||
|
||||
console.log('Should redirect:', shouldAutoRedirect, {
|
||||
'No error': !hasRedirectError,
|
||||
'No logout': !hasLogout,
|
||||
'No message': !hasMessage,
|
||||
'Under max attempts': redirectAttempts < MAX_REDIRECT_ATTEMPTS,
|
||||
'Is OAuth2': loginMethod === 'oauth2'
|
||||
});
|
||||
|
||||
if (shouldAutoRedirect && providerList && Object.keys(providerList).length > 0) {
|
||||
localStorage.setItem('ssoRedirectAttempts', redirectAttempts + 1);
|
||||
const firstProvider = Object.keys(providerList)[0];
|
||||
window.location.href = firstProvider;
|
||||
}
|
||||
|
||||
// Reset redirect attempts if successful login or after 1 hour
|
||||
const lastAttemptTime = parseInt(localStorage.getItem('lastRedirectAttempt') || '0');
|
||||
if (Date.now() - lastAttemptTime > 3600000) { // 1 hour
|
||||
localStorage.setItem('ssoRedirectAttempts', '0');
|
||||
}
|
||||
localStorage.setItem('lastRedirectAttempt', Date.now().toString());
|
||||
|
||||
|
||||
const defaultLocale = getStoredOrDefaultLocale();
|
||||
checkUserLanguage(defaultLocale);
|
||||
|
||||
const dropdownItems = document.querySelectorAll('.lang_dropdown-item');
|
||||
let activeItem;
|
||||
|
||||
for (let i = 0; i < dropdownItems.length; i++) {
|
||||
const item = dropdownItems[i];
|
||||
item.classList.remove('active');
|
||||
if (item.dataset.bsLanguageCode === defaultLocale) {
|
||||
item.classList.add('active');
|
||||
activeItem = item;
|
||||
}
|
||||
item.addEventListener('click', handleDropdownItemClick);
|
||||
}
|
||||
|
||||
const dropdown = document.getElementById('languageDropdown');
|
||||
|
||||
if (activeItem) {
|
||||
dropdown.innerHTML = activeItem.innerHTML; // This will set the dropdown button's content to the active language's flag and name
|
||||
}
|
||||
});
|
||||
const redirectAttempts = parseInt(localStorage.getItem('ssoRedirectAttempts') || '0');
|
||||
const urlParams = new URLSearchParams(window.location.search);
|
||||
const hasRedirectError = urlParams.has('error');
|
||||
const hasLogout = urlParams.has('logout');
|
||||
const hasMessage = urlParams.has('message');
|
||||
const MAX_REDIRECT_ATTEMPTS = 3;
|
||||
|
||||
document.addEventListener('modeChanged', function(e) {
|
||||
var mode = e.detail;
|
||||
|
||||
document.body.classList.remove("light-mode", "dark-mode", "rainbow-mode"); // remove all mode classes first
|
||||
|
||||
switch (mode) {
|
||||
case "on":
|
||||
document.body.classList.add("dark-mode");
|
||||
break;
|
||||
case "off":
|
||||
document.body.classList.add("light-mode");
|
||||
break;
|
||||
case "rainbow":
|
||||
document.body.classList.add("rainbow-mode");
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
|
||||
const runningEE = /*[[${@runningEE}]]*/ false;
|
||||
const SSOAutoLogin = /*[[${@SSOAutoLogin}]]*/ false;
|
||||
const loginMethod = /*[[${loginMethod}]]*/ 'normal';
|
||||
const providerList = /*[[${providerlist}]]*/ {};
|
||||
const shouldAutoRedirect = !hasRedirectError &&
|
||||
!hasLogout &&
|
||||
!hasMessage &&
|
||||
redirectAttempts < MAX_REDIRECT_ATTEMPTS &&
|
||||
loginMethod !== 'normal' && runningEE && SSOAutoLogin;
|
||||
|
||||
console.log('Should redirect:', shouldAutoRedirect, {
|
||||
'No error': !hasRedirectError,
|
||||
'No logout': !hasLogout,
|
||||
'No message': !hasMessage,
|
||||
'Under max attempts': redirectAttempts < MAX_REDIRECT_ATTEMPTS,
|
||||
'Is only OAuth2': loginMethod === 'oauth2'
|
||||
});
|
||||
|
||||
if (shouldAutoRedirect && providerList && Object.keys(providerList).length > 0) {
|
||||
localStorage.setItem('ssoRedirectAttempts', redirectAttempts + 1);
|
||||
const firstProvider = Object.keys(providerList)[0];
|
||||
window.location.href = firstProvider;
|
||||
}
|
||||
|
||||
// Reset redirect attempts if successful login or after 1 hour
|
||||
const lastAttemptTime = parseInt(localStorage.getItem('lastRedirectAttempt') || '0');
|
||||
if (Date.now() - lastAttemptTime > 3600000) { // 1 hour
|
||||
localStorage.setItem('ssoRedirectAttempts', '0');
|
||||
}
|
||||
localStorage.setItem('lastRedirectAttempt', Date.now().toString());
|
||||
|
||||
|
||||
const defaultLocale = getStoredOrDefaultLocale();
|
||||
checkUserLanguage(defaultLocale);
|
||||
|
||||
const dropdownItems = document.querySelectorAll('.lang_dropdown-item');
|
||||
let activeItem;
|
||||
|
||||
for (let i = 0; i < dropdownItems.length; i++) {
|
||||
const item = dropdownItems[i];
|
||||
item.classList.remove('active');
|
||||
if (item.dataset.bsLanguageCode === defaultLocale) {
|
||||
item.classList.add('active');
|
||||
activeItem = item;
|
||||
}
|
||||
item.addEventListener('click', handleDropdownItemClick);
|
||||
}
|
||||
|
||||
const dropdown = document.getElementById('languageDropdown');
|
||||
|
||||
if (activeItem) {
|
||||
dropdown.innerHTML = activeItem.innerHTML; // This will set the dropdown button's content to the active language's flag and name
|
||||
}
|
||||
});
|
||||
</script>
|
||||
<div class="text-center">
|
||||
<img class="my-4" th:src="@{'/favicon.svg'}" alt="favicon" width="144" height="144">
|
||||
|
@ -16,15 +16,15 @@
|
||||
<span class="material-symbols-rounded tool-header-icon history">update</span>
|
||||
<span class="tool-header-text" th:text="#{releases.header}">Release Notes</span>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="alert alert-info" role="alert">
|
||||
<strong th:text="#{releases.current.version}">Current Installed Version</strong>:
|
||||
<strong th:text="#{releases.current.version}">Current Installed Version</strong>:
|
||||
<span id="currentVersion" th:text="${@appVersion}"></span>
|
||||
</div>
|
||||
|
||||
<div class="alert alert-warning" role="alert">
|
||||
<span th:text="#{releases.note}">All release notes are only available in English</span>
|
||||
</div>
|
||||
<div class="alert alert-warning" role="alert">
|
||||
<span th:text="#{releases.note}">All release notes are only available in English</span>
|
||||
</div>
|
||||
|
||||
<div id="loading" class="text-center my-4">
|
||||
<div class="spinner-border text-primary" role="status">
|
||||
@ -51,42 +51,42 @@
|
||||
.release-notes-container {
|
||||
margin-top: 2rem;
|
||||
}
|
||||
|
||||
|
||||
.release-card {
|
||||
border: 1px solid #dee2e6;
|
||||
border-radius: 0.25rem;
|
||||
margin-bottom: 1.5rem;
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
|
||||
.release-card.current-version {
|
||||
border-color: #28a745;
|
||||
background-color: rgba(40, 167, 69, 0.05);
|
||||
}
|
||||
|
||||
|
||||
.release-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
|
||||
.release-header h3 {
|
||||
margin: 0;
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
|
||||
.version {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
|
||||
.release-date {
|
||||
color: #6c757d;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
|
||||
.release-body {
|
||||
font-size: 0.9rem;
|
||||
white-space: pre-wrap;
|
||||
@ -105,17 +105,17 @@
|
||||
|
||||
<script th:inline="javascript">
|
||||
/*<![CDATA[*/
|
||||
|
||||
|
||||
// Get the current version from the appVersion bean
|
||||
const appVersion = [[${@appVersion}]];
|
||||
|
||||
const appVersion = /*[[${@appVersion}]]*/ '';
|
||||
|
||||
// GitHub API configuration
|
||||
const REPO_OWNER = 'Stirling-Tools';
|
||||
const REPO_NAME = 'Stirling-PDF';
|
||||
const GITHUB_API = 'https://api.github.com/repos/' + REPO_OWNER + '/' + REPO_NAME;
|
||||
const GITHUB_URL = 'https://github.com/' + REPO_OWNER + '/' + REPO_NAME;
|
||||
const MAX_RELEASES = 8;
|
||||
|
||||
const GITHUB_URL = 'https://github.com/' + REPO_OWNER + '/' + REPO_NAME;
|
||||
const MAX_RELEASES = 8;
|
||||
|
||||
// Secure element creation helper
|
||||
function createElement(tag, attributes = {}, children = []) {
|
||||
const element = document.createElement(tag);
|
||||
@ -134,7 +134,6 @@
|
||||
return element;
|
||||
}
|
||||
|
||||
|
||||
const ALLOWED_TAGS = {
|
||||
'a': ['href', 'target', 'rel', 'class'],
|
||||
'img': ['src', 'alt', 'width', 'height', 'style'],
|
||||
@ -149,7 +148,7 @@
|
||||
try {
|
||||
const parser = new DOMParser();
|
||||
const doc = parser.parseFromString(htmlString, 'text/html');
|
||||
|
||||
|
||||
function sanitizeNode(node) {
|
||||
// Safety check for null/undefined
|
||||
if (!node) return null;
|
||||
@ -162,7 +161,7 @@
|
||||
// Handle element nodes
|
||||
if (node.nodeType === Node.ELEMENT_NODE) {
|
||||
const tagName = node.tagName.toLowerCase();
|
||||
|
||||
|
||||
// Check if tag is allowed
|
||||
if (!ALLOWED_TAGS[tagName]) {
|
||||
return document.createTextNode(node.textContent);
|
||||
@ -170,7 +169,7 @@
|
||||
|
||||
// Create new element
|
||||
const cleanElement = document.createElement(tagName);
|
||||
|
||||
|
||||
// Copy allowed attributes
|
||||
const allowedAttributes = ALLOWED_TAGS[tagName];
|
||||
Array.from(node.attributes).forEach(attr => {
|
||||
@ -244,7 +243,7 @@
|
||||
let lastIndex = 0;
|
||||
const result = document.createElement('span');
|
||||
const urlRegex = new RegExp('https://github\\.com/' + REPO_OWNER + '/' + REPO_NAME + '/(?:issues|pull)/(\\d+)', 'g');
|
||||
|
||||
|
||||
while ((match = urlRegex.exec(text)) !== null) {
|
||||
// Add text before the match
|
||||
if (match.index > lastIndex) {
|
||||
@ -259,7 +258,7 @@
|
||||
link.target = '_blank';
|
||||
link.rel = 'noopener noreferrer';
|
||||
result.appendChild(link);
|
||||
|
||||
|
||||
lastIndex = match.index + match[0].length;
|
||||
}
|
||||
|
||||
@ -274,15 +273,15 @@
|
||||
// Update formatText function to handle processGitHubReferences properly
|
||||
function formatText(text) {
|
||||
const container = document.createElement('div');
|
||||
|
||||
|
||||
// Split the text into lines
|
||||
const textWithoutComments = text.replace(/<!--[\s\S]*?-->/g, '');
|
||||
const lines = textWithoutComments.split('\n');
|
||||
let currentList = null;
|
||||
|
||||
|
||||
lines.forEach(line => {
|
||||
const trimmedLine = line.trim();
|
||||
|
||||
|
||||
// Skip empty lines but add spacing
|
||||
if (!trimmedLine) {
|
||||
if (currentList) {
|
||||
@ -292,14 +291,14 @@ function formatText(text) {
|
||||
container.appendChild(document.createElement('br'));
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// Check if the line is HTML
|
||||
if (trimmedLine.startsWith('<') && trimmedLine.endsWith('>')) {
|
||||
if (currentList) {
|
||||
container.appendChild(currentList);
|
||||
currentList = null;
|
||||
}
|
||||
|
||||
|
||||
const safeElement = createSafeElement(trimmedLine);
|
||||
if (safeElement) {
|
||||
container.appendChild(safeElement);
|
||||
@ -309,7 +308,7 @@ function formatText(text) {
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// Check for headers
|
||||
const headerMatch = trimmedLine.match(/^(#{1,3})\s+(.+)$/);
|
||||
if (headerMatch) {
|
||||
@ -326,40 +325,40 @@ function formatText(text) {
|
||||
container.appendChild(header);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// Check for bullet points
|
||||
const bulletMatch = trimmedLine.match(/^[-*]\s+(.+)$/);
|
||||
if (bulletMatch) {
|
||||
if (!currentList) {
|
||||
currentList = document.createElement('ul');
|
||||
}
|
||||
|
||||
|
||||
const listContent = bulletMatch[1];
|
||||
const listItem = document.createElement('li');
|
||||
|
||||
|
||||
// Process GitHub references in list items
|
||||
listItem.appendChild(processGitHubReferences(listContent));
|
||||
currentList.appendChild(listItem);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// If we reach here and have a list, append it
|
||||
if (currentList) {
|
||||
container.appendChild(currentList);
|
||||
currentList = null;
|
||||
}
|
||||
|
||||
|
||||
// Handle regular paragraph
|
||||
const paragraph = document.createElement('p');
|
||||
paragraph.appendChild(processGitHubReferences(trimmedLine));
|
||||
container.appendChild(paragraph);
|
||||
});
|
||||
|
||||
|
||||
// Append any remaining list
|
||||
if (currentList) {
|
||||
container.appendChild(currentList);
|
||||
}
|
||||
|
||||
|
||||
return container;
|
||||
}
|
||||
|
||||
@ -368,7 +367,7 @@ function compareVersions(v1, v2) {
|
||||
const normalize = v => v.replace(/^v/, '');
|
||||
const v1Parts = normalize(v1).split('.').map(Number);
|
||||
const v2Parts = normalize(v2).split('.').map(Number);
|
||||
|
||||
|
||||
for (let i = 0; i < Math.max(v1Parts.length, v2Parts.length); i++) {
|
||||
const v1Part = v1Parts[i] || 0;
|
||||
const v2Part = v2Parts[i] || 0;
|
||||
@ -386,7 +385,7 @@ async function loadReleases() {
|
||||
try {
|
||||
loading.classList.remove('d-none');
|
||||
errorMessage.classList.add('d-none');
|
||||
|
||||
|
||||
// Clear container safely
|
||||
while (container.firstChild) {
|
||||
container.removeChild(container.firstChild);
|
||||
@ -400,8 +399,8 @@ async function loadReleases() {
|
||||
releases.sort((a, b) => compareVersions(b.tag_name, a.tag_name));
|
||||
|
||||
// Find index of current version
|
||||
const currentVersionIndex = releases.findIndex(release =>
|
||||
compareVersions(release.tag_name, 'v' + appVersion) === 0 ||
|
||||
const currentVersionIndex = releases.findIndex(release =>
|
||||
compareVersions(release.tag_name, 'v' + appVersion) === 0 ||
|
||||
compareVersions(release.tag_name, appVersion) === 0
|
||||
);
|
||||
|
||||
@ -425,20 +424,20 @@ async function loadReleases() {
|
||||
|
||||
relevantReleases.forEach((release, index) => {
|
||||
const isCurrentVersion = index === 0; // First release in the array is current version
|
||||
|
||||
|
||||
const releaseCard = createElement('div', {
|
||||
class: `release-card ${isCurrentVersion ? 'current-version' : ''}`
|
||||
});
|
||||
|
||||
const header = createElement('div', { class: 'release-header' });
|
||||
|
||||
|
||||
const h3 = createElement('h3', {}, [
|
||||
createElement('span', { class: 'version' }, [release.tag_name]),
|
||||
createElement('span', { class: 'release-date' }, [
|
||||
new Date(release.created_at).toLocaleDateString()
|
||||
])
|
||||
]);
|
||||
|
||||
|
||||
header.appendChild(h3);
|
||||
|
||||
if (isCurrentVersion) {
|
||||
@ -451,7 +450,7 @@ async function loadReleases() {
|
||||
|
||||
const body = createElement('div', { class: 'release-body' });
|
||||
body.appendChild(formatText(release.body || 'No release notes available.'));
|
||||
|
||||
|
||||
releaseCard.appendChild(body);
|
||||
container.appendChild(releaseCard);
|
||||
});
|
||||
@ -467,7 +466,7 @@ async function loadReleases() {
|
||||
|
||||
// Load releases when the page loads
|
||||
document.addEventListener('DOMContentLoaded', loadReleases);
|
||||
|
||||
|
||||
/*]]>*/
|
||||
</script>
|
||||
</body>
|
||||
|
@ -21,9 +21,9 @@
|
||||
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, remoteCall='false', accept='application/pdf')}"></div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label th:text="#{validateSignature.selectCustomCert}" ></label>
|
||||
<div th:replace="~{fragments/common :: fileSelector(name='certFile', multipleInputsForSingleRequest=false, notRequired=true, remoteCall='false', accept='.cer,.crt,.pem')}"></div>
|
||||
</div>
|
||||
<label th:text="#{validateSignature.selectCustomCert}" ></label>
|
||||
<div th:replace="~{fragments/common :: fileSelector(name='certFile', multipleInputsForSingleRequest=false, notRequired=true, remoteCall='false', accept='.cer,.crt,.pem')}"></div>
|
||||
</div>
|
||||
<div class="mb-3 text-left">
|
||||
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{validateSignature.submit}"></button>
|
||||
</div>
|
||||
|
@ -29,7 +29,7 @@ public class SPDFApplicationTest {
|
||||
|
||||
@Mock
|
||||
private ApplicationProperties applicationProperties;
|
||||
|
||||
|
||||
@InjectMocks
|
||||
private SPDFApplication SPDFApplication;
|
||||
|
||||
|
@ -103,4 +103,4 @@ class DatabaseConfigTest {
|
||||
|
||||
assertThrows(UnsupportedProviderException.class, () -> databaseConfig.dataSource());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -98,4 +98,4 @@ class RearrangePagesPDFControllerTest {
|
||||
assertNotNull(newPageOrder, "Returning null instead of page order list");
|
||||
assertEquals(Arrays.stream(expectedPageOrder.split(",")).map(Integer::parseInt).toList(), newPageOrder, "Page order doesn't match");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user