1
0
mirror of https://github.com/Stirling-Tools/Stirling-PDF.git synced 2024-09-28 15:50:08 +02:00

HTML, CSS, JS and JAVA corrections (#810)

* CSS corrections

* HTML corrections

* JS corrections

* JAVA corrections

* remove tab

* CSS corrections 2

* JS corrections 2

* back to the roots

* max-linie 127

* add slash hr|br

* return bootstrap-icons.css

* return bootstrap-icons.min.css

* return bootstrap.min.css

* Update bootstrap-icons.css

* Update bootstrap-icons.min.css

* Update bootstrap-icons.min.css

* Update bootstrap.min.css

* CSS corrections

* HTML corrections

* JS corrections

* JAVA corrections

* remove tab

* CSS corrections 2

* JS corrections 2

* back to the roots

* max-linie 127

* add slash hr|br

* return bootstrap-icons.css

* Update bootstrap-icons.css

* Bootstrap CSS

* Update prism.css
This commit is contained in:
Ludy 2024-02-16 22:49:06 +01:00 committed by GitHub
parent 68f582bcb9
commit e4a76e96af
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
124 changed files with 9025 additions and 9424 deletions

View File

@ -33,6 +33,8 @@ public class AccountWebController {
return "redirect:/";
}
model.addAttribute("currentPage", "login");
if (request.getParameter("error") != null) {
model.addAttribute("error", request.getParameter("error"));
@ -112,6 +114,7 @@ public class AccountWebController {
model.addAttribute("role", user.get().getRolesAsString());
model.addAttribute("settings", settingsJson);
model.addAttribute("changeCredsFlag", user.get().isFirstLogin());
model.addAttribute("currentPage", "account");
}
} else {
return "redirect:/";

View File

@ -0,0 +1,4 @@
.buttons-container {
margin-top: 20px;
text-align: center;
}

View File

@ -0,0 +1,28 @@
#box-drag-container {
position: relative;
margin: 20px 0;
}
#pdf-canvas {
box-shadow: 0px 0px 8px rgba(0, 0, 0, 0.384);
width: 100%;
}
.draggable-buttons-box {
position: absolute;
top: 0;
padding: 10px;
width: 100%;
display: flex;
gap: 5px;
}
.draggable-buttons-box > button {
z-index: 10;
background-color: rgba(13, 110, 253, 0.1);
}
.draggable-canvas {
border: 1px solid red;
position: absolute;
touch-action: none;
user-select: none;
top: 0px;
left: 0;
}

View File

@ -1,5 +1,7 @@
/* Dark Mode Styles */
body, select, textarea {
body,
select,
textarea {
--body-background-color: 51, 51, 51;
--base-font-color: 255, 255, 255;
background-color: rgb(var(--body-background-color)) !important;
@ -53,7 +55,8 @@ table thead {
background-color: #333 !important;
border: 1px solid #444;
}
table th, table td {
table th,
table td {
border: 1px solid #444 !important;
color: white;
}
@ -120,16 +123,19 @@ hr {
color: #ffffff;
}
#global-buttons-container input:disabled::-webkit-input-placeholder { /* WebKit browsers */
color: #6E6865;
#global-buttons-container input:disabled::-webkit-input-placeholder {
/* WebKit browsers */
color: #6e6865;
}
#global-buttons-container input:disabled:-moz-placeholder { /* Mozilla Firefox 4 to 18 */
color: #6E6865;
#global-buttons-container input:disabled:-moz-placeholder {
/* Mozilla Firefox 4 to 18 */
color: #6e6865;
}
#global-buttons-container input:disabled::-moz-placeholder { /* Mozilla Firefox 19+ */
color: #6E6865;
#global-buttons-container input:disabled::-moz-placeholder {
/* Mozilla Firefox 19+ */
color: #6e6865;
}
#global-buttons-container input:disabled:-ms-input-placeholder { /* Internet Explorer 10+ */
color: #6E6865;
#global-buttons-container input:disabled:-ms-input-placeholder {
/* Internet Explorer 10+ */
color: #6e6865;
}

View File

@ -36,12 +36,12 @@
visibility: hidden !important;
}
html[lang-direction=ltr] .drag-manager_draghover img {
html[lang-direction="ltr"] .drag-manager_draghover img {
left: calc(50% + 62.5px) !important;
}
html[lang-direction=rtl] .drag-manager_draghover img {
left: 125px
html[lang-direction="rtl"] .drag-manager_draghover img {
left: 125px;
}
.drag-manager_dragging-container .hide-on-drag {
@ -51,9 +51,9 @@ html[lang-direction=rtl] .drag-manager_draghover img {
.drag-manager_endpoint {
width: 80px;
height: 100%;
background-color: #FFFFFF10;
background-color: #ffffff10;
transition: width 0.1s;
animation: end-drop-expand .3s ease;
animation: end-drop-expand 0.3s ease;
display: flex;
align-items: center;
justify-content: center;

View File

@ -0,0 +1,88 @@
h1 {
text-align: center;
margin-top: 10%;
}
p {
text-align: center;
margin-top: 2em;
}
.button:hover {
background-color: #005b7f;
}
.features-container {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(21rem, 3fr));
gap: 25px 30px;
}
.feature-card {
border: 1px solid rgba(0, 0, 0, 0.125);
border-radius: 0.25rem;
padding: 1.25rem;
display: flex;
flex-direction: column;
align-items: flex-start;
}
.feature-card .card-text {
flex: 1;
}
#support-section {
background-color: #f9f9f9;
padding: 4rem;
margin-top: 1rem;
text-align: center;
}
#support-section h1 {
margin-top: 0;
}
#support-section p {
margin-top: 0;
}
#button-group {
display: flex;
justify-content: center;
flex-wrap: wrap;
}
#github-button,
#discord-button {
display: inline-block;
padding: 1rem 2rem;
margin: 1rem;
background-color: #008cba;
color: #fff;
font-size: 1.2rem;
text-align: center;
text-decoration: none;
border-radius: 3rem;
transition: all 0.3s ease-in-out;
}
#github-button:hover,
#discord-button:hover,
#home-button:hover {
background-color: #005b7f;
}
#home-button {
display: block;
width: 200px;
height: 50px;
margin: 2em auto;
background-color: #008cba;
color: white;
text-align: center;
line-height: 50px;
text-decoration: none;
font-weight: bold;
border-radius: 25px;
transition: all 0.3s ease-in-out;
}

View File

@ -28,7 +28,7 @@
}
#helpModal .feature-card {
border: 1px solid rgba(0, 0, 0, .125);
border: 1px solid rgba(0, 0, 0, 0.125);
border-radius: 0.25rem;
padding: 1.25rem;
display: flex;
@ -61,11 +61,12 @@
flex-wrap: wrap;
}
#github-button, #discord-button {
#github-button,
#discord-button {
display: inline-block;
padding: 1rem 2rem;
margin: 1rem;
background-color: #008CBA;
background-color: #008cba;
color: #fff;
font-size: 1.2rem;
text-align: center;
@ -74,7 +75,9 @@
transition: all 0.3s ease-in-out;
}
#github-button:hover, #discord-button:hover, #home-button:hover {
#github-button:hover,
#discord-button:hover,
#home-button:hover {
background-color: #005b7f;
}
@ -83,7 +86,7 @@
width: 200px;
height: 50px;
margin: 2em auto;
background-color: #008CBA;
background-color: #008cba;
color: white;
text-align: center;
line-height: 50px;

View File

@ -0,0 +1,20 @@
#footer {
display: flex;
flex-direction: column; /* Stack children vertically */
justify-content: center;
align-items: center;
width: 100%;
}
.footer-center {
display: flex;
align-items: center; /* Center children horizontally */
flex-grow: 1;
}
.footer-powered-by {
margin-top: auto; /* Pushes the text to the bottom */
color: grey;
text-align: center; /* Centers the text inside the div */
width: 100%; /* Full width to center the text properly */
}

View File

@ -9,7 +9,9 @@
border: 2px solid black; /* Add border */
}
.pdf, .player, .projectile {
.pdf,
.player,
.projectile {
position: absolute;
}
.pdf {
@ -25,7 +27,10 @@
width: 5px;
height: 10px;
}
#score, #level, #lives, #high-score {
#score,
#level,
#lives,
#high-score {
color: black;
font-family: sans-serif;
position: absolute;

View File

@ -25,10 +25,10 @@
margin-right: auto;
}*/
html[lang-direction=ltr] * {
html[lang-direction="ltr"] * {
direction: ltr;
}
html[lang-direction=rtl] * {
html[lang-direction="rtl"] * {
direction: rtl;
text-align: right;
}

View File

@ -1,5 +1,5 @@
#searchBar {
background-image: url('../images/search.svg');
background-image: url("../images/search.svg");
background-position: 16px 16px;
background-repeat: no-repeat;
width: 100%;
@ -7,8 +7,6 @@
margin-bottom: 12px;
padding: 12px 20px 12px 40px;
border: 1px solid #ddd;
}
.dark-mode-search {
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' hei… 0 0-.115-.1zM12 6.5a5.5 5.5 0 1 1-11 0 5.5 5.5 0 0 1 11 0z'/%3E%3C/svg%3E") !important;
@ -17,8 +15,6 @@
border-color: #343a40 !important;
}
.features-container {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(15rem, 3fr));
@ -26,14 +22,16 @@
}
.feature-card {
border: 2px solid rgba(0, 0, 0, .25);
border: 2px solid rgba(0, 0, 0, 0.25);
border-radius: 0.25rem;
padding: 1.25rem;
display: flex;
flex-direction: column;
align-items: flex-start;
background: rgba(13, 110, 253, 0.05);
transition: transform 0.3s, border 0.3s;
transition:
transform 0.3s,
border 0.3s;
transform-origin: center center;
outline: 2px solid transparent;
}
@ -52,7 +50,7 @@
}
.feature-card:hover {
outline: 1px solid rgba(0, 0, 0, .5);
outline: 1px solid rgba(0, 0, 0, 0.5);
cursor: pointer;
transform: scale(1.1);
}

View File

@ -1,4 +1,3 @@
#image-highlighter {
position: fixed;
display: flex;
@ -8,14 +7,18 @@
visibility: hidden;
align-items: center;
justify-content: center;
transition: visbility 0.1s linear, background-color 0.1s linear;
transition:
visbility 0.1s linear,
background-color 0.1s linear;
}
#image-highlighter > * {
max-width: 80vw;
max-height: 80vh;
animation: image-highlight .1s linear;
transition: transform .1s linear, opacity .1s linear;
animation: image-highlight 0.1s linear;
transition:
transform 0.1s linear,
opacity 0.1s linear;
}
#image-highlighter > *.remove {

View File

@ -0,0 +1,9 @@
td a {
text-decoration: none;
}
td a:hover,
td a:focus {
text-decoration: underline;
/* Adds underline on hover/focus for clarity */
}

View File

@ -4,7 +4,6 @@ body {
--base-font-color: 33, 37, 41;
}
#global-buttons-container input {
background-color: #ffffff;
/*caret-color: #ffffff;*/

View File

@ -0,0 +1,111 @@
html,
body {
height: 100%;
}
body {
display: flex;
align-items: center;
padding-top: 40px;
padding-bottom: 40px;
background-color: #f5f5f5;
}
.form-signin {
width: 100%;
max-width: 330px;
padding: 15px;
margin: auto;
}
.form-signin .checkbox {
font-weight: 400;
}
.form-signin .form-floating:focus-within {
z-index: 2;
}
.form-signin input[type="text"] {
margin-bottom: -1px;
border-bottom-right-radius: 0;
border-bottom-left-radius: 0;
}
.form-signin input[type="password"] {
margin-bottom: 10px;
border-top-left-radius: 0;
border-top-right-radius: 0;
}
.container-flex {
display: flex;
flex-direction: column;
min-height: 100vh;
width: 100%; /* Set width to 100% */
align-items: center; /* Center its children horizontally */
}
.footer-bottom {
margin-top: auto;
}
body.light-mode input:-webkit-autofill,
body.light-mode input:-webkit-autofill:hover,
body.light-mode input:-webkit-autofill:focus,
body.light-mode input:-webkit-autofill:active {
-webkit-text-fill-color: #212529; /* Dark font color */
-webkit-box-shadow: 0 0 0 1000px #f8f9fa inset; /* Light background color */
}
/* Dark Mode */
body.dark-mode input:-webkit-autofill,
body.dark-mode input:-webkit-autofill:hover,
body.dark-mode input:-webkit-autofill:focus,
body.dark-mode input:-webkit-autofill:active {
-webkit-text-fill-color: #f8f9fa; /* Light font color */
-webkit-box-shadow: 0 0 0 1000px #212529 inset; /* Dark background color */
}
/* Light Mode */
body.light-mode .form-floating > input:focus + label {
color: #212529 !important; /* Dark text for light background */
}
/* Dark Mode */
body.dark-mode .form-floating > input:focus + label {
color: #fff !important; /* Light text for dark background */
}
body.light-mode .form-floating > label {
color: #212529 !important; /* Dark text for light background */
}
body.dark-mode .form-floating > label {
color: #fff !important; /* Light text for dark background */
}
/* Removing default styles for ul and li */
ul {
list-style: none;
padding: 0;
margin: 0;
}
li {
list-style: none;
}
/* Positioning the container of these elements to the top right */
.your-container-class {
position: absolute;
top: 0;
right: 0;
display: flex;
}
/* Styling for the dropdown */
.dropdown-menu {
min-width: 200px; /* or whatever width you prefer */
}
.navbar-icon {
width: 40px;
height: 40px;
}

View File

@ -25,5 +25,4 @@
.move-down span {
font-weight: bold;
font-size: 1.2em;
}

View File

@ -0,0 +1,119 @@
.multi-tool-container {
max-width: 95vw;
margin: 0 auto;
}
#global-buttons-container {
display: flex;
gap: 10px;
align-items: start;
background-color: rgba(13, 110, 253, 0.1);
border: 1px solid rgba(0, 0, 0, 0.25);
backdrop-filter: blur(2px);
top: 10px;
z-index: 10;
padding: 10px;
border-radius: 8px;
}
#global-buttons-container > * {
padding: 0.6rem 0.75rem;
}
#global-buttons-container svg {
width: 20px;
height: 20px;
}
#export-button {
margin-left: auto;
}
#pages-container-wrapper {
--background-color: rgba(0, 0, 0, 0.025);
--scroll-bar-color: #f1f1f1;
--scroll-bar-thumb: #888;
--scroll-bar-thumb-hover: #555;
background-color: var(--background-color);
width: 100%;
display: flex;
flex-direction: column;
padding: 10px 25px;
border-radius: 10px;
overflow-y: hidden;
overflow-x: auto;
min-height: 275px;
margin: 0 0 30px 0;
}
#pages-container {
margin: auto;
gap: 0px;
display: flex;
align-items: center;
justify-content: center;
}
/* width */
#pages-container-wrapper::-webkit-scrollbar {
width: 10px;
height: 10px;
}
/* Track */
#pages-container-wrapper::-webkit-scrollbar-track {
background: var(--scroll-bar-color);
}
/* Handle */
#pages-container-wrapper::-webkit-scrollbar-thumb {
border-radius: 10px;
background: var(--scroll-bar-thumb);
}
/* Handle on hover */
#pages-container-wrapper::-webkit-scrollbar-thumb:hover {
background: var(--scroll-bar-thumb-hover);
}
.page-container {
height: 250px;
display: flex;
align-items: center;
flex-direction: column-reverse;
aspect-ratio: 1;
text-align: center;
position: relative;
user-select: none;
transition: width 1s linear;
}
.page-container img {
/* max-width: calc(100% - 15px); */
max-height: calc(100% - 15px);
max-width: 237px;
display: block;
position: absolute;
left: 50%;
top: 50%;
translate: -50% -50%;
box-shadow: 0px 0px 8px rgba(0, 0, 0, 0.384);
border-radius: 4px;
transition: rotate 0.3s;
}
#add-pdf-button {
margin: 0 auto;
}
.page-number {
position: absolute;
top: 5px;
right: 5px;
color: white;
background-color: #007bff; /* Primary blue color */
padding: 3px 6px;
border-radius: 4px;
font-size: 12px;
z-index: 2;
}

View File

@ -1,8 +1,13 @@
#navbarSearch {
top: 100%;
right: 0;
transition: all 0.3s;
max-height: 0;
overflow: hidden;
}
#navbarSearch.show {
max-height: 300px; /* Adjust this to your desired max height */
}
#searchForm {
@ -14,6 +19,8 @@
max-height: 200px; /* Adjust this value as needed */
overflow-y: auto;
width: 100%;
max-width: 300px; /* Adjust to your preferred width */
transition: height 0.3s ease; /* Smooth height transition */
}
#searchResults .dropdown-item {
@ -34,7 +41,36 @@
text-overflow: ellipsis; /* Add ellipsis for long text */
}
#search-icon i {
font-size: 24px; /* Adjust this to your desired size */
transition: color 0.3s;
}
#search-icon:hover i {
color: #666; /* Adjust this to your hover color */
}
.search-input {
transition:
border 0.3s,
box-shadow 0.3s;
}
.search-input:focus {
border-color: #666; /* Adjust this to your focus color */
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); /* Adjust this to your desired shadow */
}
/* Set a fixed height and styling for each search result item */
.search-results a {
display: flex;
align-items: center;
gap: 10px; /* space between icon and text */
height: 40px; /* Adjust based on your design */
overflow: hidden; /* Prevent content from overflowing */
white-space: nowrap; /* Prevent text from wrapping to next line */
text-overflow: ellipsis; /* Truncate text if it's too long */
}
.main-icon {
width: 36px;
@ -64,7 +100,7 @@
}
.nav-item-separator::before {
content: '';
content: "";
position: absolute;
left: 0;
top: 10%; /* Adjust the top and bottom margins as needed */

View File

@ -1,4 +1,3 @@
.pdf-actions_button-container {
z-index: 2;
display: flex;
@ -46,11 +45,11 @@
right: -20px;
}
html[lang-direction=ltr] .pdf-actions_insert-file-button-container.right {
html[lang-direction="ltr"] .pdf-actions_insert-file-button-container.right {
display: none;
}
html[lang-direction=rtl] .pdf-actions_insert-file-button-container.left {
html[lang-direction="rtl"] .pdf-actions_insert-file-button-container.left {
display: none;
}
@ -64,12 +63,11 @@ html[lang-direction=rtl] .pdf-actions_insert-file-button-container.left {
translate: 0 -50%;
}
html[lang-direction=ltr] .pdf-actions_container:last-child > .pdf-actions_insert-file-button-container.right {
html[lang-direction="ltr"] .pdf-actions_container:last-child > .pdf-actions_insert-file-button-container.right {
display: block;
}
html[lang-direction=rtl] .pdf-actions_container:last-child > .pdf-actions_insert-file-button-container.left {
html[lang-direction="rtl"] .pdf-actions_container:last-child > .pdf-actions_insert-file-button-container.left {
display: block;
}

View File

@ -0,0 +1,21 @@
.btn-margin {
margin-right: 2px;
}
.bordered-box {
border: 1px solid #ddd;
padding: 20px;
margin: 20px;
width: 70%;
}
.center-element {
width: 80%;
text-align: center;
margin: auto;
}
.element-margin {
margin: 10px 0;
/* Adjust this value to increase/decrease the margin as needed */
}

View File

@ -1,37 +1,113 @@
/* Rainbow Mode Styles */
body {
background: linear-gradient(90deg, rgba(255,0,0,1) 0%, rgba(255,154,0,1) 10%, rgba(208,222,33,1) 20%, rgba(79,220,74,1) 30%, rgba(63,218,216,1) 40%, rgba(47,201,226,1) 50%, rgba(28,127,238,1) 60%, rgba(95,21,242,1) 70%, rgba(186,12,248,1) 80%, rgba(251,7,217,1) 90%, rgba(255,0,0,1) 100%);
background: linear-gradient(
90deg,
rgba(255, 0, 0, 1) 0%,
rgba(255, 154, 0, 1) 10%,
rgba(208, 222, 33, 1) 20%,
rgba(79, 220, 74, 1) 30%,
rgba(63, 218, 216, 1) 40%,
rgba(47, 201, 226, 1) 50%,
rgba(28, 127, 238, 1) 60%,
rgba(95, 21, 242, 1) 70%,
rgba(186, 12, 248, 1) 80%,
rgba(251, 7, 217, 1) 90%,
rgba(255, 0, 0, 1) 100%
);
color: #fff !important;
--body-background-color: 255, 255, 255;
--base-font-color: 33, 37, 41;
}
.dark-card {
background: linear-gradient(90deg, rgba(255,0,0,1) 0%, rgba(255,154,0,1) 10%, rgba(208,222,33,1) 20%, rgba(79,220,74,1) 30%, rgba(63,218,216,1) 40%, rgba(47,201,226,1) 50%, rgba(28,127,238,1) 60%, rgba(95,21,242,1) 70%, rgba(186,12,248,1) 80%, rgba(251,7,217,1) 90%, rgba(255,0,0,1) 100%) !important;
background: linear-gradient(
90deg,
rgba(255, 0, 0, 1) 0%,
rgba(255, 154, 0, 1) 10%,
rgba(208, 222, 33, 1) 20%,
rgba(79, 220, 74, 1) 30%,
rgba(63, 218, 216, 1) 40%,
rgba(47, 201, 226, 1) 50%,
rgba(28, 127, 238, 1) 60%,
rgba(95, 21, 242, 1) 70%,
rgba(186, 12, 248, 1) 80%,
rgba(251, 7, 217, 1) 90%,
rgba(255, 0, 0, 1) 100%
) !important;
color: white !important;
}
.jumbotron {
background: linear-gradient(90deg, rgba(255,0,0,1) 0%, rgba(255,154,0,1) 10%, rgba(208,222,33,1) 20%, rgba(79,220,74,1) 30%, rgba(63,218,216,1) 40%, rgba(47,201,226,1) 50%, rgba(28,127,238,1) 60%, rgba(95,21,242,1) 70%, rgba(186,12,248,1) 80%, rgba(251,7,217,1) 90%, rgba(255,0,0,1) 100%);
background: linear-gradient(
90deg,
rgba(255, 0, 0, 1) 0%,
rgba(255, 154, 0, 1) 10%,
rgba(208, 222, 33, 1) 20%,
rgba(79, 220, 74, 1) 30%,
rgba(63, 218, 216, 1) 40%,
rgba(47, 201, 226, 1) 50%,
rgba(28, 127, 238, 1) 60%,
rgba(95, 21, 242, 1) 70%,
rgba(186, 12, 248, 1) 80%,
rgba(251, 7, 217, 1) 90%,
rgba(255, 0, 0, 1) 100%
);
color: #fff !important;
}
.list-group {
background: linear-gradient(90deg, rgba(255,0,0,1) 0%, rgba(255,154,0,1) 10%, rgba(208,222,33,1) 20%, rgba(79,220,74,1) 30%, rgba(63,218,216,1) 40%, rgba(47,201,226,1) 50%, rgba(28,127,238,1) 60%, rgba(95,21,242,1) 70%, rgba(186,12,248,1) 80%, rgba(251,7,217,1) 90%, rgba(255,0,0,1) 100%) !important;
background: linear-gradient(
90deg,
rgba(255, 0, 0, 1) 0%,
rgba(255, 154, 0, 1) 10%,
rgba(208, 222, 33, 1) 20%,
rgba(79, 220, 74, 1) 30%,
rgba(63, 218, 216, 1) 40%,
rgba(47, 201, 226, 1) 50%,
rgba(28, 127, 238, 1) 60%,
rgba(95, 21, 242, 1) 70%,
rgba(186, 12, 248, 1) 80%,
rgba(251, 7, 217, 1) 90%,
rgba(255, 0, 0, 1) 100%
) !important;
color: fff !important;
}
.list-group-item {
background: linear-gradient(90deg, rgba(255,0,0,1) 0%, rgba(255,154,0,1) 10%, rgba(208,222,33,1) 20%, rgba(79,220,74,1) 30%, rgba(63,218,216,1) 40%, rgba(47,201,226,1) 50%, rgba(28,127,238,1) 60%, rgba(95,21,242,1) 70%, rgba(186,12,248,1) 80%, rgba(251,7,217,1) 90%, rgba(255,0,0,1) 100%) !important;
background: linear-gradient(
90deg,
rgba(255, 0, 0, 1) 0%,
rgba(255, 154, 0, 1) 10%,
rgba(208, 222, 33, 1) 20%,
rgba(79, 220, 74, 1) 30%,
rgba(63, 218, 216, 1) 40%,
rgba(47, 201, 226, 1) 50%,
rgba(28, 127, 238, 1) 60%,
rgba(95, 21, 242, 1) 70%,
rgba(186, 12, 248, 1) 80%,
rgba(251, 7, 217, 1) 90%,
rgba(255, 0, 0, 1) 100%
) !important;
color: fff !important;
}
#support-section {
background: linear-gradient(90deg, rgba(255,0,0,1) 0%, rgba(255,154,0,1) 10%, rgba(208,222,33,1) 20%, rgba(79,220,74,1) 30%, rgba(63,218,216,1) 40%, rgba(47,201,226,1) 50%, rgba(28,127,238,1) 60%, rgba(95,21,242,1) 70%, rgba(186,12,248,1) 80%, rgba(251,7,217,1) 90%, rgba(255,0,0,1) 100%) !important;
background: linear-gradient(
90deg,
rgba(255, 0, 0, 1) 0%,
rgba(255, 154, 0, 1) 10%,
rgba(208, 222, 33, 1) 20%,
rgba(79, 220, 74, 1) 30%,
rgba(63, 218, 216, 1) 40%,
rgba(47, 201, 226, 1) 50%,
rgba(28, 127, 238, 1) 60%,
rgba(95, 21, 242, 1) 70%,
rgba(186, 12, 248, 1) 80%,
rgba(251, 7, 217, 1) 90%,
rgba(255, 0, 0, 1) 100%
) !important;
}
#pages-container-wrapper {
--background-color: rgba(255, 255, 255, 0.046) !important;
--scroll-bar-color: #4c4c4c !important;
--scroll-bar-thumb: #d3d3d3 !important;
--scroll-bar-thumb-hover: #ffffff !important;
}

View File

@ -0,0 +1,29 @@
#pdf-preview {
margin: 0 auto;
display: block;
max-width: calc(100% - 30px);
max-height: calc(100% - 30px);
box-shadow: 0 0 4px rgba(100, 100, 100, 0.25);
transition: rotate 0.3s;
position: absolute;
top: 50%;
left: 50%;
translate: -50% -50%;
}
.previewContainer {
aspect-ratio: 1;
width: 100%;
border: 1px solid rgba(0, 0, 0, 0.125);
border-radius: 0.25rem;
margin: 1rem 0;
padding: 15px;
display: block;
overflow: hidden;
position: relative;
}
.buttonContainer {
display: flex;
justify-content: space-around;
}

View File

@ -0,0 +1,39 @@
select#font-select,
select#font-select option {
height: 60px; /* Adjust as needed */
font-size: 30px; /* Adjust as needed */
}
.drawing-pad-container {
text-align: center;
}
#drawing-pad-canvas {
background: rgba(125, 125, 125, 0.2);
width: 100%;
height: 300px;
}
#box-drag-container {
position: relative;
margin: 20px 0;
}
.draggable-buttons-box {
position: absolute;
top: 0;
padding: 10px;
width: 100%;
display: flex;
gap: 5px;
}
.draggable-buttons-box > button {
z-index: 10;
background-color: rgba(13, 110, 253, 0.1);
}
.draggable-canvas {
border: 1px solid red;
position: absolute;
touch-action: none;
user-select: none;
top: 0px;
left: 0;
}

View File

@ -0,0 +1,10 @@
.pdf-visual-aid {
width: 150px; /* Adjust as needed */
height: 200px; /* Adjust as needed */
border: 1px solid black; /* Represents the PDF page */
position: relative;
}
.line {
position: absolute;
background-color: red; /* Line color */
}

View File

@ -0,0 +1,41 @@
.a4container {
position: relative;
width: 50%;
aspect-ratio: 0.707;
border: 1px solid #ddd;
box-sizing: border-box;
background-color: white;
}
.pageNumber {
position: absolute;
display: flex;
justify-content: center;
align-items: center;
font-size: 1em;
color: #333;
cursor: pointer;
background-color: #ccc;
width: 15%;
height: 15%;
transform: translate(-50%, -50%);
}
.pageNumber:hover {
background-color: #eee;
}
#myForm {
display: flex;
justify-content: center;
align-items: center;
margin-top: 20px;
}
.selectedPosition {
background-color: #0a0;
}
.selectedPosition.selectedHovered {
background-color: #006600;
}

View File

@ -1,6 +1,4 @@
.tab-group {
}
.tab-container {

View File

@ -1,5 +1,5 @@
var toggleCount = 0
var lastToggleTime = Date.now()
var toggleCount = 0;
var lastToggleTime = Date.now();
var elements = {
lightModeStyles: null,
@ -11,18 +11,18 @@ var elements = {
navbar: null,
navIcons: null,
navDropdownMenus: null,
}
};
function getElements() {
elements.lightModeStyles = document.getElementById("light-mode-styles")
elements.darkModeStyles = document.getElementById("dark-mode-styles")
elements.rainbowModeStyles = document.getElementById("rainbow-mode-styles")
elements.darkModeIcon = document.getElementById("dark-mode-icon")
elements.searchBar = document.getElementById("searchBar")
elements.formControls = document.querySelectorAll(".form-control")
elements.navbar = document.querySelectorAll("nav.navbar")
elements.navIcons = document.querySelectorAll("nav .icon, .navbar-icon")
elements.navDropdownMenus = document.querySelectorAll("nav .dropdown-menu")
elements.lightModeStyles = document.getElementById("light-mode-styles");
elements.darkModeStyles = document.getElementById("dark-mode-styles");
elements.rainbowModeStyles = document.getElementById("rainbow-mode-styles");
elements.darkModeIcon = document.getElementById("dark-mode-icon");
elements.searchBar = document.getElementById("searchBar");
elements.formControls = document.querySelectorAll(".form-control");
elements.navbar = document.querySelectorAll("nav.navbar");
elements.navIcons = document.querySelectorAll("nav .icon, .navbar-icon");
elements.navDropdownMenus = document.querySelectorAll(".dropdown-menu");
}
function setMode(mode) {
var event = new CustomEvent("modeChanged", { detail: mode });
@ -48,22 +48,22 @@ function setMode(mode) {
elements.searchBar.classList.add("dark-mode-search");
}
if (elements && elements.formControls) {
elements.formControls.forEach(input => input.classList.add("bg-dark", "text-white"));
elements.formControls.forEach((input) => input.classList.add("bg-dark", "text-white"));
}
if (elements && elements.navbar) {
elements.navbar.forEach(navElement => {
elements.navbar.forEach((navElement) => {
navElement.classList.remove("navbar-light", "bg-light");
navElement.classList.add("navbar-dark", "bg-dark");
});
}
if (elements && elements.navDropdownMenus) {
elements.navDropdownMenus.forEach(menu => menu.classList.add("dropdown-menu-dark"));
elements.navDropdownMenus.forEach((menu) => menu.classList.add("dropdown-menu-dark"));
}
if (elements && elements.navIcons) {
elements.navIcons.forEach(icon => (icon.style.filter = "invert(1)"));
elements.navIcons.forEach((icon) => (icon.style.filter = "invert(1)"));
}
var tables = document.querySelectorAll(".table");
tables.forEach(table => {
tables.forEach((table) => {
table.classList.add("table-dark");
});
if (jumbotron) {
@ -78,22 +78,22 @@ function setMode(mode) {
elements.searchBar.classList.remove("dark-mode-search");
}
if (elements && elements.formControls) {
elements.formControls.forEach(input => input.classList.remove("bg-dark", "text-white"));
elements.formControls.forEach((input) => input.classList.remove("bg-dark", "text-white"));
}
if (elements && elements.navbar) {
elements.navbar.forEach(navElement => {
elements.navbar.forEach((navElement) => {
navElement.classList.remove("navbar-dark", "bg-dark");
navElement.classList.add("navbar-light", "bg-light");
});
}
if (elements && elements.navDropdownMenus) {
elements.navDropdownMenus.forEach(menu => menu.classList.remove("dropdown-menu-dark"));
elements.navDropdownMenus.forEach((menu) => menu.classList.remove("dropdown-menu-dark"));
}
if (elements && elements.navIcons) {
elements.navIcons.forEach(icon => (icon.style.filter = "none"));
elements.navIcons.forEach((icon) => (icon.style.filter = "none"));
}
var tables = document.querySelectorAll(".table-dark");
tables.forEach(table => {
tables.forEach((table) => {
table.classList.remove("table-dark");
});
if (jumbotron) {
@ -108,36 +108,36 @@ function setMode(mode) {
}
function toggleDarkMode() {
var currentTime = Date.now()
var currentTime = Date.now();
if (currentTime - lastToggleTime < 1000) {
toggleCount++
toggleCount++;
} else {
toggleCount = 1
toggleCount = 1;
}
lastToggleTime = currentTime
lastToggleTime = currentTime;
if (toggleCount >= 18) {
localStorage.setItem("dark-mode", "rainbow")
setMode("rainbow")
localStorage.setItem("dark-mode", "rainbow");
setMode("rainbow");
} else if (localStorage.getItem("dark-mode") == "on") {
localStorage.setItem("dark-mode", "off")
setMode("off")
localStorage.setItem("dark-mode", "off");
setMode("off");
} else {
localStorage.setItem("dark-mode", "on")
setMode("on")
localStorage.setItem("dark-mode", "on");
setMode("on");
}
}
document.addEventListener("DOMContentLoaded", function () {
getElements()
getElements();
var currentMode = localStorage.getItem("dark-mode")
var currentMode = localStorage.getItem("dark-mode");
if (currentMode === "on" || currentMode === "off" || currentMode === "rainbow") {
setMode(currentMode)
setMode(currentMode);
} else if (window.matchMedia && window.matchMedia("(prefers-color-scheme: dark)").matches) {
setMode("on")
setMode("on");
} else {
setMode("off")
setMode("off");
}
var darkModeToggle = document.getElementById("dark-mode-toggle");
@ -147,4 +147,4 @@ document.addEventListener("DOMContentLoaded", function () {
toggleDarkMode();
});
}
})
});

View File

@ -8,11 +8,11 @@ function showErrorBanner(message, stackTrace) {
let firstErrorOccurred = false;
$(document).ready(function () {
$('form').submit(async function(event) {
$("form").submit(async function (event) {
event.preventDefault();
firstErrorOccurred = false;
const url = this.action;
const files = $('#fileInput-input')[0].files;
const files = $("#fileInput-input")[0].files;
const formData = new FormData(this);
// Remove empty file entries
@ -21,53 +21,51 @@ $(document).ready(function() {
formData.delete(key);
}
}
const override = $('#override').val() || '';
const originalButtonText = $('#submitBtn').text();
$('#submitBtn').text('Processing...');
const override = $("#override").val() || "";
const originalButtonText = $("#submitBtn").text();
$("#submitBtn").text("Processing...");
console.log(override);
try {
if (remoteCall === true) {
if (override === 'multi' || (!multiple && files.length > 1) && override !== 'single' ) {
if (override === "multi" || (!multiple && files.length > 1 && override !== "single")) {
await submitMultiPdfForm(url, files);
} else {
await handleSingleDownload(url, formData);
}
}
$('#submitBtn').text(originalButtonText);
$("#submitBtn").text(originalButtonText);
} catch (error) {
handleDownloadError(error);
$('#submitBtn').text(originalButtonText);
$("#submitBtn").text(originalButtonText);
console.error(error);
}
});
});
async function handleSingleDownload(url, formData, isMulti = false, isZip = false) {
try {
const response = await fetch(url, { method: 'POST', body: formData });
const contentType = response.headers.get('content-type');
const response = await fetch(url, { method: "POST", body: formData });
const contentType = response.headers.get("content-type");
if (!response.ok) {
if (contentType && contentType.includes('application/json')) {
if (contentType && contentType.includes("application/json")) {
return handleJsonResponse(response);
console.error('Throwing error banner, response was not okay');
console.error("Throwing error banner, response was not okay");
}
throw new Error(`HTTP error! status: ${response.status}`);
}
const contentDisposition = response.headers.get('Content-Disposition');
const contentDisposition = response.headers.get("Content-Disposition");
let filename = getFilenameFromContentDisposition(contentDisposition);
const blob = await response.blob();
if (contentType.includes('application/pdf') || contentType.includes('image/')) {
if (contentType.includes("application/pdf") || contentType.includes("image/")) {
return handleResponse(blob, filename, !isMulti, isZip);
} else {
return handleResponse(blob, filename, false, isZip);
}
} catch (error) {
console.error('Error in handleSingleDownload:', error);
console.error("Error in handleSingleDownload:", error);
throw error; // Re-throw the error if you want it to be handled higher up.
}
}
@ -75,43 +73,44 @@ async function handleSingleDownload(url, formData, isMulti = false , isZip = fal
function getFilenameFromContentDisposition(contentDisposition) {
let filename;
if (contentDisposition && contentDisposition.indexOf('attachment') !== -1) {
filename = decodeURIComponent(contentDisposition.split('filename=')[1].replace(/"/g, '')).trim();
if (contentDisposition && contentDisposition.indexOf("attachment") !== -1) {
filename = decodeURIComponent(contentDisposition.split("filename=")[1].replace(/"/g, "")).trim();
} else {
// If the Content-Disposition header is not present or does not contain the filename, use a default filename
filename = 'download';
filename = "download";
}
return filename;
}
async function handleJsonResponse(response) {
const json = await response.json();
const errorMessage = JSON.stringify(json, null, 2);
if (errorMessage.toLowerCase().includes('the password is incorrect') || errorMessage.toLowerCase().includes('Password is not provided') || errorMessage.toLowerCase().includes('PDF contains an encryption dictionary')) {
if (
errorMessage.toLowerCase().includes("the password is incorrect") ||
errorMessage.toLowerCase().includes("Password is not provided") ||
errorMessage.toLowerCase().includes("PDF contains an encryption dictionary")
) {
if (!firstErrorOccurred) {
firstErrorOccurred = true;
alert(pdfPasswordPrompt);
}
} else {
showErrorBanner(json.error + ':' + json.message, json.trace);
showErrorBanner(json.error + ":" + json.message, json.trace);
}
}
async function handleResponse(blob, filename, considerViewOptions = false, isZip = false) {
if (!blob) return;
const downloadOption = localStorage.getItem('downloadOption');
const downloadOption = localStorage.getItem("downloadOption");
if (considerViewOptions) {
if (downloadOption === 'sameWindow') {
if (downloadOption === "sameWindow") {
const url = URL.createObjectURL(blob);
window.location.href = url;
return;
} else if (downloadOption === 'newWindow') {
} else if (downloadOption === "newWindow") {
const url = URL.createObjectURL(blob);
window.open(url, '_blank');
window.open(url, "_blank");
return;
}
}
@ -130,11 +129,11 @@ let urls = []; // An array to hold all the URLs
function downloadFile(blob, filename) {
if (!(blob instanceof Blob)) {
console.error('Invalid blob passed to downloadFile function');
console.error("Invalid blob passed to downloadFile function");
return;
}
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
const a = document.createElement("a");
a.href = url;
a.download = filename;
a.click();
@ -143,26 +142,23 @@ function downloadFile(blob, filename) {
return { filename, blob };
}
async function submitMultiPdfForm(url, files) {
const zipThreshold = parseInt(localStorage.getItem('zipThreshold'), 10) || 4;
const zipThreshold = parseInt(localStorage.getItem("zipThreshold"), 10) || 4;
const zipFiles = files.length > zipThreshold;
let jszip = null;
// Show the progress bar
$('#progressBarContainer').show();
$("#progressBarContainer").show();
// Initialize the progress bar
let progressBar = $('#progressBar');
progressBar.css('width', '0%');
progressBar.attr('aria-valuenow', 0);
progressBar.attr('aria-valuemax', files.length);
let progressBar = $("#progressBar");
progressBar.css("width", "0%");
progressBar.attr("aria-valuenow", 0);
progressBar.attr("aria-valuemax", files.length);
if (zipFiles) {
jszip = new JSZip();
}
// Get the form with the method attribute set to POST
let postForm = document.querySelector('form[method="POST"]');
@ -174,7 +170,7 @@ async function submitMultiPdfForm(url, files) {
console.log("No form with POST method found.");
}
//Remove file to reuse parameters for other runs
formData.delete('fileInput');
formData.delete("fileInput");
// Remove empty file entries
for (let [key, value] of formData.entries()) {
if (value instanceof File && !value.name) {
@ -188,14 +184,14 @@ async function submitMultiPdfForm(url, files) {
}
for (const chunk of chunks) {
const promises = chunk.map(async file => {
const promises = chunk.map(async (file) => {
let fileFormData = new FormData();
fileFormData.append('fileInput', file);
fileFormData.append("fileInput", file);
console.log(fileFormData);
// Add other form data
for (let pair of formData.entries()) {
fileFormData.append(pair[0], pair[1]);
console.log(pair[0]+ ', ' + pair[1]);
console.log(pair[0] + ", " + pair[1]);
}
try {
@ -213,7 +209,6 @@ async function submitMultiPdfForm(url, files) {
}
});
await Promise.all(promises);
}
if (zipFiles) {
@ -221,21 +216,19 @@ async function submitMultiPdfForm(url, files) {
const content = await jszip.generateAsync({ type: "blob" });
downloadFile(content, "files.zip");
} catch (error) {
console.error('Error generating ZIP file: ' + error);
console.error("Error generating ZIP file: " + error);
}
}
progressBar.css('width', '100%');
progressBar.attr('aria-valuenow', Array.from(files).length);
progressBar.css("width", "100%");
progressBar.attr("aria-valuenow", Array.from(files).length);
}
function updateProgressBar(progressBar, files) {
let progress = ((progressBar.attr('aria-valuenow') / files.length) * 100) + (100 / files.length);
progressBar.css('width', progress + '%');
progressBar.attr('aria-valuenow', parseInt(progressBar.attr('aria-valuenow')) + 1);
let progress = (progressBar.attr("aria-valuenow") / files.length) * 100 + 100 / files.length;
progressBar.css("width", progress + "%");
progressBar.attr("aria-valuenow", parseInt(progressBar.attr("aria-valuenow")) + 1);
}
window.addEventListener('unload', () => {
window.addEventListener("unload", () => {
for (const url of urls) {
URL.revokeObjectURL(url);
}

View File

@ -1,24 +1,23 @@
const DraggableUtils = {
boxDragContainer: document.getElementById('box-drag-container'),
pdfCanvas: document.getElementById('pdf-canvas'),
boxDragContainer: document.getElementById("box-drag-container"),
pdfCanvas: document.getElementById("pdf-canvas"),
nextId: 0,
pdfDoc: null,
pageIndex: 0,
documentsMap: new Map(),
init() {
interact('.draggable-canvas')
interact(".draggable-canvas")
.draggable({
listeners: {
move: (event) => {
const target = event.target;
const x = (parseFloat(target.getAttribute('data-bs-x')) || 0) + event.dx;
const y = (parseFloat(target.getAttribute('data-bs-y')) || 0) + event.dy;
const x = (parseFloat(target.getAttribute("data-bs-x")) || 0) + event.dx;
const y = (parseFloat(target.getAttribute("data-bs-y")) || 0) + event.dy;
target.style.transform = `translate(${x}px, ${y}px)`;
target.setAttribute('data-bs-x', x);
target.setAttribute('data-bs-y', y);
target.setAttribute("data-bs-x", x);
target.setAttribute("data-bs-y", y);
this.onInteraction(target);
},
@ -28,9 +27,9 @@ const DraggableUtils = {
edges: { left: true, right: true, bottom: true, top: true },
listeners: {
move: (event) => {
var target = event.target
var x = (parseFloat(target.getAttribute('data-bs-x')) || 0)
var y = (parseFloat(target.getAttribute('data-bs-y')) || 0)
var target = event.target;
var x = parseFloat(target.getAttribute("data-bs-x")) || 0;
var y = parseFloat(target.getAttribute("data-bs-y")) || 0;
// check if control key is pressed
if (event.ctrlKey) {
@ -49,18 +48,18 @@ const DraggableUtils = {
event.rect.height = height;
}
target.style.width = event.rect.width + 'px'
target.style.height = event.rect.height + 'px'
target.style.width = event.rect.width + "px";
target.style.height = event.rect.height + "px";
// translate when resizing from top or left edges
x += event.deltaRect.left
y += event.deltaRect.top
x += event.deltaRect.left;
y += event.deltaRect.top;
target.style.transform = 'translate(' + x + 'px,' + y + 'px)'
target.style.transform = "translate(" + x + "px," + y + "px)";
target.setAttribute('data-bs-x', x)
target.setAttribute('data-bs-y', y)
target.textContent = Math.round(event.rect.width) + '\u00D7' + Math.round(event.rect.height)
target.setAttribute("data-bs-x", x);
target.setAttribute("data-bs-y", y);
target.textContent = Math.round(event.rect.width) + "\u00D7" + Math.round(event.rect.height);
this.onInteraction(target);
},
@ -79,17 +78,17 @@ const DraggableUtils = {
},
createDraggableCanvas() {
const createdCanvas = document.createElement('canvas');
const createdCanvas = document.createElement("canvas");
createdCanvas.id = `draggable-canvas-${this.nextId++}`;
createdCanvas.classList.add("draggable-canvas");
const x = 0;
const y = 20;
createdCanvas.style.transform = `translate(${x}px, ${y}px)`;
createdCanvas.setAttribute('data-bs-x', x);
createdCanvas.setAttribute('data-bs-y', y);
createdCanvas.setAttribute("data-bs-x", x);
createdCanvas.setAttribute("data-bs-y", y);
createdCanvas.onclick = e => this.onInteraction(e.target);
createdCanvas.onclick = (e) => this.onInteraction(e.target);
this.boxDragContainer.appendChild(createdCanvas);
return createdCanvas;
@ -127,11 +126,11 @@ const DraggableUtils = {
var myContext = createdCanvas.getContext("2d");
myContext.drawImage(myImage, 0, 0);
resolve(createdCanvas);
}
})
};
});
},
deleteAllDraggableCanvases() {
this.boxDragContainer.querySelectorAll(".draggable-canvas").forEach(el => el.remove());
this.boxDragContainer.querySelectorAll(".draggable-canvas").forEach((el) => el.remove());
},
deleteDraggableCanvas(element) {
if (element) {
@ -149,8 +148,14 @@ const DraggableUtils = {
}
const elements = [...this.boxDragContainer.querySelectorAll(".draggable-canvas")];
const draggablesData = elements.map(el => {return{element:el, offsetWidth:el.offsetWidth, offsetHeight:el.offsetHeight}});
elements.forEach(el => this.boxDragContainer.removeChild(el));
const draggablesData = elements.map((el) => {
return {
element: el,
offsetWidth: el.offsetWidth,
offsetHeight: el.offsetHeight,
};
});
elements.forEach((el) => this.boxDragContainer.removeChild(el));
pagesMap[this.pageIndex] = draggablesData;
pagesMap[this.pageIndex + "-offsetWidth"] = this.pdfCanvas.offsetWidth;
@ -167,7 +172,7 @@ const DraggableUtils = {
const draggablesData = pagesMap[this.pageIndex];
if (draggablesData) {
draggablesData.forEach(draggableData => this.boxDragContainer.appendChild(draggableData.element));
draggablesData.forEach((draggableData) => this.boxDragContainer.appendChild(draggableData.element));
}
this.documentsMap.set(this.pdfDoc, pagesMap);
@ -192,7 +197,7 @@ const DraggableUtils = {
// render the page onto the canvas
var renderContext = {
canvasContext: this.pdfCanvas.getContext("2d"),
viewport: page.getViewport({ scale: 1 })
viewport: page.getViewport({ scale: 1 }),
};
await page.render(renderContext).promise;
@ -213,12 +218,12 @@ const DraggableUtils = {
}
},
parseTransform(element) {
},
parseTransform(element) {},
async getOverlayedPdfDocument() {
const pdfBytes = await this.pdfDoc.getData();
const pdfDocModified = await PDFLib.PDFDocument.load(pdfBytes, { ignoreEncryption: true });
const pdfDocModified = await PDFLib.PDFDocument.load(pdfBytes, {
ignoreEncryption: true,
});
this.storePageContents();
const pagesMap = this.documentsMap.get(this.pdfDoc);
@ -241,7 +246,7 @@ const DraggableUtils = {
const pdfImageObject = await pdfDocModified.embedPng(draggableImgBytes);
// calculate the position in the pdf document
const tansform = draggableElement.style.transform.replace(/[^.,-\d]/g, '');
const tansform = draggableElement.style.transform.replace(/[^.,-\d]/g, "");
const transformComponents = tansform.split(",");
const draggablePositionPixels = {
x: parseFloat(transformComponents[0]),
@ -254,13 +259,13 @@ const DraggableUtils = {
y: draggablePositionPixels.y / offsetHeight,
width: draggablePositionPixels.width / offsetWidth,
height: draggablePositionPixels.height / offsetHeight,
}
};
const draggablePositionPdf = {
x: draggablePositionRelative.x * page.getWidth(),
y: draggablePositionRelative.y * page.getHeight(),
width: draggablePositionRelative.width * page.getWidth(),
height: draggablePositionRelative.height * page.getHeight(),
}
};
// draw the image
page.drawImage(pdfImageObject, {
@ -275,7 +280,7 @@ const DraggableUtils = {
this.loadPageContents();
return pdfDocModified;
},
}
};
document.addEventListener("DOMContentLoaded", () => {
DraggableUtils.init();

View File

@ -13,10 +13,10 @@ function toggletrace() {
}
function copytrace() {
var flip = false
var flip = false;
if (!traceVisible) {
toggletrace()
flip = true
toggletrace();
flip = true;
}
var traceContent = document.getElementById("traceContent");
var range = document.createRange();
@ -26,7 +26,7 @@ function copytrace() {
document.execCommand("copy");
window.getSelection().removeAllRanges();
if (flip) {
toggletrace()
toggletrace();
}
}
@ -46,5 +46,5 @@ function adjustContainerHeight() {
}
}
function showHelp() {
$('#helpModal').modal('show');
$("#helpModal").modal("show");
}

View File

@ -1,24 +1,24 @@
function updateFavoritesDropdown() {
var dropdown = document.querySelector('#favoritesDropdown');
var dropdown = document.querySelector("#favoritesDropdown");
// Check if dropdown exists
if (!dropdown) {
console.error('Dropdown element with ID "favoritesDropdown" not found!');
return; // Exit the function
}
dropdown.innerHTML = ''; // Clear the current favorites
dropdown.innerHTML = ""; // Clear the current favorites
var hasFavorites = false;
for (var i = 0; i < localStorage.length; i++) {
var key = localStorage.key(i);
if (localStorage.getItem(key) === 'favorite') {
if (localStorage.getItem(key) === "favorite") {
// Find the corresponding navbar entry
var navbarEntry = document.querySelector(`a[href='${key}']`);
if (navbarEntry) {
// Create a new dropdown entry
var dropdownItem = document.createElement('a');
dropdownItem.className = 'dropdown-item';
var dropdownItem = document.createElement("a");
dropdownItem.className = "dropdown-item";
dropdownItem.href = navbarEntry.href;
dropdownItem.innerHTML = navbarEntry.innerHTML;
dropdown.appendChild(dropdownItem);
@ -31,15 +31,15 @@ function updateFavoritesDropdown() {
// Show or hide the default item based on whether there are any favorites
if (!hasFavorites) {
var defaultItem = document.createElement('a');
defaultItem.className = 'dropdown-item';
var defaultItem = document.createElement("a");
defaultItem.className = "dropdown-item";
defaultItem.textContent = noFavourites;
dropdown.appendChild(defaultItem);
}
}
// Ensure that the DOM content has been fully loaded before calling the function
document.addEventListener('DOMContentLoaded', function() {
console.log('DOMContentLoaded event fired');
document.addEventListener("DOMContentLoaded", function () {
console.log("DOMContentLoaded event fired");
updateFavoritesDropdown();
});

View File

@ -1,11 +1,11 @@
document.addEventListener('DOMContentLoaded', function() {
document.querySelectorAll('.custom-file-chooser').forEach(setupFileInput);
document.addEventListener("DOMContentLoaded", function () {
document.querySelectorAll(".custom-file-chooser").forEach(setupFileInput);
});
function setupFileInput(chooser) {
const elementId = chooser.getAttribute('data-bs-element-id');
const filesSelected = chooser.getAttribute('data-bs-files-selected');
const pdfPrompt = chooser.getAttribute('data-bs-pdf-prompt');
const elementId = chooser.getAttribute("data-bs-element-id");
const filesSelected = chooser.getAttribute("data-bs-files-selected");
const pdfPrompt = chooser.getAttribute("data-bs-pdf-prompt");
let allFiles = [];
let overlay;
@ -14,21 +14,21 @@ function setupFileInput(chooser) {
const dragenterListener = function () {
dragCounter++;
if (!overlay) {
overlay = document.createElement('div');
overlay.style.position = 'fixed';
overlay = document.createElement("div");
overlay.style.position = "fixed";
overlay.style.top = 0;
overlay.style.left = 0;
overlay.style.width = '100%';
overlay.style.height = '100%';
overlay.style.background = 'rgba(0, 0, 0, 0.5)';
overlay.style.color = '#fff';
overlay.style.zIndex = '1000';
overlay.style.display = 'flex';
overlay.style.alignItems = 'center';
overlay.style.justifyContent = 'center';
overlay.style.pointerEvents = 'none';
overlay.innerHTML = '<p>Drop files anywhere to upload</p>';
document.getElementById('content-wrap').appendChild(overlay);
overlay.style.width = "100%";
overlay.style.height = "100%";
overlay.style.background = "rgba(0, 0, 0, 0.5)";
overlay.style.color = "#fff";
overlay.style.zIndex = "1000";
overlay.style.display = "flex";
overlay.style.alignItems = "center";
overlay.style.justifyContent = "center";
overlay.style.pointerEvents = "none";
overlay.innerHTML = "<p>Drop files anywhere to upload</p>";
document.getElementById("content-wrap").appendChild(overlay);
}
};
@ -52,7 +52,7 @@ function setupFileInput(chooser) {
}
const dataTransfer = new DataTransfer();
allFiles.forEach(file => dataTransfer.items.add(file));
allFiles.forEach((file) => dataTransfer.items.add(file));
const fileInput = document.getElementById(elementId);
fileInput.files = dataTransfer.files;
@ -64,10 +64,10 @@ function setupFileInput(chooser) {
dragCounter = 0;
fileInput.dispatchEvent(new Event('change', { bubbles: true }));
fileInput.dispatchEvent(new Event("change", { bubbles: true }));
};
['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => {
["dragenter", "dragover", "dragleave", "drop"].forEach((eventName) => {
document.body.addEventListener(eventName, preventDefaults, false);
});
@ -76,9 +76,9 @@ function setupFileInput(chooser) {
e.stopPropagation();
}
document.body.addEventListener('dragenter', dragenterListener);
document.body.addEventListener('dragleave', dragleaveListener);
document.body.addEventListener('drop', dropListener);
document.body.addEventListener("dragenter", dragenterListener);
document.body.addEventListener("dragleave", dragleaveListener);
document.body.addEventListener("drop", dropListener);
$("#" + elementId).on("change", function (e) {
allFiles = Array.from(e.target.files);
@ -87,16 +87,19 @@ function setupFileInput(chooser) {
function handleFileInputChange(inputElement) {
const files = allFiles;
const fileNames = files.map(f => f.name);
const fileNames = files.map((f) => f.name);
const selectedFilesContainer = $(inputElement).siblings(".selected-files");
selectedFilesContainer.empty();
fileNames.forEach(fileName => {
fileNames.forEach((fileName) => {
selectedFilesContainer.append("<div>" + fileName + "</div>");
});
if (fileNames.length === 1) {
$(inputElement).siblings(".custom-file-label").addClass("selected").html(fileNames[0]);
} else if (fileNames.length > 1) {
$(inputElement).siblings(".custom-file-label").addClass("selected").html(fileNames.length + " " + filesSelected);
$(inputElement)
.siblings(".custom-file-label")
.addClass("selected")
.html(fileNames.length + " " + filesSelected);
} else {
$(inputElement).siblings(".custom-file-label").addClass("selected").html(pdfPrompt);
}

View File

@ -1,17 +1,17 @@
function initializeGame() {
const gameContainer = document.getElementById('game-container');
const player = document.getElementById('player');
const gameContainer = document.getElementById("game-container");
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';
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 levelElement = document.getElementById('level');
const livesElement = document.getElementById('lives');
const highScoreElement = document.getElementById('high-score');
const scoreElement = document.getElementById("score");
const levelElement = document.getElementById("level");
const livesElement = document.getElementById("lives");
const highScoreElement = document.getElementById("high-score");
let pdfSize = gameContainer.clientWidth * 0.0625; // 5% of container width
let projectileWidth = gameContainer.clientWidth * 0.00625; // 0.00625; // 0.5% of container width
@ -23,12 +23,9 @@ function initializeGame() {
let lastProjectileTime = 0;
let lives = 3;
let highScore = localStorage.getItem('highScore') ? parseInt(localStorage.getItem('highScore')) : 0;
let highScore = localStorage.getItem("highScore") ? parseInt(localStorage.getItem("highScore")) : 0;
updateHighScore();
const keysPressed = {};
const pdfs = [];
const projectiles = [];
@ -38,13 +35,13 @@ function initializeGame() {
let gameOver = false;
function handleKeys() {
if (keysPressed['ArrowLeft']) {
if (keysPressed["ArrowLeft"]) {
playerX -= 10;
}
if (keysPressed['ArrowRight']) {
if (keysPressed["ArrowRight"]) {
playerX += 10;
}
if (keysPressed[' '] && !gameOver) {
if (keysPressed[" "] && !gameOver) {
const currentTime = new Date().getTime();
if (currentTime - lastProjectileTime >= fireRate) {
shootProjectile();
@ -54,69 +51,60 @@ function initializeGame() {
updatePlayerPosition();
}
document.addEventListener('keydown', (event) => {
if (event.key === ' ') {
document.addEventListener("keydown", (event) => {
if (event.key === " ") {
event.preventDefault();
}
keysPressed[event.key] = true;
handleKeys();
});
document.addEventListener('keyup', (event) => {
document.addEventListener("keyup", (event) => {
keysPressed[event.key] = false;
});
function updatePlayerPosition() {
player.style.left = playerX + 'px';
player.style.bottom = playerY + 'px';
player.style.left = playerX + "px";
player.style.bottom = playerY + "px";
}
function updateLives() {
livesElement.textContent = 'Lives: ' + lives;
livesElement.textContent = "Lives: " + lives;
}
function updateHighScore() {
highScoreElement.textContent = 'High Score: ' + highScore;
highScoreElement.textContent = "High Score: " + highScore;
}
function shootProjectile() {
const projectile = document.createElement('div');
projectile.classList.add('projectile');
projectile.style.backgroundColor = 'black';
projectile.style.width = projectileWidth + 'px';
projectile.style.height = projectileHeight + 'px';
projectile.style.left = (playerX + playerSize / 2 - projectileWidth / 2) + 'px';
projectile.style.top = (gameContainer.clientHeight - playerY - playerSize) + 'px';
const projectile = document.createElement("div");
projectile.classList.add("projectile");
projectile.style.backgroundColor = "black";
projectile.style.width = projectileWidth + "px";
projectile.style.height = projectileHeight + "px";
projectile.style.left = playerX + playerSize / 2 - projectileWidth / 2 + "px";
projectile.style.top = gameContainer.clientHeight - playerY - playerSize + "px";
gameContainer.appendChild(projectile);
projectiles.push(projectile);
}
function spawnPdf() {
const pdf = document.createElement('img');
pdf.src = 'images/file-earmark-pdf.svg';
pdf.classList.add('pdf');
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';
const pdf = document.createElement("img");
pdf.src = "images/file-earmark-pdf.svg";
pdf.classList.add("pdf");
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";
gameContainer.appendChild(pdf);
pdfs.push(pdf);
}
function resetEnemies() {
pdfs.forEach((pdf) => gameContainer.removeChild(pdf));
pdfs.length = 0;
}
function updateGame() {
if (gameOver || paused) return;
@ -138,9 +126,8 @@ function initializeGame() {
endGame();
return;
}
} else {
pdf.style.top = pdfY + 'px';
pdf.style.top = pdfY + "px";
// Check for collision with player
if (collisionDetected(player, pdf)) {
@ -153,7 +140,7 @@ function initializeGame() {
}
}
}
};
}
projectiles.forEach((projectile, projectileIndex) => {
const projectileY = parseInt(projectile.style.top) - 10;
@ -161,7 +148,7 @@ function initializeGame() {
gameContainer.removeChild(projectile);
projectiles.splice(projectileIndex, 1);
} else {
projectile.style.top = projectileY + 'px';
projectile.style.top = projectileY + "px";
}
for (let pdfIndex = 0; pdfIndex < pdfs.length; pdfIndex++) {
@ -200,27 +187,23 @@ function initializeGame() {
updateScore();
updateLives();
levelElement.textContent = 'Level: ' + level;
levelElement.textContent = "Level: " + level;
pdfSpeed = 1;
clearTimeout(spawnPdfTimeout); // Clear the existing spawnPdfTimeout
setTimeout(updateGame, 1000 / 60);
spawnPdfInterval();
}
function updateScore() {
scoreElement.textContent = 'Score: ' + score;
scoreElement.textContent = "Score: " + score;
checkLevelUp();
}
function checkLevelUp() {
const newLevel = Math.floor(score / 100) + 1;
if (newLevel > level) {
level = newLevel;
levelElement.textContent = 'Level: ' + level;
levelElement.textContent = "Level: " + level;
pdfSpeed += 0.2;
}
}
@ -228,28 +211,20 @@ function initializeGame() {
function collisionDetected(a, b) {
const rectA = a.getBoundingClientRect();
const rectB = b.getBoundingClientRect();
return (
rectA.left < rectB.right &&
rectA.right > rectB.left &&
rectA.top < rectB.bottom &&
rectA.bottom > rectB.top
);
return rectA.left < rectB.right && rectA.right > rectB.left && rectA.top < rectB.bottom && rectA.bottom > rectB.top;
}
function endGame() {
gameOver = true;
if (score > highScore) {
highScore = score;
localStorage.setItem('highScore', highScore);
localStorage.setItem("highScore", highScore);
updateHighScore();
}
alert('Game Over! Your final score is: ' + score);
document.getElementById('game-container-wrapper').close();
alert("Game Over! Your final score is: " + score);
document.getElementById("game-container-wrapper").close();
}
let spawnPdfTimeout;
const BASE_SPAWN_INTERVAL_MS = 1250; // milliseconds before a new enemy spawns
@ -274,8 +249,7 @@ function initializeGame() {
updateGame();
spawnPdfInterval();
document.addEventListener('visibilitychange', function() {
document.addEventListener("visibilitychange", function () {
if (document.hidden) {
paused = true;
} else {
@ -283,7 +257,6 @@ function initializeGame() {
updateGame();
spawnPdfInterval();
}
});
window.resetGame = resetGame;

View File

@ -1,6 +1,6 @@
function compareVersions(version1, version2) {
const v1 = version1.split('.');
const v2 = version2.split('.');
const v1 = version1.split(".");
const v2 = version2.split(".");
for (let i = 0; i < v1.length || i < v2.length; i++) {
const n1 = parseInt(v1[i]) || 0;
@ -16,7 +16,6 @@ function compareVersions(version1, version2) {
return 0;
}
async function getLatestReleaseVersion() {
const url = "https://api.github.com/repos/Stirling-Tools/Stirling-PDF/releases/latest";
try {
@ -36,20 +35,18 @@ async function checkForUpdate() {
updateBtn.style.display = "none";
}
const latestVersion = await getLatestReleaseVersion();
console.log("latestVersion=" + latestVersion)
console.log("currentVersion=" + currentVersion)
console.log("compareVersions(latestVersion, currentVersion) > 0)=" + compareVersions(latestVersion, currentVersion))
console.log("latestVersion=" + latestVersion);
console.log("currentVersion=" + currentVersion);
console.log("compareVersions(latestVersion, currentVersion) > 0)=" + compareVersions(latestVersion, currentVersion));
if (latestVersion && compareVersions(latestVersion, currentVersion) > 0) {
document.getElementById("update-btn").style.display = "block";
console.log("visible")
console.log("visible");
} else {
console.log("hidden")
console.log("hidden");
}
}
document.addEventListener('DOMContentLoaded', (event) => {
document.addEventListener("DOMContentLoaded", (event) => {
checkForUpdate();
});

View File

@ -1,18 +1,18 @@
function filterCards() {
var input = document.getElementById('searchBar');
var input = document.getElementById("searchBar");
var filter = input.value.toUpperCase();
var cards = document.querySelectorAll('.feature-card');
var cards = document.querySelectorAll(".feature-card");
for (var i = 0; i < cards.length; i++) {
var card = cards[i];
var title = card.querySelector('h5.card-title').innerText;
var text = card.querySelector('p.card-text').innerText;
var title = card.querySelector("h5.card-title").innerText;
var text = card.querySelector("p.card-text").innerText;
// Get the navbar tags associated with the card
var navbarItem = document.querySelector(`a.dropdown-item[href="${card.id}"]`);
var navbarTags = navbarItem ? navbarItem.getAttribute('data-bs-tags') : '';
var navbarTags = navbarItem ? navbarItem.getAttribute("data-bs-tags") : "";
var content = title + ' ' + text + ' ' + navbarTags;
var content = title + " " + text + " " + navbarTags;
if (content.toUpperCase().indexOf(filter) > -1) {
card.style.display = "";
@ -22,19 +22,17 @@ function filterCards() {
}
}
function toggleFavorite(element) {
var img = element.querySelector('img');
var card = element.closest('.feature-card');
var img = element.querySelector("img");
var card = element.closest(".feature-card");
var cardId = card.id;
if (img.src.endsWith('star.svg')) {
img.src = 'images/star-fill.svg';
card.classList.add('favorite');
localStorage.setItem(cardId, 'favorite');
if (img.src.endsWith("star.svg")) {
img.src = "images/star-fill.svg";
card.classList.add("favorite");
localStorage.setItem(cardId, "favorite");
} else {
img.src = 'images/star.svg';
card.classList.remove('favorite');
img.src = "images/star.svg";
card.classList.remove("favorite");
localStorage.removeItem(cardId);
}
reorderCards();
@ -43,11 +41,11 @@ function toggleFavorite(element) {
}
function reorderCards() {
var container = document.querySelector('.features-container');
var cards = Array.from(container.getElementsByClassName('feature-card'));
var container = document.querySelector(".features-container");
var cards = Array.from(container.getElementsByClassName("feature-card"));
cards.sort(function (a, b) {
var aIsFavorite = localStorage.getItem(a.id) === 'favorite';
var bIsFavorite = localStorage.getItem(b.id) === 'favorite';
var aIsFavorite = localStorage.getItem(a.id) === "favorite";
var bIsFavorite = localStorage.getItem(b.id) === "favorite";
if (aIsFavorite && !bIsFavorite) {
return -1;
}
@ -61,13 +59,13 @@ function reorderCards() {
});
}
function initializeCards() {
var cards = document.querySelectorAll('.feature-card');
var cards = document.querySelectorAll(".feature-card");
cards.forEach(function (card) {
var cardId = card.id;
var img = card.querySelector('.favorite-icon img');
if (localStorage.getItem(cardId) === 'favorite') {
img.src = 'images/star-fill.svg';
card.classList.add('favorite');
var img = card.querySelector(".favorite-icon img");
if (localStorage.getItem(cardId) === "favorite") {
img.src = "images/star-fill.svg";
card.classList.add("favorite");
}
});
reorderCards();

View File

@ -1,44 +1,42 @@
document.addEventListener('DOMContentLoaded', function() {
setLanguageForDropdown('.lang_dropdown-item');
document.addEventListener("DOMContentLoaded", function () {
setLanguageForDropdown(".lang_dropdown-item");
// Detect the browser's preferred language
let browserLang = navigator.language || navigator.userLanguage;
// Convert to a format consistent with your language codes (e.g., en-GB, fr-FR)
browserLang = browserLang.replace('-', '_');
browserLang = browserLang.replace("-", "_");
// Check if the dropdown contains the browser's language
const dropdownLangExists = document.querySelector(`.lang_dropdown-item[data-language-code="${browserLang}"]`);
// Set the default language to browser's language or 'en_GB' if not found in the dropdown
const defaultLocale = dropdownLangExists ? browserLang : 'en_GB';
const storedLocale = localStorage.getItem('languageCode') || defaultLocale;
const defaultLocale = dropdownLangExists ? browserLang : "en_GB";
const storedLocale = localStorage.getItem("languageCode") || defaultLocale;
const dropdownItems = document.querySelectorAll('.lang_dropdown-item');
const dropdownItems = document.querySelectorAll(".lang_dropdown-item");
for (let i = 0; i < dropdownItems.length; i++) {
const item = dropdownItems[i];
item.classList.remove('active');
item.classList.remove("active");
if (item.dataset.languageCode === storedLocale) {
item.classList.add('active');
item.classList.add("active");
}
item.addEventListener('click', handleDropdownItemClick);
item.addEventListener("click", handleDropdownItemClick);
}
});
function setLanguageForDropdown(dropdownClass) {
const defaultLocale = document.documentElement.lang || 'en_GB';
const storedLocale = localStorage.getItem('languageCode') || defaultLocale;
const defaultLocale = document.documentElement.lang || "en_GB";
const storedLocale = localStorage.getItem("languageCode") || defaultLocale;
const dropdownItems = document.querySelectorAll(dropdownClass);
for (let i = 0; i < dropdownItems.length; i++) {
const item = dropdownItems[i];
item.classList.remove('active');
item.classList.remove("active");
if (item.dataset.languageCode === storedLocale) {
item.classList.add('active');
item.classList.add("active");
}
item.addEventListener('click', handleDropdownItemClick);
item.addEventListener("click", handleDropdownItemClick);
}
}
@ -46,25 +44,32 @@ function handleDropdownItemClick(event) {
event.preventDefault();
const languageCode = event.currentTarget.dataset.bsLanguageCode; // change this to event.currentTarget
if (languageCode) {
localStorage.setItem('languageCode', languageCode);
localStorage.setItem("languageCode", languageCode);
const currentUrl = window.location.href;
if (currentUrl.indexOf('?lang=') === -1) {
window.location.href = currentUrl + '?lang=' + languageCode;
if (currentUrl.indexOf("?lang=") === -1) {
window.location.href = currentUrl + "?lang=" + languageCode;
} else {
window.location.href = currentUrl.replace(/\?lang=\w{2,}/, '?lang=' + languageCode);
window.location.href = currentUrl.replace(/\?lang=\w{2,}/, "?lang=" + languageCode);
}
} else {
console.error("Language code is not set for this item."); // for debugging
}
}
document.addEventListener('DOMContentLoaded', function() {
document.querySelectorAll('.nav-item.dropdown').forEach((element) => {
document.addEventListener("DOMContentLoaded", function () {
document.querySelectorAll(".nav-item.dropdown").forEach((element) => {
const dropdownMenu = element.querySelector(".dropdown-menu");
if (dropdownMenu.id !== 'favoritesDropdown' && dropdownMenu.children.length <= 2 && dropdownMenu.querySelectorAll("hr.dropdown-divider").length === dropdownMenu.children.length) {
if (element.previousElementSibling && element.previousElementSibling.classList.contains('nav-item') && element.previousElementSibling.classList.contains('nav-item-separator')) {
if (
dropdownMenu.id !== "favoritesDropdown" &&
dropdownMenu.children.length <= 2 &&
dropdownMenu.querySelectorAll("hr.dropdown-divider").length === dropdownMenu.children.length
) {
if (
element.previousElementSibling &&
element.previousElementSibling.classList.contains("nav-item") &&
element.previousElementSibling.classList.contains("nav-item-separator")
) {
element.previousElementSibling.remove();
}
element.remove();
@ -72,9 +77,12 @@ document.addEventListener('DOMContentLoaded', function() {
});
//Sort languages by alphabet
const list = Array.from(document.querySelector('.dropdown-menu[aria-labelledby="languageDropdown"]').children).filter(child => child.matches('a'));
list.sort(function(a, b) {
const list = Array.from(document.querySelector('.dropdown-menu[aria-labelledby="languageDropdown"]').children).filter(
(child) => child.matches("a"),
);
list
.sort(function (a, b) {
return a.textContent.toUpperCase().localeCompare(b.textContent.toUpperCase());
}).forEach(node => document.querySelector('.dropdown-menu[aria-labelledby="languageDropdown"]').appendChild(node));
})
.forEach((node) => document.querySelector('.dropdown-menu[aria-labelledby="languageDropdown"]').appendChild(node));
});

View File

@ -10,21 +10,21 @@ async function downloadFilesWithCallback(processFileCallback) {
jszip = new JSZip();
}
const promises = Array.from(files).map(async file => {
const promises = Array.from(files).map(async (file) => {
const { processedData, fileName } = await processFileCallback(file);
if (zipFiles) {
jszip.file(fileName, processedData);
} else {
const url = URL.createObjectURL(processedData);
const downloadOption = localStorage.getItem('downloadOption');
const downloadOption = localStorage.getItem("downloadOption");
if (downloadOption === 'sameWindow') {
if (downloadOption === "sameWindow") {
window.location.href = url;
} else if (downloadOption === 'newWindow') {
window.open(url, '_blank');
} else if (downloadOption === "newWindow") {
window.open(url, "_blank");
} else {
const downloadLink = document.createElement('a');
const downloadLink = document.createElement("a");
downloadLink.href = url;
downloadLink.download = fileName;
downloadLink.click();
@ -37,7 +37,7 @@ async function downloadFilesWithCallback(processFileCallback) {
if (zipFiles) {
const content = await jszip.generateAsync({ type: "blob" });
const url = URL.createObjectURL(content);
const a = document.createElement('a');
const a = document.createElement("a");
a.href = url;
a.download = "files.zip";
document.body.appendChild(a);

View File

@ -1,6 +1,6 @@
let currentSort = {
field: null,
descending: false
descending: false,
};
document.getElementById("fileInput-input").addEventListener("change", function () {
@ -106,7 +106,7 @@ function sortFiles(comparator) {
// Update the files property
const dataTransfer = new DataTransfer();
sortedFilesArray.forEach(file => dataTransfer.items.add(file));
sortedFilesArray.forEach((file) => dataTransfer.items.add(file));
document.getElementById("fileInput-input").files = dataTransfer.files;
}

View File

@ -15,21 +15,21 @@ class DragDropManager {
this.wrapper = document.getElementById(wrapperId);
this.pageDragging = false;
this.hoveredEl = undefined;
this.draggelEl = undefined
this.draggelEl = undefined;
this.draggedImageEl = undefined;
var styleElement = document.createElement('link');
styleElement.rel = 'stylesheet';
styleElement.href = 'css/dragdrop.css'
var styleElement = document.createElement("link");
styleElement.rel = "stylesheet";
styleElement.href = "css/dragdrop.css";
document.head.appendChild(styleElement);
const div = document.createElement('div');
div.classList.add('drag-manager_endpoint');
const div = document.createElement("div");
div.classList.add("drag-manager_endpoint");
div.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-file-earmark-arrow-down" viewBox="0 0 16 16">
<path d="M8.5 6.5a.5.5 0 0 0-1 0v3.793L6.354 9.146a.5.5 0 1 0-.708.708l2 2a.5.5 0 0 0 .708 0l2-2a.5.5 0 0 0-.708-.708L8.5 10.293V6.5z"/>
<path d="M14 14V4.5L9.5 0H4a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h8a2 2 0 0 0 2-2zM9.5 3A1.5 1.5 0 0 0 11 4.5h2V14a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1V2a1 1 0 0 1 1-1h5.5v2z"/>
</svg>`
</svg>`;
this.endInsertionElement = div;
this.startDraggingPage = this.startDraggingPage.bind(this);
@ -39,49 +39,48 @@ class DragDropManager {
this.adapt(div);
}
startDraggingPage(div,) {
startDraggingPage(div) {
this.pageDragging = true;
this.draggedEl = div;
const img = div.querySelector('img');
div.classList.add('drag-manager_dragging');
const img = div.querySelector("img");
div.classList.add("drag-manager_dragging");
const imageSrc = img.src;
const imgEl = document.createElement('img');
imgEl.classList.add('dragged-img');
const imgEl = document.createElement("img");
imgEl.classList.add("dragged-img");
imgEl.src = imageSrc;
this.draggedImageEl = imgEl;
imgEl.style.visibility = 'hidden';
imgEl.style.transform = `rotate(${img.style.rotate === '' ? '0deg' : img.style.rotate}) translate(-50%, -50%)`;
imgEl.style.visibility = "hidden";
imgEl.style.transform = `rotate(${img.style.rotate === "" ? "0deg" : img.style.rotate}) translate(-50%, -50%)`;
this.dragContainer.appendChild(imgEl);
window.addEventListener('mouseup', this.stopDraggingPage)
window.addEventListener('mousemove', this.onDragEl)
this.wrapper.classList.add('drag-manager_dragging-container');
window.addEventListener("mouseup", this.stopDraggingPage);
window.addEventListener("mousemove", this.onDragEl);
this.wrapper.classList.add("drag-manager_dragging-container");
this.wrapper.appendChild(this.endInsertionElement);
}
onDragEl(mouseEvent) {
const { clientX, clientY } = mouseEvent;
if (this.draggedImageEl) {
this.draggedImageEl.style.visibility = 'visible';
this.draggedImageEl.style.visibility = "visible";
this.draggedImageEl.style.left = `${clientX}px`;
this.draggedImageEl.style.top = `${clientY}px`;
}
}
stopDraggingPage() {
window.removeEventListener('mousemove', this.onDragEl);
this.wrapper.classList.remove('drag-manager_dragging-container');
window.removeEventListener("mousemove", this.onDragEl);
this.wrapper.classList.remove("drag-manager_dragging-container");
this.wrapper.removeChild(this.endInsertionElement);
window.removeEventListener('mouseup', this.stopDraggingPage)
window.removeEventListener("mouseup", this.stopDraggingPage);
this.draggedImageEl = undefined;
this.pageDragging = false;
this.draggedEl.classList.remove('drag-manager_dragging');
this.hoveredEl?.classList.remove('drag-manager_draghover');
this.draggedEl.classList.remove("drag-manager_dragging");
this.hoveredEl?.classList.remove("drag-manager_draghover");
this.dragContainer.childNodes.forEach((dragChild) => {
this.dragContainer.removeChild(dragChild);
})
});
if (!this.hoveredEl) {
return;
}
@ -96,27 +95,26 @@ class DragDropManager {
this.movePageTo = movePageTo;
}
adapt(div) {
const onDragStart = () => {
this.startDraggingPage(div);
}
};
const onMouseEnter = () => {
if (this.pageDragging) {
this.hoveredEl = div;
div.classList.add('drag-manager_draghover');
}
div.classList.add("drag-manager_draghover");
}
};
const onMouseLeave = () => {
this.hoveredEl = undefined
div.classList.remove('drag-manager_draghover');
}
this.hoveredEl = undefined;
div.classList.remove("drag-manager_draghover");
};
div.addEventListener('dragstart', onDragStart);
div.addEventListener('mouseenter', onMouseEnter);
div.addEventListener('mouseleave', onMouseLeave);
div.addEventListener("dragstart", onDragStart);
div.addEventListener("mouseenter", onMouseEnter);
div.addEventListener("mouseleave", onMouseLeave);
return div;
}

View File

@ -4,24 +4,24 @@ class ImageHiglighter {
this.imageHighlighter = document.getElementById(id);
this.imageHighlightCallback = this.imageHighlightCallback.bind(this);
var styleElement = document.createElement('link');
styleElement.rel = 'stylesheet';
styleElement.href = 'css/imageHighlighter.css'
var styleElement = document.createElement("link");
styleElement.rel = "stylesheet";
styleElement.href = "css/imageHighlighter.css";
document.head.appendChild(styleElement);
this.imageHighlighter.onclick = () => {
this.imageHighlighter.childNodes.forEach((child) => {
child.classList.add('remove');
child.classList.add("remove");
setTimeout(() => {
this.imageHighlighter.removeChild(child);
}, 100)
})
}
}, 100);
});
};
}
imageHighlightCallback(highlightEvent) {
var bigImg = document.createElement('img');
var bigImg = document.createElement("img");
bigImg.onclick = (imageClickEvent) => {
// This prevents the highlighter's onClick from closing the image when clicking
// on the image instead of next to it.
@ -30,15 +30,15 @@ class ImageHiglighter {
};
bigImg.src = highlightEvent.target.src;
this.imageHighlighter.appendChild(bigImg);
};
}
setActions() {
// not needed in this case
}
adapt(div) {
const img = div.querySelector('.page-image');
img.addEventListener('click', this.imageHighlightCallback)
const img = div.querySelector(".page-image");
img.addEventListener("click", this.imageHighlightCallback);
return div;
}
}

View File

@ -6,16 +6,16 @@ class PdfActionsManager {
this.pagesContainer = document.getElementById(id);
this.pageDirection = document.documentElement.getAttribute("lang-direction");
var styleElement = document.createElement('link');
styleElement.rel = 'stylesheet';
styleElement.href = 'css/pdfActions.css'
var styleElement = document.createElement("link");
styleElement.rel = "stylesheet";
styleElement.href = "css/pdfActions.css";
document.head.appendChild(styleElement);
}
getPageContainer(element) {
var container = element
while (!container.classList.contains('page-container')) {
var container = element;
while (!container.classList.contains("page-container")) {
container = container.parentNode;
}
return container;
@ -36,29 +36,29 @@ class PdfActionsManager {
if (sibling) {
this.movePageTo(imgContainer, sibling.nextSibling, true);
}
};
}
rotateCCWButtonCallback(e) {
var imgContainer = this.getPageContainer(e.target);
const img = imgContainer.querySelector("img");
this.rotateElement(img, -90)
};
this.rotateElement(img, -90);
}
rotateCWButtonCallback(e) {
var imgContainer = this.getPageContainer(e.target);
const img = imgContainer.querySelector("img");
this.rotateElement(img, 90)
};
this.rotateElement(img, 90);
}
deletePageButtonCallback(e) {
var imgContainer = this.getPageContainer(e.target);
this.pagesContainer.removeChild(imgContainer);
if (this.pagesContainer.childElementCount === 0) {
const filenameInput = document.getElementById('filename-input');
const filenameParagraph = document.getElementById('filename');
const downloadBtn = document.getElementById('export-button');
const filenameInput = document.getElementById("filename-input");
const filenameParagraph = document.getElementById("filename");
const downloadBtn = document.getElementById("export-button");
filenameInput.disabled = true;
filenameInput.value = "";
@ -66,12 +66,12 @@ class PdfActionsManager {
downloadBtn.disabled = true;
}
};
}
insertFileButtonCallback(e) {
var imgContainer = this.getPageContainer(e.target);
this.addPdfs(imgContainer)
};
this.addPdfs(imgContainer);
}
setActions({ movePageTo, addPdfs, rotateElement }) {
this.movePageTo = movePageTo;
@ -87,26 +87,26 @@ class PdfActionsManager {
}
adapt(div) {
div.classList.add('pdf-actions_container');
const leftDirection = this.pageDirection === 'rtl' ? 'right' : 'left'
const rightDirection = this.pageDirection === 'rtl' ? 'left' : 'right'
const buttonContainer = document.createElement('div');
div.classList.add("pdf-actions_container");
const leftDirection = this.pageDirection === "rtl" ? "right" : "left";
const rightDirection = this.pageDirection === "rtl" ? "left" : "right";
const buttonContainer = document.createElement("div");
buttonContainer.classList.add("pdf-actions_button-container", "hide-on-drag");
const moveUp = document.createElement('button');
const moveUp = document.createElement("button");
moveUp.classList.add("pdf-actions_move-left-button", "btn", "btn-secondary");
moveUp.innerHTML = `<i class="bi bi-arrow-${leftDirection}-short"></i>`;
moveUp.onclick = this.moveUpButtonCallback;
buttonContainer.appendChild(moveUp);
const moveDown = document.createElement('button');
const moveDown = document.createElement("button");
moveDown.classList.add("pdf-actions_move-right-button", "btn", "btn-secondary");
moveDown.innerHTML = `<i class="bi bi-arrow-${rightDirection}-short"></i>`;
moveDown.onclick = this.moveDownButtonCallback;
buttonContainer.appendChild(moveDown);
const rotateCCW = document.createElement('button');
const rotateCCW = document.createElement("button");
rotateCCW.classList.add("btn", "btn-secondary");
rotateCCW.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-arrow-counterclockwise" viewBox="0 0 16 16">
<path fill-rule="evenodd" d="M8 3a5 5 0 1 1-4.546 2.914.5.5 0 0 0-.908-.417A6 6 0 1 0 8 2v1z" />
@ -115,7 +115,7 @@ class PdfActionsManager {
rotateCCW.onclick = this.rotateCCWButtonCallback;
buttonContainer.appendChild(rotateCCW);
const rotateCW = document.createElement('button');
const rotateCW = document.createElement("button");
rotateCW.classList.add("btn", "btn-secondary");
rotateCW.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-arrow-clockwise" viewBox="0 0 16 16">
<path fill-rule="evenodd" d="M8 3a5 5 0 1 0 4.546 2.914.5.5 0 0 1 .908-.417A6 6 0 1 1 8 2v1z" />
@ -124,7 +124,7 @@ class PdfActionsManager {
rotateCW.onclick = this.rotateCWButtonCallback;
buttonContainer.appendChild(rotateCW);
const deletePage = document.createElement('button');
const deletePage = document.createElement("button");
deletePage.classList.add("btn", "btn-danger");
deletePage.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-trash" viewBox="0 0 16 16">
<path d="M5.5 5.5A.5.5 0 0 1 6 6v6a.5.5 0 0 1-1 0V6a.5.5 0 0 1 .5-.5Zm2.5 0a.5.5 0 0 1 .5.5v6a.5.5 0 0 1-1 0V6a.5.5 0 0 1 .5-.5Zm3 .5a.5.5 0 0 0-1 0v6a.5.5 0 0 0 1 0V6Z"/>
@ -135,14 +135,15 @@ class PdfActionsManager {
div.appendChild(buttonContainer);
const insertFileButtonContainer = document.createElement('div');
const insertFileButtonContainer = document.createElement("div");
insertFileButtonContainer.classList.add(
"pdf-actions_insert-file-button-container",
leftDirection,
`align-center-${leftDirection}`);
`align-center-${leftDirection}`,
);
const insertFileButton = document.createElement('button');
const insertFileButton = document.createElement("button");
insertFileButton.classList.add("btn", "btn-primary", "pdf-actions_insert-file-button");
insertFileButton.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-file-earmark-plus" viewBox="0 0 16 16">
<path d="M8 6.5a.5.5 0 0 1 .5.5v1.5H10a.5.5 0 0 1 0 1H8.5V11a.5.5 0 0 1-1 0V9.5H6a.5.5 0 0 1 0-1h1.5V7a.5.5 0 0 1 .5-.5z"/>
@ -154,13 +155,14 @@ class PdfActionsManager {
div.appendChild(insertFileButtonContainer);
// add this button to every element, but only show it on the last one :D
const insertFileButtonRightContainer = document.createElement('div');
const insertFileButtonRightContainer = document.createElement("div");
insertFileButtonRightContainer.classList.add(
"pdf-actions_insert-file-button-container",
rightDirection,
`align-center-${rightDirection}`);
`align-center-${rightDirection}`,
);
const insertFileButtonRight = document.createElement('button');
const insertFileButtonRight = document.createElement("button");
insertFileButtonRight.classList.add("btn", "btn-primary", "pdf-actions_insert-file-button");
insertFileButtonRight.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-file-earmark-plus" viewBox="0 0 16 16">
<path d="M8 6.5a.5.5 0 0 1 .5.5v1.5H10a.5.5 0 0 1 0 1H8.5V11a.5.5 0 0 1-1 0V9.5H6a.5.5 0 0 1 0-1h1.5V7a.5.5 0 0 1 .5-.5z"/>
@ -172,20 +174,20 @@ class PdfActionsManager {
div.appendChild(insertFileButtonRightContainer);
const adaptPageNumber = (pageNumber, div) => {
const pageNumberElement = document.createElement('span');
pageNumberElement.classList.add('page-number');
const pageNumberElement = document.createElement("span");
pageNumberElement.classList.add("page-number");
pageNumberElement.textContent = pageNumber;
div.insertBefore(pageNumberElement, div.firstChild);
};
div.addEventListener('mouseenter', () => {
div.addEventListener("mouseenter", () => {
const pageNumber = Array.from(div.parentNode.children).indexOf(div) + 1;
adaptPageNumber(pageNumber, div);
});
div.addEventListener('mouseleave', () => {
const pageNumberElement = div.querySelector('.page-number');
div.addEventListener("mouseleave", () => {
const pageNumberElement = div.querySelector(".page-number");
if (pageNumberElement) {
div.removeChild(pageNumberElement);
}

View File

@ -6,7 +6,7 @@ class PdfContainer {
downloadLink;
constructor(id, wrapperId, pdfAdapters) {
this.pagesContainer = document.getElementById(id)
this.pagesContainer = document.getElementById(id);
this.pagesContainerWrapper = document.getElementById(wrapperId);
this.downloadLink = null;
this.movePageTo = this.movePageTo.bind(this);
@ -21,21 +21,21 @@ class PdfContainer {
this.pdfAdapters = pdfAdapters;
this.pdfAdapters.forEach(adapter => {
this.pdfAdapters.forEach((adapter) => {
adapter.setActions({
movePageTo: this.movePageTo,
addPdfs: this.addPdfs,
rotateElement: this.rotateElement,
updateFilename: this.updateFilename
})
})
updateFilename: this.updateFilename,
});
});
window.addPdfs = this.addPdfs;
window.exportPdf = this.exportPdf;
window.rotateAll = this.rotateAll;
const filenameInput = document.getElementById('filename-input');
const downloadBtn = document.getElementById('export-button');
const filenameInput = document.getElementById("filename-input");
const downloadBtn = document.getElementById("export-button");
filenameInput.onkeyup = this.updateFilename;
filenameInput.onkeydown = this.preventIllegalChars;
@ -57,19 +57,17 @@ class PdfContainer {
if (scrollTo) {
const { width } = startElement.getBoundingClientRect();
const vector = (endIndex !== -1 && startIndex > endIndex)
? 0-width
: width;
const vector = endIndex !== -1 && startIndex > endIndex ? 0 - width : width;
this.pagesContainerWrapper.scroll({
left: this.pagesContainerWrapper.scrollLeft + vector,
})
});
}
}
addPdfs(nextSiblingElement) {
var input = document.createElement('input');
input.type = 'file';
var input = document.createElement("input");
input.type = "file";
input.multiple = true;
input.setAttribute("accept", "application/pdf");
input.onchange = async (e) => {
@ -77,7 +75,7 @@ class PdfContainer {
this.addPdfsFromFiles(files, nextSiblingElement);
this.updateFilename(files ? files[0].name : "");
}
};
input.click();
}
@ -88,7 +86,7 @@ class PdfContainer {
await this.addPdfFile(files[i], nextSiblingElement);
}
document.querySelectorAll(".enable-on-file").forEach(element => {
document.querySelectorAll(".enable-on-file").forEach((element) => {
element.disabled = false;
});
}
@ -98,7 +96,7 @@ class PdfContainer {
if (!lastTransform) {
lastTransform = "0";
}
const lastAngle = parseInt(lastTransform.replace(/[^\d-]/g, ''));
const lastAngle = parseInt(lastTransform.replace(/[^\d-]/g, ""));
const newAngle = lastAngle + deg;
element.style.rotate = newAngle + "deg";
@ -108,13 +106,13 @@ class PdfContainer {
const { renderer, pdfDocument } = await this.loadFile(file);
for (var i = 0; i < renderer.pageCount; i++) {
const div = document.createElement('div');
const div = document.createElement("div");
div.classList.add("page-container");
var img = document.createElement('img');
img.classList.add('page-image')
const imageSrc = await renderer.renderPage(i)
var img = document.createElement("img");
img.classList.add("page-image");
const imageSrc = await renderer.renderPage(i);
img.src = imageSrc;
img.pageIdx = i;
img.rend = renderer;
@ -122,8 +120,8 @@ class PdfContainer {
div.appendChild(img);
this.pdfAdapters.forEach((adapter) => {
adapter.adapt?.(div)
})
adapter.adapt?.(div);
});
if (nextSiblingElement) {
this.pagesContainer.insertBefore(div, nextSiblingElement);
} else {
@ -140,7 +138,7 @@ class PdfContainer {
}
async toRenderer(objectUrl) {
pdfjsLib.GlobalWorkerOptions.workerSrc = 'pdfjs/pdf.worker.js'
pdfjsLib.GlobalWorkerOptions.workerSrc = "pdfjs/pdf.worker.js";
const pdf = await pdfjsLib.getDocument(objectUrl).promise;
return {
document: pdf,
@ -162,104 +160,103 @@ class PdfContainer {
// render the page onto the canvas
var renderContext = {
canvasContext: canvas.getContext("2d"),
viewport: page.getViewport({ scale: 1 })
viewport: page.getViewport({ scale: 1 }),
};
await page.render(renderContext).promise;
return canvas.toDataURL();
}
},
};
}
async toPdfLib(objectUrl) {
const existingPdfBytes = await fetch(objectUrl).then(res => res.arrayBuffer());
const pdfDoc = await PDFLib.PDFDocument.load(existingPdfBytes, { ignoreEncryption: true });
const existingPdfBytes = await fetch(objectUrl).then((res) => res.arrayBuffer());
const pdfDoc = await PDFLib.PDFDocument.load(existingPdfBytes, {
ignoreEncryption: true,
});
return pdfDoc;
}
rotateAll(deg) {
for (var i = 0; i < this.pagesContainer.childNodes.length; i++) {
const img = this.pagesContainer.childNodes[i].querySelector("img");
if (!img) continue;
this.rotateElement(img, deg)
this.rotateElement(img, deg);
}
}
async exportPdf() {
const pdfDoc = await PDFLib.PDFDocument.create();
const pageContainers = this.pagesContainer.querySelectorAll('.page-container'); // Select all .page-container elements
const pageContainers = this.pagesContainer.querySelectorAll(".page-container"); // Select all .page-container elements
for (var i = 0; i < pageContainers.length; i++) {
const img = pageContainers[i].querySelector("img"); // Find the img element within each .page-container
if (!img) continue;
const pages = await pdfDoc.copyPages(img.doc, [img.pageIdx])
const pages = await pdfDoc.copyPages(img.doc, [img.pageIdx]);
const page = pages[0];
const rotation = img.style.rotate;
if (rotation) {
const rotationAngle = parseInt(rotation.replace(/[^\d-]/g, ''));
page.setRotation(PDFLib.degrees(page.getRotation().angle + rotationAngle))
const rotationAngle = parseInt(rotation.replace(/[^\d-]/g, ""));
page.setRotation(PDFLib.degrees(page.getRotation().angle + rotationAngle));
}
pdfDoc.addPage(page);
}
const pdfBytes = await pdfDoc.save();
const pdfBlob = new Blob([pdfBytes], { type: 'application/pdf' });
const pdfBlob = new Blob([pdfBytes], { type: "application/pdf" });
const url = URL.createObjectURL(pdfBlob);
const downloadOption = localStorage.getItem('downloadOption');
const downloadOption = localStorage.getItem("downloadOption");
const filenameInput = document.getElementById('filename-input');
const filenameInput = document.getElementById("filename-input");
let inputArr = filenameInput.value.split('.');
let inputArr = filenameInput.value.split(".");
if (inputArr !== null && inputArr !== undefined && inputArr.length > 0) {
inputArr = inputArr.filter(n => n); // remove all empty strings, nulls or undefined
inputArr = inputArr.filter((n) => n); // remove all empty strings, nulls or undefined
if (inputArr.length > 1) {
inputArr.pop(); // remove right part after last dot
}
filenameInput.value = inputArr.join('');
filenameInput.value = inputArr.join("");
this.fileName = filenameInput.value;
}
if (!filenameInput.value.includes('.pdf')) {
filenameInput.value = filenameInput.value + '.pdf';
if (!filenameInput.value.includes(".pdf")) {
filenameInput.value = filenameInput.value + ".pdf";
this.fileName = filenameInput.value;
}
if (downloadOption === 'sameWindow') {
if (downloadOption === "sameWindow") {
// Open the file in the same window
window.location.href = url;
} else if (downloadOption === 'newWindow') {
} else if (downloadOption === "newWindow") {
// Open the file in a new window
window.open(url, '_blank');
window.open(url, "_blank");
} else {
// Download the file
this.downloadLink = document.createElement('a');
this.downloadLink.id = 'download-link';
this.downloadLink = document.createElement("a");
this.downloadLink.id = "download-link";
this.downloadLink.href = url;
// downloadLink.download = this.fileName ? this.fileName : 'managed.pdf';
// downloadLink.download = this.fileName;
this.downloadLink.setAttribute('download', this.fileName ? this.fileName : 'managed.pdf');
this.downloadLink.setAttribute('target', '_blank');
this.downloadLink.setAttribute("download", this.fileName ? this.fileName : "managed.pdf");
this.downloadLink.setAttribute("target", "_blank");
this.downloadLink.onclick = this.setDownloadAttribute;
this.downloadLink.click();
}
}
setDownloadAttribute() {
this.downloadLink.setAttribute("download", this.fileName ? this.fileName : 'managed.pdf');
this.downloadLink.setAttribute("download", this.fileName ? this.fileName : "managed.pdf");
}
updateFilename(fileName = "") {
const filenameInput = document.getElementById('filename-input');
const pagesContainer = document.getElementById('pages-container');
const downloadBtn = document.getElementById('export-button');
const filenameInput = document.getElementById("filename-input");
const pagesContainer = document.getElementById("pages-container");
const downloadBtn = document.getElementById("export-button");
downloadBtn.disabled = pagesContainer.childElementCount === 0
downloadBtn.disabled = pagesContainer.childElementCount === 0;
if (!this.fileName) {
this.fileName = fileName;

View File

@ -8,7 +8,7 @@ class FileDragManager {
this.setCallback(cb);
// Prevent default behavior for drag events
['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => {
["dragenter", "dragover", "dragleave", "drop"].forEach((eventName) => {
document.body.addEventListener(eventName, preventDefaults, false);
});
@ -21,10 +21,10 @@ class FileDragManager {
this.dragleaveListener = this.dragleaveListener.bind(this);
this.dropListener = this.dropListener.bind(this);
document.body.addEventListener('dragenter', this.dragenterListener);
document.body.addEventListener('dragleave', this.dragleaveListener);
document.body.addEventListener("dragenter", this.dragenterListener);
document.body.addEventListener("dragleave", this.dragleaveListener);
// Add drop event listener
document.body.addEventListener('drop', this.dropListener);
document.body.addEventListener("drop", this.dropListener);
}
setActions({ updateFilename }) {
@ -43,23 +43,23 @@ class FileDragManager {
this.dragCounter++;
if (!this.overlay) {
// Create and show the overlay
this.overlay = document.createElement('div');
this.overlay.style.position = 'fixed';
this.overlay = document.createElement("div");
this.overlay.style.position = "fixed";
this.overlay.style.top = 0;
this.overlay.style.left = 0;
this.overlay.style.width = '100%';
this.overlay.style.height = '100%';
this.overlay.style.background = 'rgba(0, 0, 0, 0.5)';
this.overlay.style.color = '#fff';
this.overlay.style.zIndex = '1000';
this.overlay.style.display = 'flex';
this.overlay.style.alignItems = 'center';
this.overlay.style.justifyContent = 'center';
this.overlay.style.pointerEvents = 'none';
this.overlay.innerHTML = '<p>Drop files anywhere to upload</p>';
document.getElementById('content-wrap').appendChild(this.overlay);
this.overlay.style.width = "100%";
this.overlay.style.height = "100%";
this.overlay.style.background = "rgba(0, 0, 0, 0.5)";
this.overlay.style.color = "#fff";
this.overlay.style.zIndex = "1000";
this.overlay.style.display = "flex";
this.overlay.style.alignItems = "center";
this.overlay.style.justifyContent = "center";
this.overlay.style.pointerEvents = "none";
this.overlay.innerHTML = "<p>Drop files anywhere to upload</p>";
document.getElementById("content-wrap").appendChild(this.overlay);
}
}
};
dragleaveListener() {
this.dragCounter--;
@ -70,16 +70,17 @@ class FileDragManager {
this.overlay = null;
}
}
};
}
dropListener(e) {
const dt = e.dataTransfer;
const files = dt.files;
this.callback(files).catch((err) => {
this.callback(files)
.catch((err) => {
console.error(err);
//maybe
}).finally(() => {
})
.finally(() => {
// Hide and remove the overlay
if (this.overlay) {
this.overlay.remove();
@ -88,7 +89,7 @@ class FileDragManager {
this.updateFilename(files ? files[0].name : "");
});
};
}
}
export default FileDragManager;

View File

@ -1,7 +1,7 @@
const scrollDivHorizontally = (id) => {
var scrollDelta = 0; // variable to store the accumulated scroll delta
var isScrolling = false; // variable to track if scroll is already in progress
const divToScrollHorizontally = document.getElementById(id)
const divToScrollHorizontally = document.getElementById(id);
function scrollLoop() {
// Scroll the div horizontally by a fraction of the accumulated scroll delta
divToScrollHorizontally.scrollLeft += scrollDelta * 0.1;
@ -17,7 +17,6 @@ const scrollDivHorizontally = (id) => {
}
}
divToScrollHorizontally.addEventListener("wheel", function (e) {
e.preventDefault(); // prevent default mousewheel behavior
@ -30,6 +29,6 @@ const scrollDivHorizontally = (id) => {
requestAnimationFrame(scrollLoop);
}
});
}
};
export default scrollDivHorizontally;

View File

@ -1,15 +1,15 @@
document.getElementById('validateButton').addEventListener('click', function(event) {
document.getElementById("validateButton").addEventListener("click", function (event) {
event.preventDefault();
validatePipeline();
});
function validatePipeline() {
let pipelineListItems = document.getElementById('pipelineList').children;
let pipelineListItems = document.getElementById("pipelineList").children;
let isValid = true;
let containsAddPassword = false;
for (let i = 0; i < pipelineListItems.length - 1; i++) {
let currentOperation = pipelineListItems[i].querySelector('.operationName').textContent;
let nextOperation = pipelineListItems[i + 1].querySelector('.operationName').textContent;
if (currentOperation === '/add-password') {
let currentOperation = pipelineListItems[i].querySelector(".operationName").textContent;
let nextOperation = pipelineListItems[i + 1].querySelector(".operationName").textContent;
if (currentOperation === "/add-password") {
containsAddPassword = true;
}
@ -17,39 +17,46 @@ function validatePipeline() {
let nextOperationDescription = apiDocs[nextOperation]?.post?.description || "";
// Strip off 'ZIP-' prefix
currentOperationDescription = currentOperationDescription.replace("ZIP-", '');
nextOperationDescription = nextOperationDescription.replace("ZIP-", '');
currentOperationDescription = currentOperationDescription.replace("ZIP-", "");
nextOperationDescription = nextOperationDescription.replace("ZIP-", "");
let currentOperationOutput = currentOperationDescription.match(/Output:([A-Z\/]*)/)?.[1] || "";
let nextOperationInput = nextOperationDescription.match(/Input:([A-Z\/]*)/)?.[1] || "";
// Splitting in case of multiple possible output/input
let currentOperationOutputArr = currentOperationOutput.split('/');
let nextOperationInputArr = nextOperationInput.split('/');
let currentOperationOutputArr = currentOperationOutput.split("/");
let nextOperationInputArr = nextOperationInput.split("/");
if (currentOperationOutput !== 'ANY' && nextOperationInput !== 'ANY') {
let intersection = currentOperationOutputArr.filter(value => nextOperationInputArr.includes(value));
if (currentOperationOutput !== "ANY" && nextOperationInput !== "ANY") {
let intersection = currentOperationOutputArr.filter((value) => nextOperationInputArr.includes(value));
console.log(`Intersection: ${intersection}`);
if (intersection.length === 0) {
updateValidateButton(false);
isValid = false;
console.log(`Incompatible operations: The output of operation '${currentOperation}' (${currentOperationOutput}) is not compatible with the input of the following operation '${nextOperation}' (${nextOperationInput}).`);
alert(`Incompatible operations: The output of operation '${currentOperation}' (${currentOperationOutput}) is not compatible with the input of the following operation '${nextOperation}' (${nextOperationInput}).`);
console.log(
`Incompatible operations: The output of operation '${currentOperation}' (${currentOperationOutput}) is not compatible with the input of the following operation '${nextOperation}' (${nextOperationInput}).`,
);
alert(
`Incompatible operations: The output of operation '${currentOperation}' (${currentOperationOutput}) is not compatible with the input of the following operation '${nextOperation}' (${nextOperationInput}).`,
);
break;
}
}
}
if (containsAddPassword && pipelineListItems[pipelineListItems.length - 1].querySelector('.operationName').textContent !== '/add-password') {
if (
containsAddPassword &&
pipelineListItems[pipelineListItems.length - 1].querySelector(".operationName").textContent !== "/add-password"
) {
updateValidateButton(false);
alert('The "add-password" operation should be at the end of the operations sequence. Please adjust the operations order.');
return false;
}
if (isValid) {
console.log('Pipeline is valid');
console.log("Pipeline is valid");
// Continue with the pipeline operation
} else {
console.error('Pipeline is not valid');
console.error("Pipeline is not valid");
// Stop operation, maybe display an error to the user
}
updateValidateButton(isValid);
@ -57,97 +64,79 @@ function validatePipeline() {
}
function updateValidateButton(isValid) {
var validateButton = document.getElementById('validateButton');
var validateButton = document.getElementById("validateButton");
if (isValid) {
validateButton.classList.remove('btn-danger');
validateButton.classList.add('btn-success');
validateButton.classList.remove("btn-danger");
validateButton.classList.add("btn-success");
} else {
validateButton.classList.remove('btn-success');
validateButton.classList.add('btn-danger');
validateButton.classList.remove("btn-success");
validateButton.classList.add("btn-danger");
}
}
document.getElementById('submitConfigBtn').addEventListener('click', function() {
document.getElementById("submitConfigBtn").addEventListener("click", function () {
if (validatePipeline() === false) {
return;
}
let selectedOperation = document.getElementById('operationsDropdown').value;
let selectedOperation = document.getElementById("operationsDropdown").value;
var pipelineName = document.getElementById('pipelineName').value;
let pipelineList = document.getElementById('pipelineList').children;
var pipelineName = document.getElementById("pipelineName").value;
let pipelineList = document.getElementById("pipelineList").children;
let pipelineConfig = {
"name": pipelineName,
"pipeline": [],
"_examples": {
"outputDir": "{outputFolder}/{folderName}",
"outputFileName": "{filename}-{pipelineName}-{date}-{time}"
name: pipelineName,
pipeline: [],
_examples: {
outputDir: "{outputFolder}/{folderName}",
outputFileName: "{filename}-{pipelineName}-{date}-{time}",
},
"outputDir": "httpWebRequest",
"outputFileName": "{filename}"
outputDir: "httpWebRequest",
outputFileName: "{filename}",
};
for (let i = 0; i < pipelineList.length; i++) {
let operationName = pipelineList[i].querySelector('.operationName').textContent;
let operationName = pipelineList[i].querySelector(".operationName").textContent;
let parameters = operationSettings[operationName] || {};
pipelineConfig.pipeline.push({
"operation": operationName,
"parameters": parameters
operation: operationName,
parameters: parameters,
});
}
let pipelineConfigJson = JSON.stringify(pipelineConfig, null, 2);
let formData = new FormData();
let fileInput = document.getElementById('fileInput-input');
let fileInput = document.getElementById("fileInput-input");
let files = fileInput.files;
for (let i = 0; i < files.length; i++) {
console.log("files[i]", files[i].name);
formData.append('fileInput', files[i], files[i].name);
formData.append("fileInput", files[i], files[i].name);
}
console.log("pipelineConfigJson", pipelineConfigJson);
formData.append('json', pipelineConfigJson);
formData.append("json", pipelineConfigJson);
console.log("formData", formData);
fetch('api/v1/pipeline/handleData', {
method: 'POST',
body: formData
fetch("api/v1/pipeline/handleData", {
method: "POST",
body: formData,
})
.then(response => {
.then((response) => {
// Save the response to use it later
const responseToUseLater = response;
return response.blob().then(blob => {
return response.blob().then((blob) => {
let url = window.URL.createObjectURL(blob);
let a = document.createElement('a');
let a = document.createElement("a");
a.href = url;
// Use responseToUseLater instead of response
const contentDisposition = responseToUseLater.headers.get('Content-Disposition');
let filename = 'download';
if (contentDisposition && contentDisposition.indexOf('attachment') !== -1) {
filename = decodeURIComponent(contentDisposition.split('filename=')[1].replace(/"/g, '')).trim();
const contentDisposition = responseToUseLater.headers.get("Content-Disposition");
let filename = "download";
if (contentDisposition && contentDisposition.indexOf("attachment") !== -1) {
filename = decodeURIComponent(contentDisposition.split("filename=")[1].replace(/"/g, "")).trim();
}
a.download = filename;
@ -157,30 +146,28 @@ document.getElementById('submitConfigBtn').addEventListener('click', function()
});
})
.catch((error) => {
console.error('Error:', error);
console.error("Error:", error);
});
});
let apiDocs = {};
let apiSchemas = {};
let operationSettings = {};
fetch('v1/api-docs')
.then(response => response.json())
.then(data => {
fetch("v1/api-docs")
.then((response) => response.json())
.then((data) => {
apiDocs = data.paths;
apiSchemas = data.components.schemas;
let operationsDropdown = document.getElementById('operationsDropdown');
let operationsDropdown = document.getElementById("operationsDropdown");
const ignoreOperations = ["/api/v1/pipeline/handleData", "/api/v1/pipeline/operationToIgnore"]; // Add the operations you want to ignore here
operationsDropdown.innerHTML = '';
operationsDropdown.innerHTML = "";
let operationsByTag = {};
// Group operations by tags
Object.keys(data.paths).forEach(operationPath => {
Object.keys(data.paths).forEach((operationPath) => {
let operation = data.paths[operationPath].post;
if (!operation || !operation.description) {
console.log(operationPath);
@ -196,7 +183,7 @@ fetch('v1/api-docs')
});
// Sort operations within each tag alphabetically
Object.keys(operationsByTag).forEach(tag => {
Object.keys(operationsByTag).forEach((tag) => {
operationsByTag[tag].sort();
});
@ -204,22 +191,21 @@ fetch('v1/api-docs')
let tagOrder = ["General", "Security", "Convert", "Misc", "Filter"];
// Create dropdown options
tagOrder.forEach(tag => {
tagOrder.forEach((tag) => {
if (operationsByTag[tag]) {
let group = document.createElement('optgroup');
let group = document.createElement("optgroup");
group.label = tag;
operationsByTag[tag].forEach(operationPath => {
let option = document.createElement('option');
let operationPathDisplay = operationPath
operationPathDisplay = operationPath.replace(new RegExp("api/v1/" + tag.toLowerCase() + "/", 'i'), "");
operationsByTag[tag].forEach((operationPath) => {
let option = document.createElement("option");
let operationPathDisplay = operationPath;
operationPathDisplay = operationPath.replace(new RegExp("api/v1/" + tag.toLowerCase() + "/", "i"), "");
if (operationPath.includes("/convert")) {
operationPathDisplay = operationPathDisplay.replace(/^\//, '').replaceAll("/", " to ");
operationPathDisplay = operationPathDisplay.replace(/^\//, "").replaceAll("/", " to ");
} else {
operationPathDisplay = operationPathDisplay.replace(/\//g, ''); // Remove slashes
operationPathDisplay = operationPathDisplay.replace(/\//g, ""); // Remove slashes
}
operationPathDisplay = operationPathDisplay.replaceAll(" ", "-");
option.textContent = operationPathDisplay;
@ -232,12 +218,11 @@ fetch('v1/api-docs')
});
});
document.getElementById("addOperationBtn").addEventListener("click", function () {
let selectedOperation = document.getElementById("operationsDropdown").value;
let pipelineList = document.getElementById("pipelineList");
document.getElementById('addOperationBtn').addEventListener('click', function() {
let selectedOperation = document.getElementById('operationsDropdown').value;
let pipelineList = document.getElementById('pipelineList');
let listItem = document.createElement('li');
let listItem = document.createElement("li");
listItem.className = "list-group-item";
let hasSettings = false;
if (apiDocs[selectedOperation] && apiDocs[selectedOperation].post) {
@ -246,32 +231,31 @@ document.getElementById('addOperationBtn').addEventListener('click', function()
// Check if parameters exist
if (postMethod.parameters && postMethod.parameters.length > 0) {
hasSettings = true;
} else if (postMethod.requestBody && postMethod.requestBody.content['multipart/form-data']) {
} else if (postMethod.requestBody && postMethod.requestBody.content["multipart/form-data"]) {
// Extract the reference key
const refKey = postMethod.requestBody.content['multipart/form-data'].schema['$ref'].split('/').pop();
const refKey = postMethod.requestBody.content["multipart/form-data"].schema["$ref"].split("/").pop();
// Check if the referenced schema exists and has properties more than just its input file
if (apiSchemas[refKey]) {
const properties = apiSchemas[refKey].properties;
const propertyKeys = Object.keys(properties);
// Check if there's more than one property or if there's exactly one property and its format is not 'binary'
if (propertyKeys.length > 1 || (propertyKeys.length === 1 && properties[propertyKeys[0]].format !== 'binary')) {
if (propertyKeys.length > 1 || (propertyKeys.length === 1 && properties[propertyKeys[0]].format !== "binary")) {
hasSettings = true;
}
}
}
}
listItem.innerHTML = `
<div class="d-flex justify-content-between align-items-center w-100">
<div class="operationName">${selectedOperation}</div>
<div class="arrows d-flex">
<button class="btn btn-secondary move-up ms-1"><span>&uarr;</span></button>
<button class="btn btn-secondary move-down ms-1"><span>&darr;</span></button>
<button class="btn ${hasSettings ? 'btn-warning' : 'btn-secondary'} pipelineSettings ms-1" ${hasSettings ? "" : "disabled"}>
<button class="btn ${hasSettings ? "btn-warning" : "btn-secondary"} pipelineSettings ms-1" ${
hasSettings ? "" : "disabled"
}>
<span style="color: ${hasSettings ? "white" : "grey"};"></span>
</button>
<button class="btn btn-danger remove ms-1"><span>X</span></button>
@ -279,10 +263,9 @@ document.getElementById('addOperationBtn').addEventListener('click', function()
</div>
`;
pipelineList.appendChild(listItem);
listItem.querySelector('.move-up').addEventListener('click', function(event) {
listItem.querySelector(".move-up").addEventListener("click", function (event) {
event.preventDefault();
if (listItem.previousElementSibling) {
pipelineList.insertBefore(listItem, listItem.previousElementSibling);
@ -290,56 +273,57 @@ document.getElementById('addOperationBtn').addEventListener('click', function()
}
});
listItem.querySelector('.move-down').addEventListener('click', function(event) {
listItem.querySelector(".move-down").addEventListener("click", function (event) {
event.preventDefault();
if (listItem.nextElementSibling) {
pipelineList.insertBefore(listItem.nextElementSibling, listItem);
updateConfigInDropdown();
}
});
listItem.querySelector('.remove').addEventListener('click', function(event) {
listItem.querySelector(".remove").addEventListener("click", function (event) {
event.preventDefault();
pipelineList.removeChild(listItem);
hideOrShowPipelineHeader();
updateConfigInDropdown();
});
listItem.querySelector('.pipelineSettings').addEventListener('click', function(event) {
listItem.querySelector(".pipelineSettings").addEventListener("click", function (event) {
event.preventDefault();
showpipelineSettingsModal(selectedOperation);
hideOrShowPipelineHeader();
});
function showpipelineSettingsModal(operation) {
let pipelineSettingsModal = document.getElementById('pipelineSettingsModal');
let pipelineSettingsContent = document.getElementById('pipelineSettingsContent');
let pipelineSettingsModal = document.getElementById("pipelineSettingsModal");
let pipelineSettingsContent = document.getElementById("pipelineSettingsContent");
let operationData = apiDocs[operation].post.parameters || [];
// Resolve the $ref reference to get actual schema properties
let refKey = apiDocs[operation].post.requestBody.content['multipart/form-data'].schema['$ref'].split('/').pop();
let refKey = apiDocs[operation].post.requestBody.content["multipart/form-data"].schema["$ref"].split("/").pop();
let requestBodyData = apiSchemas[refKey].properties || {};
// Combine operationData and requestBodyData into a single array
operationData = operationData.concat(Object.keys(requestBodyData).map(key => ({
operationData = operationData.concat(
Object.keys(requestBodyData).map((key) => ({
name: key,
schema: requestBodyData[key]
})));
schema: requestBodyData[key],
})),
);
pipelineSettingsContent.innerHTML = '';
pipelineSettingsContent.innerHTML = "";
operationData.forEach(parameter => {
operationData.forEach((parameter) => {
// If the parameter name is 'fileInput', return early to skip the rest of this iteration
if (parameter.name === 'fileInput') return;
if (parameter.name === "fileInput") return;
let parameterDiv = document.createElement('div');
let parameterDiv = document.createElement("div");
parameterDiv.className = "mb-3";
let parameterLabel = document.createElement('label');
let parameterLabel = document.createElement("label");
parameterLabel.textContent = `${parameter.name} (${parameter.schema.type}): `;
parameterLabel.title = parameter.schema.description;
parameterLabel.setAttribute('for', parameter.name);
parameterLabel.setAttribute("for", parameter.name);
parameterDiv.appendChild(parameterLabel);
let defaultValue = parameter.schema.example;
@ -350,12 +334,12 @@ document.getElementById('addOperationBtn').addEventListener('click', function()
// check if enum exists in schema
if (parameter.schema.enum) {
// if enum exists, create a select element
parameterInput = document.createElement('select');
parameterInput = document.createElement("select");
parameterInput.className = "form-control";
// iterate over each enum value and create an option for it
parameter.schema.enum.forEach(value => {
let option = document.createElement('option');
parameter.schema.enum.forEach((value) => {
let option = document.createElement("option");
option.value = value;
option.text = value;
parameterInput.appendChild(option);
@ -363,47 +347,47 @@ document.getElementById('addOperationBtn').addEventListener('click', function()
} else {
// switch-case statement for handling non-enum types
switch (parameter.schema.type) {
case 'string':
if (parameter.schema.format === 'binary') {
case "string":
if (parameter.schema.format === "binary") {
// This is a file input
//parameterInput = document.createElement('input');
//parameterInput.type = 'file';
//parameterInput.className = "form-control";
parameterInput = document.createElement('input');
parameterInput.type = 'text';
parameterInput = document.createElement("input");
parameterInput.type = "text";
parameterInput.className = "form-control";
parameterInput.value = "FileInputPathToBeInputtedManuallyForOffline";
} else {
parameterInput = document.createElement('input');
parameterInput.type = 'text';
parameterInput = document.createElement("input");
parameterInput.type = "text";
parameterInput.className = "form-control";
if (defaultValue !== undefined) parameterInput.value = defaultValue;
}
break;
case 'number':
case 'integer':
parameterInput = document.createElement('input');
parameterInput.type = 'number';
case "number":
case "integer":
parameterInput = document.createElement("input");
parameterInput.type = "number";
parameterInput.className = "form-control";
if (defaultValue !== undefined) parameterInput.value = defaultValue;
break;
case 'boolean':
parameterInput = document.createElement('input');
parameterInput.type = 'checkbox';
case "boolean":
parameterInput = document.createElement("input");
parameterInput.type = "checkbox";
if (defaultValue === true) parameterInput.checked = true;
break;
case 'array':
case 'object':
case "array":
case "object":
//TODO compare to doc and check if fileInput array? parameter.schema.format === 'binary'
parameterInput = document.createElement('textarea');
parameterInput = document.createElement("textarea");
parameterInput.placeholder = `Enter a JSON formatted ${parameter.schema.type}, If this is a fileInput, it is not currently supported`;
parameterInput.className = "form-control";
break;
default:
parameterInput = document.createElement('input');
parameterInput.type = 'text';
parameterInput = document.createElement("input");
parameterInput.type = "text";
parameterInput.className = "form-control";
if (defaultValue !== undefined) parameterInput.value = defaultValue;
}
@ -416,15 +400,15 @@ document.getElementById('addOperationBtn').addEventListener('click', function()
let savedValue = operationSettings[operation][parameter.name];
switch (parameter.schema.type) {
case 'number':
case 'integer':
case "number":
case "integer":
parameterInput.value = savedValue.toString();
break;
case 'boolean':
case "boolean":
parameterInput.checked = savedValue;
break;
case 'array':
case 'object':
case "array":
case "object":
parameterInput.value = JSON.stringify(savedValue);
break;
default:
@ -438,27 +422,27 @@ document.getElementById('addOperationBtn').addEventListener('click', function()
});
if (hasSettings) {
let saveButton = document.createElement('button');
let saveButton = document.createElement("button");
saveButton.textContent = saveSettings;
saveButton.className = "btn btn-primary";
saveButton.addEventListener('click', function(event) {
saveButton.addEventListener("click", function (event) {
event.preventDefault();
let settings = {};
operationData.forEach(parameter => {
operationData.forEach((parameter) => {
if (parameter.name !== "fileInput") {
let value = document.getElementById(parameter.name).value;
switch (parameter.schema.type) {
case 'number':
case 'integer':
case "number":
case "integer":
settings[parameter.name] = Number(value);
break;
case 'boolean':
case "boolean":
settings[parameter.name] = document.getElementById(parameter.name).checked;
break;
case 'array':
case 'object':
if (value === null || value === '') {
settings[parameter.name] = '';
case "array":
case "object":
if (value === null || value === "") {
settings[parameter.name] = "";
} else {
try {
settings[parameter.name] = JSON.parse(value);
@ -493,17 +477,10 @@ document.getElementById('addOperationBtn').addEventListener('click', function()
showpipelineSettingsModal(selectedOperation);
updateConfigInDropdown();
hideOrShowPipelineHeader();
});
function updateConfigInDropdown() {
let pipelineSelect = document.getElementById('pipelineSelect');
let pipelineSelect = document.getElementById("pipelineSelect");
let selectedOption = pipelineSelect.options[pipelineSelect.selectedIndex];
// Get the current configuration as JSON
@ -516,53 +493,50 @@ function updateConfigInDropdown() {
// Update the value of the selected option with the new configuration
selectedOption.value = pipelineConfigJson;
}
var saveBtn = document.getElementById('savePipelineBtn');
var saveBtn = document.getElementById("savePipelineBtn");
// Remove any existing event listeners
saveBtn.removeEventListener('click', savePipeline);
saveBtn.removeEventListener("click", savePipeline);
// Add the event listener
saveBtn.addEventListener('click', savePipeline);
console.log("saveBtn", saveBtn)
saveBtn.addEventListener("click", savePipeline);
console.log("saveBtn", saveBtn);
function configToJson() {
if (!validatePipeline()) {
return null; // Return null if validation fails
}
var pipelineName = document.getElementById('pipelineName').value;
let pipelineList = document.getElementById('pipelineList').children;
var pipelineName = document.getElementById("pipelineName").value;
let pipelineList = document.getElementById("pipelineList").children;
let pipelineConfig = {
"name": pipelineName,
"pipeline": [],
"_examples": {
"outputDir": "{outputFolder}/{folderName}",
"outputFileName": "{filename}-{pipelineName}-{date}-{time}"
name: pipelineName,
pipeline: [],
_examples: {
outputDir: "{outputFolder}/{folderName}",
outputFileName: "{filename}-{pipelineName}-{date}-{time}",
},
"outputDir": "{outputFolder}",
"outputFileName": "{filename}"
outputDir: "{outputFolder}",
outputFileName: "{filename}",
};
for (let i = 0; i < pipelineList.length; i++) {
let operationName = pipelineList[i].querySelector('.operationName').textContent;
let operationName = pipelineList[i].querySelector(".operationName").textContent;
let parameters = operationSettings[operationName] || {};
parameters['fileInput'] = 'automated';
parameters["fileInput"] = "automated";
pipelineConfig.pipeline.push({
"operation": operationName,
"parameters": parameters
operation: operationName,
parameters: parameters,
});
}
return JSON.stringify(pipelineConfig, null, 2);
}
function savePipeline() {
let pipelineConfigJson = configToJson();
if (!pipelineConfigJson) {
@ -570,79 +544,76 @@ function savePipeline() {
return;
}
let pipelineName = document.getElementById('pipelineName').value;
let pipelineName = document.getElementById("pipelineName").value;
console.log("Downloading...");
let a = document.createElement('a');
a.href = URL.createObjectURL(new Blob([pipelineConfigJson], { type: 'application/json' }));
a.download = pipelineName + '.json';
a.style.display = 'none';
let a = document.createElement("a");
a.href = URL.createObjectURL(new Blob([pipelineConfigJson], { type: "application/json" }));
a.download = pipelineName + ".json";
a.style.display = "none";
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
}
async function processPipelineConfig(configString) {
console.log("configString", configString);
let pipelineConfig = JSON.parse(configString);
let pipelineList = document.getElementById('pipelineList');
let pipelineList = document.getElementById("pipelineList");
while (pipelineList.firstChild) {
pipelineList.removeChild(pipelineList.firstChild);
}
document.getElementById('pipelineName').value = pipelineConfig.name
document.getElementById("pipelineName").value = pipelineConfig.name;
for (const operationConfig of pipelineConfig.pipeline) {
let operationsDropdown = document.getElementById('operationsDropdown');
let operationsDropdown = document.getElementById("operationsDropdown");
operationsDropdown.value = operationConfig.operation;
operationSettings[operationConfig.operation] = operationConfig.parameters;
// assuming addOperation is async
await new Promise((resolve) => {
document.getElementById('addOperationBtn').addEventListener('click', resolve, { once: true });
document.getElementById('addOperationBtn').click();
document.getElementById("addOperationBtn").addEventListener("click", resolve, { once: true });
document.getElementById("addOperationBtn").click();
});
let lastOperation = pipelineList.lastChild;
Object.keys(operationConfig.parameters).forEach(parameterName => {
Object.keys(operationConfig.parameters).forEach((parameterName) => {
let input = document.getElementById(parameterName);
if (input) {
switch (input.type) {
case 'checkbox':
case "checkbox":
input.checked = operationConfig.parameters[parameterName];
break;
case 'number':
case "number":
input.value = operationConfig.parameters[parameterName].toString();
break;
case 'file':
if (parameterName !== 'fileInput') {
case "file":
if (parameterName !== "fileInput") {
// Create a new file input element
let newInput = document.createElement('input');
newInput.type = 'file';
let newInput = document.createElement("input");
newInput.type = "file";
newInput.id = parameterName;
// Add the new file input to the main page (change the selector according to your needs)
document.querySelector('#main').appendChild(newInput);
document.querySelector("#main").appendChild(newInput);
}
break;
case 'text':
case 'textarea':
case "text":
case "textarea":
default:
input.value = JSON.stringify(operationConfig.parameters[parameterName]);
}
}
});
}
}
document.getElementById('uploadPipelineBtn').addEventListener('click', function() {
document.getElementById('uploadPipelineInput').click();
document.getElementById("uploadPipelineBtn").addEventListener("click", function () {
document.getElementById("uploadPipelineInput").click();
});
document.getElementById('uploadPipelineInput').addEventListener('change', function(e) {
document.getElementById("uploadPipelineInput").addEventListener("change", function (e) {
let reader = new FileReader();
reader.onload = function (event) {
processPipelineConfig(event.target.result);
@ -651,21 +622,20 @@ document.getElementById('uploadPipelineInput').addEventListener('change', functi
hideOrShowPipelineHeader();
});
document.getElementById('pipelineSelect').addEventListener('change', function(e) {
document.getElementById("pipelineSelect").addEventListener("change", function (e) {
let selectedPipelineJson = e.target.value; // assuming the selected value is the JSON string of the pipeline config
processPipelineConfig(selectedPipelineJson);
});
function hideOrShowPipelineHeader() {
var pipelineHeader = document.getElementById('pipelineHeader');
var pipelineList = document.getElementById('pipelineList');
var pipelineHeader = document.getElementById("pipelineHeader");
var pipelineList = document.getElementById("pipelineList");
if (pipelineList.children.length === 0) {
// Hide the pipeline header if there are no items in the pipeline list
pipelineHeader.style.display = 'none';
pipelineHeader.style.display = "none";
} else {
// Show the pipeline header if there are items in the pipeline list
pipelineHeader.style.display = 'block';
pipelineHeader.style.display = "block";
}
}

View File

@ -1,15 +1,15 @@
// Toggle search bar when the search icon is clicked
document.querySelector('#search-icon').addEventListener('click', function(e) {
document.querySelector("#search-icon").addEventListener("click", function (e) {
e.preventDefault();
var searchBar = document.querySelector('#navbarSearch');
searchBar.classList.toggle('show');
var searchBar = document.querySelector("#navbarSearch");
searchBar.classList.toggle("show");
});
window.onload = function () {
var items = document.querySelectorAll('.dropdown-item, .nav-link');
var dummyContainer = document.createElement('div');
dummyContainer.style.position = 'absolute';
dummyContainer.style.visibility = 'hidden';
dummyContainer.style.whiteSpace = 'nowrap'; // Ensure we measure full width
var items = document.querySelectorAll(".dropdown-item, .nav-link");
var dummyContainer = document.createElement("div");
dummyContainer.style.position = "absolute";
dummyContainer.style.visibility = "hidden";
dummyContainer.style.whiteSpace = "nowrap"; // Ensure we measure full width
document.body.appendChild(dummyContainer);
var maxWidth = 0;
@ -31,36 +31,39 @@ window.onload = function() {
};
// Show search results as user types in search box
document.querySelector('#navbarSearchInput').addEventListener('input', function(e) {
document.querySelector("#navbarSearchInput").addEventListener("input", function (e) {
var searchText = e.target.value.toLowerCase();
var items = document.querySelectorAll('.dropdown-item, .nav-link');
var resultsBox = document.querySelector('#searchResults');
var items = document.querySelectorAll(".dropdown-item, .nav-link");
var resultsBox = document.querySelector("#searchResults");
// Clear any previous results
resultsBox.innerHTML = '';
resultsBox.innerHTML = "";
items.forEach(function (item) {
var titleElement = item.querySelector('.icon-text');
var iconElement = item.querySelector('.icon');
var itemHref = item.getAttribute('href');
var tags = item.getAttribute('data-bs-tags') || ""; // If no tags, default to empty string
var titleElement = item.querySelector(".icon-text");
var iconElement = item.querySelector(".icon");
var itemHref = item.getAttribute("href");
var tags = item.getAttribute("data-bs-tags") || ""; // If no tags, default to empty string
if (titleElement && iconElement && itemHref !== '#') {
if (titleElement && iconElement && itemHref !== "#") {
var title = titleElement.innerText;
if ((title.toLowerCase().indexOf(searchText) !== -1 || tags.toLowerCase().indexOf(searchText) !== -1) && !resultsBox.querySelector(`a[href="${item.getAttribute('href')}"]`)) {
var result = document.createElement('a');
if (
(title.toLowerCase().indexOf(searchText) !== -1 || tags.toLowerCase().indexOf(searchText) !== -1) &&
!resultsBox.querySelector(`a[href="${item.getAttribute("href")}"]`)
) {
var result = document.createElement("a");
result.href = itemHref;
result.classList.add('dropdown-item');
result.classList.add("dropdown-item");
var resultIcon = document.createElement('img');
var resultIcon = document.createElement("img");
resultIcon.src = iconElement.src;
resultIcon.alt = 'icon';
resultIcon.classList.add('icon');
resultIcon.alt = "icon";
resultIcon.classList.add("icon");
result.appendChild(resultIcon);
var resultText = document.createElement('span');
var resultText = document.createElement("span");
resultText.textContent = title;
resultText.classList.add('icon-text');
resultText.classList.add("icon-text");
result.appendChild(resultText);
resultsBox.appendChild(result);
@ -69,7 +72,5 @@ document.querySelector('#navbarSearchInput').addEventListener('input', function(
});
// Set the width of the search results box to the maximum width
resultsBox.style.width = window.navItemMaxWidth + 'px';
resultsBox.style.width = window.navItemMaxWidth + "px";
});

View File

@ -1,42 +1,33 @@
// Get the download option from local storage, or set it to 'sameWindow' if it doesn't exist
var downloadOption = localStorage.getItem('downloadOption')
|| 'sameWindow';
var downloadOption = localStorage.getItem("downloadOption") || "sameWindow";
// 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
document.getElementById('downloadOption').addEventListener(
'change',
function() {
document.getElementById("downloadOption").addEventListener("change", function () {
downloadOption = this.value;
localStorage.setItem('downloadOption',
downloadOption);
localStorage.setItem("downloadOption", downloadOption);
});
// Get the zipThreshold value from local storage, or set it to 0 if it doesn't exist
var zipThreshold = parseInt(localStorage.getItem('zipThreshold'), 10) || 4;
var zipThreshold = parseInt(localStorage.getItem("zipThreshold"), 10) || 4;
// Set the value of the slider and the display span
document.getElementById('zipThreshold').value = zipThreshold;
document.getElementById('zipThresholdValue').textContent = zipThreshold;
document.getElementById("zipThreshold").value = zipThreshold;
document.getElementById("zipThresholdValue").textContent = zipThreshold;
// 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;
document.getElementById('zipThresholdValue').textContent = zipThreshold;
localStorage.setItem('zipThreshold', zipThreshold);
document.getElementById("zipThresholdValue").textContent = zipThreshold;
localStorage.setItem("zipThreshold", zipThreshold);
});
var boredWaiting = localStorage.getItem("boredWaiting") || "disabled";
document.getElementById("boredWaiting").checked = boredWaiting === "enabled";
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);
document.getElementById("boredWaiting").addEventListener("change", function () {
boredWaiting = this.checked ? "enabled" : "disabled";
localStorage.setItem("boredWaiting", boredWaiting);
});

View File

@ -1,20 +1,19 @@
TabContainer = {
initTabGroups() {
const groups = document.querySelectorAll(".tab-group");
const unloadedGroups = [...groups].filter(g => !g.initialised);
unloadedGroups.forEach(group => {
const unloadedGroups = [...groups].filter((g) => !g.initialised);
unloadedGroups.forEach((group) => {
const containers = group.querySelectorAll(".tab-container");
const tabTitles = [...containers].map(c => c.getAttribute("title"));
const tabTitles = [...containers].map((c) => c.getAttribute("title"));
const tabList = document.createElement("div");
tabList.classList.add("tab-buttons");
tabTitles.forEach(title => {
tabTitles.forEach((title) => {
const tabButton = document.createElement("button");
tabButton.innerHTML = title;
tabButton.onclick = e => {
tabButton.onclick = (e) => {
this.setActiveTab(e.target);
}
};
tabList.appendChild(tabButton);
});
group.prepend(tabList);
@ -25,15 +24,15 @@ TabContainer = {
});
},
setActiveTab(tabButton) {
const group = tabButton.closest(".tab-group")
const group = tabButton.closest(".tab-group");
group.querySelectorAll(".active").forEach(el => el.classList.remove("active"));
group.querySelectorAll(".active").forEach((el) => el.classList.remove("active"));
tabButton.classList.add("active");
group.querySelector(`[title="${tabButton.innerHTML}"]`).classList.add("active");
},
}
};
document.addEventListener("DOMContentLoaded", () => {
TabContainer.initTabGroups();
})
});

View File

@ -116,7 +116,6 @@
top: 0;
}
:root {
--annotation-unfocused-field-background: url("data:image/svg+xml;charset=UTF-8,<svg width='1px' height='1px' xmlns='http://www.w3.org/2000/svg'><rect width='100%' height='100%' style='fill:rgba(0, 54, 255, 0.13);'/></svg>");
--input-focus-border-color: Highlight;
@ -139,9 +138,7 @@
.annotationLayer .textWidgetAnnotation :is(input, textarea):required,
.annotationLayer .choiceWidgetAnnotation select:required,
.annotationLayer
.buttonWidgetAnnotation:is(.checkBox, .radioButton)
input:required {
.annotationLayer .buttonWidgetAnnotation:is(.checkBox, .radioButton) input:required {
outline: 1.5px solid selectedItem;
}
@ -228,9 +225,7 @@
height: 100%;
}
.annotationLayer
:is(.linkAnnotation, .buttonWidgetAnnotation.pushButton):not(.hasBorder)
> a:hover {
.annotationLayer :is(.linkAnnotation, .buttonWidgetAnnotation.pushButton):not(.hasBorder) > a:hover {
opacity: 0.2;
background-color: rgba(255, 255, 0, 1);
box-shadow: 0 2px 10px rgba(255, 255, 0, 1);
@ -268,9 +263,7 @@
.annotationLayer .textWidgetAnnotation :is(input, textarea):required,
.annotationLayer .choiceWidgetAnnotation select:required,
.annotationLayer
.buttonWidgetAnnotation:is(.checkBox, .radioButton)
input:required {
.annotationLayer .buttonWidgetAnnotation:is(.checkBox, .radioButton) input:required {
outline: 1.5px solid red;
}
@ -288,9 +281,7 @@ input:required {
.annotationLayer .textWidgetAnnotation :is(input, textarea)[disabled],
.annotationLayer .choiceWidgetAnnotation select[disabled],
.annotationLayer
.buttonWidgetAnnotation:is(.checkBox, .radioButton)
input[disabled] {
.annotationLayer .buttonWidgetAnnotation:is(.checkBox, .radioButton) input[disabled] {
background: none;
border: 2px solid var(--input-disabled-border-color);
cursor: not-allowed;
@ -298,9 +289,7 @@ input[disabled] {
.annotationLayer .textWidgetAnnotation :is(input, textarea):hover,
.annotationLayer .choiceWidgetAnnotation select:hover,
.annotationLayer
.buttonWidgetAnnotation:is(.checkBox, .radioButton)
input:hover {
.annotationLayer .buttonWidgetAnnotation:is(.checkBox, .radioButton) input:hover {
border: 2px solid var(--input-hover-border-color);
}
@ -489,7 +478,6 @@ input:hover {
z-index: -1;
}
:root {
--xfa-unfocused-field-background: url("data:image/svg+xml;charset=UTF-8,<svg width='1px' height='1px' xmlns='http://www.w3.org/2000/svg'><rect width='100%' height='100%' style='fill:rgba(0, 54, 255, 0.13);'/></svg>");
--xfa-focus-outline: auto;
@ -827,10 +815,7 @@ input:hover {
--freetext-padding: 2px;
--resizer-bg-color: var(--outline-color);
--resizer-size: 6px;
--resizer-shift: calc(
0px - (var(--outline-width) + var(--resizer-size)) / 2 -
var(--outline-around-width)
);
--resizer-shift: calc(0px - (var(--outline-width) + var(--resizer-size)) / 2 - var(--outline-around-width));
--editorFreeText-editing-cursor: text;
--editorInk-editing-cursor: url(../images/cursor-editorInk.svg) 0 16, pointer;
@ -853,8 +838,7 @@ input:hover {
@media (-webkit-min-device-pixel-ratio: 1.1), (min-resolution: 1.1dppx) {
:root {
--editorFreeText-editing-cursor: url(../images/cursor-editorFreeText.svg) 0 16,
text;
--editorFreeText-editing-cursor: url(../images/cursor-editorFreeText.svg) 0 16, text;
}
}
@ -1109,154 +1093,282 @@ input:hover {
}
.annotationEditorLayer[data-main-rotation="0"]
:is([data-editor-rotation="0"], [data-editor-rotation="180"]) > .resizers > .resizer.topLeft,
:is([data-editor-rotation="0"], [data-editor-rotation="180"])
> .resizers
> .resizer.topLeft,
.annotationEditorLayer[data-main-rotation="90"]
:is([data-editor-rotation="270"], [data-editor-rotation="90"]) > .resizers > .resizer.topLeft,
:is([data-editor-rotation="270"], [data-editor-rotation="90"])
> .resizers
> .resizer.topLeft,
.annotationEditorLayer[data-main-rotation="180"]
:is([data-editor-rotation="180"], [data-editor-rotation="0"]) > .resizers > .resizer.topLeft,
:is([data-editor-rotation="180"], [data-editor-rotation="0"])
> .resizers
> .resizer.topLeft,
.annotationEditorLayer[data-main-rotation="270"]
:is([data-editor-rotation="90"], [data-editor-rotation="270"]) > .resizers > .resizer.topLeft,
:is([data-editor-rotation="90"], [data-editor-rotation="270"])
> .resizers
> .resizer.topLeft,
.annotationEditorLayer[data-main-rotation="0"]
:is([data-editor-rotation="0"], [data-editor-rotation="180"]) > .resizers > .resizer.bottomRight,
:is([data-editor-rotation="0"], [data-editor-rotation="180"])
> .resizers
> .resizer.bottomRight,
.annotationEditorLayer[data-main-rotation="90"]
:is([data-editor-rotation="270"], [data-editor-rotation="90"]) > .resizers > .resizer.bottomRight,
:is([data-editor-rotation="270"], [data-editor-rotation="90"])
> .resizers
> .resizer.bottomRight,
.annotationEditorLayer[data-main-rotation="180"]
:is([data-editor-rotation="180"], [data-editor-rotation="0"]) > .resizers > .resizer.bottomRight,
:is([data-editor-rotation="180"], [data-editor-rotation="0"])
> .resizers
> .resizer.bottomRight,
.annotationEditorLayer[data-main-rotation="270"]
:is([data-editor-rotation="90"], [data-editor-rotation="270"]) > .resizers > .resizer.bottomRight {
:is([data-editor-rotation="90"], [data-editor-rotation="270"])
> .resizers
> .resizer.bottomRight {
cursor: nwse-resize;
}
.annotationEditorLayer[data-main-rotation="0"]
:is([data-editor-rotation="0"], [data-editor-rotation="180"]) > .resizers > .resizer.topMiddle,
:is([data-editor-rotation="0"], [data-editor-rotation="180"])
> .resizers
> .resizer.topMiddle,
.annotationEditorLayer[data-main-rotation="90"]
:is([data-editor-rotation="270"], [data-editor-rotation="90"]) > .resizers > .resizer.topMiddle,
:is([data-editor-rotation="270"], [data-editor-rotation="90"])
> .resizers
> .resizer.topMiddle,
.annotationEditorLayer[data-main-rotation="180"]
:is([data-editor-rotation="180"], [data-editor-rotation="0"]) > .resizers > .resizer.topMiddle,
:is([data-editor-rotation="180"], [data-editor-rotation="0"])
> .resizers
> .resizer.topMiddle,
.annotationEditorLayer[data-main-rotation="270"]
:is([data-editor-rotation="90"], [data-editor-rotation="270"]) > .resizers > .resizer.topMiddle,
:is([data-editor-rotation="90"], [data-editor-rotation="270"])
> .resizers
> .resizer.topMiddle,
.annotationEditorLayer[data-main-rotation="0"]
:is([data-editor-rotation="0"], [data-editor-rotation="180"]) > .resizers > .resizer.bottomMiddle,
:is([data-editor-rotation="0"], [data-editor-rotation="180"])
> .resizers
> .resizer.bottomMiddle,
.annotationEditorLayer[data-main-rotation="90"]
:is([data-editor-rotation="270"], [data-editor-rotation="90"]) > .resizers > .resizer.bottomMiddle,
:is([data-editor-rotation="270"], [data-editor-rotation="90"])
> .resizers
> .resizer.bottomMiddle,
.annotationEditorLayer[data-main-rotation="180"]
:is([data-editor-rotation="180"], [data-editor-rotation="0"]) > .resizers > .resizer.bottomMiddle,
:is([data-editor-rotation="180"], [data-editor-rotation="0"])
> .resizers
> .resizer.bottomMiddle,
.annotationEditorLayer[data-main-rotation="270"]
:is([data-editor-rotation="90"], [data-editor-rotation="270"]) > .resizers > .resizer.bottomMiddle {
:is([data-editor-rotation="90"], [data-editor-rotation="270"])
> .resizers
> .resizer.bottomMiddle {
cursor: ns-resize;
}
.annotationEditorLayer[data-main-rotation="0"]
:is([data-editor-rotation="0"], [data-editor-rotation="180"]) > .resizers > .resizer.topRight,
:is([data-editor-rotation="0"], [data-editor-rotation="180"])
> .resizers
> .resizer.topRight,
.annotationEditorLayer[data-main-rotation="90"]
:is([data-editor-rotation="270"], [data-editor-rotation="90"]) > .resizers > .resizer.topRight,
:is([data-editor-rotation="270"], [data-editor-rotation="90"])
> .resizers
> .resizer.topRight,
.annotationEditorLayer[data-main-rotation="180"]
:is([data-editor-rotation="180"], [data-editor-rotation="0"]) > .resizers > .resizer.topRight,
:is([data-editor-rotation="180"], [data-editor-rotation="0"])
> .resizers
> .resizer.topRight,
.annotationEditorLayer[data-main-rotation="270"]
:is([data-editor-rotation="90"], [data-editor-rotation="270"]) > .resizers > .resizer.topRight,
:is([data-editor-rotation="90"], [data-editor-rotation="270"])
> .resizers
> .resizer.topRight,
.annotationEditorLayer[data-main-rotation="0"]
:is([data-editor-rotation="0"], [data-editor-rotation="180"]) > .resizers > .resizer.bottomLeft,
:is([data-editor-rotation="0"], [data-editor-rotation="180"])
> .resizers
> .resizer.bottomLeft,
.annotationEditorLayer[data-main-rotation="90"]
:is([data-editor-rotation="270"], [data-editor-rotation="90"]) > .resizers > .resizer.bottomLeft,
:is([data-editor-rotation="270"], [data-editor-rotation="90"])
> .resizers
> .resizer.bottomLeft,
.annotationEditorLayer[data-main-rotation="180"]
:is([data-editor-rotation="180"], [data-editor-rotation="0"]) > .resizers > .resizer.bottomLeft,
:is([data-editor-rotation="180"], [data-editor-rotation="0"])
> .resizers
> .resizer.bottomLeft,
.annotationEditorLayer[data-main-rotation="270"]
:is([data-editor-rotation="90"], [data-editor-rotation="270"]) > .resizers > .resizer.bottomLeft {
:is([data-editor-rotation="90"], [data-editor-rotation="270"])
> .resizers
> .resizer.bottomLeft {
cursor: nesw-resize;
}
.annotationEditorLayer[data-main-rotation="0"]
:is([data-editor-rotation="0"], [data-editor-rotation="180"]) > .resizers > .resizer.middleRight,
:is([data-editor-rotation="0"], [data-editor-rotation="180"])
> .resizers
> .resizer.middleRight,
.annotationEditorLayer[data-main-rotation="90"]
:is([data-editor-rotation="270"], [data-editor-rotation="90"]) > .resizers > .resizer.middleRight,
:is([data-editor-rotation="270"], [data-editor-rotation="90"])
> .resizers
> .resizer.middleRight,
.annotationEditorLayer[data-main-rotation="180"]
:is([data-editor-rotation="180"], [data-editor-rotation="0"]) > .resizers > .resizer.middleRight,
:is([data-editor-rotation="180"], [data-editor-rotation="0"])
> .resizers
> .resizer.middleRight,
.annotationEditorLayer[data-main-rotation="270"]
:is([data-editor-rotation="90"], [data-editor-rotation="270"]) > .resizers > .resizer.middleRight,
:is([data-editor-rotation="90"], [data-editor-rotation="270"])
> .resizers
> .resizer.middleRight,
.annotationEditorLayer[data-main-rotation="0"]
:is([data-editor-rotation="0"], [data-editor-rotation="180"]) > .resizers > .resizer.middleLeft,
:is([data-editor-rotation="0"], [data-editor-rotation="180"])
> .resizers
> .resizer.middleLeft,
.annotationEditorLayer[data-main-rotation="90"]
:is([data-editor-rotation="270"], [data-editor-rotation="90"]) > .resizers > .resizer.middleLeft,
:is([data-editor-rotation="270"], [data-editor-rotation="90"])
> .resizers
> .resizer.middleLeft,
.annotationEditorLayer[data-main-rotation="180"]
:is([data-editor-rotation="180"], [data-editor-rotation="0"]) > .resizers > .resizer.middleLeft,
:is([data-editor-rotation="180"], [data-editor-rotation="0"])
> .resizers
> .resizer.middleLeft,
.annotationEditorLayer[data-main-rotation="270"]
:is([data-editor-rotation="90"], [data-editor-rotation="270"]) > .resizers > .resizer.middleLeft {
:is([data-editor-rotation="90"], [data-editor-rotation="270"])
> .resizers
> .resizer.middleLeft {
cursor: ew-resize;
}
.annotationEditorLayer[data-main-rotation="0"]
:is([data-editor-rotation="90"], [data-editor-rotation="270"]) > .resizers > .resizer.topLeft,
:is([data-editor-rotation="90"], [data-editor-rotation="270"])
> .resizers
> .resizer.topLeft,
.annotationEditorLayer[data-main-rotation="90"]
:is([data-editor-rotation="0"], [data-editor-rotation="180"]) > .resizers > .resizer.topLeft,
:is([data-editor-rotation="0"], [data-editor-rotation="180"])
> .resizers
> .resizer.topLeft,
.annotationEditorLayer[data-main-rotation="180"]
:is([data-editor-rotation="270"], [data-editor-rotation="90"]) > .resizers > .resizer.topLeft,
:is([data-editor-rotation="270"], [data-editor-rotation="90"])
> .resizers
> .resizer.topLeft,
.annotationEditorLayer[data-main-rotation="270"]
:is([data-editor-rotation="180"], [data-editor-rotation="0"]) > .resizers > .resizer.topLeft,
:is([data-editor-rotation="180"], [data-editor-rotation="0"])
> .resizers
> .resizer.topLeft,
.annotationEditorLayer[data-main-rotation="0"]
:is([data-editor-rotation="90"], [data-editor-rotation="270"]) > .resizers > .resizer.bottomRight,
:is([data-editor-rotation="90"], [data-editor-rotation="270"])
> .resizers
> .resizer.bottomRight,
.annotationEditorLayer[data-main-rotation="90"]
:is([data-editor-rotation="0"], [data-editor-rotation="180"]) > .resizers > .resizer.bottomRight,
:is([data-editor-rotation="0"], [data-editor-rotation="180"])
> .resizers
> .resizer.bottomRight,
.annotationEditorLayer[data-main-rotation="180"]
:is([data-editor-rotation="270"], [data-editor-rotation="90"]) > .resizers > .resizer.bottomRight,
:is([data-editor-rotation="270"], [data-editor-rotation="90"])
> .resizers
> .resizer.bottomRight,
.annotationEditorLayer[data-main-rotation="270"]
:is([data-editor-rotation="180"], [data-editor-rotation="0"]) > .resizers > .resizer.bottomRight {
:is([data-editor-rotation="180"], [data-editor-rotation="0"])
> .resizers
> .resizer.bottomRight {
cursor: nesw-resize;
}
.annotationEditorLayer[data-main-rotation="0"]
:is([data-editor-rotation="90"], [data-editor-rotation="270"]) > .resizers > .resizer.topMiddle,
:is([data-editor-rotation="90"], [data-editor-rotation="270"])
> .resizers
> .resizer.topMiddle,
.annotationEditorLayer[data-main-rotation="90"]
:is([data-editor-rotation="0"], [data-editor-rotation="180"]) > .resizers > .resizer.topMiddle,
:is([data-editor-rotation="0"], [data-editor-rotation="180"])
> .resizers
> .resizer.topMiddle,
.annotationEditorLayer[data-main-rotation="180"]
:is([data-editor-rotation="270"], [data-editor-rotation="90"]) > .resizers > .resizer.topMiddle,
:is([data-editor-rotation="270"], [data-editor-rotation="90"])
> .resizers
> .resizer.topMiddle,
.annotationEditorLayer[data-main-rotation="270"]
:is([data-editor-rotation="180"], [data-editor-rotation="0"]) > .resizers > .resizer.topMiddle,
:is([data-editor-rotation="180"], [data-editor-rotation="0"])
> .resizers
> .resizer.topMiddle,
.annotationEditorLayer[data-main-rotation="0"]
:is([data-editor-rotation="90"], [data-editor-rotation="270"]) > .resizers > .resizer.bottomMiddle,
:is([data-editor-rotation="90"], [data-editor-rotation="270"])
> .resizers
> .resizer.bottomMiddle,
.annotationEditorLayer[data-main-rotation="90"]
:is([data-editor-rotation="0"], [data-editor-rotation="180"]) > .resizers > .resizer.bottomMiddle,
:is([data-editor-rotation="0"], [data-editor-rotation="180"])
> .resizers
> .resizer.bottomMiddle,
.annotationEditorLayer[data-main-rotation="180"]
:is([data-editor-rotation="270"], [data-editor-rotation="90"]) > .resizers > .resizer.bottomMiddle,
:is([data-editor-rotation="270"], [data-editor-rotation="90"])
> .resizers
> .resizer.bottomMiddle,
.annotationEditorLayer[data-main-rotation="270"]
:is([data-editor-rotation="180"], [data-editor-rotation="0"]) > .resizers > .resizer.bottomMiddle {
:is([data-editor-rotation="180"], [data-editor-rotation="0"])
> .resizers
> .resizer.bottomMiddle {
cursor: ew-resize;
}
.annotationEditorLayer[data-main-rotation="0"]
:is([data-editor-rotation="90"], [data-editor-rotation="270"]) > .resizers > .resizer.topRight,
:is([data-editor-rotation="90"], [data-editor-rotation="270"])
> .resizers
> .resizer.topRight,
.annotationEditorLayer[data-main-rotation="90"]
:is([data-editor-rotation="0"], [data-editor-rotation="180"]) > .resizers > .resizer.topRight,
:is([data-editor-rotation="0"], [data-editor-rotation="180"])
> .resizers
> .resizer.topRight,
.annotationEditorLayer[data-main-rotation="180"]
:is([data-editor-rotation="270"], [data-editor-rotation="90"]) > .resizers > .resizer.topRight,
:is([data-editor-rotation="270"], [data-editor-rotation="90"])
> .resizers
> .resizer.topRight,
.annotationEditorLayer[data-main-rotation="270"]
:is([data-editor-rotation="180"], [data-editor-rotation="0"]) > .resizers > .resizer.topRight,
:is([data-editor-rotation="180"], [data-editor-rotation="0"])
> .resizers
> .resizer.topRight,
.annotationEditorLayer[data-main-rotation="0"]
:is([data-editor-rotation="90"], [data-editor-rotation="270"]) > .resizers > .resizer.bottomLeft,
:is([data-editor-rotation="90"], [data-editor-rotation="270"])
> .resizers
> .resizer.bottomLeft,
.annotationEditorLayer[data-main-rotation="90"]
:is([data-editor-rotation="0"], [data-editor-rotation="180"]) > .resizers > .resizer.bottomLeft,
:is([data-editor-rotation="0"], [data-editor-rotation="180"])
> .resizers
> .resizer.bottomLeft,
.annotationEditorLayer[data-main-rotation="180"]
:is([data-editor-rotation="270"], [data-editor-rotation="90"]) > .resizers > .resizer.bottomLeft,
:is([data-editor-rotation="270"], [data-editor-rotation="90"])
> .resizers
> .resizer.bottomLeft,
.annotationEditorLayer[data-main-rotation="270"]
:is([data-editor-rotation="180"], [data-editor-rotation="0"]) > .resizers > .resizer.bottomLeft {
:is([data-editor-rotation="180"], [data-editor-rotation="0"])
> .resizers
> .resizer.bottomLeft {
cursor: nwse-resize;
}
.annotationEditorLayer[data-main-rotation="0"]
:is([data-editor-rotation="90"], [data-editor-rotation="270"]) > .resizers > .resizer.middleRight,
:is([data-editor-rotation="90"], [data-editor-rotation="270"])
> .resizers
> .resizer.middleRight,
.annotationEditorLayer[data-main-rotation="90"]
:is([data-editor-rotation="0"], [data-editor-rotation="180"]) > .resizers > .resizer.middleRight,
:is([data-editor-rotation="0"], [data-editor-rotation="180"])
> .resizers
> .resizer.middleRight,
.annotationEditorLayer[data-main-rotation="180"]
:is([data-editor-rotation="270"], [data-editor-rotation="90"]) > .resizers > .resizer.middleRight,
:is([data-editor-rotation="270"], [data-editor-rotation="90"])
> .resizers
> .resizer.middleRight,
.annotationEditorLayer[data-main-rotation="270"]
:is([data-editor-rotation="180"], [data-editor-rotation="0"]) > .resizers > .resizer.middleRight,
:is([data-editor-rotation="180"], [data-editor-rotation="0"])
> .resizers
> .resizer.middleRight,
.annotationEditorLayer[data-main-rotation="0"]
:is([data-editor-rotation="90"], [data-editor-rotation="270"]) > .resizers > .resizer.middleLeft,
:is([data-editor-rotation="90"], [data-editor-rotation="270"])
> .resizers
> .resizer.middleLeft,
.annotationEditorLayer[data-main-rotation="90"]
:is([data-editor-rotation="0"], [data-editor-rotation="180"]) > .resizers > .resizer.middleLeft,
:is([data-editor-rotation="0"], [data-editor-rotation="180"])
> .resizers
> .resizer.middleLeft,
.annotationEditorLayer[data-main-rotation="180"]
:is([data-editor-rotation="270"], [data-editor-rotation="90"]) > .resizers > .resizer.middleLeft,
:is([data-editor-rotation="270"], [data-editor-rotation="90"])
> .resizers
> .resizer.middleLeft,
.annotationEditorLayer[data-main-rotation="270"]
:is([data-editor-rotation="180"], [data-editor-rotation="0"]) > .resizers > .resizer.middleLeft {
:is([data-editor-rotation="180"], [data-editor-rotation="0"])
> .resizers
> .resizer.middleLeft {
cursor: ns-resize;
}
@ -1266,48 +1378,57 @@ input:hover {
[data-main-rotation="90"] [data-editor-rotation="0"],
[data-main-rotation="180"] [data-editor-rotation="270"],
[data-main-rotation="270"] [data-editor-rotation="180"]
) .altText {
)
.altText {
rotate: 270deg;
}
[dir="ltr"] .annotationEditorLayer
[dir="ltr"]
.annotationEditorLayer
:is(
[data-main-rotation="0"] [data-editor-rotation="90"],
[data-main-rotation="90"] [data-editor-rotation="0"],
[data-main-rotation="180"] [data-editor-rotation="270"],
[data-main-rotation="270"] [data-editor-rotation="180"]
) .altText {
)
.altText {
inset-inline-start: calc(100% - 8px);
}
[dir="ltr"] .annotationEditorLayer
[dir="ltr"]
.annotationEditorLayer
:is(
[data-main-rotation="0"] [data-editor-rotation="90"],
[data-main-rotation="90"] [data-editor-rotation="0"],
[data-main-rotation="180"] [data-editor-rotation="270"],
[data-main-rotation="270"] [data-editor-rotation="180"]
) .altText.small {
)
.altText.small {
inset-inline-start: calc(100% + 8px);
inset-block-start: 100%;
}
[dir="rtl"] .annotationEditorLayer
[dir="rtl"]
.annotationEditorLayer
:is(
[data-main-rotation="0"] [data-editor-rotation="90"],
[data-main-rotation="90"] [data-editor-rotation="0"],
[data-main-rotation="180"] [data-editor-rotation="270"],
[data-main-rotation="270"] [data-editor-rotation="180"]
) .altText {
)
.altText {
inset-block-end: calc(100% - 8px);
}
[dir="rtl"] .annotationEditorLayer
[dir="rtl"]
.annotationEditorLayer
:is(
[data-main-rotation="0"] [data-editor-rotation="90"],
[data-main-rotation="90"] [data-editor-rotation="0"],
[data-main-rotation="180"] [data-editor-rotation="270"],
[data-main-rotation="270"] [data-editor-rotation="180"]
) .altText.small {
)
.altText.small {
inset-inline-start: -8px;
inset-block-start: 0;
}
@ -1318,7 +1439,8 @@ input:hover {
[data-main-rotation="90"] [data-editor-rotation="90"],
[data-main-rotation="180"] [data-editor-rotation="0"],
[data-main-rotation="270"] [data-editor-rotation="270"]
) .altText {
)
.altText {
rotate: 180deg;
inset-block-end: calc(100% - 8px);
@ -1331,7 +1453,8 @@ input:hover {
[data-main-rotation="90"] [data-editor-rotation="90"],
[data-main-rotation="180"] [data-editor-rotation="0"],
[data-main-rotation="270"] [data-editor-rotation="270"]
) .altText.small {
)
.altText.small {
inset-inline-start: 100%;
inset-block-start: -8px;
}
@ -1342,48 +1465,57 @@ input:hover {
[data-main-rotation="90"] [data-editor-rotation="180"],
[data-main-rotation="180"] [data-editor-rotation="90"],
[data-main-rotation="270"] [data-editor-rotation="0"]
) .altText {
)
.altText {
rotate: 90deg;
}
[dir="ltr"] .annotationEditorLayer
[dir="ltr"]
.annotationEditorLayer
:is(
[data-main-rotation="0"] [data-editor-rotation="270"],
[data-main-rotation="90"] [data-editor-rotation="180"],
[data-main-rotation="180"] [data-editor-rotation="90"],
[data-main-rotation="270"] [data-editor-rotation="0"]
) .altText {
)
.altText {
inset-block-end: calc(100% - 8px);
}
[dir="ltr"] .annotationEditorLayer
[dir="ltr"]
.annotationEditorLayer
:is(
[data-main-rotation="0"] [data-editor-rotation="270"],
[data-main-rotation="90"] [data-editor-rotation="180"],
[data-main-rotation="180"] [data-editor-rotation="90"],
[data-main-rotation="270"] [data-editor-rotation="0"]
) .altText.small {
)
.altText.small {
inset-inline-start: -8px;
inset-block-start: 0;
}
[dir="rtl"] .annotationEditorLayer
[dir="rtl"]
.annotationEditorLayer
:is(
[data-main-rotation="0"] [data-editor-rotation="270"],
[data-main-rotation="90"] [data-editor-rotation="180"],
[data-main-rotation="180"] [data-editor-rotation="90"],
[data-main-rotation="270"] [data-editor-rotation="0"]
) .altText {
)
.altText {
inset-inline-start: calc(100% - 8px);
}
[dir="rtl"] .annotationEditorLayer
[dir="rtl"]
.annotationEditorLayer
:is(
[data-main-rotation="0"] [data-editor-rotation="270"],
[data-main-rotation="90"] [data-editor-rotation="180"],
[data-main-rotation="180"] [data-editor-rotation="90"],
[data-main-rotation="270"] [data-editor-rotation="0"]
) .altText.small {
)
.altText.small {
inset-inline-start: calc(100% + 8px);
inset-block-start: 100%;
}
@ -1421,7 +1553,6 @@ input:hover {
}
.altText.small {
inset-block-end: unset;
inset-inline-start: 0;
inset-block-start: calc(100% + 8px);
@ -1515,7 +1646,6 @@ input:hover {
}
@media (prefers-color-scheme: dark) {
.altText .tooltip.show {
--alt-text-tooltip-bg: #1c1b22;
--alt-text-tooltip-fg: #fbfbfe;
@ -1524,7 +1654,6 @@ input:hover {
}
@media screen and (forced-colors: active) {
.altText .tooltip.show {
--alt-text-tooltip-bg: Canvas;
--alt-text-tooltip-fg: CanvasText;
@ -1581,7 +1710,6 @@ input:hover {
}
@media (prefers-color-scheme: dark) {
#altTextDialog {
--dialog-bg-color: #1c1b22;
--dialog-border-color: #1c1b22;
@ -1604,7 +1732,6 @@ input:hover {
}
@media screen and (forced-colors: active) {
#altTextDialog {
--dialog-bg-color: Canvas;
--dialog-border-color: CanvasText;
@ -2022,8 +2149,8 @@ input:hover {
--toolbar-border-color: rgba(184, 184, 184, 1);
--toolbar-box-shadow: 0 1px 0 var(--toolbar-border-color);
--toolbar-border-bottom: none;
--toolbarSidebar-box-shadow: inset calc(-1px * var(--dir-factor)) 0 0 rgba(0, 0, 0, 0.25),
0 1px 0 rgba(0, 0, 0, 0.15), 0 0 1px rgba(0, 0, 0, 0.1);
--toolbarSidebar-box-shadow: inset calc(-1px * var(--dir-factor)) 0 0 rgba(0, 0, 0, 0.25), 0 1px 0 rgba(0, 0, 0, 0.15),
0 0 1px rgba(0, 0, 0, 0.1);
--toolbarSidebar-border-bottom: none;
--button-hover-color: rgba(221, 222, 223, 1);
--toggled-btn-color: rgba(0, 0, 0, 1);
@ -2319,8 +2446,7 @@ body {
font: message-box;
}
:is(.toolbar, .editorParamsToolbar, .findbar, #sidebarContainer)
:is(input, button, select),
:is(.toolbar, .editorParamsToolbar, .findbar, #sidebarContainer) :is(input, button, select),
.secondaryToolbar :is(input, button, a, select) {
outline: none;
font: message-box;
@ -2428,8 +2554,7 @@ body {
animation: progressIndeterminate 1s linear infinite;
}
#outerContainer.sidebarResizing
:is(#sidebarContainer, #viewerContainer, #loadingBar) {
#outerContainer.sidebarResizing :is(#sidebarContainer, #viewerContainer, #loadingBar) {
/* Improve responsiveness and avoid visual glitches when the sidebar is resized. */
transition-duration: 0s;
}
@ -2600,7 +2725,8 @@ body {
.doorHanger,
.doorHangerRight {
border-radius: 2px;
box-shadow: 0 1px 5px var(--doorhanger-border-color),
box-shadow:
0 1px 5px var(--doorhanger-border-color),
0 0 0 1px var(--doorhanger-border-color);
border: var(--doorhanger-border-color-whcm);
}
@ -3455,8 +3581,7 @@ dialog :link {
cursor: grab !important;
}
.grab-to-pan-grab
*:not(input):not(textarea):not(button):not(select):not(:link) {
.grab-to-pan-grab *:not(input):not(textarea):not(button):not(select):not(:link) {
cursor: inherit !important;
}

View File

@ -1,22 +1,21 @@
<!DOCTYPE html>
<html th:lang="${#locale.toString()}" th:lang-direction="#{language.direction}" xmlns:th="http://www.thymeleaf.org">
<html th:lang="${#locale.language}" th:lang-direction="#{language.direction}" xmlns:th="http://www.thymeleaf.org">
<head>
<th:block th:insert="~{fragments/common :: head(title='<3')}"></th:block>
</head>
<body>
<div id="page-container">
<div id="content-wrap">
<div th:insert="~{fragments/navbar.html :: navbar}"></div>
<br> <br>
<th:block th:insert="~{fragments/navbar.html :: navbar}"></th:block>
<br /><br />
<div class="container">
<div class="row justify-content-center">
<div class="col-md-6">
<div class="col-md-6"></div>
</div>
</div>
</div>
<div th:insert="~{fragments/footer.html :: footer}"></div>
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
</div>
</body>
</html>

View File

@ -1,45 +1,43 @@
<!doctype html>
<html th:lang="${#locale.toString()}" th:lang-direction="#{language.direction}" xmlns:th="http://www.thymeleaf.org">
<th:block th:insert="~{fragments/common :: head(title=#{account.title}, header=#{account.header})}"></th:block>
<!DOCTYPE html>
<html th:lang="${#locale.language}" th:lang-direction="#{language.direction}" xmlns:th="http://www.thymeleaf.org">
<head>
<th:block th:insert="~{fragments/common :: head(title=#{account.title})}"></th:block>
</head>
<body>
<th:block th:insert="~{fragments/common :: game}"></th:block>
<div id="page-container">
<div id="content-wrap">
<div th:insert="~{fragments/navbar.html :: navbar}"></div>
<br> <br>
<th:block th:insert="~{fragments/navbar.html :: navbar}"></th:block>
<br /><br />
<div class="container">
<div class="row justify-content-center">
<div class="col-md-9">
<!-- User Settings Title -->
<h2 class="text-center" th:text="#{account.accountSettings}">User Settings</h2>
<hr>
<div th:if="${param.messageType != null and param.messageType.size() > 0 and param.messageType[0] == 'notAuthenticated'}" class="alert alert-danger">
<hr />
<th:block th:if="${param.messageType != null and param.messageType.size() > 0}">
<div th:if="${param.messageType[0] == 'notAuthenticated'}" class="alert alert-danger">
<span th:text="#{notAuthenticatedMessage}">Default message if not found</span>
</div>
<div th:if="${param.messageType != null and param.messageType.size() > 0 and param.messageType[0] == 'userNotFound'}" class="alert alert-danger">
<div th:if="${param.messageType[0] == 'userNotFound'}" class="alert alert-danger">
<span th:text="#{userNotFoundMessage}">Default message if not found</span>
</div>
<div th:if="${param.messageType != null and param.messageType.size() > 0 and param.messageType[0] == 'incorrectPassword'}" class="alert alert-danger">
<div th:if="${param.messageType[0] == 'incorrectPassword'}" class="alert alert-danger">
<span th:text="#{incorrectPasswordMessage}">Default message if not found</span>
</div>
<div th:if="${param.messageType != null and param.messageType.size() > 0 and param.messageType[0] == 'usernameExists'}" class="alert alert-danger">
<div th:if="${param.messageType[0] == 'usernameExists'}" class="alert alert-danger">
<span th:text="#{usernameExistsMessage}">Default message if not found</span>
</div>
</th:block>
<!-- At the top of the user settings -->
<h3 class="text-center"><span th:text="#{welcome} + ' ' + ${username}">User</span>!</h3>
<div th:if="${error}" class="alert alert-danger" role="alert">
<th:block th:if="${error}">
<div class="alert alert-danger" role="alert">
<span th:text="${error}">Error Message</span>
</div>
</th:block>
<!-- Change Username Form -->
<h4></h4>
<form action="api/v1/user/change-username" method="post">
@ -56,7 +54,7 @@
</div>
</form>
<hr> <!-- Separator Line -->
<hr /> <!-- Separator Line -->
<!-- Change Password Form -->
<h4 th:text="#{account.changePassword}">Change Password?</h4>
@ -78,12 +76,10 @@
</div>
</form>
<hr>
<hr />
<div class="card">
<div class="card-header" th:text="#{account.yourApiKey}">
</div>
<div class="card-header" th:text="#{account.yourApiKey}"></div>
<div class="card-body">
<div class="input-group mb-3">
<input type="password" class="form-control" id="apiKey" th:placeholder="#{account.yourApiKey}" readonly>
@ -97,8 +93,6 @@
<button class="btn btn-outline-secondary" id="refreshBtn" type="button" onclick="refreshApiKey()">
<img class="blackwhite-icon" id="eyeIcon" src="images/arrow-clockwise.svg" alt="Refresh API-Key" style="height:20px;">
</button>
</div>
</div>
</div>
@ -111,7 +105,6 @@
document.execCommand("copy");
}
function showApiKey() {
const apiKeyElement = document.getElementById("apiKey");
const copyBtn = document.getElementById("copyBtn");
@ -189,8 +182,7 @@
});
</script>
<hr> <!-- Separator Line -->
<hr /> <!-- Separator Line -->
<h4 th:text="#{account.syncTitle}">Sync browser settings with Account</h4>
<div class="container mt-4">
@ -214,23 +206,6 @@
</div>
</div>
<style>
.container {
width: 100%;
max-width: 800px;
margin: 0 auto;
}
.buttons-container {
margin-top: 20px;
text-align: center;
}
</style>
<script th:inline="javascript">
document.addEventListener("DOMContentLoaded", function() {
const settingsTableBody = document.querySelector("#settingsTable tbody");
@ -271,7 +246,6 @@
location.reload(); // Refresh the page after sync
});
document.getElementById('syncToAccount').addEventListener('click', function() {
let form = document.createElement("form");
form.method = "POST";
@ -293,18 +267,7 @@
});
});
</script>
<div class="mb-3 mt-4">
<a href="logout">
<button type="button" class="btn btn-danger" th:text="#{account.signOut}">Sign Out</button>
@ -312,15 +275,12 @@
<a th:if="${role == 'ROLE_ADMIN'}" href="addUsers" target="_blank">
<button type="button" class="btn btn-info" th:text="#{account.adminSettings}">Admin Settings</button>
</a>
</div>
</div>
</div>
</div>
</div>
<div th:insert="~{fragments/footer.html :: footer}"></div>
</div>
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
</div>
</body>
</html>

View File

@ -1,24 +1,21 @@
<!DOCTYPE html>
<html th:lang="${#locale.toString()}" th:lang-direction="#{language.direction}" xmlns:th="http://www.thymeleaf.org">
<html th:lang="${#locale.language}" th:lang-direction="#{language.direction}" xmlns:th="http://www.thymeleaf.org">
<head>
<th:block th:insert="~{fragments/common :: head(title=#{adminUserSettings.title}, header=#{adminUserSettings.header})}"></th:block>
</head>
<body>
<th:block th:insert="~{fragments/common :: game}"></th:block>
<div id="page-container">
<div id="content-wrap">
<div th:insert="~{fragments/navbar.html :: navbar}"></div>
<br> <br>
<th:block th:insert="~{fragments/navbar.html :: navbar}"></th:block>
<br /><br />
<div class="container">
<div class="row justify-content-center">
<div class="col-md-8">
<!-- User Settings Title -->
<h2 class="text-center" th:text="#{adminUserSettings.header}">Admin User Control Settings</h2>
<table class="table">
<thead>
<tr>
@ -40,8 +37,6 @@
</tbody>
</table>
<h2 th:text="#{adminUserSettings.addUser}">Add New User</h2>
<div th:if="${param.messageType != null and param.messageType.size() > 0 and param.messageType[0] == 'usernameExists'}" class="alert alert-danger">
<span th:text="#{usernameExistsMessage}">Default message if not found</span>
@ -76,9 +71,8 @@
</div>
</div>
</div>
</div>
<div th:insert="~{fragments/footer.html :: footer}"></div>
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
</div>
</body>
</html>

View File

@ -1,15 +1,15 @@
<!DOCTYPE html>
<html th:lang="${#locale.toString()}" th:lang-direction="#{language.direction}" xmlns:th="http://www.thymeleaf.org">
<html th:lang="${#locale.language}" th:lang-direction="#{language.direction}" xmlns:th="http://www.thymeleaf.org">
<head>
<th:block th:insert="~{fragments/common :: head(title=#{autoSplitPDF.title}, header=#{autoSplitPDF.header})}"></th:block>
</head>
<body>
<th:block th:insert="~{fragments/common :: game}"></th:block>
<div id="page-container">
<div id="content-wrap">
<div th:insert="~{fragments/navbar.html :: navbar}"></div>
<br> <br>
<th:block th:insert="~{fragments/navbar.html :: navbar}"></th:block>
<br /><br />
<div class="container">
<div class="row justify-content-center">
<div class="col-md-6">
@ -36,9 +36,8 @@
</div>
</div>
</div>
</div>
<div th:insert="~{fragments/footer.html :: footer}"></div>
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
</div>
</body>
</html>

View File

@ -1,39 +1,39 @@
<!doctype html>
<html th:lang="${#locale.toString()}" th:lang-direction="#{language.direction}" xmlns:th="http://www.thymeleaf.org">
<!DOCTYPE html>
<html th:lang="${#locale.language}" th:lang-direction="#{language.direction}" xmlns:th="http://www.thymeleaf.org">
<head>
<th:block th:insert="~{fragments/common :: head(title=#{changeCreds.title}, header=#{changeCreds.header})}"></th:block>
</head>
<body>
<th:block th:insert="~{fragments/common :: game}"></th:block>
<div id="page-container">
<div id="content-wrap">
<div th:insert="~{fragments/navbar.html :: navbar}"></div>
<br> <br>
<th:block th:insert="~{fragments/navbar.html :: navbar}"></th:block>
<br /><br />
<div class="container">
<div class="row justify-content-center">
<div class="col-md-9">
<!-- User Settings Title -->
<h2 class="text-center" th:text="#{changeCreds.header}">User Settings</h2>
<hr>
<div th:if="${param.messageType != null and param.messageType.size() > 0 and param.messageType[0] == 'notAuthenticated'}" class="alert alert-danger">
<hr />
<th:block th:if="${param.messageType != null and param.messageType.size() > 0}">
<div th:if="${param.messageType[0] == 'notAuthenticated'}" class="alert alert-danger">
<span th:text="#{notAuthenticatedMessage}">Default message if not found</span>
</div>
<div th:if="${param.messageType != null and param.messageType.size() > 0 and param.messageType[0] == 'userNotFound'}" class="alert alert-danger">
<div th:if="${param.messageType[0] == 'userNotFound'}" class="alert alert-danger">
<span th:text="#{userNotFoundMessage}">Default message if not found</span>
</div>
<div th:if="${param.messageType != null and param.messageType.size() > 0 and param.messageType[0] == 'incorrectPassword'}" class="alert alert-danger">
<div th:if="${param.messageType[0] == 'incorrectPassword'}" class="alert alert-danger">
<span th:text="#{incorrectPasswordMessage}">Default message if not found</span>
</div>
<div th:if="${param.messageType != null and param.messageType.size() > 0 and param.messageType[0] == 'usernameExists'}" class="alert alert-danger">
<div th:if="${param.messageType[0] == 'usernameExists'}" class="alert alert-danger">
<span th:text="#{usernameExistsMessage}">Default message if not found</span>
</div>
</th:block>
<!-- At the top of the user settings -->
<h3 class="text-center"><span th:text="#{welcome} + ' ' + ${username}">User</span>!</h3>
<!-- Change Username Form -->
<h4></h4>
<h4 th:text="#{changeCreds.changeUserAndPassword}">Change Username and password</h4>
@ -58,7 +58,6 @@
<button type="submit" class="btn btn-primary" th:text="#{changeCreds.submit}">Change credentials!</button>
</div>
</form>
<script>
document.addEventListener("DOMContentLoaded", function() {
const form = document.querySelector('form[action="api/v1/user/change-username-and-password"]');
@ -75,16 +74,10 @@
});
</script>
</div>
</div>
</div>
</div>
<div th:insert="~{fragments/footer.html :: footer}"></div>
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
</div>
</body>
</html>

View File

@ -1,13 +1,15 @@
<!DOCTYPE html>
<html th:lang="${#locale.toString()}" th:lang-direction="#{language.direction}" xmlns:th="http://www.thymeleaf.org">
<html th:lang="${#locale.language}" th:lang-direction="#{language.direction}" xmlns:th="http://www.thymeleaf.org">
<head>
<th:block th:insert="~{fragments/common :: head(title=#{BookToPDF.title}, header=#{BookToPDF.header})}"></th:block>
</head>
<body>
<th:block th:insert="~{fragments/common :: game}"></th:block>
<div id="page-container">
<div id="content-wrap">
<div th:insert="~{fragments/navbar.html :: navbar}"></div>
<br> <br>
<th:block th:insert="~{fragments/navbar.html :: navbar}"></th:block>
<br /><br />
<div class="container">
<div class="row justify-content-center">
<div class="col-md-6">
@ -23,7 +25,7 @@
</div>
</div>
</div>
<div th:insert="~{fragments/footer.html :: footer}"></div>
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
</div>
</body>
</html>

View File

@ -1,37 +1,34 @@
<!DOCTYPE html>
<html th:lang="${#locale.toString()}" th:lang-direction="#{language.direction}" xmlns:th="http://www.thymeleaf.org">
<html th:lang="${#locale.language}" th:lang-direction="#{language.direction}" xmlns:th="http://www.thymeleaf.org">
<head>
<th:block th:insert="~{fragments/common :: head(title=#{fileToPDF.title}, header=#{fileToPDF.header})}"></th:block>
</head>
<body>
<th:block th:insert="~{fragments/common :: game}"></th:block>
<div id="page-container">
<div id="content-wrap">
<div th:insert="~{fragments/navbar.html :: navbar}"></div>
<br> <br>
<th:block th:insert="~{fragments/navbar.html :: navbar}"></th:block>
<br /><br />
<div class="container">
<div class="row justify-content-center">
<div class="col-md-6">
<h2 th:text="#{fileToPDF.header}"></h2>
<p th:text="#{processTimeWarning}">
<p th:text="#{processTimeWarning}"></p>
<form method="post" enctype="multipart/form-data" th:action="@{api/v1/convert/file/pdf}">
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false)}"></div>
<br>
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{fileToPDF.submit}"></button>
</form>
<p class="mt-3" th:text="#{fileToPDF.credit}"></p>
<p class="mt-3" th:text="#{fileToPDF.supportedFileTypes}"></p>
<p th:utext="#{fileToPDF.fileTypesList}"></p>
<a href="https://help.libreoffice.org/latest/en-US/text/shared/guide/supported_formats.html">https://help.libreoffice.org/latest/en-US/text/shared/guide/supported_formats.html</a>
</div>
</div>
</div>
</div>
<div th:insert="~{fragments/footer.html :: footer}"></div>
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
</div>
</body>
</html>

View File

@ -1,28 +1,27 @@
<!DOCTYPE html>
<html th:lang="${#locale.toString()}" th:lang-direction="#{language.direction}" xmlns:th="http://www.thymeleaf.org">
<html th:lang="${#locale.language}" th:lang-direction="#{language.direction}" xmlns:th="http://www.thymeleaf.org">
<head>
<th:block th:insert="~{fragments/common :: head(title=#{HTMLToPDF.title}, header=#{HTMLToPDF.header})}"></th:block>
</head>
<body>
<th:block th:insert="~{fragments/common :: game}"></th:block>
<div id="page-container">
<div id="content-wrap">
<div th:insert="~{fragments/navbar.html :: navbar}"></div>
<br> <br>
<th:block th:insert="~{fragments/navbar.html :: navbar}"></th:block>
<br /><br />
<div class="container">
<div class="row justify-content-center">
<div class="mb-3">
<h2 th:text="#{HTMLToPDF.header}"></h2>
<form method="post" enctype="multipart/form-data" th:action="@{api/v1/convert/html/pdf}">
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false, accept='text/html,application/zip' )}"></div>
<div class="mb-3">
<label for="zoom" th:text="#{HTMLToPDF.zoom}" class="form-label"></label>
<input type="number" step="0.1" class="form-control" id="zoom" name="zoom" value="1" />
</div>
<br>
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{HTMLToPDF.submit}"></button>
</form>
<p class="mt-3" th:text="#{HTMLToPDF.help}"></p>
<p class="mt-3" th:text="#{HTMLToPDF.credit}"></p>
@ -30,7 +29,7 @@
</div>
</div>
</div>
<div th:insert="~{fragments/footer.html :: footer}"></div>
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
</div>
</body>
</html>

View File

@ -1,23 +1,21 @@
<!DOCTYPE html>
<html th:lang="${#locale.toString()}" th:lang-direction="#{language.direction}" xmlns:th="http://www.thymeleaf.org">
<html th:lang="${#locale.language}" th:lang-direction="#{language.direction}" xmlns:th="http://www.thymeleaf.org">
<head>
<th:block th:insert="~{fragments/common :: head(title=#{imageToPDF.title}, header=#{imageToPDF.header})}"></th:block>
</head>
<body>
<th:block th:insert="~{fragments/common :: game}"></th:block>
<div id="page-container">
<div id="content-wrap">
<div th:insert="~{fragments/navbar.html :: navbar}"></div>
<br> <br>
<th:block th:insert="~{fragments/navbar.html :: navbar}"></th:block>
<br /><br />
<div class="container">
<div class="row justify-content-center">
<div class="col-md-6">
<h2 th:text="#{imageToPDF.header}"></h2>
<form method="post" enctype="multipart/form-data" th:action="@{api/v1/convert/img/pdf}">
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false, accept='image/*', inputText=#{imgPrompt})}"></div>
<div class="mb-3">
<label for="fitOption" th:text="#{imageToPDF.selectLabel}">Fit Options</label>
<select class="form-control" id="fitOption" name="fitOption">
@ -48,8 +46,7 @@
<option value="convert" th:text=#{imageToPDF.selectText.5} selected></option>
</select>
</div>
<br> <br>
<br /><br />
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{imageToPDF.submit}"></button>
<script>
$('#fileInput-input').on('change', function() {
@ -73,16 +70,13 @@
override.value = "multi";
}
});
</script>
</form>
</div>
</div>
</div>
</div>
<div th:insert="~{fragments/footer.html :: footer}"></div>
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
</div>
</body>
</html>

View File

@ -1,13 +1,15 @@
<!DOCTYPE html>
<html th:lang="${#locale.toString()}" th:lang-direction="#{language.direction}" xmlns:th="http://www.thymeleaf.org">
<html th:lang="${#locale.language}" th:lang-direction="#{language.direction}" xmlns:th="http://www.thymeleaf.org">
<head>
<th:block th:insert="~{fragments/common :: head(title=#{MarkdownToPDF.title}, header=#{MarkdownToPDF.header})}"></th:block>
</head>
<body>
<th:block th:insert="~{fragments/common :: game}"></th:block>
<div id="page-container">
<div id="content-wrap">
<div th:insert="~{fragments/navbar.html :: navbar}"></div>
<br> <br>
<th:block th:insert="~{fragments/navbar.html :: navbar}"></th:block>
<br /><br />
<div class="container">
<div class="row justify-content-center">
<div class="col-md-6">
@ -16,7 +18,6 @@
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false, accept='text/markdown')}"></div>
<br>
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{MarkdownToPDF.submit}"></button>
</form>
<p class="mt-3" th:text="#{MarkdownToPDF.help}"></p>
<p class="mt-3" th:text="#{MarkdownToPDF.credit}"></p>
@ -24,7 +25,7 @@
</div>
</div>
</div>
<div th:insert="~{fragments/footer.html :: footer}"></div>
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
</div>
</body>
</html>

View File

@ -1,29 +1,24 @@
<!DOCTYPE html>
<html th:lang="${#locale.toString()}"
th:lang-direction="#{language.direction}"
xmlns:th="http://www.thymeleaf.org">
<th:block
th:insert="~{fragments/common :: head(title=#{PDFToBook.title}, header=#{PDFToBook.header})}"></th:block>
<html th:lang="${#locale.language}" th:lang-direction="#{language.direction}" xmlns:th="http://www.thymeleaf.org">
<head>
<th:block th:insert="~{fragments/common :: head(title=#{PDFToBook.title}, header=#{PDFToBook.header})}"></th:block>
</head>
<body>
<th:block th:insert="~{fragments/common :: game}"></th:block>
<div id="page-container">
<div id="content-wrap">
<div th:insert="~{fragments/navbar.html :: navbar}"></div>
<br> <br>
<th:block th:insert="~{fragments/navbar.html :: navbar}"></th:block>
<br /><br />
<div class="container">
<div class="row justify-content-center">
<div class="col-md-6">
<h2 th:text="#{PDFToBook.header}"></h2>
<form method="post" enctype="multipart/form-data"
th:action="@{api/v1/convert/pdf/book}">
<div
th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false, accept='application/pdf')}"></div>
<form method="post" enctype="multipart/form-data" th:action="@{api/v1/convert/pdf/book}">
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false, accept='application/pdf')}"></div>
<div class="mb-3">
<label th:text="#{PDFToBook.selectText.1}"></label> <select
class="form-control" name="outputFormat">
<label th:text="#{PDFToBook.selectText.1}"></label>
<select class="form-control" name="outputFormat">
<option value="epub">EPUB</option>
<option value="mobi">MOBI</option>
<option value="azw3">AZW3</option>
@ -36,12 +31,9 @@
<option value="pdb">PDB</option>
<option value="lrf">LRF</option>
</select>
</div>
<br>
<button type="submit" id="submitBtn" class="btn btn-primary"
th:text="#{PDFToBook.submit}"></button>
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{PDFToBook.submit}"></button>
</form>
<p class="mt-3" th:text="#{PDFToBook.help}"></p>
<p class="mt-3" th:text="#{PDFToBook.credit}"></p>
@ -49,7 +41,7 @@
</div>
</div>
</div>
<div th:insert="~{fragments/footer.html :: footer}"></div>
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
</div>
</body>
</html>

View File

@ -1,14 +1,14 @@
<!DOCTYPE html>
<html th:lang="${#locale.toString()}" th:lang-direction="#{language.direction}" xmlns:th="http://www.thymeleaf.org">
<html th:lang="${#locale.language}" th:lang-direction="#{language.direction}" xmlns:th="http://www.thymeleaf.org">
<head>
<th:block th:insert="~{fragments/common :: head(title=#{PDFToCSV.title}, header=#{PDFToCSV.header})}"></th:block>
</head>
<body>
<div id="page-container">
<div id="content-wrap">
<div th:insert="~{fragments/navbar.html :: navbar}"></div>
</br></br>
<th:block th:insert="~{fragments/navbar.html :: navbar}"></th:block>
<br /><br />
<div class="container">
<div class="row justify-content-center">
<div class="col-md-6">
@ -22,19 +22,15 @@
<div style="position: relative; display: inline-block;">
<div>
<div style="display:none ;margin: 3px;position: absolute;top: 0;width: 120px;justify-content:space-between;z-index: 10" id="pagination-button-container">
<button id='previous-page-btn' style='opacity: 80% ; width: 50px; height: 30px; display: flex;align-items: center;justify-content: center; background: grey; color: #ffffff; ;border: none;outline: none; border-radius: 4px;'> < </button>
<button id='next-page-btn' style='opacity: 80% ; width: 50px; height: 30px; display: flex;align-items: center;justify-content: center; background: grey; color: #ffffff; ;border: none;outline: none; border-radius: 4px;'> > </button>
</div>
<canvas id="crop-pdf-canvas" style="position: absolute; top: 0; left: 0; z-index: 1;"></canvas>
</div>
<canvas id="overlayCanvas" style="position: absolute; top: 0; left: 0; z-index: 2;"></canvas>
</div>
<script>
let pdfCanvas = document.getElementById('crop-pdf-canvas');
let overlayCanvas = document.getElementById('overlayCanvas');
// let paginationBtnContainer = ;
@ -61,7 +57,6 @@
let rectHeight = 0;
btn1Object.addEventListener('click',function (e){
if (currentPage !== 1) {
currentPage = currentPage - 1;
pageId.value = currentPage;
@ -83,9 +78,7 @@
});
btn2Object.addEventListener('click',function (e){
if (currentPage !== totalPages){
currentPage=currentPage+1;
pageId.value = currentPage;
@ -106,20 +99,18 @@
});
fileInput.addEventListener('change', function(e) {
file = e.target.files[0];
if (file.type === 'application/pdf') {
let reader = new FileReader();
reader.onload = function(ev) {
let typedArray = new Uint8Array(reader.result);
pdfjsLib.GlobalWorkerOptions.workerSrc = 'pdfjs/pdf.worker.js'
pdfjsLib.GlobalWorkerOptions.workerSrc = 'pdfjs/pdf.worker.js';
pdfjsLib.getDocument(typedArray).promise.then(function(pdf) {
pdfDoc = pdf;
totalPages = pdf.numPages;
renderPage(currentPage);
});
pageId.value = currentPage;
};
reader.readAsArrayBuffer(file);
document.getElementById("pagination-button-container").style.display="flex";
@ -127,7 +118,6 @@
}
});
function renderPage(pageNumber) {
pdfDoc.getPage(pageNumber).then(function(page) {
let viewport = page.getViewport({ scale: 1.0 });
@ -142,18 +132,12 @@
pdfCanvas.classList.add("shadow-canvas");
});
}
</script>
</div>
</div>
</div>
</div>
<div th:insert="~{fragments/footer.html :: footer}"></div>
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
</div>
</body>
</html>

View File

@ -1,13 +1,15 @@
<!DOCTYPE html>
<html th:lang="${#locale.toString()}" th:lang-direction="#{language.direction}" xmlns:th="http://www.thymeleaf.org">
<html th:lang="${#locale.language}" th:lang-direction="#{language.direction}" xmlns:th="http://www.thymeleaf.org">
<head>
<th:block th:insert="~{fragments/common :: head(title=#{PDFToHTML.title}, header=#{PDFToHTML.header})}"></th:block>
</head>
<body>
<th:block th:insert="~{fragments/common :: game}"></th:block>
<div id="page-container">
<div id="content-wrap">
<div th:insert="~{fragments/navbar.html :: navbar}"></div>
<br> <br>
<th:block th:insert="~{fragments/navbar.html :: navbar}"></th:block>
<br /><br />
<div class="container">
<div class="row justify-content-center">
<div class="col-md-6">
@ -16,14 +18,13 @@
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false, accept='application/pdf')}"></div>
<br>
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{PDFToHTML.submit}"></button>
</form>
<p class="mt-3" th:text="#{PDFToHTML.credit}"></p>
</div>
</div>
</div>
</div>
<div th:insert="~{fragments/footer.html :: footer}"></div>
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
</div>
</body>
</html>

View File

@ -1,16 +1,15 @@
<!DOCTYPE html>
<html th:lang="${#locale.toString()}" th:lang-direction="#{language.direction}" xmlns:th="http://www.thymeleaf.org">
<html th:lang="${#locale.language}" th:lang-direction="#{language.direction}" xmlns:th="http://www.thymeleaf.org">
<head>
<th:block th:insert="~{fragments/common :: head(title=#{pdfToImage.title}, header=#{pdfToImage.header})}"></th:block>
</head>
<body>
<th:block th:insert="~{fragments/common :: game}"></th:block>
<div id="page-container">
<div id="content-wrap">
<div th:insert="~{fragments/navbar.html :: navbar}"></div>
<br> <br>
<th:block th:insert="~{fragments/navbar.html :: navbar}"></th:block>
<br /><br />
<div class="container">
<div class="row justify-content-center">
<div class="col-md-6">
@ -49,13 +48,11 @@
</div>
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{pdfToImage.submit}"></button>
</form>
</div>
</div>
</div>
</div>
<div th:insert="~{fragments/footer.html :: footer}"></div>
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
</div>
</body>
</html>

View File

@ -1,15 +1,15 @@
<!DOCTYPE html>
<html th:lang="${#locale.toString()}" th:lang-direction="#{language.direction}" xmlns:th="http://www.thymeleaf.org">
<html th:lang="${#locale.language}" th:lang-direction="#{language.direction}" xmlns:th="http://www.thymeleaf.org">
<head>
<th:block th:insert="~{fragments/common :: head(title=#{pdfToPDFA.title}, header=#{pdfToPDFA.header})}"></th:block>
</head>
<body>
<th:block th:insert="~{fragments/common :: game}"></th:block>
<div id="page-container">
<div id="content-wrap">
<div th:insert="~{fragments/navbar.html :: navbar}"></div>
<br> <br>
<th:block th:insert="~{fragments/navbar.html :: navbar}"></th:block>
<br /><br />
<div class="container">
<div class="row justify-content-center">
<div class="col-md-6">
@ -24,9 +24,8 @@
</div>
</div>
</div>
</div>
<div th:insert="~{fragments/footer.html :: footer}"></div>
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
</div>
</body>
</html>

View File

@ -1,20 +1,21 @@
<!DOCTYPE html>
<html th:lang="${#locale.toString()}" th:lang-direction="#{language.direction}" xmlns:th="http://www.thymeleaf.org">
<html th:lang="${#locale.language}" th:lang-direction="#{language.direction}" xmlns:th="http://www.thymeleaf.org">
<head>
<th:block th:insert="~{fragments/common :: head(title=#{PDFToPresentation.title}, header=#{PDFToPresentation.header})}"></th:block>
</head>
<body>
<th:block th:insert="~{fragments/common :: game}"></th:block>
<div id="page-container">
<div id="content-wrap">
<div th:insert="~{fragments/navbar.html :: navbar}"></div>
<br> <br>
<th:block th:insert="~{fragments/navbar.html :: navbar}"></th:block>
<br /><br />
<div class="container">
<div class="row justify-content-center">
<div class="col-md-6">
<h2 th:text="#{PDFToPresentation.header}"></h2>
<form method="post" enctype="multipart/form-data" th:action="@{api/v1/convert/pdf/presentation}">
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false, accept='application/pdf')}"></div>
<div class="mb-3">
<label th:text="#{PDFToPresentation.selectText.1}"></label>
<select class="form-control" name="outputFormat">
@ -25,14 +26,13 @@
</div>
<br>
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{PDFToPresentation.submit}"></button>
</form>
<p class="mt-3" th:text="#{PDFToPresentation.credit}"></p>
</div>
</div>
</div>
</div>
<div th:insert="~{fragments/footer.html :: footer}"></div>
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
</div>
</body>
</html>

View File

@ -1,20 +1,21 @@
<!DOCTYPE html>
<html th:lang="${#locale.toString()}" th:lang-direction="#{language.direction}" xmlns:th="http://www.thymeleaf.org">
<html th:lang="${#locale.language}" th:lang-direction="#{language.direction}" xmlns:th="http://www.thymeleaf.org">
<head>
<th:block th:insert="~{fragments/common :: head(title=#{PDFToText.title}, header=#{PDFToText.header})}"></th:block>
</head>
<body>
<th:block th:insert="~{fragments/common :: game}"></th:block>
<div id="page-container">
<div id="content-wrap">
<div th:insert="~{fragments/navbar.html :: navbar}"></div>
<br> <br>
<th:block th:insert="~{fragments/navbar.html :: navbar}"></th:block>
<br /><br />
<div class="container">
<div class="row justify-content-center">
<div class="col-md-6">
<h2 th:text="#{PDFToText.header}"></h2>
<form method="post" enctype="multipart/form-data" th:action="@{api/v1/convert/pdf/text}">
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false, accept='application/pdf')}"></div>
<div class="mb-3">
<label th:text="#{PDFToText.selectText.1}"></label>
<select class="form-control" name="outputFormat">
@ -24,12 +25,14 @@
</div>
<br>
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{PDFToText.submit}"></button>
</form>
<p class="mt-3" th:text="#{PDFToText.credit}"></p>
</div>
</div>
</div>
</div>
<div th:insert="~{fragments/footer.html :: footer}"></div>
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
</div>
</body>
</html>

View File

@ -1,20 +1,21 @@
<!DOCTYPE html>
<html th:lang="${#locale.toString()}" th:lang-direction="#{language.direction}" xmlns:th="http://www.thymeleaf.org">
<html th:lang="${#locale.language}" th:lang-direction="#{language.direction}" xmlns:th="http://www.thymeleaf.org">
<head>
<th:block th:insert="~{fragments/common :: head(title=#{PDFToWord.title}, header=#{PDFToWord.header})}"></th:block>
</head>
<body>
<th:block th:insert="~{fragments/common :: game}"></th:block>
<div id="page-container">
<div id="content-wrap">
<div th:insert="~{fragments/navbar.html :: navbar}"></div>
<br> <br>
<th:block th:insert="~{fragments/navbar.html :: navbar}"></th:block>
<br /><br />
<div class="container">
<div class="row justify-content-center">
<div class="col-md-6">
<h2 th:text="#{PDFToWord.header}"></h2>
<form method="post" enctype="multipart/form-data" th:action="@{api/v1/convert/pdf/word}">
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false, accept='application/pdf')}"></div>
<div class="mb-3">
<label th:text="#{PDFToWord.selectText.1}"></label>
<select class="form-control" name="outputFormat">
@ -25,14 +26,13 @@
</div>
<br>
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{PDFToWord.submit}"></button>
</form>
<p class="mt-3" th:text="#{PDFToWord.credit}"></p>
</div>
</div>
</div>
</div>
<div th:insert="~{fragments/footer.html :: footer}"></div>
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
</div>
</body>
</html>

View File

@ -1,13 +1,15 @@
<!DOCTYPE html>
<html th:lang="${#locale.toString()}" th:lang-direction="#{language.direction}" xmlns:th="http://www.thymeleaf.org">
<html th:lang="${#locale.language}" th:lang-direction="#{language.direction}" xmlns:th="http://www.thymeleaf.org">
<head>
<th:block th:insert="~{fragments/common :: head(title=#{PDFToXML.title}, header=#{PDFToXML.header})}"></th:block>
</head>
<body>
<th:block th:insert="~{fragments/common :: game}"></th:block>
<div id="page-container">
<div id="content-wrap">
<div th:insert="~{fragments/navbar.html :: navbar}"></div>
<br> <br>
<th:block th:insert="~{fragments/navbar.html :: navbar}"></th:block>
<br /><br />
<div class="container">
<div class="row justify-content-center">
<div class="col-md-6">
@ -16,14 +18,13 @@
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false, accept='application/pdf')}"></div>
<br>
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{PDFToXML.submit}"></button>
</form>
<p class="mt-3" th:text="#{PDFToXML.credit}"></p>
</div>
</div>
</div>
</div>
<div th:insert="~{fragments/footer.html :: footer}"></div>
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
</div>
</body>
</html>

View File

@ -1,13 +1,15 @@
<!DOCTYPE html>
<html th:lang="${#locale.toString()}" th:lang-direction="#{language.direction}" xmlns:th="http://www.thymeleaf.org">
<html th:lang="${#locale.language}" th:lang-direction="#{language.direction}" xmlns:th="http://www.thymeleaf.org">
<head>
<th:block th:insert="~{fragments/common :: head(title=#{URLToPDF.title}, header=#{URLToPDF.header})}"></th:block>
</head>
<body>
<th:block th:insert="~{fragments/common :: game}"></th:block>
<div id="page-container">
<div id="content-wrap">
<div th:insert="~{fragments/navbar.html :: navbar}"></div>
<br> <br>
<th:block th:insert="~{fragments/navbar.html :: navbar}"></th:block>
<br /><br />
<div class="container">
<div class="row justify-content-center">
<div class="col-md-6">
@ -16,14 +18,13 @@
<input type="text" class="form-control" id="urlInput" name="urlInput">
<br>
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{URLToPDF.submit}"></button>
</form>
<p class="mt-3" th:text="#{URLToPDF.credit}"></p>
</div>
</div>
</div>
</div>
<div th:insert="~{fragments/footer.html :: footer}"></div>
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
</div>
</body>
</html>

View File

@ -1,14 +1,14 @@
<!DOCTYPE html>
<html th:lang="${#locale.toString()}" th:lang-direction="#{language.direction}" xmlns:th="http://www.thymeleaf.org">
<html th:lang="${#locale.language}" th:lang-direction="#{language.direction}" xmlns:th="http://www.thymeleaf.org">
<head>
<th:block th:insert="~{fragments/common :: head(title=#{crop.title}, header=#{crop.header})}"></th:block>
</head>
<body>
<div id="page-container">
<div id="content-wrap">
<div th:insert="~{fragments/navbar.html :: navbar}"></div>
<br> <br>
<th:block th:insert="~{fragments/navbar.html :: navbar}"></th:block>
<br /><br />
<div class="container">
<div class="row justify-content-center">
<div class="col-md-6">
@ -25,9 +25,7 @@
<canvas id="crop-pdf-canvas" style="position: absolute; top: 0; left: 0; z-index: 1;"></canvas>
<canvas id="overlayCanvas" style="position: absolute; top: 0; left: 0; z-index: 2;"></canvas>
</div>
<script>
let pdfCanvas = document.getElementById('crop-pdf-canvas');
let overlayCanvas = document.getElementById('overlayCanvas');
@ -115,7 +113,6 @@
overlayContext.clearRect(0, 0, overlayCanvas.width, overlayCanvas.height); // Clear the overlay
});
function renderPage(pageNumber) {
pdfDoc.getPage(pageNumber).then(function(page) {
let viewport = page.getViewport({ scale: 1.0 });
@ -130,18 +127,9 @@
pdfCanvas.classList.add("shadow-canvas");
});
}
</script>
</div>
</div>
</div>
</div>
<div th:insert="~{fragments/footer.html :: footer}"></div>
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
</div>
</body>
</html>

View File

@ -1,119 +1,30 @@
<!DOCTYPE html>
<html th:lang="${#locale.toString()}" th:lang-direction="#{language.direction}" xmlns:th="http://www.thymeleaf.org">
<html th:lang="${#locale.language}" th:lang-direction="#{language.direction}" xmlns:th="http://www.thymeleaf.org">
<head>
<title>Error! :(</title>
<th:block th:insert="~{fragments/common :: head(title='')}"></th:block>
<style>
h1 {
text-align: center;
margin-top: 10%;
}
p {
text-align: center;
margin-top: 2em;
}
.button:hover {
background-color: #005b7f;
}
.features-container {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(21rem, 3fr));
gap: 25px 30px;
}
.feature-card {
border: 1px solid rgba(0, 0, 0, .125);
border-radius: 0.25rem;
padding: 1.25rem;
display: flex;
flex-direction: column;
align-items: flex-start;
}
.feature-card .card-text {
flex: 1;
}
#support-section {
background-color: #f9f9f9;
padding: 4rem;
margin-top: 1rem;
text-align: center;
}
#support-section h1 {
margin-top: 0;
}
#support-section p {
margin-top: 0;
}
#button-group {
display: flex;
justify-content: center;
flex-wrap: wrap;
}
#github-button, #discord-button {
display: inline-block;
padding: 1rem 2rem;
margin: 1rem;
background-color: #008CBA;
color: #fff;
font-size: 1.2rem;
text-align: center;
text-decoration: none;
border-radius: 3rem;
transition: all 0.3s ease-in-out;
}
#github-button:hover, #discord-button:hover, #home-button:hover {
background-color: #005b7f;
}
#home-button {
display: block;
width: 200px;
height: 50px;
margin: 2em auto;
background-color: #008CBA;
color: white;
text-align: center;
line-height: 50px;
text-decoration: none;
font-weight: bold;
border-radius: 25px;
transition: all 0.3s ease-in-out;
}
</style>
<th:block th:insert="~{fragments/common :: head(title='404 - Page Not Found | Oops, we tripped in the code!')}"></th:block>
</head>
<body>
<div id="page-container">
<div id="content-wrap">
<div th:insert="~{fragments/navbar.html :: navbar}"></div>
<th:block th:insert="~{fragments/navbar.html :: navbar}"></th:block>
<div th:insert="~{fragments/errorBanner.html :: errorBanner}"></div>
<div class="container">
<div id="support-section">
<h1 class="display-2">Oops!</h1>
<p class="lead" th:if="${param.status == '404'}">We can't seem to find the page you're looking for.</p>
<p class="lead" th:unless="${param.status == '404'}">Something went wrong</p>
<p class="lead" th:if="${param.status == '404'}">
We can't seem to find the page you're looking for.
</p>
<p class="lead" th:unless="${param.status == '404'}">
Something went wrong
</p>
<br>
<h2>Need help / Found a issue?</h2>
<p>If you're still having trouble, don't hesitate to reach out to us for help. You can submit a ticket on our GitHub page or contact us through Discord:</p>
<p>
If you're still having trouble, don't hesitate to reach out to us
for help. You can submit a ticket on our GitHub page or contact us
through Discord:
</p>
<div id="button-group">
<a href="https://github.com/Stirling-Tools/Stirling-PDF/issues" id="github-button" target="_blank">Submit a ticket on GitHub</a>
<a href="https://discord.gg/Cn8pWhQRxZ" id="discord-button" target="_blank">Join our Discord server</a>
@ -122,10 +33,7 @@ margin-top: 0;
</div>
</div>
</div>
<div th:insert="~{fragments/footer.html :: footer}"></div>
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
</div>
</body>
</html>

View File

@ -1,14 +1,14 @@
<!DOCTYPE html>
<html th:lang="${#locale.toString()}" th:lang-direction="#{language.direction}" xmlns:th="http://www.thymeleaf.org">
<html th:lang="${#locale.language}" th:lang-direction="#{language.direction}" xmlns:th="http://www.thymeleaf.org">
<head>
<th:block th:insert="~{fragments/common :: head(title=#{pageExtracter.title}, header=#{pageExtracter.header})}"></th:block>
</head>
<body>
<div id="page-container">
<div id="content-wrap">
<div th:insert="~{fragments/navbar.html :: navbar}"></div>
<br> <br>
<th:block th:insert="~{fragments/navbar.html :: navbar}"></th:block>
<br /><br />
<div class="container">
<div class="row justify-content-center">
<div class="col-md-6">
@ -27,7 +27,7 @@
</div>
</div>
</div>
<div th:insert="~{fragments/footer.html :: footer}"></div>
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
</div>
</body>
</html>

View File

@ -1,4 +1,4 @@
<div th:fragment="card" class="feature-card" th:id="${id}" th:if="${@endpointConfiguration.isEndpointEnabled(cardLink)}" data-bs-tags="${tags}">
<div th:fragment="card" class="feature-card" th:id="${id}" th:if="${@endpointConfiguration.isEndpointEnabled(cardLink)}" th:data-bs-tags="${tags}">
<a th:href="${cardLink}">
<div class="d-flex align-items-center"> <!-- Add a flex container to align the SVG and title -->
<img th:if="${svgPath}" id="card-icon" class="home-card-icon home-card-icon-colour" th:src="${svgPath}" alt="Icon" width="30" height="30">

View File

@ -1,5 +1,4 @@
<head th:fragment="head">
<th:block th:fragment="head">
<!-- Title -->
<title th:text="${@appName} + (${title} != null and ${title} != '' ? ' - ' + ${title} : '')"></title>
@ -46,15 +45,30 @@
<link rel="stylesheet" th:href="@{css/dark-mode.css}" id="dark-mode-styles">
<link rel="stylesheet" th:href="@{css/rainbow-mode.css}" id="rainbow-mode-styles" disabled="true">
<link rel="stylesheet" href="css/tab-container.css">
<link rel="stylesheet" href="css/navbar.css">
<link rel="stylesheet" th:href="@{/css/error.css}" th:if="${error}">
<link rel="stylesheet" href="css/home.css" th:if="${currentPage == 'home'}">
<link rel="stylesheet" href="css/account.css" th:if="${currentPage == 'account'}">
<link rel="stylesheet" href="css/licenses.css" th:if="${currentPage == 'licenses'}">
<link rel="stylesheet" href="css/multi-tool.css" th:if="${currentPage == 'multi-tool'}">
<link rel="stylesheet" href="css/rotate-pdf.css" th:if="${currentPage == 'rotate-pdf'}">
<link rel="stylesheet" href="css/stamp.css" th:if="${currentPage == 'stamp'}">
<link rel="stylesheet" href="css/fileSelect.css">
<link rel="stylesheet" href="css/footer.css">
<!-- Help Modal -->
<link rel="stylesheet" href="css/errorBanner.css">
<script src="js/tab-container.js"></script>
</head>
<script src="js/darkmode.js"></script>
</th:block>
<th:block th:fragment="game">
<dialog id="game-container-wrapper" class="game-container-wrapper" data-bs-modal>
<script>
console.log("loaded game")
console.log("loaded game");
$(document).ready(function() {
function loadGameScript(callback) {
console.log('loadGameScript called');
@ -64,7 +78,7 @@
document.body.appendChild(script);
}
let gameScriptLoaded = false;
const gameDialog = document.getElementById('game-container-wrapper')
const gameDialog = document.getElementById('game-container-wrapper');
$('#show-game-btn').on('click', function() {
console.log('Show game button clicked');
if (!gameScriptLoaded) {
@ -87,7 +101,7 @@
e.clientY < dialogDimensions.top ||
e.clientY > dialogDimensions.bottom
) {
gameDialog.close()
gameDialog.close();
}
})
})
@ -111,10 +125,7 @@
</script>
<script src="js/downloader.js"></script>
<div class="custom-file-chooser" th:attr="data-bs-unique-id=${name},
data-bs-element-id=${name+'-input'},
data-bs-files-selected=#{filesSelected},
data-bs-pdf-prompt=#{pdfPrompt}">
<div class="custom-file-chooser" th:attr="data-bs-unique-id=${name}, data-bs-element-id=${name+'-input'}, data-bs-files-selected=#{filesSelected}, data-bs-pdf-prompt=#{pdfPrompt}">
<div class="mb-3">
<input type="file" class="form-control" th:name="${name}" th:id="${name}+'-input'" th:accept="${accept}" multiple th:required="${notRequired} ? null : 'required'">
</div>
@ -129,9 +140,5 @@
</div>
</div>
<button type="button" class="btn btn-primary" id="show-game-btn" style="display:none;">Bored waiting?</button>
<script src="js/fileInput.js"></script>
<link rel="stylesheet" href="css/fileSelect.css">
</th:block>

View File

@ -1,23 +1,5 @@
<th:block th:fragment="errorBanner">
<style>
#github-button,
#discord-button {
display: inline-block;
padding: 1rem 2rem;
background-color: #008CBA;
color: #fff;
text-align: center;
text-decoration: none;
border-radius: 3rem;
transition: all 0.3s ease-in-out;
}
#github-button:hover,
#discord-button:hover {
background-color: #005b7f;
}
</style>
<br th:if="${message}">
<div id="errorContainer" th:if="${message}" class="alert alert-danger alert-dismissible fade show" role="alert">
<h4 class="alert-heading" th:text="'Error: ' + ${status} + ' ' + ${error}"></h4>

View File

@ -1,6 +1,4 @@
<th:block th:fragment="errorBannerPerPage">
<div id="errorContainer" class="alert alert-danger alert-dismissible fade show" role="alert" style="display: none;">
<h4 class="alert-heading">Error</h4>
<p></p>
@ -16,14 +14,8 @@
<pre id="traceContent"></pre>
</div>
</div>
</div>
<!-- Help Modal -->
<link rel="stylesheet" href="css/errorBanner.css">
<div class="modal fade" id="helpModal" tabindex="-1" role="dialog" aria-labelledby="helpModalLabel" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered" role="document" id="helpModalDialog">
<div class="modal-content">
@ -34,7 +26,6 @@
</button>
</div>
<div class="modal-body">
<div class="container">
<div id="support-section">
<h1 class="display-2">Oops!</h1>

View File

@ -1,58 +1,19 @@
<div th:fragment="footer">
<footer id="footer" class="text-center py-3">
<footer th:fragment="footer" id="footer" class="text-center py-3">
<div class="footer-center">
<a href="https://github.com/Stirling-Tools/Stirling-PDF"
target="_blank" class="mx-1" title="Visit Github Repository"><img
src="images/github.svg"></img></a> <a
href="https://hub.docker.com/r/frooodle/s-pdf" target="_blank"
class="mx-1" title="See Docker Hub"><img src="images/docker.svg"></img></a>
<a href="https://discord.gg/Cn8pWhQRxZ" target="_blank" class="mx-1"
title="Join Discord Channel"><img src="images/discord.svg"></img></a>
<a href="https://github.com/sponsors/Frooodle" target="_blank"
class="mx-1" title="Donate"><img
src="images/suit-heart-fill.svg"></img></a>
<a href="https://github.com/Stirling-Tools/Stirling-PDF" target="_blank" class="mx-1" title="Visit Github Repository">
<img src="images/github.svg">
</a>
<a href="https://hub.docker.com/r/frooodle/s-pdf" target="_blank" class="mx-1" title="See Docker Hub">
<img src="images/docker.svg">
</a>
<a href="https://discord.gg/Cn8pWhQRxZ" target="_blank" class="mx-1" title="Join Discord Channel">
<img src="images/discord.svg">
</a>
<a href="https://github.com/sponsors/Frooodle" target="_blank" class="mx-1" title="Donate">
<img src="images/suit-heart-fill.svg">
</a>
<div style="color: grey;" th:if="${@appName} != 'Stirling PDF'" class="footer-powered-by" th:text="#{poweredBy} + ' Stirling PDF'"></div>
</div>
<a href="licenses" id="licenses" target="_blank" class="mx-1" title="" th:text="#{licenses.nav}">Licenses</a>
</footer>
</div>
<style>
#footer {
display: flex;
flex-direction: column; /* Stack children vertically */
justify-content: center;
align-items: center;
width: 100%;
}
.footer-center {
display: flex;
flex-direction: column; /* Stack items vertically */
justify-content: center; /* Center children vertically */
align-items: center; /* Center children horizontally */
flex-grow: 1;
}
.footer-powered-by {
margin-top: auto; /* Pushes the text to the bottom */
color: grey;
text-align: center; /* Centers the text inside the div */
width: 100%; /* Full width to center the text properly */
}
.right-link-container {
align-self: flex-end; /* Align to the end of the flex container */
padding-right: 20px;
}
</style>

View File

@ -1,107 +1,28 @@
<th:block th:fragment="langs">
<a class="dropdown-item lang_dropdown-item" href=""
data-bs-language-code="bg_BG"> <img src="images/flags/bg.svg"
alt="icon" width="20" height="15"> Български
</a>
<a class="dropdown-item lang_dropdown-item" href=""
data-bs-language-code="ar_AR"> <img src="images/flags/sa.svg"
alt="icon" width="20" height="15"> العربية
</a>
<a class="dropdown-item lang_dropdown-item" href=""
data-bs-language-code="ca_CA"> <img src="images/flags/es-ct.svg"
alt="icon" width="20" height="15"> Català
</a>
<a class="dropdown-item lang_dropdown-item" href=""
data-bs-language-code="zh_CN"> <img src="images/flags/cn.svg"
alt="icon" width="20" height="15"> 简体中文
</a>
<a class="dropdown-item lang_dropdown-item" href=""
data-bs-language-code="zh_TW"> <img src="images/flags/tw.svg"
alt="icon" width="20" height="15"> 正體中文
</a>
<a class="dropdown-item lang_dropdown-item" href=""
data-bs-language-code="de_DE"> <img src="images/flags/de.svg"
alt="icon" width="20" height="15"> Deutsch
</a>
<a class="dropdown-item lang_dropdown-item" href=""
data-bs-language-code="en_GB"> <img src="images/flags/gb.svg"
alt="icon" width="20" height="15"> English (GB)
</a>
<a class="dropdown-item lang_dropdown-item" href=""
data-bs-language-code="en_US"> <img src="images/flags/us.svg"
alt="icon" width="20" height="15"> English (US)
</a>
<a class="dropdown-item lang_dropdown-item" href=""
data-bs-language-code="eu_ES"> <img src="images/flags/eu.svg"
alt="icon" width="20" height="15"> Euskara
</a>
<a class="dropdown-item lang_dropdown-item" href=""
data-bs-language-code="es_ES"> <img src="images/flags/es.svg"
alt="icon" width="20" height="15"> Español
</a>
<a class="dropdown-item lang_dropdown-item" href=""
data-bs-language-code="fr_FR"> <img src="images/flags/fr.svg"
alt="icon" width="20" height="15"> Français
</a>
<a class="dropdown-item lang_dropdown-item" href=""
data-bs-language-code="id_ID"> <img src="images/flags/id.svg"
alt="icon" width="20" height="15"> Indonesia
</a>
<a class="dropdown-item lang_dropdown-item" href=""
data-bs-language-code="it_IT"> <img src="images/flags/it.svg"
alt="icon" width="20" height="15"> Italiano
</a>
<a class="dropdown-item lang_dropdown-item" href=""
data-bs-language-code="nl_NL"> <img src="images/flags/nl.svg"
alt="icon" width="20" height="15"> Nederlands
</a>
<a class="dropdown-item lang_dropdown-item" href=""
data-bs-language-code="pl_PL"> <img src="images/flags/pl.svg"
alt="icon" width="20" height="15"> Polski
</a>
<a class="dropdown-item lang_dropdown-item" href=""
data-bs-language-code="pt_BR"> <img src="images/flags/pt_br.svg"
alt="icon" width="20" height="15"> Português (BR)
</a>
<a class="dropdown-item lang_dropdown-item" href=""
data-bs-language-code="ro_RO"> <img src="images/flags/ro.svg"
alt="icon" width="20" height="15"> Romanian
</a>
<a class="dropdown-item lang_dropdown-item" href=""
data-bs-language-code="sv_SE"> <img src="images/flags/se.svg"
alt="icon" width="20" height="15"> Svenska
</a>
<a class="dropdown-item lang_dropdown-item" href=""
data-bs-language-code="tr_TR"> <img src="images/flags/tr.svg"
alt="icon" width="20" height="15"> Türkçe
</a>
<a class="dropdown-item lang_dropdown-item" href=""
data-bs-language-code="ru_RU"> <img src="images/flags/ru.svg"
alt="icon" width="20" height="15"> Русский
</a>
<a class="dropdown-item lang_dropdown-item" href=""
data-bs-language-code="ko_KR"> <img src="images/flags/kr.svg"
alt="icon" width="20" height="15"> 한국어
</a>
<a class="dropdown-item lang_dropdown-item" href=""
data-bs-language-code="ja_JP"> <img src="images/flags/jp.svg"
alt="icon" width="20" height="15"> 日本語
</a>
<a class="dropdown-item lang_dropdown-item" href=""
data-bs-language-code="el_GR"> <img src="images/flags/gr.svg"
alt="icon" width="20" height="15"> Ελληνικά
</a>
<a class="dropdown-item lang_dropdown-item" href=""
data-bs-language-code="hu_HU"> <img src="images/flags/hu.svg"
alt="icon" width="20" height="15"> Hungarian
</a>
<a class="dropdown-item lang_dropdown-item" href=""
data-bs-language-code="hi_IN"> <img src="images/flags/in.svg"
alt="icon" width="20" height="15"> हिन्दी
</a>
<a class="dropdown-item lang_dropdown-item" href=""
data-language-code="sr-Latn-RS"> <img src="images/flags/rs.svg"
alt="icon" width="20" height="15"> Srpski
</a>
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="bg_BG"> <img src="images/flags/bg.svg" alt="icon" width="20" height="15"> Български</a>
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="ar_AR"> <img src="images/flags/sa.svg" alt="icon" width="20" height="15"> العربية</a>
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="ca_CA"> <img src="images/flags/es-ct.svg" alt="icon" width="20" height="15"> Català</a>
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="zh_CN"> <img src="images/flags/cn.svg" alt="icon" width="20" height="15"> 简体中文</a>
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="zh_TW"> <img src="images/flags/tw.svg" alt="icon" width="20" height="15"> 正體中文</a>
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="de_DE"> <img src="images/flags/de.svg" alt="icon" width="20" height="15"> Deutsch</a>
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="en_GB"> <img src="images/flags/gb.svg" alt="icon" width="20" height="15"> English (GB)</a>
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="en_US"> <img src="images/flags/us.svg" alt="icon" width="20" height="15"> English (US)</a>
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="eu_ES"> <img src="images/flags/eu.svg" alt="icon" width="20" height="15"> Euskara</a>
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="es_ES"> <img src="images/flags/es.svg" alt="icon" width="20" height="15"> Español</a>
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="fr_FR"> <img src="images/flags/fr.svg" alt="icon" width="20" height="15"> Français</a>
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="id_ID"> <img src="images/flags/id.svg" alt="icon" width="20" height="15"> Indonesia</a>
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="it_IT"> <img src="images/flags/it.svg" alt="icon" width="20" height="15"> Italiano</a>
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="nl_NL"> <img src="images/flags/nl.svg" alt="icon" width="20" height="15"> Nederlands</a>
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="pl_PL"> <img src="images/flags/pl.svg" alt="icon" width="20" height="15"> Polski</a>
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="pt_BR"> <img src="images/flags/pt_br.svg" alt="icon" width="20" height="15"> Português (BR)</a>
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="ro_RO"> <img src="images/flags/ro.svg" alt="icon" width="20" height="15"> Romanian</a>
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="sv_SE"> <img src="images/flags/se.svg" alt="icon" width="20" height="15"> Svenska</a>
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="tr_TR"> <img src="images/flags/tr.svg" alt="icon" width="20" height="15"> Türkçe</a>
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="ru_RU"> <img src="images/flags/ru.svg" alt="icon" width="20" height="15"> Русский</a>
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="ko_KR"> <img src="images/flags/kr.svg" alt="icon" width="20" height="15"> 한국어</a>
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="ja_JP"> <img src="images/flags/jp.svg" alt="icon" width="20" height="15"> 日本語</a>
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="el_GR"> <img src="images/flags/gr.svg" alt="icon" width="20" height="15"> Ελληνικά</a>
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="hu_HU"> <img src="images/flags/hu.svg" alt="icon" width="20" height="15"> Hungarian</a>
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="hi_IN"> <img src="images/flags/in.svg" alt="icon" width="20" height="15"> हिन्दी</a>
<a class="dropdown-item lang_dropdown-item" href="" data-language-code="sr-Latn-RS"> <img src="images/flags/rs.svg" alt="icon" width="20" height="15"> Srpski</a>
</th:block>

View File

@ -1,31 +1,21 @@
<div th:fragment="navbar" class="mx-auto">
<script src="js/languageSelection.js"></script>
<script th:inline="javascript">
const currentVersion = /*[[${@appVersion}]]*/ '';
const noFavourites = /*[[#{noFavourites}]]*/ '';
</script>
<script th:src="@{js/githubVersion.js}"></script>
<link rel="stylesheet" href="css/navbar.css">
<nav class="navbar navbar-expand-lg navbar-light bg-light">
<div class="container ">
<a class="navbar-brand" href="#" th:href="@{/}" >
<a class="navbar-brand" href="#" th:href="@{/}" style="display: flex;">
<img class="main-icon" src="favicon.svg?v=2" alt="icon">
<span class="icon-text" th:text="${@navBarText}"></span>
</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav me-auto flex-nowrap">
<li class="nav-item">
<a class="nav-link" href="#" th:href="@{multi-tool}" th:classappend="${currentPage}=='multi-tool' ? 'active' : ''" th:title="#{home.multiTool.desc}">
<img class="icon" src="images/tools.svg" alt="icon">
@ -39,7 +29,6 @@
<span class="icon-text" th:text="#{home.pipeline.title}"></span>
</a>
</li>
<li class="nav-item nav-item-separator"></li>
<li class="nav-item dropdown" th:classappend="${currentPage}=='remove-pages' OR ${currentPage}=='merge-pdfs' OR ${currentPage}=='split-pdfs' OR ${currentPage}=='crop' OR ${currentPage}=='adjust-contrast' OR ${currentPage}=='pdf-organizer' OR ${currentPage}=='rotate-pdf' OR ${currentPage}=='multi-page-layout' OR ${currentPage}=='scale-pages' OR ${currentPage}=='auto-split-pdf' OR ${currentPage}=='extract-page' OR ${currentPage}=='pdf-to-single-page' ? 'active' : ''">
<a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
@ -65,9 +54,11 @@
<div th:replace="~{fragments/navbarEntry :: navbarEntry ('split-pdf-by-sections', 'images/layout-split.svg', 'home.split-by-sections.title', 'home.split-by-sections.desc', 'split-by-sections.tags')}"></div>
</div>
</li>
<li class="nav-item nav-item-separator"></li>
<li class="nav-item dropdown" th:classappend="${currentPage}=='pdf-to-img' OR ${currentPage}=='img-to-pdf' OR ${currentPage}=='pdf-to-pdfa' OR ${currentPage}=='file-to-pdf' OR ${currentPage}=='xlsx-to-pdf' OR ${currentPage}=='pdf-to-word' OR ${currentPage}=='pdf-to-presentation' OR ${currentPage}=='pdf-to-text' OR ${currentPage}=='pdf-to-html' OR ${currentPage}=='pdf-to-xml' ? 'active' : ''"><a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-bs-toggle="dropdown"
aria-haspopup="true" aria-expanded="false">
<li class="nav-item dropdown" th:classappend="${currentPage}=='pdf-to-img' OR ${currentPage}=='img-to-pdf' OR ${currentPage}=='pdf-to-pdfa' OR ${currentPage}=='file-to-pdf' OR ${currentPage}=='xlsx-to-pdf' OR ${currentPage}=='pdf-to-word' OR ${currentPage}=='pdf-to-presentation' OR ${currentPage}=='pdf-to-text' OR ${currentPage}=='pdf-to-html' OR ${currentPage}=='pdf-to-xml' ? 'active' : ''">
<a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<img class="icon" src="images/arrow-left-right.svg" alt="icon" style="width: 16px; height: 16px; vertical-align: middle;">
<span class="icon-text" th:text="#{navbar.convert}"></span>
</a>
@ -110,14 +101,15 @@
</li>
<li class="nav-item nav-item-separator"></li>
<li class="nav-item dropdown" th:classappend="${currentPage}=='sign' OR ${currentPage}=='repair' OR ${currentPage}=='compare' OR ${currentPage}=='show-javascript' OR ${currentPage}=='flatten' OR ${currentPage}=='remove-blanks' OR ${currentPage}=='remove-annotations' OR ${currentPage}=='extract-image-scans' OR ${currentPage}=='change-metadata' OR ${currentPage}=='add-image' OR ${currentPage}=='ocr-pdf' OR ${currentPage}=='change-permissions' OR ${currentPage}=='extract-images' OR ${currentPage}=='compress-pdf' OR ${currentPage}=='add-page-numbers' OR ${currentPage}=='auto-rename' OR ${currentPage}=='get-info-on-pdf' ? 'active' : ''">
<a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<img class="icon" src="images/card-list.svg" alt="icon" style="width: 16px; height: 16px; vertical-align: middle;">
<span class="icon-text" th:text="#{navbar.other}"></span>
</a>
<div class="dropdown-menu" aria-labelledby="navbarDropdown">
<!--<div th:replace="~{fragments/navbarEntry :: navbarEntry ('pipeline', 'images/pipeline.svg', 'home.pipeline.title', 'home.pipeline.desc', 'pipeline.tags')}"></div> -->
<div th:replace="~{fragments/navbarEntry :: navbarEntry ('view-pdf', 'images/book-opened.svg', 'home.viewPdf.title', 'home.viewPdf.desc', 'viewPdf.tags')}"></div>
<div th:replace="~{fragments/navbarEntry :: navbarEntry ('ocr-pdf', 'images/search.svg', 'home.ocr.title', 'home.ocr.desc', 'ocr.tags')}"></div>
<div th:replace="~{fragments/navbarEntry :: navbarEntry ('add-image', 'images/file-earmark-richtext.svg', 'home.addImage.title', 'home.addImage.desc', 'addImage.tags')}"></div>
<div th:replace="~{fragments/navbarEntry :: navbarEntry ('compress-pdf', 'images/file-zip.svg', 'home.compressPdfs.title', 'home.compressPdfs.desc', 'compressPdfs.tags')}"></div>
@ -137,8 +129,6 @@
<div th:replace="~{fragments/navbarEntry :: navbarEntry ('stamp', 'images/stamp.svg', 'home.AddStampRequest.title', 'home.AddStampRequest.desc', 'AddStampRequest.tags')}"></div>
</div>
</li>
</ul>
<ul class="navbar-nav flex-nowrap">
<li class="nav-item dropdown">
@ -149,18 +139,12 @@
<!-- Dropdown items will be added here by JavaScript -->
</div>
</li>
<script src="js/languageSelection.js"></script>
<script src="js/darkmode.js"></script>
<li class="nav-item">
<a class="nav-link" id="dark-mode-toggle" href="#">
<img class="navbar-icon" id="dark-mode-icon" src="moon.svg" alt="icon" />
</a>
</li>
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="#" id="languageDropdown" role="button" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="currentColor" class="bi bi-globe2 globe-icon" viewBox="0 0 20 20">
@ -171,16 +155,12 @@
<th:block th:insert="~{fragments/languages :: langs}"></th:block>
</div>
</li>
<li class="nav-item">
<!-- Settings Button -->
<a href="#" class="nav-link" data-bs-toggle="modal" data-bs-target="#settingsModal">
<img class="navbar-icon" src="images/gear.svg" alt="icon" width="24" height="24">
</a>
</li>
<!-- Search Button and Search Bar -->
<li class="nav-item position-relative">
<a href="#" class="nav-link" id="search-icon">
@ -195,66 +175,14 @@
<div id="searchResults" class="border p-2 bg-white search-results"></div>
</div>
</li>
<style>
#search-icon i {
font-size: 24px; /* Adjust this to your desired size */
transition: color 0.3s;
}
#search-icon:hover i {
color: #666; /* Adjust this to your hover color */
}
#navbarSearch {
transition: all 0.3s;
max-height: 0;
overflow: hidden;
}
#navbarSearch.show {
max-height: 300px; /* Adjust this to your desired max height */
}
.search-input {
transition: border 0.3s, box-shadow 0.3s;
}
.search-input:focus {
border-color: #666; /* Adjust this to your focus color */
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); /* Adjust this to your desired shadow */
}
#searchResults {
max-width: 300px; /* Adjust to your preferred width */
transition: height 0.3s ease; /* Smooth height transition */
}
/* Set a fixed height and styling for each search result item */
.search-results a {
display: flex;
align-items: center;
gap: 10px; /* space between icon and text */
height: 40px; /* Adjust based on your design */
overflow: hidden; /* Prevent content from overflowing */
white-space: nowrap; /* Prevent text from wrapping to next line */
text-overflow: ellipsis; /* Truncate text if it's too long */
}
</style>
</ul>
</div>
</div>
<script src="js/favourites.js"></script>
<script src="js/search.js"></script>
</nav>
<div th:insert="~{fragments/errorBannerPerPage.html :: errorBannerPerPage}"></div>
<th:block th:insert="~{fragments/errorBannerPerPage.html :: errorBannerPerPage}"></th:block>
<div class="modal fade" id="settingsModal" tabindex="-1" role="dialog" aria-labelledby="settingsModalLabel" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered" role="document">
<div class="modal-content dark-card">
@ -305,11 +233,5 @@
</div>
</div>
</div>
<script src="js/settings.js"></script>
</div>

View File

@ -1,6 +1,6 @@
<div th:fragment="navbarEntry (endpoint, imgSrc, titleKey, descKey, tagKey)" th:if="${@endpointConfiguration.isEndpointEnabled(endpoint)}">
<th:block th:fragment="navbarEntry(endpoint, imgSrc, titleKey, descKey, tagKey)" th:if="${@endpointConfiguration.isEndpointEnabled(endpoint)}">
<a class="dropdown-item" href="#" th:href="@{${endpoint}}" th:classappend="${endpoint.equals(currentPage)} ? 'active' : ''" th:title="#{${descKey}}" th:data-bs-tags="#{${tagKey}}">
<img class="icon" th:src="@{${imgSrc}}" alt="icon">
<span class="icon-text" th:text="#{${titleKey}}"></span>
</a>
</div>
</th:block>

View File

@ -1,16 +1,13 @@
<!doctype html>
<html th:lang="${#locale.toString()}" th:lang-direction="#{language.direction}" xmlns:th="http://www.thymeleaf.org">
<!DOCTYPE html>
<html th:lang="${#locale.language}" th:lang-direction="#{language.direction}" xmlns:th="http://www.thymeleaf.org">
<head>
<th:block th:insert="~{fragments/common :: head(title='')}"></th:block>
<link rel="stylesheet" href="css/home.css">
</head>
<body>
<div id="page-container">
<div id="content-wrap">
<div th:insert="~{fragments/navbar.html :: navbar}"></div>
<th:block th:insert="~{fragments/navbar.html :: navbar}"></th:block>
<!-- Jumbotron -->
<div class="bg-light p-5 rounded d-none d-md-block" id="jumbotron">
<div class="container">
@ -26,90 +23,84 @@
<br>
<input type="text" id="searchBar" onkeyup="filterCards()" th:placeholder="#{home.searchBar}">
<div class="features-container">
<th:block th:if="${@enableAlphaFunctionality}">
<div th:replace="~{fragments/card :: card(id='pipeline', cardTitle=#{home.pipeline.title}, cardText=#{home.pipeline.desc}, cardLink='pipeline', svgPath='images/pipeline.svg')}"></div>
<div th:replace="~{fragments/card :: card(id='pipeline', cardTitle=#{home.pipeline.title}, cardText=#{home.pipeline.desc}, cardLink='pipeline', svgPath='images/pipeline.svg', tags=#{pipeline.tags})}"></div>
</th:block>
<div th:replace="~{fragments/card :: card(id='view-pdf', cardTitle=#{home.viewPdf.title}, cardText=#{home.viewPdf.desc}, cardLink='view-pdf', svgPath='images/book-opened.svg', tags=#{viewPdf.tags})}"></div>
<div th:replace="~{fragments/card :: card(id='multi-tool', cardTitle=#{home.multiTool.title}, cardText=#{home.multiTool.desc}, cardLink='multi-tool', svgPath='images/tools.svg', tags=#{multiTool.tags})}"></div>
<div th:replace="~{fragments/card :: card(id='merge-pdfs', cardTitle=#{home.merge.title}, cardText=#{home.merge.desc}, cardLink='merge-pdfs', svgPath='images/union.svg', tags=#{merge.tags})}"></div>
<div th:replace="~{fragments/card :: card(id='split-pdfs', cardTitle=#{home.split.title}, cardText=#{home.split.desc}, cardLink='split-pdfs', svgPath='images/layout-split.svg', tags=#{split.tags})}"></div>
<div th:replace="~{fragments/card :: card(id='view-pdf', cardTitle=#{home.viewPdf.title}, cardText=#{home.viewPdf.desc}, cardLink='view-pdf', svgPath='images/book-opened.svg')}"></div>
<div th:replace="~{fragments/card :: card(id='multi-tool', cardTitle=#{home.multiTool.title}, cardText=#{home.multiTool.desc}, cardLink='multi-tool', svgPath='images/tools.svg')}"></div>
<div th:replace="~{fragments/card :: card(id='merge-pdfs', cardTitle=#{home.merge.title}, cardText=#{home.merge.desc}, cardLink='merge-pdfs', svgPath='images/union.svg')}"></div>
<div th:replace="~{fragments/card :: card(id='split-pdfs', cardTitle=#{home.split.title}, cardText=#{home.split.desc}, cardLink='split-pdfs', svgPath='images/layout-split.svg')}"></div>
<div th:replace="~{fragments/card :: card(id='rotate-pdf', cardTitle=#{home.rotate.title}, cardText=#{home.rotate.desc}, cardLink='rotate-pdf', svgPath='images/arrow-clockwise.svg', tags=#{rotate.tags})}"></div>
<div th:replace="~{fragments/card :: card(id='crop', cardTitle=#{home.crop.title}, cardText=#{home.crop.desc}, cardLink='crop', svgPath='images/crop.svg', tags=#{crop.tags})}"></div>
<div th:replace="~{fragments/card :: card(id='add-page-numbers', cardTitle=#{home.add-page-numbers.title}, cardText=#{home.add-page-numbers.desc}, cardLink='add-page-numbers', svgPath='images/add-page-numbers.svg', tags=#{add-page-numbers.tags})}"></div>
<div th:replace="~{fragments/card :: card(id='rotate-pdf', cardTitle=#{home.rotate.title}, cardText=#{home.rotate.desc}, cardLink='rotate-pdf', svgPath='images/arrow-clockwise.svg')}"></div>
<div th:replace="~{fragments/card :: card(id='crop', cardTitle=#{home.crop.title}, cardText=#{home.crop.desc}, cardLink='crop', svgPath='images/crop.svg')}"></div>
<div th:replace="~{fragments/card :: card(id='add-page-numbers', cardTitle=#{home.add-page-numbers.title}, cardText=#{home.add-page-numbers.desc}, cardLink='add-page-numbers', svgPath='images/add-page-numbers.svg')}"></div>
<div th:replace="~{fragments/card :: card(id='adjust-contrast', cardTitle=#{home.adjust-contrast.title}, cardText=#{home.adjust-contrast.desc}, cardLink='adjust-contrast', svgPath='images/adjust-contrast.svg', tags=#{adjust-contrast.tags})}"></div>
<div th:replace="~{fragments/card :: card(id='img-to-pdf', cardTitle=#{home.imageToPdf.title}, cardText=#{home.imageToPdf.desc}, cardLink='img-to-pdf', svgPath='images/image.svg', tags=#{imageToPdf.tags})}"></div>
<div th:replace="~{fragments/card :: card(id='pdf-to-img', cardTitle=#{home.pdfToImage.title}, cardText=#{home.pdfToImage.desc}, cardLink='pdf-to-img', svgPath='images/image.svg', tags=#{pdfToImage.tags})}"></div>
<div th:replace="~{fragments/card :: card(id='adjust-contrast', cardTitle=#{home.adjust-contrast.title}, cardText=#{home.adjust-contrast.desc}, cardLink='adjust-contrast', svgPath='images/adjust-contrast.svg')}"></div>
<div th:replace="~{fragments/card :: card(id='img-to-pdf', cardTitle=#{home.imageToPdf.title}, cardText=#{home.imageToPdf.desc}, cardLink='img-to-pdf', svgPath='images/image.svg')}"></div>
<div th:replace="~{fragments/card :: card(id='pdf-to-img', cardTitle=#{home.pdfToImage.title}, cardText=#{home.pdfToImage.desc}, cardLink='pdf-to-img', svgPath='images/image.svg')}"></div>
<div th:replace="~{fragments/card :: card(id='pdf-organizer', cardTitle=#{home.pdfOrganiser.title}, cardText=#{home.pdfOrganiser.desc}, cardLink='pdf-organizer', svgPath='images/sort-numeric-down.svg', tags=#{pdfOrganiser.tags})}"></div>
<div th:replace="~{fragments/card :: card(id='add-image', cardTitle=#{home.addImage.title}, cardText=#{home.addImage.desc}, cardLink='add-image', svgPath='images/file-earmark-richtext.svg', tags=#{addImage.tags})}"></div>
<div th:replace="~{fragments/card :: card(id='add-watermark', cardTitle=#{home.watermark.title}, cardText=#{home.watermark.desc}, cardLink='add-watermark', svgPath='images/droplet.svg', tags=#{watermark.tags})}"></div>
<div th:replace="~{fragments/card :: card(id='pdf-organizer', cardTitle=#{home.pdfOrganiser.title}, cardText=#{home.pdfOrganiser.desc}, cardLink='pdf-organizer', svgPath='images/sort-numeric-down.svg')}"></div>
<div th:replace="~{fragments/card :: card(id='add-image', cardTitle=#{home.addImage.title}, cardText=#{home.addImage.desc}, cardLink='add-image', svgPath='images/file-earmark-richtext.svg')}"></div>
<div th:replace="~{fragments/card :: card(id='add-watermark', cardTitle=#{home.watermark.title}, cardText=#{home.watermark.desc}, cardLink='add-watermark', svgPath='images/droplet.svg')}"></div>
<div th:replace="~{fragments/card :: card(id='file-to-pdf', cardTitle=#{home.fileToPDF.title}, cardText=#{home.fileToPDF.desc}, cardLink='file-to-pdf', svgPath='images/file.svg', tags=#{fileToPDF.tags})}"></div>
<div th:replace="~{fragments/card :: card(id='remove-pages', cardTitle=#{home.removePages.title}, cardText=#{home.removePages.desc}, cardLink='remove-pages', svgPath='images/file-earmark-x.svg', tags=#{removePages.tags})}"></div>
<div th:replace="~{fragments/card :: card(id='add-password', cardTitle=#{home.addPassword.title}, cardText=#{home.addPassword.desc}, cardLink='add-password', svgPath='images/lock.svg', tags=#{addPassword.tags})}"></div>
<div th:replace="~{fragments/card :: card(id='file-to-pdf', cardTitle=#{home.fileToPDF.title}, cardText=#{home.fileToPDF.desc}, cardLink='file-to-pdf', svgPath='images/file.svg')}"></div>
<div th:replace="~{fragments/card :: card(id='remove-pages', cardTitle=#{home.removePages.title}, cardText=#{home.removePages.desc}, cardLink='remove-pages', svgPath='images/file-earmark-x.svg')}"></div>
<div th:replace="~{fragments/card :: card(id='add-password', cardTitle=#{home.addPassword.title}, cardText=#{home.addPassword.desc}, cardLink='add-password', svgPath='images/lock.svg')}"></div>
<div th:replace="~{fragments/card :: card(id='remove-password', cardTitle=#{home.removePassword.title}, cardText=#{home.removePassword.desc}, cardLink='remove-password', svgPath='images/unlock.svg', tags=#{removePassword.tags})}"></div>
<div th:replace="~{fragments/card :: card(id='compress-pdf', cardTitle=#{home.compressPdfs.title}, cardText=#{home.compressPdfs.desc}, cardLink='compress-pdf', svgPath='images/file-zip.svg', tags=#{compressPdfs.tags})}"></div>
<div th:replace="~{fragments/card :: card(id='change-metadata', cardTitle=#{home.changeMetadata.title}, cardText=#{home.changeMetadata.desc}, cardLink='change-metadata', svgPath='images/clipboard-data.svg', tags=#{changeMetadata.tags})}"></div>
<div th:replace="~{fragments/card :: card(id='remove-password', cardTitle=#{home.removePassword.title}, cardText=#{home.removePassword.desc}, cardLink='remove-password', svgPath='images/unlock.svg')}"></div>
<div th:replace="~{fragments/card :: card(id='compress-pdf', cardTitle=#{home.compressPdfs.title}, cardText=#{home.compressPdfs.desc}, cardLink='compress-pdf', svgPath='images/file-zip.svg')}"></div>
<div th:replace="~{fragments/card :: card(id='change-metadata', cardTitle=#{home.changeMetadata.title}, cardText=#{home.changeMetadata.desc}, cardLink='change-metadata', svgPath='images/clipboard-data.svg')}"></div>
<div th:replace="~{fragments/card :: card(id='change-permissions', cardTitle=#{home.permissions.title}, cardText=#{home.permissions.desc}, cardLink='change-permissions', svgPath='images/shield-lock.svg', tags=#{permissions.tags})}"></div>
<div th:replace="~{fragments/card :: card(id='ocr-pdf', cardTitle=#{home.ocr.title}, cardText=#{home.ocr.desc}, cardLink='ocr-pdf', svgPath='images/search.svg', tags=#{ocr.tags})}"></div>
<div th:replace="~{fragments/card :: card(id='extract-images', cardTitle=#{home.extractImages.title}, cardText=#{home.extractImages.desc}, cardLink='extract-images', svgPath='images/images.svg', tags=#{extractImages.tags})}"></div>
<div th:replace="~{fragments/card :: card(id='change-permissions', cardTitle=#{home.permissions.title}, cardText=#{home.permissions.desc}, cardLink='change-permissions', svgPath='images/shield-lock.svg')}"></div>
<div th:replace="~{fragments/card :: card(id='ocr-pdf', cardTitle=#{home.ocr.title}, cardText=#{home.ocr.desc}, cardLink='ocr-pdf', svgPath='images/search.svg')}"></div>
<div th:replace="~{fragments/card :: card(id='extract-images', cardTitle=#{home.extractImages.title}, cardText=#{home.extractImages.desc}, cardLink='extract-images', svgPath='images/images.svg')}"></div>
<div th:replace="~{fragments/card :: card(id='pdf-to-pdfa', cardTitle=#{home.pdfToPDFA.title}, cardText=#{home.pdfToPDFA.desc}, cardLink='pdf-to-pdfa', svgPath='images/file-earmark-pdf.svg', tags=#{pdfToPDFA.tags})}"></div>
<div th:replace="~{fragments/card :: card(id='pdf-to-word', cardTitle=#{home.PDFToWord.title}, cardText=#{home.PDFToWord.desc}, cardLink='pdf-to-word', svgPath='images/file-earmark-word.svg', tags=#{PDFToWord.tags})}"></div>
<div th:replace="~{fragments/card :: card(id='pdf-to-presentation', cardTitle=#{home.PDFToPresentation.title}, cardText=#{home.PDFToPresentation.desc}, cardLink='pdf-to-presentation', svgPath='images/file-earmark-ppt.svg', tags=#{PDFToPresentation.tags})}"></div>
<div th:replace="~{fragments/card :: card(id='pdf-to-pdfa', cardTitle=#{home.pdfToPDFA.title}, cardText=#{home.pdfToPDFA.desc}, cardLink='pdf-to-pdfa', svgPath='images/file-earmark-pdf.svg')}"></div>
<div th:replace="~{fragments/card :: card(id='pdf-to-word', cardTitle=#{home.PDFToWord.title}, cardText=#{home.PDFToWord.desc}, cardLink='pdf-to-word', svgPath='images/file-earmark-word.svg')}"></div>
<div th:replace="~{fragments/card :: card(id='pdf-to-presentation', cardTitle=#{home.PDFToPresentation.title}, cardText=#{home.PDFToPresentation.desc}, cardLink='pdf-to-presentation', svgPath='images/file-earmark-ppt.svg')}"></div>
<div th:replace="~{fragments/card :: card(id='pdf-to-text', cardTitle=#{home.PDFToText.title}, cardText=#{home.PDFToText.desc}, cardLink='pdf-to-text', svgPath='images/filetype-txt.svg', tags=#{PDFToText.tags})}"></div>
<div th:replace="~{fragments/card :: card(id='pdf-to-html', cardTitle=#{home.PDFToHTML.title}, cardText=#{home.PDFToHTML.desc}, cardLink='pdf-to-html', svgPath='images/filetype-html.svg', tags=#{PDFToHTML.tags})}"></div>
<div th:replace="~{fragments/card :: card(id='pdf-to-xml', cardTitle=#{home.PDFToXML.title}, cardText=#{home.PDFToXML.desc}, cardLink='pdf-to-xml', svgPath='images/filetype-xml.svg', tags=#{PDFToXML.tags})}"></div>
<div th:replace="~{fragments/card :: card(id='pdf-to-text', cardTitle=#{home.PDFToText.title}, cardText=#{home.PDFToText.desc}, cardLink='pdf-to-text', svgPath='images/filetype-txt.svg')}"></div>
<div th:replace="~{fragments/card :: card(id='pdf-to-html', cardTitle=#{home.PDFToHTML.title}, cardText=#{home.PDFToHTML.desc}, cardLink='pdf-to-html', svgPath='images/filetype-html.svg')}"></div>
<div th:replace="~{fragments/card :: card(id='pdf-to-xml', cardTitle=#{home.PDFToXML.title}, cardText=#{home.PDFToXML.desc}, cardLink='pdf-to-xml', svgPath='images/filetype-xml.svg')}"></div>
<div th:replace="~{fragments/card :: card(id='extract-image-scans', cardTitle=#{home.ScannerImageSplit.title}, cardText=#{home.ScannerImageSplit.desc}, cardLink='extract-image-scans', svgPath='images/scanner.svg', tags=#{ScannerImageSplit.tags})}"></div>
<div th:replace="~{fragments/card :: card(id='sign', cardTitle=#{home.sign.title}, cardText=#{home.sign.desc}, cardLink='sign', svgPath='images/sign.svg', tags=#{sign.tags})}"></div>
<div th:replace="~{fragments/card :: card(id='flatten', cardTitle=#{home.flatten.title}, cardText=#{home.flatten.desc}, cardLink='flatten', svgPath='images/flatten.svg', tags=#{flatten.tags})}"></div>
<div th:replace="~{fragments/card :: card(id='extract-image-scans', cardTitle=#{home.ScannerImageSplit.title}, cardText=#{home.ScannerImageSplit.desc}, cardLink='extract-image-scans', svgPath='images/scanner.svg')}"></div>
<div th:replace="~{fragments/card :: card(id='sign', cardTitle=#{home.sign.title}, cardText=#{home.sign.desc}, cardLink='sign', svgPath='images/sign.svg')}"></div>
<div th:replace="~{fragments/card :: card(id='flatten', cardTitle=#{home.flatten.title}, cardText=#{home.flatten.desc}, cardLink='flatten', svgPath='images/flatten.svg')}"></div>
<div th:replace="~{fragments/card :: card(id='repair', cardTitle=#{home.repair.title}, cardText=#{home.repair.desc}, cardLink='repair', svgPath='images/wrench.svg', tags=#{repair.tags})}"></div>
<div th:replace="~{fragments/card :: card(id='remove-blanks', cardTitle=#{home.removeBlanks.title}, cardText=#{home.removeBlanks.desc}, cardLink='remove-blanks', svgPath='images/blank-file.svg', tags=#{removeBlanks.tags})}"></div>
<div th:replace="~{fragments/card :: card(id='remove-annotations', cardTitle=#{home.removeAnnotations.title}, cardText=#{home.removeAnnotations.desc}, cardLink='remove-annotations', svgPath='images/no-chat.svg', tags=#{removeAnnotations.tags})}"></div>
<div th:replace="~{fragments/card :: card(id='compare', cardTitle=#{home.compare.title}, cardText=#{home.compare.desc}, cardLink='compare', svgPath='images/scales.svg', tags=#{compare.tags})}"></div>
<div th:replace="~{fragments/card :: card(id='repair', cardTitle=#{home.repair.title}, cardText=#{home.repair.desc}, cardLink='repair', svgPath='images/wrench.svg')}"></div>
<div th:replace="~{fragments/card :: card(id='remove-blanks', cardTitle=#{home.removeBlanks.title}, cardText=#{home.removeBlanks.desc}, cardLink='remove-blanks', svgPath='images/blank-file.svg')}"></div>
<div th:replace="~{fragments/card :: card(id='remove-annotations', cardTitle=#{home.removeAnnotations.title}, cardText=#{home.removeAnnotations.desc}, cardLink='remove-annotations', svgPath='images/no-chat.svg')}"></div>
<div th:replace="~{fragments/card :: card(id='compare', cardTitle=#{home.compare.title}, cardText=#{home.compare.desc}, cardLink='compare', svgPath='images/scales.svg')}"></div>
<div th:replace="~{fragments/card :: card(id='cert-sign', cardTitle=#{home.certSign.title}, cardText=#{home.certSign.desc}, cardLink='cert-sign', svgPath='images/award.svg')}"></div>
<div th:replace="~{fragments/card :: card(id='multi-page-layout', cardTitle=#{home.pageLayout.title}, cardText=#{home.pageLayout.desc}, cardLink='multi-page-layout', svgPath='images/page-layout.svg')}"></div>
<div th:replace="~{fragments/card :: card(id='scale-pages', cardTitle=#{home.scalePages.title}, cardText=#{home.scalePages.desc}, cardLink='scale-pages', svgPath='images/scale-pages.svg')}"></div>
<div th:replace="~{fragments/card :: card(id='auto-rename', cardTitle=#{home.auto-rename.title}, cardText=#{home.auto-rename.desc}, cardLink='auto-rename', svgPath='images/fonts.svg')}"></div>
<div th:replace="~{fragments/card :: card(id='auto-split-pdf', cardTitle=#{home.autoSplitPDF.title}, cardText=#{home.autoSplitPDF.desc}, cardLink='auto-split-pdf', svgPath='images/layout-split.svg')}"></div>
<div th:replace="~{fragments/card :: card(id='sanitize-pdf', cardTitle=#{home.sanitizePdf.title}, cardText=#{home.sanitizePdf.desc}, cardLink='sanitize-pdf', svgPath='images/sanitize.svg')}"></div>
<div th:replace="~{fragments/card :: card(id='url-to-pdf', cardTitle=#{home.URLToPDF.title}, cardText=#{home.URLToPDF.desc}, cardLink='url-to-pdf', svgPath='images/url.svg')}"></div>
<div th:replace="~{fragments/card :: card(id='html-to-pdf', cardTitle=#{home.HTMLToPDF.title}, cardText=#{home.HTMLToPDF.desc}, cardLink='html-to-pdf', svgPath='images/html.svg')}"></div>
<div th:replace="~{fragments/card :: card(id='markdown-to-pdf', cardTitle=#{home.MarkdownToPDF.title}, cardText=#{home.MarkdownToPDF.desc}, cardLink='markdown-to-pdf', svgPath='images/markdown.svg')}"></div>
<div th:replace="~{fragments/card :: card(id='get-info-on-pdf', cardTitle=#{home.getPdfInfo.title}, cardText=#{home.getPdfInfo.desc}, cardLink='get-info-on-pdf', svgPath='images/info.svg')}"></div>
<div th:replace="~{fragments/card :: card(id='extract-page', cardTitle=#{home.extractPage.title}, cardText=#{home.extractPage.desc}, cardLink='extract-page', svgPath='images/extract.svg')}"></div>
<div th:replace="~{fragments/card :: card(id='pdf-to-single-page', cardTitle=#{home.PdfToSinglePage.title}, cardText=#{home.PdfToSinglePage.desc}, cardLink='pdf-to-single-page', svgPath='images/single-page.svg')}"></div>
<div th:replace="~{fragments/card :: card(id='show-javascript', cardTitle=#{home.showJS.title}, cardText=#{home.showJS.desc}, cardLink='show-javascript', svgPath='images/js.svg')}"></div>
<div th:replace="~{fragments/card :: card(id='auto-redact', cardTitle=#{home.autoRedact.title}, cardText=#{home.autoRedact.desc}, cardLink='auto-redact', svgPath='images/eraser-fill.svg')}"></div>
<div th:replace="~{fragments/card :: card(id='pdf-to-csv', cardTitle=#{home.tableExtraxt.title}, cardText=#{home.tableExtraxt.desc}, cardLink='pdf-to-csv', svgPath='images/pdf-csv.svg')}"></div>
<div th:replace="~{fragments/card :: card(id='split-by-size-or-count', cardTitle=#{home.autoSizeSplitPDF.title}, cardText=#{home.autoSizeSplitPDF.desc}, cardLink='split-by-size-or-count', svgPath='images/layout-split.svg')}"></div>
<div th:replace="~{fragments/card :: card(id='overlay-pdf', cardTitle=#{home.overlay-pdfs.title}, cardText=#{home.overlay-pdfs.desc}, cardLink='overlay-pdf', svgPath='images/overlay.svg')}"></div>
<div th:replace="~{fragments/card :: card(id='split-pdf-by-sections', cardTitle=#{home.split-by-sections.title}, cardText=#{home.split-by-sections.desc}, cardLink='split-pdf-by-sections', svgPath='images/layout-split.svg')}"></div>
<div th:replace="~{fragments/card :: card(id='book-to-pdf', cardTitle=#{home.BookToPDF.title}, cardText=#{home.BookToPDF.desc}, cardLink='book-to-pdf', svgPath='images/book.svg')}"></div>
<div th:replace="~{fragments/card :: card(id='pdf-to-book', cardTitle=#{home.PDFToBook.title}, cardText=#{home.PDFToBook.desc}, cardLink='pdf-to-book', svgPath='images/book.svg')}"></div>
<div th:replace="~{fragments/card :: card(id='stamp', cardTitle=#{home.AddStampRequest.title}, cardText=#{home.AddStampRequest.desc}, cardLink='stamp', svgPath='images/stamp.svg')}"></div>
<div th:replace="~{fragments/card :: card(id='cert-sign', cardTitle=#{home.certSign.title}, cardText=#{home.certSign.desc}, cardLink='cert-sign', svgPath='images/award.svg', tags=#{certSign.tags})}"></div>
<div th:replace="~{fragments/card :: card(id='multi-page-layout', cardTitle=#{home.pageLayout.title}, cardText=#{home.pageLayout.desc}, cardLink='multi-page-layout', svgPath='images/page-layout.svg', tags=#{pageLayout.tags})}"></div>
<div th:replace="~{fragments/card :: card(id='scale-pages', cardTitle=#{home.scalePages.title}, cardText=#{home.scalePages.desc}, cardLink='scale-pages', svgPath='images/scale-pages.svg', tags=#{scalePages.tags})}"></div>
<div th:replace="~{fragments/card :: card(id='auto-rename', cardTitle=#{home.auto-rename.title}, cardText=#{home.auto-rename.desc}, cardLink='auto-rename', svgPath='images/fonts.svg', tags=#{auto-rename.tags})}"></div>
<div th:replace="~{fragments/card :: card(id='auto-split-pdf', cardTitle=#{home.autoSplitPDF.title}, cardText=#{home.autoSplitPDF.desc}, cardLink='auto-split-pdf', svgPath='images/layout-split.svg', tags=#{autoSplitPDF.tags})}"></div>
<div th:replace="~{fragments/card :: card(id='sanitize-pdf', cardTitle=#{home.sanitizePdf.title}, cardText=#{home.sanitizePdf.desc}, cardLink='sanitize-pdf', svgPath='images/sanitize.svg', tags=#{sanitizePdf.tags})}"></div>
<div th:replace="~{fragments/card :: card(id='url-to-pdf', cardTitle=#{home.URLToPDF.title}, cardText=#{home.URLToPDF.desc}, cardLink='url-to-pdf', svgPath='images/url.svg', tags=#{URLToPDF.tags})}"></div>
<div th:replace="~{fragments/card :: card(id='html-to-pdf', cardTitle=#{home.HTMLToPDF.title}, cardText=#{home.HTMLToPDF.desc}, cardLink='html-to-pdf', svgPath='images/html.svg', tags=#{HTMLToPDF.tags})}"></div>
<div th:replace="~{fragments/card :: card(id='markdown-to-pdf', cardTitle=#{home.MarkdownToPDF.title}, cardText=#{home.MarkdownToPDF.desc}, cardLink='markdown-to-pdf', svgPath='images/markdown.svg', tags=#{MarkdownToPDF.tags})}"></div>
<div th:replace="~{fragments/card :: card(id='get-info-on-pdf', cardTitle=#{home.getPdfInfo.title}, cardText=#{home.getPdfInfo.desc}, cardLink='get-info-on-pdf', svgPath='images/info.svg', tags=#{getPdfInfo.tags})}"></div>
<div th:replace="~{fragments/card :: card(id='extract-page', cardTitle=#{home.extractPage.title}, cardText=#{home.extractPage.desc}, cardLink='extract-page', svgPath='images/extract.svg', tags=#{extractPage.tags})}"></div>
<div th:replace="~{fragments/card :: card(id='pdf-to-single-page', cardTitle=#{home.PdfToSinglePage.title}, cardText=#{home.PdfToSinglePage.desc}, cardLink='pdf-to-single-page', svgPath='images/single-page.svg', tags=#{PdfToSinglePage.tags})}"></div>
<div th:replace="~{fragments/card :: card(id='show-javascript', cardTitle=#{home.showJS.title}, cardText=#{home.showJS.desc}, cardLink='show-javascript', svgPath='images/js.svg', tags=#{showJS.tags})}"></div>
<div th:replace="~{fragments/card :: card(id='auto-redact', cardTitle=#{home.autoRedact.title}, cardText=#{home.autoRedact.desc}, cardLink='auto-redact', svgPath='images/eraser-fill.svg', tags=#{autoRedact.tags})}"></div>
<div th:replace="~{fragments/card :: card(id='pdf-to-csv', cardTitle=#{home.tableExtraxt.title}, cardText=#{home.tableExtraxt.desc}, cardLink='pdf-to-csv', svgPath='images/pdf-csv.svg', tags=#{tableExtraxt.tags})}"></div>
<div th:replace="~{fragments/card :: card(id='split-by-size-or-count', cardTitle=#{home.autoSizeSplitPDF.title}, cardText=#{home.autoSizeSplitPDF.desc}, cardLink='split-by-size-or-count', svgPath='images/layout-split.svg', tags=#{autoSizeSplitPDF.tags})}"></div>
<div th:replace="~{fragments/card :: card(id='overlay-pdf', cardTitle=#{home.overlay-pdfs.title}, cardText=#{home.overlay-pdfs.desc}, cardLink='overlay-pdf', svgPath='images/overlay.svg', tags=#{overlay-pdfs.tags})}"></div>
<div th:replace="~{fragments/card :: card(id='split-pdf-by-sections', cardTitle=#{home.split-by-sections.title}, cardText=#{home.split-by-sections.desc}, cardLink='split-pdf-by-sections', svgPath='images/layout-split.svg', tags=#{split-by-sections.tags})}"></div>
<div th:replace="~{fragments/card :: card(id='book-to-pdf', cardTitle=#{home.BookToPDF.title}, cardText=#{home.BookToPDF.desc}, cardLink='book-to-pdf', svgPath='images/book.svg', tags=#{BookToPDF.tags})}"></div>
<div th:replace="~{fragments/card :: card(id='pdf-to-book', cardTitle=#{home.PDFToBook.title}, cardText=#{home.PDFToBook.desc}, cardLink='pdf-to-book', svgPath='images/book.svg', tags=#{PDFToBook.tags})}"></div>
<div th:replace="~{fragments/card :: card(id='stamp', cardTitle=#{home.AddStampRequest.title}, cardText=#{home.AddStampRequest.desc}, cardLink='stamp', svgPath='images/stamp.svg', tags=#{AddStampRequest.tags})}"></div>
</div>
</div> </div>
<div th:insert="~{fragments/footer.html :: footer}"></div>
</div>
</div>
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
</div>
</body>
</html>

View File

@ -1,28 +1,14 @@
<!DOCTYPE html>
<html th:lang="${#locale.toString()}"
th:lang-direction="#{language.direction}"
xmlns:th="http://www.thymeleaf.org">
<html th:lang="${#locale.language}" th:lang-direction="#{language.direction}" xmlns:th="http://www.thymeleaf.org">
<head>
<th:block th:insert="~{fragments/common :: head(title=#{licenses.title}, header=#{licenses.title})}"></th:block>
</head>
<th:block
th:insert="~{fragments/common :: head(title=#{licenses.title}, header=#{licenses.title})}"></th:block>
<style>
td a {
text-decoration: none;
}
td a:hover, td a:focus {
text-decoration: underline;
/* Adds underline on hover/focus for clarity */
}
</style>
<body>
<div id="page-container">
<div id="content-wrap">
<div th:insert="~{fragments/navbar.html :: navbar}"></div>
<br> <br>
<th:block th:insert="~{fragments/navbar.html :: navbar}"></th:block>
<br /><br />
<div class="container">
<div class="row justify-content-center">
<div class="col-md-6">
@ -50,7 +36,7 @@ td a:hover, td a:focus {
</div>
</div>
</div>
<div th:insert="~{fragments/footer.html :: footer}"></div>
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
</div>
</body>
</html>

View File

@ -1,130 +1,14 @@
<!doctype html>
<html th:lang="${#locale.toString()}" th:lang-direction="#{language.direction}" xmlns:th="http://www.thymeleaf.org">
<!DOCTYPE html>
<html th:lang="${#locale.language}" th:lang-direction="#{language.direction}" xmlns:th="http://www.thymeleaf.org">
<head>
<th:block th:insert="~{fragments/common :: head(title=#{login.title}, header=#{login.header})}"></th:block>
<script src="js/darkmode.js"></script>
<style>
html, body {
height: 100%;
}
<link rel="stylesheet" href="css/login.css">
</head>
body {
display: flex;
align-items: center;
padding-top: 40px;
padding-bottom: 40px;
background-color: #f5f5f5;
}
.form-signin {
width: 100%;
max-width: 330px;
padding: 15px;
margin: auto;
}
.form-signin .checkbox {
font-weight: 400;
}
.form-signin .form-floating:focus-within {
z-index: 2;
}
.form-signin input[type="text"] {
margin-bottom: -1px;
border-bottom-right-radius: 0;
border-bottom-left-radius: 0;
}
.form-signin input[type="password"] {
margin-bottom: 10px;
border-top-left-radius: 0;
border-top-right-radius: 0;
}
.container-flex {
display: flex;
flex-direction: column;
min-height: 100vh;
width: 100%; /* Set width to 100% */
align-items: center; /* Center its children horizontally */
}
.footer-bottom {
margin-top: auto;
}
body.light-mode input:-webkit-autofill,
body.light-mode input:-webkit-autofill:hover,
body.light-mode input:-webkit-autofill:focus,
body.light-mode input:-webkit-autofill:active {
-webkit-text-fill-color: #212529; /* Dark font color */
-webkit-box-shadow: 0 0 0 1000px #f8f9fa inset; /* Light background color */
}
/* Dark Mode */
body.dark-mode input:-webkit-autofill,
body.dark-mode input:-webkit-autofill:hover,
body.dark-mode input:-webkit-autofill:focus,
body.dark-mode input:-webkit-autofill:active {
-webkit-text-fill-color: #f8f9fa; /* Light font color */
-webkit-box-shadow: 0 0 0 1000px #212529 inset; /* Dark background color */
}
/* Light Mode */
body.light-mode .form-floating > input:focus + label {
color: #212529 !important; /* Dark text for light background */
}
/* Dark Mode */
body.dark-mode .form-floating > input:focus + label {
color: #fff !important; /* Light text for dark background */
}
body.light-mode .form-floating > label {
color: #212529 !important; /* Dark text for light background */
}
body.dark-mode .form-floating > label {
color: #fff !important; /* Light text for dark background */
}
/* Removing default styles for ul and li */
ul {
list-style: none;
padding: 0;
margin: 0;
}
li {
list-style: none;
}
/* Positioning the container of these elements to the top right */
.your-container-class {
position: absolute;
top: 0;
right: 0;
display: flex;
}
/* Styling for the dropdown */
.dropdown-menu {
min-width: 200px; /* or whatever width you prefer */
}
.navbar-icon {
width: 40px;
height: 40px;
}
</style>
<body>
<div class="your-container-class">
</div>
<div class="your-container-class"></div>
<div class="container-flex">
<main class="form-signin text-center">
<script>
function setInputMode(elementId, mode) {
var inputElement = document.getElementById(elementId);
@ -142,13 +26,10 @@ function setInputMode(elementId, mode) {
break;
case "rainbow":
// Assuming you have defined some classes for rainbow mode
break;
}
}
document.addEventListener('modeChanged', function(e) {
var mode = e.detail;
@ -167,7 +48,6 @@ document.addEventListener('modeChanged', function(e) {
document.body.classList.add("rainbow-mode");
break;
}
});
document.addEventListener('DOMContentLoaded', function() {
@ -249,13 +129,8 @@ function handleDropdownItemClick(event) {
console.error("Language code is not set for this item.");
}
}
</script>
<div th:if="${logoutMessage}" class="alert alert-success"
th:text="${logoutMessage}"></div>
<div th:if="${logoutMessage}" class="alert alert-success" th:text="${logoutMessage}"></div>
<div th:if="${param.messageType != null and param.messageType.size() > 0 and param.messageType[0] == 'credsUpdated'}" class="alert alert-success">
<span th:text="#{changedCredsMessage}">Default message if not found</span>
</div>
@ -265,12 +140,10 @@ function handleDropdownItemClick(event) {
<h2 class="h5 mb-3 fw-normal" th:text="#{login.signinTitle}">Please sign in</h2>
<div class="form-floating">
<input type="text" class="form-control bg-dark text-light" id="username" name="username"
placeholder="admin"> <label for="username" th:text="#{username}">Username</label>
<input type="text" class="form-control bg-dark text-light" id="username" name="username" placeholder="admin"> <label for="username" th:text="#{username}">Username</label>
</div>
<div class="form-floating">
<input type="password" class="form-control bg-dark text-light" id="password" name="password"
placeholder="Password"> <label for="password" th:text="#{password}">Password</label>
<input type="password" class="form-control bg-dark text-light" id="password" name="password" placeholder="Password"> <label for="password" th:text="#{password}">Password</label>
</div>
<div class="checkbox mb-3">
@ -278,8 +151,7 @@ function handleDropdownItemClick(event) {
<span th:text="#{login.rememberme}"></span>
</label>
</div>
<button class="w-100 btn btn-lg btn-primary" type="submit" th:text="#{login.signin}">Sign
in</button>
<button class="w-100 btn btn-lg btn-primary" type="submit" th:text="#{login.signin}">Sign in</button>
</form>
<div class="mt-3"> <!-- Added a margin-top for spacing -->
<div class="dropdown">
@ -292,22 +164,12 @@ function handleDropdownItemClick(event) {
</div>
</div>
</div>
<div class="text-danger text-center">
<div th:if="${error == 'badcredentials'}" th:text="#{login.invalid}">Invalid username or
password.</div>
<div th:if="${error == 'locked'}" th:text="#{login.locked}">Your account has been locked.
<div th:if="${error == 'badcredentials'}" th:text="#{login.invalid}">Invalid username or password.</div>
<div th:if="${error == 'locked'}" th:text="#{login.locked}">Your account has been locked. </div>
</div>
</div>
</main>
<div th:insert="~{fragments/footer.html :: footer}"></div>
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
</div>
</body>
</html>

View File

@ -1,14 +1,15 @@
<!DOCTYPE html>
<html th:lang="${#locale.toString()}" th:lang-direction="#{language.direction}" xmlns:th="http://www.thymeleaf.org">
<html th:lang="${#locale.language}" th:lang-direction="#{language.direction}" xmlns:th="http://www.thymeleaf.org">
<head>
<th:block th:insert="~{fragments/common :: head(title=#{merge.title}, header=#{merge.header})}"></th:block>
<link rel="stylesheet" href="css/merge.css">
</head>
<body>
<div id="page-container">
<div id="content-wrap">
<div th:insert="~{fragments/navbar.html :: navbar}"></div>
<br> <br>
<th:block th:insert="~{fragments/navbar.html :: navbar}"></th:block>
<br /><br />
<div class="container" id="dropContainer">
<div class="row justify-content-center">
<div class="col-md-6">
@ -17,7 +18,6 @@
<div class="mb-3">
<label th:text="#{multiPdfDropPrompt}"></label>
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=true, accept='application/pdf')}"></div>
</div>
<div class="mb-3">
<ul id="selectedFiles" class="list-group"></ul>
@ -28,13 +28,12 @@
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{merge.submit}"></button>
</div>
</form>
<link rel="stylesheet" href="css/merge.css">
<script src="js/merge.js"></script>
</div>
</div>
</div>
</div>
<div th:insert="~{fragments/footer.html :: footer}"></div>
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
</div>
</body>
</html>

View File

@ -1,16 +1,16 @@
<!DOCTYPE html>
<html th:lang="${#locale.language}" th:lang-direction="#{language.direction}" xmlns:th="http://www.thymeleaf.org">
<head>
<th:block th:insert="~{fragments/common :: head(title=#{addImage.title}, header=#{addImage.header})}"></th:block>
<script src="js/thirdParty/interact.min.js"></script>
<link rel="stylesheet" href="css/add-image.css">
</head>
<body>
<div id="page-container">
<div id="content-wrap">
<div th:insert="~{fragments/navbar.html :: navbar}"></div>
<br> <br>
<th:block th:insert="~{fragments/navbar.html :: navbar}"></th:block>
<br /><br />
<div class="container">
<div class="row justify-content-center">
<div class="col-md-6">
@ -31,7 +31,7 @@
document.querySelectorAll(".show-on-file-selected").forEach(el => {
el.style.cssText = '';
})
});
}
});
document.addEventListener("DOMContentLoaded", () => {
@ -84,36 +84,6 @@
</svg>
</button>
</div>
<style>
#box-drag-container {
position: relative;
margin: 20px 0;
}
#pdf-canvas {
box-shadow: 0px 0px 8px rgba(0, 0, 0, 0.384);
width: 100%;
}
.draggable-buttons-box {
position: absolute;
top: 0;
padding: 10px;
width: 100%;
display: flex;
gap: 5px;
}
.draggable-buttons-box > button {
z-index: 10;
background-color: rgba(13, 110, 253, 0.1);
}
.draggable-canvas {
border: 1px solid red;
position: absolute;
touch-action: none;
user-select: none;
top: 0px;
left: 0;
}
</style>
</div>
<!-- download button -->
@ -135,7 +105,8 @@
</div>
</div>
</div>
<div th:insert="~{fragments/footer.html :: footer}"></div>
</div>
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
</div>
</body>
</html>

View File

@ -1,37 +1,7 @@
<!DOCTYPE html>
<html th:lang="${#locale.toString()}"
th:lang-direction="#{language.direction}"
xmlns:th="http://www.thymeleaf.org">
<th:block
th:insert="~{fragments/common :: head(title=#{addPageNumbers.title}, header=#{addPageNumbers.header})}"></th:block>
<body>
<th:block th:insert="~{fragments/common :: game}"></th:block>
<div id="page-container">
<div id="content-wrap">
<div th:insert="~{fragments/navbar.html :: navbar}"></div>
<br> <br>
<div class="container">
<div class="row justify-content-center">
<div class="col-md-6">
<h2 th:text="#{addPageNumbers.header}"></h2>
<form method="post" enctype="multipart/form-data"
th:action="@{api/v1/misc/add-page-numbers}">
<div
th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false, accept='application/pdf')}"></div>
<br>
<div class="mb-3">
<label for="customMargin" th:text="#{addPageNumbers.selectText.2}"></label> <select
class="form-control" id="customMargin" name="customMargin"
required>
<option value="small" th:text="#{sizes.small}"></option>
<option value="medium" selected th:text="#{sizes.medium}"></option>
<option value="large" th:text="#{sizes.large}"></option>
<option value="x-large" th:text="#{sizes.x-large}"></option>
</select>
</div>
<html th:lang="${#locale.language}" th:lang-direction="#{language.direction}" xmlns:th="http://www.thymeleaf.org">
<head>
<th:block th:insert="~{fragments/common :: head(title=#{addPageNumbers.title}, header=#{addPageNumbers.header})}"></th:block>
<style>
.a4container {
position: relative;
@ -75,8 +45,30 @@
background-color: #006600;
}
</style>
</head>
<body>
<th:block th:insert="~{fragments/common :: game}"></th:block>
<div id="page-container">
<div id="content-wrap">
<th:block th:insert="~{fragments/navbar.html :: navbar}"></th:block>
<br /><br />
<div class="container">
<div class="row justify-content-center">
<div class="col-md-6">
<h2 th:text="#{addPageNumbers.header}"></h2>
<form method="post" enctype="multipart/form-data" th:action="@{api/v1/misc/add-page-numbers}">
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false, accept='application/pdf')}"></div>
<br>
<div class="mb-3">
<label for="customMargin" th:text="#{addPageNumbers.selectText.2}"></label>
<select class="form-control" id="customMargin" name="customMargin" required>
<option value="small" th:text="#{sizes.small}"></option>
<option value="medium" selected th:text="#{sizes.medium}"></option>
<option value="large" th:text="#{sizes.large}"></option>
<option value="x-large" th:text="#{sizes.x-large}"></option>
</select>
</div>
<div class="mb-3">
<label for="position" th:text="#{addPageNumbers.selectText.3}"></label>
<div class="a4container">
@ -91,29 +83,21 @@
<div class="pageNumber" id="9" style="top: 90%; left: 90%;">9</div>
</div>
</div>
<input type="hidden" id="numberInput" name="position" min="1"
max="9" value="8" required />
<input type="hidden" id="numberInput" name="position" min="1" max="9" value="8" required />
<div class="mb-3">
<label for="startingNumber" th:text="#{addPageNumbers.selectText.4}"></label> <input
type="number" class="form-control" id="startingNumber"
name="startingNumber" min="1" required value="1" />
<label for="startingNumber" th:text="#{addPageNumbers.selectText.4}"></label>
<input type="number" class="form-control" id="startingNumber" name="startingNumber" min="1" required value="1" />
</div>
<div class="mb-3">
<label for="pagesToNumber" th:text="#{addPageNumbers.selectText.5}"></label> <input
type="text" class="form-control" id="pagesToNumber"
name="pagesToNumber"
th:placeholder="#{addPageNumbers.numberPagesDesc}" />
<label for="pagesToNumber" th:text="#{addPageNumbers.selectText.5}"></label>
<input type="text" class="form-control" id="pagesToNumber" name="pagesToNumber" th:placeholder="#{addPageNumbers.numberPagesDesc}" />
</div>
<div class="mb-3">
<label for="customText" th:text="#{addPageNumbers.selectText.6}"></label> <input type="text"
class="form-control" id="customText" name="customText"
th:placeholder="#{addPageNumbers.customNumberDesc}" />
<label for="customText" th:text="#{addPageNumbers.selectText.6}"></label>
<input type="text" class="form-control" id="customText" name="customText" th:placeholder="#{addPageNumbers.customNumberDesc}" />
</div>
<button type="submit" id="submitBtn" class="btn btn-primary"
th:text="#{addPageNumbers.submit}"></button>
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{addPageNumbers.submit}"></button>
</form>
</div>
</div>
</div>
@ -146,9 +130,8 @@
});
});
</script>
</div>
<div th:insert="~{fragments/footer.html :: footer}"></div>
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
</div>
</body>
</html>

View File

@ -1,17 +1,14 @@
<!DOCTYPE html>
<html th:lang="${#locale.toString()}"
th:lang-direction="#{language.direction}"
xmlns:th="http://www.thymeleaf.org">
<th:block
th:insert="~{fragments/common :: head(title=#{adjustContrast.title}, header=#{adjustContrast.header})}"></th:block>
<html th:lang="${#locale.language}" th:lang-direction="#{language.direction}" xmlns:th="http://www.thymeleaf.org">
<head>
<th:block th:insert="~{fragments/common :: head(title=#{adjustContrast.title}, header=#{adjustContrast.header})}"></th:block>
</head>
<body>
<div id="page-container">
<div id="content-wrap">
<div th:insert="~{fragments/navbar.html :: navbar}"></div>
<br> <br>
<th:block th:insert="~{fragments/navbar.html :: navbar}"></th:block>
<br /><br />
<div class="container">
<div class="row justify-content-center">
<div class="col-md-12">
@ -304,7 +301,7 @@
</div>
</div>
</div>
<div th:insert="~{fragments/footer.html :: footer}"></div>
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
</div>
</body>
</html>

View File

@ -1,15 +1,15 @@
<!DOCTYPE html>
<html th:lang="${#locale.toString()}" th:lang-direction="#{language.direction}" xmlns:th="http://www.thymeleaf.org">
<html th:lang="${#locale.language}" th:lang-direction="#{language.direction}" xmlns:th="http://www.thymeleaf.org">
<head>
<th:block th:insert="~{fragments/common :: head(title=#{autoCrop.title}, header=#{autoCrop.header})}"></th:block>
</head>
<body>
<th:block th:insert="~{fragments/common :: game}"></th:block>
<div id="page-container">
<div id="content-wrap">
<div th:insert="~{fragments/navbar.html :: navbar}"></div>
<br> <br>
<th:block th:insert="~{fragments/navbar.html :: navbar}"></th:block>
<br /><br />
<div class="container">
<div class="row justify-content-center">
<div class="col-md-6">
@ -23,9 +23,8 @@
</div>
</div>
</div>
</div>
<div th:insert="~{fragments/footer.html :: footer}"></div>
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
</div>
</body>
</html>

View File

@ -1,15 +1,15 @@
<!DOCTYPE html>
<html th:lang="${#locale.toString()}" th:lang-direction="#{language.direction}" xmlns:th="http://www.thymeleaf.org">
<html th:lang="${#locale.language}" th:lang-direction="#{language.direction}" xmlns:th="http://www.thymeleaf.org">
<head>
<th:block th:insert="~{fragments/common :: head(title=#{auto-rename.title}, header=#{auto-rename.header})}"></th:block>
</head>
<body>
<th:block th:insert="~{fragments/common :: game}"></th:block>
<div id="page-container">
<div id="content-wrap">
<div th:insert="~{fragments/navbar.html :: navbar}"></div>
<br> <br>
<th:block th:insert="~{fragments/navbar.html :: navbar}"></th:block>
<br /><br />
<div class="container">
<div class="row justify-content-center">
<div class="col-md-6">
@ -22,9 +22,8 @@
</div>
</div>
</div>
</div>
<div th:insert="~{fragments/footer.html :: footer}"></div>
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
</div>
</body>
</html>

View File

@ -1,32 +1,29 @@
<!DOCTYPE html>
<html th:lang="${#locale.toString()}" th:lang-direction="#{language.direction}" xmlns:th="http://www.thymeleaf.org">
<html th:lang="${#locale.language}" th:lang-direction="#{language.direction}" xmlns:th="http://www.thymeleaf.org">
<head>
<th:block th:insert="~{fragments/common :: head(title=#{changeMetadata.title}, header=#{changeMetadata.header})}"></th:block>
</head>
<body>
<div id="page-container">
<div id="content-wrap">
<div th:insert="~{fragments/navbar.html :: navbar}"></div>
<br> <br>
<th:block th:insert="~{fragments/navbar.html :: navbar}"></th:block>
<br /><br />
<div class="container">
<div class="row justify-content-center">
<div class="col-md-6">
<h2 th:text="#{changeMetadata.header}"></h2>
<form method="post" id="form1" enctype="multipart/form-data" th:action="@{api/v1/misc/update-metadata}">
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false, accept='application/pdf')}"></div>
<p class="text-muted" th:text="#{changeMetadata.selectText.1}"></p>
<div class="mb-3-inline form-check">
<input type="checkbox" class="form-check-input" id="deleteAll" name="deleteAll">
<label class="ms-3" for="deleteAll" th:text="#{changeMetadata.selectText.2}" ></label>
</div>
<div class="mb-3-inline form-check">
<input type="checkbox" class="form-check-input" id="customModeCheckbox">
<label class="ms-3" for="customModeCheckbox" th:text="#{changeMetadata.selectText.3}"></label>
</div>
<div class="mb-3">
<label class="form-check-label" for="author" th:text="#{changeMetadata.author}"></label>
<input type="text" class="form-control" id="author" name="author">
@ -85,7 +82,6 @@
<br>
<button class="btn btn-primary" type="submit" id="submitBtn" th:text="#{changeMetadata.submit}"></button>
<script>
const deleteAllCheckbox = document.querySelector("#deleteAll");
let inputs = document.querySelectorAll("input");
const customMetadataDiv = document.getElementById('customMetadata');
@ -102,20 +98,10 @@
});
});
const customModeCheckbox = document.getElementById('customModeCheckbox');
const addMetadataBtn = document.getElementById("addMetadataBtn");
const customMetadataFormContainer = document.getElementById("customMetadataEntries");
var count = 1;
const fileInput = document.querySelector("#fileInput-input");
const authorInput = document.querySelector("#author");
const creationDateInput = document.querySelector("#creationDate");
@ -126,21 +112,14 @@
const subjectInput = document.querySelector("#subject");
const titleInput = document.querySelector("#title");
const trappedInput = document.querySelector("#trapped");
var lastPDFFileMeta = null;
fileInput.addEventListener("change", async function() {
while (otherMetadataEntriesDiv.firstChild) {
otherMetadataEntriesDiv.removeChild(otherMetadataEntriesDiv.firstChild);
}
while (customMetadataFormContainer.firstChild) {
customMetadataFormContainer.removeChild(customMetadataFormContainer.firstChild);
}
const file = this.files[0];
var url = URL.createObjectURL(file)
pdfjsLib.GlobalWorkerOptions.workerSrc = 'pdfjs/pdf.worker.js'
@ -177,7 +156,6 @@
});
addMetadataBtn.addEventListener("click", () => {
const keyInput = document.createElement("input");
keyInput.type = "text";
keyInput.placeholder = 'Key';
@ -196,7 +174,6 @@
formGroup.appendChild(keyInput);
formGroup.appendChild(valueInput);
customMetadataFormContainer.appendChild(formGroup);
});
function convertDateFormat(dateTimeString) {
@ -216,8 +193,6 @@
function addExtra() {
const event = document.getElementById("customModeCheckbox");
if (event.checked && lastPDFFile.Custom != null) {
customMetadataDiv.style.display = 'block';
for (const [key, value] of Object.entries(lastPDFFile.Custom)) {
@ -226,9 +201,6 @@
}
const entryDiv = document.createElement('div');
entryDiv.className = 'mb-3';
entryDiv.innerHTML = `<div class="mb-3"><label class="form-check-label" for="${key}">${key}:</label><input name="${key}" value="${value}" type="text" class="form-control" id="${key}"></div>`;
otherMetadataEntriesDiv.appendChild(entryDiv);
}
@ -238,24 +210,18 @@
otherMetadataEntriesDiv.removeChild(otherMetadataEntriesDiv.firstChild);
}
}
}
customModeCheckbox.addEventListener('change', (event) => {
addExtra();
});
</script>
</form>
</div>
</div>
</div>
</div>
<div th:insert="~{fragments/footer.html :: footer}"></div>
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
</div>
</body>
</html>

View File

@ -1,25 +1,30 @@
<!DOCTYPE html>
<html th:lang="${#locale.toString()}" th:lang-direction="#{language.direction}" xmlns:th="http://www.thymeleaf.org">
<html th:lang="${#locale.language}" th:lang-direction="#{language.direction}" xmlns:th="http://www.thymeleaf.org">
<head>
<th:block th:insert="~{fragments/common :: head(title=#{compare.title}, header=#{compare.header})}"></th:block>
<style>
.result-column {
border: 1px solid #ccc;
padding: 15px;
overflow-y: auto;
height: calc(100vh - 400px);
white-space: pre-wrap;
}
</style>
</head>
<body>
<div id="page-container">
<div id="content-wrap">
<div th:insert="~{fragments/navbar.html :: navbar}"></div>
<br> <br>
<th:block th:insert="~{fragments/navbar.html :: navbar}"></th:block>
<br /><br />
<div class="container">
<div class="row justify-content-center">
<div class="col-md-9">
<h2 th:text="#{compare.header}"></h2>
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false, accept='application/pdf', remoteCall='false')}"></div>
<div th:replace="~{fragments/common :: fileSelector(name='fileInput2', multiple=false, accept='application/pdf', remoteCall='false')}"></div>
<button class="btn btn-primary" onclick="comparePDFs()" th:text="#{compare.submit}"></button>
<div class="row">
<div class="col-md-6">
<h3 th:text="#{compare.document.1}"></h3>
@ -30,15 +35,6 @@
<div id="result2" class="result-column"></div>
</div>
</div>
<style>
.result-column {
border: 1px solid #ccc;
padding: 15px;
overflow-y: auto;
height: calc(100vh - 400px);
white-space: pre-wrap;
}
</style>
<script>
// get the elements
var result1 = document.getElementById('result1');
@ -127,7 +123,6 @@
return differences;
};
const differences = diff(text1, text2);
const displayDifferences = (differences) => {
@ -186,5 +181,7 @@
</div>
</div>
</div>
<div th:insert="~{fragments/footer.html :: footer}"></div>
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
</div>
</body>
</html>

View File

@ -1,16 +1,16 @@
<!DOCTYPE html>
<html th:lang="${#locale.toString()}" th:lang-direction="#{language.direction}" xmlns:th="http://www.thymeleaf.org">
<html th:lang="${#locale.language}" th:lang-direction="#{language.direction}" xmlns:th="http://www.thymeleaf.org">
<head>
<th:block th:insert="~{fragments/common :: head(title=#{compress.title}, header=#{compress.header})}"></th:block>
</head>
<body>
<th:block th:insert="~{fragments/common :: game}"></th:block>
<div id="page-container">
<div id="content-wrap">
<div th:insert="~{fragments/navbar.html :: navbar}"></div>
<br> <br>
<th:block th:insert="~{fragments/navbar.html :: navbar}"></th:block>
<br /><br />
<div class="container">
<div class="row justify-content-center">
<div class="col-md-6">
@ -29,7 +29,6 @@
</select>
</div>
</div>
<div class="card mb-3">
<div class="card-body">
<h4 th:text="#{compress.selectText.4}"></h4>
@ -43,8 +42,7 @@
</div>
</div>
</div>
<div th:insert="~{fragments/footer.html :: footer}"></div>
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
</div>
</body>
</html>

View File

@ -1,14 +1,14 @@
<!DOCTYPE html>
<html th:lang="${#locale.toString()}" th:lang-direction="#{language.direction}" xmlns:th="http://www.thymeleaf.org">
<html th:lang="${#locale.language}" th:lang-direction="#{language.direction}" xmlns:th="http://www.thymeleaf.org">
<head>
<th:block th:insert="~{fragments/common :: head(title=#{home.ScannerImageSplit.title}, header=#{home.ScannerImageSplit.header})}"></th:block>
</head>
<body>
<div id="page-container">
<div id="content-wrap">
<div th:insert="~{fragments/navbar.html :: navbar}"></div>
<br> <br>
<th:block th:insert="~{fragments/navbar.html :: navbar}"></th:block>
<br /><br />
<div class="container">
<div class="row justify-content-center">
<div class="col-md-6">
@ -43,12 +43,11 @@
</div>
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{genericSubmit}"></button>
</form>
</div>
</div>
</div>
</div>
<div th:insert="~{fragments/footer.html :: footer}"></div>
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
</div>
</body>
</html>

View File

@ -1,19 +1,18 @@
<!DOCTYPE html>
<html th:lang="${#locale.toString()}" th:lang-direction="#{language.direction}" xmlns:th="http://www.thymeleaf.org">
<html th:lang="${#locale.language}" th:lang-direction="#{language.direction}" xmlns:th="http://www.thymeleaf.org">
<head>
<th:block th:insert="~{fragments/common :: head(title=#{extractImages.title}, header=#{extractImages.header})}"></th:block>
</head>
<body>
<div id="page-container">
<div id="content-wrap">
<div th:insert="~{fragments/navbar.html :: navbar}"></div>
<br> <br>
<th:block th:insert="~{fragments/navbar.html :: navbar}"></th:block>
<br /><br />
<div class="container">
<div class="row justify-content-center">
<div class="col-md-6">
<h2 th:text="#{extractImages.header}"></h2>
<form id="multiPdfForm" th:action="@{api/v1/misc/extract-images}" method="post" enctype="multipart/form-data">
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false, accept='application/pdf')}"></div>
<div class="mb-3">
@ -30,7 +29,7 @@
</div>
</div>
</div>
<div th:insert="~{fragments/footer.html :: footer}"></div>
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
</div>
</body>
</html>

View File

@ -1,15 +1,14 @@
<!DOCTYPE html>
<html th:lang="${#locale.toString()}" th:lang-direction="#{language.direction}" xmlns:th="http://www.thymeleaf.org">
<html th:lang="${#locale.language}" th:lang-direction="#{language.direction}" xmlns:th="http://www.thymeleaf.org">
<head>
<th:block th:insert="~{fragments/common :: head(title=#{flatten.title}, header=#{flatten.header})}"></th:block>
</head>
<body>
<div id="page-container">
<div id="content-wrap">
<div th:insert="~{fragments/navbar.html :: navbar}"></div>
<br> <br>
<th:block th:insert="~{fragments/navbar.html :: navbar}"></th:block>
<br /><br />
<div class="container">
<div class="row justify-content-center">
<div class="col-md-6">
@ -44,13 +43,12 @@
await downloadFilesWithCallback(processFile);
});
</script>
</form>
</div>
</div>
</div>
</div>
<div th:insert="~{fragments/footer.html :: footer}"></div>
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
</div>
</body>

View File

@ -1,9 +1,7 @@
<!DOCTYPE html>
<html th:lang="${#locale.toString()}" th:lang-direction="#{language.direction}" xmlns:th="http://www.thymeleaf.org">
<th:block th:insert="~{fragments/common :: head(title=#{ocr.title}, header=#{ocr.header})}"></th:block>
<html th:lang="${#locale.language}" th:lang-direction="#{language.direction}" xmlns:th="http://www.thymeleaf.org">
<head>
<th:block th:insert="~{fragments/common :: head(title=#{ocr.title}, header=#{ocr.header})}"></th:block>
<script>
function handleLangSelection() {
let checkboxes = document.getElementsByName("languages");
@ -32,8 +30,8 @@
<th:block th:insert="~{fragments/common :: game}"></th:block>
<div id="page-container">
<div id="content-wrap">
<div th:insert="~{fragments/navbar.html :: navbar}"></div>
<br> <br>
<th:block th:insert="~{fragments/navbar.html :: navbar}"></th:block>
<br /><br />
<div class="container">
<div class="row justify-content-center">
<div class="col-md-6">
@ -42,14 +40,14 @@
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false, accept='application/pdf')}"></div>
<div class="mb-3">
<label for="languages" class="form-label" th:text="#{ocr.selectText.1}"></label>
<hr>
<hr />
<div id="languages">
<div th:each="language, iterStat : ${languages}">
<input type="checkbox" th:name="languages" th:value="${language}" required th:id="${'language-' + language}" onchange="handleLangSelection()" />
<label class="form-check-label" th:for="${'language-' + language}" th:text="${language}"></label>
</div>
</div>
<hr>
<hr />
</div>
<div class="mb-3">
<label th:text="#{ocr.selectText.10}"></label>
@ -81,8 +79,6 @@
<input type="checkbox" class="form-check-input" name="removeImagesAfter" id="removeImagesAfter" />
<label class="form-check-label" for="removeImagesAfter" th:text="#{ocr.selectText.11}"></label>
</div>
<div class="mb-3">
<label th:text="#{ocr.selectText.12}"></label>
<select class="form-control" name="ocrRenderType">
@ -239,7 +235,6 @@
label.textContent = getFullLanguageName(languageCode);
});
});
</script>
<p th:text="#{ocr.credit}"></p>
<p th:text="#{ocr.help}"></p>
@ -248,7 +243,7 @@
</div>
</div>
</div>
<div th:insert="~{fragments/footer.html :: footer}"></div>
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
</div>
</body>
</html>

View File

@ -1,12 +1,14 @@
<!DOCTYPE html>
<html th:lang="${#locale.toString()}" th:lang-direction="#{language.direction}" xmlns:th="http://www.thymeleaf.org">
<html th:lang="${#locale.language}" th:lang-direction="#{language.direction}" xmlns:th="http://www.thymeleaf.org">
<head>
<th:block th:insert="~{fragments/common :: head(title=#{removeAnnotations.title}, header=#{removeAnnotations.header})}"></th:block>
</head>
<body>
<div id="page-container">
<div id="content-wrap">
<div th:insert="~{fragments/navbar.html :: navbar}"></div>
<br> <br>
<th:block th:insert="~{fragments/navbar.html :: navbar}"></th:block>
<br /><br />
<div class="container">
<div class="row justify-content-center">
<div class="col-md-6">
@ -21,7 +23,7 @@
</div>
</div>
</div>
<div th:insert="~{fragments/footer.html :: footer}"></div>
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
</div>
<script src="js/local-pdf-input-download.js"></script>
<script>
@ -60,5 +62,4 @@
});
</script>
</body>
</html>

View File

@ -1,19 +1,18 @@
<!DOCTYPE html>
<html th:lang="${#locale.toString()}" th:lang-direction="#{language.direction}" xmlns:th="http://www.thymeleaf.org">
<html th:lang="${#locale.language}" th:lang-direction="#{language.direction}" xmlns:th="http://www.thymeleaf.org">
<head>
<th:block th:insert="~{fragments/common :: head(title=#{removeBlanks.title}, header=#{removeBlanks.header})}"></th:block>
</head>
<body>
<div id="page-container">
<div id="content-wrap">
<div th:insert="~{fragments/navbar.html :: navbar}"></div>
<br> <br>
<th:block th:insert="~{fragments/navbar.html :: navbar}"></th:block>
<br /><br />
<div class="container">
<div class="row justify-content-center">
<div class="col-md-6">
<h2 th:text="#{removeBlanks.header}"></h2>
<form id="multiPdfForm" th:action="@{api/v1/misc/remove-blanks}" method="post" enctype="multipart/form-data">
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false, accept='application/pdf')}"></div>
<div class="mb-3">
@ -32,7 +31,7 @@
</div>
</div>
</div>
<div th:insert="~{fragments/footer.html :: footer}"></div>
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
</div>
</body>
</html>

View File

@ -1,15 +1,14 @@
<!DOCTYPE html>
<html th:lang="${#locale.toString()}" th:lang-direction="#{language.direction}" xmlns:th="http://www.thymeleaf.org">
<html th:lang="${#locale.language}" th:lang-direction="#{language.direction}" xmlns:th="http://www.thymeleaf.org">
<head>
<th:block th:insert="~{fragments/common :: head(title=#{repair.title}, header=#{repair.header})}"></th:block>
</head>
<body>
<div id="page-container">
<div id="content-wrap">
<div th:insert="~{fragments/navbar.html :: navbar}"></div>
<br> <br>
<th:block th:insert="~{fragments/navbar.html :: navbar}"></th:block>
<br /><br />
<div class="container">
<div class="row justify-content-center">
<div class="col-md-6">
@ -22,8 +21,7 @@
</div>
</div>
</div>
<div th:insert="~{fragments/footer.html :: footer}"></div>
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
</div>
</body>
</html>

View File

@ -1,28 +1,31 @@
<!DOCTYPE html>
<html th:lang="${#locale.toString()}"
th:lang-direction="#{language.direction}"
xmlns:th="http://www.thymeleaf.org">
<th:block
th:insert="~{fragments/common :: head(title=#{showJS.title}, header=#{showJS.header})}"></th:block>
<body>
<html th:lang="${#locale.language}" th:lang-direction="#{language.direction}" xmlns:th="http://www.thymeleaf.org">
<head>
<th:block th:insert="~{fragments/common :: head(title=#{showJS.title}, header=#{showJS.header})}"></th:block>
<link href="css/prism.css" rel="stylesheet" />
<script src="js/thirdParty/prism.js"></script>
<style>
/* Add a max-height and make it scrollable */
#script-content {
max-height: 1000px; /* Adjust this to your preferred maximum height */
overflow-y: auto;
}
</style>
</head>
<body>
<div id="page-container">
<div id="content-wrap">
<div th:insert="~{fragments/navbar.html :: navbar}"></div>
<br> <br>
<th:block th:insert="~{fragments/navbar.html :: navbar}"></th:block>
<br /><br />
<div class="container">
<div class="row justify-content-center">
<div class="col-md-12">
<h2 th:text="#{showJS.header}"></h2>
<form id="pdfInfoForm" method="post" enctype="multipart/form-data"
th:action="@{show-javascript}">
<div
th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false, remoteCall='false', accept='application/pdf')}"></div>
<form id="pdfInfoForm" method="post" enctype="multipart/form-data" th:action="@{show-javascript}">
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false, remoteCall='false', accept='application/pdf')}"></div>
<br>
<button type="submit" id="submitBtn" class="btn btn-primary"
th:text="#{showJS.submit}"></button>
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{showJS.submit}"></button>
</form>
<div class="container mt-5">
<!-- Iterate over each main section in the JSON -->
@ -31,17 +34,8 @@
</div>
<!-- Button to download the JSON -->
<a href="#" id="downloadJS" class="btn btn-primary mt-3"
style="display: none;" th:text="#{showJS.downloadJS}">Download
JSON</a>
<a href="#" id="downloadJS" class="btn btn-primary mt-3" style="display: none;" th:text="#{showJS.downloadJS}">Download JSON</a>
</div>
<style>
/* Add a max-height and make it scrollable */
#script-content {
max-height: 1000px; /* Adjust this to your preferred maximum height */
overflow-y: auto;
}
</style>
<script>
document.querySelector('#pdfInfoForm').addEventListener('submit', function(event){
event.preventDefault();
@ -92,9 +86,8 @@
</div>
</div>
</div>
</div>
<div th:insert="~{fragments/footer.html :: footer}"></div>
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
</div>
</body>
</html>

Some files were not shown because too many files have changed in this diff Show More