This commit is contained in:
Anthony Stirling 2023-04-30 17:29:35 +01:00
parent c174ca1c7e
commit 2608aa4c97
5 changed files with 309 additions and 226 deletions

View file

@ -54,7 +54,7 @@ public class OCRController {
throws IOException, InterruptedException { throws IOException, InterruptedException {
// --output-type pdfa // --output-type pdfa
if (selectedLanguages == null || selectedLanguages.size() < 1) { if (selectedLanguages == null || selectedLanguages.isEmpty()) {
throw new IOException("Please select at least one language."); throw new IOException("Please select at least one language.");
} }
@ -62,7 +62,7 @@ public class OCRController {
List<String> availableLanguages = getAvailableTesseractLanguages(); List<String> availableLanguages = getAvailableTesseractLanguages();
// Validate selected languages // Validate selected languages
selectedLanguages = selectedLanguages.stream().filter(availableLanguages::contains).collect(Collectors.toList()); selectedLanguages = selectedLanguages.stream().filter(availableLanguages::contains).toList();
if (selectedLanguages.isEmpty()) { if (selectedLanguages.isEmpty()) {
throw new IOException("None of the selected languages are valid."); throw new IOException("None of the selected languages are valid.");

View file

@ -1,15 +1,22 @@
document.addEventListener('DOMContentLoaded', function() { function initializeGame() {
const gameContainer = document.getElementById('game-container'); const gameContainer = document.getElementById('game-container');
const player = document.getElementById('player'); const player = document.getElementById('player');
let playerSize = gameContainer.clientWidth * 0.0625; // 5% of container width
player.style.width = playerSize + 'px';
player.style.height = playerSize + 'px';
let playerX = gameContainer.clientWidth / 2 - playerSize / 2;
let playerY = gameContainer.clientHeight * 0.1;
const scoreElement = document.getElementById('score'); const scoreElement = document.getElementById('score');
const levelElement = document.getElementById('level'); const levelElement = document.getElementById('level');
const livesElement = document.getElementById('lives'); const livesElement = document.getElementById('lives');
const highScoreElement = document.getElementById('high-score'); const highScoreElement = document.getElementById('high-score');
let pdfSize = gameContainer.clientWidth * 0.0625; // 5% of container width
let projectileWidth = gameContainer.clientWidth * 0.00625; // 0.5% of container width
let projectileHeight = gameContainer.clientHeight * 0.01667; // 1% of container height
let playerX = gameContainer.clientWidth / 2;
let playerY = 50;
let paused = false; let paused = false;
const fireRate = 200; // Time between shots in milliseconds const fireRate = 200; // Time between shots in milliseconds
let lastProjectileTime = 0; let lastProjectileTime = 0;
@ -48,6 +55,9 @@ document.addEventListener('DOMContentLoaded', function() {
document.addEventListener('keydown', (event) => { document.addEventListener('keydown', (event) => {
if (event.key === ' ') {
event.preventDefault();
}
keysPressed[event.key] = true; keysPressed[event.key] = true;
handleKeys(); handleKeys();
}); });
@ -75,25 +85,29 @@ document.addEventListener('DOMContentLoaded', function() {
const projectile = document.createElement('div'); const projectile = document.createElement('div');
projectile.classList.add('projectile'); projectile.classList.add('projectile');
projectile.style.backgroundColor = 'black'; projectile.style.backgroundColor = 'black';
projectile.style.width = '5px'; projectile.style.width = projectileWidth + 'px';
projectile.style.height = '10px'; projectile.style.height = projectileHeight + 'px';
projectile.style.left = playerX + 20 + 'px'; projectile.style.left = (playerX + playerSize / 2 - projectileWidth / 2) + 'px';
projectile.style.top = (gameContainer.clientHeight - playerY - 20) + 'px'; projectile.style.top = (gameContainer.clientHeight - playerY - playerSize) + 'px';
gameContainer.appendChild(projectile); gameContainer.appendChild(projectile);
projectiles.push(projectile); projectiles.push(projectile);
} }
function spawnPdf() { function spawnPdf() {
const pdf = document.createElement('img'); const pdf = document.createElement('img');
pdf.src = 'images/file-earmark-pdf.svg'; pdf.src = 'images/file-earmark-pdf.svg';
pdf.classList.add('pdf'); pdf.classList.add('pdf');
pdf.style.left = Math.floor(Math.random() * (gameContainer.clientWidth - 50)) + 'px'; pdf.style.width = pdfSize + 'px';
pdf.style.height = pdfSize + 'px';
pdf.style.left = Math.floor(Math.random() * (gameContainer.clientWidth - pdfSize)) + 'px';
pdf.style.top = '0px'; pdf.style.top = '0px';
gameContainer.appendChild(pdf); gameContainer.appendChild(pdf);
pdfs.push(pdf); pdfs.push(pdf);
} }
function resetEnemies() { function resetEnemies() {
pdfs.forEach((pdf) => gameContainer.removeChild(pdf)); pdfs.forEach((pdf) => gameContainer.removeChild(pdf));
pdfs.length = 0; pdfs.length = 0;
@ -137,30 +151,7 @@ function resetEnemies() {
} }
}); });
function resetGame() {
playerX = gameContainer.clientWidth / 2;
playerY = 50;
updatePlayerPosition();
pdfs.forEach((pdf) => gameContainer.removeChild(pdf));
projectiles.forEach((projectile) => gameContainer.removeChild(projectile));
pdfs.length = 0;
projectiles.length = 0;
score = 0;
level = 1;
lives = 3;
pdfSpeed = 2;
gameOver = false;
updateScore();
updateLevel();
updateLives();
setTimeout(updateGame, 1000 / 60);
spawnPdfInterval();
}
@ -189,7 +180,31 @@ function resetEnemies() {
setTimeout(updateGame, 1000 / 60); setTimeout(updateGame, 1000 / 60);
} }
function resetGame() {
playerX = gameContainer.clientWidth / 2;
playerY = 50;
updatePlayerPosition();
pdfs.forEach((pdf) => gameContainer.removeChild(pdf));
projectiles.forEach((projectile) => gameContainer.removeChild(projectile));
pdfs.length = 0;
projectiles.length = 0;
score = 0;
level = 1;
lives = 3;
pdfSpeed = 1;
gameOver = false;
updateScore();
updateLives();
levelElement.textContent = 'Level: ' + level;
clearTimeout(spawnPdfTimeout); // Clear the existing spawnPdfTimeout
setTimeout(updateGame, 1000 / 60);
spawnPdfInterval();
}
@ -228,7 +243,9 @@ function resetEnemies() {
updateHighScore(); updateHighScore();
} }
alert('Game Over! Your final score is: ' + score); alert('Game Over! Your final score is: ' + score);
resetGame(); setTimeout(() => { // Wrap the resetGame() call in a setTimeout
resetGame();
}, 0);
} }
@ -237,10 +254,13 @@ function resetEnemies() {
let spawnPdfTimeout; let spawnPdfTimeout;
function spawnPdfInterval() { function spawnPdfInterval() {
console.log("spawnPdfInterval");
if (gameOver || paused) { if (gameOver || paused) {
console.log("spawnPdfInterval 2");
clearTimeout(spawnPdfTimeout); clearTimeout(spawnPdfTimeout);
return; return;
} }
console.log("spawnPdfInterval 3");
spawnPdf(); spawnPdf();
spawnPdfTimeout = setTimeout(spawnPdfInterval, 1000 - level * 50); spawnPdfTimeout = setTimeout(spawnPdfInterval, 1000 - level * 50);
} }
@ -261,4 +281,6 @@ function resetEnemies() {
}); });
}); }
window.initializeGame = initializeGame;

View file

@ -11,67 +11,7 @@
<div class="container"> <div class="container">
<div class="row justify-content-center"> <div class="row justify-content-center">
<div class="col-md-6"> <div class="col-md-6">
<div class="game-container-wrapper" style="position: relative;">
<div id="game-container">
<div id="lives">Lives: 3</div>
<div id="score">Score: 0</div>
<div id="high-score">High Score: 0</div>
<div id="level">Level: 1</div>
<img src="favicon.svg" class="player" id="player">
</div>
<style>
#game-container {
position: relative;
width: 800px;
height: 600px;
background-color: transparent;
margin: auto;
overflow: hidden;
border: 2px solid black; /* Add border */
}
.pdf, .player, .projectile {
position: absolute;
}
.pdf {
width: 50px;
height: 50px;
}
.player {
width: 50px;
height: 50px;
}
.projectile {
background-color: black !important;
width: 5px;
height: 10px;
}
#score, #level, #lives, #high-score {
color: black;
font-family: sans-serif;
position: absolute;
font-size: 24px;
}
#score {
top: 10px;
left: 10px;
}
#lives {
top: 10px;
left: 150px;
}
#high-score {
top: 10px;
left: 290px;
}
#level {
top: 10px;
right: 10px;
}
</style>
<script src="js/game.js"></script>
</div>
</div>
</div> </div>
</div> </div>
</div> </div>

View file

@ -102,144 +102,161 @@ document.addEventListener("DOMContentLoaded", function () {
</head> </head>
<th:block th:fragment="fileSelector(name, multiple)" th:with="accept=${accept} ?: '*/*', inputText=${inputText} ?: #{pdfPrompt}"> <th:block th:fragment="fileSelector(name, multiple)" th:with="accept=${accept} ?: '*/*', inputText=${inputText} ?: #{pdfPrompt}">
<div class="custom-file-chooser">
<div class="custom-file">
<input type="file" class="custom-file-input" th:name="${name}" th:id="${name}+'-input'" th:accept="${accept}" multiple>
<label class="custom-file-label" th:for="${name}+'-input'" th:text="${inputText}"></label>
</div>
<div class="selected-files"></div>
</div>
<br>
<div id="progressBarContainer" style="display: none; position: relative;">
<div class="progress" style="height: 1rem;">
<div id="progressBar" class="progress-bar progress-bar-striped progress-bar-animated bg-success" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100" style="width: 0%;">
<span class="sr-only">Loading...</span>
</div>
</div>
</div>
<script>
<script>
$('form').submit(function(event) { $(document).ready(function() {
var processing = "Processing..."
var submitButtonText = $('#submitBtn').text() function loadGameScript(callback) {
console.log('loadGameScript called');
$('#submitBtn').text('Processing...'); const script = document.createElement('script');
console.log("start download code") script.src = 'js/game.js';
var files = $('#fileInput-input')[0].files; script.onload = callback;
var url = this.action; document.body.appendChild(script);
console.log(url) }
event.preventDefault(); // Prevent the default form handling behavior let gameScriptLoaded = false;
/* Check if ${multiple} is false */ $('#show-game-btn').on('click', function() {
var multiple = [[${multiple}]] || false; console.log('Show game button clicked');
var override = $('#override').val() || ''; if (!gameScriptLoaded) {
console.log("override=" + override) console.log('Show game button load');
if (override === 'multi' || (!multiple && files.length > 1) && override !== 'single' ) { loadGameScript(function() {
console.log("multi parallel download") console.log('Game script loaded');
submitMultiPdfForm(event,url); window.initializeGame();
} else { gameScriptLoaded = true;
console.log("start single download") });
}
$('#game-container-wrapper').show();
});
// Get the selected download option from localStorage
const downloadOption = localStorage.getItem('downloadOption');
var formData = new FormData($('form')[0]);
// Send the request to the server using the fetch() API
fetch(url, {
method: 'POST',
body: formData
}).then(response => {
if (!response) {
throw new Error('Received null response for file ' + i);
}
console.log("load single download")
// Extract the filename from the Content-Disposition header, if present
let filename = null;
const contentDispositionHeader = response.headers.get('Content-Disposition');
console.log(contentDispositionHeader)
if (contentDispositionHeader && contentDispositionHeader.indexOf('attachment') !== -1) {
filename = decodeURIComponent(contentDispositionHeader.split('filename=')[1].replace(/"/g, ''));
} else {
// If the Content-Disposition header is not present or does not contain the filename, use a default filename
filename = 'download';
}
console.log("filename=" + filename)
$('form').submit(function(event) {
const contentType = response.headers.get('Content-Type'); const boredWaiting = localStorage.getItem('boredWaiting');
console.log("contentType=" + contentType) if (boredWaiting === 'enabled') {
// Check if the response is a PDF or an image $('#show-game-btn').show();
if (contentType.includes('pdf') || contentType.includes('image')) { }
response.blob().then(blob => { var processing = "Processing..."
console.log("pdf/image") var submitButtonText = $('#submitBtn').text()
$('#submitBtn').text('Processing...');
console.log("start download code")
var files = $('#fileInput-input')[0].files;
var url = this.action;
console.log(url)
event.preventDefault(); // Prevent the default form handling behavior
/* Check if ${multiple} is false */
var multiple = [[${multiple}]] || false;
var override = $('#override').val() || '';
console.log("override=" + override)
if (override === 'multi' || (!multiple && files.length > 1) && override !== 'single' ) {
console.log("multi parallel download")
submitMultiPdfForm(event,url);
} else {
console.log("start single download")
// Perform the appropriate action based on the download option // Get the selected download option from localStorage
if (downloadOption === 'sameWindow') { const downloadOption = localStorage.getItem('downloadOption');
console.log("same window")
// Open the file in the same window var formData = new FormData($('form')[0]);
window.location.href = URL.createObjectURL(blob);
} else if (downloadOption === 'newWindow') { // Send the request to the server using the fetch() API
console.log("new window") fetch(url, {
method: 'POST',
body: formData
}).then(response => {
if (!response) {
throw new Error('Received null response for file ' + i);
}
console.log("load single download")
// Open the file in a new window
window.open(URL.createObjectURL(blob), '_blank'); // Extract the filename from the Content-Disposition header, if present
} else { let filename = null;
console.log("else save") const contentDispositionHeader = response.headers.get('Content-Disposition');
console.log(contentDispositionHeader)
if (contentDispositionHeader && contentDispositionHeader.indexOf('attachment') !== -1) {
filename = decodeURIComponent(contentDispositionHeader.split('filename=')[1].replace(/"/g, ''));
} else {
// If the Content-Disposition header is not present or does not contain the filename, use a default filename
filename = 'download';
}
console.log("filename=" + filename)
// Download the file
const contentType = response.headers.get('Content-Type');
console.log("contentType=" + contentType)
// Check if the response is a PDF or an image
if (contentType.includes('pdf') || contentType.includes('image')) {
response.blob().then(blob => {
console.log("pdf/image")
// Perform the appropriate action based on the download option
if (downloadOption === 'sameWindow') {
console.log("same window")
// Open the file in the same window
window.location.href = URL.createObjectURL(blob);
} else if (downloadOption === 'newWindow') {
console.log("new window")
// Open the file in a new window
window.open(URL.createObjectURL(blob), '_blank');
} else {
console.log("else save")
// Download the file
const link = document.createElement('a');
link.href = URL.createObjectURL(blob);
link.download = filename;
link.click();
}
});
} else if (contentType.includes('json')) {
// Handle the JSON response
response.json().then(data => {
// Format the error message
const errorMessage = JSON.stringify(data, null, 2);
// Display the error message in an alert
alert(`An error occurred: ${errorMessage}`);
});
} else {
response.blob().then(blob => {
console.log("else save 2 zip")
// For ZIP files or other file types, just download the file
const link = document.createElement('a'); const link = document.createElement('a');
link.href = URL.createObjectURL(blob); link.href = URL.createObjectURL(blob);
link.download = filename; link.download = filename;
link.click(); link.click();
} });
}); }
} else if (contentType.includes('json')) { })
// Handle the JSON response .catch(error => {
response.json().then(data => { console.log("error listener")
// Format the error message
const errorMessage = JSON.stringify(data, null, 2);
// Display the error message in an alert
alert(`An error occurred: ${errorMessage}`);
});
} else {
response.blob().then(blob => {
console.log("else save 2 zip")
// For ZIP files or other file types, just download the file // Extract the error message and stack trace from the response
const link = document.createElement('a'); const errorMessage = error.message;
link.href = URL.createObjectURL(blob); const stackTrace = error.stack;
link.download = filename;
link.click(); // Create an error message to display to the user
}); const message = `${errorMessage}\n\n${stackTrace}`;
}
}) $('#submitBtn').text(submitButtonText);
.catch(error => {
console.log("error listener") // Display the error message to the user
alert(message);
// Extract the error message and stack trace from the response
const errorMessage = error.message; });
const stackTrace = error.stack;
// Create an error message to display to the user
const message = `${errorMessage}\n\n${stackTrace}`;
$('#submitBtn').text(submitButtonText);
// Display the error message to the user }
alert(message); $('#submitBtn').text(submitButtonText);
});
}); });
}
$('#submitBtn').text(submitButtonText);
});
async function submitMultiPdfForm(event, url) { async function submitMultiPdfForm(event, url) {
// Get the selected PDF files // Get the selected PDF files
let files = $('#fileInput-input')[0].files; let files = $('#fileInput-input')[0].files;
@ -297,7 +314,7 @@ document.addEventListener("DOMContentLoaded", function () {
if (!contentDisposition) { if (!contentDisposition) {
//throw new Error('Content-Disposition header not found for file ' + i); //throw new Error('Content-Disposition header not found for file ' + i);
} else { } else {
fileName = contentDisposition.split('filename=')[1].replace(/"/g, ''); fileName = contentDisposition.split('filename=')[1].replace(/"/g, '');
} }
console.log('Received response for file ' + i + ': ' + response); console.log('Received response for file ' + i + ': ' + response);
@ -371,10 +388,10 @@ document.addEventListener("DOMContentLoaded", function () {
} }
} }
function updateProgressBar(progressBar, files) { function updateProgressBar(progressBar, files) {
let progress = ((progressBar.attr('aria-valuenow') / files.length) * 100) + (100 / files.length); let progress = ((progressBar.attr('aria-valuenow') / files.length) * 100) + (100 / files.length);
progressBar.css('width', progress + '%'); progressBar.css('width', progress + '%');
progressBar.attr('aria-valuenow', parseInt(progressBar.attr('aria-valuenow')) + 1); progressBar.attr('aria-valuenow', parseInt(progressBar.attr('aria-valuenow')) + 1);
} }
@ -382,6 +399,92 @@ document.addEventListener("DOMContentLoaded", function () {
</script> </script>
<div class="custom-file-chooser">
<div class="custom-file">
<input type="file" class="custom-file-input" th:name="${name}" th:id="${name}+'-input'" th:accept="${accept}" multiple>
<label class="custom-file-label" th:for="${name}+'-input'" th:text="${inputText}"></label>
</div>
<div class="selected-files"></div>
</div>
<br>
<div id="progressBarContainer" style="display: none; position: relative;">
<div class="progress" style="height: 1rem;">
<div id="progressBar" class="progress-bar progress-bar-striped progress-bar-animated bg-success" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100" style="width: 0%;">
<span class="sr-only">Loading...</span>
</div>
</div>
</div>
<button type="button" class="btn btn-primary" id="show-game-btn" style="display:none;">Bored waiting?</button>
<div id="game-container-wrapper" class="game-container-wrapper" style="display: none;">
<div id="game-container">
<div id="lives">Lives: 3</div>
<div id="score">Score: 0</div>
<div id="high-score">High Score: 0</div>
<div id="level">Level: 1</div>
<img src="favicon.svg" class="player" id="player">
</div>
<style>
#game-container {
position: relative;
width: 100%;
height: 0;
padding-bottom: 75%; /* 4:3 aspect ratio */
background-color: transparent;
margin: auto;
overflow: hidden;
border: 2px solid black; /* Add border */
}
.pdf, .player, .projectile {
position: absolute;
}
.pdf {
width: 50px;
height: 50px;
}
.player {
width: 50px;
height: 50px;
}
.projectile {
background-color: black !important;
width: 5px;
height: 10px;
}
#score, #level, #lives, #high-score {
color: black;
font-family: sans-serif;
position: absolute;
font-size: calc(14px + 0.25vw); /* Reduced font size */
}
#score {
top: 10px;
left: 10px;
}
#lives {
top: 10px;
left: calc(7vw); /* Adjusted position */
}
#high-score {
top: 10px;
left: calc(14vw); /* Adjusted position */
}
#level {
top: 10px;
right: 10px;
}
</style>
</div>
<script th:inline="javascript"> <script th:inline="javascript">
document.addEventListener('DOMContentLoaded', function () { document.addEventListener('DOMContentLoaded', function () {

View file

@ -369,6 +369,12 @@ function compareVersions(version1, version2) {
<input type="range" class="custom-range" min="1" max="9" step="1" id="zipThreshold" value="4"> <input type="range" class="custom-range" min="1" max="9" step="1" id="zipThreshold" value="4">
<span id="zipThresholdValue" class="ml-2"></span> <span id="zipThresholdValue" class="ml-2"></span>
</div> </div>
<div class="form-group">
<div class="custom-control custom-checkbox">
<input type="checkbox" class="custom-control-input" id="boredWaiting">
<label class="custom-control-label" for="boredWaiting">Bored Waiting? :)</label>
</div>
</div>
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal" th:text="#{close}"></button> <button type="button" class="btn btn-secondary" data-dismiss="modal" th:text="#{close}"></button>
@ -386,6 +392,7 @@ function compareVersions(version1, version2) {
// Set the selected option in the dropdown // Set the selected option in the dropdown
document.getElementById('downloadOption').value = downloadOption; document.getElementById('downloadOption').value = downloadOption;
// Save the selected option to local storage when the dropdown value changes // Save the selected option to local storage when the dropdown value changes
document.getElementById('downloadOption').addEventListener( document.getElementById('downloadOption').addEventListener(
'change', 'change',
@ -403,6 +410,8 @@ function compareVersions(version1, version2) {
document.getElementById('zipThreshold').value = zipThreshold; document.getElementById('zipThreshold').value = zipThreshold;
document.getElementById('zipThresholdValue').textContent = zipThreshold; document.getElementById('zipThresholdValue').textContent = zipThreshold;
// Save the selected value to local storage when the slider value changes // Save the selected value to local storage when the slider value changes
document.getElementById('zipThreshold').addEventListener('input', function () { document.getElementById('zipThreshold').addEventListener('input', function () {
zipThreshold = this.value; zipThreshold = this.value;
@ -410,6 +419,15 @@ function compareVersions(version1, version2) {
localStorage.setItem('zipThreshold', zipThreshold); localStorage.setItem('zipThreshold', zipThreshold);
}); });
var boredWaiting = localStorage.getItem('boredWaiting') || 'disabled';
document.getElementById('boredWaiting').checked = boredWaiting === 'enabled';
document.getElementById('boredWaiting').addEventListener('change', function() {
boredWaiting = this.checked ? 'enabled' : 'disabled';
localStorage.setItem('boredWaiting', boredWaiting);
});
</script> </script>