mirror of
				https://github.com/Frooodle/Stirling-PDF.git
				synced 2025-10-25 11:17:28 +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