refactor: extract inline JS into app/ modules, remove dead overtype-webcomponent

- Remove overtype-webcomponent.min.js (zero references)
- Extract copyLogContent + fallbackCopy + HTMX tab-updater → app/admin-logs.js
  (removes duplicate from both system.php and parametres.php)
- Extract copyUrl → app/clipboard.js (shared by acces.php)
- Extract tag/language pill-search logic → app/pill-search.js
  Generalized with data-pill-search attributes, auto-inits via
  DOMContentLoaded + htmx:afterSwap
- Extract access-request form handler → app/access-request.js
  (was inline in templates/public/tfe.php)

Files created: admin-logs.js, clipboard.js, pill-search.js, access-request.js
Files modified: 9 templates/controllers to drop inline scripts and
  reference external JS files
This commit is contained in:
Pontoporeia
2026-05-11 19:37:31 +02:00
parent 04094d802d
commit b56d073210
31 changed files with 430 additions and 1724 deletions

View File

@@ -29,6 +29,7 @@ extract($vars);
$pageTitle = 'Accès';
$isAdmin = true;
$bodyClass = 'admin-body';
$extraJs = ['/assets/js/app/clipboard.js'];
require_once APP_ROOT . '/templates/head.php';
echo '<link rel="stylesheet" href="/assets/css/file-access.css">';

View File

@@ -55,7 +55,7 @@ function wasSelected($key, $value) {
$isAdmin = true;
$bodyClass = 'admin-body';
$extraCss = ['/assets/css/form.css', '/assets/css/filepond.min.css', '/assets/css/filepond-plugin-image-preview.min.css'];
$extraJs = ['/assets/js/filepond.min.js', '/assets/js/filepond-plugin-file-validate-type.min.js', '/assets/js/filepond-plugin-file-validate-size.min.js', '/assets/js/filepond-plugin-image-preview.min.js', '/assets/js/filepond-plugin-image-exif-orientation.min.js', '/assets/js/file-upload-filepond.js', '/assets/js/beforeunload-guard.js', '/assets/js/upload-progress.js'];
$extraJs = ['/assets/js/vendor/filepond.min.js', '/assets/js/vendor/filepond-plugin-file-validate-type.min.js', '/assets/js/vendor/filepond-plugin-file-validate-size.min.js', '/assets/js/vendor/filepond-plugin-image-preview.min.js', '/assets/js/vendor/filepond-plugin-image-exif-orientation.min.js', '/assets/js/app/file-upload-filepond.js', '/assets/js/app/beforeunload-guard.js', '/assets/js/app/upload-progress.js', '/assets/js/app/pill-search.js'];
require_once APP_ROOT . '/templates/head.php';
include APP_ROOT . '/templates/header.php';
include APP_ROOT . '/templates/admin/add.php';

View File

@@ -70,7 +70,7 @@ $extraJsInline = '';
if ($editType === 'page' || $editType === 'about_page') {
$initialContent = $page["content"] ?? "";
$extraJs = ["/assets/js/overtype.min.js"];
$extraJs = ["/assets/js/vendor/overtype.min.js"];
$extraJsInline = <<<'JS'
var OT = window.OverType.default || window.OverType;
var hidden = document.getElementById('content');
@@ -83,7 +83,7 @@ var editor = new OT(document.getElementById('editor'), {
JS;
} elseif ($editType === 'form_help') {
$initialContent = $formHelpContent;
$extraJs = ["/assets/js/overtype.min.js"];
$extraJs = ["/assets/js/vendor/overtype.min.js"];
$extraJsInline = <<<'JS'
var OT = window.OverType.default || window.OverType;
var hidden = document.getElementById('content');

View File

@@ -40,7 +40,7 @@ try {
$isAdmin = true; $bodyClass = 'admin-body';
$extraCss = ['/assets/css/form.css', '/assets/css/filepond.min.css', '/assets/css/filepond-plugin-image-preview.min.css'];
$extraJs = ['/assets/js/filepond.min.js', '/assets/js/filepond-plugin-file-validate-type.min.js', '/assets/js/filepond-plugin-file-validate-size.min.js', '/assets/js/filepond-plugin-image-preview.min.js', '/assets/js/filepond-plugin-image-exif-orientation.min.js', '/assets/js/file-upload-filepond.js', '/assets/js/beforeunload-guard.js', '/assets/js/upload-progress.js'];
$extraJs = ['/assets/js/vendor/filepond.min.js', '/assets/js/vendor/filepond-plugin-file-validate-type.min.js', '/assets/js/vendor/filepond-plugin-file-validate-size.min.js', '/assets/js/vendor/filepond-plugin-image-preview.min.js', '/assets/js/vendor/filepond-plugin-image-exif-orientation.min.js', '/assets/js/app/file-upload-filepond.js', '/assets/js/app/beforeunload-guard.js', '/assets/js/app/upload-progress.js', '/assets/js/app/pill-search.js'];
require_once APP_ROOT . '/templates/head.php';
include APP_ROOT . '/templates/header.php';
include APP_ROOT . '/templates/admin/edit.php';

View File

@@ -0,0 +1,97 @@
/**
* access-request.js — handles the "Demander l'accès" form on public thesis pages.
*
* Shows/hides the justification textarea based on email domain (@erg.school / @erg.be).
* Submits via fetch() to /request-access and displays success/error messages.
* Handles the special "recipient_rejected" status to let the user fix their email.
*
* Expects a form with:
* #access-request-form — the form (needs data-thesis-id)
* #access-email — email input
* #justification-container — wrapper div for justification
* #access-justification — justification textarea
* #access-request-message — message display div
*/
(function () {
'use strict';
var form = document.getElementById('access-request-form');
if (!form) return;
var emailInput = document.getElementById('access-email');
var justificationContainer = document.getElementById('justification-container');
var justificationInput = document.getElementById('access-justification');
var messageDiv = document.getElementById('access-request-message');
if (!emailInput || !messageDiv) return;
// Show/hide justification based on email domain
emailInput.addEventListener('input', function () {
var email = this.value.trim().toLowerCase();
var isErg = email.endsWith('@erg.school') || email.endsWith('@erg.be');
if (justificationContainer) justificationContainer.style.display = isErg ? 'none' : 'block';
if (justificationInput) justificationInput.required = !isErg;
});
function showRetryPrompt(rejectedEmail, serverMessage) {
messageDiv.style.display = 'block';
messageDiv.className = 'tfe-access-message tfe-access-error';
messageDiv.innerHTML =
'<strong>Adresse e-mail introuvable sur le serveur de l\'ERG.</strong><br>' +
'<small>' + serverMessage.replace(/</g, '&lt;') + '</small><br><br>' +
'Corrigez votre adresse e-mail et réessayez.';
emailInput.value = rejectedEmail;
emailInput.classList.add('input-error');
emailInput.focus();
emailInput.select();
emailInput.addEventListener('input', function clearError() {
emailInput.classList.remove('input-error');
emailInput.removeEventListener('input', clearError);
});
}
form.addEventListener('submit', function (e) {
e.preventDefault();
var submitBtn = form.querySelector('button[type="submit"]');
submitBtn.disabled = true;
submitBtn.textContent = 'Envoi en cours...';
messageDiv.style.display = 'none';
var submittedEmail = emailInput.value.trim();
var formData = new FormData(form);
formData.append('thesis_id', form.getAttribute('data-thesis-id'));
fetch('/request-access', {
method: 'POST',
body: formData
})
.then(function (response) { return response.json(); })
.then(function (data) {
submitBtn.disabled = false;
submitBtn.textContent = 'Demander l\'accès';
if (data.status === 'recipient_rejected') {
showRetryPrompt(submittedEmail, data.message);
return;
}
messageDiv.style.display = 'block';
if (data.success) {
messageDiv.className = 'tfe-access-message tfe-access-success';
messageDiv.textContent = data.message;
form.reset();
} else {
messageDiv.className = 'tfe-access-message tfe-access-error';
messageDiv.textContent = data.message || 'Une erreur est survenue. Veuillez réessayer.';
}
})
.catch(function () {
submitBtn.disabled = false;
submitBtn.textContent = 'Demander l\'accès';
messageDiv.style.display = 'block';
messageDiv.className = 'tfe-access-message tfe-access-error';
messageDiv.textContent = 'Erreur de connexion. Veuillez réessayer.';
});
});
})();

View File

@@ -0,0 +1,64 @@
/**
* admin-logs.js — log viewer utilities shared by system.php and parametres.php.
*
* Provides:
* - copyLogContent(btn) — copy visible log lines to clipboard
* - HTMX afterSwap handler to update active tab class on #sys-tab-panel
*/
(function () {
'use strict';
window.copyLogContent = function (btn) {
var logOut = document.querySelector('#log-output');
if (!logOut) return;
var text = Array.from(logOut.querySelectorAll('.log-line'))
.map(function (el) { return el.textContent; }).join('\n');
if (navigator.clipboard && navigator.clipboard.writeText) {
navigator.clipboard.writeText(text).then(function () {
btn.textContent = '\u2713 Copi\u00e9';
btn.classList.add('copied');
setTimeout(function () { btn.textContent = 'Copier'; btn.classList.remove('copied'); }, 2000);
});
} else {
window._fallbackCopy(text, btn);
}
};
window._fallbackCopy = function (text, btn) {
var ta = document.createElement('textarea');
ta.value = text;
ta.style.cssText = 'position:fixed;opacity:0';
document.body.appendChild(ta);
ta.select();
try {
document.execCommand('copy');
btn.textContent = '\u2713 Copi\u00e9';
btn.classList.add('copied');
setTimeout(function () { btn.textContent = 'Copier'; btn.classList.remove('copied'); }, 2000);
} catch (e) {}
document.body.removeChild(ta);
};
// Update active tab class after each HTMX swap on #sys-tab-panel
document.body.addEventListener('htmx:afterSwap', function (evt) {
if (!(evt.detail.target && evt.detail.target.id === 'sys-tab-panel')) return;
var rc = evt.detail.requestConfig;
var tab = null;
// Tab clicks carry ?tab=… in the path
var qIdx = rc.path.indexOf('?');
if (qIdx !== -1) {
tab = new URLSearchParams(rc.path.substring(qIdx + 1)).get('tab');
}
// Line-count form sends tab via hx-vals in parameters
if (!tab && rc.parameters && rc.parameters.tab) {
tab = rc.parameters.tab;
}
if (!tab) return;
document.querySelectorAll('.sys-tabs .sys-tab').forEach(function (a) {
var isActive = a.getAttribute('data-tab') === tab;
a.classList.toggle('active', isActive);
if (isActive) a.setAttribute('aria-current', 'page');
else a.removeAttribute('aria-current');
});
});
})();

View File

@@ -0,0 +1,38 @@
/**
* clipboard.js — lightweight URL copy helper.
*
* Usage:
* <input type="hidden" id="url-123" value="https://...">
* <button onclick="copyUrl(123)">Copier</button>
*
* Or with a custom selector pattern:
* <button onclick="copyUrlFrom(document.getElementById('my-url'))">Copier</button>
*/
(function () {
'use strict';
window.copyUrl = function (id) {
var input = document.getElementById('url-' + id);
if (input) {
window.copyUrlFrom(input);
}
};
window.copyUrlFrom = function (sourceEl) {
var text = sourceEl.value || sourceEl.textContent || '';
if (!text) return;
navigator.clipboard.writeText(text).then(function () {
var btn = window.event && window.event.target ? window.event.target.closest('button') : null;
if (btn) {
var origTitle = btn.getAttribute('title') || '';
var origText = btn.textContent;
btn.setAttribute('title', '\u2713 Copi\u00e9');
btn.textContent = '\u2713 Copi\u00e9';
setTimeout(function () {
btn.setAttribute('title', origTitle);
btn.textContent = origText;
}, 1200);
}
});
};
})();

View File

@@ -0,0 +1,171 @@
/**
* pill-search.js — generalized pill-based search component for tags and languages.
*
* Initialisez avec un conteneur ayant l'attribut data-pill-search :
* <div data-pill-search data-pill-name="tag" data-pill-max="10" data-pill-min="3" data-pill-required="1">
*
* DOM attendu à l'intérieur du conteneur :
* - .tag-search-pills → conteneur des pills
* - .tag-search-input → champ de recherche (avec hx-post, hx-trigger, etc.)
* - .tag-search-suggestions → dropdown
* - .tag-search-count → compteur
* - .tag-search-counter → wrapper du compteur
* - .tag-search-input-wrap → wrapper du champ de recherche
* - .tag-search-max-msg → message "maximum atteint"
*
* Options (par attribut data) :
* data-pill-name → nom pour les inputs cachés (ex: "tag", "language_autre")
* data-pill-max → max pills (default 10)
* data-pill-min → min pills requis (default 0)
* data-pill-required → si "1", active l'affichage du minimum
* data-pill-role → "tag" (lowercase) ou "lang" (ucfirst)
*/
(function () {
'use strict';
function initAll() {
document.querySelectorAll('[data-pill-search]:not([data-pill-search-initialized])').forEach(function (container) {
container.setAttribute('data-pill-search-initialized', '1');
initPillSearch(container);
});
}
document.addEventListener('DOMContentLoaded', initAll);
document.body.addEventListener('htmx:afterSwap', initAll);
function initPillSearch(container) {
var pills = container.querySelector('.tag-search-pills');
var search = container.querySelector('.tag-search-input');
var dropdown = container.querySelector('.tag-search-suggestions');
var countEl = container.querySelector('.tag-search-count');
var counter = container.querySelector('.tag-search-counter');
var maxTags = parseInt(container.getAttribute('data-pill-max')) || 10;
var minTags = parseInt(container.getAttribute('data-pill-min')) || 0;
var required = container.getAttribute('data-pill-required') === '1';
var inputName = container.getAttribute('data-pill-name') || 'tag';
var role = container.getAttribute('data-pill-role') || 'tag';
var selectedIdx = -1;
if (!pills || !search || !dropdown) return;
function normalize(name) {
return name.trim().replace(/\s+/g, ' ').toLowerCase();
}
function pillAlreadyExists(name) {
var norm = normalize(name);
var existing = pills.querySelectorAll('.tag-pill-name');
for (var i = 0; i < existing.length; i++) {
if (normalize(existing[i].textContent) === norm) return true;
}
return false;
}
function updateCount() {
var n = pills.querySelectorAll('.tag-pill').length;
var suffix = required ? ' (min ' + minTags + ')' : '';
if (countEl) countEl.textContent = n + '/' + maxTags + suffix;
if (counter) counter.style.display = (n > 0 || required) ? '' : 'none';
if (countEl && required) {
countEl.style.color = n < minTags ? 'var(--text-danger)' : 'var(--accent)';
}
var wrap = container.querySelector('.tag-search-input-wrap');
var maxMsg = container.querySelector('.tag-search-max-msg');
if (n >= maxTags) {
if (wrap) wrap.style.display = 'none';
if (maxMsg) maxMsg.style.display = '';
} else {
if (wrap) { wrap.style.display = ''; if (search) search.style.display = ''; }
if (maxMsg) maxMsg.style.display = 'none';
}
}
pills.addEventListener('click', function (e) {
var btn = e.target.closest('.tag-pill-remove');
if (!btn) return;
var pill = btn.closest('.tag-pill');
pill.remove();
updateCount();
var wrap = container.querySelector('.tag-search-input-wrap');
var inp = container.querySelector('.tag-search-input');
if (wrap && inp) { wrap.style.display = ''; inp.style.display = ''; }
});
function highlight(idx) {
var items = dropdown.querySelectorAll('.tag-search-item');
for (var i = 0; i < items.length; i++) {
items[i].classList.toggle('tag-search-item--highlight', i === idx);
}
}
function selectPill(btn) {
var name = normalize(btn.getAttribute('data-tag-name') || '');
if (!name) return;
if (pillAlreadyExists(name)) return;
if ((pills.querySelectorAll('.tag-pill').length) >= maxTags) return;
var escaped = htmlEscape(name);
var pill = document.createElement('span');
pill.className = 'tag-pill';
pill.innerHTML = '<input type="hidden" name="' + inputName + '[]" value="' + escaped + '">'
+ '<span class="tag-pill-name">' + escaped + '</span>'
+ '<button type="button" class="tag-pill-remove" title="Retirer \u00AB\u00A0' + escaped + '\u00A0\u00BB" aria-label="Retirer ' + escaped + '">'
+ '<svg width="16" height="16" fill="currentColor" viewBox="0 0 256 256"><path d="M216,48H176V40a24,24,0,0,0-24-24H104A24,24,0,0,0,80,40v8H40a8,8,0,0,0,0,16h8V208a16,16,0,0,0,16,16H192a16,16,0,0,0,16-16V64h8a8,8,0,0,0,0-16ZM112,168V104a8,8,0,0,1,16,0v64a8,8,0,0,1-16,0Zm48,0V104a8,8,0,0,1,16,0v64a8,8,0,0,1-16,0Z"></path></svg>'
+ '</button>';
pills.appendChild(pill);
updateCount();
search.value = '';
dropdown.innerHTML = '';
selectedIdx = -1;
search.focus();
}
dropdown.addEventListener('click', function (e) {
var btn = e.target.closest('.tag-search-item');
if (!btn) return;
selectPill(btn);
});
search.addEventListener('keydown', function (e) {
var items = dropdown.querySelectorAll('.tag-search-item');
if (e.key === 'ArrowDown' || e.key === 'ArrowUp') {
e.preventDefault();
if (items.length === 0) return;
if (e.key === 'ArrowDown') {
selectedIdx = (selectedIdx + 1) % items.length;
} else {
selectedIdx = selectedIdx <= 0 ? items.length - 1 : selectedIdx - 1;
}
highlight(selectedIdx);
} else if (e.key === 'Enter') {
if (items.length > 0) {
e.preventDefault();
if (selectedIdx >= 0 && selectedIdx < items.length) {
selectPill(items[selectedIdx]);
} else {
selectPill(items[0]);
}
}
} else if (e.key === 'Escape') {
dropdown.innerHTML = '';
selectedIdx = -1;
}
});
search.addEventListener('blur', function () {
setTimeout(function () {
if (!dropdown.contains(document.activeElement)) {
dropdown.innerHTML = '';
selectedIdx = -1;
}
}, 150);
});
function htmlEscape(str) {
var el = document.createElement('span');
el.textContent = str;
return el.innerHTML;
}
}
})();

View File

@@ -1,9 +0,0 @@
/*!
* FilePondPluginFileValidateSize 2.2.8
* Licensed under MIT, https://opensource.org/licenses/MIT/
* Please visit https://pqina.nl/filepond/ for details.
*/
/* eslint-disable */
!function(e,i){"object"==typeof exports&&"undefined"!=typeof module?module.exports=i():"function"==typeof define&&define.amd?define(i):(e=e||self).FilePondPluginFileValidateSize=i()}(this,function(){"use strict";var e=function(e){var i=e.addFilter,E=e.utils,l=E.Type,_=E.replaceInString,n=E.toNaturalFileSize;return i("ALLOW_HOPPER_ITEM",function(e,i){var E=i.query;if(!E("GET_ALLOW_FILE_SIZE_VALIDATION"))return!0;var l=E("GET_MAX_FILE_SIZE");if(null!==l&&e.size>l)return!1;var _=E("GET_MIN_FILE_SIZE");return!(null!==_&&e.size<_)}),i("LOAD_FILE",function(e,i){var E=i.query;return new Promise(function(i,l){if(!E("GET_ALLOW_FILE_SIZE_VALIDATION"))return i(e);var I=E("GET_FILE_VALIDATE_SIZE_FILTER");if(I&&!I(e))return i(e);var t=E("GET_MAX_FILE_SIZE");if(null!==t&&e.size>t)l({status:{main:E("GET_LABEL_MAX_FILE_SIZE_EXCEEDED"),sub:_(E("GET_LABEL_MAX_FILE_SIZE"),{filesize:n(t,".",E("GET_FILE_SIZE_BASE"),E("GET_FILE_SIZE_LABELS",E))})}});else{var L=E("GET_MIN_FILE_SIZE");if(null!==L&&e.size<L)l({status:{main:E("GET_LABEL_MIN_FILE_SIZE_EXCEEDED"),sub:_(E("GET_LABEL_MIN_FILE_SIZE"),{filesize:n(L,".",E("GET_FILE_SIZE_BASE"),E("GET_FILE_SIZE_LABELS",E))})}});else{var a=E("GET_MAX_TOTAL_FILE_SIZE");if(null!==a)if(E("GET_ACTIVE_ITEMS").reduce(function(e,i){return e+i.fileSize},0)>a)return void l({status:{main:E("GET_LABEL_MAX_TOTAL_FILE_SIZE_EXCEEDED"),sub:_(E("GET_LABEL_MAX_TOTAL_FILE_SIZE"),{filesize:n(a,".",E("GET_FILE_SIZE_BASE"),E("GET_FILE_SIZE_LABELS",E))})}});i(e)}}})}),{options:{allowFileSizeValidation:[!0,l.BOOLEAN],maxFileSize:[null,l.INT],minFileSize:[null,l.INT],maxTotalFileSize:[null,l.INT],fileValidateSizeFilter:[null,l.FUNCTION],labelMinFileSizeExceeded:["File is too small",l.STRING],labelMinFileSize:["Minimum file size is {filesize}",l.STRING],labelMaxFileSizeExceeded:["File is too large",l.STRING],labelMaxFileSize:["Maximum file size is {filesize}",l.STRING],labelMaxTotalFileSizeExceeded:["Maximum total size exceeded",l.STRING],labelMaxTotalFileSize:["Maximum total file size is {filesize}",l.STRING]}}};return"undefined"!=typeof window&&void 0!==window.document&&document.dispatchEvent(new CustomEvent("FilePond:pluginloaded",{detail:e})),e});

View File

@@ -1,9 +0,0 @@
/*!
* FilePondPluginFileValidateType 1.2.9
* Licensed under MIT, https://opensource.org/licenses/MIT/
* Please visit https://pqina.nl/filepond/ for details.
*/
/* eslint-disable */
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e=e||self).FilePondPluginFileValidateType=t()}(this,function(){"use strict";var e=function(e){var t=e.addFilter,n=e.utils,i=n.Type,T=n.isString,E=n.replaceInString,l=n.guesstimateMimeType,o=n.getExtensionFromFilename,r=n.getFilenameFromURL,u=function(e,t){return e.some(function(e){return/\*$/.test(e)?(n=e,(/^[^/]+/.exec(t)||[]).pop()===n.slice(0,-2)):e===t;var n})},a=function(e,t,n){if(0===t.length)return!0;var i=function(e){var t="";if(T(e)){var n=r(e),i=o(n);i&&(t=l(i))}else t=e.type;return t}(e);return n?new Promise(function(T,E){n(e,i).then(function(e){u(t,e)?T():E()}).catch(E)}):u(t,i)};return t("SET_ATTRIBUTE_TO_OPTION_MAP",function(e){return Object.assign(e,{accept:"acceptedFileTypes"})}),t("ALLOW_HOPPER_ITEM",function(e,t){var n=t.query;return!n("GET_ALLOW_FILE_TYPE_VALIDATION")||a(e,n("GET_ACCEPTED_FILE_TYPES"))}),t("LOAD_FILE",function(e,t){var n=t.query;return new Promise(function(t,i){if(n("GET_ALLOW_FILE_TYPE_VALIDATION")){var T=n("GET_ACCEPTED_FILE_TYPES"),l=n("GET_FILE_VALIDATE_TYPE_DETECT_TYPE"),o=a(e,T,l),r=function(){var e,t=T.map((e=n("GET_FILE_VALIDATE_TYPE_LABEL_EXPECTED_TYPES_MAP"),function(t){return null!==e[t]&&(e[t]||t)})).filter(function(e){return!1!==e}),l=t.filter(function(e,n){return t.indexOf(e)===n});i({status:{main:n("GET_LABEL_FILE_TYPE_NOT_ALLOWED"),sub:E(n("GET_FILE_VALIDATE_TYPE_LABEL_EXPECTED_TYPES"),{allTypes:l.join(", "),allButLastType:l.slice(0,-1).join(", "),lastType:l[l.length-1]})}})};if("boolean"==typeof o)return o?t(e):r();o.then(function(){t(e)}).catch(r)}else t(e)})}),{options:{allowFileTypeValidation:[!0,i.BOOLEAN],acceptedFileTypes:[[],i.ARRAY],labelFileTypeNotAllowed:["File is of invalid type",i.STRING],fileValidateTypeLabelExpectedTypes:["Expects {allButLastType} or {lastType}",i.STRING],fileValidateTypeLabelExpectedTypesMap:[{},i.OBJECT],fileValidateTypeDetectType:[null,i.FUNCTION]}}};return"undefined"!=typeof window&&void 0!==window.document&&document.dispatchEvent(new CustomEvent("FilePond:pluginloaded",{detail:e})),e});

View File

@@ -1,9 +0,0 @@
/*!
* FilePondPluginImageExifOrientation 1.0.11
* Licensed under MIT, https://opensource.org/licenses/MIT/
* Please visit https://pqina.nl/filepond/ for details.
*/
/* eslint-disable */
!function(A,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):(A=A||self).FilePondPluginImageExifOrientation=e()}(this,function(){"use strict";var A=65496,e=65505,n=1165519206,t=18761,i=274,r=65280,o=function(A,e){var n=arguments.length>2&&void 0!==arguments[2]&&arguments[2];return A.getUint16(e,n)},a=function(A,e){var n=arguments.length>2&&void 0!==arguments[2]&&arguments[2];return A.getUint32(e,n)},u="undefined"!=typeof window&&void 0!==window.document,d=void 0,f=u?new Image:{};f.onload=function(){return d=f.naturalWidth>f.naturalHeight},f.src="data:image/jpg;base64,/9j/4AAQSkZJRgABAQEASABIAAD/4QA6RXhpZgAATU0AKgAAAAgAAwESAAMAAAABAAYAAAEoAAMAAAABAAIAAAITAAMAAAABAAEAAAAAAAD/2wBDAP//////////////////////////////////////////////////////////////////////////////////////wAALCAABAAIBASIA/8QAJgABAAAAAAAAAAAAAAAAAAAAAxABAAAAAAAAAAAAAAAAAAAAAP/aAAgBAQAAPwBH/9k=";var l=function(u){var f=u.addFilter,l=u.utils,c=l.Type,g=l.isFile;return f("DID_LOAD_ITEM",function(u,f){var l=f.query;return new Promise(function(f,c){var s=u.file;if(!(g(s)&&function(A){return/^image\/jpeg/.test(A.type)}(s)&&l("GET_ALLOW_IMAGE_EXIF_ORIENTATION")&&d))return f(u);(function(u){return new Promise(function(d,f){var l=new FileReader;l.onload=function(u){var f=new DataView(u.target.result);if(o(f,0)===A){for(var l=f.byteLength,c=2;c<l;){var g=o(f,c);if(c+=2,g===e){if(a(f,c+=2)!==n)break;var s=o(f,c+=6)===t;c+=a(f,c+4,s);var v=o(f,c,s);c+=2;for(var w=0;w<v;w++)if(o(f,c+12*w,s)===i)return void d(o(f,c+12*w+8,s))}else{if((g&r)!==r)break;c+=o(f,c)}}d(-1)}else d(-1)},l.readAsArrayBuffer(u.slice(0,65536))})})(s).then(function(A){u.setMetadata("exif",{orientation:A}),f(u)})})}),{options:{allowImageExifOrientation:[!0,c.BOOLEAN]}}};return"undefined"!=typeof window&&void 0!==window.document&&document.dispatchEvent(new CustomEvent("FilePond:pluginloaded",{detail:l})),l});

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -396,15 +396,16 @@ function renderShareLinkForm(string $slug, array $link): void
<link rel="stylesheet" href="<?= App::assetV('/assets/css/form.css') ?>">
<link rel="stylesheet" href="<?= App::assetV('/assets/css/filepond.min.css') ?>">
<link rel="stylesheet" href="<?= App::assetV('/assets/css/filepond-plugin-image-preview.min.css') ?>">
<script src="<?= App::assetV('/assets/js/filepond.min.js') ?>" defer></script>
<script src="<?= App::assetV('/assets/js/filepond-plugin-file-validate-type.min.js') ?>" defer></script>
<script src="<?= App::assetV('/assets/js/filepond-plugin-file-validate-size.min.js') ?>" defer></script>
<script src="<?= App::assetV('/assets/js/filepond-plugin-image-preview.min.js') ?>" defer></script>
<script src="<?= App::assetV('/assets/js/filepond-plugin-image-exif-orientation.min.js') ?>" defer></script>
<script src="<?= App::assetV('/assets/js/file-upload-filepond.js') ?>" defer></script>
<script src="<?= App::assetV('/assets/js/beforeunload-guard.js') ?>" defer></script>
<script src="<?= App::assetV('/assets/js/upload-progress.js') ?>" defer></script>
<script src="<?= App::assetV('/assets/js/htmx.min.js') ?>" defer></script>
<script src="<?= App::assetV('/assets/js/vendor/filepond.min.js') ?>" defer></script>
<script src="<?= App::assetV('/assets/js/vendor/filepond-plugin-file-validate-type.min.js') ?>" defer></script>
<script src="<?= App::assetV('/assets/js/vendor/filepond-plugin-file-validate-size.min.js') ?>" defer></script>
<script src="<?= App::assetV('/assets/js/vendor/filepond-plugin-image-preview.min.js') ?>" defer></script>
<script src="<?= App::assetV('/assets/js/vendor/filepond-plugin-image-exif-orientation.min.js') ?>" defer></script>
<script src="<?= App::assetV('/assets/js/app/file-upload-filepond.js') ?>" defer></script>
<script src="<?= App::assetV('/assets/js/app/beforeunload-guard.js') ?>" defer></script>
<script src="<?= App::assetV('/assets/js/app/upload-progress.js') ?>" defer></script>
<script src="<?= App::assetV('/assets/js/app/pill-search.js') ?>" defer></script>
<script src="<?= App::assetV('/assets/js/vendor/htmx.min.js') ?>" defer></script>
</head>
<body class="student-body">
<main id="main-content">