Compare commits
16 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 81a3967b0e | |||
| 14bae6c9ef | |||
| 43bc416554 | |||
| b81164279f | |||
| 345d22958f | |||
| 20805c11db | |||
| 957d52dd21 | |||
| f8c8725622 | |||
| f8fba4275d | |||
| 5912bc29ac | |||
| 36944c71bf | |||
| 91c0a2d9d9 | |||
| 0ff0c0d55e | |||
| 1358e17ac3 | |||
| 4ce1578bb5 | |||
| 72dac40753 |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -1 +1,3 @@
|
||||
config.php
|
||||
.vscode
|
||||
sftp.json
|
||||
@@ -1,113 +1,250 @@
|
||||
<?php
|
||||
|
||||
require('config.php');
|
||||
|
||||
require('vendor/autoload.php');
|
||||
|
||||
|
||||
|
||||
?>
|
||||
|
||||
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<!-- The above 3 meta tags *must* come first in the head; any other head content must come *after* these tags -->
|
||||
<title>Aufgabenmonitor</title>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Aufgabenmonitor</title>
|
||||
|
||||
<!-- Bootstrap -->
|
||||
<link rel="stylesheet" href="vendor/twbs/bootstrap/dist/css/bootstrap.min.css" >
|
||||
<script src="scripts/jquery-3.5.1.min.js"></script>
|
||||
<link href="css/sticky-footer.css" rel="stylesheet">
|
||||
<!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries -->
|
||||
<!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
|
||||
<!--[if lt IE 9]>
|
||||
<script src="https://oss.maxcdn.com/html5shiv/3.7.3/html5shiv.min.js"></script>
|
||||
<script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
|
||||
<![endif]-->
|
||||
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function(){
|
||||
refreshAufgabenTable();
|
||||
|
||||
});
|
||||
<!-- Bootstrap -->
|
||||
<link rel="stylesheet" href="vendor/twbs/bootstrap/dist/css/bootstrap.min.css">
|
||||
<link href="css/sticky-footer.css" rel="stylesheet">
|
||||
|
||||
function refreshAufgabenTable(){
|
||||
$('#AufgabenTableHolder').load('sources/getAufgabenTable.php', function(){
|
||||
setTimeout(refreshAufgabenTable, 30000);
|
||||
});
|
||||
}
|
||||
// function refreshCheckInTable(){
|
||||
// $('#getCheckInTableHolder').load('sources/getCheckInTable.php', function(){
|
||||
// setTimeout(refreshCheckInTable, 5000);
|
||||
// });
|
||||
// }
|
||||
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
</head>
|
||||
<body style="background-color: black;">
|
||||
<style>
|
||||
body { background-color: #000; }
|
||||
.tableFixHead { overflow:auto; }
|
||||
.tableFixHead thead th {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 1;
|
||||
background-color: #212529; /* .table-dark head */
|
||||
}
|
||||
.table-dark td, .table-dark th { vertical-align: middle; }
|
||||
</style>
|
||||
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-lg">
|
||||
<h2 class="text-light">Aufgaben</h2>
|
||||
<table class="table table-dark">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">#</th>
|
||||
<th scope="col">Bearbeiter</th>
|
||||
<th scope="col">Aufgabe</th>
|
||||
<th scope="col">Zieldatum</th>
|
||||
<th scope="col">Priorität</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="AufgabenTableHolder">
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</header>
|
||||
<script src="scripts/jquery-3.5.1.min.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="container-fluid py-3">
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<h2 class="text-light mb-3">Aufgaben</h2>
|
||||
|
||||
<!-- SCROLL CONTAINER -->
|
||||
<div id="aufgaben-scroll" class="tableFixHead">
|
||||
<table id="aufgaben-table" class="table table-dark table-striped table-hover mb-0">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col" style="width:6%">#</th>
|
||||
<th scope="col" style="width:24%">Bearbeiter</th>
|
||||
<th scope="col" style="width:40%">Aufgabe</th>
|
||||
<th scope="col" style="width:15%">Ziel</th>
|
||||
<th scope="col" style="width:15%">Prio</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="AufgabenTableHolder">
|
||||
<!-- wird via AJAX gefüllt -->
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<!-- /SCROLL CONTAINER -->
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Bootstrap JS -->
|
||||
<script src="vendor/twbs/bootstrap/dist/js/bootstrap.bundle.min.js"></script>
|
||||
|
||||
<!-- === Scroll/Reload Script (wie bei CheckIn/CheckOut) === -->
|
||||
<script type="text/javascript">
|
||||
/* ===========================
|
||||
Feature-Toggle aus PHP
|
||||
=========================== */
|
||||
const SCROLL_FLAGS = {
|
||||
aufgaben: <?php echo (defined('EnableScrollingAufgaben') && EnableScrollingAufgaben) ? 'true' : 'false'; ?>
|
||||
};
|
||||
|
||||
<!-- jQuery (necessary for Bootstrap's JavaScript plugins) -->
|
||||
<script src="https://code.jquery.com/jquery-1.12.4.min.js" integrity="sha384-nvAa0+6Qg9clwYCGGPpDQLVpLNn0fRaROjHqs13t4Ggj3Ez50XnGQqc/r8MhnRDZ" crossorigin="anonymous"></script>
|
||||
<!-- Include all compiled plugins (below), or include individual files as needed -->
|
||||
<script src="vendor/twbs/bootstrap/dist/js/bootstrap.min.js" integrity="sha384-aJ21OjlMXNL5UyIl/XNwTMqvzeRMZH2w8c5cRVpzpU8Y5bApTppSuUkhZXN0VxHd" crossorigin="anonymous"></script>
|
||||
</body>
|
||||
function isEnabled() { return !!SCROLL_FLAGS.aufgaben; }
|
||||
|
||||
/* ===========================
|
||||
Höhe der Scroll-Container
|
||||
- Voller Viewport ab Oberkante
|
||||
=========================== */
|
||||
function sizeScrollContainers() {
|
||||
const node = document.querySelector('#aufgaben-scroll');
|
||||
if (!node) return;
|
||||
const rect = node.getBoundingClientRect();
|
||||
const bottomMargin = 16;
|
||||
const minH = 120;
|
||||
const available = window.innerHeight - rect.top - bottomMargin;
|
||||
node.style.maxHeight = Math.max(minH, available) + 'px';
|
||||
}
|
||||
|
||||
/* ===========================
|
||||
Scroll-/Interaktionsstatus
|
||||
=========================== */
|
||||
const scrollState = new WeakMap(); // { userActive:boolean, autoTimer:number|null, loopHeight:number }
|
||||
|
||||
function ensureState(el) {
|
||||
if (!scrollState.get(el)) scrollState.set(el, { userActive: false, autoTimer: null, loopHeight: 0 });
|
||||
return scrollState.get(el);
|
||||
}
|
||||
|
||||
function attachScrollGuards($scroller) {
|
||||
const el = $scroller.get(0);
|
||||
if (!el || el.__guardsBound) return;
|
||||
if (!isEnabled()) return;
|
||||
const state = ensureState(el);
|
||||
|
||||
const markActive = () => {
|
||||
state.userActive = true;
|
||||
clearTimeout(state._quietT);
|
||||
state._quietT = setTimeout(() => { state.userActive = false; }, 800);
|
||||
stopAutoScroll($scroller);
|
||||
};
|
||||
|
||||
$scroller.on('wheel touchstart touchmove keydown mousedown mouseenter', markActive);
|
||||
$scroller.on('mouseleave', () => setTimeout(() => maybeStartAutoScroll($scroller), 600));
|
||||
el.__guardsBound = true;
|
||||
}
|
||||
|
||||
/* ===========================
|
||||
Nahtloser Loop (Clone)
|
||||
=========================== */
|
||||
function buildSeamlessLoop($table, $scroller) {
|
||||
$table.find('tbody.__loopClone').remove();
|
||||
|
||||
const scEl = $scroller.get(0);
|
||||
const st = ensureState(scEl);
|
||||
st.loopHeight = 0;
|
||||
|
||||
if (!isEnabled()) return;
|
||||
|
||||
const $main = $table.find('tbody').first();
|
||||
if ($main.length === 0 || $main.children().length === 0) return;
|
||||
|
||||
const needScroll = $main.get(0).offsetHeight > scEl.clientHeight + 1;
|
||||
if (!needScroll) return;
|
||||
|
||||
const $clone = $main.clone(false, false).addClass('__loopClone').attr('aria-hidden', 'true');
|
||||
$table.append($clone);
|
||||
st.loopHeight = $main.get(0).offsetHeight;
|
||||
}
|
||||
|
||||
/* ===========================
|
||||
Auto-Scroll
|
||||
=========================== */
|
||||
function startAutoScroll($scroller, speedPx = 1, stepMs = 40) {
|
||||
const el = $scroller.get(0);
|
||||
if (!isEnabled()) return;
|
||||
|
||||
const st = ensureState(el);
|
||||
if (st.autoTimer) return;
|
||||
if (st.loopHeight <= 0) return;
|
||||
|
||||
const tick = () => {
|
||||
if (st.userActive) { stopAutoScroll($scroller); return; }
|
||||
el.scrollTop += speedPx;
|
||||
if (el.scrollTop >= st.loopHeight) el.scrollTop -= st.loopHeight;
|
||||
};
|
||||
|
||||
st.autoTimer = setInterval(tick, stepMs);
|
||||
}
|
||||
|
||||
function stopAutoScroll($scroller) {
|
||||
const el = $scroller.get(0);
|
||||
const st = ensureState(el);
|
||||
if (st.autoTimer) {
|
||||
clearInterval(st.autoTimer);
|
||||
st.autoTimer = null;
|
||||
}
|
||||
}
|
||||
|
||||
function maybeStartAutoScroll($scroller) {
|
||||
const el = $scroller.get(0);
|
||||
if (!isEnabled()) { stopAutoScroll($scroller); return; }
|
||||
const st = ensureState(el);
|
||||
if (st.userActive) return;
|
||||
if (st.loopHeight > 0) startAutoScroll($scroller, 1, 80);
|
||||
else stopAutoScroll($scroller);
|
||||
}
|
||||
|
||||
/* ===========================
|
||||
Smart AJAX Reload
|
||||
=========================== */
|
||||
function smartLoad($scroller, $table, $target, url, intervalMs) {
|
||||
const scEl = $scroller.get(0);
|
||||
const st = ensureState(scEl);
|
||||
|
||||
const oldLoop = st.loopHeight > 0 ? st.loopHeight : Math.max(1, scEl.scrollHeight - scEl.clientHeight);
|
||||
const posInLoop = st.loopHeight > 0 ? (scEl.scrollTop % oldLoop) : scEl.scrollTop;
|
||||
const posRatio = Math.min(1, posInLoop / oldLoop);
|
||||
|
||||
$target.load(url, function () {
|
||||
buildSeamlessLoop($table, $scroller);
|
||||
|
||||
if (st.loopHeight > 0 && isEnabled()) {
|
||||
const newPos = Math.floor(posRatio * st.loopHeight);
|
||||
if (Math.abs(scEl.scrollTop - newPos) > 1) {
|
||||
requestAnimationFrame(() => { scEl.scrollTop = newPos; });
|
||||
}
|
||||
stopAutoScroll($scroller);
|
||||
startAutoScroll($scroller, 1, 80);
|
||||
} else {
|
||||
if (scEl.scrollTop !== 0) requestAnimationFrame(() => { scEl.scrollTop = 0; });
|
||||
stopAutoScroll($scroller);
|
||||
}
|
||||
|
||||
setTimeout(() => smartLoad($scroller, $table, $target, url, intervalMs), intervalMs);
|
||||
});
|
||||
}
|
||||
|
||||
/* ===========================
|
||||
Initialisierung
|
||||
=========================== */
|
||||
$(document).ready(function () {
|
||||
sizeScrollContainers();
|
||||
$(window).on('resize', function () {
|
||||
sizeScrollContainers();
|
||||
const $scroller = $('#aufgaben-scroll');
|
||||
const $table = $('#aufgaben-table');
|
||||
buildSeamlessLoop($table, $scroller);
|
||||
maybeStartAutoScroll($scroller);
|
||||
});
|
||||
|
||||
// Guards nur aktivieren, wenn Scrolling erlaubt
|
||||
if (isEnabled()) attachScrollGuards($('#aufgaben-scroll'));
|
||||
|
||||
// Erstladen
|
||||
(function init() {
|
||||
const $scroller = $('#aufgaben-scroll');
|
||||
const $table = $('#aufgaben-table');
|
||||
const $target = $('#AufgabenTableHolder');
|
||||
const url = 'sources/getAufgabenTable.php';
|
||||
const interval = 60000;
|
||||
|
||||
$target.load(url, function () {
|
||||
buildSeamlessLoop($table, $scroller);
|
||||
maybeStartAutoScroll($scroller);
|
||||
setTimeout(() => smartLoad($scroller, $table, $target, url, interval), interval);
|
||||
});
|
||||
})();
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
||||
<?php
|
||||
|
||||
// (Optional – hier nicht benötigt, aber gelassen falls du später etwas anzeigst)
|
||||
function getTimeFromSeconds(string $timestring) {
|
||||
|
||||
$hours = floor($timestring / 3600);
|
||||
$mins = floor($timestring / 60 % 60);
|
||||
$secs = floor($timestring % 60);
|
||||
|
||||
$timeFormat = sprintf('%02d:%02d', $hours, $mins);
|
||||
|
||||
return $timeFormat;
|
||||
$hours = floor($timestring / 3600);
|
||||
$mins = floor($timestring / 60 % 60);
|
||||
$timeFormat = sprintf('%02d:%02d', $hours, $mins);
|
||||
return $timeFormat;
|
||||
}
|
||||
?>
|
||||
?>
|
||||
|
||||
@@ -12,7 +12,7 @@ public function requestEpiApi(string $requestString){
|
||||
$requestUrl = Epirent_Connectionprotocol."://".Epirent_Server.":".Epirent_Port.$requestString;
|
||||
$response = $client->request('GET', $requestUrl,[
|
||||
'headers' =>[
|
||||
'X-EPI-NO-SESSION' => 'True',
|
||||
'X-EPI-NO-SESSION' => 'true',
|
||||
'X-EPI-ACC-TOK' => Epirent_Token
|
||||
]
|
||||
]);
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
<?php
|
||||
|
||||
require('config.php');
|
||||
require('EpiApi.php');
|
||||
require('vendor/autoload.php');
|
||||
|
||||
$Epi = new Epirent();
|
||||
|
||||
?>
|
||||
|
||||
|
||||
@@ -21,155 +19,418 @@ $Epi = new Epirent();
|
||||
<!-- Bootstrap -->
|
||||
<link rel="stylesheet" href="vendor/twbs/bootstrap/dist/css/bootstrap.min.css" >
|
||||
<script src="scripts/jquery-3.5.1.min.js"></script>
|
||||
<link href="css/sticky-footer.css" rel="stylesheet">
|
||||
<!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries -->
|
||||
<!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
|
||||
<!--[if lt IE 9]>
|
||||
<script src="https://oss.maxcdn.com/html5shiv/3.7.3/html5shiv.min.js"></script>
|
||||
<script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
|
||||
<![endif]-->
|
||||
|
||||
<link href="css/sticky-footer.css" rel="stylesheet">
|
||||
<script src="https://kit.fontawesome.com/93d71de8bc.js" crossorigin="anonymous"></script>
|
||||
<style>
|
||||
@media (min-width: 1800px) {
|
||||
.row {
|
||||
flex-wrap: nowrap; /* verhindert Umbruch */
|
||||
}
|
||||
|
||||
/* Checkout + Checkin: je 45% */
|
||||
.row > .col-sm:nth-child(1),
|
||||
.row > .col-sm:nth-child(2) {
|
||||
flex: 0 0 35%;
|
||||
max-width: 35%;
|
||||
}
|
||||
|
||||
/* Aufgaben: 10% */
|
||||
.row > .col-sm:nth-child(3) {
|
||||
flex: 0 0 30%;
|
||||
max-width: 30%;
|
||||
}
|
||||
}
|
||||
.table td, .table th {
|
||||
padding: .25rem;
|
||||
}
|
||||
|
||||
|
||||
</style>
|
||||
|
||||
<script type="text/javascript">
|
||||
// Dynamische Höhe: Wrapper exakt bis Viewport-Unterkante
|
||||
// === PHP-Flags in JS bringen ===
|
||||
const SCROLL_FLAGS = {
|
||||
checkout: <?php echo (defined('EnableScrollingCheckOut') && EnableScrollingCheckOut) ? 'true' : 'false'; ?>,
|
||||
checkin: <?php echo (defined('EnableScrollingCheckIn') && EnableScrollingCheckIn) ? 'true' : 'false'; ?>,
|
||||
aufgaben: <?php echo (defined('EnableScrollingAufgaben') && EnableScrollingAufgaben) ? 'true' : 'false'; ?>
|
||||
};
|
||||
|
||||
// Mapping: DOM-ID -> Flag-Key
|
||||
const SCROLLER_KEYS = new Map([
|
||||
['checkout-scroll', 'checkout'],
|
||||
['checkin-scroll', 'checkin'],
|
||||
['aufgaben-scroll', 'aufgaben']
|
||||
]);
|
||||
const TABLE_KEYS = new Map([
|
||||
['checkout-table', 'checkout'],
|
||||
['checkin-table', 'checkin'],
|
||||
['aufgaben-table', 'aufgaben']
|
||||
]);
|
||||
|
||||
function keyForEl(el, map) {
|
||||
if (!el || !el.id) return null;
|
||||
return map.get(el.id) || null;
|
||||
}
|
||||
function isEnabledByEl(el) {
|
||||
const k = keyForEl(el, SCROLLER_KEYS);
|
||||
return !!(k && SCROLL_FLAGS[k]);
|
||||
}
|
||||
function isEnabledByKey(key) {
|
||||
return !!SCROLL_FLAGS[key];
|
||||
}
|
||||
|
||||
// === Höhe der Scroll-Container: nebeneinander = volle Höhe; gestapelt = Drittel ===
|
||||
function sizeScrollContainers() {
|
||||
$('.tableFixHead').each(function () {
|
||||
const rect = this.getBoundingClientRect();
|
||||
const bottomMargin = 16; // kleiner Abstand zum Rand
|
||||
const available = window.innerHeight - rect.top - bottomMargin;
|
||||
this.style.maxHeight = (available > 120 ? available : 120) + 'px';
|
||||
const nodes = [
|
||||
document.querySelector('#checkout-scroll'),
|
||||
document.querySelector('#checkin-scroll'),
|
||||
document.querySelector('#aufgaben-scroll')
|
||||
].filter(Boolean);
|
||||
|
||||
if (nodes.length === 0) return;
|
||||
|
||||
// Ermitteln, ob alles in einer Zeile (nebeneinander) oder gestapelt
|
||||
const tops = nodes.map(n => n.getBoundingClientRect().top);
|
||||
const ROW_TOL = 8; // px
|
||||
const uniqueRows = [];
|
||||
tops.forEach(t => {
|
||||
const exists = uniqueRows.some(rt => Math.abs(rt - t) <= ROW_TOL);
|
||||
if (!exists) uniqueRows.push(t);
|
||||
});
|
||||
const isOneRow = uniqueRows.length === 1;
|
||||
|
||||
const bottomMargin = 16;
|
||||
const minH = 120;
|
||||
|
||||
nodes.forEach(node => {
|
||||
// Wenn Scrolling für diesen Bereich deaktiviert ist, Höhe trotzdem setzen (wie gefordert)
|
||||
const rect = node.getBoundingClientRect();
|
||||
if (isOneRow) {
|
||||
const available = window.innerHeight - rect.top - bottomMargin;
|
||||
node.style.maxHeight = Math.max(minH, available) + 'px';
|
||||
} else {
|
||||
const third = Math.floor(window.innerHeight / 3) - bottomMargin;
|
||||
node.style.maxHeight = Math.max(minH, third) + 'px';
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Lädt tbody via AJAX und erhält die Scrollposition relativ zum unteren Rand
|
||||
function smartLoad($scroller, $target, url, intervalMs) {
|
||||
const scroller = $scroller.get(0);
|
||||
// Abstand vom unteren Rand merken (wichtiger als absolute scrollTop)
|
||||
const fromBottomBefore = scroller.scrollHeight - scroller.clientHeight - scroller.scrollTop;
|
||||
// === Scroll-/Interaktionsstatus je Scroller ===
|
||||
const scrollState = new WeakMap(); // { userActive:boolean, autoTimer:number|null, loopHeight:number }
|
||||
|
||||
function ensureState(el) {
|
||||
if (!scrollState.get(el)) scrollState.set(el, { userActive: false, autoTimer: null, loopHeight: 0 });
|
||||
return scrollState.get(el);
|
||||
}
|
||||
|
||||
function attachScrollGuards($scroller) {
|
||||
const el = $scroller.get(0);
|
||||
if (!el || el.__guardsBound) return;
|
||||
if (!isEnabledByEl(el)) return; // nur wenn Feature aktiv
|
||||
const state = ensureState(el);
|
||||
|
||||
const markActive = () => {
|
||||
state.userActive = true;
|
||||
clearTimeout(state._quietT);
|
||||
state._quietT = setTimeout(() => { state.userActive = false; }, 800);
|
||||
stopAutoScroll($scroller);
|
||||
};
|
||||
|
||||
$scroller.on('wheel touchstart touchmove keydown mousedown mouseenter', markActive);
|
||||
$scroller.on('mouseleave', () => setTimeout(() => maybeStartAutoScroll($scroller), 600));
|
||||
el.__guardsBound = true;
|
||||
}
|
||||
|
||||
// === Loop aufbauen: nahtloses Doppel nur bei Bedarf & nur wenn aktiv ===
|
||||
function buildSeamlessLoop($table, $scroller) {
|
||||
$table.find('tbody.__loopClone').remove();
|
||||
|
||||
const scEl = $scroller.get(0);
|
||||
const enabled = isEnabledByEl(scEl);
|
||||
const st = ensureState(scEl);
|
||||
st.loopHeight = 0;
|
||||
|
||||
if (!enabled) return; // deaktiviert -> keine Loops
|
||||
|
||||
const $main = $table.find('tbody').first();
|
||||
if ($main.length === 0 || $main.children().length === 0) return;
|
||||
|
||||
const needScroll = $main.get(0).offsetHeight > scEl.clientHeight + 1;
|
||||
if (!needScroll) return;
|
||||
|
||||
const $clone = $main.clone(false, false).addClass('__loopClone').attr('aria-hidden', 'true');
|
||||
$table.append($clone);
|
||||
st.loopHeight = $main.get(0).offsetHeight; // Höhe des Original-Inhalts als Loop-Länge
|
||||
}
|
||||
|
||||
// === Auto-Scroll (nahtlos) ===
|
||||
function startAutoScroll($scroller, speedPx = 1, stepMs = 40) {
|
||||
const el = $scroller.get(0);
|
||||
if (!isEnabledByEl(el)) return;
|
||||
|
||||
const st = ensureState(el);
|
||||
if (st.autoTimer) return; // schon aktiv
|
||||
if (st.loopHeight <= 0) return; // kein Overflow => nicht scrollen
|
||||
|
||||
const tick = () => {
|
||||
if (st.userActive) { stopAutoScroll($scroller); return; }
|
||||
el.scrollTop += speedPx;
|
||||
if (el.scrollTop >= st.loopHeight) {
|
||||
el.scrollTop -= st.loopHeight;
|
||||
}
|
||||
};
|
||||
|
||||
st.autoTimer = setInterval(tick, stepMs);
|
||||
}
|
||||
|
||||
function stopAutoScroll($scroller) {
|
||||
const el = $scroller.get(0);
|
||||
const st = ensureState(el);
|
||||
if (st.autoTimer) {
|
||||
clearInterval(st.autoTimer);
|
||||
st.autoTimer = null;
|
||||
}
|
||||
}
|
||||
|
||||
function maybeStartAutoScroll($scroller) {
|
||||
const el = $scroller.get(0);
|
||||
if (!isEnabledByEl(el)) { stopAutoScroll($scroller); return; }
|
||||
const st = ensureState(el);
|
||||
if (st.userActive) return;
|
||||
if (st.loopHeight > 0) startAutoScroll($scroller, 1, 80);
|
||||
else stopAutoScroll($scroller);
|
||||
}
|
||||
|
||||
// === AJAX-Reload: relative Position innerhalb der Loop erhalten ===
|
||||
function smartLoad($scroller, $table, $target, url, intervalMs) {
|
||||
const scEl = $scroller.get(0);
|
||||
const st = ensureState(scEl);
|
||||
const enabled = isEnabledByEl(scEl);
|
||||
|
||||
const oldLoop = st.loopHeight > 0 ? st.loopHeight : Math.max(1, scEl.scrollHeight - scEl.clientHeight);
|
||||
const posInLoop = st.loopHeight > 0 ? (scEl.scrollTop % oldLoop) : scEl.scrollTop;
|
||||
const posRatio = Math.min(1, posInLoop / oldLoop);
|
||||
|
||||
$target.load(url, function () {
|
||||
// Nach dem Ersetzen: dieselbe Distanz zum unteren Rand wiederherstellen
|
||||
const newScrollTop = scroller.scrollHeight - scroller.clientHeight - fromBottomBefore;
|
||||
// Begrenzen, falls weniger Inhalt
|
||||
scroller.scrollTop = Math.max(0, newScrollTop);
|
||||
if (enabled) {
|
||||
buildSeamlessLoop($table, $scroller);
|
||||
if (st.loopHeight > 0) {
|
||||
const newPos = Math.floor(posRatio * st.loopHeight);
|
||||
if (Math.abs(scEl.scrollTop - newPos) > 1) {
|
||||
requestAnimationFrame(() => { scEl.scrollTop = newPos; });
|
||||
}
|
||||
stopAutoScroll($scroller);
|
||||
startAutoScroll($scroller, 1, 80);
|
||||
} else {
|
||||
if (scEl.scrollTop !== 0) requestAnimationFrame(() => { scEl.scrollTop = 0; });
|
||||
stopAutoScroll($scroller);
|
||||
}
|
||||
} else {
|
||||
$table.find('tbody.__loopClone').remove();
|
||||
stopAutoScroll($scroller);
|
||||
if (scEl.scrollTop !== 0) requestAnimationFrame(() => { scEl.scrollTop = 0; });
|
||||
}
|
||||
|
||||
// Nächstes Update planen
|
||||
setTimeout(function () {
|
||||
smartLoad($scroller, $target, url, intervalMs);
|
||||
}, intervalMs);
|
||||
setTimeout(() => smartLoad($scroller, $table, $target, url, intervalMs), intervalMs);
|
||||
});
|
||||
}
|
||||
|
||||
// === Initialisierung ===
|
||||
$(document).ready(function () {
|
||||
sizeScrollContainers();
|
||||
$(window).on('resize', sizeScrollContainers);
|
||||
|
||||
// Initial laden + Auto-Refresh, jeweils eigener Scroller
|
||||
smartLoad($('#checkout-scroll'), $('#getCheckOutTableHolder'), 'sources/getCheckOutTable.php', 5000);
|
||||
smartLoad($('#checkin-scroll'), $('#getCheckInTableHolder'), 'sources/getCheckInTable.php', 5000);
|
||||
smartLoad($('#aufgaben-scroll'), $('#AufgabenTableHolder'), 'sources/getAufgabenTable.php', 30000);
|
||||
$(window).on('resize', function () {
|
||||
sizeScrollContainers();
|
||||
['#checkout', '#checkin', '#aufgaben'].forEach(prefix => {
|
||||
const $scroller = $(`${prefix}-scroll`);
|
||||
const $table = $(`${prefix}-table`);
|
||||
buildSeamlessLoop($table, $scroller);
|
||||
maybeStartAutoScroll($scroller);
|
||||
});
|
||||
});
|
||||
|
||||
// Guards nur für aktivierte Scroller
|
||||
[['#checkout-scroll','checkout'], ['#checkin-scroll','checkin'], ['#aufgaben-scroll','aufgaben']].forEach(([sel,key]) => {
|
||||
if (isEnabledByKey(key)) attachScrollGuards($(sel));
|
||||
});
|
||||
|
||||
// Erstladen je Tabelle
|
||||
function initOne(scrollerSel, tableSel, tbodySel, url, ms, key) {
|
||||
const $scroller = $(scrollerSel);
|
||||
const $table = $(tableSel);
|
||||
const $target = $(tbodySel);
|
||||
const enabled = isEnabledByKey(key);
|
||||
|
||||
$target.load(url, function () {
|
||||
if (enabled) {
|
||||
buildSeamlessLoop($table, $scroller);
|
||||
maybeStartAutoScroll($scroller);
|
||||
} else {
|
||||
$table.find('tbody.__loopClone').remove();
|
||||
stopAutoScroll($scroller);
|
||||
}
|
||||
setTimeout(() => smartLoad($scroller, $table, $target, url, ms), ms);
|
||||
});
|
||||
}
|
||||
|
||||
initOne('#checkout-scroll', '#checkout-table', '#getCheckOutTableHolder', 'sources/getCheckOutTable.php', 10000, 'checkout');
|
||||
initOne('#checkin-scroll', '#checkin-table', '#getCheckInTableHolder', 'sources/getCheckInTable.php', 10000, 'checkin');
|
||||
initOne('#aufgaben-scroll', '#aufgaben-table', '#AufgabenTableHolder', 'sources/getAufgabenTable.php', 60000, 'aufgaben');
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
|
||||
<style>
|
||||
/* Scroll-Wrapper je Tabelle */
|
||||
.tableFixHead {
|
||||
overflow-y: auto;
|
||||
/* Höhe wird per JS dynamisch gesetzt, damit genau bis zum Viewport-Ende gescrollt wird */
|
||||
max-height: 60vh; /* Fallback */
|
||||
border: 1px solid rgba(255,255,255,.1);
|
||||
border-radius: .25rem;
|
||||
}
|
||||
/* Scroll-Wrapper je Tabelle */
|
||||
.tableFixHead {
|
||||
overflow-y: auto;
|
||||
/* Höhe wird per JS dynamisch gesetzt, damit genau bis zum Viewport-Ende gescrollt wird */
|
||||
max-height: 60vh; /* Fallback */
|
||||
border: 1px solid rgba(255,255,255,.1);
|
||||
border-radius: .25rem;
|
||||
}
|
||||
|
||||
/* Sticky Header */
|
||||
.tableFixHead thead th {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 2; /* über Body-Zellen */
|
||||
}
|
||||
/* Sticky Header */
|
||||
.tableFixHead thead th {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 2; /* über Body-Zellen */
|
||||
}
|
||||
|
||||
/* Saubere Hintergrundfarbe für sticky Header (Bootstrap .table-dark) */
|
||||
.table-dark thead th {
|
||||
background-color: #212529; /* gleiche Farbe wie .table-dark header */
|
||||
}
|
||||
/* Saubere Hintergrundfarbe für sticky Header (Bootstrap .table-dark) */
|
||||
.table-dark thead th {
|
||||
background-color: #212529; /* gleiche Farbe wie .table-dark header */
|
||||
}
|
||||
|
||||
/* Optional: dünne Trennlinien */
|
||||
.table-dark tbody tr + tr td {
|
||||
border-top: 1px solid rgba(255,255,255,.08);
|
||||
}
|
||||
</style>
|
||||
/* Optional: dünne Trennlinien */
|
||||
.table-dark tbody tr + tr td {
|
||||
border-top: 1px solid rgba(255,255,255,.08);
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body style="background-color: black;">
|
||||
<body style="margin-bottom:0px; background-color: black;">
|
||||
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-sm">
|
||||
<h2 class="text-light">Check-Out</h2>
|
||||
<div class="tableFixHead" id="checkout-scroll">
|
||||
<table class="table table-dark mb-0" id="checkout-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">#</th>
|
||||
<th scope="col">Kunde</th>
|
||||
<th scope="col">Event</th>
|
||||
<th scope="col">Dispo-Start<br><i>VB-Start</i></th>
|
||||
<?php
|
||||
|
||||
if(!HideCheckInTimeOnCheckout){
|
||||
echo "<th scope='col'>Dispo-Ende<br><i>VB-Ende</i></th>";
|
||||
}
|
||||
?>
|
||||
<th scope="col">Status</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="getCheckOutTableHolder"></tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-sm">
|
||||
<h2 class="text-light">Check-In</h2>
|
||||
<div class="tableFixHead" id="checkin-scroll">
|
||||
<table class="table table-dark mb-0" id="checkin-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">#</th>
|
||||
<th scope="col">Kunde</th>
|
||||
<th scope="col">Event</th>
|
||||
<?php
|
||||
|
||||
if(!HideCheckOutTimeOnCheckin){
|
||||
echo "<th scope='col'>Dispo-Start<br><i>RP-Start</i></th>";
|
||||
}
|
||||
?>
|
||||
<th scope="col">Dispo-Ende<br><i>RP-Ende</i></th>
|
||||
<th scope="col">Status</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="getCheckInTableHolder"></tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-sm">
|
||||
<h2 class="text-light">Aufgaben</h2>
|
||||
<div class="tableFixHead" id="aufgaben-scroll">
|
||||
<table class="table table-dark mb-0" id="aufgaben-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">#</th>
|
||||
<th scope="col">Bearbeiter</th>
|
||||
<th scope="col">Aufgabe</th>
|
||||
<th scope="col">Zieldatum</th>
|
||||
<th scope="col">Priorität</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="AufgabenTableHolder"></tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-sm" style="padding: 5px !important;">
|
||||
<h2 class="text-light">Check-Out</h2>
|
||||
<div class="tableFixHead" id="checkout-scroll">
|
||||
<table class="table table-dark mb-0" id="checkout-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">#</th>
|
||||
<th scope="col">Kunde</th>
|
||||
<th scope="col">Event</th>
|
||||
|
||||
<?php
|
||||
if(ShowCheckoutTimeOnCheckout || ShowVorbereitungTimeOnCheckout||ShowPackagingTimeOnCheckout || ShowDeliveryTimeOnCheckout){
|
||||
echo '<th scope="col">';
|
||||
}
|
||||
|
||||
if(ShowCheckoutTimeOnCheckout){
|
||||
echo "Dispo-Start";
|
||||
}
|
||||
if(ShowVorbereitungTimeOnCheckout){
|
||||
if(ShowCheckoutTimeOnCheckout){echo "<br>";}
|
||||
echo "VB-Start";
|
||||
}
|
||||
if(ShowPackagingTimeOnCheckout){
|
||||
if(ShowCheckoutTimeOnCheckout||ShowVorbereitungTimeOnCheckout){echo "<br>";}
|
||||
echo "Packen";
|
||||
}
|
||||
if(ShowDeliveryTimeOnCheckout){
|
||||
if(ShowCheckoutTimeOnCheckout || ShowVorbereitungTimeOnCheckout||ShowPackagingTimeOnCheckout){echo "<br>";}
|
||||
echo "Liefern";
|
||||
}
|
||||
if(ShowCheckoutTimeOnCheckout || ShowVorbereitungTimeOnCheckout||ShowPackagingTimeOnCheckout || ShowDeliveryTimeOnCheckout){
|
||||
echo '</th>';
|
||||
}
|
||||
|
||||
?>
|
||||
<th scope="col">Status</th>
|
||||
<?php
|
||||
if(ShowShippingIcons){
|
||||
echo "<th scope='col'>Shipping</th>";
|
||||
}
|
||||
?>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="getCheckOutTableHolder"></tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="col-sm" style="padding: 5px !important;">
|
||||
<h2 class="text-light">Check-In</h2>
|
||||
<div class="tableFixHead" id="checkin-scroll">
|
||||
<table class="table table-dark mb-0" id="checkin-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">#</th>
|
||||
<th scope="col">Kunde</th>
|
||||
<th scope="col">Event</th> <?php
|
||||
if(ShowCheckInTimeOnCheckin || ShowNachbereitungTimeOnCheckin ||ShowRePackagingTimeOnCheckin || ShowReDeliveryTimeOnCheckin){
|
||||
echo '<th scope="col">';
|
||||
}
|
||||
|
||||
if(ShowCheckInTimeOnCheckin){
|
||||
echo "Dispo-Ende";
|
||||
}
|
||||
if(ShowNachbereitungTimeOnCheckin){
|
||||
if(ShowCheckInTimeOnCheckin){echo "<br>";}
|
||||
echo "Nachbereitung";
|
||||
}
|
||||
if(ShowRePackagingTimeOnCheckin){
|
||||
if(ShowCheckInTimeOnCheckin||ShowNachbereitungTimeOnCheckin){echo "<br>";}
|
||||
echo "Zurückpacken";
|
||||
}
|
||||
if(ShowReDeliveryTimeOnCheckin){
|
||||
if(ShowCheckInTimeOnCheckin || ShowNachbereitungTimeOnCheckin||ShowRePackagingTimeOnCheckin){echo "<br>";}
|
||||
echo "Rückliefern";
|
||||
}
|
||||
if(ShowCheckInTimeOnCheckin || ShowNachbereitungTimeOnCheckin||ShowRePackagingTimeOnCheckin || ShowReDeliveryTimeOnCheckin){
|
||||
echo '</th>';
|
||||
}
|
||||
|
||||
?>
|
||||
|
||||
|
||||
|
||||
<th scope="col">Status</th>
|
||||
<?php
|
||||
if(ShowShippingIcons){
|
||||
echo "<th scope='col'>Shipping</th>";
|
||||
}
|
||||
?>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="getCheckInTableHolder"></tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-sm" style="padding: 5px !important;">
|
||||
<h2 class="text-light">Aufgaben</h2>
|
||||
<div class="tableFixHead" id="aufgaben-scroll">
|
||||
<table class="table table-dark mb-0" id="aufgaben-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">#</th>
|
||||
<th scope="col">Bearbeiter</th>
|
||||
<th scope="col">Aufgabe</th>
|
||||
<th scope="col">Ziel</th>
|
||||
<th scope="col">Prio</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="AufgabenTableHolder"></tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -182,7 +443,6 @@ $Epi = new Epirent();
|
||||
|
||||
|
||||
<!-- jQuery (necessary for Bootstrap's JavaScript plugins) -->
|
||||
<script src="https://code.jquery.com/jquery-1.12.4.min.js" integrity="sha384-nvAa0+6Qg9clwYCGGPpDQLVpLNn0fRaROjHqs13t4Ggj3Ez50XnGQqc/r8MhnRDZ" crossorigin="anonymous"></script>
|
||||
<!-- Include all compiled plugins (below), or include individual files as needed -->
|
||||
<script src="vendor/twbs/bootstrap/dist/js/bootstrap.min.js" integrity="sha384-aJ21OjlMXNL5UyIl/XNwTMqvzeRMZH2w8c5cRVpzpU8Y5bApTppSuUkhZXN0VxHd" crossorigin="anonymous"></script>
|
||||
</body>
|
||||
|
||||
495
Packmonitor.php
495
Packmonitor.php
@@ -1,9 +1,4 @@
|
||||
<?php
|
||||
|
||||
|
||||
error_reporting(E_ALL);
|
||||
|
||||
|
||||
require('config.php');
|
||||
require('EpiApi.php');
|
||||
require('vendor/autoload.php');
|
||||
@@ -14,143 +9,383 @@ $Epi = new Epirent();
|
||||
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<!-- The above 3 meta tags *must* come first in the head; any other head content must come *after* these tags -->
|
||||
<title>Packmonitor</title>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Packmonitor</title>
|
||||
|
||||
<!-- Bootstrap -->
|
||||
<link rel="stylesheet" href="vendor/twbs/bootstrap/dist/css/bootstrap.min.css" >
|
||||
<script src="scripts/jquery-3.5.1.min.js"></script>
|
||||
<link href="css/sticky-footer.css" rel="stylesheet">
|
||||
<!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries -->
|
||||
<!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
|
||||
<!--[if lt IE 9]>
|
||||
<script src="https://oss.maxcdn.com/html5shiv/3.7.3/html5shiv.min.js"></script>
|
||||
<script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
|
||||
<![endif]-->
|
||||
<!-- Bootstrap -->
|
||||
<link rel="stylesheet" href="vendor/twbs/bootstrap/dist/css/bootstrap.min.css">
|
||||
<script src="scripts/jquery-3.5.1.min.js"></script>
|
||||
<link href="css/sticky-footer.css" rel="stylesheet">
|
||||
<script src="https://kit.fontawesome.com/93d71de8bc.js" crossorigin="anonymous"></script>
|
||||
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function () {
|
||||
refreshCheckOutTable();
|
||||
refreshCheckInTable();
|
||||
});
|
||||
<script type="text/javascript">
|
||||
/* ===========================
|
||||
Feature-Toggles aus PHP
|
||||
=========================== */
|
||||
const SCROLL_FLAGS = {
|
||||
checkout: <?php echo (defined('EnableScrollingCheckOut') && EnableScrollingCheckOut) ? 'true' : 'false'; ?>,
|
||||
checkin: <?php echo (defined('EnableScrollingCheckIn') && EnableScrollingCheckIn) ? 'true' : 'false'; ?>
|
||||
};
|
||||
|
||||
function refreshCheckOutTable() {
|
||||
$('#getCheckOutTableHolder').load('sources/getCheckOutTable.php', function () {
|
||||
setTimeout(refreshCheckOutTable, 5000);
|
||||
});
|
||||
const SCROLLER_KEYS = new Map([
|
||||
['checkout-scroll', 'checkout'],
|
||||
['checkin-scroll', 'checkin']
|
||||
]);
|
||||
|
||||
function keyForEl(el) { return el && el.id ? SCROLLER_KEYS.get(el.id) : null; }
|
||||
function isEnabledByEl(el) { const k = keyForEl(el); return !!(k && SCROLL_FLAGS[k]); }
|
||||
function isEnabledByKey(key) { return !!SCROLL_FLAGS[key]; }
|
||||
|
||||
/* ===========================
|
||||
Höhe der Scroll-Container
|
||||
- 2 nebeneinander: volle Höhe bis Viewport-Ende
|
||||
- gestapelt: je 1/2 Fensterhöhe
|
||||
=========================== */
|
||||
function sizeScrollContainers() {
|
||||
const nodes = [
|
||||
document.querySelector('#checkout-scroll'),
|
||||
document.querySelector('#checkin-scroll')
|
||||
].filter(Boolean);
|
||||
|
||||
if (!nodes.length) return;
|
||||
|
||||
const tops = nodes.map(n => n.getBoundingClientRect().top);
|
||||
const ROW_TOL = 8;
|
||||
const firstTop = tops[0];
|
||||
const isOneRow = tops.every(t => Math.abs(t - firstTop) <= ROW_TOL);
|
||||
|
||||
const bottomMargin = 16;
|
||||
const minH = 120;
|
||||
|
||||
nodes.forEach(node => {
|
||||
const rect = node.getBoundingClientRect();
|
||||
if (isOneRow) {
|
||||
const available = window.innerHeight - rect.top - bottomMargin;
|
||||
node.style.maxHeight = Math.max(minH, available) + 'px';
|
||||
} else {
|
||||
const half = Math.floor(window.innerHeight / 2) - bottomMargin;
|
||||
node.style.maxHeight = Math.max(minH, half) + 'px';
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/* ===========================
|
||||
Scroll-/Interaktionsstatus
|
||||
=========================== */
|
||||
const scrollState = new WeakMap(); // { userActive:boolean, autoTimer:number|null, loopHeight:number }
|
||||
|
||||
function ensureState(el) {
|
||||
if (!scrollState.get(el)) scrollState.set(el, { userActive: false, autoTimer: null, loopHeight: 0 });
|
||||
return scrollState.get(el);
|
||||
}
|
||||
|
||||
function attachScrollGuards($scroller) {
|
||||
const el = $scroller.get(0);
|
||||
if (!el || el.__guardsBound) return;
|
||||
if (!isEnabledByEl(el)) return;
|
||||
const state = ensureState(el);
|
||||
|
||||
const markActive = () => {
|
||||
state.userActive = true;
|
||||
clearTimeout(state._quietT);
|
||||
state._quietT = setTimeout(() => { state.userActive = false; }, 800);
|
||||
stopAutoScroll($scroller);
|
||||
};
|
||||
|
||||
$scroller.on('wheel touchstart touchmove keydown mousedown mouseenter', markActive);
|
||||
$scroller.on('mouseleave', () => setTimeout(() => maybeStartAutoScroll($scroller), 600));
|
||||
el.__guardsBound = true;
|
||||
}
|
||||
|
||||
/* ===========================
|
||||
Nahtloser Loop (Clone)
|
||||
=========================== */
|
||||
function buildSeamlessLoop($table, $scroller) {
|
||||
$table.find('tbody.__loopClone').remove();
|
||||
|
||||
const scEl = $scroller.get(0);
|
||||
const enabled = isEnabledByEl(scEl);
|
||||
const st = ensureState(scEl);
|
||||
st.loopHeight = 0;
|
||||
|
||||
if (!enabled) return;
|
||||
|
||||
const $main = $table.find('tbody').first();
|
||||
if ($main.length === 0 || $main.children().length === 0) return;
|
||||
|
||||
const needScroll = $main.get(0).offsetHeight > scEl.clientHeight + 1;
|
||||
if (!needScroll) return;
|
||||
|
||||
const $clone = $main.clone(false, false).addClass('__loopClone').attr('aria-hidden', 'true');
|
||||
$table.append($clone);
|
||||
st.loopHeight = $main.get(0).offsetHeight;
|
||||
}
|
||||
|
||||
/* ===========================
|
||||
Auto-Scroll
|
||||
=========================== */
|
||||
function startAutoScroll($scroller, speedPx = 1, stepMs = 40) {
|
||||
const el = $scroller.get(0);
|
||||
if (!isEnabledByEl(el)) return;
|
||||
|
||||
const st = ensureState(el);
|
||||
if (st.autoTimer) return;
|
||||
if (st.loopHeight <= 0) return;
|
||||
|
||||
const tick = () => {
|
||||
if (st.userActive) { stopAutoScroll($scroller); return; }
|
||||
el.scrollTop += speedPx;
|
||||
if (el.scrollTop >= st.loopHeight) el.scrollTop -= st.loopHeight;
|
||||
};
|
||||
|
||||
st.autoTimer = setInterval(tick, stepMs);
|
||||
}
|
||||
|
||||
function stopAutoScroll($scroller) {
|
||||
const el = $scroller.get(0);
|
||||
const st = ensureState(el);
|
||||
if (st.autoTimer) {
|
||||
clearInterval(st.autoTimer);
|
||||
st.autoTimer = null;
|
||||
}
|
||||
}
|
||||
|
||||
function maybeStartAutoScroll($scroller) {
|
||||
const el = $scroller.get(0);
|
||||
if (!isEnabledByEl(el)) { stopAutoScroll($scroller); return; }
|
||||
const st = ensureState(el);
|
||||
if (st.userActive) return;
|
||||
if (st.loopHeight > 0) startAutoScroll($scroller, 1, 40);
|
||||
else stopAutoScroll($scroller);
|
||||
}
|
||||
|
||||
/* ===========================
|
||||
Smart AJAX Reload
|
||||
=========================== */
|
||||
function smartLoad($scroller, $table, $target, url, intervalMs) {
|
||||
const scEl = $scroller.get(0);
|
||||
const st = ensureState(scEl);
|
||||
const enabled = isEnabledByEl(scEl);
|
||||
|
||||
const oldLoop = st.loopHeight > 0 ? st.loopHeight : Math.max(1, scEl.scrollHeight - scEl.clientHeight);
|
||||
const posInLoop = st.loopHeight > 0 ? (scEl.scrollTop % oldLoop) : scEl.scrollTop;
|
||||
const posRatio = Math.min(1, posInLoop / oldLoop);
|
||||
|
||||
$target.load(url, function () {
|
||||
if (enabled) {
|
||||
buildSeamlessLoop($table, $scroller);
|
||||
if (st.loopHeight > 0) {
|
||||
const newPos = Math.floor(posRatio * st.loopHeight);
|
||||
if (Math.abs(scEl.scrollTop - newPos) > 1) {
|
||||
requestAnimationFrame(() => { scEl.scrollTop = newPos; });
|
||||
}
|
||||
stopAutoScroll($scroller);
|
||||
startAutoScroll($scroller, 1, 40);
|
||||
} else {
|
||||
if (scEl.scrollTop !== 0) requestAnimationFrame(() => { scEl.scrollTop = 0; });
|
||||
stopAutoScroll($scroller);
|
||||
}
|
||||
} else {
|
||||
$table.find('tbody.__loopClone').remove();
|
||||
stopAutoScroll($scroller);
|
||||
if (scEl.scrollTop !== 0) requestAnimationFrame(() => { scEl.scrollTop = 0; });
|
||||
}
|
||||
|
||||
setTimeout(() => smartLoad($scroller, $table, $target, url, intervalMs), intervalMs);
|
||||
});
|
||||
}
|
||||
|
||||
/* ===========================
|
||||
Initialisierung
|
||||
=========================== */
|
||||
$(document).ready(function () {
|
||||
sizeScrollContainers();
|
||||
|
||||
$(window).on('resize', function () {
|
||||
sizeScrollContainers();
|
||||
['#checkout', '#checkin'].forEach(prefix => {
|
||||
const $scroller = $(`${prefix}-scroll`);
|
||||
const $table = $(`${prefix}-table`);
|
||||
buildSeamlessLoop($table, $scroller);
|
||||
maybeStartAutoScroll($scroller);
|
||||
});
|
||||
});
|
||||
|
||||
// Guards nur für aktivierte Scroller
|
||||
[['#checkout-scroll','checkout'], ['#checkin-scroll','checkin']].forEach(([sel,key]) => {
|
||||
if (isEnabledByKey(key)) attachScrollGuards($(sel));
|
||||
});
|
||||
|
||||
// Erstladen je Tabelle
|
||||
function initOne(scrollerSel, tableSel, tbodySel, url, ms, key) {
|
||||
const $scroller = $(scrollerSel);
|
||||
const $table = $(tableSel);
|
||||
const $target = $(tbodySel);
|
||||
const enabled = isEnabledByKey(key);
|
||||
|
||||
$target.load(url, function () {
|
||||
if (enabled) {
|
||||
buildSeamlessLoop($table, $scroller);
|
||||
maybeStartAutoScroll($scroller);
|
||||
} else {
|
||||
$table.find('tbody.__loopClone').remove();
|
||||
stopAutoScroll($scroller);
|
||||
}
|
||||
setTimeout(() => smartLoad($scroller, $table, $target, url, ms), ms);
|
||||
});
|
||||
}
|
||||
|
||||
initOne('#checkout-scroll', '#checkout-table', '#getCheckOutTableHolder', 'sources/getCheckOutTable.php', 10000, 'checkout');
|
||||
initOne('#checkin-scroll', '#checkin-table', '#getCheckInTableHolder', 'sources/getCheckInTable.php', 10000, 'checkin');
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
<style>
|
||||
/* Scroll-Wrapper je Tabelle */
|
||||
.tableFixHead {
|
||||
overflow-y: auto;
|
||||
/* Höhe wird per JS dynamisch gesetzt, damit genau bis zum Viewport-Ende gescrollt wird */
|
||||
max-height: 60vh; /* Fallback */
|
||||
border: 1px solid rgba(255,255,255,.1);
|
||||
border-radius: .25rem;
|
||||
}
|
||||
|
||||
/* Sticky Header */
|
||||
.tableFixHead thead th {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 2; /* über Body-Zellen */
|
||||
}
|
||||
|
||||
/* Saubere Hintergrundfarbe für sticky Header (Bootstrap .table-dark) */
|
||||
.table-dark thead th {
|
||||
background-color: #212529; /* gleiche Farbe wie .table-dark header */
|
||||
}
|
||||
|
||||
/* Optional: dünne Trennlinien */
|
||||
.table-dark tbody tr + tr td {
|
||||
border-top: 1px solid rgba(255,255,255,.08);
|
||||
}
|
||||
|
||||
body { background-color: black; margin-bottom: 0; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<!-- Check-Out -->
|
||||
<div class="col-xl">
|
||||
<h2 class="text-light">Check-Out</h2>
|
||||
<div class="tableFixHead" id="checkout-scroll">
|
||||
<table class="table table-dark mb-0" id="checkout-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">#</th>
|
||||
<th scope="col">Kunde</th>
|
||||
<th scope="col">Event</th>
|
||||
<?php
|
||||
if(ShowCheckoutTimeOnCheckout || ShowVorbereitungTimeOnCheckout||ShowPackagingTimeOnCheckout || ShowDeliveryTimeOnCheckout){
|
||||
echo '<th scope="col">';
|
||||
}
|
||||
|
||||
if(ShowCheckoutTimeOnCheckout){
|
||||
echo "Dispo-Start";
|
||||
}
|
||||
function refreshCheckInTable() {
|
||||
$('#getCheckInTableHolder').load('sources/getCheckInTable.php', function () {
|
||||
setTimeout(refreshCheckInTable, 5000);
|
||||
});
|
||||
if(ShowVorbereitungTimeOnCheckout){
|
||||
if(ShowCheckoutTimeOnCheckout){echo "<br>";}
|
||||
echo "VB-Start";
|
||||
}
|
||||
if(ShowPackagingTimeOnCheckout){
|
||||
if(ShowCheckoutTimeOnCheckout||ShowVorbereitungTimeOnCheckout){echo "<br>";}
|
||||
echo "Packen";
|
||||
}
|
||||
if(ShowDeliveryTimeOnCheckout){
|
||||
if(ShowCheckoutTimeOnCheckout || ShowVorbereitungTimeOnCheckout||ShowPackagingTimeOnCheckout){echo "<br>";}
|
||||
echo "Liefern";
|
||||
}
|
||||
if(ShowCheckoutTimeOnCheckout || ShowVorbereitungTimeOnCheckout||ShowPackagingTimeOnCheckout || ShowDeliveryTimeOnCheckout){
|
||||
echo '</th>';
|
||||
}
|
||||
|
||||
?>
|
||||
<th scope="col">Status</th>
|
||||
<?php
|
||||
if(ShowShippingIcons){
|
||||
echo "<th scope='col'>Shipping</th>";
|
||||
}
|
||||
?>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="getCheckOutTableHolder"></tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Check-In -->
|
||||
<div class="col-xl">
|
||||
<h2 class="text-light">Check-In</h2>
|
||||
<div class="tableFixHead" id="checkin-scroll">
|
||||
<table class="table table-dark mb-0" id="checkin-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">#</th>
|
||||
<th scope="col">Kunde</th>
|
||||
<th scope="col">Event</th>
|
||||
<?php
|
||||
if(ShowCheckInTimeOnCheckin || ShowNachbereitungTimeOnCheckin ||ShowRePackagingTimeOnCheckin || ShowReDeliveryTimeOnCheckin){
|
||||
echo '<th scope="col">';
|
||||
}
|
||||
|
||||
if(ShowCheckInTimeOnCheckin){
|
||||
echo "Dispo-Ende";
|
||||
}
|
||||
if(ShowNachbereitungTimeOnCheckin){
|
||||
if(ShowCheckInTimeOnCheckin){echo "<br>";}
|
||||
echo "Nachbereitung";
|
||||
}
|
||||
if(ShowRePackagingTimeOnCheckin){
|
||||
if(ShowCheckInTimeOnCheckin||ShowNachbereitungTimeOnCheckin){echo "<br>";}
|
||||
echo "Zurückpacken";
|
||||
}
|
||||
if(ShowReDeliveryTimeOnCheckin){
|
||||
if(ShowCheckInTimeOnCheckin || ShowNachbereitungTimeOnCheckin||ShowRePackagingTimeOnCheckin){echo "<br>";}
|
||||
echo "Rückliefern";
|
||||
}
|
||||
if(ShowCheckInTimeOnCheckin || ShowNachbereitungTimeOnCheckin||ShowRePackagingTimeOnCheckin || ShowReDeliveryTimeOnCheckin){
|
||||
echo '</th>';
|
||||
}
|
||||
|
||||
?>
|
||||
<th scope="col">Status</th>
|
||||
<?php
|
||||
if(ShowShippingIcons){
|
||||
echo "<th scope='col'>Shipping</th>";
|
||||
}
|
||||
?>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="getCheckInTableHolder"></tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
</head>
|
||||
<body style="background-color: black;">
|
||||
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-lg">
|
||||
<h2 class="text-light">Check-Out
|
||||
<?php
|
||||
if (CheckOut_FutureDays != -1) {
|
||||
echo "in den nächsten " . CheckOut_FutureDays . " Tagen";
|
||||
}
|
||||
?></h2>
|
||||
<table class="table table-dark">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">#</th>
|
||||
<th scope="col">Kunde</th>
|
||||
<th scope="col">Event</th>
|
||||
<th scope="col">Dispo-Start<br><i>VB-Start</i></th>
|
||||
<?php
|
||||
|
||||
if(!HideCheckInTimeOnCheckout){
|
||||
echo "<th scope='col'>Dispo-Ende<br><i>VB-Ende</i></th>";
|
||||
}
|
||||
?>
|
||||
<th scope="col">Status</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="getCheckOutTableHolder">
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
<div class="col-lg">
|
||||
<h2 class="text-light">Check-In
|
||||
<?php
|
||||
if (CheckIn_FutureDays != -1) {
|
||||
echo "in den nächsten " . CheckIn_FutureDays . " Tagen";
|
||||
}
|
||||
?></h2>
|
||||
<table class="table table-dark">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">#</th>
|
||||
<th scope="col">Kunde</th>
|
||||
<th scope="col">Event</th>
|
||||
|
||||
<?php
|
||||
|
||||
if(!HideCheckOutTimeOnCheckin){
|
||||
echo "<th scope='col'>Dispo-Start<br><i>RP-Start</i></th>";
|
||||
}
|
||||
?>
|
||||
<th scope="col">Dispo-Ende<br><i>RP-Ende</i></th>
|
||||
<th scope="col">Status</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="getCheckInTableHolder">
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</header>
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<!-- jQuery (necessary for Bootstrap's JavaScript plugins) -->
|
||||
<script src="https://code.jquery.com/jquery-1.12.4.min.js" integrity="sha384-nvAa0+6Qg9clwYCGGPpDQLVpLNn0fRaROjHqs13t4Ggj3Ez50XnGQqc/r8MhnRDZ" crossorigin="anonymous"></script>
|
||||
<!-- Include all compiled plugins (below), or include individual files as needed -->
|
||||
<script src="vendor/twbs/bootstrap/dist/js/bootstrap.min.js" integrity="sha384-aJ21OjlMXNL5UyIl/XNwTMqvzeRMZH2w8c5cRVpzpU8Y5bApTppSuUkhZXN0VxHd" crossorigin="anonymous"></script>
|
||||
</body>
|
||||
<script src="vendor/twbs/bootstrap/dist/js/bootstrap.min.js" crossorigin="anonymous"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
||||
<?php
|
||||
|
||||
function getTimeFromSeconds(string $timestring) {
|
||||
|
||||
$hours = floor($timestring / 3600);
|
||||
$mins = floor($timestring / 60 % 60);
|
||||
$secs = floor($timestring % 60);
|
||||
|
||||
$timeFormat = sprintf('%02d:%02d', $hours, $mins);
|
||||
|
||||
return $timeFormat;
|
||||
$hours = floor($timestring / 3600);
|
||||
$mins = floor($timestring / 60 % 60);
|
||||
return sprintf('%02d:%02d', $hours, $mins);
|
||||
}
|
||||
?>
|
||||
?>
|
||||
|
||||
15
README.MD
15
README.MD
@@ -1,8 +1,8 @@
|
||||
# EPIWebview
|
||||
|
||||
**aktuellste stable Version:** 1.5.0
|
||||
**Lizenz:** Creative Commons Attribution-NonCommercial-ShareAlike 4.0 (CC BY-NC-SA 4.0). Einsehbar unter: (LICENSE.MD)
|
||||
**Kompatibilität:** Erweiterung für **Epirent** und **CrewBrain**
|
||||
- **aktuellste stable Version:** 1.5.0
|
||||
- **Lizenz:** Creative Commons Attribution-NonCommercial-ShareAlike 4.0 (CC BY-NC-SA 4.0). Einsehbar unter: (LICENSE.MD)
|
||||
- **Kompatibilität:** Erweiterung für **Epirent** und **CrewBrain**
|
||||
|
||||
---
|
||||
|
||||
@@ -36,12 +36,7 @@ Die Anwendung ist speziell für den Einsatz in Lagerprozessen entwickelt.
|
||||
1. Repository klonen oder Dateien ins Webverzeichnis kopieren:
|
||||
```bash
|
||||
git clone http://srvgitea01.vtm.zone:3000/epi/EpiWebview
|
||||
|
||||
2. Dashboard Aufrufen und auf die Config-Seite wechseln, notwendige Einstellungen vornehmen.
|
||||
## Changelog
|
||||
### Änderungen in Version 1.5.0
|
||||
|
||||
**Neu:**
|
||||
- Die Spalte **„CheckIn“** kann beim **Checkout** ausgeblendet werden.
|
||||
- Die Spalte **„CheckOut“** kann beim **Checkin** ausgeblendet werden.
|
||||
|
||||
Verschoben in Releases (Git)
|
||||
---
|
||||
@@ -2,6 +2,9 @@
|
||||
"require": {
|
||||
"guzzlehttp/guzzle": "^7.0",
|
||||
"twbs/bootstrap": "^4.5",
|
||||
"chillerlan/php-qrcode": "^5.0"
|
||||
"chillerlan/php-qrcode": "^5.0",
|
||||
"phpoffice/phpspreadsheet": "^5.1",
|
||||
"tecnickcom/tc-lib-pdf": "^8.1",
|
||||
"tecnickcom/tcpdf": "^6.10"
|
||||
}
|
||||
}
|
||||
|
||||
1419
composer.lock
generated
1419
composer.lock
generated
File diff suppressed because it is too large
Load Diff
413
dist/editconfig.php
vendored
Normal file
413
dist/editconfig.php
vendored
Normal file
@@ -0,0 +1,413 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* Config-Editor für EPI Webview
|
||||
* - Liest ../config.php (falls vorhanden) und ../example.config.php
|
||||
* - Zeigt editierbare Felder für alle Keys aus example.config.php
|
||||
* - Zeigt veraltete (nur in config.php existierende) Keys rot & disabled
|
||||
* - Speichert Werte zurück nach ../config.php
|
||||
*/
|
||||
|
||||
error_reporting(E_ALL);
|
||||
|
||||
// ---- Dateipfade anpassen falls nötig ----
|
||||
$CONFIG_FILE = realpath(__DIR__ . '/../config.php') ?: (__DIR__ . '/../config.php');
|
||||
$EXAMPLE_FILE = realpath(__DIR__ . '/../example.config.php') ?: (__DIR__ . '/../example.config.php');
|
||||
|
||||
// ---- Hilfsfunktionen: Parsing ----
|
||||
function parse_define_file(string $file): array {
|
||||
if (!is_file($file)) return ['defines' => [], 'order' => [], 'ui' => []];
|
||||
|
||||
$lines = file($file, FILE_IGNORE_NEW_LINES);
|
||||
$defines = [];
|
||||
$order = [];
|
||||
$ui = [];
|
||||
|
||||
// BOM entfernen, falls vorhanden
|
||||
if (isset($lines[0])) {
|
||||
$lines[0] = preg_replace('/^\xEF\xBB\xBF/', '', $lines[0]);
|
||||
}
|
||||
|
||||
// Regex
|
||||
$reDefine = '/^\s*define\(\s*([\'"])([^\'"]+)\1\s*,\s*(.+?)\s*\)\s*;\s*(?:(?:\/\/|#)\s*(.*))?$/u';
|
||||
$reSection = '/^\s*\/\/\s*@section\s*:\s*(.+)\s*$/iu';
|
||||
$reHR = '/^\s*\/\/\s*@hr\s*$/iu';
|
||||
$reNote = '/^\s*\/\/\s*@note\s*:\s*(.+)\s*$/iu';
|
||||
|
||||
foreach ($lines as $ln) {
|
||||
// Meta-Kommentare zuerst prüfen (UI-Struktur)
|
||||
if (preg_match($reSection, $ln, $m)) {
|
||||
$ui[] = ['kind' => 'section', 'title' => trim($m[1])];
|
||||
continue;
|
||||
}
|
||||
if (preg_match($reHR, $ln)) {
|
||||
$ui[] = ['kind' => 'hr'];
|
||||
continue;
|
||||
}
|
||||
if (preg_match($reNote, $ln, $m)) {
|
||||
$ui[] = ['kind' => 'note', 'text' => trim($m[1])];
|
||||
continue;
|
||||
}
|
||||
|
||||
// define(…) Zeilen
|
||||
if (!preg_match($reDefine, $ln, $m)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$name = $m[2];
|
||||
$rawValueExpr = trim($m[3]);
|
||||
$inlineC = isset($m[4]) ? trim($m[4]) : '';
|
||||
|
||||
// Typ bestimmen
|
||||
$type = 'string';
|
||||
$valForForm = '';
|
||||
$raw = rtrim($rawValueExpr, ',');
|
||||
|
||||
if (preg_match('/^(true|false)$/i', $raw)) {
|
||||
$type = 'bool';
|
||||
$valForForm = (strtolower($raw) === 'true');
|
||||
} elseif (preg_match('/^[+-]?\d+$/', $raw)) {
|
||||
$type = 'int';
|
||||
$valForForm = (int)$raw;
|
||||
} elseif (preg_match('/^([\'"])(.*)\1$/s', $raw, $mm)) {
|
||||
$type = 'string';
|
||||
$valForForm = stripcslashes($mm[2]);
|
||||
} else {
|
||||
$type = 'string';
|
||||
$valForForm = $raw;
|
||||
}
|
||||
|
||||
$defines[$name] = [
|
||||
'name' => $name,
|
||||
'type' => $type,
|
||||
'value' => $valForForm,
|
||||
'raw' => $rawValueExpr,
|
||||
'comment' => $inlineC,
|
||||
'line' => $ln,
|
||||
];
|
||||
$order[] = $name;
|
||||
// Wichtig: define auch in die UI-Reihenfolge aufnehmen
|
||||
$ui[] = ['kind' => 'define', 'name' => $name];
|
||||
}
|
||||
|
||||
return ['defines' => $defines, 'order' => $order, 'ui' => $ui];
|
||||
}
|
||||
|
||||
|
||||
function php_export_define_value(string $type, $value): string {
|
||||
// Baut den rechten Teil von define('X', <HIER>);
|
||||
if ($type === 'bool') {
|
||||
return $value ? 'true' : 'false';
|
||||
}
|
||||
if ($type === 'int') {
|
||||
return (string)intval($value);
|
||||
}
|
||||
// String:
|
||||
// einfache Quotes nehmen und escapen
|
||||
$escaped = str_replace("'", "\\'", (string)$value);
|
||||
return "'" . $escaped . "'";
|
||||
}
|
||||
|
||||
function h(?string $s): string { return htmlspecialchars((string)$s ?? '', ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8'); }
|
||||
|
||||
// ---- Dateien parsen ----
|
||||
$example = parse_define_file($EXAMPLE_FILE);
|
||||
$current = parse_define_file($CONFIG_FILE);
|
||||
|
||||
// Keys klassifizieren
|
||||
$exampleKeys = array_keys($example['defines']);
|
||||
$currentKeys = array_keys($current['defines']);
|
||||
|
||||
$deprecatedKeys = array_values(array_diff($currentKeys, $exampleKeys)); // nur in config.php
|
||||
$newKeys = array_values(array_diff($exampleKeys, $currentKeys)); // nur in example (neu)
|
||||
$sharedKeys = array_values(array_intersect($exampleKeys, $currentKeys));
|
||||
|
||||
// Beim Rendern: Reihenfolge aus example beibehalten
|
||||
$renderKeys = $example['order'];
|
||||
|
||||
// ---- POST: Speichern ----
|
||||
$message = '';
|
||||
$err = '';
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['__save_config']) && isset($_POST['cfg']) && is_array($_POST['cfg'])) {
|
||||
|
||||
$incoming = $_POST['cfg']; // ['NAME' => valueString/checkbox etc.]
|
||||
|
||||
// Build neuer config.php Inhalt:
|
||||
$out = [];
|
||||
$out[] = "<?php";
|
||||
$out[] = "// Diese Datei wurde durch den Epi Webview Config-Editor erzeugt/aktualisiert.";
|
||||
$out[] = "// Bearbeite Werte bevorzugt über die Weboberfläche.";
|
||||
$out[] = "";
|
||||
|
||||
// --- Inhalt in der Reihenfolge & Struktur der example.config.php ---
|
||||
foreach ($example['ui'] as $item) {
|
||||
if ($item['kind'] === 'section') {
|
||||
$out[] = "// @section: " . $item['title'];
|
||||
continue;
|
||||
}
|
||||
if ($item['kind'] === 'hr') {
|
||||
$out[] = "// @hr";
|
||||
continue;
|
||||
}
|
||||
if ($item['kind'] === 'note') {
|
||||
$out[] = "// @note: " . $item['text'];
|
||||
continue;
|
||||
}
|
||||
if ($item['kind'] !== 'define') {
|
||||
continue;
|
||||
}
|
||||
|
||||
$label = $item['name'];
|
||||
$ex = $example['defines'][$label] ?? null;
|
||||
if (!$ex) continue;
|
||||
|
||||
$type = $ex['type']; // Typ an example orientieren
|
||||
$tooltip = $ex['comment'] ?? '';
|
||||
|
||||
// Wert aus POST übernehmen (Checkbox-Fix: existiert das Feld?)
|
||||
if ($type === 'bool') {
|
||||
$bool = array_key_exists($label, $incoming);
|
||||
$valueForDefine = php_export_define_value('bool', $bool);
|
||||
} elseif ($type === 'int') {
|
||||
$raw = $incoming[$label] ?? '';
|
||||
$valueForDefine = php_export_define_value('int', $raw);
|
||||
} else {
|
||||
$raw = $incoming[$label] ?? '';
|
||||
$valueForDefine = php_export_define_value('string', $raw);
|
||||
}
|
||||
|
||||
$line = "define('{$label}', {$valueForDefine});";
|
||||
if (!empty($tooltip)) {
|
||||
$line .= " // " . $tooltip; // Kommentare als Tooltip auch in config.php mitführen
|
||||
}
|
||||
$out[] = $line;
|
||||
}
|
||||
|
||||
// Deprecated Block (nur in aktueller config.php vorhanden, nicht in example)
|
||||
if (!empty($deprecatedKeys)) {
|
||||
$out[] = "";
|
||||
$out[] = "// --- Veraltete/entfernte Parameter (nur Referenz, werden nicht mehr genutzt) ---";
|
||||
foreach ($deprecatedKeys as $key) {
|
||||
$c = $current['defines'][$key];
|
||||
$valueForDefine = php_export_define_value($c['type'], $c['value']);
|
||||
$line = "// define('{$c['name']}', {$valueForDefine});";
|
||||
if (!empty($c['comment'])) {
|
||||
$line .= " // " . $c['comment'];
|
||||
}
|
||||
$out[] = $line;
|
||||
}
|
||||
}
|
||||
|
||||
$out[] = "?>";
|
||||
$content = implode("\n", $out) . "\n";
|
||||
|
||||
try {
|
||||
if (false === file_put_contents($CONFIG_FILE, $content)) {
|
||||
$err = "Konnte config.php nicht schreiben: " . h($CONFIG_FILE);
|
||||
} else {
|
||||
$message = "Konfiguration gespeichert.";
|
||||
// Nach dem Speichern neu parsen, damit UI aktuell ist
|
||||
$current = parse_define_file($CONFIG_FILE);
|
||||
$currentKeys = array_keys($current['defines']);
|
||||
$deprecatedKeys = array_values(array_diff($currentKeys, $exampleKeys));
|
||||
$sharedKeys = array_values(array_intersect($exampleKeys, $currentKeys));
|
||||
}
|
||||
} catch (Throwable $e) {
|
||||
$err = "Fehler beim Schreiben: " . h($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ---- Werte für Formular vorbereiten ----
|
||||
// Für jeden example-Key: Formularwert = aus aktueller config (falls vorhanden), sonst example default
|
||||
function valueForForm(array $example, array $current, string $key) {
|
||||
$ex = $example['defines'][$key] ?? null;
|
||||
$cu = $current['defines'][$key] ?? null;
|
||||
$type = $ex ? $ex['type'] : ($cu ? $cu['type'] : 'string');
|
||||
$comment = $ex && $ex['comment'] !== '' ? $ex['comment'] : ($cu['comment'] ?? '');
|
||||
|
||||
if ($cu) {
|
||||
$val = $cu['value'];
|
||||
} else {
|
||||
$val = $ex['value'];
|
||||
}
|
||||
return [$type, $val, $comment];
|
||||
}
|
||||
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
|
||||
<meta name="description" content="Konfiguration bearbeiten" />
|
||||
<meta name="author" content="" />
|
||||
<title>Konfiguration – Epi Webview</title>
|
||||
|
||||
<link href="css/styles.css" rel="stylesheet" />
|
||||
<link href="https://cdn.datatables.net/1.10.20/css/dataTables.bootstrap4.min.css" rel="stylesheet" crossorigin="anonymous" />
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.1/js/all.min.js" crossorigin="anonymous"></script>
|
||||
<script src="js/jquery-3.5.1.min.js"></script>
|
||||
|
||||
<style>
|
||||
.deprecated { color: #c62828; }
|
||||
.cfg-help { margin-left: .35rem; cursor: help; }
|
||||
.form-group { margin-bottom: .75rem; }
|
||||
.small-muted { font-size:.85rem; color:#6c757d; }
|
||||
.checkbox-inline { display:flex; align-items:center; gap:.5rem; }
|
||||
.cfg-label { font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; }
|
||||
</style>
|
||||
|
||||
<script>
|
||||
$(function(){
|
||||
// Tooltips (Bootstrap 4)
|
||||
$('[data-toggle="tooltip"]').tooltip();
|
||||
|
||||
// Side UI nachladen wie in deinem Template
|
||||
loadSidenav();
|
||||
loadFooter();
|
||||
});
|
||||
function loadSidenav(){
|
||||
$('#layoutSidenav_nav').load('../sources/getSidenav.php');
|
||||
}
|
||||
function loadFooter(){
|
||||
$('#footerholder').load('../sources/getFooter.php');
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body class="sb-nav-fixed">
|
||||
<nav class="sb-topnav navbar navbar-expand navbar-dark bg-dark">
|
||||
<a class="navbar-brand" href="index.php">Epi Webview</a>
|
||||
<button class="btn btn-link btn-sm order-1 order-lg-0" id="sidebarToggle" type="button"><i class="fas fa-bars"></i></button>
|
||||
<ul class="navbar-nav ml-auto ml-md-0"></ul>
|
||||
</nav>
|
||||
|
||||
<div id="layoutSidenav">
|
||||
<div id="layoutSidenav_nav"></div>
|
||||
<div id="layoutSidenav_content">
|
||||
<main>
|
||||
<div class="container-fluid">
|
||||
<h1 class="mt-4">Konfiguration</h1>
|
||||
<ol class="breadcrumb mb-4">
|
||||
<li class="breadcrumb-item active">Dashboard / Konfiguration</li>
|
||||
</ol>
|
||||
|
||||
<?php if ($message): ?>
|
||||
<div class="alert alert-success"><?= h($message) ?></div>
|
||||
<?php endif; ?>
|
||||
<?php if ($err): ?>
|
||||
<div class="alert alert-danger"><?= h($err) ?></div>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="card mb-4">
|
||||
<div class="card-header">
|
||||
<i class="fas fa-sliders-h mr-1"></i>
|
||||
Einstellungen
|
||||
<span class="small-muted">( <code><?=h(basename($CONFIG_FILE))?></code> Source: <code><?=h(basename($EXAMPLE_FILE))?></code>)</span>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form method="post">
|
||||
<input type="hidden" name="__save_config" value="1" />
|
||||
|
||||
<?php foreach ($example['ui'] as $item): ?>
|
||||
<?php
|
||||
if ($item['kind'] === 'section') {
|
||||
echo '<h5 class="mt-3">'.h($item['title']).'</h5>';
|
||||
continue;
|
||||
}
|
||||
if ($item['kind'] === 'hr') {
|
||||
echo '<hr />';
|
||||
continue;
|
||||
}
|
||||
if ($item['kind'] === 'note') {
|
||||
echo '<p class="small-muted">'.h($item['text']).'</p>';
|
||||
continue;
|
||||
}
|
||||
if ($item['kind'] !== 'define') {
|
||||
continue;
|
||||
}
|
||||
|
||||
$key = $item['name'];
|
||||
// Nur Keys rendern, die in example vorkommen
|
||||
if (!isset($example['defines'][$key])) continue;
|
||||
|
||||
[$type, $val, $tooltip] = valueForForm($example, $current, $key);
|
||||
?>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="cfg-label" for="cfg_<?=h($key)?>">
|
||||
<?= h($key) ?>
|
||||
<?php if ($tooltip): ?>
|
||||
<i class="far fa-question-circle cfg-help"
|
||||
data-toggle="tooltip" data-placement="right"
|
||||
title="<?=h($tooltip)?>"></i>
|
||||
<?php endif; ?>
|
||||
</label>
|
||||
|
||||
<?php if ($type === 'bool'): ?>
|
||||
<div class="checkbox-inline">
|
||||
<input type="checkbox"
|
||||
id="cfg_<?=h($key)?>"
|
||||
name="cfg[<?=h($key)?>]"
|
||||
value="1" <?= $val ? 'checked' : '' ?> />
|
||||
<span><?= $val ? 'Aktiviert' : 'Deaktiviert' ?></span>
|
||||
</div>
|
||||
<?php elseif ($type === 'int'): ?>
|
||||
<input type="number" step="1" class="form-control"
|
||||
id="cfg_<?=h($key)?>"
|
||||
name="cfg[<?=h($key)?>]"
|
||||
value="<?=h((string)$val)?>" />
|
||||
<?php else: ?>
|
||||
<input type="text" class="form-control"
|
||||
id="cfg_<?=h($key)?>"
|
||||
name="cfg[<?=h($key)?>]"
|
||||
value="<?=h((string)$val)?>" />
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<?php endforeach; ?>
|
||||
|
||||
<?php if (!empty($deprecatedKeys)): ?>
|
||||
<hr />
|
||||
<h5 class="deprecated">Veraltete Parameter (nur in <code><?=h(basename($CONFIG_FILE))?></code>, nicht mehr in <code><?=h(basename($EXAMPLE_FILE))?></code>)</h5>
|
||||
<?php foreach ($deprecatedKeys as $key): ?>
|
||||
<?php $c = $current['defines'][$key]; ?>
|
||||
<div class="form-group">
|
||||
<label class="cfg-label deprecated" for="dep_<?=h($key)?>">
|
||||
<?= h($key) ?>
|
||||
<?php if (!empty($c['comment'])): ?>
|
||||
<i class="far fa-question-circle cfg-help" data-toggle="tooltip" data-placement="right" title="<?=h($c['comment'])?>"></i>
|
||||
<?php endif; ?>
|
||||
</label>
|
||||
<input type="text" class="form-control is-invalid" id="dep_<?=h($key)?>" value="<?=
|
||||
h($c['type']==='bool' ? ($c['value']?'true':'false') : (string)$c['value'])
|
||||
?>" disabled />
|
||||
<div class="invalid-feedback">Dieser Parameter ist in der aktuellen Version nicht mehr vorgesehen.</div>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="mt-4">
|
||||
<button type="submit" class="btn btn-primary"><i class="fas fa-save mr-1"></i> Speichern</button>
|
||||
<span class="small-muted ml-2">Speichert nach <code><?=h($CONFIG_FILE)?></code></span>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</main>
|
||||
<div id="footerholder"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@4.5.3/dist/js/bootstrap.bundle.min.js" crossorigin="anonymous"></script>
|
||||
<script src="js/scripts.js"></script>
|
||||
<script>
|
||||
// Sidebar Toggle (falls benötigt)
|
||||
$('#sidebarToggle').on('click', function(){ $('body').toggleClass('sb-sidenav-toggled'); });
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
209
dist/index.php
vendored
209
dist/index.php
vendored
@@ -1,141 +1,78 @@
|
||||
<?php
|
||||
// index.php – Dashboard Startseite (lädt Teil-HTMLs wie die Auftragsliste, ohne Auto-Refresh)
|
||||
require('../config.php');
|
||||
require('../EpiApi.php');
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
|
||||
<meta name="description" content="" />
|
||||
<meta name="author" content="" />
|
||||
<title>Dashboard - EpiWebview</title>
|
||||
<link href="css/styles.css" rel="stylesheet" />
|
||||
<link href="https://cdn.datatables.net/1.10.20/css/dataTables.bootstrap4.min.css" rel="stylesheet" crossorigin="anonymous" />
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.1/js/all.min.js" crossorigin="anonymous"></script>
|
||||
<script src="js/jquery-3.5.1.min.js"></script>
|
||||
<script type="text/javascript">
|
||||
|
||||
|
||||
$(document).ready(function(){
|
||||
refreshOrderTable();
|
||||
loadSidenav();
|
||||
loadFooter();
|
||||
});
|
||||
function refreshOrderTable(){
|
||||
$('#OrderTableHolder').load('../sources/getOrders.php', function(){
|
||||
setTimeout(refreshOrderTable, 5000);
|
||||
});
|
||||
}
|
||||
function loadSidenav(){
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
|
||||
<title>Dashboard - EpiWebview</title>
|
||||
|
||||
<!-- Styles -->
|
||||
<link href="css/styles.css" rel="stylesheet" />
|
||||
<link href="https://cdn.datatables.net/1.10.20/css/dataTables.bootstrap4.min.css" rel="stylesheet" crossorigin="anonymous" />
|
||||
|
||||
<!-- JS (nur 1x jQuery laden) -->
|
||||
<script src="js/jquery-3.5.1.min.js"></script>
|
||||
<script src="https://kit.fontawesome.com/93d71de8bc.js" crossorigin="anonymous"></script>
|
||||
|
||||
<style>
|
||||
.opacity-50 { opacity: .5; }
|
||||
.card .h2, .card .display-4 { font-weight: 700; }
|
||||
.kpi-updated { font-size: .82rem; opacity: .85; }
|
||||
</style>
|
||||
|
||||
<script>
|
||||
// Einmalige Loads – analog zur Auftragsliste
|
||||
$(function () {
|
||||
loadSidenav();
|
||||
loadFooter();
|
||||
|
||||
});
|
||||
|
||||
function loadSidenav() {
|
||||
$('#layoutSidenav_nav').load('../sources/getSidenav.php');
|
||||
}
|
||||
function loadFooter(){
|
||||
}
|
||||
|
||||
function loadFooter() {
|
||||
$('#footerholder').load('../sources/getFooter.php');
|
||||
}
|
||||
|
||||
|
||||
|
||||
</script>
|
||||
</head>
|
||||
<body class="sb-nav-fixed">
|
||||
<nav class="sb-topnav navbar navbar-expand navbar-dark bg-dark">
|
||||
<a class="navbar-brand" href="index.php">Epi Webview</a>
|
||||
<button class="btn btn-link btn-sm order-1 order-lg-0" id="sidebarToggle" href="#"><i class="fas fa-bars"></i></button>
|
||||
}
|
||||
|
||||
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body class="sb-nav-fixed">
|
||||
<nav class="sb-topnav navbar navbar-expand navbar-dark bg-dark">
|
||||
<a class="navbar-brand" href="index.php">Epi Webview</a>
|
||||
<button class="btn btn-link btn-sm order-1 order-lg-0" id="sidebarToggle"><i class="fas fa-bars"></i></button>
|
||||
</nav>
|
||||
|
||||
<div id="layoutSidenav">
|
||||
<div id="layoutSidenav_nav"></div>
|
||||
<div id="layoutSidenav_content">
|
||||
<main>
|
||||
<div class="container-fluid">
|
||||
<h1 class="mt-4">Dashboard</h1>
|
||||
<ol class="breadcrumb mb-4">
|
||||
<li class="breadcrumb-item active">Dashboard</li>
|
||||
</ol>
|
||||
|
||||
|
||||
|
||||
|
||||
</nav>
|
||||
<div id="layoutSidenav">
|
||||
<div id="layoutSidenav_nav">
|
||||
</nav>
|
||||
</div>
|
||||
<div id="layoutSidenav_content">
|
||||
<main>
|
||||
<div class="container-fluid">
|
||||
<h1 class="mt-4">Dashboard</h1>
|
||||
<ol class="breadcrumb mb-4">
|
||||
<li class="breadcrumb-item active">Dashboard</li>
|
||||
</ol>
|
||||
<div class="row">
|
||||
<div class="col-xl-3 col-md-6">
|
||||
<div class="card bg-primary text-white mb-4">
|
||||
<div class="card-body">Primary Card</div>
|
||||
<div class="card-footer d-flex align-items-center justify-content-between">
|
||||
<a class="small text-white stretched-link" href="#">View Details</a>
|
||||
<div class="small text-white"><i class="fas fa-angle-right"></i></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xl-3 col-md-6">
|
||||
<div class="card bg-warning text-white mb-4">
|
||||
<div class="card-body">Warning Card</div>
|
||||
<div class="card-footer d-flex align-items-center justify-content-between">
|
||||
<a class="small text-white stretched-link" href="#">View Details</a>
|
||||
<div class="small text-white"><i class="fas fa-angle-right"></i></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xl-3 col-md-6">
|
||||
<div class="card bg-success text-white mb-4">
|
||||
<div class="card-body">Success Card</div>
|
||||
<div class="card-footer d-flex align-items-center justify-content-between">
|
||||
<a class="small text-white stretched-link" href="#">View Details</a>
|
||||
<div class="small text-white"><i class="fas fa-angle-right"></i></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xl-3 col-md-6">
|
||||
<div class="card bg-danger text-white mb-4">
|
||||
<div class="card-body">Danger Card</div>
|
||||
<div class="card-footer d-flex align-items-center justify-content-between">
|
||||
<a class="small text-white stretched-link" href="#">View Details</a>
|
||||
<div class="small text-white"><i class="fas fa-angle-right"></i></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-xl-6">
|
||||
<div class="card mb-4">
|
||||
<div class="card-header">
|
||||
<i class="fas fa-chart-area mr-1"></i>
|
||||
Area Chart Example
|
||||
</div>
|
||||
<div class="card-body"><canvas id="myAreaChart" width="100%" height="40"></canvas></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xl-6">
|
||||
<div class="card mb-4">
|
||||
<div class="card-header">
|
||||
<i class="fas fa-chart-bar mr-1"></i>
|
||||
Bar Chart Example
|
||||
</div>
|
||||
<div class="card-body"><canvas id="myBarChart" width="100%" height="40"></canvas></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card mb-4">
|
||||
<div class="card-header">
|
||||
<i class="fas fa-table mr-1"></i>
|
||||
Aufträge
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="table-responsive" id="OrderTableHolder">
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
<div id="footerholder"></div>
|
||||
</div>
|
||||
</div>
|
||||
<script src="js/jquery-3.5.1.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@4.5.3/dist/js/bootstrap.bundle.min.js" crossorigin="anonymous"></script>
|
||||
<script src="js/scripts.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.8.0/Chart.min.js" crossorigin="anonymous"></script>
|
||||
<script src="assets/demo/chart-area-demo.js"></script>
|
||||
<script src="assets/demo/chart-bar-demo.js"></script>
|
||||
<script src="https://cdn.datatables.net/1.10.20/js/jquery.dataTables.min.js" crossorigin="anonymous"></script>
|
||||
<script src="https://cdn.datatables.net/1.10.20/js/dataTables.bootstrap4.min.js" crossorigin="anonymous"></script>
|
||||
<script src="assets/demo/datatables-demo.js"></script>
|
||||
</body>
|
||||
|
||||
|
||||
</div>
|
||||
</main>
|
||||
<div id="footerholder"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Bootstrap Bundle (Popper inkl.) -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@4.5.3/dist/js/bootstrap.bundle.min.js" crossorigin="anonymous"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
371
dist/insurance.php
vendored
Normal file
371
dist/insurance.php
vendored
Normal file
@@ -0,0 +1,371 @@
|
||||
<?php
|
||||
// insurance.php – Versicherungsgeräte (liegt in /dist)
|
||||
require('../config.php');
|
||||
require('../EpiApi.php');
|
||||
require_once __DIR__ . '/../vendor/autoload.php'; // PhpSpreadsheet Autoload
|
||||
date_default_timezone_set('Europe/Berlin');
|
||||
|
||||
$Epi = new Epirent();
|
||||
|
||||
function formatEuro(float $amount): string {
|
||||
$formatted = number_format(abs($amount), 2, ',', '.') . ' €';
|
||||
return $amount < 0 ? '-' . $formatted : $formatted;
|
||||
}
|
||||
|
||||
/* =========================
|
||||
Excel-Export: Konfiguration
|
||||
========================= */
|
||||
$EXCEL_CUSTOM_HEADER = [
|
||||
['EpiWebview – Versicherungsgeräte'],
|
||||
['Exportdatum: ' . date('d.m.Y H:i') . ' | Mandant: ' . (defined('Epirent_Mandant') ? Epirent_Mandant : '')]
|
||||
];
|
||||
$EXCEL_COLS = ['#','Name','Warengruppe(n)','Versicherung netto','Seriennummer','Anschaffung','Kaufpreis','Besitzer'];
|
||||
|
||||
/* =========================
|
||||
1) Daten einmalig holen & Zeilen bauen
|
||||
========================= */
|
||||
$result = $Epi->requestEpiApi('/v1/product/all?ia=true&ir=true&cl=' . Epirent_Mandant);
|
||||
$productList = json_decode($result)->payload;
|
||||
|
||||
$filteredProductArray = [];
|
||||
foreach ($productList as $product) {
|
||||
if ($product->is_stock_control && $product->is_stock_single_entry && !$product->is_virtual) {
|
||||
if (!empty($product->stock_data[0]->stock_rent) && $product->stock_data[0]->stock_rent > 0) {
|
||||
$filteredProductArray[] = $product;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$rows = [];
|
||||
foreach ($filteredProductArray as $filteredProduct) {
|
||||
$detailRes = $Epi->requestEpiApi('/v1/product/' . $filteredProduct->primary_key . '?cl=' . Epirent_Mandant);
|
||||
$insuredProduct = json_decode($detailRes)->payload[0] ?? null;
|
||||
|
||||
if (!$insuredProduct || empty($insuredProduct->pricing) || !isset($insuredProduct->pricing->insurance_net)) {
|
||||
continue;
|
||||
}
|
||||
if ((float)$insuredProduct->pricing->insurance_net == 0.0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Materials
|
||||
$MaterialList = "";
|
||||
if (!empty($insuredProduct->materials)) {
|
||||
foreach ($insuredProduct->materials as $material) {
|
||||
if ($MaterialList !== "") $MaterialList .= ", ";
|
||||
if (!empty($material->is_free_material) && $material->is_free_material) {
|
||||
$MaterialList .= $material->name;
|
||||
} else {
|
||||
$materialResult = $Epi->requestEpiApi('/v1/product/' . $material->mat_product_pk . '?cl=' . Epirent_Mandant);
|
||||
$materialProductObject = json_decode($materialResult)->payload[0] ?? null;
|
||||
$MaterialList .= $materialProductObject ? $materialProductObject->name : '';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Warengruppen
|
||||
$groupOfGoodsList = "";
|
||||
if (!empty($insuredProduct->group_of_goods)) {
|
||||
foreach ($insuredProduct->group_of_goods as $groupOfGoods) {
|
||||
if ($groupOfGoodsList !== "") $groupOfGoodsList .= ", ";
|
||||
$groupOfGoodsList .= str_replace(";", "->", $groupOfGoods->sequence_name);
|
||||
}
|
||||
}
|
||||
|
||||
// Geräte (Stocks) nur einmal je Produkt
|
||||
$stockRes = $Epi->requestEpiApi('/v1/stock/filter?ppk=' . $insuredProduct->primary_key . '&cl=' . Epirent_Mandant);
|
||||
$deviceStockObjectArray = json_decode($stockRes)->payload ?? [];
|
||||
|
||||
foreach ($deviceStockObjectArray as $device) {
|
||||
$rows[] = [
|
||||
'pk' => $insuredProduct->product_no,
|
||||
'name' => $insuredProduct->name,
|
||||
'materials' => $MaterialList,
|
||||
'groups' => $groupOfGoodsList,
|
||||
'insurance_net' => (float)$insuredProduct->pricing->insurance_net,
|
||||
'serial_no' => (string)($device->serial_no ?? ''),
|
||||
'date_purchase' => (string)($device->date_purchase ?? ''),
|
||||
'purchase_price'=> (float)($device->purchase_price ?? 0.0),
|
||||
'owner' => (string)($device->owner ?? ''),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
/* =========================
|
||||
2) XLSX sofort erzeugen (ohne setCellValueByColumnAndRow)
|
||||
========================= */
|
||||
use PhpOffice\PhpSpreadsheet\Spreadsheet;
|
||||
use PhpOffice\PhpSpreadsheet\Writer\Xlsx;
|
||||
use PhpOffice\PhpSpreadsheet\Style\Alignment;
|
||||
use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
|
||||
use PhpOffice\PhpSpreadsheet\Cell\DataType;
|
||||
use PhpOffice\PhpSpreadsheet\Shared\Date as ExcelDate;
|
||||
use PhpOffice\PhpSpreadsheet\Style\Fill;
|
||||
|
||||
$exportDir = __DIR__ . '/exports';
|
||||
if (!is_dir($exportDir)) {
|
||||
@mkdir($exportDir, 0775, true);
|
||||
}
|
||||
|
||||
$timestamp = date('Ymd_His');
|
||||
$excelFileName = "Versicherungsgeraete_{$timestamp}.xlsx";
|
||||
$excelFilePath = $exportDir . '/' . $excelFileName;
|
||||
$excelDownloadUrl = 'exports/' . $excelFileName;
|
||||
|
||||
$spreadsheet = new Spreadsheet();
|
||||
$sheet = $spreadsheet->getActiveSheet();
|
||||
$sheet->setTitle('Versicherungsgeräte');
|
||||
|
||||
// kleine Helfer zum Adressieren per Spaltenbuchstaben
|
||||
$cell = function(int $colIndex, int $rowIndex): string {
|
||||
return Coordinate::stringFromColumnIndex($colIndex) . $rowIndex;
|
||||
};
|
||||
|
||||
$colCount = count($EXCEL_COLS);
|
||||
$lastCol = Coordinate::stringFromColumnIndex($colCount);
|
||||
|
||||
// Spaltenindex-Konstanten für Summen
|
||||
$INSURANCE_COL = 4; // "Versicherung netto"
|
||||
$PURCHASE_COL = 7; // "Kaufpreis"
|
||||
|
||||
// Custom Header
|
||||
$rowIdx = 1;
|
||||
foreach ($EXCEL_CUSTOM_HEADER as $hdrLine) {
|
||||
$text = implode(' ', $hdrLine);
|
||||
$sheet->setCellValue($cell(1, $rowIdx), $text);
|
||||
$sheet->mergeCells($cell(1, $rowIdx) . ':' . $cell($colCount, $rowIdx));
|
||||
$sheet->getStyle($cell(1, $rowIdx) . ':' . $cell($colCount, $rowIdx))
|
||||
->getAlignment()->setHorizontal(Alignment::HORIZONTAL_CENTER);
|
||||
$sheet->getStyle($cell(1, $rowIdx) . ':' . $cell($colCount, $rowIdx))
|
||||
->getFont()->setBold(true)->setSize($rowIdx === 1 ? 14 : 11);
|
||||
$rowIdx++;
|
||||
}
|
||||
$rowIdx++;
|
||||
|
||||
// Tabellen-Header
|
||||
foreach ($EXCEL_COLS as $i => $label) {
|
||||
$addr = $cell($i+1, $rowIdx);
|
||||
$sheet->setCellValue($addr, $label);
|
||||
$sheet->getStyle($addr)->getFont()->setBold(true);
|
||||
$sheet->getStyle($addr)->getAlignment()->setHorizontal(Alignment::HORIZONTAL_CENTER);
|
||||
$sheet->getColumnDimension(Coordinate::stringFromColumnIndex($i+1))->setAutoSize(true);
|
||||
}
|
||||
$headerRow = $rowIdx;
|
||||
$rowIdx++;
|
||||
|
||||
// Daten
|
||||
$dataStartRow = $rowIdx;
|
||||
foreach ($rows as $r) {
|
||||
$c = 1;
|
||||
|
||||
// #
|
||||
$sheet->setCellValue($cell($c++, $rowIdx), $r['pk']);
|
||||
|
||||
// Name (+ Materials)
|
||||
$nameOut = $r['name'] . ($r['materials'] ? " | {$r['materials']}" : "");
|
||||
$sheet->setCellValue($cell($c++, $rowIdx), $nameOut);
|
||||
|
||||
// Warengruppen
|
||||
$sheet->setCellValue($cell($c++, $rowIdx), $r['groups']);
|
||||
|
||||
// Versicherung netto (numerisch, EU-Format)
|
||||
$sheet->setCellValueExplicit($cell($c, $rowIdx), (float)$r['insurance_net'], DataType::TYPE_NUMERIC);
|
||||
$sheet->getStyle($cell($c, $rowIdx))->getNumberFormat()->setFormatCode('#,##0.00 [$€-de-DE]');
|
||||
$c++;
|
||||
|
||||
// Seriennummer
|
||||
$sheet->setCellValue($cell($c++, $rowIdx), $r['serial_no']);
|
||||
|
||||
// Anschaffung (Datum)
|
||||
$excelDate = '';
|
||||
if (!empty($r['date_purchase'])) {
|
||||
$ts = strtotime($r['date_purchase']);
|
||||
if ($ts !== false) {
|
||||
$excelDate = ExcelDate::PHPToExcel($ts);
|
||||
}
|
||||
}
|
||||
if ($excelDate !== '') {
|
||||
$sheet->setCellValue($cell($c, $rowIdx), $excelDate);
|
||||
$sheet->getStyle($cell($c, $rowIdx))->getNumberFormat()->setFormatCode('dd.mm.yyyy');
|
||||
} else {
|
||||
$sheet->setCellValue($cell($c, $rowIdx), '');
|
||||
}
|
||||
$c++;
|
||||
|
||||
// Kaufpreis (numerisch, EU-Format)
|
||||
$sheet->setCellValueExplicit($cell($c, $rowIdx), abs((float)$r['purchase_price']), DataType::TYPE_NUMERIC);
|
||||
$sheet->getStyle($cell($c, $rowIdx))->getNumberFormat()->setFormatCode('#,##0.00 [$€-de-DE]');
|
||||
$c++;
|
||||
|
||||
// Besitzer
|
||||
$sheet->setCellValue($cell($c++, $rowIdx), $r['owner']);
|
||||
|
||||
$rowIdx++;
|
||||
}
|
||||
$dataEndRow = $rowIdx - 1;
|
||||
|
||||
/* === Summenzeile mit grauer Hinterlegung === */
|
||||
$totalRow = $rowIdx + 1;
|
||||
|
||||
// Label in Spalte A
|
||||
$sheet->setCellValue($cell(1, $totalRow), 'Gesamtsummen:');
|
||||
$sheet->getStyle($cell(1, $totalRow))->getFont()->setBold(true);
|
||||
|
||||
// SUM-Formel Versicherung netto (Spalte 4)
|
||||
$insColLetter = Coordinate::stringFromColumnIndex($INSURANCE_COL);
|
||||
$sheet->setCellValue(
|
||||
$cell($INSURANCE_COL, $totalRow),
|
||||
"=SUM({$insColLetter}{$dataStartRow}:{$insColLetter}{$dataEndRow})"
|
||||
);
|
||||
$sheet->getStyle($cell($INSURANCE_COL, $totalRow))
|
||||
->getNumberFormat()->setFormatCode('#,##0.00 [$€-de-DE]');
|
||||
$sheet->getStyle($cell($INSURANCE_COL, $totalRow))->getFont()->setBold(true);
|
||||
|
||||
// SUM-Formel Kaufpreis (Spalte 7)
|
||||
$purColLetter = Coordinate::stringFromColumnIndex($PURCHASE_COL);
|
||||
$sheet->setCellValue(
|
||||
$cell($PURCHASE_COL, $totalRow),
|
||||
"=SUM({$purColLetter}{$dataStartRow}:{$purColLetter}{$dataEndRow})"
|
||||
);
|
||||
$sheet->getStyle($cell($PURCHASE_COL, $totalRow))
|
||||
->getNumberFormat()->setFormatCode('#,##0.00 [$€-de-DE]');
|
||||
$sheet->getStyle($cell($PURCHASE_COL, $totalRow))->getFont()->setBold(true);
|
||||
|
||||
// graue Hinterlegung über die ganze Summenzeile
|
||||
$sheet->getStyle($cell(1, $totalRow) . ':' . $cell($colCount, $totalRow))
|
||||
->getFill()->setFillType(Fill::FILL_SOLID)->getStartColor()->setARGB('FFEFEFEF');
|
||||
|
||||
// Filter + Freeze
|
||||
$sheet->setAutoFilter("A{$headerRow}:{$lastCol}{$headerRow}");
|
||||
$sheet->freezePane("A" . ($headerRow + 1));
|
||||
|
||||
// Datei speichern
|
||||
$writer = new Xlsx($spreadsheet);
|
||||
$writer->save($excelFilePath);
|
||||
|
||||
/* =========================
|
||||
3) HTML-Ausgabe (Tabelle aus $rows) + Download-Link
|
||||
========================= */
|
||||
|
||||
// Summen für HTML
|
||||
$totalInsurance = array_sum(array_column($rows, 'insurance_net'));
|
||||
$totalPurchase = array_sum(array_map(fn($r) => abs($r['purchase_price']), $rows));
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
|
||||
<title>Dashboard - EpiWebview</title>
|
||||
|
||||
<!-- Styles -->
|
||||
<link href="css/styles.css" rel="stylesheet" />
|
||||
<link href="https://cdn.datatables.net/1.10.20/css/dataTables.bootstrap4.min.css" rel="stylesheet" crossorigin="anonymous" />
|
||||
|
||||
<!-- JS -->
|
||||
<script src="js/jquery-3.5.1.min.js"></script>
|
||||
<script src="https://kit.fontawesome.com/93d71de8bc.js" crossorigin="anonymous"></script>
|
||||
|
||||
<style>
|
||||
.opacity-50 { opacity: .5; }
|
||||
.card .h2, .card .display-4 { font-weight: 700; }
|
||||
.kpi-updated { font-size: .82rem; opacity: .85; }
|
||||
</style>
|
||||
|
||||
<script>
|
||||
$(function () {
|
||||
$('#layoutSidenav_nav').load('../sources/getSidenav.php');
|
||||
$('#footerholder').load('../sources/getFooter.php');
|
||||
});
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body class="sb-nav-fixed">
|
||||
<nav class="sb-topnav navbar navbar-expand navbar-dark bg-dark">
|
||||
<a class="navbar-brand" href="index.php">Epi Webview</a>
|
||||
<button class="btn btn-link btn-sm order-1 order-lg-0" id="sidebarToggle"><i class="fas fa-bars"></i></button>
|
||||
</nav>
|
||||
|
||||
<div id="layoutSidenav">
|
||||
<div id="layoutSidenav_nav"></div>
|
||||
<div id="layoutSidenav_content">
|
||||
<main>
|
||||
<div class="container-fluid">
|
||||
<h1 class="mt-4">Versicherung</h1>
|
||||
<ol class="breadcrumb mb-4">
|
||||
<li class="breadcrumb-item active">Versicherung</li>
|
||||
</ol>
|
||||
|
||||
<div class="card mb-4">
|
||||
<div class="card-header d-flex justify-content-between align-items-center">
|
||||
<div><i class="fas fa-table mr-1"></i> Versicherungsgeräte</div>
|
||||
<a class="btn btn-sm btn-success" href="<?php echo htmlspecialchars($excelDownloadUrl); ?>">
|
||||
<i class="fas fa-file-excel"></i> Export nach Excel
|
||||
</a>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-bordered" id="dataTable" width="100%" cellspacing="0">
|
||||
<tr>
|
||||
<th scope="col">#</th>
|
||||
<th scope="col">Name</th>
|
||||
<th scope="col">Warengruppe(n)</th>
|
||||
<th scope="col">Versicherung netto</th>
|
||||
<th scope="col">Seriennummer</th>
|
||||
<th scope="col">Anschaffung</th>
|
||||
<th scope="col">Kaufpreis</th>
|
||||
<th scope="col">Besitzer</th>
|
||||
</tr>
|
||||
<?php foreach ($rows as $r): ?>
|
||||
<tr>
|
||||
<th><?php echo htmlspecialchars((string) $r['pk']); ?></th>
|
||||
<th>
|
||||
<?php
|
||||
echo htmlspecialchars($r['name']);
|
||||
if (!empty($r['materials'])) {
|
||||
echo "<br><small>" . htmlspecialchars($r['materials']) . "</small>";
|
||||
}
|
||||
?>
|
||||
</th>
|
||||
<th><?php echo htmlspecialchars($r['groups']); ?></th>
|
||||
<th><?php echo formatEuro((float)$r['insurance_net']); ?></th>
|
||||
<th><?php echo htmlspecialchars($r['serial_no']); ?></th>
|
||||
<th>
|
||||
<?php
|
||||
$purchaseDateOut = '';
|
||||
if (!empty($r['date_purchase'])) {
|
||||
try { $purchaseDateOut = date_format(new \DateTime($r['date_purchase']), 'd.m.Y'); } catch (\Throwable $e) {}
|
||||
}
|
||||
echo htmlspecialchars($purchaseDateOut);
|
||||
?>
|
||||
</th>
|
||||
<th><?php echo formatEuro(abs((float)$r['purchase_price'])); ?></th>
|
||||
<th><?php echo htmlspecialchars($r['owner']); ?></th>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
|
||||
<!-- Summenzeile in HTML -->
|
||||
<tr style="font-weight:bold; background:#f8f9fa;">
|
||||
<td colspan="3" class="text-right">Gesamtsummen:</td>
|
||||
<td><?php echo formatEuro($totalInsurance); ?></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td><?php echo formatEuro($totalPurchase); ?></td>
|
||||
<td></td>
|
||||
</tr>
|
||||
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
<div id="footerholder"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Bootstrap Bundle -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@4.5.3/dist/js/bootstrap.bundle.min.js" crossorigin="anonymous"></script>
|
||||
</body>
|
||||
</html>
|
||||
152
dist/labelprint.php
vendored
Normal file
152
dist/labelprint.php
vendored
Normal file
@@ -0,0 +1,152 @@
|
||||
<?php
|
||||
|
||||
require('../config.php');
|
||||
require('../EpiApi.php');
|
||||
require_once __DIR__ . '/../vendor/autoload.php';
|
||||
date_default_timezone_set('Europe/Berlin');
|
||||
|
||||
$Epi = new Epirent();
|
||||
$productList = json_decode($Epi->requestEpiApi('/v1/product/all?cl=' . Epirent_Mandant))->payload;
|
||||
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
|
||||
<title>Labelprint - EpiWebview</title>
|
||||
|
||||
<!-- Styles -->
|
||||
<link href="css/styles.css" rel="stylesheet" />
|
||||
<link href="https://cdn.datatables.net/1.10.20/css/dataTables.bootstrap4.min.css" rel="stylesheet" crossorigin="anonymous" />
|
||||
|
||||
<!-- JS -->
|
||||
<script src="js/jquery-3.5.1.min.js"></script>
|
||||
<script src="https://kit.fontawesome.com/93d71de8bc.js" crossorigin="anonymous"></script>
|
||||
<script src="https://cdn.datatables.net/1.10.20/js/jquery.dataTables.min.js"></script>
|
||||
<script src="https://cdn.datatables.net/1.10.20/js/dataTables.bootstrap4.min.js"></script>
|
||||
|
||||
<style>
|
||||
.opacity-50 {
|
||||
opacity: .5;
|
||||
}
|
||||
|
||||
.card .h2,
|
||||
.card .display-4 {
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.kpi-updated {
|
||||
font-size: .82rem;
|
||||
opacity: .85;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
$(function() {
|
||||
$('#layoutSidenav_nav').load('../sources/getSidenav.php');
|
||||
$('#footerholder').load('../sources/getFooter.php');
|
||||
});
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body class="sb-nav-fixed">
|
||||
<nav class="sb-topnav navbar navbar-expand navbar-dark bg-dark">
|
||||
<a class="navbar-brand" href="index.php">Epi Webview</a>
|
||||
<button class="btn btn-link btn-sm order-1 order-lg-0" id="sidebarToggle"><i class="fas fa-bars"></i></button>
|
||||
</nav>
|
||||
|
||||
<div id="layoutSidenav">
|
||||
<div id="layoutSidenav_nav"></div>
|
||||
<div id="layoutSidenav_content">
|
||||
<main>
|
||||
<div class="container-fluid">
|
||||
<h1 class="mt-4">Labelprint</h1>
|
||||
|
||||
|
||||
<div class="card mb-4">
|
||||
<div class="card-header d-flex justify-content-between align-items-center">
|
||||
<div><i class="fas fa-table mr-1"></i> Labelprint</div>
|
||||
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="table-responsive">
|
||||
<table id="dataTable" class="table table-bordered" width="100%" cellspacing="0">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>#</th>
|
||||
<th>PN</th>
|
||||
<th>Name</th>
|
||||
<th>Aktionen</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php
|
||||
foreach ($productList as $product) {
|
||||
echo "<tr>";
|
||||
echo "<td>" . htmlspecialchars($product->primary_key) . "</td>";
|
||||
echo "<td>" . htmlspecialchars($product->product_no) . "</td>";
|
||||
echo "<td>" . htmlspecialchars($product->name) . "</td>";
|
||||
echo "
|
||||
<td>
|
||||
<a class='btn btn-sm btn-outline-primary'
|
||||
target='_blank'
|
||||
href='../sources/getProductLabel.php?id=" . urlencode($product->primary_key) . "'>
|
||||
Export
|
||||
</a>
|
||||
|
||||
<input type='number'
|
||||
id='nrInput_" . htmlspecialchars($product->primary_key) . "'
|
||||
min='1' max='9999'
|
||||
style='width:70px; margin-left:6px; text-align:center;'
|
||||
placeholder='#'>
|
||||
|
||||
<a class='btn btn-sm btn-outline-secondary'
|
||||
target='_blank'
|
||||
id='exportWithIdBtn_" . htmlspecialchars($product->primary_key) . "'
|
||||
href='#'
|
||||
onclick=\"
|
||||
var nr = document.getElementById('nrInput_" . htmlspecialchars($product->primary_key) . "').value;
|
||||
if(nr) {
|
||||
this.href = '../sources/getProductLabel.php?id=" . urlencode($product->primary_key) . "&nr=' + encodeURIComponent(nr);
|
||||
} else {
|
||||
alert('Bitte eine Nummer zwischen 1 und 9999 eingeben.');
|
||||
return false;
|
||||
}
|
||||
\">
|
||||
Export mit ID
|
||||
</a>
|
||||
</td>";
|
||||
echo "</tr>";
|
||||
}
|
||||
?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
<div id="footerholder"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Bootstrap Bundle -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@4.5.3/dist/js/bootstrap.bundle.min.js" crossorigin="anonymous"></script>
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
$('#dataTable').DataTable({
|
||||
"pageLength": 25,
|
||||
"language": {
|
||||
"url": "//cdn.datatables.net/plug-ins/1.10.20/i18n/German.json"
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -1,39 +1,78 @@
|
||||
<?php
|
||||
//EPIRENT CONNECTION EINSTELLUNGEN//
|
||||
// @section: Epirent – Verbindung
|
||||
define('Epirent_Server', 'xxx');
|
||||
define('Epirent_Connectionprotocol', 'http');
|
||||
define('Epirent_Port', '8080');
|
||||
define('Epirent_Token', 'xxx');
|
||||
define('Epirent_Mandant', '0'); //NORMALERWEISE 0
|
||||
|
||||
//CREWBRAIN CONNECTION EINSTELLUNGEN-NUR AUSFÜLLEN WENN GEWÜNSCHT//
|
||||
// @hr
|
||||
// @section: Crewbrain Verbindung
|
||||
// @note: Nur ausfüllen wenn gewünscht
|
||||
define('CrewBrain_Username', 'xxx');
|
||||
define('CrewBrain_password', 'xxx');
|
||||
define('CrewBrain_Server', 'xxx.crewbrain.com');
|
||||
define('CrewBrain_Connectionprotocol', 'https');
|
||||
define('CrewBrain_TaskListID', 6);
|
||||
|
||||
//CREWBRAIN SPEZIFISCHE EINSTELLUNGEN (AUFGABENLISTE)//
|
||||
define('Enable_QR_Code_CrewBrainAufgaben', true);
|
||||
|
||||
//EPIRENT SPEZIFISCHE EINSTELLUNGEN (CHECK IN & CHECK OUT)
|
||||
// @hr
|
||||
// @section: Epirent-Spezifische Einstellungen
|
||||
define('Enable_QR_Code_CheckOut', false); //Zeigt statt der Packscheinnummer einen Scanbaren QR Code für den CheckOut an
|
||||
define('Enable_QR_Code_CheckIn', false); //Zeigt statt der Packscheinnummer einen Scanbaren QR Code für den CheckIn an
|
||||
define('Vorbereitungs_Zeitvariable', 'Packen'); //Name des zu verwendenden Zeitabschnitts, der Zusätzlich zur DispoZeit beim Check Out Angezeigt werden soll
|
||||
define('Rueckpacken_Zeitvariable', 'Rückpacken'); //Name des zu verwendenden Zeitabschnitts, der Zusätzlich zur DispoZeit beim Check In Angezeigt werden soll
|
||||
define('Nachbereitung_Zeitvariable', 'Rückpacken'); //Name des zu verwendenden Zeitabschnitts aus dem Auftrag, der die Nachbereitung angibt
|
||||
define('Rückpacken_Zeitvariable', 'Rückpacken'); //Name des zu verwendenden Zeitabschnitts aus dem Auftrag, der das Rückpacken angibt.
|
||||
define('UseDeliveredForCheckOutCompleted', true); //Nutzt statt dem erfolgreichen abschließen eines Packscheins durch ausbuchen das erfolgreiche Liefern (Geliefert != 00.00.00)
|
||||
define('EnableScrollingCheckOut', true); //Aktiviert das automatische Scrollen der CheckOut Liste
|
||||
define('EnableScrollingCheckIn', true); //Aktiviert das automatische Scrollen der CheckIn Liste
|
||||
define('EnableScrollingAufgaben', true); //Aktiviert das automatische Scrollen der Aufgabenliste
|
||||
// @note: -------------------- CheckOutRowMarkSource: Konfig Zusände --------------------
|
||||
// @note: 1 = $packingjob->date_start (Dispo Start)
|
||||
// @note: 2 = $VorbereitungsTimeDetail->date_start (Vorbereitung Start)
|
||||
// @note: 3 = $PackingNoteDetail->date_packing (Packen Zeit)
|
||||
// @note: 4 = $PackingNoteDetail->date_delivery (Delivery Zeit)
|
||||
define('CheckOutRowMarkSource', 4);
|
||||
|
||||
define('UsePackingNoteDateForCheckout', true); // Nutzt statt den Zeitabscnitten aus dem Auftrag die Informationen aus dem Packschein für den Checkout. Wenn die UseDispo Variablen false sind, werden diese Variablen für das Rowmarking genutzt falls "true".
|
||||
define('UsePackingNoteDateForCheckin', true); // Nutzt statt den Zeitabscnitten aus dem Auftrag die Informationen aus dem Packschein für den Checkout. Wenn die UseDispo Variablen false sind, werden diese Variablen für das Rowmarking genutzt falls "true".
|
||||
// @note: -------------------- CheckInRowMarkSource: Konfig Zusände --------------------
|
||||
// @note: 1 = $packingjob->date_end (Dispo Ende)
|
||||
// @note: 2 = $NachbereitungssTimeDetail->date_start (Nachbereitung Start)
|
||||
// @note: 3 = $RePackagingTimeDetail->date_start (Rückpacken Zeit AUS AUFTRAG)
|
||||
// @note: 4 = $PackingNoteDetail->date_redelivery (ReDelivery Zeit)
|
||||
define('CheckInRowMarkSource', 4);
|
||||
|
||||
define('CheckOut_UseDispoStartForRowMarking', false); //else: Use Same Variable as "Vorbereitung Zeitvariable" | Konfiguration, welche Zeit für die Zeilenmarkierung beim CheckOut Verwendet werden soll
|
||||
define('CheckIn_UseDispoEndForRowMarking', false); //else: Use Same Variable as "Rueckpacken Zeitvariable" | Konfiguration, welche Zeit für die Zeilenmarkierung beim Check In Verwendet werden soll
|
||||
|
||||
define('HideCheckInTimeOnCheckout', true); //Versteckt die CheckIn Zeit im Checkout
|
||||
define('HideCheckOutTimeOnCheckin', true); //Versteckt die CheckOut Zeit im CheckIn
|
||||
define('ShowCheckoutTimeOnCheckout', true); //Zeigt die Checkout Zeit im Checkout
|
||||
define('ShowVorbereitungTimeOnCheckout', true); //Zeigt die Vorbereitungs Zeitvariable im Checkout
|
||||
define('ShowPackagingTimeOnCheckout', true); //Zeigt die Packenzeit im Checkout
|
||||
define('ShowDeliveryTimeOnCheckout', true); //Zeigt die Lieferzeit im Checkout
|
||||
define('ShowTimesOnCheckout', true); //Aktiviert das anzeigen der Uhrzeit im Checkout
|
||||
// @hr
|
||||
define('ShowCheckInTimeOnCheckin', true); //Zeigt die CheckIn Zeit im CheckIn
|
||||
define('ShowNachbereitungTimeOnCheckin', true); //Zeigt die Nachbereitungs Zeitvariable im CheckIn
|
||||
define('ShowRePackagingTimeOnCheckin', true); //Zeigt die Rückpackzeit im Checkin <br><b>Achtung: Zeit nur Im Auftrag festlegbar</b>
|
||||
define('ShowReDeliveryTimeOnCheckin', true); //Zeigt die geplante Rücklieferung im Checkin
|
||||
define('ShowTimesOnCheckin', true); //Aktiviert das anzeigen der Uhrzeit im CheckIn
|
||||
|
||||
// @section: Epirent-Spezifische Einstellungen - Shipping
|
||||
define('ShowShippingIcons', true); //Zeigt Lieferung / Rücklieferungs Icons an
|
||||
define('KurierContainsText', 'Kurier'); //Text, der in der Versandart enthalten sein muss (enthält), damit diese als KURIER erkannt wird.
|
||||
define('SpeditionContainsText', 'Spedition'); //Text, der in der Versandart enthalten sein muss (enthält), damit diese als SPEDITION erkannt wird.
|
||||
define('DHLContainsText', 'DHL'); //Text, der in der Versandart enthalten sein muss (enthält), damit diese als DHL erkannt wird.
|
||||
define('LKWContainsText', 'LKW'); //Text, der in der Versandart enthalten sein muss (enthält), damit diese als eigener LKW erkannt wird.
|
||||
define('TransporterContainsText', 'Sprinter'); //Text, der in der Versandart enthalten sein muss (enthält), damit diese als eigener Transporter erkannt wird.
|
||||
define('PKWContainsText', 'PKW'); //Text, der in der Versandart enthalten sein muss (enthält), damit diese als eigener PKW erkannt wird.
|
||||
|
||||
define('UseShippingStatus', true); //Nutzt den Packscheinstatus als Shipping Status. Hierdurch können die Icons unter verschiedenen Vorraussetzungen Grün werden, wenn der Versand organisiert wurde
|
||||
define('ShippingOutOrganizedStatus', 'Hinversand OK'); //Packschein Status (EpirentDropdown) der Gematcht werden muss, dass der Hinversand Organisiert / Bestellt wurde
|
||||
define('ShippingInOrganizedStatus', 'Rückversand OK'); //Packschein Status (EpirentDropdown) der Gematcht werden muss, dass der Rückversand Organisiert / Bestellt wurde
|
||||
define('ShippingOrganizedStatus', 'Versand OK'); //Packschein Status (EpirentDropdown) der Gematcht werden muss, dass der komplette Versand Organisiert / Bestellt wurde
|
||||
|
||||
// @section: Epirent-Spezifische Einstellungen - Anzeigesortierung
|
||||
define('CheckOut_FutureDays', -1); // Konfiguration, wie viele Tage in der Zukunft der CheckOut angezeigt werden soll. '-1' zeigt alle an. Abhängig von der Variablen CheckOut_UseDispoStartForRowMarking
|
||||
define('CheckIn_FutureDays', -1); // Konfiguration, wie viele Tage in der Zukunft der CheckIn angezeigt werden soll. '-1' zeigt alle an. Abhängig von der Variablen CheckIn_UseDispoEndForRowMarking
|
||||
define('SortCheckOut', 2); // Konfiguration, welcher Datensatz für die Sortierung Verwendet werden soll. Möglichkeiten '1': Packscheinnummer / '2': Dispostart
|
||||
define('SortCheckIn', 2); // Konfiguration, welcher Datensatz für die Sortierung Verwendet werden soll. Möglichkeiten '1': Packscheinnummer / '2': Dispoende
|
||||
|
||||
// @section: Epirent-Spezifische Einstellungen - Labeldruck
|
||||
define('Labelprint_Logopath', '/../src/assets/img/logo.png'); //Pfad zum Logo für den Etikettendruck
|
||||
define('Labelprint_Addresstext', '<b>VT-Media</b><br/>Augsburger Straße 22<br/>87672 Roßhaupten<br/>info@vt-media.de<br/>vt-media.de<br/>08367/4619-990'); //Text der im Adressfeld der Etiketten angezeigt wird. max 6 Zeilen Empfohlen. Umbruch mit <br>. Fett mit <b>Text</b>
|
||||
?>
|
||||
|
||||
@@ -3,22 +3,35 @@
|
||||
<editor-bookmarks xmlns="http://www.netbeans.org/ns/editor-bookmarks/2" lastBookmarkId="0"/>
|
||||
<open-files xmlns="http://www.netbeans.org/ns/projectui-open-files/2">
|
||||
<group>
|
||||
<file>file:/C:/xampp/htdocs/EpiWebview/sources/getCheckOutCards.php</file>
|
||||
<file>file:/C:/xampp/htdocs/EpiWebview/dist/index.html</file>
|
||||
<file>file:/C:/xampp/htdocs/EpiWebview/.git/config</file>
|
||||
<file>file:/C:/xampp/htdocs/EpiWebview/PackAufgabenMonitor.php</file>
|
||||
<file>file:/C:/xampp/htdocs/EpiWebview/sources/getSidenav.php</file>
|
||||
<file>file:/C:/xampp/htdocs/EpiWebview/dist/index.php</file>
|
||||
<file>file:/C:/xampp/htdocs/EpiWebview/sources/getOrderDetails.php</file>
|
||||
<file>file:/C:/xampp/htdocs/EpiWebview/config.php</file>
|
||||
<file>file:/C:/xampp/htdocs/EpiWebview/EpiApi.php</file>
|
||||
<file>file:/C:/xampp/htdocs/EpiWebview/sources/getOrders.php</file>
|
||||
<file>file:/C:/xampp/htdocs/EpiWebview/dist/css/styles.css</file>
|
||||
<file>file:/C:/xampp/htdocs/EpiWebview/Aufgabenmonitor.php</file>
|
||||
<file>file:/C:/xampp/htdocs/EpiWebview/dist/layout-sidenav-light.html</file>
|
||||
<file>file:/C:/xampp/htdocs/EpiWebview/dist/editconfig.php</file>
|
||||
<file>file:/C:/xampp/htdocs/EpiWebview/composer.json</file>
|
||||
<file>file:/C:/xampp/htdocs/EpiWebview/sources/getCheckInTable.php</file>
|
||||
<file>file:/C:/xampp/htdocs/EpiWebview/CrewbrainApi.php</file>
|
||||
<file>file:/C:/xampp/htdocs/EpiWebview/example.config.php</file>
|
||||
<file>file:/C:/xampp/htdocs/EpiWebview/composer.lock</file>
|
||||
<file>file:/C:/xampp/htdocs/EpiWebview/info.php</file>
|
||||
<file>file:/C:/xampp/htdocs/EpiWebview/Packmonitor.php</file>
|
||||
<file>file:/C:/xampp/htdocs/EpiWebview/index.php</file>
|
||||
<file>file:/C:/xampp/htdocs/EpiWebview/.gitignore</file>
|
||||
<file>file:/C:/xampp/htdocs/EpiWebview/PackAufgabenMonitor.php</file>
|
||||
<file>file:/C:/xampp/htdocs/EpiWebview/sources/getSidenav.php</file>
|
||||
<file>file:/C:/xampp/htdocs/EpiWebview/README.MD</file>
|
||||
<file>file:/C:/xampp/htdocs/EpiWebview/config.php</file>
|
||||
<file>file:/C:/xampp/htdocs/EpiWebview/EpiApi.php</file>
|
||||
<file>file:/C:/xampp/htdocs/EpiWebview/sources/getOrders.php</file>
|
||||
<file>file:/C:/xampp/htdocs/EpiWebview/sources/getCheckInCards.php</file>
|
||||
<file>file:/C:/xampp/htdocs/EpiWebview/dist/orders.php</file>
|
||||
<file>file:/C:/xampp/htdocs/EpiWebview/dist/insurance.php</file>
|
||||
<file>file:/C:/xampp/htdocs/EpiWebview/LICENSE.MD</file>
|
||||
<file>file:/C:/xampp/htdocs/EpiWebview/vendor/autoload.php</file>
|
||||
<file>file:/C:/xampp/htdocs/EpiWebview/sources/getAufgabenTable.php</file>
|
||||
<file>file:/C:/xampp/htdocs/EpiWebview/sources/getCheckOutTable.php</file>
|
||||
</group>
|
||||
|
||||
@@ -62,9 +62,13 @@ foreach ($data_output as $aufgabe) {
|
||||
}
|
||||
echo "<td>";
|
||||
|
||||
|
||||
$i=0;
|
||||
foreach($AufgabeDetail->Responsibles as $bearbeiter){
|
||||
echo $bearbeiter->Name;
|
||||
if($i>0){
|
||||
echo ", ";
|
||||
}
|
||||
$i++;
|
||||
}
|
||||
echo "</td>";
|
||||
echo "<td>" . $aufgabe->Titel . "</td>";
|
||||
|
||||
193
sources/getCheckInCards.php
Normal file
193
sources/getCheckInCards.php
Normal file
@@ -0,0 +1,193 @@
|
||||
<?php
|
||||
// ../sources/getCheckInCards.php
|
||||
error_reporting(E_ALL);
|
||||
require('../config.php');
|
||||
require('../EpiApi.php');
|
||||
|
||||
$Epi = new Epirent();
|
||||
|
||||
use chillerlan\QRCode\{ QRCode, QROptions };
|
||||
require('../vendor/autoload.php');
|
||||
|
||||
const APP_TZ = 'Europe/Berlin';
|
||||
|
||||
/** ---------- Helpers (wie in deiner Check-In-Datei) ---------- */
|
||||
function dt(?string $s): ?DateTimeImmutable {
|
||||
if (!$s) return null;
|
||||
try { return new DateTimeImmutable($s, new DateTimeZone(APP_TZ)); }
|
||||
catch (Throwable $e) { return null; }
|
||||
}
|
||||
function dayStart(DateTimeImmutable $d): DateTimeImmutable {
|
||||
return $d->setTime(0, 0, 0);
|
||||
}
|
||||
function rowClassForDate(?DateTimeImmutable $markDate, ?DateTimeImmutable $today): string {
|
||||
if (!$markDate || !$today) return '';
|
||||
if ($markDate == $today) return 'text-dark bg-warning';
|
||||
if ($markDate < $today) return 'bg-danger';
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Row-Marking Quelle (Check-In) – identisch zu deiner Tabelle:
|
||||
* 1 = $packingjob->date_end
|
||||
* 2 = $NachbereitungsTimeDetail->date_start
|
||||
* 3 = $RePackagingTimeDetail->date_start
|
||||
* 4 = $PackingNoteDetail->date_redelivery
|
||||
* Fallback: $packingjob->date_end
|
||||
*/
|
||||
function resolveRowMarkDateCheckIn(
|
||||
$packingjob,
|
||||
$NachbereitungsTimeDetail,
|
||||
$RePackagingTimeDetail,
|
||||
$PackingNoteDetail,
|
||||
int $source
|
||||
): ?DateTimeImmutable {
|
||||
$candidate = null;
|
||||
switch ($source) {
|
||||
case 1: $candidate = dt($packingjob->date_end ?? null); break;
|
||||
case 2: $candidate = dt($NachbereitungsTimeDetail->date_start ?? null); break;
|
||||
case 3: $candidate = dt($RePackagingTimeDetail->date_start ?? null); break;
|
||||
case 4: $candidate = dt($PackingNoteDetail->date_redelivery ?? null); break;
|
||||
default: $candidate = null; break;
|
||||
}
|
||||
if (!$candidate) {
|
||||
$candidate = dt($packingjob->date_end ?? null);
|
||||
}
|
||||
return $candidate ? dayStart($candidate) : null;
|
||||
}
|
||||
|
||||
/** ---------- Daten holen (wie in deiner Check-In-Datei) ---------- */
|
||||
// Offene Packingnotes für Check-IN-Seite: isco=False
|
||||
$result = $Epi->requestEpiApi('/v1/packingnote/open?isco=False&cl=' . Epirent_Mandant);
|
||||
$data_output = json_decode($result)->payload ?? [];
|
||||
|
||||
/** ---------- Zähler ---------- */
|
||||
$today = dayStart(new DateTimeImmutable('today', new DateTimeZone(APP_TZ)));
|
||||
|
||||
$limit = null;
|
||||
if (CheckIn_FutureDays != -1) {
|
||||
$limit = $today->modify('+' . (int)CheckIn_FutureDays . ' day');
|
||||
}
|
||||
|
||||
$cntOverdue = 0;
|
||||
$cntToday = 0;
|
||||
$cntFuture = 0;
|
||||
|
||||
/** ---------- Zählen mit exakt deiner Logik ---------- */
|
||||
foreach ($data_output as $packingjob) {
|
||||
if ($packingjob->is_archived == true) continue;
|
||||
|
||||
// OrderDetails (für Nachbereitung / Rückpacken)
|
||||
$orderRes = $Epi->requestEpiApi('/v1/order/' . $packingjob->order_pk . '?cl=' . Epirent_Mandant);
|
||||
$orderdetail_output = json_decode($orderRes)->payload[0] ?? null;
|
||||
|
||||
// PackingNote Details (für Redelivery)
|
||||
$pnRes = $Epi->requestEpiApi('/v1/packingnote/' . $packingjob->primary_key . '?cl=' . Epirent_Mandant);
|
||||
$PackingNoteDetail = json_decode($pnRes)->payload[0] ?? null;
|
||||
|
||||
// Zeit-Slots aus dem Schedule
|
||||
$NachbereitungsTimeDetail = null;
|
||||
$RePackagingTimeDetail = null;
|
||||
if ($orderdetail_output && !empty($orderdetail_output->order_schedule)) {
|
||||
foreach ($orderdetail_output->order_schedule as $scheduledetail) {
|
||||
if ($scheduledetail->name == Nachbereitung_Zeitvariable) {
|
||||
$NachbereitungsTimeDetail = $scheduledetail;
|
||||
}
|
||||
if ($scheduledetail->name == Rückpacken_Zeitvariable) {
|
||||
$RePackagingTimeDetail = $scheduledetail;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Row-Mark Source (mit Override-Flag)
|
||||
$source = CheckIn_UseDispoEndForRowMarking ? 1 : (int)CheckInRowMarkSource;
|
||||
|
||||
$markDate = resolveRowMarkDateCheckIn(
|
||||
$packingjob,
|
||||
$NachbereitungsTimeDetail,
|
||||
$RePackagingTimeDetail,
|
||||
$PackingNoteDetail,
|
||||
$source
|
||||
);
|
||||
if (!$markDate) continue;
|
||||
|
||||
// Fenster berücksichtigen (gleich wie in deiner Tabelle)
|
||||
if (CheckIn_FutureDays != -1 && $limit && $markDate > $limit) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Zählen
|
||||
if ($markDate < $today) {
|
||||
$cntOverdue++;
|
||||
} elseif ($markDate == $today) {
|
||||
$cntToday++;
|
||||
} else {
|
||||
$cntFuture++;
|
||||
}
|
||||
}
|
||||
|
||||
$ts = (new DateTimeImmutable('now', new DateTimeZone(APP_TZ)))->format('H:i:s');
|
||||
?>
|
||||
<h3 class="mb-3">
|
||||
<span class="badge badge-secondary d-block w-100 py-2 text-center">CheckIn</span>
|
||||
</h3>
|
||||
<div class="row">
|
||||
<div class="col-xl-4 col-md-6">
|
||||
<div class="card bg-danger text-white mb-4">
|
||||
<div class="card-body d-flex justify-content-between align-items-center">
|
||||
<div>
|
||||
<div class="small">Überfällige CheckIns</div>
|
||||
<div class="h2 mb-0"><?= (int)$cntOverdue ?></div>
|
||||
</div>
|
||||
<i class="fa-solid fa-triangle-exclamation fa-2x" style="opacity:.6"></i>
|
||||
</div>
|
||||
<div class="card-footer d-flex justify-content-between">
|
||||
<span class="small text-white-50">Stand <?= htmlspecialchars($ts) ?></span>
|
||||
<span class="small">bis <?= htmlspecialchars($today->format('d.m.Y')) ?></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-xl-4 col-md-6">
|
||||
<div class="card bg-warning text-dark mb-4">
|
||||
<div class="card-body d-flex justify-content-between align-items-center">
|
||||
<div>
|
||||
<div class="small">Heutige CheckIns</div>
|
||||
<div class="h2 mb-0"><?= (int)$cntToday ?></div>
|
||||
</div>
|
||||
<i class="fa-solid fa-boxes-packing fa-2x" style="opacity:.6"></i>
|
||||
</div>
|
||||
<div class="card-footer d-flex justify-content-between">
|
||||
<span class="small text-muted">Stand <?= htmlspecialchars($ts) ?></span>
|
||||
<span class="small"><?= htmlspecialchars($today->format('d.m.Y')) ?></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-xl-4 col-md-6">
|
||||
<div class="card bg-success text-white mb-4">
|
||||
<div class="card-body d-flex justify-content-between align-items-center">
|
||||
<div>
|
||||
<div class="small">
|
||||
Zukünftige CheckIns
|
||||
<?php if (CheckIn_FutureDays != -1 && $limit): ?>
|
||||
(≤ <?= htmlspecialchars($limit->format('d.m.Y')) ?>)
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<div class="h2 mb-0"><?= (int)$cntFuture ?></div>
|
||||
</div>
|
||||
<i class="fa-solid fa-forward-step fa-2x" style="opacity:.6"></i>
|
||||
</div>
|
||||
<div class="card-footer d-flex justify-content-between">
|
||||
<span class="small text-white-50">Stand <?= htmlspecialchars($ts) ?></span>
|
||||
<span class="small">
|
||||
<?php if (CheckIn_FutureDays == -1): ?>
|
||||
unbegrenzt
|
||||
<?php else: ?>
|
||||
bis <?= htmlspecialchars($limit->format('d.m.Y')) ?>
|
||||
<?php endif; ?>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -10,148 +10,250 @@ use chillerlan\QRCode\{
|
||||
|
||||
require('../vendor/autoload.php');
|
||||
|
||||
|
||||
|
||||
$options = new QROptions([
|
||||
'imageBase64' => false,
|
||||
'qrCodeHeight' => 75,
|
||||
'qrCodeWidth' => 75,
|
||||
'version' => -1,
|
||||
'quietzoneSize' => 1
|
||||
]);
|
||||
|
||||
]);
|
||||
|
||||
$Epi = new Epirent();
|
||||
|
||||
|
||||
$result = $Epi->requestEpiApi('/v1/packingnote/open?isco=False&cl=' . Epirent_Mandant);
|
||||
|
||||
$data_output = json_decode($result)->payload;
|
||||
|
||||
/** ---------- Helpers & Marking (analog Checkout) ---------- */
|
||||
const APP_TZ = 'Europe/Berlin';
|
||||
|
||||
function dt(?string $s): ?DateTimeImmutable {
|
||||
if (!$s) return null;
|
||||
try {
|
||||
return new DateTimeImmutable($s, new DateTimeZone(APP_TZ));
|
||||
} catch (Throwable $e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function dayStart(DateTimeImmutable $d): DateTimeImmutable {
|
||||
return $d->setTime(0, 0, 0);
|
||||
}
|
||||
|
||||
function rowClassForDate(?DateTimeImmutable $markDate, ?DateTimeImmutable $today): string {
|
||||
if (!$markDate || !$today) return '';
|
||||
if ($markDate == $today) return 'text-dark bg-warning';
|
||||
if ($markDate < $today) return 'bg-danger';
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Row-Marking Quelle (Check-In):
|
||||
* 1 = $packingjob->date_end
|
||||
* 2 = $NachbereitungsTimeDetail->date_start
|
||||
* 3 = $RePackagingTimeDetail->date_start
|
||||
* 4 = $PackingNoteDetail->date_redelivery
|
||||
* Fallback: $packingjob->date_end
|
||||
*/
|
||||
function resolveRowMarkDateCheckIn(
|
||||
$packingjob,
|
||||
$NachbereitungsTimeDetail,
|
||||
$RePackagingTimeDetail,
|
||||
$PackingNoteDetail,
|
||||
int $source
|
||||
): ?DateTimeImmutable {
|
||||
$candidate = null;
|
||||
switch ($source) {
|
||||
case 1: $candidate = dt($packingjob->date_end ?? null); break;
|
||||
case 2: $candidate = dt($NachbereitungsTimeDetail->date_start ?? null); break;
|
||||
case 3: $candidate = dt($RePackagingTimeDetail->date_start ?? null); break;
|
||||
case 4: $candidate = dt($PackingNoteDetail->date_redelivery ?? null); break;
|
||||
default: $candidate = null; break;
|
||||
}
|
||||
if (!$candidate) {
|
||||
$candidate = dt($packingjob->date_end ?? null);
|
||||
}
|
||||
return $candidate ? dayStart($candidate) : null;
|
||||
}
|
||||
|
||||
function echoMarkedTimeLine(?string $dateStr, ?int $timeSeconds, DateTimeImmutable $today, $ReturnTime): void {
|
||||
if (!$dateStr) return;
|
||||
$d = dt($dateStr);
|
||||
if (!$d) return;
|
||||
|
||||
$mark = dayStart($d);
|
||||
$cls = rowClassForDate($mark, $today);
|
||||
|
||||
echo $cls ? "<span class=\"{$cls}\">" : "";
|
||||
echo date_format(new \DateTime($dateStr), 'd.m.Y');
|
||||
if ($ReturnTime) {
|
||||
echo " " . getTimeFromSeconds((string)$timeSeconds);
|
||||
}
|
||||
echo $cls ? "</span>" : "";
|
||||
}
|
||||
|
||||
/** ---------- Sortierung nach Ende ---------- */
|
||||
if (SortCheckIn == 2) {
|
||||
|
||||
|
||||
|
||||
// Prüfen, ob $data_output ein Array ist
|
||||
if (is_array($data_output)) {
|
||||
usort($data_output, function ($a, $b) {
|
||||
// Konvertiere time_start von Millisekunden in Sekunden
|
||||
$timeStartA = $a->time_end / 1000; // Zeit in Sekunden
|
||||
$timeStartB = $b->time_end / 1000;
|
||||
|
||||
// Kombiniere date_start mit time_start
|
||||
$datetimeA = strtotime($a->date_end) + $timeStartA;
|
||||
$datetimeB = strtotime($b->date_end) + $timeStartB;
|
||||
|
||||
// Vergleich für die Sortierung
|
||||
$timeEndA = $a->time_end / 1000;
|
||||
$timeEndB = $b->time_end / 1000;
|
||||
$datetimeA = strtotime($a->date_end) + $timeEndA;
|
||||
$datetimeB = strtotime($b->date_end) + $timeEndB;
|
||||
return $datetimeA <=> $datetimeB;
|
||||
});
|
||||
|
||||
// Sortierte Daten ausgeben oder weiterverarbeiten
|
||||
// print_r($data_output);
|
||||
} else {
|
||||
echo "Daten konnten nicht verarbeitet werden.";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** ---------- Tabelle ---------- */
|
||||
foreach ($data_output as $packingjob) {
|
||||
|
||||
|
||||
|
||||
if ($packingjob->is_archived != true) {
|
||||
|
||||
//get OrderDetails
|
||||
// OrderDetails
|
||||
$result = $Epi->requestEpiApi('/v1/order/' . $packingjob->order_pk . '?cl=' . Epirent_Mandant);
|
||||
|
||||
$orderdetail_output = json_decode($result)->payload[0];
|
||||
|
||||
$NachbereitungsTimeDetail;
|
||||
// PackingNote Details
|
||||
$PackingNoteDetailResult = $Epi->requestEpiApi('/v1/packingnote/' . $packingjob->primary_key . '?cl=' . Epirent_Mandant);
|
||||
$PackingNoteDetail = json_decode($PackingNoteDetailResult)->payload[0];
|
||||
|
||||
// Zeit-Slots aus dem Schedule
|
||||
$NachbereitungsTimeDetail = null;
|
||||
$RePackagingTimeDetail = null;
|
||||
|
||||
foreach ($orderdetail_output->order_schedule as $scheduledetail) {
|
||||
|
||||
if ($scheduledetail->name == Rueckpacken_Zeitvariable) {
|
||||
if ($scheduledetail->name == Nachbereitung_Zeitvariable) {
|
||||
$NachbereitungsTimeDetail = $scheduledetail;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//End Of get Order Details
|
||||
|
||||
|
||||
|
||||
if (CheckIn_UseDispoEndForRowMarking || ($NachbereitungsTimeDetail->date_start == null)) {
|
||||
$date = new DateTime($packingjob->date_end);
|
||||
} else {
|
||||
$date = new DateTime($NachbereitungsTimeDetail->date_start);
|
||||
}
|
||||
$date->setTime(0, 0, 0);
|
||||
$today = new DateTime();
|
||||
$today->setTime(0, 0, 0);
|
||||
$todayFilter = new DateTime();
|
||||
$todayFilter->setTime(0, 0, 0);
|
||||
if (CheckIn_FutureDays == -1 || $date <= ($todayFilter->modify('+' . CheckIn_FutureDays . ' day'))) {
|
||||
//prüfe, ob entweder unbegrenzte (-1) Anzeige Aktiv ist, oder das Datum kleiner oder Gleich heute + Zukunftsspanne ist
|
||||
|
||||
if ($date == $today) {
|
||||
|
||||
echo "<tr class='text-dark bg-warning'>";
|
||||
} else if ($date < $today) {
|
||||
echo "<tr class=' bg-danger'>";
|
||||
} else {
|
||||
echo "<tr>";
|
||||
if ($scheduledetail->name == Rückpacken_Zeitvariable) {
|
||||
$RePackagingTimeDetail = $scheduledetail;
|
||||
}
|
||||
}
|
||||
|
||||
// --- Row-Marking bestimmen (konfigurierbar, analog Checkout) ---
|
||||
$today = dayStart(new DateTimeImmutable('today', new DateTimeZone(APP_TZ)));
|
||||
$limit = null;
|
||||
if (CheckIn_FutureDays != -1) {
|
||||
$limit = $today->modify('+' . (int)CheckIn_FutureDays . ' day');
|
||||
}
|
||||
|
||||
// Override-Flag wie bei Checkout: Dispo-Ende erzwingen
|
||||
$source = CheckIn_UseDispoEndForRowMarking ? 1 : (int)CheckInRowMarkSource;
|
||||
|
||||
$markDate = resolveRowMarkDateCheckIn(
|
||||
$packingjob,
|
||||
$NachbereitungsTimeDetail,
|
||||
$RePackagingTimeDetail,
|
||||
$PackingNoteDetail,
|
||||
$source
|
||||
);
|
||||
|
||||
if (CheckIn_FutureDays == -1 || ($markDate && (!$limit || $markDate <= $limit))) {
|
||||
$trClass = rowClassForDate($markDate, $today);
|
||||
echo $trClass ? "<tr class='{$trClass}'>" : "<tr>";
|
||||
|
||||
// QR / Kopfspalten
|
||||
if (Enable_QR_Code_CheckIn) {
|
||||
echo "<td>" . '<div style="width: 5vb;">' . (new QRCode($options))->render($packingjob->packingnote_no) . "</div></td>";
|
||||
echo "<td><div style=\"width: 5vb;\">" . (new QRCode($options))->render($packingjob->packingnote_no) . "</div></td>";
|
||||
} else {
|
||||
echo "<td>" . $packingjob->packingnote_no . "</td>";
|
||||
}
|
||||
echo "<td>" . $packingjob->contact->name . "</td>";
|
||||
echo "<td>" . $packingjob->event . "</td>";
|
||||
if(!HideCheckOutTimeOnCheckin){
|
||||
if ($NachbereitungsTimeDetail->date_start != null) {
|
||||
echo "<td><small>" . date_format(new \DateTime($packingjob->date_start), 'd.m.Y') . " " . getTimeFromSeconds($packingjob->time_start) . "</small><br><i>" . date_format(new \DateTime($NachbereitungsTimeDetail->date_start), 'd.m.Y') . " " . getTimeFromSeconds($NachbereitungsTimeDetail->time_start) . "</i></td>";
|
||||
} else {
|
||||
echo "<td>" . date_format(new \DateTime($packingjob->date_start), 'd.m.Y') . " " . getTimeFromSeconds($packingjob->time_start) . "</td>";
|
||||
|
||||
// Zeitspalte öffnen, wenn mindestens eine Anzeige aktiv ist
|
||||
if (ShowCheckInTimeOnCheckin || ShowNachbereitungTimeOnCheckin || ShowRePackagingTimeOnCheckin || ShowReDeliveryTimeOnCheckin) {
|
||||
echo "<td>";
|
||||
}
|
||||
|
||||
// Dispo-Ende (Check-In Termin) — ENDE korrekt verwendet
|
||||
if (ShowCheckInTimeOnCheckin && $packingjob->date_end != null) {
|
||||
echoMarkedTimeLine($packingjob->date_end, (int)$packingjob->time_end, $today, ShowTimesOnCheckin);
|
||||
}
|
||||
if ($NachbereitungsTimeDetail->date_end != null) {
|
||||
echo "<td><small>" . date_format(new \DateTime($packingjob->date_end), 'd.m.Y') . " " . getTimeFromSeconds($packingjob->time_end) . "</small><br><i>" . date_format(new \DateTime($NachbereitungsTimeDetail->date_end), 'd.m.Y') . " " . getTimeFromSeconds($NachbereitungsTimeDetail->time_end) . "</i></td>";
|
||||
} else {
|
||||
echo "<td>" . date_format(new \DateTime($packingjob->date_end), 'd.m.Y') . " " . getTimeFromSeconds($packingjob->time_end) . "</td>";
|
||||
|
||||
// Nachbereitung (START) — wie von dir bereits genutzt
|
||||
if (ShowNachbereitungTimeOnCheckin && $NachbereitungsTimeDetail && $NachbereitungsTimeDetail->date_start != null) {
|
||||
if (ShowCheckInTimeOnCheckin) echo "<br>";
|
||||
echoMarkedTimeLine($NachbereitungsTimeDetail->date_start, (int)$NachbereitungsTimeDetail->time_start, $today, ShowTimesOnCheckin);
|
||||
}
|
||||
|
||||
// Rückpacken (START)
|
||||
if (ShowRePackagingTimeOnCheckin && $RePackagingTimeDetail && $RePackagingTimeDetail->date_start != null) {
|
||||
if (ShowCheckInTimeOnCheckin || ShowNachbereitungTimeOnCheckin) echo "<br>";
|
||||
echoMarkedTimeLine($RePackagingTimeDetail->date_start, (int)$RePackagingTimeDetail->time_start, $today, ShowTimesOnCheckin);
|
||||
}
|
||||
|
||||
// Rücklieferung (REDELIVERY) — delivery ↔ redelivery gespiegelt
|
||||
if (ShowReDeliveryTimeOnCheckin && $PackingNoteDetail && $PackingNoteDetail->date_redelivery != null) {
|
||||
if (ShowCheckInTimeOnCheckin || ShowNachbereitungTimeOnCheckin || ShowRePackagingTimeOnCheckin) echo "<br>";
|
||||
echoMarkedTimeLine($PackingNoteDetail->date_redelivery, (int)$PackingNoteDetail->time_redelivery, $today, ShowTimesOnCheckin);
|
||||
}
|
||||
|
||||
if (ShowCheckInTimeOnCheckin || ShowNachbereitungTimeOnCheckin || ShowRePackagingTimeOnCheckin || ShowReDeliveryTimeOnCheckin) {
|
||||
echo "</td>";
|
||||
}
|
||||
|
||||
// Fortschritt
|
||||
echo "<td>";
|
||||
|
||||
|
||||
|
||||
if ($packingjob->is_all_in ==0) {
|
||||
if ($packingjob->is_all_in == 0) {
|
||||
echo "<span class='badge badge-success'>";
|
||||
} else {
|
||||
|
||||
echo '<div class="progress"><div class="progress-bar progress-bar-striped progress-bar-animated" role="progressbar" style="width: '.(( $packingjob->pieces_sum_total - abs($packingjob->is_all_out) - abs($packingjob->is_all_in)) / ($packingjob->pieces_sum_total - $packingjob ->is_all_out)) *100 .'%" aria-valuenow="' .( $packingjob->pieces_sum_total - abs($packingjob->is_all_out) - abs($packingjob->is_all_in)) . '" aria-valuemin="0" aria-valuemax="' . ($packingjob->pieces_sum_total - $packingjob ->is_all_out). '"></div></div>';
|
||||
|
||||
echo '<div class="progress"><div class="progress-bar progress-bar-striped progress-bar-animated" role="progressbar" style="width: ' .
|
||||
(( $packingjob->pieces_sum_total - abs($packingjob->is_all_out) - abs($packingjob->is_all_in)) / ($packingjob->pieces_sum_total - $packingjob->is_all_out)) * 100 .
|
||||
'%" aria-valuenow="' . ( $packingjob->pieces_sum_total - abs($packingjob->is_all_out) - abs($packingjob->is_all_in)) .
|
||||
'" aria-valuemin="0" aria-valuemax="' . ($packingjob->pieces_sum_total - $packingjob->is_all_out) . '"></div></div>';
|
||||
echo "<span class='badge badge-info'>";
|
||||
}
|
||||
|
||||
echo ( $packingjob->pieces_sum_total - abs($packingjob->is_all_out) - abs($packingjob->is_all_in)) . "/" . ($packingjob->pieces_sum_total - $packingjob ->is_all_out) . " (" . $packingjob->pieces_sum_total.")";
|
||||
echo "</span><td>";
|
||||
|
||||
echo ( $packingjob->pieces_sum_total - abs($packingjob->is_all_out) - abs($packingjob->is_all_in)) . "/" . ($packingjob->pieces_sum_total - $packingjob->is_all_out) . " (" . $packingjob->pieces_sum_total . ")";
|
||||
echo "</span></td>";
|
||||
|
||||
// Shipping-Icons (inbound)
|
||||
if (ShowShippingIcons) {
|
||||
if (UseShippingStatus) {
|
||||
if (($PackingNoteDetail->status == ShippingInOrganizedStatus) || ($PackingNoteDetail->status == ShippingOrganizedStatus)) {
|
||||
echo "<td style='color:#66FF00; text-align:center;'>";
|
||||
} else {
|
||||
echo "<td style='text-align:center;'>";
|
||||
}
|
||||
|
||||
if ($PackingNoteDetail->is_self_redeliver) {
|
||||
echo '<i class="fa-solid fa-person-walking fa-lg"></i>';
|
||||
} else {
|
||||
if (preg_match('/' . KurierContainsText . '/i', $PackingNoteDetail->shipping_in)) {
|
||||
echo '<i class="fa-solid fa-hand-holding-dollar fa-lg"></i>';
|
||||
}
|
||||
if (preg_match('/' . SpeditionContainsText . '/i', $PackingNoteDetail->shipping_in)) {
|
||||
echo '<i class="fa-solid fa-truck fa-lg"></i>';
|
||||
}
|
||||
if (preg_match('/' . DHLContainsText . '/i', $PackingNoteDetail->shipping_in)) {
|
||||
echo '<i class="fa-brands fa-dhl fa-2xl"></i>';
|
||||
}
|
||||
if (preg_match('/' . LKWContainsText . '/i', $PackingNoteDetail->shipping_in)) {
|
||||
echo '<i class="fa-solid fa-industry fa-lg"></i><i class="fa-solid fa-truck fa-lg"></i>';
|
||||
}
|
||||
if (preg_match('/' . TransporterContainsText . '/i', $PackingNoteDetail->shipping_in)) {
|
||||
echo '<i class="fa-solid fa-industry fa-lg"></i><i class="fa-solid fa-van-shuttle fa-lg"></i>';
|
||||
}
|
||||
if (preg_match('/' . PKWContainsText . '/i', $PackingNoteDetail->shipping_in)) {
|
||||
echo '<i class="fa-solid fa-industry fa-lg"></i><i class="fa-solid fa-car fa-lg"></i>';
|
||||
}
|
||||
}
|
||||
echo "</td>";
|
||||
}
|
||||
|
||||
echo "</tr>";
|
||||
}
|
||||
|
||||
echo "</tr>";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getTimeFromSeconds(string $timestring) {
|
||||
|
||||
$hours = floor($timestring / 3600);
|
||||
$mins = floor($timestring / 60 % 60);
|
||||
$secs = floor($timestring % 60);
|
||||
|
||||
$mins = floor($timestring / 60 % 60);
|
||||
$secs = floor($timestring % 60);
|
||||
$timeFormat = sprintf('%02d:%02d', $hours, $mins);
|
||||
|
||||
return $timeFormat;
|
||||
}
|
||||
|
||||
?>
|
||||
189
sources/getCheckOutCards.php
Normal file
189
sources/getCheckOutCards.php
Normal file
@@ -0,0 +1,189 @@
|
||||
<?php
|
||||
// sources/getCheckoutTable.php – zählt statt Tabellenausgabe drei KPIs als Karten
|
||||
error_reporting(E_ALL);
|
||||
require('../config.php');
|
||||
require('../EpiApi.php');
|
||||
|
||||
$Epi = new Epirent();
|
||||
|
||||
const APP_TZ = 'Europe/Berlin';
|
||||
|
||||
/** ---- Helpers (aus deiner bisherigen Datei) ---- */
|
||||
function dt(?string $s): ?DateTimeImmutable {
|
||||
if (!$s) return null;
|
||||
try { return new DateTimeImmutable($s, new DateTimeZone(APP_TZ)); }
|
||||
catch (Throwable $e) { return null; }
|
||||
}
|
||||
function dayStart(DateTimeImmutable $d): DateTimeImmutable {
|
||||
return $d->setTime(0, 0, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Quelle für das Markierungsdatum (identisch zu deiner Liste):
|
||||
* 1 = $packingjob->date_start
|
||||
* 2 = $VorbereitungsTimeDetail->date_start
|
||||
* 3 = $PackingNoteDetail->date_packing
|
||||
* 4 = $PackingNoteDetail->date_delivery
|
||||
* Fallback: $packingjob->date_start
|
||||
*/
|
||||
function resolveRowMarkDate($packingjob, $VorbereitungsTimeDetail, $PackingNoteDetail, int $source): ?DateTimeImmutable {
|
||||
$candidate = null;
|
||||
switch ($source) {
|
||||
case 1: $candidate = dt($packingjob->date_start ?? null); break;
|
||||
case 2: $candidate = dt($VorbereitungsTimeDetail->date_start ?? null); break;
|
||||
case 3: $candidate = dt($PackingNoteDetail->date_packing ?? null); break;
|
||||
case 4: $candidate = dt($PackingNoteDetail->date_delivery ?? null); break;
|
||||
default:$candidate = null; break;
|
||||
}
|
||||
if (!$candidate) $candidate = dt($packingjob->date_start ?? null);
|
||||
return $candidate ? dayStart($candidate) : null;
|
||||
}
|
||||
|
||||
/** ---- Daten laden wie bisher (Checkout-Quelle) ---- */
|
||||
if (UseDeliveredForCheckOutCompleted) {
|
||||
// isco=False -> Checkout-Ansicht offen
|
||||
$result = $Epi->requestEpiApi('/v1/packingnote/open?isco=False&cl=' . Epirent_Mandant);
|
||||
} else {
|
||||
// isci=False -> Alternative Logik (wie in deiner Datei)
|
||||
$result = $Epi->requestEpiApi('/v1/packingnote/open?isci=False&cl=' . Epirent_Mandant);
|
||||
}
|
||||
$data_output = json_decode($result)->payload ?? [];
|
||||
|
||||
/** ---- Zähler vorbereiten ---- */
|
||||
$today = dayStart(new DateTimeImmutable('today', new DateTimeZone(APP_TZ)));
|
||||
$limit = null; // optionales Fenster wie in deiner Liste
|
||||
if (CheckOut_FutureDays != -1) {
|
||||
$limit = $today->modify('+' . (int)CheckOut_FutureDays . ' day');
|
||||
}
|
||||
|
||||
$cntOverdue = 0;
|
||||
$cntToday = 0;
|
||||
$cntFuture = 0;
|
||||
|
||||
/** ---- Iteration identisch zur bisherigen Logik ---- */
|
||||
foreach ($data_output as $packingjob) {
|
||||
|
||||
// Details nur holen, wenn nötig & wie gehabt
|
||||
$PackingNoteDetail = null;
|
||||
if ($packingjob->is_archived != true && UseDeliveredForCheckOutCompleted) {
|
||||
$PackingNoteDetailResult = $Epi->requestEpiApi('/v1/packingnote/' . $packingjob->primary_key . '?cl=' . Epirent_Mandant);
|
||||
$PackingNoteDetail = json_decode($PackingNoteDetailResult)->payload[0] ?? null;
|
||||
}
|
||||
|
||||
// Aufnahmebedingung exakt wie bei dir:
|
||||
if (
|
||||
$packingjob->is_archived != true
|
||||
&& (
|
||||
!UseDeliveredForCheckOutCompleted
|
||||
|| ($PackingNoteDetail && ($PackingNoteDetail->date_delivered !== "0000-00-00" || (int)$PackingNoteDetail->time_delivered !== 0))
|
||||
)
|
||||
&& ($PackingNoteDetail && (int)$PackingNoteDetail->is_all_out !== 0)
|
||||
) {
|
||||
// OrderDetails (für Vorbereitungszeit)
|
||||
$result = $Epi->requestEpiApi('/v1/order/' . $packingjob->order_pk . '?cl=' . Epirent_Mandant);
|
||||
$orderdetail_output = (json_decode($result)->payload[0] ?? null);
|
||||
|
||||
// PackingNoteDetail nachladen, falls oben noch nicht geholt
|
||||
if (!UseDeliveredForCheckOutCompleted) {
|
||||
$PackingNoteDetailResult = $Epi->requestEpiApi('/v1/packingnote/' . $packingjob->primary_key . '?cl=' . Epirent_Mandant);
|
||||
$PackingNoteDetail = json_decode($PackingNoteDetailResult)->payload[0] ?? null;
|
||||
if (!$PackingNoteDetail) continue;
|
||||
}
|
||||
|
||||
// Vorbereitungs-Zeitdetail ermitteln
|
||||
$VorbereitungsTimeDetail = null;
|
||||
if ($orderdetail_output && !empty($orderdetail_output->order_schedule)) {
|
||||
foreach ($orderdetail_output->order_schedule as $scheduledetail) {
|
||||
if ($scheduledetail->name == Vorbereitungs_Zeitvariable) {
|
||||
$VorbereitungsTimeDetail = $scheduledetail;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Markierungsdatum wie in der Tabelle
|
||||
$markDate = resolveRowMarkDate($packingjob, $VorbereitungsTimeDetail, $PackingNoteDetail, (int)CheckOutRowMarkSource);
|
||||
if (!$markDate) continue;
|
||||
|
||||
// Optionales Zukunftsfenster berücksichtigen (wie bisher)
|
||||
if (CheckOut_FutureDays != -1 && $limit && $markDate > $limit) {
|
||||
// außerhalb des Fensters ignorieren
|
||||
continue;
|
||||
}
|
||||
|
||||
// Zählen
|
||||
if ($markDate < $today) {
|
||||
$cntOverdue++;
|
||||
} elseif ($markDate == $today) {
|
||||
$cntToday++;
|
||||
} else { // $markDate > $today
|
||||
$cntFuture++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Zeitstempel für die Karten
|
||||
$ts = (new DateTimeImmutable('now', new DateTimeZone(APP_TZ)))->format('H:i:s');
|
||||
|
||||
/** ---- Ausgabe: 3 Bootstrap-Karten ---- */
|
||||
?>
|
||||
<h3 class="mb-3">
|
||||
<span class="badge badge-secondary d-block w-100 py-2 text-center">Checkout</span>
|
||||
</h3>
|
||||
|
||||
<div class="row">
|
||||
|
||||
<div class="col-xl-4 col-md-6">
|
||||
<div class="card bg-danger text-white mb-4">
|
||||
<div class="card-body d-flex justify-content-between align-items-center">
|
||||
<div>
|
||||
<div class="small">Überfällige CheckOuts</div>
|
||||
<div class="h2 mb-0"><?= (int)$cntOverdue ?></div>
|
||||
</div>
|
||||
<i class="fa-solid fa-triangle-exclamation fa-2x" style="opacity:.6"></i>
|
||||
</div>
|
||||
<div class="card-footer d-flex justify-content-between">
|
||||
<span class="small text-white-50">Stand <?= htmlspecialchars($ts) ?></span>
|
||||
<span class="small">bis <?= htmlspecialchars($today->format('d.m.Y')) ?></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-xl-4 col-md-6">
|
||||
<div class="card bg-warning text-dark mb-4">
|
||||
<div class="card-body d-flex justify-content-between align-items-center">
|
||||
<div>
|
||||
<div class="small">Heutige CheckOuts</div>
|
||||
<div class="h2 mb-0"><?= (int)$cntToday ?></div>
|
||||
</div>
|
||||
<i class="fa-solid fa-dolly fa-2x" style="opacity:.6"></i>
|
||||
</div>
|
||||
<div class="card-footer d-flex justify-content-between">
|
||||
<span class="small text-muted">Stand <?= htmlspecialchars($ts) ?></span>
|
||||
<span class="small"><?= htmlspecialchars($today->format('d.m.Y')) ?></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-xl-4 col-md-6">
|
||||
<div class="card bg-success text-white mb-4">
|
||||
<div class="card-body d-flex justify-content-between align-items-center">
|
||||
<div>
|
||||
<div class="small">Zukünftige CheckOuts<?= (CheckOut_FutureDays != -1 ? ' (≤ '.$limit->format('d.m.Y').')' : '') ?></div>
|
||||
<div class="h2 mb-0"><?= (int)$cntFuture ?></div>
|
||||
</div>
|
||||
<i class="fa-solid fa-forward-step fa-2x" style="opacity:.6"></i>
|
||||
</div>
|
||||
<div class="card-footer d-flex justify-content-between">
|
||||
<span class="small text-white-50">Stand <?= htmlspecialchars($ts) ?></span>
|
||||
<span class="small">
|
||||
<?php if (CheckOut_FutureDays == -1): ?>
|
||||
unbegrenzt
|
||||
<?php else: ?>
|
||||
bis <?= htmlspecialchars($limit->format('d.m.Y')) ?>
|
||||
<?php endif; ?>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,5 +1,6 @@
|
||||
<?php
|
||||
|
||||
|
||||
error_reporting(E_ALL);
|
||||
require('../config.php');
|
||||
require('../EpiApi.php');
|
||||
@@ -19,20 +20,65 @@ $options = new QROptions([
|
||||
'quietzoneSize' => 1
|
||||
]);
|
||||
|
||||
|
||||
$Epi = new Epirent();
|
||||
|
||||
const APP_TZ = 'Europe/Berlin';
|
||||
|
||||
$result = $Epi->requestEpiApi('/v1/packingnote/open?isci=False&cl=' . Epirent_Mandant);
|
||||
/** Hilfsfunktionen für die Row-Marking-Logik (ohne weitere Änderungen am Rest) */
|
||||
function dt(?string $s): ?DateTimeImmutable {
|
||||
if (!$s)
|
||||
return null;
|
||||
try {
|
||||
return new DateTimeImmutable($s, new DateTimeZone(APP_TZ));
|
||||
} catch (Throwable $e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function dayStart(DateTimeImmutable $d): DateTimeImmutable {
|
||||
return $d->setTime(0, 0, 0);
|
||||
}
|
||||
|
||||
function resolveRowMarkDate($packingjob, $VorbereitungsTimeDetail, $PackingNoteDetail, int $source): ?DateTimeImmutable {
|
||||
$candidate = null;
|
||||
switch ($source) {
|
||||
case 1: $candidate = dt($packingjob->date_start ?? null);
|
||||
break;
|
||||
case 2: $candidate = dt($VorbereitungsTimeDetail->date_start ?? null);
|
||||
break;
|
||||
case 3: $candidate = dt($PackingNoteDetail->date_packing ?? null);
|
||||
break;
|
||||
case 4: $candidate = dt($PackingNoteDetail->date_delivery ?? null);
|
||||
break;
|
||||
default: $candidate = null;
|
||||
break;
|
||||
}
|
||||
if (!$candidate) {
|
||||
$candidate = dt($packingjob->date_start ?? null);
|
||||
}
|
||||
return $candidate ? dayStart($candidate) : null;
|
||||
}
|
||||
|
||||
function rowClassForDate(?DateTimeImmutable $markDate, ?DateTimeImmutable $today): string {
|
||||
if (!$markDate || !$today)
|
||||
return '';
|
||||
if ($markDate == $today)
|
||||
return 'text-dark bg-warning';
|
||||
if ($markDate < $today)
|
||||
return 'bg-danger';
|
||||
return '';
|
||||
}
|
||||
|
||||
if (UseDeliveredForCheckOutCompleted) {
|
||||
$result = $Epi->requestEpiApi('/v1/packingnote/open?isci=true&cl=' . Epirent_Mandant);
|
||||
|
||||
} else {
|
||||
$result = $Epi->requestEpiApi('/v1/packingnote/open?isci=False&cl=' . Epirent_Mandant);
|
||||
}
|
||||
$data_output = json_decode($result)->payload;
|
||||
|
||||
|
||||
|
||||
if (SortCheckOut == 2) {
|
||||
|
||||
|
||||
|
||||
// Prüfen, ob $data_output ein Array ist
|
||||
if (is_array($data_output)) {
|
||||
usort($data_output, function ($a, $b) {
|
||||
@@ -57,45 +103,56 @@ if (SortCheckOut == 2) {
|
||||
|
||||
foreach ($data_output as $packingjob) {
|
||||
|
||||
$PackingNoteDetail = null;
|
||||
|
||||
if ($packingjob->is_archived != true && UseDeliveredForCheckOutCompleted) {
|
||||
$PackingNoteDetailResult = $Epi->requestEpiApi('/v1/packingnote/' . $packingjob->primary_key . '?cl=' . Epirent_Mandant);
|
||||
$PackingNoteDetail = json_decode($PackingNoteDetailResult)->payload[0];
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
if ($packingjob->is_archived != true) {
|
||||
|
||||
if (
|
||||
($packingjob->is_archived ?? false) != true
|
||||
&& (
|
||||
((int)($PackingNoteDetail->is_all_out ?? 0)) !== 0
|
||||
|| (($PackingNoteDetail->date_delivered ?? "0000-00-00") === "0000-00-00"
|
||||
|| (int)($PackingNoteDetail->time_delivered ?? 0) === 0)
|
||||
)
|
||||
) {
|
||||
//get OrderDetails
|
||||
$result = $Epi->requestEpiApi('/v1/order/' . $packingjob->order_pk . '?cl=' . Epirent_Mandant);
|
||||
|
||||
$orderdetail_output = json_decode($result)->payload[0];
|
||||
|
||||
$VorbereitungsTimeDetail;
|
||||
// get PackingNote Details, aber nur wenn nicht schon vor der schleife geholt.
|
||||
if (!UseDeliveredForCheckOutCompleted) {
|
||||
$PackingNoteDetailResult = $Epi->requestEpiApi('/v1/packingnote/' . $packingjob->primary_key . '?cl=' . Epirent_Mandant);
|
||||
$PackingNoteDetail = json_decode($PackingNoteDetailResult)->payload[0];
|
||||
}
|
||||
|
||||
$VorbereitungsTimeDetail = null;
|
||||
|
||||
foreach ($orderdetail_output->order_schedule as $scheduledetail) {
|
||||
|
||||
if ($scheduledetail->name == Vorbereitungs_Zeitvariable) {
|
||||
$VorbereitungsTimeDetail = $scheduledetail;
|
||||
}
|
||||
}
|
||||
//End Of get Order Details
|
||||
|
||||
if (CheckOut_UseDispoStartForRowMarking || ($VorbereitungsTimeDetail->date_start == null)) {
|
||||
$date = new DateTime($packingjob->date_start);
|
||||
} else {
|
||||
$date = new DateTime($VorbereitungsTimeDetail->date_start);
|
||||
// --- Row-Marking Datum bestimmen (konfigurierbar) ---
|
||||
$today = dayStart(new DateTimeImmutable('today', new DateTimeZone(APP_TZ)));
|
||||
$todayFilter = $today; // für die Window-Berechnung
|
||||
$limit = null;
|
||||
if (CheckOut_FutureDays != -1) {
|
||||
$limit = $todayFilter->modify('+' . (int) CheckOut_FutureDays . ' day');
|
||||
}
|
||||
|
||||
$date->setTime(0, 0, 0);
|
||||
$today = new DateTime();
|
||||
$today->setTime(0, 0, 0);
|
||||
$markDate = resolveRowMarkDate($packingjob, $VorbereitungsTimeDetail, $PackingNoteDetail, (int) CheckOutRowMarkSource);
|
||||
|
||||
$todayFilter = new DateTime();
|
||||
$todayFilter->setTime(0, 0, 0);
|
||||
if (CheckOut_FutureDays == -1 || $date <= ($todayFilter->modify('+' . CheckOut_FutureDays . ' day'))) {
|
||||
//prüfe, ob entweder unbegrenzte (-1) Anzeige Aktiv ist, oder das Datum kleiner oder Gleich heute + Zukunftsspanne ist
|
||||
if ($date == $today) {
|
||||
if (CheckOut_FutureDays == -1 || ($markDate && $markDate <= $limit)) {
|
||||
$trClass = rowClassForDate($markDate, $today);
|
||||
|
||||
echo "<tr class='text-dark bg-warning'>";
|
||||
} else if ($date < $today) {
|
||||
echo "<tr class=' bg-danger'>";
|
||||
if ($trClass) {
|
||||
echo "<tr class='{$trClass}'>";
|
||||
} else {
|
||||
echo "<tr>";
|
||||
}
|
||||
@@ -107,20 +164,55 @@ foreach ($data_output as $packingjob) {
|
||||
}
|
||||
echo "<td>" . $packingjob->contact->name . "</td>";
|
||||
echo "<td>" . $packingjob->event . "</td>";
|
||||
if ($VorbereitungsTimeDetail->date_start != null) {
|
||||
echo "<td><small>" . date_format(new \DateTime($packingjob->date_start), 'd.m.Y') . " " . getTimeFromSeconds($packingjob->time_start) . "</small><br><i>" . date_format(new \DateTime($VorbereitungsTimeDetail->date_start), 'd.m.Y') . " " . getTimeFromSeconds($VorbereitungsTimeDetail->time_start) . "</i></td>";
|
||||
} else {
|
||||
echo "<td>" . date_format(new \DateTime($packingjob->date_start), 'd.m.Y') . " " . getTimeFromSeconds($packingjob->time_start) . "</td>";
|
||||
}
|
||||
if(!HideCheckInTimeOnCheckout){
|
||||
if (($VorbereitungsTimeDetail->date_end != null)) {
|
||||
echo "<td><small>" . date_format(new \DateTime($packingjob->date_end), 'd.m.Y') . " " . getTimeFromSeconds($packingjob->time_end) . "</small><br><i>" . date_format(new \DateTime($VorbereitungsTimeDetail->date_end), 'd.m.Y') . " " . getTimeFromSeconds($VorbereitungsTimeDetail->time_end) . "</i></td>";
|
||||
} else {
|
||||
echo "<td>" . date_format(new \DateTime($packingjob->date_end), 'd.m.Y') . " " . getTimeFromSeconds($packingjob->time_end) . "</td>";
|
||||
}
|
||||
}
|
||||
echo "<td>";
|
||||
|
||||
if (ShowCheckoutTimeOnCheckout || ShowVorbereitungTimeOnCheckout || ShowPackagingTimeOnCheckout || ShowDeliveryTimeOnCheckout) { echo "<td>";}
|
||||
|
||||
|
||||
if (ShowCheckoutTimeOnCheckout && $packingjob->date_start != null) {
|
||||
echoMarkedTimeLine($packingjob->date_start, (int) $packingjob->time_start, $today, ShowTimesOnCheckout);
|
||||
}
|
||||
if (ShowVorbereitungTimeOnCheckout && $VorbereitungsTimeDetail && $VorbereitungsTimeDetail->date_start != null) {
|
||||
if (ShowCheckoutTimeOnCheckout) {
|
||||
echo "<br>";
|
||||
}
|
||||
|
||||
if($packingjob->is_all_out != 0){
|
||||
echoMarkedTimeLine($VorbereitungsTimeDetail->date_start, (int) $VorbereitungsTimeDetail->time_start, $today, ShowTimesOnCheckout);
|
||||
}else{
|
||||
echo "<span class='bg-success text-light'>";
|
||||
echo date_format(new \DateTime($VorbereitungsTimeDetail->date_start), 'd.m.Y');
|
||||
if($VorbereitungsTimeDetail->time_start){echo " ".getTimeFromSeconds((string) $VorbereitungsTimeDetail->time_start);}
|
||||
echo "</span>";
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
if (ShowPackagingTimeOnCheckout && $PackingNoteDetail && $PackingNoteDetail->date_packing != null) {
|
||||
if (ShowCheckoutTimeOnCheckout || ShowVorbereitungTimeOnCheckout) {
|
||||
echo "<br>";
|
||||
}
|
||||
if($packingjob->is_all_out != 0){
|
||||
echoMarkedTimeLine($PackingNoteDetail->date_packing, (int) $PackingNoteDetail->time_packing, $today, ShowTimesOnCheckout);
|
||||
}else{
|
||||
echo "<span class='bg-success text-light'>";
|
||||
echo date_format(new \DateTime($PackingNoteDetail->date_packing), 'd.m.Y');
|
||||
if($PackingNoteDetail->time_packing){echo " ".getTimeFromSeconds((string) $PackingNoteDetail->time_packing);}
|
||||
echo "</span>";
|
||||
}
|
||||
}
|
||||
if (ShowDeliveryTimeOnCheckout && $PackingNoteDetail && $PackingNoteDetail->date_delivery != null) {
|
||||
if (ShowCheckoutTimeOnCheckout || ShowVorbereitungTimeOnCheckout || ShowPackagingTimeOnCheckout) {
|
||||
echo "<br>";
|
||||
}
|
||||
echoMarkedTimeLine($PackingNoteDetail->date_delivery, (int) $PackingNoteDetail->time_delivery, $today, ShowTimesOnCheckout);
|
||||
}
|
||||
|
||||
|
||||
if (ShowCheckoutTimeOnCheckout || ShowVorbereitungTimeOnCheckout || ShowPackagingTimeOnCheckout || ShowDeliveryTimeOnCheckout) {
|
||||
echo "</td>";
|
||||
}
|
||||
|
||||
echo "<td>";
|
||||
|
||||
if ($packingjob->is_all_out == 0) {
|
||||
echo "<span class='badge badge-success'>";
|
||||
@@ -132,12 +224,66 @@ foreach ($data_output as $packingjob) {
|
||||
}
|
||||
|
||||
echo ($packingjob->pieces_sum_total - abs($packingjob->is_all_out)) . "/" . $packingjob->pieces_sum_total;
|
||||
echo "</span><td>";
|
||||
echo "</span></td>";
|
||||
|
||||
if (ShowShippingIcons) {
|
||||
|
||||
if (UseShippingStatus) {
|
||||
if (($PackingNoteDetail->status == ShippingOutOrganizedStatus) || ($PackingNoteDetail->status == ShippingOrganizedStatus)) {
|
||||
echo "<td style='color:#66FF00; text-align:center;'>";
|
||||
} else {
|
||||
echo "<td style='text-align:center;'>";
|
||||
}
|
||||
|
||||
if ($PackingNoteDetail->is_self_pickup) {
|
||||
echo '<i class="fa-solid fa-person-walking fa-lg"></i>';
|
||||
} else {
|
||||
if (preg_match('/' . KurierContainsText . '/i', $PackingNoteDetail->shipping_out)) {
|
||||
echo '<i class="fa-solid fa-hand-holding-dollar fa-lg"></i>';
|
||||
}
|
||||
if (preg_match('/' . SpeditionContainsText . '/i', $PackingNoteDetail->shipping_out)) {
|
||||
echo '<i class="fa-solid fa-truck fa-lg"></i>';
|
||||
}
|
||||
if (preg_match('/' . DHLContainsText . '/i', $PackingNoteDetail->shipping_out)) {
|
||||
echo '<i class="fa-brands fa-dhl fa-2xl"></i>';
|
||||
}
|
||||
if (preg_match('/' . LKWContainsText . '/i', $PackingNoteDetail->shipping_out)) {
|
||||
echo '<i class="fa-solid fa-industry fa-lg"></i><i class="fa-solid fa-truck fa-lg"></i>';
|
||||
}
|
||||
if (preg_match('/' . TransporterContainsText . '/i', $PackingNoteDetail->shipping_out)) {
|
||||
echo '<i class="fa-solid fa-industry fa-lg"></i><i class="fa-solid fa-van-shuttle fa-lg"></i>';
|
||||
}
|
||||
if (preg_match('/' . PKWContainsText . '/i', $PackingNoteDetail->shipping_out)) {
|
||||
echo '<i class="fa-solid fa-industry fa-lg"></i><i class="fa-solid fa-car fa-lg"></i>';
|
||||
}
|
||||
}
|
||||
echo "</td>";
|
||||
}
|
||||
|
||||
echo "</tr>";
|
||||
}
|
||||
|
||||
echo "</tr>";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function echoMarkedTimeLine(?string $dateStr, ?int $timeSeconds, DateTimeImmutable $today, $ReturnTime): void {
|
||||
if (!$dateStr)
|
||||
return;
|
||||
$d = dt($dateStr);
|
||||
if (!$d)
|
||||
return;
|
||||
|
||||
$mark = dayStart($d);
|
||||
$cls = rowClassForDate($mark, $today);
|
||||
|
||||
echo $cls ? "<span class=\"{$cls}\">" : "";
|
||||
echo date_format(new \DateTime($dateStr), 'd.m.Y');
|
||||
if($ReturnTime){echo " ".getTimeFromSeconds((string) $timeSeconds);}
|
||||
echo $cls ? "</span>" : "";
|
||||
}
|
||||
|
||||
function getTimeFromSeconds(string $timestring) {
|
||||
|
||||
$hours = floor($timestring / 3600);
|
||||
@@ -149,4 +295,4 @@ function getTimeFromSeconds(string $timestring) {
|
||||
return $timeFormat;
|
||||
}
|
||||
|
||||
?>
|
||||
?>
|
||||
|
||||
877
sources/getProductLabel.php
Normal file
877
sources/getProductLabel.php
Normal file
@@ -0,0 +1,877 @@
|
||||
<?php
|
||||
error_reporting(E_ALL & ~E_DEPRECATED);
|
||||
|
||||
require('../config.php');
|
||||
require('../EpiApi.php');
|
||||
require_once __DIR__ . '/../vendor/autoload.php';
|
||||
require_once __DIR__ . '/../vendor/tecnickcom/tcpdf/tcpdf.php';
|
||||
date_default_timezone_set('Europe/Berlin');
|
||||
$id = isset($_GET['id']) ? (int)$_GET['id'] : 0;
|
||||
$deviceNumber = (!empty($_GET['nr'])) ? '--' . trim($_GET['nr']) : '';
|
||||
|
||||
$Epi = new Epirent();
|
||||
$product =json_decode($Epi->requestEpiApi('/v1/product/'.$id.'?cl=' . Epirent_Mandant))->payload[0];
|
||||
$productImage =json_decode($Epi->requestEpiApi('/v1/product/image/'.$id.'?cl=' . Epirent_Mandant))->payload[0]->image_data;
|
||||
|
||||
|
||||
function s(?string $v): string { return trim((string)$v); }
|
||||
function fnum(?float $v, int $dec = 2): string {
|
||||
if ($v === null) return '';
|
||||
$n = number_format((float)$v, $dec, ',', '.');
|
||||
return rtrim(rtrim($n, '0'), ',');
|
||||
}
|
||||
|
||||
//Set Variables
|
||||
$productName = $product->name;
|
||||
$productNumber = $product->product_no;
|
||||
|
||||
$bruttoWeightSum = $product->tech_data->weight_gro;
|
||||
|
||||
$bundle = buildBundleForProduct($Epi, $product);
|
||||
|
||||
/** Test-Daten
|
||||
*/
|
||||
$dimsBHT =$product->tech_data->width_gro." x ".$product->tech_data->depth_gro." x ".$product->tech_data->height_gro;
|
||||
$volume = ((float)$product->tech_data->height_gro * (float)$product->tech_data->width_gro * (float)$product->tech_data->depth_gro)* 0.000001;
|
||||
$storageLoc = $product->product_data->storage_location_nearby;
|
||||
|
||||
/** Layout */
|
||||
$PAGE_SIZE = 'A4';
|
||||
$MARGIN_LEFT = 8;
|
||||
$MARGIN_TOP = 8;
|
||||
$MARGIN_RIGHT = 8;
|
||||
$MARGIN_BOTTOM = 10;
|
||||
|
||||
$CELL_PAD = 1.6;
|
||||
$LINE_GRAY = '#efefef';
|
||||
|
||||
$LEVEL_MIN = 1; $LEVEL_MAX = 5;
|
||||
$INDENT_PX_PER_LEVEL = 10;
|
||||
$BASE_FONT_PX = 10;
|
||||
$FONT_STEP_PX = 1.5;
|
||||
|
||||
|
||||
|
||||
/** TCPDF */
|
||||
$pdf = new TCPDF('P', 'mm', $PAGE_SIZE, true, 'UTF-8', false);
|
||||
|
||||
// 1) Pfade sauber
|
||||
$fontDir = realpath(__DIR__ . '/../src/assets/font') . '/';
|
||||
$tcpdfFontsDir = defined('K_PATH_FONTS') ? K_PATH_FONTS : (dirname((new \ReflectionClass('TCPDF'))->getFileName()) . '/fonts/');
|
||||
|
||||
// 2) Rechte (einmalig sicherstellen)
|
||||
// -> hast du schon gefixt
|
||||
|
||||
// 3) TTFs registrieren und Rückgabewerte merken
|
||||
$fontReg = file_exists($fontDir.'Hurme3/HurmeGeometricSans3-Regular.ttf')
|
||||
? TCPDF_FONTS::addTTFfont($fontDir.'Hurme3/HurmeGeometricSans3-Regular.ttf', 'TrueTypeUnicode', '', 96)
|
||||
: false;
|
||||
$fontBold = file_exists($fontDir.'Hurme1/HurmeGeometricSans1-Bold.ttf')
|
||||
? TCPDF_FONTS::addTTFfont($fontDir.'Hurme1/HurmeGeometricSans1-Bold.ttf', 'TrueTypeUnicode', '', 96)
|
||||
: false;
|
||||
$fontItal = file_exists($fontDir.'Hurme3/HurmeGeometricSans3-Italic.ttf')
|
||||
? TCPDF_FONTS::addTTFfont($fontDir.'Hurme3/HurmeGeometricSans3-Italic.ttf', 'TrueTypeUnicode', '', 96)
|
||||
: false;
|
||||
$fontBI = file_exists($fontDir.'Hurme1/HurmeGeometricSans1-BoldItalic.ttf')
|
||||
? TCPDF_FONTS::addTTFfont($fontDir.'Hurme1/HurmeGeometricSans1-BoldItalic.ttf', 'TrueTypeUnicode', '', 96)
|
||||
: false;
|
||||
|
||||
// 4) Fallback setzen
|
||||
$baseFont = $fontReg ?: 'dejavusans';
|
||||
|
||||
// 5) Header/Footer und Defaults
|
||||
$pdf->setHeaderFont([$baseFont, '', 10]);
|
||||
$pdf->setFooterFont([$baseFont, '', 9]);
|
||||
$pdf->SetDefaultMonospacedFont('courier');
|
||||
|
||||
// 6) *** AB JETZT KEIN dejavusans MEHR SETZEN! ***
|
||||
$pdf->SetFont($baseFont, '', 10.5);
|
||||
|
||||
|
||||
|
||||
$pdf->SetCreator('VT-Media EpiWebview');
|
||||
$pdf->SetAuthor('VT-Media');
|
||||
$pdf->SetTitle('Kistenetikett '.$productName);
|
||||
$pdf->SetSubject('Kistenetikett');
|
||||
|
||||
$pdf->setPrintHeader(false);
|
||||
$pdf->setPrintFooter(false);
|
||||
$pdf->SetMargins($MARGIN_LEFT, $MARGIN_TOP, $MARGIN_RIGHT);
|
||||
$pdf->SetAutoPageBreak(true, $MARGIN_BOTTOM);
|
||||
$pdf->setImageScale(1.0);
|
||||
$pdf->setFontSubsetting(true);
|
||||
$pdf->setCellPadding($CELL_PAD);
|
||||
|
||||
$pdf->AddPage();
|
||||
|
||||
|
||||
|
||||
|
||||
// --- LOGO in fester Box mit Rahmen, zentriert & verzerrungsfrei ---
|
||||
$logoPath = __DIR__ . Labelprint_Logopath;
|
||||
|
||||
$startX = $pdf->GetX();
|
||||
$startY = $pdf->GetY();
|
||||
|
||||
// feste Rahmen-Größe (in mm)
|
||||
$frameW = 38.0; // Gesamtbreite des Rahmens
|
||||
$frameH = 20; // Gesamthöhe des Rahmens
|
||||
$frameOffsetX = 1.4; // Abstand vom linken Margin (wie vorher genutzt)
|
||||
$frameOffsetY = 1.5; // Abstand von oben (wie vorher genutzt)
|
||||
|
||||
// Rahmen-Position
|
||||
$frameX = $startX + $frameOffsetX;
|
||||
$frameY = $startY + $frameOffsetY;
|
||||
|
||||
// Rahmen zeichnen
|
||||
$pdf->SetLineWidth(0.3);
|
||||
$pdf->Rect($frameX, $frameY, $frameW, $frameH, 'D');
|
||||
|
||||
// Logo innerhalb des Rahmens mit Innenabstand
|
||||
$innerPad = 1.2; // mm Luft im Rahmen
|
||||
$maxW = $frameW - 2 * $innerPad;
|
||||
$maxH = $frameH - 2 * $innerPad;
|
||||
|
||||
if (is_file($logoPath)) {
|
||||
// Seitenverhältnis ermitteln
|
||||
$imgWpx = $imgHpx = 0;
|
||||
if (@list($imgWpx, $imgHpx) = @getimagesize($logoPath)) {
|
||||
$ratio = ($imgHpx > 0) ? ($imgWpx / $imgHpx) : 1.0;
|
||||
|
||||
// proportional in den Rahmen einpassen
|
||||
if ($maxW / $maxH > $ratio) {
|
||||
$drawH = $maxH;
|
||||
$drawW = $maxH * $ratio;
|
||||
} else {
|
||||
$drawW = $maxW;
|
||||
$drawH = $maxW / $ratio;
|
||||
}
|
||||
|
||||
// zentrieren
|
||||
$imgX = $frameX + ($frameW - $drawW) / 2;
|
||||
$imgY = $frameY + ($frameH - $drawH) / 2;
|
||||
|
||||
// ausgeben
|
||||
$pdf->Image($logoPath, $imgX, $imgY, $drawW, $drawH, '', '', '', false, 300);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
$afterLogoX = $frameX + $frameW - 1.4; // gleicher Abstand wie zuvor, jetzt basierend auf Rahmenbreite
|
||||
$afterLogoBottomY = $frameY + $frameH; // falls du unten bündig etwas brauchst
|
||||
$pdf->SetXY($afterLogoX, $startY); // Cursor rechts neben dem Logo positionieren
|
||||
|
||||
|
||||
|
||||
$addrHtml = '
|
||||
<div style="line-height:1.25; font-size:6px; margin:0; padding:0;">'.Labelprint_Addresstext.'</div>';
|
||||
// Feste Box rechts neben dem Logo (direkt bündig)
|
||||
$addrBoxW = 30.0; // feste Breite (mm)
|
||||
$addrBoxH = 20.0; // feste Höhe (mm)
|
||||
$addrPad = 0.5; // Innenabstand Inhalt → Rahmen (mm)
|
||||
|
||||
// bündig rechts neben dem Logo-Rahmen starten
|
||||
$addrX = $frameX + $frameW; // kein zusätzlicher Abstand
|
||||
$addrY = $startY+1.5; // gleiche Oberkante wie Logo
|
||||
|
||||
// 1) festen Rahmen zeichnen
|
||||
$pdf->SetLineWidth(0.3);
|
||||
$pdf->Rect($addrX, $addrY, $addrBoxW, $addrBoxH, 'D');
|
||||
|
||||
// 2) HTML-Inhalt in die Box schreiben, mit Clipping (Überhang wird abgeschnitten)
|
||||
$pdf->StartTransform();
|
||||
$pdf->Rect($addrX, $addrY, $addrBoxW, $addrBoxH, 'CNZ'); // Clip auf Box setzen
|
||||
|
||||
$pdf->writeHTMLCell(
|
||||
$addrBoxW - 2*$addrPad, // feste Breite des Inhalts
|
||||
$addrBoxH - 2*$addrPad, // feste Höhe des Inhalts
|
||||
$addrX + $addrPad, // x mit Innenabstand
|
||||
$addrY + $addrPad, // y mit Innenabstand
|
||||
$addrHtml, // dein HTML
|
||||
0, // kein Border (Rahmen haben wir schon)
|
||||
0, // ln
|
||||
false, // fill
|
||||
true, // reseth
|
||||
'L', // align
|
||||
true // autopadding
|
||||
);
|
||||
|
||||
$pdf->StopTransform();
|
||||
|
||||
// Cursor steht bereits auf: $pdf->SetXY($addrX + $addrBoxW, $addrY);
|
||||
|
||||
// 1) HTML OHNE border (sonst Doppelrahmen)
|
||||
$productionHtml = '
|
||||
<div style="line-height:1.3; font-size:8px; margin:0; padding:0;">
|
||||
<b>Produktion</b><br/><br><br><br>
|
||||
</div>';
|
||||
|
||||
// 2) Feste Box rechts neben der Adress-Box
|
||||
$prodX = $addrX + $addrBoxW; // bündig rechts neben Adresse
|
||||
$prodY = $addrY; // gleiche Oberkante
|
||||
// Breite: restliche Seitenbreite bis zum rechten Rand
|
||||
$prodW = $pdf->getPageWidth() - $MARGIN_RIGHT - $prodX+2;
|
||||
// Höhe: fix (anpassen nach Wunsch)
|
||||
$prodH = 20.0; // mm
|
||||
$prodPad = 0; // Innenabstand
|
||||
|
||||
// 3) EIN sichtbarer Rahmen zeichnen
|
||||
$pdf->SetLineWidth(0.3);
|
||||
$pdf->Rect($prodX, $prodY, $prodW, $prodH, 'D');
|
||||
|
||||
// 4) Inhalt in die Box schreiben und clippen (Überhang abgeschnitten)
|
||||
$pdf->StartTransform();
|
||||
$pdf->Rect($prodX, $prodY, $prodW, $prodH, 'CNZ'); // Clip auf Box setzen
|
||||
|
||||
$pdf->writeHTMLCell(
|
||||
$prodW - 2*$prodPad, // feste Breite des Inhalts
|
||||
$prodH - 2*$prodPad, // feste Höhe des Inhalts
|
||||
$prodX + $prodPad, // x mit Innenabstand
|
||||
$prodY + $prodPad, // y mit Innenabstand
|
||||
$productionHtml, // Inhalt (ohne border!)
|
||||
0, // kein Border (Rahmen haben wir schon)
|
||||
0, // ln
|
||||
false, // fill
|
||||
true, // reseth
|
||||
'L', // align
|
||||
true // autopadding
|
||||
);
|
||||
|
||||
$pdf->StopTransform();
|
||||
|
||||
// 5) (Optional) Cursor direkt rechts neben der Box positionieren
|
||||
$pdf->SetXY($prodX + $prodW, $prodY);
|
||||
|
||||
// === Name-Box: feste Größe, Text bricht, kein Doppelrahmen ===
|
||||
|
||||
// Breite: gesamte Restbreite bis zum rechten Rand (unter Adresse + Produktion)
|
||||
$nameX = $frameX; // beginnt rechts neben dem Logo/Adressbereich
|
||||
$nameW = $pdf->getPageWidth() - $MARGIN_RIGHT - $nameX-65.2;
|
||||
|
||||
// Höhe & Padding der Box (anpassen nach Wunsch)
|
||||
$nameBoxH = 18.2; // mm - feste Höhe
|
||||
$namePad = 1.5; // mm - Innenabstand
|
||||
|
||||
// Y: direkt unter die höhere der beiden Boxen (Adresse vs. Produktion)
|
||||
$yAfterRow = max($addrY + $addrBoxH, $prodY + $prodH) + 0.0; // +2 mm Abstand
|
||||
$nameY = $yAfterRow;
|
||||
|
||||
// Inhalt OHNE border (sonst Doppelrahmen)
|
||||
$nameHtml = '
|
||||
<div style="font-size:15px; font-weight:700; line-height:1.3; margin:0; padding:0;">' . htmlspecialchars($productName) . '
|
||||
</div>';
|
||||
|
||||
// 1) Sichtbaren Rahmen zeichnen (feste Größe)
|
||||
$pdf->SetLineWidth(0.3);
|
||||
$pdf->Rect($nameX, $nameY, $nameW, $nameBoxH, 'D');
|
||||
|
||||
// 2) Inhalt in die Box schreiben und clippen
|
||||
$pdf->StartTransform();
|
||||
$pdf->Rect($nameX, $nameY, $nameW, $nameBoxH, 'CNZ'); // Clip auf Box setzen
|
||||
|
||||
$pdf->writeHTMLCell(
|
||||
$nameW - 2*$namePad, // feste Breite für den Text
|
||||
$nameBoxH - 2*$namePad, // feste Höhe für den Text
|
||||
$nameX + $namePad, // x mit Innenabstand
|
||||
$nameY + $namePad, // y mit Innenabstand
|
||||
$nameHtml,
|
||||
0, // kein HTML-Border
|
||||
0, // ln
|
||||
false, // fill
|
||||
true, // reseth
|
||||
'L', // align
|
||||
true // autopadding
|
||||
);
|
||||
|
||||
$pdf->StopTransform();
|
||||
|
||||
// Optional: Cursor unter die Box setzen (falls du danach im Fluss weitermachen willst)
|
||||
$pdf->SetXY($nameX, $nameY + $nameBoxH);
|
||||
|
||||
// 1) Box-Geometrie (immer gleich groß)
|
||||
$pnBoxW = 67.0; // mm – Gesamtbreite der Box
|
||||
$pnBoxH = 18.2; // mm – Gesamthöhe der Box
|
||||
$pnBoxPad = 2.0; // mm – Innenabstand innerhalb der Box
|
||||
|
||||
// Position der Box (aktuell am rechten Rand oben; passe frei an)
|
||||
$pnBoxX = $pdf->getPageWidth() - $MARGIN_RIGHT - $pnBoxW+1.8; // bündig am rechten Rand
|
||||
$pnBoxY = $startY+21.5; // gleiche Oberkante wie Kopf
|
||||
|
||||
// 2) Sichtbaren Rahmen zeichnen (einmalig)
|
||||
$pdf->SetLineWidth(0.3);
|
||||
$pdf->Rect($pnBoxX, $pnBoxY, $pnBoxW, $pnBoxH, 'D');
|
||||
|
||||
// 3) Clipping aktivieren, damit Inhalt in der Box bleibt
|
||||
$pdf->StartTransform();
|
||||
$pdf->Rect($pnBoxX, $pnBoxY, $pnBoxW, $pnBoxH, 'CNZ'); // Clip auf die Box
|
||||
|
||||
// 4) Produktnummer: frei verschiebbar innerhalb der Box
|
||||
$pnCombined = trim($productNumber . $deviceNumber);
|
||||
$pnText = htmlspecialchars($pnCombined !== '' ? $pnCombined : 'N/A');
|
||||
|
||||
// Offsets innerhalb der Box – hier stellst du die Position ein
|
||||
$pnTextOffsetX = 2.0; // mm von linker Innenkante
|
||||
$pnTextOffsetY = 1; // mm von oberer Innenkante
|
||||
$pnTextW = $pnBoxW - 2*$pnBoxPad; // nutzbare Breite
|
||||
$pnTextH = 8.0; // feste Höhe für den PN-Textbereich (mm)
|
||||
|
||||
$pnHtml = '
|
||||
<div style="font-size:20px; line-height:1.25; margin:0; padding:0;">
|
||||
'.$pnText.'
|
||||
</div>';
|
||||
|
||||
// PN-Text rendern (ohne HTML-Border!)
|
||||
$pdf->writeHTMLCell(
|
||||
$pnTextW, // Breite Textbereich
|
||||
$pnTextH, // Höhe Textbereich
|
||||
$pnBoxX + $pnBoxPad + $pnTextOffsetX, // X in der Box
|
||||
$pnBoxY + $pnBoxPad + $pnTextOffsetY, // Y in der Box
|
||||
$pnHtml,
|
||||
0, 0, false, true, 'L', true
|
||||
);
|
||||
|
||||
// 5) QR-Code: frei verschiebbar innerhalb der Box, verzerrungsfrei per size
|
||||
$qrData = trim((string)($productNumber . $deviceNumber));
|
||||
if ($qrData === '') {
|
||||
$qrData = 'N/A';
|
||||
}
|
||||
|
||||
$qrSize = 16.0; // mm – Kantenlänge des QR
|
||||
$qrOffsetX = $pnBoxW - $pnBoxPad - $qrSize - 2.0; // mm von linker Innenkante
|
||||
$qrOffsetY = $pnBoxH - $pnBoxPad - $qrSize +0.6; // mm von oberer Innenkante
|
||||
|
||||
$qrX = $pnBoxX + $qrOffsetX;
|
||||
$qrY = $pnBoxY + $qrOffsetY;
|
||||
|
||||
$qrStyle = [
|
||||
'border' => 0,
|
||||
'vpadding' => 0,
|
||||
'hpadding' => 0,
|
||||
'fgcolor' => [0,0,0],
|
||||
'bgcolor' => false,
|
||||
];
|
||||
|
||||
// QR rendern (Warnings/Deprecated für diesen Block stumm schalten)
|
||||
$_old_reporting = error_reporting();
|
||||
error_reporting($_old_reporting & ~(E_WARNING | E_DEPRECATED | E_NOTICE));
|
||||
$pdf->write2DBarcode($qrData, 'QRCODE,H', $qrX, $qrY, $qrSize, $qrSize, $qrStyle, 'N');
|
||||
error_reporting($_old_reporting);
|
||||
|
||||
// 6) Clipping beenden
|
||||
$pdf->StopTransform();
|
||||
|
||||
// 7) Optional: Cursor unter die PN-Box setzen, falls du im Fluss weiter willst
|
||||
$pdf->SetXY($pnBoxX, $pnBoxY + $pnBoxH);
|
||||
|
||||
/** Bundle */
|
||||
|
||||
$bundleHeader = '<div style=" background-color: black; color: white; font-size:10px; font-weight:700;line-height:1.4; margin:4px 0 3px 0;"> Bundleinhalt</div>';
|
||||
$pdf->writeHTMLCell(70, 50, 7.65, 46, $bundleHeader, 0, 0, 0, true, 'L', true);
|
||||
$bundleHeaderFillable = '<div style="border: 1px solid black; line-heigt:0.6"></div>';
|
||||
//$pdf->writeHTMLCell(130.8, 39.8, 74.5, 46, $bundleHeaderFillable, 0, 0, 0, false, 'L', true);
|
||||
drawFixedBox($pdf, 76.0, 47.7, 127.8, 4.65);
|
||||
|
||||
|
||||
$bundleRows = '';
|
||||
|
||||
$BASE_FONT_PX = 10.0; // Level 1
|
||||
$FONT_STEP_PX = 1.5; // je Ebene kleiner
|
||||
$MIN_FONT_PX = 6.0;
|
||||
$LINE_HEIGHT_BASE = 1.15; // Basis Zeilenhöhe
|
||||
$LINE_HEIGHT_STEP = 0.05; // je Ebene etwas kompakter
|
||||
$ROW_PAD_V_MM = 0.5; // vertikales Padding je Zeile
|
||||
$ROW_PAD_H_MM = 0.8; // horizontales Padding
|
||||
$WEIGHT_PAD_RIGHT_MM = 1.6; // mehr Abstand zum rechten Rand
|
||||
$INDENT_MM_PER_LVL = 2.8; // Einrückung je Ebene in mm (stabil!)
|
||||
|
||||
foreach ($bundle as $item) {
|
||||
$qty = (int)($item['qty'] ?? 1);
|
||||
$text = htmlspecialchars($item['text'] ?? '');
|
||||
$lvl = max($LEVEL_MIN, min($LEVEL_MAX, (int)($item['level'] ?? 1)));
|
||||
$wkg = isset($item['weight_kg']) ? (float)$item['weight_kg'] : null;
|
||||
|
||||
$textFontPx = max($MIN_FONT_PX, $BASE_FONT_PX - ($lvl - 1) * $FONT_STEP_PX);
|
||||
$weightFontPx = max($MIN_FONT_PX, $BASE_FONT_PX - ($lvl - 1) * $FONT_STEP_PX);
|
||||
$lineHeight = max(1.0, $LINE_HEIGHT_BASE - ($lvl - 1) * $LINE_HEIGHT_STEP);
|
||||
|
||||
// Einrückung robust über mm – aber nur Tabelle nutzen, wenn > 0 mm
|
||||
$indentMm = max(0, ($lvl - 1) * $INDENT_MM_PER_LVL);
|
||||
|
||||
// Name-Spalte: ohne Spacer-Tabelle bei Level 1 (indent=0), sonst mit
|
||||
if ($indentMm > 0) {
|
||||
$nameCellHtml = '
|
||||
<table cellspacing="0" cellpadding="0" width="100%">
|
||||
<tr>
|
||||
<td style="width:'.$indentMm.'mm; padding:0; margin:0;"></td>
|
||||
<td style="font-size:'.$textFontPx.'px; line-height:'.$lineHeight.'; padding:0; margin:0;">'.$text.'</td>
|
||||
</tr>
|
||||
</table>';
|
||||
} else {
|
||||
// Level 1: direkt rendern
|
||||
$nameCellHtml = '<span style="font-size:'.$textFontPx.'px; line-height:'.$lineHeight.';">'.$text.'</span>';
|
||||
}
|
||||
|
||||
$bundleRows .= '
|
||||
<tr>
|
||||
<!-- Anzahl -->
|
||||
<td style="
|
||||
width:12%;
|
||||
text-align:right;
|
||||
vertical-align:middle;
|
||||
font-size:'.$textFontPx.'px;
|
||||
line-height:'.$lineHeight.';
|
||||
padding:'.$ROW_PAD_V_MM.'mm '.$ROW_PAD_H_MM.'mm '.$ROW_PAD_V_MM.'mm '.$ROW_PAD_H_MM.'mm;">
|
||||
'.$qty.'
|
||||
</td>
|
||||
|
||||
<!-- Name mit Einrückung -->
|
||||
<td style="
|
||||
width:68%;
|
||||
vertical-align:middle;
|
||||
padding:'.$ROW_PAD_V_MM.'mm '.$ROW_PAD_H_MM.'mm;">
|
||||
'.$nameCellHtml.'
|
||||
</td>
|
||||
|
||||
<!-- Gewicht rechts mit extra Innenabstand -->
|
||||
<td style="
|
||||
width:20%;
|
||||
text-align:right;
|
||||
vertical-align:middle;
|
||||
font-size:'.$weightFontPx.'px;
|
||||
line-height:'.$lineHeight.';
|
||||
padding:'.$ROW_PAD_V_MM.'mm '.$WEIGHT_PAD_RIGHT_MM.'mm '.$ROW_PAD_V_MM.'mm '.$ROW_PAD_H_MM.'mm;">
|
||||
'.($wkg !== null ? fnum($wkg).' kg' : '').'
|
||||
</td>
|
||||
</tr>';
|
||||
}
|
||||
|
||||
$bundleHtml = '
|
||||
<table cellspacing="0" cellpadding="0" style="border:1px solid #000; border-collapse:collapse;" width="100%">
|
||||
<tbody>'.$bundleRows.'</tbody>
|
||||
</table>';
|
||||
|
||||
$pdf->writeHTMLCell(
|
||||
$pdf->getPageWidth() - 12.4,
|
||||
0,
|
||||
7.8,
|
||||
50.8,
|
||||
$bundleHtml,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
true,
|
||||
'L',
|
||||
true
|
||||
);
|
||||
|
||||
|
||||
|
||||
/** ---------- Meta-Block dynamisch unterhalb der Bundle-Tabelle ---------- */
|
||||
|
||||
// Höhe der zuletzt gerenderten Bundle-Tabelle holen
|
||||
$bundleH = $pdf->getLastH();
|
||||
|
||||
// Y-Position direkt nach der Bundle-Tabelle (mit 3 mm Abstand)
|
||||
$metaY = 47.6 + $bundleH; // 50 = dein bisheriges Y der Bundle-Tabelle
|
||||
$metaX = $MARGIN_LEFT;
|
||||
$metaW = $pdf->getPageWidth() - $MARGIN_LEFT - $MARGIN_RIGHT;
|
||||
|
||||
// Falls Meta-Block zu nah am Seitenende wäre → neue Seite
|
||||
$usableBottom = $pdf->getPageHeight() - $MARGIN_BOTTOM;
|
||||
if ($metaY > $usableBottom - 40) { // 40 mm Puffer für Meta
|
||||
$pdf->AddPage();
|
||||
$metaY = $MARGIN_TOP;
|
||||
}
|
||||
|
||||
// --- Inhalt wie gehabt ---
|
||||
$leftMeta = '
|
||||
<table cellspacing="0" cellpadding="'.$CELL_PAD.'" width="100%">
|
||||
<tbody>
|
||||
|
||||
<!-- Maße -->
|
||||
<tr>
|
||||
<!-- Zusammengefasste linke Zelle: hat Rahmen, innen KEIN Rahmen -->
|
||||
<td width="58%" border="1" style="padding-left:20px;">
|
||||
<table cellspacing="0" cellpadding="0" border="0" width="100%">
|
||||
<tr>
|
||||
<td width="64%">Maße <small>L x B x H</small></td>
|
||||
<td width="36%">in cm</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
<!-- rechte Zelle mit Wert -->
|
||||
<td width="42%" border="1"><b> '.htmlspecialchars($dimsBHT).'</b></td>
|
||||
</tr>
|
||||
|
||||
<!-- Gewicht -->
|
||||
<tr>
|
||||
<td width="58%" border="1" style="padding-left:20px;">
|
||||
<table cellspacing="0" cellpadding="0" border="0" width="100%">
|
||||
<tr>
|
||||
<td width="64%">Gewicht</td>
|
||||
<td width="36%">in Kg</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
<td width="42%" border="1"><b> '.fnum($bruttoWeightSum).' Kg</b></td>
|
||||
</tr>
|
||||
|
||||
<!-- Volumen -->
|
||||
<tr>
|
||||
<td width="58%" border="1" style="padding-left:20px;">
|
||||
<table cellspacing="0" cellpadding="0" border="0" width="100%">
|
||||
<tr>
|
||||
<td width="64%">Volumen</td>
|
||||
<td width="36%">in m³</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
<td width="42%" border="1"><b> '.fnum($volume, 2).' m³</b></td>
|
||||
</tr>
|
||||
|
||||
<!-- Lagerplatz mit innerer Tabelle (optisch perfekt ausgerichtet) -->
|
||||
<tr>
|
||||
<td width="30%" border="1" style="padding-left:20px;">
|
||||
<table cellspacing="0" cellpadding="0" border="0" width="100%">
|
||||
<tr>
|
||||
<td width="100%">Lagerplatz</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
<td width="70%" border="1"><b> '.htmlspecialchars($storageLoc).'</b></td>
|
||||
</tr>
|
||||
|
||||
</tbody>
|
||||
</table>';
|
||||
|
||||
// ---- Koordinaten der Meta-Zeile bestimmen (nimm deine Werte) ----
|
||||
// Falls du sie schon berechnet hast, nutze deine $metaX, $metaY, $metaW.
|
||||
// Beispiel, wie du sie meist setzt:
|
||||
$metaX = $MARGIN_LEFT;
|
||||
$metaW = $pdf->getPageWidth() - $MARGIN_LEFT - $MARGIN_RIGHT;
|
||||
// $metaY hast du zuvor als Ziel-Y für den Meta-Block berechnet:
|
||||
/// $metaY = ... (z.B. 50 + $bundleH + 3);
|
||||
|
||||
// ---- Spaltenbreiten wie in deinem metaWrap: 62% | 2% | 36% ----
|
||||
$leftW = $metaW * 0.62;
|
||||
$gapW = $metaW * 0.02;
|
||||
$rightW = $metaW * 0.36;
|
||||
|
||||
// Obere linke Ecke der rechten Spalte:
|
||||
$rightX = $metaX + $leftW + $gapW;
|
||||
$rightY = $metaY;
|
||||
|
||||
// ---- Fester Rahmen in der rechten Spalte ----
|
||||
$frameOuterPad = 1.0; // mm: Abstand vom Spaltenrand
|
||||
$frameW = $rightW - 2*$frameOuterPad+8; // Rahmenbreite
|
||||
$frameH = 24.38; // mm: feste Rahmenhöhe, nach Wunsch
|
||||
$frameX = $rightX + $frameOuterPad-5.2;
|
||||
$frameY = $rightY + $frameOuterPad+0.5;
|
||||
|
||||
$pdf->SetLineWidth(0.3);
|
||||
$pdf->Rect($frameX, $frameY, $frameW, $frameH, 'D'); // der feste Rahmen
|
||||
|
||||
// ---- Base64-Bild zentriert & verzerrungsfrei in den Rahmen ----
|
||||
$tmpImg = tempnam(sys_get_temp_dir(), 'epi_') . '.png';
|
||||
file_put_contents($tmpImg, base64_decode($productImage));
|
||||
|
||||
list($imgWpx, $imgHpx) = getimagesize($tmpImg);
|
||||
$imgRatio = ($imgHpx > 0) ? ($imgWpx / $imgHpx) : 1.0;
|
||||
|
||||
$innerPad = 2.0; // mm: Innenabstand im Rahmen
|
||||
$maxW = $frameW - 2*$innerPad;
|
||||
$maxH = $frameH - 2*$innerPad;
|
||||
|
||||
// proportional einpassen
|
||||
if ($maxW / $maxH > $imgRatio) {
|
||||
$drawH = $maxH;
|
||||
$drawW = $maxH * $imgRatio;
|
||||
} else {
|
||||
$drawW = $maxW;
|
||||
$drawH = $maxW / $imgRatio;
|
||||
}
|
||||
|
||||
// zentrieren
|
||||
$imgX = $frameX + ($frameW - $drawW) / 2;
|
||||
$imgY = $frameY + ($frameH - $drawH) / 2;
|
||||
|
||||
$pdf->Image($tmpImg, $imgX, $imgY, $drawW, $drawH, '', '', '', false, 300);
|
||||
@unlink($tmpImg);
|
||||
|
||||
$metaWrap = '
|
||||
<table cellspacing="0" cellpadding="0" border="0" width="100%" style="margin-top:2px;">
|
||||
<tr>
|
||||
<td width="62%">'.$leftMeta.'</td>
|
||||
<td width="2%"></td>
|
||||
<td width="36%">'.$rightMeta.'</td>
|
||||
</tr>
|
||||
</table>';
|
||||
|
||||
// Meta-Block exakt an berechneter Position ausgeben
|
||||
$pdf->writeHTMLCell($metaW, 0, $metaX, $metaY, $metaWrap, 0, 0, 0, true, 'L', true);
|
||||
/** Output */
|
||||
// Falls trotz allem vorher etwas ausgegeben wurde (Warnings), Buffer leeren:
|
||||
if (ob_get_length()) { ob_end_clean(); }
|
||||
|
||||
|
||||
|
||||
// Äußerer Rahmen, Größenberechnung
|
||||
|
||||
// 1) Höhe der META-Zelle (zuletzt geschrieben) holen:
|
||||
$metaH = $pdf->getLastH();
|
||||
|
||||
// 2) Y der Bundle-Tabelle kennst du (50.8 in deinem Code) + deren Höhe:
|
||||
$bundleY = 50.8; // dein fixer Y für die Bundle-Tabelle
|
||||
// $bundleH hast du bereits oben: $bundleH = $pdf->getLastH();
|
||||
|
||||
// 3) Bottom-Kanten aller Boxen berechnen:
|
||||
$bottoms = [
|
||||
$frameY + $frameH, // Logo-Box
|
||||
$addrY + $addrBoxH, // Adress-Box
|
||||
$prodY + $prodH, // Produktion-Box
|
||||
$nameY + $nameBoxH, // Name-Box
|
||||
$pnBoxY + $pnBoxH, // PN/QR-Box
|
||||
$bundleY + $bundleH, // Bundle-Tabelle
|
||||
$metaY + $metaH, // Meta-Block (linke Tabelle + rechter Bildrahmen)
|
||||
];
|
||||
|
||||
// 4) Start-Y des Contentbereichs: oben an deinem Kopf beginnen
|
||||
$outerY = min($frameY, $addrY, $prodY, $nameY, $pnBoxY, 46.0 /*Bundle-Header-Y*/, $metaY);
|
||||
|
||||
// 5) Untere Kante ist die größte Bottom-Kante:
|
||||
$outerBottom = max($bottoms);
|
||||
|
||||
// 6) Außenrahmen-Geometrie:
|
||||
$outerX = $MARGIN_LEFT+1.2;
|
||||
$outerW = $pdf->getPageWidth() - $MARGIN_LEFT - $MARGIN_RIGHT+1;
|
||||
$outerH = max(2, $outerBottom - $outerY)-1.2; // Mindeshöhe absichern
|
||||
|
||||
// 7) Dicke Linie zeichnen (z.B. 1.2 mm)
|
||||
drawFixedBox($pdf, $outerX, $outerY, $outerW, $outerH, 1, '', 0, false);
|
||||
|
||||
|
||||
|
||||
$filename = 'Kistenetikett_'.preg_replace('/\s+/', '_', trim($productName)).'_'.$productNumber.$deviceNumber.'_'.date('Ymd_His').'.pdf';
|
||||
$pdf->Output($filename, 'I');
|
||||
|
||||
|
||||
/**
|
||||
* Zeichnet eine flexible, leere oder beschriftete Box mit TCPDF.
|
||||
*
|
||||
* @param TCPDF $pdf Referenz auf dein TCPDF-Objekt
|
||||
* @param float $x Linke obere Ecke (mm)
|
||||
* @param float $y Obere Position (mm)
|
||||
* @param float $w Breite (mm)
|
||||
* @param float $h Höhe (mm)
|
||||
* @param float $border Linienstärke in mm (z. B. 0.3)
|
||||
* @param string $text (Optional) Inhalt oder Beschriftung
|
||||
* @param float $pad Innenabstand in mm
|
||||
* @param bool $fill true = grau hinterlegt, false = leer
|
||||
* @param string|null $style (Optional) Linienstil – 'solid', 'dashed', 'dotted'
|
||||
*/
|
||||
function drawFixedBox(
|
||||
TCPDF $pdf,
|
||||
float $x,
|
||||
float $y,
|
||||
float $w,
|
||||
float $h,
|
||||
float $border = 0.3,
|
||||
string $text = '',
|
||||
float $pad = 1.5,
|
||||
bool $fill = false,
|
||||
?string $style = 'solid'
|
||||
): void {
|
||||
// Farbe für Füllung (hellgrau, wenn aktiviert)
|
||||
$fillColor = [240, 240, 240];
|
||||
|
||||
// Linienstil (falls angegeben)
|
||||
switch ($style) {
|
||||
case 'dashed':
|
||||
$pdf->SetLineStyle(['width' => $border, 'dash' => '3,2']);
|
||||
break;
|
||||
case 'dotted':
|
||||
$pdf->SetLineStyle(['width' => $border, 'dash' => '1,2']);
|
||||
break;
|
||||
default:
|
||||
$pdf->SetLineWidth($border);
|
||||
$pdf->SetLineStyle(['width' => $border]);
|
||||
break;
|
||||
}
|
||||
|
||||
// 1) Rahmen (und ggf. Füllung)
|
||||
if ($fill) {
|
||||
$pdf->SetFillColorArray($fillColor);
|
||||
$pdf->Rect($x, $y, $w, $h, 'DF'); // Draw + Fill
|
||||
} else {
|
||||
$pdf->Rect($x, $y, $w, $h, 'D'); // nur Rahmen
|
||||
}
|
||||
|
||||
// 2) Optionaler Textinhalt – sauber eingepasst
|
||||
if (trim($text) !== '') {
|
||||
$pdf->StartTransform();
|
||||
$pdf->Rect($x, $y, $w, $h, 'CNZ'); // Clip auf Box setzen
|
||||
|
||||
$pdf->writeHTMLCell(
|
||||
$w - 2*$pad, $h - 2*$pad,
|
||||
$x + $pad, $y + $pad,
|
||||
'<div style="font-size:9px; line-height:1.2;">'.htmlspecialchars($text).'</div>',
|
||||
0, 0, false, true, 'L', true
|
||||
);
|
||||
|
||||
$pdf->StopTransform();
|
||||
}
|
||||
|
||||
// Linienstil zurücksetzen
|
||||
$pdf->SetLineStyle(['width' => 0.3, 'dash' => 0]);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Produkt-Details mit Cache holen (vermeidet doppelte API-Calls).
|
||||
*
|
||||
* @param Epirent $Epi
|
||||
* @param int|string $productPk
|
||||
* @param array $cache Referenz-Array für Produkt-Cache
|
||||
* @return object|null
|
||||
*/
|
||||
function fetchProductDetailCached($Epi, $productPk, array &$cache) {
|
||||
$key = (string)$productPk;
|
||||
if (isset($cache[$key])) {
|
||||
return $cache[$key];
|
||||
}
|
||||
try {
|
||||
$res = $Epi->requestEpiApi('/v1/product/' . $key . '?cl=' . Epirent_Mandant);
|
||||
$payload = json_decode($res, false);
|
||||
$prod = $payload->payload[0] ?? null;
|
||||
if ($prod) {
|
||||
$cache[$key] = $prod;
|
||||
return $prod;
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
// optional: loggen
|
||||
}
|
||||
$cache[$key] = null;
|
||||
return null;
|
||||
}
|
||||
/**
|
||||
* Rekursive Hilfsfunktion: fügt Materialien (und deren Submaterialien) zu $bundle hinzu
|
||||
* und summiert alle Bruttogewichte in $bruttoWeightSum.
|
||||
*
|
||||
* @param Epirent $Epi
|
||||
* @param array $materials Materialliste (vom Produkt)
|
||||
* @param int $level aktuelle Ebene (1..MAX_BUNDLE_LEVEL)
|
||||
* @param array &$bundle Ergebnisliste (wird befüllt)
|
||||
* @param array &$cache Produkt-Cache
|
||||
* @param array &$seenStack Schutz gegen Zyklen (Stack von Produkt-Keys)
|
||||
* @param float $qtyMultiplier Multiplikator für Mengen aus übergeordneten Ebenen
|
||||
* @param float &$bruttoWeightSum Summiert alle weight_gro-Werte × Menge
|
||||
*/
|
||||
function addMaterialsRecursive($Epi, array $materials, int $level, array &$bundle, array &$cache, array &$seenStack, float $qtyMultiplier = 1.0): void
|
||||
{
|
||||
global $bruttoWeightSum;
|
||||
|
||||
$MAX_BUNDLE_LEVEL = 5;
|
||||
if ($level > $MAX_BUNDLE_LEVEL || empty($materials)) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($materials as $mat) {
|
||||
$isFree = !empty($mat->is_free_material);
|
||||
$amount = isset($mat->amount) ? (float)$mat->amount : 1.0;
|
||||
$effQty = $amount * $qtyMultiplier;
|
||||
|
||||
// 🟢 1. FREIES MATERIAL → nur Name + Menge, kein weiterer Drilldown
|
||||
if ($isFree) {
|
||||
$name = isset($mat->name) ? (string)$mat->name : '(Unbenanntes freies Material)';
|
||||
$bundle[] = [
|
||||
'qty' => $effQty,
|
||||
'text' => $name,
|
||||
'level' => $level,
|
||||
'weight_kg' => null,
|
||||
];
|
||||
continue; // ⬅️ ganz wichtig: hier abbrechen
|
||||
}
|
||||
|
||||
// 🟡 2. "Normales" Produkt-Material
|
||||
$childPk = $mat->mat_product_pk ?? null;
|
||||
if (!$childPk) {
|
||||
$bundle[] = [
|
||||
'qty' => $effQty,
|
||||
'text' => '(Unbekanntes Material)',
|
||||
'level' => $level,
|
||||
'weight_kg' => null,
|
||||
];
|
||||
continue;
|
||||
}
|
||||
|
||||
// Zyklen-Schutz
|
||||
$childKey = (string)$childPk;
|
||||
if (in_array($childKey, $seenStack, true)) {
|
||||
$bundle[] = [
|
||||
'qty' => $effQty,
|
||||
'text' => '[Zyklus erkannt bei Produkt ' . $childKey . ']',
|
||||
'level' => $level,
|
||||
'weight_kg' => null,
|
||||
];
|
||||
continue;
|
||||
}
|
||||
|
||||
// Produktdetail abrufen (Cache-basiert)
|
||||
$child = fetchProductDetailCached($Epi, $childPk, $cache);
|
||||
|
||||
// Basisinfos
|
||||
$childName = $child->name ?? ('Produkt ' . $childKey);
|
||||
$childWeightNet = $child->tech_data->weight_net ?? null;
|
||||
$childWeightGross = $child->tech_data->weight_gro ?? null;
|
||||
$childNumber = $child->product_no ?? null;
|
||||
|
||||
// 🧮 Bruttogewicht zur globalen Summe addieren
|
||||
if (!empty($childWeightGross)) {
|
||||
$bruttoWeightSum += $effQty * (float)$childWeightGross;
|
||||
}
|
||||
|
||||
// 📦 Anzeige: Artikelnummer in Klammern voranstellen
|
||||
$displayName = $childNumber ? '(' . $childNumber . ') ' . $childName : $childName;
|
||||
|
||||
$bundle[] = [
|
||||
'qty' => $effQty,
|
||||
'text' => (string)$displayName,
|
||||
'level' => $level,
|
||||
'weight_kg' => $childWeightNet ? (float)$childWeightNet : null,
|
||||
];
|
||||
|
||||
// 🔁 Rekursion für Sub-Materialien
|
||||
if ($child && !empty($child->materials) && $level < $MAX_BUNDLE_LEVEL) {
|
||||
$seenStack[] = $childKey;
|
||||
addMaterialsRecursive($Epi, (array)$child->materials, $level + 1, $bundle, $cache, $seenStack, $effQty);
|
||||
array_pop($seenStack);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Einstieg: Baut das Bundle eines Produkts bis Tiefe MAX_BUNDLE_LEVEL
|
||||
* und liefert zusätzlich die Bruttogewichtsumme zurück.
|
||||
*
|
||||
* @param Epirent $Epi
|
||||
* @param object $product
|
||||
* @param float &$bruttoWeightSum Rückgabe der aufsummierten weight_gro
|
||||
* @return array
|
||||
*/
|
||||
function buildBundleForProduct($Epi, $product, float &$bruttoWeightSum = 0.0): array
|
||||
{
|
||||
$bundle = [];
|
||||
$cache = [];
|
||||
$seen = [];
|
||||
$materials = isset($product->materials) ? (array)$product->materials : [];
|
||||
|
||||
addMaterialsRecursive($Epi, $materials, 1, $bundle, $cache, $seen, 1.0, $bruttoWeightSum);
|
||||
return $bundle;
|
||||
}
|
||||
@@ -8,9 +8,17 @@
|
||||
Dashboard
|
||||
</a>
|
||||
<a class="nav-link" href=orders.php>
|
||||
<div class="sb-nav-link-icon"><i class="fas fa-tachometer-alt"></i></div>
|
||||
<div class="sb-nav-link-icon"><i class="fas fa-money-bill-wave"></i></div>
|
||||
Aufträge
|
||||
</a>
|
||||
<a class="nav-link" href=insurance.php>
|
||||
<div class="sb-nav-link-icon"><i class="fas fa-car-burst"></i></div>
|
||||
Versicherung
|
||||
</a>
|
||||
<a class="nav-link" href=labelprint.php>
|
||||
<div class="sb-nav-link-icon"><i class="fas fa-tags"></i></div>
|
||||
Labelprint
|
||||
</a>
|
||||
|
||||
<div class="sb-sidenav-menu-heading">Addons</div>
|
||||
<a class="nav-link" target="_blank" href="../Packmonitor.php">
|
||||
@@ -25,6 +33,13 @@
|
||||
<div class="sb-nav-link-icon"><i class="fas fa-list"></i></div>
|
||||
Pack- & Aufgabenmonitor
|
||||
</a>
|
||||
|
||||
|
||||
<div class="sb-sidenav-menu-heading">Config</div>
|
||||
<a class="nav-link" href=editconfig.php>
|
||||
<div class="sb-nav-link-icon"><i class="fas fa-tachometer-alt"></i></div>
|
||||
Config
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sb-sidenav-footer">
|
||||
|
||||
BIN
src/assets/font/Hurme1/HurmeGeometricSans1-Black.ttf
Normal file
BIN
src/assets/font/Hurme1/HurmeGeometricSans1-Black.ttf
Normal file
Binary file not shown.
BIN
src/assets/font/Hurme1/HurmeGeometricSans1-BlackOblique.ttf
Normal file
BIN
src/assets/font/Hurme1/HurmeGeometricSans1-BlackOblique.ttf
Normal file
Binary file not shown.
BIN
src/assets/font/Hurme1/HurmeGeometricSans1-Bold.ttf
Normal file
BIN
src/assets/font/Hurme1/HurmeGeometricSans1-Bold.ttf
Normal file
Binary file not shown.
BIN
src/assets/font/Hurme1/HurmeGeometricSans1-BoldOblique.ttf
Normal file
BIN
src/assets/font/Hurme1/HurmeGeometricSans1-BoldOblique.ttf
Normal file
Binary file not shown.
BIN
src/assets/font/Hurme1/HurmeGeometricSans1-Hairline.ttf
Normal file
BIN
src/assets/font/Hurme1/HurmeGeometricSans1-Hairline.ttf
Normal file
Binary file not shown.
BIN
src/assets/font/Hurme1/HurmeGeometricSans1-HairlineOblique.ttf
Normal file
BIN
src/assets/font/Hurme1/HurmeGeometricSans1-HairlineOblique.ttf
Normal file
Binary file not shown.
BIN
src/assets/font/Hurme1/HurmeGeometricSans1-Light.ttf
Normal file
BIN
src/assets/font/Hurme1/HurmeGeometricSans1-Light.ttf
Normal file
Binary file not shown.
BIN
src/assets/font/Hurme1/HurmeGeometricSans1-LightOblique.ttf
Normal file
BIN
src/assets/font/Hurme1/HurmeGeometricSans1-LightOblique.ttf
Normal file
Binary file not shown.
BIN
src/assets/font/Hurme1/HurmeGeometricSans1-Regular.ttf
Normal file
BIN
src/assets/font/Hurme1/HurmeGeometricSans1-Regular.ttf
Normal file
Binary file not shown.
BIN
src/assets/font/Hurme1/HurmeGeometricSans1-RegularOblique.ttf
Normal file
BIN
src/assets/font/Hurme1/HurmeGeometricSans1-RegularOblique.ttf
Normal file
Binary file not shown.
BIN
src/assets/font/Hurme1/HurmeGeometricSans1-SemiBold.ttf
Normal file
BIN
src/assets/font/Hurme1/HurmeGeometricSans1-SemiBold.ttf
Normal file
Binary file not shown.
BIN
src/assets/font/Hurme1/HurmeGeometricSans1-SemiBoldOblique.ttf
Normal file
BIN
src/assets/font/Hurme1/HurmeGeometricSans1-SemiBoldOblique.ttf
Normal file
Binary file not shown.
BIN
src/assets/font/Hurme1/HurmeGeometricSans1-Thin.ttf
Normal file
BIN
src/assets/font/Hurme1/HurmeGeometricSans1-Thin.ttf
Normal file
Binary file not shown.
BIN
src/assets/font/Hurme1/HurmeGeometricSans1-ThinOblique.ttf
Normal file
BIN
src/assets/font/Hurme1/HurmeGeometricSans1-ThinOblique.ttf
Normal file
Binary file not shown.
BIN
src/assets/font/Hurme3/HurmeGeometricSans3-Black.ttf
Normal file
BIN
src/assets/font/Hurme3/HurmeGeometricSans3-Black.ttf
Normal file
Binary file not shown.
BIN
src/assets/font/Hurme3/HurmeGeometricSans3-BlackOblique.ttf
Normal file
BIN
src/assets/font/Hurme3/HurmeGeometricSans3-BlackOblique.ttf
Normal file
Binary file not shown.
BIN
src/assets/font/Hurme3/HurmeGeometricSans3-Bold.ttf
Normal file
BIN
src/assets/font/Hurme3/HurmeGeometricSans3-Bold.ttf
Normal file
Binary file not shown.
BIN
src/assets/font/Hurme3/HurmeGeometricSans3-BoldOblique.ttf
Normal file
BIN
src/assets/font/Hurme3/HurmeGeometricSans3-BoldOblique.ttf
Normal file
Binary file not shown.
BIN
src/assets/font/Hurme3/HurmeGeometricSans3-Hairline.ttf
Normal file
BIN
src/assets/font/Hurme3/HurmeGeometricSans3-Hairline.ttf
Normal file
Binary file not shown.
BIN
src/assets/font/Hurme3/HurmeGeometricSans3-HairlineOblique.ttf
Normal file
BIN
src/assets/font/Hurme3/HurmeGeometricSans3-HairlineOblique.ttf
Normal file
Binary file not shown.
BIN
src/assets/font/Hurme3/HurmeGeometricSans3-Light.ttf
Normal file
BIN
src/assets/font/Hurme3/HurmeGeometricSans3-Light.ttf
Normal file
Binary file not shown.
BIN
src/assets/font/Hurme3/HurmeGeometricSans3-LightOblique.ttf
Normal file
BIN
src/assets/font/Hurme3/HurmeGeometricSans3-LightOblique.ttf
Normal file
Binary file not shown.
BIN
src/assets/font/Hurme3/HurmeGeometricSans3-Regular.ttf
Normal file
BIN
src/assets/font/Hurme3/HurmeGeometricSans3-Regular.ttf
Normal file
Binary file not shown.
BIN
src/assets/font/Hurme3/HurmeGeometricSans3-RegularOblique.ttf
Normal file
BIN
src/assets/font/Hurme3/HurmeGeometricSans3-RegularOblique.ttf
Normal file
Binary file not shown.
BIN
src/assets/font/Hurme3/HurmeGeometricSans3-SemiBold.ttf
Normal file
BIN
src/assets/font/Hurme3/HurmeGeometricSans3-SemiBold.ttf
Normal file
Binary file not shown.
BIN
src/assets/font/Hurme3/HurmeGeometricSans3-SemiBoldOblique.ttf
Normal file
BIN
src/assets/font/Hurme3/HurmeGeometricSans3-SemiBoldOblique.ttf
Normal file
Binary file not shown.
BIN
src/assets/font/Hurme3/HurmeGeometricSans3-Thin.ttf
Normal file
BIN
src/assets/font/Hurme3/HurmeGeometricSans3-Thin.ttf
Normal file
Binary file not shown.
BIN
src/assets/font/Hurme3/HurmeGeometricSans3-ThinOblique.ttf
Normal file
BIN
src/assets/font/Hurme3/HurmeGeometricSans3-ThinOblique.ttf
Normal file
Binary file not shown.
BIN
src/assets/img/logo.png
Normal file
BIN
src/assets/img/logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 30 KiB |
10663
src/fa/css/all.css
Normal file
10663
src/fa/css/all.css
Normal file
File diff suppressed because it is too large
Load Diff
9
src/fa/css/all.min.css
vendored
Normal file
9
src/fa/css/all.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
2219
src/fa/css/brands.css
Normal file
2219
src/fa/css/brands.css
Normal file
File diff suppressed because it is too large
Load Diff
6
src/fa/css/brands.min.css
vendored
Normal file
6
src/fa/css/brands.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
8361
src/fa/css/fontawesome.css
vendored
Normal file
8361
src/fa/css/fontawesome.css
vendored
Normal file
File diff suppressed because it is too large
Load Diff
8
src/fa/css/fontawesome.min.css
vendored
Normal file
8
src/fa/css/fontawesome.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
31
src/fa/css/regular.css
Normal file
31
src/fa/css/regular.css
Normal file
@@ -0,0 +1,31 @@
|
||||
/*!
|
||||
* Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com
|
||||
* License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
|
||||
* Copyright 2025 Fonticons, Inc.
|
||||
*/
|
||||
:root, :host {
|
||||
--fa-family-classic: "Font Awesome 7 Free";
|
||||
--fa-font-regular: normal 400 1em/1 var(--fa-family-classic);
|
||||
/* deprecated: this older custom property will be removed next major release */
|
||||
--fa-style-family-classic: var(--fa-family-classic);
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Font Awesome 7 Free";
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: block;
|
||||
src: url("../webfonts/fa-regular-400.woff2");
|
||||
}
|
||||
.far {
|
||||
--fa-family: var(--fa-family-classic);
|
||||
--fa-style: 400;
|
||||
}
|
||||
|
||||
.fa-classic {
|
||||
--fa-family: var(--fa-family-classic);
|
||||
}
|
||||
|
||||
.fa-regular {
|
||||
--fa-style: 400;
|
||||
}
|
||||
6
src/fa/css/regular.min.css
vendored
Normal file
6
src/fa/css/regular.min.css
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
/*!
|
||||
* Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com
|
||||
* License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
|
||||
* Copyright 2025 Fonticons, Inc.
|
||||
*/
|
||||
:host,:root{--fa-family-classic:"Font Awesome 7 Free";--fa-font-regular:normal 400 1em/1 var(--fa-family-classic);--fa-style-family-classic:var(--fa-family-classic)}@font-face{font-family:"Font Awesome 7 Free";font-style:normal;font-weight:400;font-display:block;src:url(../webfonts/fa-regular-400.woff2)}.far{--fa-style:400}.fa-classic,.far{--fa-family:var(--fa-family-classic)}.fa-regular{--fa-style:400}
|
||||
31
src/fa/css/solid.css
Normal file
31
src/fa/css/solid.css
Normal file
@@ -0,0 +1,31 @@
|
||||
/*!
|
||||
* Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com
|
||||
* License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
|
||||
* Copyright 2025 Fonticons, Inc.
|
||||
*/
|
||||
:root, :host {
|
||||
--fa-family-classic: "Font Awesome 7 Free";
|
||||
--fa-font-solid: normal 900 1em/1 var(--fa-family-classic);
|
||||
/* deprecated: this older custom property will be removed next major release */
|
||||
--fa-style-family-classic: var(--fa-family-classic);
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Font Awesome 7 Free";
|
||||
font-style: normal;
|
||||
font-weight: 900;
|
||||
font-display: block;
|
||||
src: url("../webfonts/fa-solid-900.woff2");
|
||||
}
|
||||
.fas {
|
||||
--fa-family: var(--fa-family-classic);
|
||||
--fa-style: 900;
|
||||
}
|
||||
|
||||
.fa-classic {
|
||||
--fa-family: var(--fa-family-classic);
|
||||
}
|
||||
|
||||
.fa-solid {
|
||||
--fa-style: 900;
|
||||
}
|
||||
6
src/fa/css/solid.min.css
vendored
Normal file
6
src/fa/css/solid.min.css
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
/*!
|
||||
* Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com
|
||||
* License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
|
||||
* Copyright 2025 Fonticons, Inc.
|
||||
*/
|
||||
:host,:root{--fa-family-classic:"Font Awesome 7 Free";--fa-font-solid:normal 900 1em/1 var(--fa-family-classic);--fa-style-family-classic:var(--fa-family-classic)}@font-face{font-family:"Font Awesome 7 Free";font-style:normal;font-weight:900;font-display:block;src:url(../webfonts/fa-solid-900.woff2)}.fas{--fa-style:900}.fa-classic,.fas{--fa-family:var(--fa-family-classic)}.fa-solid{--fa-style:900}
|
||||
556
src/fa/css/svg-with-js.css
Normal file
556
src/fa/css/svg-with-js.css
Normal file
@@ -0,0 +1,556 @@
|
||||
/*!
|
||||
* Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com
|
||||
* License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
|
||||
* Copyright 2025 Fonticons, Inc.
|
||||
*/
|
||||
:root, :host {
|
||||
--fa-font-solid: normal 900 1em/1 "Font Awesome 7 Free";
|
||||
--fa-font-regular: normal 400 1em/1 "Font Awesome 7 Free";
|
||||
--fa-font-light: normal 300 1em/1 "Font Awesome 7 Pro";
|
||||
--fa-font-thin: normal 100 1em/1 "Font Awesome 7 Pro";
|
||||
--fa-font-duotone: normal 900 1em/1 "Font Awesome 7 Duotone";
|
||||
--fa-font-duotone-regular: normal 400 1em/1 "Font Awesome 7 Duotone";
|
||||
--fa-font-duotone-light: normal 300 1em/1 "Font Awesome 7 Duotone";
|
||||
--fa-font-duotone-thin: normal 100 1em/1 "Font Awesome 7 Duotone";
|
||||
--fa-font-brands: normal 400 1em/1 "Font Awesome 7 Brands";
|
||||
--fa-font-sharp-solid: normal 900 1em/1 "Font Awesome 7 Sharp";
|
||||
--fa-font-sharp-regular: normal 400 1em/1 "Font Awesome 7 Sharp";
|
||||
--fa-font-sharp-light: normal 300 1em/1 "Font Awesome 7 Sharp";
|
||||
--fa-font-sharp-thin: normal 100 1em/1 "Font Awesome 7 Sharp";
|
||||
--fa-font-sharp-duotone-solid: normal 900 1em/1 "Font Awesome 7 Sharp Duotone";
|
||||
--fa-font-sharp-duotone-regular: normal 400 1em/1 "Font Awesome 7 Sharp Duotone";
|
||||
--fa-font-sharp-duotone-light: normal 300 1em/1 "Font Awesome 7 Sharp Duotone";
|
||||
--fa-font-sharp-duotone-thin: normal 100 1em/1 "Font Awesome 7 Sharp Duotone";
|
||||
--fa-font-slab-regular: normal 400 1em/1 "Font Awesome 7 Slab";
|
||||
--fa-font-slab-press-regular: normal 400 1em/1 "Font Awesome 7 Slab Press";
|
||||
--fa-font-whiteboard-semibold: normal 600 1em/1 "Font Awesome 7 Whiteboard";
|
||||
--fa-font-thumbprint-light: normal 300 1em/1 "Font Awesome 7 Thumbprint";
|
||||
--fa-font-notdog-solid: normal 900 1em/1 "Font Awesome 7 Notdog";
|
||||
--fa-font-notdog-duo-solid: normal 900 1em/1 "Font Awesome 7 Notdog Duo";
|
||||
--fa-font-etch-solid: normal 900 1em/1 "Font Awesome 7 Etch";
|
||||
--fa-font-jelly-regular: normal 400 1em/1 "Font Awesome 7 Jelly";
|
||||
--fa-font-jelly-fill-regular: normal 400 1em/1 "Font Awesome 7 Jelly Fill";
|
||||
--fa-font-jelly-duo-regular: normal 400 1em/1 "Font Awesome 7 Jelly Duo";
|
||||
--fa-font-chisel-regular: normal 400 1em/1 "Font Awesome 7 Chisel";
|
||||
--fa-font-utility-semibold: normal 600 1em/1 "Font Awesome 7 Utility";
|
||||
--fa-font-utility-duo-semibold: normal 600 1em/1 "Font Awesome 7 Utility Duo";
|
||||
--fa-font-utility-fill-semibold: normal 600 1em/1 "Font Awesome 7 Utility Fill";
|
||||
}
|
||||
|
||||
.svg-inline--fa {
|
||||
box-sizing: content-box;
|
||||
display: var(--fa-display, inline-block);
|
||||
height: 1em;
|
||||
overflow: visible;
|
||||
vertical-align: -0.125em;
|
||||
width: var(--fa-width, 1.25em);
|
||||
}
|
||||
.svg-inline--fa.fa-2xs {
|
||||
vertical-align: 0.1em;
|
||||
}
|
||||
.svg-inline--fa.fa-xs {
|
||||
vertical-align: 0em;
|
||||
}
|
||||
.svg-inline--fa.fa-sm {
|
||||
vertical-align: -0.0714285714em;
|
||||
}
|
||||
.svg-inline--fa.fa-lg {
|
||||
vertical-align: -0.2em;
|
||||
}
|
||||
.svg-inline--fa.fa-xl {
|
||||
vertical-align: -0.25em;
|
||||
}
|
||||
.svg-inline--fa.fa-2xl {
|
||||
vertical-align: -0.3125em;
|
||||
}
|
||||
.svg-inline--fa.fa-pull-left,
|
||||
.svg-inline--fa .fa-pull-start {
|
||||
float: inline-start;
|
||||
margin-inline-end: var(--fa-pull-margin, 0.3em);
|
||||
}
|
||||
.svg-inline--fa.fa-pull-right,
|
||||
.svg-inline--fa .fa-pull-end {
|
||||
float: inline-end;
|
||||
margin-inline-start: var(--fa-pull-margin, 0.3em);
|
||||
}
|
||||
.svg-inline--fa.fa-li {
|
||||
width: var(--fa-li-width, 2em);
|
||||
inset-inline-start: calc(-1 * var(--fa-li-width, 2em));
|
||||
inset-block-start: 0.25em; /* syncing vertical alignment with Web Font rendering */
|
||||
}
|
||||
|
||||
.fa-layers-counter, .fa-layers-text {
|
||||
display: inline-block;
|
||||
position: absolute;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.fa-layers {
|
||||
display: inline-block;
|
||||
height: 1em;
|
||||
position: relative;
|
||||
text-align: center;
|
||||
vertical-align: -0.125em;
|
||||
width: var(--fa-width, 1.25em);
|
||||
}
|
||||
.fa-layers .svg-inline--fa {
|
||||
inset: 0;
|
||||
margin: auto;
|
||||
position: absolute;
|
||||
transform-origin: center center;
|
||||
}
|
||||
|
||||
.fa-layers-text {
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
transform-origin: center center;
|
||||
}
|
||||
|
||||
.fa-layers-counter {
|
||||
background-color: var(--fa-counter-background-color, #ff253a);
|
||||
border-radius: var(--fa-counter-border-radius, 1em);
|
||||
box-sizing: border-box;
|
||||
color: var(--fa-inverse, #fff);
|
||||
line-height: var(--fa-counter-line-height, 1);
|
||||
max-width: var(--fa-counter-max-width, 5em);
|
||||
min-width: var(--fa-counter-min-width, 1.5em);
|
||||
overflow: hidden;
|
||||
padding: var(--fa-counter-padding, 0.25em 0.5em);
|
||||
right: var(--fa-right, 0);
|
||||
text-overflow: ellipsis;
|
||||
top: var(--fa-top, 0);
|
||||
transform: scale(var(--fa-counter-scale, 0.25));
|
||||
transform-origin: top right;
|
||||
}
|
||||
|
||||
.fa-layers-bottom-right {
|
||||
bottom: var(--fa-bottom, 0);
|
||||
right: var(--fa-right, 0);
|
||||
top: auto;
|
||||
transform: scale(var(--fa-layers-scale, 0.25));
|
||||
transform-origin: bottom right;
|
||||
}
|
||||
|
||||
.fa-layers-bottom-left {
|
||||
bottom: var(--fa-bottom, 0);
|
||||
left: var(--fa-left, 0);
|
||||
right: auto;
|
||||
top: auto;
|
||||
transform: scale(var(--fa-layers-scale, 0.25));
|
||||
transform-origin: bottom left;
|
||||
}
|
||||
|
||||
.fa-layers-top-right {
|
||||
top: var(--fa-top, 0);
|
||||
right: var(--fa-right, 0);
|
||||
transform: scale(var(--fa-layers-scale, 0.25));
|
||||
transform-origin: top right;
|
||||
}
|
||||
|
||||
.fa-layers-top-left {
|
||||
left: var(--fa-left, 0);
|
||||
right: auto;
|
||||
top: var(--fa-top, 0);
|
||||
transform: scale(var(--fa-layers-scale, 0.25));
|
||||
transform-origin: top left;
|
||||
}
|
||||
|
||||
.fa-1x {
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
.fa-2x {
|
||||
font-size: 2em;
|
||||
}
|
||||
|
||||
.fa-3x {
|
||||
font-size: 3em;
|
||||
}
|
||||
|
||||
.fa-4x {
|
||||
font-size: 4em;
|
||||
}
|
||||
|
||||
.fa-5x {
|
||||
font-size: 5em;
|
||||
}
|
||||
|
||||
.fa-6x {
|
||||
font-size: 6em;
|
||||
}
|
||||
|
||||
.fa-7x {
|
||||
font-size: 7em;
|
||||
}
|
||||
|
||||
.fa-8x {
|
||||
font-size: 8em;
|
||||
}
|
||||
|
||||
.fa-9x {
|
||||
font-size: 9em;
|
||||
}
|
||||
|
||||
.fa-10x {
|
||||
font-size: 10em;
|
||||
}
|
||||
|
||||
.fa-2xs {
|
||||
font-size: calc(10 / 16 * 1em); /* converts a 10px size into an em-based value that's relative to the scale's 16px base */
|
||||
line-height: calc(1 / 10 * 1em); /* sets the line-height of the icon back to that of it's parent */
|
||||
vertical-align: calc((6 / 10 - 0.375) * 1em); /* vertically centers the icon taking into account the surrounding text's descender */
|
||||
}
|
||||
|
||||
.fa-xs {
|
||||
font-size: calc(12 / 16 * 1em); /* converts a 12px size into an em-based value that's relative to the scale's 16px base */
|
||||
line-height: calc(1 / 12 * 1em); /* sets the line-height of the icon back to that of it's parent */
|
||||
vertical-align: calc((6 / 12 - 0.375) * 1em); /* vertically centers the icon taking into account the surrounding text's descender */
|
||||
}
|
||||
|
||||
.fa-sm {
|
||||
font-size: calc(14 / 16 * 1em); /* converts a 14px size into an em-based value that's relative to the scale's 16px base */
|
||||
line-height: calc(1 / 14 * 1em); /* sets the line-height of the icon back to that of it's parent */
|
||||
vertical-align: calc((6 / 14 - 0.375) * 1em); /* vertically centers the icon taking into account the surrounding text's descender */
|
||||
}
|
||||
|
||||
.fa-lg {
|
||||
font-size: calc(20 / 16 * 1em); /* converts a 20px size into an em-based value that's relative to the scale's 16px base */
|
||||
line-height: calc(1 / 20 * 1em); /* sets the line-height of the icon back to that of it's parent */
|
||||
vertical-align: calc((6 / 20 - 0.375) * 1em); /* vertically centers the icon taking into account the surrounding text's descender */
|
||||
}
|
||||
|
||||
.fa-xl {
|
||||
font-size: calc(24 / 16 * 1em); /* converts a 24px size into an em-based value that's relative to the scale's 16px base */
|
||||
line-height: calc(1 / 24 * 1em); /* sets the line-height of the icon back to that of it's parent */
|
||||
vertical-align: calc((6 / 24 - 0.375) * 1em); /* vertically centers the icon taking into account the surrounding text's descender */
|
||||
}
|
||||
|
||||
.fa-2xl {
|
||||
font-size: calc(32 / 16 * 1em); /* converts a 32px size into an em-based value that's relative to the scale's 16px base */
|
||||
line-height: calc(1 / 32 * 1em); /* sets the line-height of the icon back to that of it's parent */
|
||||
vertical-align: calc((6 / 32 - 0.375) * 1em); /* vertically centers the icon taking into account the surrounding text's descender */
|
||||
}
|
||||
|
||||
.fa-width-auto {
|
||||
--fa-width: auto;
|
||||
}
|
||||
|
||||
.fa-fw,
|
||||
.fa-width-fixed {
|
||||
--fa-width: 1.25em;
|
||||
}
|
||||
|
||||
.fa-ul {
|
||||
list-style-type: none;
|
||||
margin-inline-start: var(--fa-li-margin, 2.5em);
|
||||
padding-inline-start: 0;
|
||||
}
|
||||
.fa-ul > li {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.fa-li {
|
||||
inset-inline-start: calc(-1 * var(--fa-li-width, 2em));
|
||||
position: absolute;
|
||||
text-align: center;
|
||||
width: var(--fa-li-width, 2em);
|
||||
line-height: inherit;
|
||||
}
|
||||
|
||||
/* Heads Up: Bordered Icons will not be supported in the future!
|
||||
- This feature will be deprecated in the next major release of Font Awesome (v8)!
|
||||
- You may continue to use it in this version *v7), but it will not be supported in Font Awesome v8.
|
||||
*/
|
||||
/* Notes:
|
||||
* --@{v.$css-prefix}-border-width = 1/16 by default (to render as ~1px based on a 16px default font-size)
|
||||
* --@{v.$css-prefix}-border-padding =
|
||||
** 3/16 for vertical padding (to give ~2px of vertical whitespace around an icon considering it's vertical alignment)
|
||||
** 4/16 for horizontal padding (to give ~4px of horizontal whitespace around an icon)
|
||||
*/
|
||||
.fa-border {
|
||||
border-color: var(--fa-border-color, #eee);
|
||||
border-radius: var(--fa-border-radius, 0.1em);
|
||||
border-style: var(--fa-border-style, solid);
|
||||
border-width: var(--fa-border-width, 0.0625em);
|
||||
box-sizing: var(--fa-border-box-sizing, content-box);
|
||||
padding: var(--fa-border-padding, 0.1875em 0.25em);
|
||||
}
|
||||
|
||||
.fa-pull-left,
|
||||
.fa-pull-start {
|
||||
float: inline-start;
|
||||
margin-inline-end: var(--fa-pull-margin, 0.3em);
|
||||
}
|
||||
|
||||
.fa-pull-right,
|
||||
.fa-pull-end {
|
||||
float: inline-end;
|
||||
margin-inline-start: var(--fa-pull-margin, 0.3em);
|
||||
}
|
||||
|
||||
.fa-beat {
|
||||
animation-name: fa-beat;
|
||||
animation-delay: var(--fa-animation-delay, 0s);
|
||||
animation-direction: var(--fa-animation-direction, normal);
|
||||
animation-duration: var(--fa-animation-duration, 1s);
|
||||
animation-iteration-count: var(--fa-animation-iteration-count, infinite);
|
||||
animation-timing-function: var(--fa-animation-timing, ease-in-out);
|
||||
}
|
||||
|
||||
.fa-bounce {
|
||||
animation-name: fa-bounce;
|
||||
animation-delay: var(--fa-animation-delay, 0s);
|
||||
animation-direction: var(--fa-animation-direction, normal);
|
||||
animation-duration: var(--fa-animation-duration, 1s);
|
||||
animation-iteration-count: var(--fa-animation-iteration-count, infinite);
|
||||
animation-timing-function: var(--fa-animation-timing, cubic-bezier(0.28, 0.84, 0.42, 1));
|
||||
}
|
||||
|
||||
.fa-fade {
|
||||
animation-name: fa-fade;
|
||||
animation-delay: var(--fa-animation-delay, 0s);
|
||||
animation-direction: var(--fa-animation-direction, normal);
|
||||
animation-duration: var(--fa-animation-duration, 1s);
|
||||
animation-iteration-count: var(--fa-animation-iteration-count, infinite);
|
||||
animation-timing-function: var(--fa-animation-timing, cubic-bezier(0.4, 0, 0.6, 1));
|
||||
}
|
||||
|
||||
.fa-beat-fade {
|
||||
animation-name: fa-beat-fade;
|
||||
animation-delay: var(--fa-animation-delay, 0s);
|
||||
animation-direction: var(--fa-animation-direction, normal);
|
||||
animation-duration: var(--fa-animation-duration, 1s);
|
||||
animation-iteration-count: var(--fa-animation-iteration-count, infinite);
|
||||
animation-timing-function: var(--fa-animation-timing, cubic-bezier(0.4, 0, 0.6, 1));
|
||||
}
|
||||
|
||||
.fa-flip {
|
||||
animation-name: fa-flip;
|
||||
animation-delay: var(--fa-animation-delay, 0s);
|
||||
animation-direction: var(--fa-animation-direction, normal);
|
||||
animation-duration: var(--fa-animation-duration, 1s);
|
||||
animation-iteration-count: var(--fa-animation-iteration-count, infinite);
|
||||
animation-timing-function: var(--fa-animation-timing, ease-in-out);
|
||||
}
|
||||
|
||||
.fa-shake {
|
||||
animation-name: fa-shake;
|
||||
animation-delay: var(--fa-animation-delay, 0s);
|
||||
animation-direction: var(--fa-animation-direction, normal);
|
||||
animation-duration: var(--fa-animation-duration, 1s);
|
||||
animation-iteration-count: var(--fa-animation-iteration-count, infinite);
|
||||
animation-timing-function: var(--fa-animation-timing, linear);
|
||||
}
|
||||
|
||||
.fa-spin {
|
||||
animation-name: fa-spin;
|
||||
animation-delay: var(--fa-animation-delay, 0s);
|
||||
animation-direction: var(--fa-animation-direction, normal);
|
||||
animation-duration: var(--fa-animation-duration, 2s);
|
||||
animation-iteration-count: var(--fa-animation-iteration-count, infinite);
|
||||
animation-timing-function: var(--fa-animation-timing, linear);
|
||||
}
|
||||
|
||||
.fa-spin-reverse {
|
||||
--fa-animation-direction: reverse;
|
||||
}
|
||||
|
||||
.fa-pulse,
|
||||
.fa-spin-pulse {
|
||||
animation-name: fa-spin;
|
||||
animation-direction: var(--fa-animation-direction, normal);
|
||||
animation-duration: var(--fa-animation-duration, 1s);
|
||||
animation-iteration-count: var(--fa-animation-iteration-count, infinite);
|
||||
animation-timing-function: var(--fa-animation-timing, steps(8));
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.fa-beat,
|
||||
.fa-bounce,
|
||||
.fa-fade,
|
||||
.fa-beat-fade,
|
||||
.fa-flip,
|
||||
.fa-pulse,
|
||||
.fa-shake,
|
||||
.fa-spin,
|
||||
.fa-spin-pulse {
|
||||
animation: none !important;
|
||||
transition: none !important;
|
||||
}
|
||||
}
|
||||
@keyframes fa-beat {
|
||||
0%, 90% {
|
||||
transform: scale(1);
|
||||
}
|
||||
45% {
|
||||
transform: scale(var(--fa-beat-scale, 1.25));
|
||||
}
|
||||
}
|
||||
@keyframes fa-bounce {
|
||||
0% {
|
||||
transform: scale(1, 1) translateY(0);
|
||||
}
|
||||
10% {
|
||||
transform: scale(var(--fa-bounce-start-scale-x, 1.1), var(--fa-bounce-start-scale-y, 0.9)) translateY(0);
|
||||
}
|
||||
30% {
|
||||
transform: scale(var(--fa-bounce-jump-scale-x, 0.9), var(--fa-bounce-jump-scale-y, 1.1)) translateY(var(--fa-bounce-height, -0.5em));
|
||||
}
|
||||
50% {
|
||||
transform: scale(var(--fa-bounce-land-scale-x, 1.05), var(--fa-bounce-land-scale-y, 0.95)) translateY(0);
|
||||
}
|
||||
57% {
|
||||
transform: scale(1, 1) translateY(var(--fa-bounce-rebound, -0.125em));
|
||||
}
|
||||
64% {
|
||||
transform: scale(1, 1) translateY(0);
|
||||
}
|
||||
100% {
|
||||
transform: scale(1, 1) translateY(0);
|
||||
}
|
||||
}
|
||||
@keyframes fa-fade {
|
||||
50% {
|
||||
opacity: var(--fa-fade-opacity, 0.4);
|
||||
}
|
||||
}
|
||||
@keyframes fa-beat-fade {
|
||||
0%, 100% {
|
||||
opacity: var(--fa-beat-fade-opacity, 0.4);
|
||||
transform: scale(1);
|
||||
}
|
||||
50% {
|
||||
opacity: 1;
|
||||
transform: scale(var(--fa-beat-fade-scale, 1.125));
|
||||
}
|
||||
}
|
||||
@keyframes fa-flip {
|
||||
50% {
|
||||
transform: rotate3d(var(--fa-flip-x, 0), var(--fa-flip-y, 1), var(--fa-flip-z, 0), var(--fa-flip-angle, -180deg));
|
||||
}
|
||||
}
|
||||
@keyframes fa-shake {
|
||||
0% {
|
||||
transform: rotate(-15deg);
|
||||
}
|
||||
4% {
|
||||
transform: rotate(15deg);
|
||||
}
|
||||
8%, 24% {
|
||||
transform: rotate(-18deg);
|
||||
}
|
||||
12%, 28% {
|
||||
transform: rotate(18deg);
|
||||
}
|
||||
16% {
|
||||
transform: rotate(-22deg);
|
||||
}
|
||||
20% {
|
||||
transform: rotate(22deg);
|
||||
}
|
||||
32% {
|
||||
transform: rotate(-12deg);
|
||||
}
|
||||
36% {
|
||||
transform: rotate(12deg);
|
||||
}
|
||||
40%, 100% {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
}
|
||||
@keyframes fa-spin {
|
||||
0% {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
100% {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
.fa-rotate-90 {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
|
||||
.fa-rotate-180 {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
|
||||
.fa-rotate-270 {
|
||||
transform: rotate(270deg);
|
||||
}
|
||||
|
||||
.fa-flip-horizontal {
|
||||
transform: scale(-1, 1);
|
||||
}
|
||||
|
||||
.fa-flip-vertical {
|
||||
transform: scale(1, -1);
|
||||
}
|
||||
|
||||
.fa-flip-both,
|
||||
.fa-flip-horizontal.fa-flip-vertical {
|
||||
transform: scale(-1, -1);
|
||||
}
|
||||
|
||||
.fa-rotate-by {
|
||||
transform: rotate(var(--fa-rotate-angle, 0));
|
||||
}
|
||||
|
||||
.svg-inline--fa .fa-primary {
|
||||
fill: var(--fa-primary-color, currentColor);
|
||||
opacity: var(--fa-primary-opacity, 1);
|
||||
}
|
||||
|
||||
.svg-inline--fa .fa-secondary {
|
||||
fill: var(--fa-secondary-color, currentColor);
|
||||
opacity: var(--fa-secondary-opacity, 0.4);
|
||||
}
|
||||
|
||||
.svg-inline--fa.fa-swap-opacity .fa-primary {
|
||||
opacity: var(--fa-secondary-opacity, 0.4);
|
||||
}
|
||||
|
||||
.svg-inline--fa.fa-swap-opacity .fa-secondary {
|
||||
opacity: var(--fa-primary-opacity, 1);
|
||||
}
|
||||
|
||||
.svg-inline--fa mask .fa-primary,
|
||||
.svg-inline--fa mask .fa-secondary {
|
||||
fill: black;
|
||||
}
|
||||
|
||||
.svg-inline--fa.fa-inverse {
|
||||
fill: var(--fa-inverse, #fff);
|
||||
}
|
||||
|
||||
.fa-stack {
|
||||
display: inline-block;
|
||||
height: 2em;
|
||||
line-height: 2em;
|
||||
position: relative;
|
||||
vertical-align: middle;
|
||||
width: 2.5em;
|
||||
}
|
||||
|
||||
.fa-inverse {
|
||||
color: var(--fa-inverse, #fff);
|
||||
}
|
||||
|
||||
.svg-inline--fa.fa-stack-1x {
|
||||
--fa-width: 1.25em;
|
||||
height: 1em;
|
||||
width: var(--fa-width);
|
||||
}
|
||||
.svg-inline--fa.fa-stack-2x {
|
||||
--fa-width: 2.5em;
|
||||
height: 2em;
|
||||
width: var(--fa-width);
|
||||
}
|
||||
|
||||
.fa-stack-1x,
|
||||
.fa-stack-2x {
|
||||
inset: 0;
|
||||
margin: auto;
|
||||
position: absolute;
|
||||
z-index: var(--fa-stack-z-index, auto);
|
||||
}
|
||||
6
src/fa/css/svg-with-js.min.css
vendored
Normal file
6
src/fa/css/svg-with-js.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
182
src/fa/css/svg.css
Normal file
182
src/fa/css/svg.css
Normal file
@@ -0,0 +1,182 @@
|
||||
/*!
|
||||
* Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com
|
||||
* License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
|
||||
* Copyright 2025 Fonticons, Inc.
|
||||
*/
|
||||
:root, :host {
|
||||
--fa-font-solid: normal 900 1em/1 "Font Awesome 7 Free";
|
||||
--fa-font-regular: normal 400 1em/1 "Font Awesome 7 Free";
|
||||
--fa-font-light: normal 300 1em/1 "Font Awesome 7 Pro";
|
||||
--fa-font-thin: normal 100 1em/1 "Font Awesome 7 Pro";
|
||||
--fa-font-duotone: normal 900 1em/1 "Font Awesome 7 Duotone";
|
||||
--fa-font-duotone-regular: normal 400 1em/1 "Font Awesome 7 Duotone";
|
||||
--fa-font-duotone-light: normal 300 1em/1 "Font Awesome 7 Duotone";
|
||||
--fa-font-duotone-thin: normal 100 1em/1 "Font Awesome 7 Duotone";
|
||||
--fa-font-brands: normal 400 1em/1 "Font Awesome 7 Brands";
|
||||
--fa-font-sharp-solid: normal 900 1em/1 "Font Awesome 7 Sharp";
|
||||
--fa-font-sharp-regular: normal 400 1em/1 "Font Awesome 7 Sharp";
|
||||
--fa-font-sharp-light: normal 300 1em/1 "Font Awesome 7 Sharp";
|
||||
--fa-font-sharp-thin: normal 100 1em/1 "Font Awesome 7 Sharp";
|
||||
--fa-font-sharp-duotone-solid: normal 900 1em/1 "Font Awesome 7 Sharp Duotone";
|
||||
--fa-font-sharp-duotone-regular: normal 400 1em/1 "Font Awesome 7 Sharp Duotone";
|
||||
--fa-font-sharp-duotone-light: normal 300 1em/1 "Font Awesome 7 Sharp Duotone";
|
||||
--fa-font-sharp-duotone-thin: normal 100 1em/1 "Font Awesome 7 Sharp Duotone";
|
||||
--fa-font-slab-regular: normal 400 1em/1 "Font Awesome 7 Slab";
|
||||
--fa-font-slab-press-regular: normal 400 1em/1 "Font Awesome 7 Slab Press";
|
||||
--fa-font-whiteboard-semibold: normal 600 1em/1 "Font Awesome 7 Whiteboard";
|
||||
--fa-font-thumbprint-light: normal 300 1em/1 "Font Awesome 7 Thumbprint";
|
||||
--fa-font-notdog-solid: normal 900 1em/1 "Font Awesome 7 Notdog";
|
||||
--fa-font-notdog-duo-solid: normal 900 1em/1 "Font Awesome 7 Notdog Duo";
|
||||
--fa-font-etch-solid: normal 900 1em/1 "Font Awesome 7 Etch";
|
||||
--fa-font-jelly-regular: normal 400 1em/1 "Font Awesome 7 Jelly";
|
||||
--fa-font-jelly-fill-regular: normal 400 1em/1 "Font Awesome 7 Jelly Fill";
|
||||
--fa-font-jelly-duo-regular: normal 400 1em/1 "Font Awesome 7 Jelly Duo";
|
||||
--fa-font-chisel-regular: normal 400 1em/1 "Font Awesome 7 Chisel";
|
||||
--fa-font-utility-semibold: normal 600 1em/1 "Font Awesome 7 Utility";
|
||||
--fa-font-utility-duo-semibold: normal 600 1em/1 "Font Awesome 7 Utility Duo";
|
||||
--fa-font-utility-fill-semibold: normal 600 1em/1 "Font Awesome 7 Utility Fill";
|
||||
}
|
||||
|
||||
.svg-inline--fa {
|
||||
box-sizing: content-box;
|
||||
display: var(--fa-display, inline-block);
|
||||
height: 1em;
|
||||
overflow: visible;
|
||||
vertical-align: -0.125em;
|
||||
width: var(--fa-width, 1.25em);
|
||||
}
|
||||
.svg-inline--fa.fa-2xs {
|
||||
vertical-align: 0.1em;
|
||||
}
|
||||
.svg-inline--fa.fa-xs {
|
||||
vertical-align: 0em;
|
||||
}
|
||||
.svg-inline--fa.fa-sm {
|
||||
vertical-align: -0.0714285714em;
|
||||
}
|
||||
.svg-inline--fa.fa-lg {
|
||||
vertical-align: -0.2em;
|
||||
}
|
||||
.svg-inline--fa.fa-xl {
|
||||
vertical-align: -0.25em;
|
||||
}
|
||||
.svg-inline--fa.fa-2xl {
|
||||
vertical-align: -0.3125em;
|
||||
}
|
||||
.svg-inline--fa.fa-li {
|
||||
width: var(--fa-li-width, 2em);
|
||||
inset-inline-start: calc(-1 * var(--fa-li-width, 2em));
|
||||
inset-block-start: 0.25em; /* syncing vertical alignment with Web Font rendering */
|
||||
}
|
||||
|
||||
.fa-layers-counter, .fa-layers-text {
|
||||
display: inline-block;
|
||||
position: absolute;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.fa-layers {
|
||||
display: inline-block;
|
||||
height: 1em;
|
||||
position: relative;
|
||||
text-align: center;
|
||||
vertical-align: -0.125em;
|
||||
width: var(--fa-width, 1.25em);
|
||||
}
|
||||
.fa-layers .svg-inline--fa {
|
||||
inset: 0;
|
||||
margin: auto;
|
||||
position: absolute;
|
||||
transform-origin: center center;
|
||||
}
|
||||
|
||||
.fa-layers-text {
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
transform-origin: center center;
|
||||
}
|
||||
|
||||
.fa-layers-counter {
|
||||
background-color: var(--fa-counter-background-color, #ff253a);
|
||||
border-radius: var(--fa-counter-border-radius, 1em);
|
||||
box-sizing: border-box;
|
||||
color: var(--fa-inverse, #fff);
|
||||
line-height: var(--fa-counter-line-height, 1);
|
||||
max-width: var(--fa-counter-max-width, 5em);
|
||||
min-width: var(--fa-counter-min-width, 1.5em);
|
||||
overflow: hidden;
|
||||
padding: var(--fa-counter-padding, 0.25em 0.5em);
|
||||
right: var(--fa-right, 0);
|
||||
text-overflow: ellipsis;
|
||||
top: var(--fa-top, 0);
|
||||
transform: scale(var(--fa-counter-scale, 0.25));
|
||||
transform-origin: top right;
|
||||
}
|
||||
|
||||
.fa-layers-bottom-right {
|
||||
bottom: var(--fa-bottom, 0);
|
||||
right: var(--fa-right, 0);
|
||||
top: auto;
|
||||
transform: scale(var(--fa-layers-scale, 0.25));
|
||||
transform-origin: bottom right;
|
||||
}
|
||||
|
||||
.fa-layers-bottom-left {
|
||||
bottom: var(--fa-bottom, 0);
|
||||
left: var(--fa-left, 0);
|
||||
right: auto;
|
||||
top: auto;
|
||||
transform: scale(var(--fa-layers-scale, 0.25));
|
||||
transform-origin: bottom left;
|
||||
}
|
||||
|
||||
.fa-layers-top-right {
|
||||
top: var(--fa-top, 0);
|
||||
right: var(--fa-right, 0);
|
||||
transform: scale(var(--fa-layers-scale, 0.25));
|
||||
transform-origin: top right;
|
||||
}
|
||||
|
||||
.fa-layers-top-left {
|
||||
left: var(--fa-left, 0);
|
||||
right: auto;
|
||||
top: var(--fa-top, 0);
|
||||
transform: scale(var(--fa-layers-scale, 0.25));
|
||||
transform-origin: top left;
|
||||
}
|
||||
|
||||
.svg-inline--fa .fa-primary {
|
||||
fill: var(--fa-primary-color, currentColor);
|
||||
opacity: var(--fa-primary-opacity, 1);
|
||||
}
|
||||
|
||||
.svg-inline--fa .fa-secondary {
|
||||
fill: var(--fa-secondary-color, currentColor);
|
||||
opacity: var(--fa-secondary-opacity, 0.4);
|
||||
}
|
||||
|
||||
.svg-inline--fa.fa-swap-opacity .fa-primary {
|
||||
opacity: var(--fa-secondary-opacity, 0.4);
|
||||
}
|
||||
|
||||
.svg-inline--fa.fa-swap-opacity .fa-secondary {
|
||||
opacity: var(--fa-primary-opacity, 1);
|
||||
}
|
||||
|
||||
.svg-inline--fa mask .fa-primary,
|
||||
.svg-inline--fa mask .fa-secondary {
|
||||
fill: black;
|
||||
}
|
||||
|
||||
.svg-inline--fa.fa-inverse {
|
||||
fill: var(--fa-inverse, #fff);
|
||||
}
|
||||
|
||||
.fa-stack-1x,
|
||||
.fa-stack-2x {
|
||||
inset: 0;
|
||||
margin: auto;
|
||||
position: absolute;
|
||||
z-index: var(--fa-stack-z-index, auto);
|
||||
}
|
||||
6
src/fa/css/svg.min.css
vendored
Normal file
6
src/fa/css/svg.min.css
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
/*!
|
||||
* Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com
|
||||
* License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
|
||||
* Copyright 2025 Fonticons, Inc.
|
||||
*/
|
||||
:host,:root{--fa-font-solid:normal 900 1em/1 "Font Awesome 7 Free";--fa-font-regular:normal 400 1em/1 "Font Awesome 7 Free";--fa-font-light:normal 300 1em/1 "Font Awesome 7 Pro";--fa-font-thin:normal 100 1em/1 "Font Awesome 7 Pro";--fa-font-duotone:normal 900 1em/1 "Font Awesome 7 Duotone";--fa-font-duotone-regular:normal 400 1em/1 "Font Awesome 7 Duotone";--fa-font-duotone-light:normal 300 1em/1 "Font Awesome 7 Duotone";--fa-font-duotone-thin:normal 100 1em/1 "Font Awesome 7 Duotone";--fa-font-brands:normal 400 1em/1 "Font Awesome 7 Brands";--fa-font-sharp-solid:normal 900 1em/1 "Font Awesome 7 Sharp";--fa-font-sharp-regular:normal 400 1em/1 "Font Awesome 7 Sharp";--fa-font-sharp-light:normal 300 1em/1 "Font Awesome 7 Sharp";--fa-font-sharp-thin:normal 100 1em/1 "Font Awesome 7 Sharp";--fa-font-sharp-duotone-solid:normal 900 1em/1 "Font Awesome 7 Sharp Duotone";--fa-font-sharp-duotone-regular:normal 400 1em/1 "Font Awesome 7 Sharp Duotone";--fa-font-sharp-duotone-light:normal 300 1em/1 "Font Awesome 7 Sharp Duotone";--fa-font-sharp-duotone-thin:normal 100 1em/1 "Font Awesome 7 Sharp Duotone";--fa-font-slab-regular:normal 400 1em/1 "Font Awesome 7 Slab";--fa-font-slab-press-regular:normal 400 1em/1 "Font Awesome 7 Slab Press";--fa-font-whiteboard-semibold:normal 600 1em/1 "Font Awesome 7 Whiteboard";--fa-font-thumbprint-light:normal 300 1em/1 "Font Awesome 7 Thumbprint";--fa-font-notdog-solid:normal 900 1em/1 "Font Awesome 7 Notdog";--fa-font-notdog-duo-solid:normal 900 1em/1 "Font Awesome 7 Notdog Duo";--fa-font-etch-solid:normal 900 1em/1 "Font Awesome 7 Etch";--fa-font-jelly-regular:normal 400 1em/1 "Font Awesome 7 Jelly";--fa-font-jelly-fill-regular:normal 400 1em/1 "Font Awesome 7 Jelly Fill";--fa-font-jelly-duo-regular:normal 400 1em/1 "Font Awesome 7 Jelly Duo";--fa-font-chisel-regular:normal 400 1em/1 "Font Awesome 7 Chisel";--fa-font-utility-semibold:normal 600 1em/1 "Font Awesome 7 Utility";--fa-font-utility-duo-semibold:normal 600 1em/1 "Font Awesome 7 Utility Duo";--fa-font-utility-fill-semibold:normal 600 1em/1 "Font Awesome 7 Utility Fill"}.svg-inline--fa{box-sizing:content-box;display:var(--fa-display,inline-block);height:1em;overflow:visible;vertical-align:-.125em;width:var(--fa-width,1.25em)}.svg-inline--fa.fa-2xs{vertical-align:.1em}.svg-inline--fa.fa-xs{vertical-align:0}.svg-inline--fa.fa-sm{vertical-align:-.0714285714em}.svg-inline--fa.fa-lg{vertical-align:-.2em}.svg-inline--fa.fa-xl{vertical-align:-.25em}.svg-inline--fa.fa-2xl{vertical-align:-.3125em}.svg-inline--fa.fa-li{width:var(--fa-li-width,2em);inset-inline-start:calc(var(--fa-li-width, 2em)*-1);inset-block-start:.25em}.fa-layers-counter,.fa-layers-text{display:inline-block;position:absolute;text-align:center}.fa-layers{display:inline-block;height:1em;position:relative;text-align:center;vertical-align:-.125em;width:var(--fa-width,1.25em)}.fa-layers .svg-inline--fa{inset:0;margin:auto;position:absolute;transform-origin:center center}.fa-layers-text{left:50%;top:50%;transform:translate(-50%,-50%);transform-origin:center center}.fa-layers-counter{background-color:var(--fa-counter-background-color,#ff253a);border-radius:var(--fa-counter-border-radius,1em);box-sizing:border-box;color:var(--fa-inverse,#fff);line-height:var(--fa-counter-line-height,1);max-width:var(--fa-counter-max-width,5em);min-width:var(--fa-counter-min-width,1.5em);overflow:hidden;padding:var(--fa-counter-padding,.25em .5em);right:var(--fa-right,0);text-overflow:ellipsis;top:var(--fa-top,0);transform:scale(var(--fa-counter-scale,.25));transform-origin:top right}.fa-layers-bottom-right{bottom:var(--fa-bottom,0);right:var(--fa-right,0);top:auto;transform:scale(var(--fa-layers-scale,.25));transform-origin:bottom right}.fa-layers-bottom-left{bottom:var(--fa-bottom,0);left:var(--fa-left,0);right:auto;top:auto;transform:scale(var(--fa-layers-scale,.25));transform-origin:bottom left}.fa-layers-top-right{top:var(--fa-top,0);right:var(--fa-right,0);transform:scale(var(--fa-layers-scale,.25));transform-origin:top right}.fa-layers-top-left{left:var(--fa-left,0);right:auto;top:var(--fa-top,0);transform:scale(var(--fa-layers-scale,.25));transform-origin:top left}.svg-inline--fa .fa-primary{fill:var(--fa-primary-color,currentColor);opacity:var(--fa-primary-opacity,1)}.svg-inline--fa .fa-secondary{fill:var(--fa-secondary-color,currentColor)}.svg-inline--fa .fa-secondary,.svg-inline--fa.fa-swap-opacity .fa-primary{opacity:var(--fa-secondary-opacity,.4)}.svg-inline--fa.fa-swap-opacity .fa-secondary{opacity:var(--fa-primary-opacity,1)}.svg-inline--fa mask .fa-primary,.svg-inline--fa mask .fa-secondary{fill:#000}.svg-inline--fa.fa-inverse{fill:var(--fa-inverse,#fff)}.fa-stack-1x,.fa-stack-2x{inset:0;margin:auto;position:absolute;z-index:var(--fa-stack-z-index,auto)}
|
||||
27
src/fa/css/v4-font-face.css
Normal file
27
src/fa/css/v4-font-face.css
Normal file
@@ -0,0 +1,27 @@
|
||||
/*!
|
||||
* Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com
|
||||
* License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
|
||||
* Copyright 2025 Fonticons, Inc.
|
||||
*/
|
||||
@font-face {
|
||||
font-family: "FontAwesome";
|
||||
font-display: block;
|
||||
src: url("../webfonts/fa-solid-900.woff2") format("woff2");
|
||||
}
|
||||
@font-face {
|
||||
font-family: "FontAwesome";
|
||||
font-display: block;
|
||||
src: url("../webfonts/fa-brands-400.woff2") format("woff2");
|
||||
}
|
||||
@font-face {
|
||||
font-family: "FontAwesome";
|
||||
font-display: block;
|
||||
src: url("../webfonts/fa-regular-400.woff2") format("woff2");
|
||||
unicode-range: U+F003, U+F006, U+F014, U+F016-F017, U+F01A-F01B, U+F01D, U+F022, U+F03E, U+F044, U+F046, U+F05C-F05D, U+F06E, U+F070, U+F087-F088, U+F08A, U+F094, U+F096-F097, U+F09D, U+F0A0, U+F0A2, U+F0A4-F0A7, U+F0C5, U+F0C7, U+F0E5-F0E6, U+F0EB, U+F0F6-F0F8, U+F10C, U+F114-F115, U+F118-F11A, U+F11C-F11D, U+F133, U+F147, U+F14E, U+F150-F152, U+F185-F186, U+F18E, U+F190-F192, U+F196, U+F1C1-F1C9, U+F1D9, U+F1DB, U+F1E3, U+F1EA, U+F1F7, U+F1F9, U+F20A, U+F247-F248, U+F24A, U+F24D, U+F255-F25B, U+F25D, U+F271-F274, U+F278, U+F27B, U+F28C, U+F28E, U+F29C, U+F2B5, U+F2B7, U+F2BA, U+F2BC, U+F2BE, U+F2C0-F2C1, U+F2C3, U+F2D0, U+F2D2, U+F2D4, U+F2DC;
|
||||
}
|
||||
@font-face {
|
||||
font-family: "FontAwesome";
|
||||
font-display: block;
|
||||
src: url("../webfonts/fa-v4compatibility.woff2") format("woff2");
|
||||
unicode-range: U+F041, U+F047, U+F065-F066, U+F07D-F07E, U+F080, U+F08B, U+F08E, U+F090, U+F09A, U+F0AC, U+F0AE, U+F0B2, U+F0D0, U+F0D6, U+F0E4, U+F0EC, U+F10A-F10B, U+F123, U+F13E, U+F148-F149, U+F14C, U+F156, U+F15E, U+F160-F161, U+F163, U+F175-F178, U+F195, U+F1F8, U+F219, U+F27A;
|
||||
}
|
||||
6
src/fa/css/v4-font-face.min.css
vendored
Normal file
6
src/fa/css/v4-font-face.min.css
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
/*!
|
||||
* Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com
|
||||
* License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
|
||||
* Copyright 2025 Fonticons, Inc.
|
||||
*/
|
||||
@font-face{font-family:"FontAwesome";font-display:block;src:url(../webfonts/fa-solid-900.woff2) format("woff2")}@font-face{font-family:"FontAwesome";font-display:block;src:url(../webfonts/fa-brands-400.woff2) format("woff2")}@font-face{font-family:"FontAwesome";font-display:block;src:url(../webfonts/fa-regular-400.woff2) format("woff2");unicode-range:u+f003,u+f006,u+f014,u+f016-f017,u+f01a-f01b,u+f01d,u+f022,u+f03e,u+f044,u+f046,u+f05c-f05d,u+f06e,u+f070,u+f087-f088,u+f08a,u+f094,u+f096-f097,u+f09d,u+f0a0,u+f0a2,u+f0a4-f0a7,u+f0c5,u+f0c7,u+f0e5-f0e6,u+f0eb,u+f0f6-f0f8,u+f10c,u+f114-f115,u+f118-f11a,u+f11c-f11d,u+f133,u+f147,u+f14e,u+f150-f152,u+f185-f186,u+f18e,u+f190-f192,u+f196,u+f1c1-f1c9,u+f1d9,u+f1db,u+f1e3,u+f1ea,u+f1f7,u+f1f9,u+f20a,u+f247-f248,u+f24a,u+f24d,u+f255-f25b,u+f25d,u+f271-f274,u+f278,u+f27b,u+f28c,u+f28e,u+f29c,u+f2b5,u+f2b7,u+f2ba,u+f2bc,u+f2be,u+f2c0-f2c1,u+f2c3,u+f2d0,u+f2d2,u+f2d4,u+f2dc}@font-face{font-family:"FontAwesome";font-display:block;src:url(../webfonts/fa-v4compatibility.woff2) format("woff2");unicode-range:u+f041,u+f047,u+f065-f066,u+f07d-f07e,u+f080,u+f08b,u+f08e,u+f090,u+f09a,u+f0ac,u+f0ae,u+f0b2,u+f0d0,u+f0d6,u+f0e4,u+f0ec,u+f10a-f10b,u+f123,u+f13e,u+f148-f149,u+f14c,u+f156,u+f15e,u+f160-f161,u+f163,u+f175-f178,u+f195,u+f1f8,u+f219,u+f27a}
|
||||
2818
src/fa/css/v4-shims.css
Normal file
2818
src/fa/css/v4-shims.css
Normal file
File diff suppressed because it is too large
Load Diff
6
src/fa/css/v4-shims.min.css
vendored
Normal file
6
src/fa/css/v4-shims.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
23
src/fa/css/v5-font-face.css
Normal file
23
src/fa/css/v5-font-face.css
Normal file
@@ -0,0 +1,23 @@
|
||||
/*!
|
||||
* Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com
|
||||
* License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
|
||||
* Copyright 2025 Fonticons, Inc.
|
||||
*/
|
||||
@font-face {
|
||||
font-family: "Font Awesome 5 Brands";
|
||||
font-display: block;
|
||||
font-weight: 400;
|
||||
src: url("../webfonts/fa-brands-400.woff2") format("woff2");
|
||||
}
|
||||
@font-face {
|
||||
font-family: "Font Awesome 5 Free";
|
||||
font-display: block;
|
||||
font-weight: 900;
|
||||
src: url("../webfonts/fa-solid-900.woff2") format("woff2");
|
||||
}
|
||||
@font-face {
|
||||
font-family: "Font Awesome 5 Free";
|
||||
font-display: block;
|
||||
font-weight: 400;
|
||||
src: url("../webfonts/fa-regular-400.woff2") format("woff2");
|
||||
}
|
||||
6
src/fa/css/v5-font-face.min.css
vendored
Normal file
6
src/fa/css/v5-font-face.min.css
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
/*!
|
||||
* Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com
|
||||
* License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
|
||||
* Copyright 2025 Fonticons, Inc.
|
||||
*/
|
||||
@font-face{font-family:"Font Awesome 5 Brands";font-display:block;font-weight:400;src:url(../webfonts/fa-brands-400.woff2) format("woff2")}@font-face{font-family:"Font Awesome 5 Free";font-display:block;font-weight:900;src:url(../webfonts/fa-solid-900.woff2) format("woff2")}@font-face{font-family:"Font Awesome 5 Free";font-display:block;font-weight:400;src:url(../webfonts/fa-regular-400.woff2) format("woff2")}
|
||||
BIN
src/fa/webfonts/fa-brands-400.woff2
Normal file
BIN
src/fa/webfonts/fa-brands-400.woff2
Normal file
Binary file not shown.
BIN
src/fa/webfonts/fa-regular-400.woff2
Normal file
BIN
src/fa/webfonts/fa-regular-400.woff2
Normal file
Binary file not shown.
BIN
src/fa/webfonts/fa-solid-900.woff2
Normal file
BIN
src/fa/webfonts/fa-solid-900.woff2
Normal file
Binary file not shown.
BIN
src/fa/webfonts/fa-v4compatibility.woff2
Normal file
BIN
src/fa/webfonts/fa-v4compatibility.woff2
Normal file
Binary file not shown.
18
vendor/autoload.php
vendored
18
vendor/autoload.php
vendored
@@ -2,6 +2,24 @@
|
||||
|
||||
// autoload.php @generated by Composer
|
||||
|
||||
if (PHP_VERSION_ID < 50600) {
|
||||
if (!headers_sent()) {
|
||||
header('HTTP/1.1 500 Internal Server Error');
|
||||
}
|
||||
$err = 'Composer 2.3.0 dropped support for autoloading on PHP <5.6 and you are running '.PHP_VERSION.', please upgrade PHP or use Composer 2.2 LTS via "composer self-update --2.2". Aborting.'.PHP_EOL;
|
||||
if (!ini_get('display_errors')) {
|
||||
if (PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') {
|
||||
fwrite(STDERR, $err);
|
||||
} elseif (!headers_sent()) {
|
||||
echo $err;
|
||||
}
|
||||
}
|
||||
trigger_error(
|
||||
$err,
|
||||
E_USER_ERROR
|
||||
);
|
||||
}
|
||||
|
||||
require_once __DIR__ . '/composer/autoload_real.php';
|
||||
|
||||
return ComposerAutoloaderInitab00f29709bdbfe3a1abd7d2d66d569c::getLoader();
|
||||
|
||||
851
vendor/chillerlan/php-qrcode/.idea/codeStyles/Project.xml
generated
vendored
851
vendor/chillerlan/php-qrcode/.idea/codeStyles/Project.xml
generated
vendored
@@ -1,851 +0,0 @@
|
||||
<component name="ProjectCodeStyleConfiguration">
|
||||
<code_scheme name="Project" version="173">
|
||||
<option name="AUTODETECT_INDENTS" value="false" />
|
||||
<option name="OTHER_INDENT_OPTIONS">
|
||||
<value>
|
||||
<option name="USE_TAB_CHARACTER" value="true" />
|
||||
<option name="SMART_TABS" value="true" />
|
||||
</value>
|
||||
</option>
|
||||
<option name="RIGHT_MARGIN" value="130" />
|
||||
<CssCodeStyleSettings>
|
||||
<option name="HEX_COLOR_LOWER_CASE" value="true" />
|
||||
<option name="HEX_COLOR_SHORT_FORMAT" value="true" />
|
||||
<option name="KEEP_SINGLE_LINE_BLOCKS" value="true" />
|
||||
<option name="SPACE_BEFORE_OPENING_BRACE" value="false" />
|
||||
</CssCodeStyleSettings>
|
||||
<DB2CodeStyleSettings version="6">
|
||||
<option name="USE_GENERAL_STYLE" value="false" />
|
||||
<option name="TYPE_CASE" value="3" />
|
||||
<option name="CUSTOM_TYPE_CASE" value="3" />
|
||||
<option name="ALIAS_CASE" value="4" />
|
||||
<option name="BUILT_IN_CASE" value="0" />
|
||||
<option name="QUERY_TRUE_INDENT" value="false" />
|
||||
<option name="QUERY_ALIGN_ELEMENTS" value="false" />
|
||||
<option name="QUERY_ALIGN_LINE_COMMENTS" value="false" />
|
||||
<option name="INSERT_EL_COMMA" value="2" />
|
||||
<option name="SET_EL_WRAP" value="0" />
|
||||
<option name="SET_EL_COMMA" value="0" />
|
||||
<option name="WITH_EL_WRAP" value="0" />
|
||||
<option name="WITH_EL_COMMA" value="0" />
|
||||
<option name="SELECT_EL_WRAP" value="3" />
|
||||
<option name="SELECT_EL_COMMA" value="2" />
|
||||
<option name="FROM_EL_WRAP" value="2" />
|
||||
<option name="FROM_EL_COMMA" value="2" />
|
||||
<option name="FROM_PLACE_ON" value="10" />
|
||||
<option name="WHERE_EL_WRAP" value="3" />
|
||||
<option name="WHERE_EL_BOUND" value="2" />
|
||||
<option name="ORDER_EL_COMMA" value="2" />
|
||||
<option name="TABLE_OPENING" value="1" />
|
||||
<option name="TABLE_CONTENT" value="2" />
|
||||
<option name="TABLE_CLOSING" value="3" />
|
||||
<option name="TABLE_ALTER_INSTRUCTION_ALIGN" value="false" />
|
||||
<option name="POST_OPT_WRAP_1" value="true" />
|
||||
<option name="POST_OPT_ALIGN" value="false" />
|
||||
<option name="ROUTINE_ARG_COMMA" value="2" />
|
||||
<option name="ROUTINE_ARG_ALIGN_TYPES" value="true" />
|
||||
<option name="IMP_DECLARE_EL_WRAP" value="1" />
|
||||
<option name="IMP_IF_THEN_WRAP_THEN" value="true" />
|
||||
<option name="CORTEGE_SPACE_BEFORE_L_PAREN" value="false" />
|
||||
<option name="EXPR_CASE_WHEN_WRAP" value="false" />
|
||||
<option name="EXPR_CASE_THEN_WRAP" value="true" />
|
||||
<option name="PRIMARY_KEY_NAME_TEMPLATE" value="{table}_{columns}_pk" />
|
||||
</DB2CodeStyleSettings>
|
||||
<DerbyCodeStyleSettings version="6">
|
||||
<option name="USE_GENERAL_STYLE" value="false" />
|
||||
<option name="TYPE_CASE" value="3" />
|
||||
<option name="CUSTOM_TYPE_CASE" value="3" />
|
||||
<option name="ALIAS_CASE" value="4" />
|
||||
<option name="BUILT_IN_CASE" value="0" />
|
||||
<option name="QUERY_TRUE_INDENT" value="false" />
|
||||
<option name="QUERY_ALIGN_ELEMENTS" value="false" />
|
||||
<option name="QUERY_ALIGN_LINE_COMMENTS" value="false" />
|
||||
<option name="INSERT_EL_COMMA" value="2" />
|
||||
<option name="SET_EL_WRAP" value="0" />
|
||||
<option name="SET_EL_COMMA" value="0" />
|
||||
<option name="WITH_EL_WRAP" value="0" />
|
||||
<option name="WITH_EL_COMMA" value="0" />
|
||||
<option name="SELECT_EL_WRAP" value="3" />
|
||||
<option name="SELECT_EL_COMMA" value="2" />
|
||||
<option name="FROM_EL_WRAP" value="2" />
|
||||
<option name="FROM_EL_COMMA" value="2" />
|
||||
<option name="FROM_PLACE_ON" value="10" />
|
||||
<option name="WHERE_EL_WRAP" value="3" />
|
||||
<option name="WHERE_EL_BOUND" value="2" />
|
||||
<option name="ORDER_EL_COMMA" value="2" />
|
||||
<option name="TABLE_OPENING" value="1" />
|
||||
<option name="TABLE_CONTENT" value="2" />
|
||||
<option name="TABLE_CLOSING" value="3" />
|
||||
<option name="TABLE_ALTER_INSTRUCTION_ALIGN" value="false" />
|
||||
<option name="POST_OPT_WRAP_1" value="true" />
|
||||
<option name="POST_OPT_ALIGN" value="false" />
|
||||
<option name="ROUTINE_ARG_COMMA" value="2" />
|
||||
<option name="ROUTINE_ARG_ALIGN_TYPES" value="true" />
|
||||
<option name="IMP_DECLARE_EL_WRAP" value="1" />
|
||||
<option name="IMP_IF_THEN_WRAP_THEN" value="true" />
|
||||
<option name="CORTEGE_SPACE_BEFORE_L_PAREN" value="false" />
|
||||
<option name="EXPR_CASE_WHEN_WRAP" value="false" />
|
||||
<option name="EXPR_CASE_THEN_WRAP" value="true" />
|
||||
<option name="PRIMARY_KEY_NAME_TEMPLATE" value="{table}_{columns}_pk" />
|
||||
</DerbyCodeStyleSettings>
|
||||
<H2CodeStyleSettings version="6">
|
||||
<option name="USE_GENERAL_STYLE" value="false" />
|
||||
<option name="TYPE_CASE" value="3" />
|
||||
<option name="CUSTOM_TYPE_CASE" value="3" />
|
||||
<option name="ALIAS_CASE" value="4" />
|
||||
<option name="BUILT_IN_CASE" value="0" />
|
||||
<option name="QUERY_TRUE_INDENT" value="false" />
|
||||
<option name="QUERY_ALIGN_ELEMENTS" value="false" />
|
||||
<option name="QUERY_ALIGN_LINE_COMMENTS" value="false" />
|
||||
<option name="INSERT_EL_COMMA" value="2" />
|
||||
<option name="SET_EL_WRAP" value="0" />
|
||||
<option name="SET_EL_COMMA" value="0" />
|
||||
<option name="WITH_EL_WRAP" value="0" />
|
||||
<option name="WITH_EL_COMMA" value="0" />
|
||||
<option name="SELECT_EL_WRAP" value="3" />
|
||||
<option name="SELECT_EL_COMMA" value="2" />
|
||||
<option name="FROM_EL_WRAP" value="2" />
|
||||
<option name="FROM_EL_COMMA" value="2" />
|
||||
<option name="FROM_PLACE_ON" value="10" />
|
||||
<option name="WHERE_EL_WRAP" value="3" />
|
||||
<option name="WHERE_EL_BOUND" value="2" />
|
||||
<option name="ORDER_EL_COMMA" value="2" />
|
||||
<option name="TABLE_OPENING" value="1" />
|
||||
<option name="TABLE_CONTENT" value="2" />
|
||||
<option name="TABLE_CLOSING" value="3" />
|
||||
<option name="TABLE_ALTER_INSTRUCTION_ALIGN" value="false" />
|
||||
<option name="POST_OPT_WRAP_1" value="true" />
|
||||
<option name="POST_OPT_ALIGN" value="false" />
|
||||
<option name="ROUTINE_ARG_COMMA" value="2" />
|
||||
<option name="ROUTINE_ARG_ALIGN_TYPES" value="true" />
|
||||
<option name="IMP_DECLARE_EL_WRAP" value="1" />
|
||||
<option name="IMP_IF_THEN_WRAP_THEN" value="true" />
|
||||
<option name="CORTEGE_SPACE_BEFORE_L_PAREN" value="false" />
|
||||
<option name="EXPR_CASE_WHEN_WRAP" value="false" />
|
||||
<option name="EXPR_CASE_THEN_WRAP" value="true" />
|
||||
<option name="PRIMARY_KEY_NAME_TEMPLATE" value="{table}_{columns}_pk" />
|
||||
</H2CodeStyleSettings>
|
||||
<HSQLCodeStyleSettings version="6">
|
||||
<option name="USE_GENERAL_STYLE" value="false" />
|
||||
<option name="TYPE_CASE" value="3" />
|
||||
<option name="CUSTOM_TYPE_CASE" value="3" />
|
||||
<option name="ALIAS_CASE" value="4" />
|
||||
<option name="BUILT_IN_CASE" value="0" />
|
||||
<option name="QUERY_TRUE_INDENT" value="false" />
|
||||
<option name="QUERY_ALIGN_ELEMENTS" value="false" />
|
||||
<option name="QUERY_ALIGN_LINE_COMMENTS" value="false" />
|
||||
<option name="INSERT_EL_COMMA" value="2" />
|
||||
<option name="SET_EL_WRAP" value="0" />
|
||||
<option name="SET_EL_COMMA" value="0" />
|
||||
<option name="WITH_EL_WRAP" value="0" />
|
||||
<option name="WITH_EL_COMMA" value="0" />
|
||||
<option name="SELECT_EL_WRAP" value="3" />
|
||||
<option name="SELECT_EL_COMMA" value="2" />
|
||||
<option name="FROM_EL_WRAP" value="2" />
|
||||
<option name="FROM_EL_COMMA" value="2" />
|
||||
<option name="FROM_PLACE_ON" value="10" />
|
||||
<option name="WHERE_EL_WRAP" value="3" />
|
||||
<option name="WHERE_EL_BOUND" value="2" />
|
||||
<option name="ORDER_EL_COMMA" value="2" />
|
||||
<option name="TABLE_OPENING" value="1" />
|
||||
<option name="TABLE_CONTENT" value="2" />
|
||||
<option name="TABLE_CLOSING" value="3" />
|
||||
<option name="TABLE_ALTER_INSTRUCTION_ALIGN" value="false" />
|
||||
<option name="POST_OPT_WRAP_1" value="true" />
|
||||
<option name="POST_OPT_ALIGN" value="false" />
|
||||
<option name="ROUTINE_ARG_COMMA" value="2" />
|
||||
<option name="ROUTINE_ARG_ALIGN_TYPES" value="true" />
|
||||
<option name="IMP_DECLARE_EL_WRAP" value="1" />
|
||||
<option name="IMP_IF_THEN_WRAP_THEN" value="true" />
|
||||
<option name="CORTEGE_SPACE_BEFORE_L_PAREN" value="false" />
|
||||
<option name="EXPR_CASE_WHEN_WRAP" value="false" />
|
||||
<option name="EXPR_CASE_THEN_WRAP" value="true" />
|
||||
<option name="PRIMARY_KEY_NAME_TEMPLATE" value="{table}_{columns}_pk" />
|
||||
</HSQLCodeStyleSettings>
|
||||
<HTMLCodeStyleSettings>
|
||||
<option name="HTML_ATTRIBUTE_WRAP" value="0" />
|
||||
<option name="HTML_TEXT_WRAP" value="0" />
|
||||
<option name="HTML_ENFORCE_QUOTES" value="true" />
|
||||
</HTMLCodeStyleSettings>
|
||||
<JSCodeStyleSettings version="0">
|
||||
<option name="FORCE_SEMICOLON_STYLE" value="true" />
|
||||
<option name="ALIGN_OBJECT_PROPERTIES" value="2" />
|
||||
<option name="ALIGN_VAR_STATEMENTS" value="1" />
|
||||
<option name="SPACE_BEFORE_FUNCTION_LEFT_PARENTH" value="false" />
|
||||
<option name="SPACE_BEFORE_CLASS_LBRACE" value="false" />
|
||||
<option name="USE_DOUBLE_QUOTES" value="false" />
|
||||
<option name="FORCE_QUOTE_STYlE" value="true" />
|
||||
<option name="ENFORCE_TRAILING_COMMA" value="WhenMultiline" />
|
||||
<option name="IMPORTS_WRAP" value="1" />
|
||||
<option name="SPACE_BEFORE_ASYNC_ARROW_LPAREN" value="false" />
|
||||
</JSCodeStyleSettings>
|
||||
<MSSQLCodeStyleSettings version="6">
|
||||
<option name="USE_GENERAL_STYLE" value="false" />
|
||||
<option name="TYPE_CASE" value="3" />
|
||||
<option name="CUSTOM_TYPE_CASE" value="3" />
|
||||
<option name="ALIAS_CASE" value="4" />
|
||||
<option name="BUILT_IN_CASE" value="0" />
|
||||
<option name="QUERY_TRUE_INDENT" value="false" />
|
||||
<option name="QUERY_ALIGN_ELEMENTS" value="false" />
|
||||
<option name="QUERY_ALIGN_LINE_COMMENTS" value="false" />
|
||||
<option name="INSERT_EL_COMMA" value="2" />
|
||||
<option name="SET_EL_WRAP" value="0" />
|
||||
<option name="SET_EL_COMMA" value="0" />
|
||||
<option name="WITH_EL_WRAP" value="0" />
|
||||
<option name="WITH_EL_COMMA" value="0" />
|
||||
<option name="SELECT_EL_WRAP" value="3" />
|
||||
<option name="SELECT_EL_COMMA" value="2" />
|
||||
<option name="FROM_EL_WRAP" value="2" />
|
||||
<option name="FROM_EL_COMMA" value="2" />
|
||||
<option name="FROM_PLACE_ON" value="10" />
|
||||
<option name="WHERE_EL_WRAP" value="3" />
|
||||
<option name="WHERE_EL_BOUND" value="2" />
|
||||
<option name="ORDER_EL_COMMA" value="2" />
|
||||
<option name="TABLE_OPENING" value="1" />
|
||||
<option name="TABLE_CONTENT" value="2" />
|
||||
<option name="TABLE_CLOSING" value="3" />
|
||||
<option name="TABLE_ALTER_INSTRUCTION_ALIGN" value="false" />
|
||||
<option name="POST_OPT_WRAP_1" value="true" />
|
||||
<option name="POST_OPT_ALIGN" value="false" />
|
||||
<option name="ROUTINE_ARG_COMMA" value="2" />
|
||||
<option name="ROUTINE_ARG_ALIGN_TYPES" value="true" />
|
||||
<option name="IMP_DECLARE_EL_WRAP" value="1" />
|
||||
<option name="IMP_IF_THEN_WRAP_THEN" value="true" />
|
||||
<option name="CORTEGE_SPACE_BEFORE_L_PAREN" value="false" />
|
||||
<option name="EXPR_CASE_WHEN_WRAP" value="false" />
|
||||
<option name="EXPR_CASE_THEN_WRAP" value="true" />
|
||||
<option name="PRIMARY_KEY_NAME_TEMPLATE" value="{table}_{columns}_pk" />
|
||||
</MSSQLCodeStyleSettings>
|
||||
<MySQLCodeStyleSettings version="6">
|
||||
<option name="USE_GENERAL_STYLE" value="false" />
|
||||
<option name="TYPE_CASE" value="3" />
|
||||
<option name="CUSTOM_TYPE_CASE" value="3" />
|
||||
<option name="ALIAS_CASE" value="4" />
|
||||
<option name="BUILT_IN_CASE" value="0" />
|
||||
<option name="QUERY_TRUE_INDENT" value="false" />
|
||||
<option name="QUERY_ALIGN_ELEMENTS" value="false" />
|
||||
<option name="QUERY_ALIGN_LINE_COMMENTS" value="false" />
|
||||
<option name="INSERT_EL_COMMA" value="2" />
|
||||
<option name="SET_EL_WRAP" value="0" />
|
||||
<option name="SET_EL_COMMA" value="0" />
|
||||
<option name="WITH_EL_WRAP" value="0" />
|
||||
<option name="WITH_EL_COMMA" value="0" />
|
||||
<option name="SELECT_EL_WRAP" value="3" />
|
||||
<option name="SELECT_EL_COMMA" value="2" />
|
||||
<option name="FROM_EL_WRAP" value="2" />
|
||||
<option name="FROM_EL_COMMA" value="2" />
|
||||
<option name="FROM_PLACE_ON" value="10" />
|
||||
<option name="WHERE_EL_WRAP" value="3" />
|
||||
<option name="WHERE_EL_BOUND" value="2" />
|
||||
<option name="ORDER_EL_COMMA" value="2" />
|
||||
<option name="TABLE_OPENING" value="1" />
|
||||
<option name="TABLE_CONTENT" value="2" />
|
||||
<option name="TABLE_CLOSING" value="3" />
|
||||
<option name="TABLE_ALTER_INSTRUCTION_ALIGN" value="false" />
|
||||
<option name="POST_OPT_WRAP_1" value="true" />
|
||||
<option name="POST_OPT_ALIGN" value="false" />
|
||||
<option name="ROUTINE_ARG_COMMA" value="2" />
|
||||
<option name="ROUTINE_ARG_ALIGN_TYPES" value="true" />
|
||||
<option name="IMP_DECLARE_EL_WRAP" value="1" />
|
||||
<option name="IMP_IF_THEN_WRAP_THEN" value="true" />
|
||||
<option name="CORTEGE_SPACE_BEFORE_L_PAREN" value="false" />
|
||||
<option name="EXPR_CASE_WHEN_WRAP" value="false" />
|
||||
<option name="EXPR_CASE_THEN_WRAP" value="true" />
|
||||
<option name="PRIMARY_KEY_NAME_TEMPLATE" value="{table}_{columns}_pk" />
|
||||
</MySQLCodeStyleSettings>
|
||||
<OracleCodeStyleSettings version="6">
|
||||
<option name="USE_GENERAL_STYLE" value="false" />
|
||||
<option name="TYPE_CASE" value="3" />
|
||||
<option name="CUSTOM_TYPE_CASE" value="3" />
|
||||
<option name="ALIAS_CASE" value="4" />
|
||||
<option name="BUILT_IN_CASE" value="0" />
|
||||
<option name="QUERY_TRUE_INDENT" value="false" />
|
||||
<option name="QUERY_ALIGN_ELEMENTS" value="false" />
|
||||
<option name="QUERY_ALIGN_LINE_COMMENTS" value="false" />
|
||||
<option name="INSERT_EL_COMMA" value="2" />
|
||||
<option name="SET_EL_WRAP" value="0" />
|
||||
<option name="SET_EL_COMMA" value="0" />
|
||||
<option name="WITH_EL_WRAP" value="0" />
|
||||
<option name="WITH_EL_COMMA" value="0" />
|
||||
<option name="SELECT_EL_WRAP" value="3" />
|
||||
<option name="SELECT_EL_COMMA" value="2" />
|
||||
<option name="FROM_EL_WRAP" value="2" />
|
||||
<option name="FROM_EL_COMMA" value="2" />
|
||||
<option name="FROM_PLACE_ON" value="10" />
|
||||
<option name="WHERE_EL_WRAP" value="3" />
|
||||
<option name="WHERE_EL_BOUND" value="2" />
|
||||
<option name="ORDER_EL_COMMA" value="2" />
|
||||
<option name="TABLE_OPENING" value="1" />
|
||||
<option name="TABLE_CONTENT" value="2" />
|
||||
<option name="TABLE_CLOSING" value="3" />
|
||||
<option name="TABLE_ALTER_INSTRUCTION_ALIGN" value="false" />
|
||||
<option name="POST_OPT_WRAP_1" value="true" />
|
||||
<option name="POST_OPT_ALIGN" value="false" />
|
||||
<option name="ROUTINE_ARG_COMMA" value="2" />
|
||||
<option name="ROUTINE_ARG_ALIGN_TYPES" value="true" />
|
||||
<option name="IMP_DECLARE_EL_WRAP" value="1" />
|
||||
<option name="IMP_IF_THEN_WRAP_THEN" value="true" />
|
||||
<option name="CORTEGE_SPACE_BEFORE_L_PAREN" value="false" />
|
||||
<option name="EXPR_CASE_WHEN_WRAP" value="false" />
|
||||
<option name="EXPR_CASE_THEN_WRAP" value="true" />
|
||||
<option name="PRIMARY_KEY_NAME_TEMPLATE" value="{table}_{columns}_pk" />
|
||||
</OracleCodeStyleSettings>
|
||||
<PHPCodeStyleSettings>
|
||||
<option name="ALIGN_KEY_VALUE_PAIRS" value="true" />
|
||||
<option name="ALIGN_PHPDOC_PARAM_NAMES" value="true" />
|
||||
<option name="ALIGN_PHPDOC_COMMENTS" value="true" />
|
||||
<option name="ALIGN_ASSIGNMENTS" value="true" />
|
||||
<option name="CONCAT_SPACES" value="false" />
|
||||
<option name="COMMA_AFTER_LAST_ARRAY_ELEMENT" value="true" />
|
||||
<option name="PHPDOC_BLANK_LINE_BEFORE_TAGS" value="true" />
|
||||
<option name="PHPDOC_BLANK_LINES_AROUND_PARAMETERS" value="true" />
|
||||
<option name="PHPDOC_WRAP_LONG_LINES" value="true" />
|
||||
<option name="LOWER_CASE_BOOLEAN_CONST" value="true" />
|
||||
<option name="LOWER_CASE_NULL_CONST" value="true" />
|
||||
<option name="ELSE_IF_STYLE" value="COMBINE" />
|
||||
<option name="FIELDS_DEFAULT_VISIBILITY" value="protected" />
|
||||
<option name="BLANK_LINES_BEFORE_RETURN_STATEMENT" value="1" />
|
||||
<option name="KEEP_RPAREN_AND_LBRACE_ON_ONE_LINE" value="true" />
|
||||
<option name="ALIGN_CLASS_CONSTANTS" value="true" />
|
||||
<option name="KEEP_BLANK_LINES_AFTER_LBRACE" value="1" />
|
||||
<option name="SPACE_BEFORE_CLOSURE_LEFT_PARENTHESIS" value="false" />
|
||||
<option name="FORCE_SHORT_DECLARATION_ARRAY_STYLE" value="true" />
|
||||
<option name="SPACE_AROUND_ASSIGNMENT_IN_DECLARE" value="true" />
|
||||
<option name="SPACE_AFTER_COLON_IN_RETURN_TYPE" value="false" />
|
||||
<option name="PHPDOC_USE_FQCN" value="true" />
|
||||
<option name="MULTILINE_CHAINED_CALLS_SEMICOLON_ON_NEW_LINE" value="true" />
|
||||
<option name="PREFER_TEMPLATE_INDENTS" value="true" />
|
||||
</PHPCodeStyleSettings>
|
||||
<PostgresCodeStyleSettings version="6">
|
||||
<option name="USE_GENERAL_STYLE" value="false" />
|
||||
<option name="TYPE_CASE" value="3" />
|
||||
<option name="CUSTOM_TYPE_CASE" value="3" />
|
||||
<option name="ALIAS_CASE" value="4" />
|
||||
<option name="BUILT_IN_CASE" value="0" />
|
||||
<option name="QUERY_TRUE_INDENT" value="false" />
|
||||
<option name="QUERY_ALIGN_ELEMENTS" value="false" />
|
||||
<option name="QUERY_ALIGN_LINE_COMMENTS" value="false" />
|
||||
<option name="INSERT_EL_COMMA" value="2" />
|
||||
<option name="SET_EL_WRAP" value="0" />
|
||||
<option name="SET_EL_COMMA" value="0" />
|
||||
<option name="WITH_EL_WRAP" value="0" />
|
||||
<option name="WITH_EL_COMMA" value="0" />
|
||||
<option name="SELECT_EL_WRAP" value="3" />
|
||||
<option name="SELECT_EL_COMMA" value="2" />
|
||||
<option name="FROM_EL_WRAP" value="2" />
|
||||
<option name="FROM_EL_COMMA" value="2" />
|
||||
<option name="FROM_PLACE_ON" value="10" />
|
||||
<option name="WHERE_EL_WRAP" value="3" />
|
||||
<option name="WHERE_EL_BOUND" value="2" />
|
||||
<option name="ORDER_EL_COMMA" value="2" />
|
||||
<option name="TABLE_OPENING" value="1" />
|
||||
<option name="TABLE_CONTENT" value="2" />
|
||||
<option name="TABLE_CLOSING" value="3" />
|
||||
<option name="TABLE_ALTER_INSTRUCTION_ALIGN" value="false" />
|
||||
<option name="POST_OPT_WRAP_1" value="true" />
|
||||
<option name="POST_OPT_ALIGN" value="false" />
|
||||
<option name="ROUTINE_ARG_COMMA" value="2" />
|
||||
<option name="ROUTINE_ARG_ALIGN_TYPES" value="true" />
|
||||
<option name="IMP_DECLARE_EL_WRAP" value="1" />
|
||||
<option name="IMP_IF_THEN_WRAP_THEN" value="true" />
|
||||
<option name="CORTEGE_SPACE_BEFORE_L_PAREN" value="false" />
|
||||
<option name="EXPR_CASE_WHEN_WRAP" value="false" />
|
||||
<option name="EXPR_CASE_THEN_WRAP" value="true" />
|
||||
<option name="PRIMARY_KEY_NAME_TEMPLATE" value="{table}_{columns}_pk" />
|
||||
</PostgresCodeStyleSettings>
|
||||
<SQLiteCodeStyleSettings version="6">
|
||||
<option name="USE_GENERAL_STYLE" value="false" />
|
||||
<option name="TYPE_CASE" value="3" />
|
||||
<option name="CUSTOM_TYPE_CASE" value="3" />
|
||||
<option name="ALIAS_CASE" value="4" />
|
||||
<option name="BUILT_IN_CASE" value="0" />
|
||||
<option name="QUERY_TRUE_INDENT" value="false" />
|
||||
<option name="QUERY_ALIGN_ELEMENTS" value="false" />
|
||||
<option name="QUERY_ALIGN_LINE_COMMENTS" value="false" />
|
||||
<option name="INSERT_EL_COMMA" value="2" />
|
||||
<option name="SET_EL_WRAP" value="0" />
|
||||
<option name="SET_EL_COMMA" value="0" />
|
||||
<option name="WITH_EL_WRAP" value="0" />
|
||||
<option name="WITH_EL_COMMA" value="0" />
|
||||
<option name="SELECT_EL_WRAP" value="3" />
|
||||
<option name="SELECT_EL_COMMA" value="2" />
|
||||
<option name="FROM_EL_WRAP" value="2" />
|
||||
<option name="FROM_EL_COMMA" value="2" />
|
||||
<option name="FROM_PLACE_ON" value="10" />
|
||||
<option name="WHERE_EL_WRAP" value="3" />
|
||||
<option name="WHERE_EL_BOUND" value="2" />
|
||||
<option name="ORDER_EL_COMMA" value="2" />
|
||||
<option name="TABLE_OPENING" value="1" />
|
||||
<option name="TABLE_CONTENT" value="2" />
|
||||
<option name="TABLE_CLOSING" value="3" />
|
||||
<option name="TABLE_ALTER_INSTRUCTION_ALIGN" value="false" />
|
||||
<option name="POST_OPT_WRAP_1" value="true" />
|
||||
<option name="POST_OPT_ALIGN" value="false" />
|
||||
<option name="ROUTINE_ARG_COMMA" value="2" />
|
||||
<option name="ROUTINE_ARG_ALIGN_TYPES" value="true" />
|
||||
<option name="IMP_DECLARE_EL_WRAP" value="1" />
|
||||
<option name="IMP_IF_THEN_WRAP_THEN" value="true" />
|
||||
<option name="CORTEGE_SPACE_BEFORE_L_PAREN" value="false" />
|
||||
<option name="EXPR_CASE_WHEN_WRAP" value="false" />
|
||||
<option name="EXPR_CASE_THEN_WRAP" value="true" />
|
||||
<option name="PRIMARY_KEY_NAME_TEMPLATE" value="{table}_{columns}_pk" />
|
||||
</SQLiteCodeStyleSettings>
|
||||
<SqlCodeStyleSettings version="6">
|
||||
<option name="TYPE_CASE" value="3" />
|
||||
<option name="CUSTOM_TYPE_CASE" value="3" />
|
||||
<option name="ALIAS_CASE" value="4" />
|
||||
<option name="BUILT_IN_CASE" value="0" />
|
||||
<option name="QUERY_TRUE_INDENT" value="false" />
|
||||
<option name="QUERY_ALIGN_ELEMENTS" value="false" />
|
||||
<option name="QUERY_ALIGN_LINE_COMMENTS" value="false" />
|
||||
<option name="INSERT_EL_COMMA" value="2" />
|
||||
<option name="SET_EL_WRAP" value="0" />
|
||||
<option name="SET_EL_COMMA" value="0" />
|
||||
<option name="WITH_EL_WRAP" value="0" />
|
||||
<option name="WITH_EL_COMMA" value="0" />
|
||||
<option name="SELECT_EL_WRAP" value="3" />
|
||||
<option name="SELECT_EL_COMMA" value="2" />
|
||||
<option name="SELECT_ALIGN_AS" value="false" />
|
||||
<option name="FROM_EL_WRAP" value="2" />
|
||||
<option name="FROM_EL_COMMA" value="2" />
|
||||
<option name="FROM_PLACE_ON" value="10" />
|
||||
<option name="WHERE_EL_WRAP" value="3" />
|
||||
<option name="WHERE_EL_BOUND" value="2" />
|
||||
<option name="ORDER_EL_COMMA" value="2" />
|
||||
<option name="TABLE_OPENING" value="1" />
|
||||
<option name="TABLE_CONTENT" value="2" />
|
||||
<option name="TABLE_CLOSING" value="3" />
|
||||
<option name="TABLE_ALTER_INSTRUCTION_ALIGN" value="false" />
|
||||
<option name="POST_OPT_WRAP_1" value="true" />
|
||||
<option name="POST_OPT_ALIGN" value="false" />
|
||||
<option name="ROUTINE_ARG_COMMA" value="2" />
|
||||
<option name="ROUTINE_ARG_ALIGN_TYPES" value="true" />
|
||||
<option name="IMP_DECLARE_EL_WRAP" value="1" />
|
||||
<option name="IMP_IF_THEN_INDENT_THEN_ELSE" value="true" />
|
||||
<option name="IMP_IF_THEN_INDENT_END" value="true" />
|
||||
<option name="CORTEGE_SPACE_BEFORE_L_PAREN" value="false" />
|
||||
<option name="EXPR_CASE_WHEN_WRAP" value="false" />
|
||||
<option name="PRIMARY_KEY_NAME_TEMPLATE" value="{table}_{columns}_pk" />
|
||||
<option name="ALIGN_AS_IN_SELECT_STATEMENT" value="false" />
|
||||
<option name="NEW_LINE_BEFORE_THEN" value="false" />
|
||||
<option name="INDENT_SELECT_INTO_CLAUSE" value="true" />
|
||||
</SqlCodeStyleSettings>
|
||||
<SybaseCodeStyleSettings version="6">
|
||||
<option name="USE_GENERAL_STYLE" value="false" />
|
||||
<option name="TYPE_CASE" value="3" />
|
||||
<option name="CUSTOM_TYPE_CASE" value="3" />
|
||||
<option name="ALIAS_CASE" value="4" />
|
||||
<option name="BUILT_IN_CASE" value="0" />
|
||||
<option name="QUERY_TRUE_INDENT" value="false" />
|
||||
<option name="QUERY_ALIGN_ELEMENTS" value="false" />
|
||||
<option name="QUERY_ALIGN_LINE_COMMENTS" value="false" />
|
||||
<option name="INSERT_EL_COMMA" value="2" />
|
||||
<option name="SET_EL_WRAP" value="0" />
|
||||
<option name="SET_EL_COMMA" value="0" />
|
||||
<option name="WITH_EL_WRAP" value="0" />
|
||||
<option name="WITH_EL_COMMA" value="0" />
|
||||
<option name="SELECT_EL_WRAP" value="3" />
|
||||
<option name="SELECT_EL_COMMA" value="2" />
|
||||
<option name="FROM_EL_WRAP" value="2" />
|
||||
<option name="FROM_EL_COMMA" value="2" />
|
||||
<option name="FROM_PLACE_ON" value="10" />
|
||||
<option name="WHERE_EL_WRAP" value="3" />
|
||||
<option name="WHERE_EL_BOUND" value="2" />
|
||||
<option name="ORDER_EL_COMMA" value="2" />
|
||||
<option name="TABLE_OPENING" value="1" />
|
||||
<option name="TABLE_CONTENT" value="2" />
|
||||
<option name="TABLE_CLOSING" value="3" />
|
||||
<option name="TABLE_ALTER_INSTRUCTION_ALIGN" value="false" />
|
||||
<option name="POST_OPT_WRAP_1" value="true" />
|
||||
<option name="POST_OPT_ALIGN" value="false" />
|
||||
<option name="ROUTINE_ARG_COMMA" value="2" />
|
||||
<option name="ROUTINE_ARG_ALIGN_TYPES" value="true" />
|
||||
<option name="IMP_DECLARE_EL_WRAP" value="1" />
|
||||
<option name="IMP_IF_THEN_WRAP_THEN" value="true" />
|
||||
<option name="CORTEGE_SPACE_BEFORE_L_PAREN" value="false" />
|
||||
<option name="EXPR_CASE_WHEN_WRAP" value="false" />
|
||||
<option name="EXPR_CASE_THEN_WRAP" value="true" />
|
||||
<option name="PRIMARY_KEY_NAME_TEMPLATE" value="{table}_{columns}_pk" />
|
||||
</SybaseCodeStyleSettings>
|
||||
<XML>
|
||||
<option name="XML_ATTRIBUTE_WRAP" value="0" />
|
||||
<option name="XML_TEXT_WRAP" value="0" />
|
||||
<option name="XML_KEEP_WHITE_SPACES_INSIDE_CDATA" value="true" />
|
||||
</XML>
|
||||
<codeStyleSettings language="DB2">
|
||||
<indentOptions>
|
||||
<option name="USE_TAB_CHARACTER" value="true" />
|
||||
<option name="SMART_TABS" value="true" />
|
||||
<option name="KEEP_INDENTS_ON_EMPTY_LINES" value="true" />
|
||||
</indentOptions>
|
||||
</codeStyleSettings>
|
||||
<codeStyleSettings language="Derby">
|
||||
<indentOptions>
|
||||
<option name="USE_TAB_CHARACTER" value="true" />
|
||||
<option name="SMART_TABS" value="true" />
|
||||
<option name="KEEP_INDENTS_ON_EMPTY_LINES" value="true" />
|
||||
</indentOptions>
|
||||
</codeStyleSettings>
|
||||
<codeStyleSettings language="H2">
|
||||
<indentOptions>
|
||||
<option name="USE_TAB_CHARACTER" value="true" />
|
||||
<option name="SMART_TABS" value="true" />
|
||||
<option name="KEEP_INDENTS_ON_EMPTY_LINES" value="true" />
|
||||
</indentOptions>
|
||||
</codeStyleSettings>
|
||||
<codeStyleSettings language="HSQLDB">
|
||||
<indentOptions>
|
||||
<option name="USE_TAB_CHARACTER" value="true" />
|
||||
<option name="SMART_TABS" value="true" />
|
||||
<option name="KEEP_INDENTS_ON_EMPTY_LINES" value="true" />
|
||||
</indentOptions>
|
||||
</codeStyleSettings>
|
||||
<codeStyleSettings language="HTML">
|
||||
<option name="SOFT_MARGINS" value="130" />
|
||||
<indentOptions>
|
||||
<option name="CONTINUATION_INDENT_SIZE" value="4" />
|
||||
<option name="USE_TAB_CHARACTER" value="true" />
|
||||
<option name="SMART_TABS" value="true" />
|
||||
</indentOptions>
|
||||
</codeStyleSettings>
|
||||
<codeStyleSettings language="JSON">
|
||||
<indentOptions>
|
||||
<option name="INDENT_SIZE" value="4" />
|
||||
<option name="CONTINUATION_INDENT_SIZE" value="4" />
|
||||
<option name="USE_TAB_CHARACTER" value="true" />
|
||||
<option name="SMART_TABS" value="true" />
|
||||
</indentOptions>
|
||||
</codeStyleSettings>
|
||||
<codeStyleSettings language="JavaScript">
|
||||
<option name="KEEP_BLANK_LINES_IN_CODE" value="1" />
|
||||
<option name="ELSE_ON_NEW_LINE" value="true" />
|
||||
<option name="WHILE_ON_NEW_LINE" value="true" />
|
||||
<option name="CATCH_ON_NEW_LINE" value="true" />
|
||||
<option name="FINALLY_ON_NEW_LINE" value="true" />
|
||||
<option name="ALIGN_MULTILINE_CHAINED_METHODS" value="true" />
|
||||
<option name="ALIGN_MULTILINE_PARAMETERS" value="false" />
|
||||
<option name="ALIGN_MULTILINE_BINARY_OPERATION" value="true" />
|
||||
<option name="ALIGN_MULTILINE_TERNARY_OPERATION" value="true" />
|
||||
<option name="ALIGN_MULTILINE_ARRAY_INITIALIZER_EXPRESSION" value="true" />
|
||||
<option name="SPACE_BEFORE_IF_PARENTHESES" value="false" />
|
||||
<option name="SPACE_BEFORE_WHILE_PARENTHESES" value="false" />
|
||||
<option name="SPACE_BEFORE_FOR_PARENTHESES" value="false" />
|
||||
<option name="SPACE_BEFORE_CATCH_PARENTHESES" value="false" />
|
||||
<option name="SPACE_BEFORE_SWITCH_PARENTHESES" value="false" />
|
||||
<option name="SPACE_BEFORE_METHOD_LBRACE" value="false" />
|
||||
<option name="SPACE_BEFORE_IF_LBRACE" value="false" />
|
||||
<option name="SPACE_BEFORE_ELSE_LBRACE" value="false" />
|
||||
<option name="SPACE_BEFORE_WHILE_LBRACE" value="false" />
|
||||
<option name="SPACE_BEFORE_FOR_LBRACE" value="false" />
|
||||
<option name="SPACE_BEFORE_DO_LBRACE" value="false" />
|
||||
<option name="SPACE_BEFORE_SWITCH_LBRACE" value="false" />
|
||||
<option name="SPACE_BEFORE_TRY_LBRACE" value="false" />
|
||||
<option name="SPACE_BEFORE_CATCH_LBRACE" value="false" />
|
||||
<option name="SPACE_BEFORE_FINALLY_LBRACE" value="false" />
|
||||
<option name="SPACE_BEFORE_ELSE_KEYWORD" value="false" />
|
||||
<option name="SPACE_BEFORE_WHILE_KEYWORD" value="false" />
|
||||
<option name="SPACE_BEFORE_CATCH_KEYWORD" value="false" />
|
||||
<option name="SPACE_BEFORE_FINALLY_KEYWORD" value="false" />
|
||||
<option name="BINARY_OPERATION_SIGN_ON_NEXT_LINE" value="true" />
|
||||
<option name="TERNARY_OPERATION_SIGNS_ON_NEXT_LINE" value="true" />
|
||||
<option name="ARRAY_INITIALIZER_WRAP" value="1" />
|
||||
<option name="ARRAY_INITIALIZER_LBRACE_ON_NEXT_LINE" value="true" />
|
||||
<option name="ARRAY_INITIALIZER_RBRACE_ON_NEXT_LINE" value="true" />
|
||||
<option name="PLACE_ASSIGNMENT_SIGN_ON_NEXT_LINE" value="true" />
|
||||
<option name="IF_BRACE_FORCE" value="3" />
|
||||
<option name="DOWHILE_BRACE_FORCE" value="3" />
|
||||
<option name="WHILE_BRACE_FORCE" value="3" />
|
||||
<option name="FOR_BRACE_FORCE" value="3" />
|
||||
<indentOptions>
|
||||
<option name="USE_TAB_CHARACTER" value="true" />
|
||||
<option name="SMART_TABS" value="true" />
|
||||
</indentOptions>
|
||||
</codeStyleSettings>
|
||||
<codeStyleSettings language="Markdown">
|
||||
<indentOptions>
|
||||
<option name="USE_TAB_CHARACTER" value="true" />
|
||||
<option name="SMART_TABS" value="true" />
|
||||
</indentOptions>
|
||||
</codeStyleSettings>
|
||||
<codeStyleSettings language="MySQL">
|
||||
<indentOptions>
|
||||
<option name="USE_TAB_CHARACTER" value="true" />
|
||||
<option name="SMART_TABS" value="true" />
|
||||
<option name="KEEP_INDENTS_ON_EMPTY_LINES" value="true" />
|
||||
</indentOptions>
|
||||
</codeStyleSettings>
|
||||
<codeStyleSettings language="Oracle">
|
||||
<indentOptions>
|
||||
<option name="USE_TAB_CHARACTER" value="true" />
|
||||
<option name="SMART_TABS" value="true" />
|
||||
<option name="KEEP_INDENTS_ON_EMPTY_LINES" value="true" />
|
||||
</indentOptions>
|
||||
</codeStyleSettings>
|
||||
<codeStyleSettings language="PHP">
|
||||
<option name="KEEP_BLANK_LINES_IN_DECLARATIONS" value="1" />
|
||||
<option name="KEEP_BLANK_LINES_IN_CODE" value="1" />
|
||||
<option name="KEEP_BLANK_LINES_BEFORE_RBRACE" value="1" />
|
||||
<option name="BLANK_LINES_AFTER_PACKAGE" value="1" />
|
||||
<option name="BLANK_LINES_BEFORE_IMPORTS" value="0" />
|
||||
<option name="BLANK_LINES_AFTER_CLASS_HEADER" value="1" />
|
||||
<option name="BLANK_LINES_BEFORE_CLASS_END" value="1" />
|
||||
<option name="CLASS_BRACE_STYLE" value="1" />
|
||||
<option name="METHOD_BRACE_STYLE" value="1" />
|
||||
<option name="ELSE_ON_NEW_LINE" value="true" />
|
||||
<option name="WHILE_ON_NEW_LINE" value="true" />
|
||||
<option name="CATCH_ON_NEW_LINE" value="true" />
|
||||
<option name="FINALLY_ON_NEW_LINE" value="true" />
|
||||
<option name="SPECIAL_ELSE_IF_TREATMENT" value="true" />
|
||||
<option name="ALIGN_MULTILINE_CHAINED_METHODS" value="true" />
|
||||
<option name="ALIGN_MULTILINE_PARAMETERS_IN_CALLS" value="true" />
|
||||
<option name="ALIGN_MULTILINE_BINARY_OPERATION" value="true" />
|
||||
<option name="ALIGN_MULTILINE_TERNARY_OPERATION" value="true" />
|
||||
<option name="ALIGN_MULTILINE_EXTENDS_LIST" value="true" />
|
||||
<option name="ALIGN_MULTILINE_ARRAY_INITIALIZER_EXPRESSION" value="true" />
|
||||
<option name="ALIGN_GROUP_FIELD_DECLARATIONS" value="true" />
|
||||
<option name="SPACE_BEFORE_IF_PARENTHESES" value="false" />
|
||||
<option name="SPACE_BEFORE_WHILE_PARENTHESES" value="false" />
|
||||
<option name="SPACE_BEFORE_FOR_PARENTHESES" value="false" />
|
||||
<option name="SPACE_BEFORE_CATCH_PARENTHESES" value="false" />
|
||||
<option name="SPACE_BEFORE_SWITCH_PARENTHESES" value="false" />
|
||||
<option name="SPACE_BEFORE_CLASS_LBRACE" value="false" />
|
||||
<option name="SPACE_BEFORE_METHOD_LBRACE" value="false" />
|
||||
<option name="SPACE_BEFORE_IF_LBRACE" value="false" />
|
||||
<option name="SPACE_BEFORE_ELSE_LBRACE" value="false" />
|
||||
<option name="SPACE_BEFORE_WHILE_LBRACE" value="false" />
|
||||
<option name="SPACE_BEFORE_FOR_LBRACE" value="false" />
|
||||
<option name="SPACE_BEFORE_DO_LBRACE" value="false" />
|
||||
<option name="SPACE_BEFORE_SWITCH_LBRACE" value="false" />
|
||||
<option name="SPACE_BEFORE_TRY_LBRACE" value="false" />
|
||||
<option name="SPACE_BEFORE_CATCH_LBRACE" value="false" />
|
||||
<option name="SPACE_BEFORE_FINALLY_LBRACE" value="false" />
|
||||
<option name="CALL_PARAMETERS_LPAREN_ON_NEXT_LINE" value="true" />
|
||||
<option name="CALL_PARAMETERS_RPAREN_ON_NEXT_LINE" value="true" />
|
||||
<option name="METHOD_PARAMETERS_LPAREN_ON_NEXT_LINE" value="true" />
|
||||
<option name="METHOD_PARAMETERS_RPAREN_ON_NEXT_LINE" value="true" />
|
||||
<option name="PARENTHESES_EXPRESSION_LPAREN_WRAP" value="true" />
|
||||
<option name="PARENTHESES_EXPRESSION_RPAREN_WRAP" value="true" />
|
||||
<option name="BINARY_OPERATION_SIGN_ON_NEXT_LINE" value="true" />
|
||||
<option name="TERNARY_OPERATION_SIGNS_ON_NEXT_LINE" value="true" />
|
||||
<option name="FOR_STATEMENT_RPAREN_ON_NEXT_LINE" value="true" />
|
||||
<option name="ARRAY_INITIALIZER_WRAP" value="1" />
|
||||
<option name="ARRAY_INITIALIZER_LBRACE_ON_NEXT_LINE" value="true" />
|
||||
<option name="ARRAY_INITIALIZER_RBRACE_ON_NEXT_LINE" value="true" />
|
||||
<option name="PLACE_ASSIGNMENT_SIGN_ON_NEXT_LINE" value="true" />
|
||||
<option name="IF_BRACE_FORCE" value="3" />
|
||||
<option name="DOWHILE_BRACE_FORCE" value="3" />
|
||||
<option name="WHILE_BRACE_FORCE" value="3" />
|
||||
<option name="FOR_BRACE_FORCE" value="3" />
|
||||
<indentOptions>
|
||||
<option name="USE_TAB_CHARACTER" value="true" />
|
||||
<option name="SMART_TABS" value="true" />
|
||||
</indentOptions>
|
||||
<arrangement>
|
||||
<rules>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<CONST />
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<FIELD />
|
||||
<PUBLIC />
|
||||
<STATIC />
|
||||
</AND>
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<FIELD />
|
||||
<PROTECTED />
|
||||
<STATIC />
|
||||
</AND>
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<FIELD />
|
||||
<PRIVATE />
|
||||
<STATIC />
|
||||
</AND>
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<FIELD />
|
||||
<PUBLIC />
|
||||
</AND>
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<FIELD />
|
||||
<PROTECTED />
|
||||
</AND>
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<FIELD />
|
||||
<PRIVATE />
|
||||
</AND>
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<CONSTRUCTOR />
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<METHOD />
|
||||
<PUBLIC />
|
||||
<STATIC />
|
||||
</AND>
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<METHOD />
|
||||
<PROTECTED />
|
||||
<STATIC />
|
||||
</AND>
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<METHOD />
|
||||
<PRIVATE />
|
||||
<STATIC />
|
||||
</AND>
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<METHOD />
|
||||
<PUBLIC />
|
||||
</AND>
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<METHOD />
|
||||
<PROTECTED />
|
||||
</AND>
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<METHOD />
|
||||
<PRIVATE />
|
||||
</AND>
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<TRAIT />
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<INTERFACE />
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<CLASS />
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
</rules>
|
||||
</arrangement>
|
||||
</codeStyleSettings>
|
||||
<codeStyleSettings language="PostgreSQL">
|
||||
<indentOptions>
|
||||
<option name="USE_TAB_CHARACTER" value="true" />
|
||||
<option name="SMART_TABS" value="true" />
|
||||
<option name="KEEP_INDENTS_ON_EMPTY_LINES" value="true" />
|
||||
</indentOptions>
|
||||
</codeStyleSettings>
|
||||
<codeStyleSettings language="SQL">
|
||||
<indentOptions>
|
||||
<option name="USE_TAB_CHARACTER" value="true" />
|
||||
<option name="SMART_TABS" value="true" />
|
||||
<option name="KEEP_INDENTS_ON_EMPTY_LINES" value="true" />
|
||||
</indentOptions>
|
||||
</codeStyleSettings>
|
||||
<codeStyleSettings language="SQLite">
|
||||
<indentOptions>
|
||||
<option name="USE_TAB_CHARACTER" value="true" />
|
||||
<option name="SMART_TABS" value="true" />
|
||||
<option name="KEEP_INDENTS_ON_EMPTY_LINES" value="true" />
|
||||
</indentOptions>
|
||||
</codeStyleSettings>
|
||||
<codeStyleSettings language="Sybase">
|
||||
<indentOptions>
|
||||
<option name="USE_TAB_CHARACTER" value="true" />
|
||||
<option name="SMART_TABS" value="true" />
|
||||
<option name="KEEP_INDENTS_ON_EMPTY_LINES" value="true" />
|
||||
</indentOptions>
|
||||
</codeStyleSettings>
|
||||
<codeStyleSettings language="TSQL">
|
||||
<indentOptions>
|
||||
<option name="USE_TAB_CHARACTER" value="true" />
|
||||
<option name="SMART_TABS" value="true" />
|
||||
<option name="KEEP_INDENTS_ON_EMPTY_LINES" value="true" />
|
||||
</indentOptions>
|
||||
</codeStyleSettings>
|
||||
<codeStyleSettings language="XML">
|
||||
<indentOptions>
|
||||
<option name="USE_TAB_CHARACTER" value="true" />
|
||||
<option name="SMART_TABS" value="true" />
|
||||
</indentOptions>
|
||||
</codeStyleSettings>
|
||||
</code_scheme>
|
||||
</component>
|
||||
5
vendor/chillerlan/php-qrcode/.idea/codeStyles/codeStyleConfig.xml
generated
vendored
5
vendor/chillerlan/php-qrcode/.idea/codeStyles/codeStyleConfig.xml
generated
vendored
@@ -1,5 +0,0 @@
|
||||
<component name="ProjectCodeStyleConfiguration">
|
||||
<state>
|
||||
<option name="USE_PER_PROJECT_SETTINGS" value="true" />
|
||||
</state>
|
||||
</component>
|
||||
27
vendor/chillerlan/php-qrcode/.idea/inspectionProfiles/Project_Default.xml
generated
vendored
27
vendor/chillerlan/php-qrcode/.idea/inspectionProfiles/Project_Default.xml
generated
vendored
@@ -1,27 +0,0 @@
|
||||
<component name="InspectionProjectProfileManager">
|
||||
<profile version="1.0">
|
||||
<option name="myName" value="Project Default" />
|
||||
<inspection_tool class="JSAccessibilityCheck" enabled="true" level="WARNING" enabled_by_default="true" editorAttributes="WARNING_ATTRIBUTES" />
|
||||
<inspection_tool class="MessDetectorValidationInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="PhpAssignmentInConditionInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="PhpCSValidationInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="PhpComposerExtensionStubsInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="PhpDivisionByZeroInspection" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="PhpInternalEntityUsedInspection" enabled="true" level="INFO" enabled_by_default="true" />
|
||||
<inspection_tool class="PhpMethodOrClassCallIsNotCaseSensitiveInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="PhpMissingDocCommentInspection" enabled="true" level="INFO" enabled_by_default="true" />
|
||||
<inspection_tool class="PhpMissingParentCallMagicInspection" enabled="false" level="WARNING" enabled_by_default="false">
|
||||
<option name="ENABLE_FOR_SLEEP" value="false" />
|
||||
<option name="ENABLE_FOR_WAKEUP" value="false" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="PhpMultipleClassesDeclarationsInOneFile" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="PhpStatementHasEmptyBodyInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="PhpUsageOfSilenceOperatorInspection" enabled="true" level="STFU!" enabled_by_default="true" />
|
||||
<inspection_tool class="PhpVoidFunctionResultUsedInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="SpellCheckingInspection" enabled="false" level="TYPO" enabled_by_default="false">
|
||||
<option name="processCode" value="true" />
|
||||
<option name="processLiterals" value="true" />
|
||||
<option name="processComments" value="true" />
|
||||
</inspection_tool>
|
||||
</profile>
|
||||
</component>
|
||||
41
vendor/chillerlan/php-qrcode/README.md
vendored
41
vendor/chillerlan/php-qrcode/README.md
vendored
@@ -13,24 +13,24 @@ It also features a QR Code reader based on a [PHP port](https://github.com/khana
|
||||
[![Packagist downloads][downloads-badge]][downloads]
|
||||
[![Documentation][readthedocs-badge]][readthedocs]
|
||||
|
||||
[php-badge]: https://img.shields.io/packagist/php-v/chillerlan/php-qrcode?logo=php&color=8892BF
|
||||
[php-badge]: https://img.shields.io/packagist/php-v/chillerlan/php-qrcode?logo=php&color=8892BF&logoColor=fff
|
||||
[php]: https://www.php.net/supported-versions.php
|
||||
[packagist-badge]: https://img.shields.io/packagist/v/chillerlan/php-qrcode.svg?logo=packagist
|
||||
[packagist-badge]: https://img.shields.io/packagist/v/chillerlan/php-qrcode.svg?logo=packagist&logoColor=fff
|
||||
[packagist]: https://packagist.org/packages/chillerlan/php-qrcode
|
||||
[gh-action-badge]: https://img.shields.io/github/actions/workflow/status/chillerlan/php-qrcode/ci.yml?branch=v5.0.x&logo=github
|
||||
[gh-action-badge]: https://img.shields.io/github/actions/workflow/status/chillerlan/php-qrcode/ci.yml?branch=v5.0.x&logo=github&logoColor=fff
|
||||
[gh-action]: https://github.com/chillerlan/php-qrcode/actions/workflows/ci.yml?query=branch%3Amain
|
||||
[coverage-badge]: https://img.shields.io/codecov/c/github/chillerlan/php-qrcode/v5.0.x?logo=codecov
|
||||
[coverage-badge]: https://img.shields.io/codecov/c/github/chillerlan/php-qrcode/v5.0.x?logo=codecov&logoColor=fff
|
||||
[coverage]: https://app.codecov.io/gh/chillerlan/php-qrcode/tree/v5.0.x
|
||||
[codacy-badge]: https://img.shields.io/codacy/grade/edccfc4fe5a34b74b1c53ee03f097b8d/v5.0.x?logo=codacy
|
||||
[codacy-badge]: https://img.shields.io/codacy/grade/edccfc4fe5a34b74b1c53ee03f097b8d/v5.0.x?logo=codacy&logoColor=fff
|
||||
[codacy]: https://app.codacy.com/gh/chillerlan/php-qrcode/dashboard?branch=v5.0.x
|
||||
[downloads-badge]: https://img.shields.io/packagist/dt/chillerlan/php-qrcode?logo=packagist
|
||||
[downloads-badge]: https://img.shields.io/packagist/dt/chillerlan/php-qrcode?logo=packagist&logoColor=fff
|
||||
[downloads]: https://packagist.org/packages/chillerlan/php-qrcode/stats
|
||||
[readthedocs-badge]: https://img.shields.io/readthedocs/php-qrcode/v5.0.x?logo=readthedocs
|
||||
[readthedocs-badge]: https://img.shields.io/readthedocs/php-qrcode/v5.0.x?logo=readthedocs&logoColor=fff
|
||||
[readthedocs]: https://php-qrcode.readthedocs.io/en/v5.0.x/
|
||||
|
||||
## Overview
|
||||
# Overview
|
||||
|
||||
### Features
|
||||
## Features
|
||||
|
||||
- Creation of [Model 2 QR Codes](https://www.qrcode.com/en/codes/model12.html), [Version 1 to 40](https://www.qrcode.com/en/about/version.html)
|
||||
- [ECC Levels](https://www.qrcode.com/en/about/error_correction.html) L/M/Q/H supported
|
||||
@@ -52,7 +52,7 @@ It also features a QR Code reader based on a [PHP port](https://github.com/khana
|
||||
- QR Code reader (via GD and ImageMagick)
|
||||
|
||||
|
||||
### Requirements
|
||||
## Requirements
|
||||
|
||||
- PHP 7.4+
|
||||
- [`ext-mbstring`](https://www.php.net/manual/book.mbstring.php)
|
||||
@@ -65,16 +65,21 @@ It also features a QR Code reader based on a [PHP port](https://github.com/khana
|
||||
For the QRCode reader, either `ext-gd` or `ext-imagick` is required!
|
||||
|
||||
|
||||
## Documentation
|
||||
# Documentation
|
||||
|
||||
- The user manual is at https://php-qrcode.readthedocs.io/ ([sources](https://github.com/chillerlan/php-qrcode/tree/v5.0.x/docs))
|
||||
- An API documentation created with [phpDocumentor](https://www.phpdoc.org/) can be found at https://chillerlan.github.io/php-qrcode/
|
||||
- The documentation for the `QROptions` container can be found here: [chillerlan/php-settings-container](https://github.com/chillerlan/php-settings-container#readme)
|
||||
|
||||
**Important: Please use the examples from the branch that matches your installed php-qrcode version (
|
||||
[v4.x](https://github.com/chillerlan/php-qrcode/tree/v4.3.x/examples),
|
||||
[v5.x](https://github.com/chillerlan/php-qrcode/tree/v5.0.x/examples),
|
||||
[dev-main](https://github.com/chillerlan/php-qrcode/tree/main/examples)
|
||||
)!**
|
||||
|
||||
## Installation with [composer](https://getcomposer.org)
|
||||
|
||||
See [the installation guide](https://php-qrcode.readthedocs.io/en/v5.0.x/Usage-Installation.html) for more info!
|
||||
See [the installation guide](https://php-qrcode.readthedocs.io/en/v5.0.x/Usage/Installation.html) for more info!
|
||||
|
||||
|
||||
### Terminal
|
||||
@@ -117,7 +122,7 @@ Also, have a look [in the examples folder](https://github.com/chillerlan/php-qrc
|
||||
</p>
|
||||
|
||||
|
||||
### Reading QR Codes
|
||||
## Reading QR Codes
|
||||
|
||||
Using the built-in QR Code reader is pretty straight-forward:
|
||||
|
||||
@@ -139,30 +144,30 @@ catch(Throwable $e){
|
||||
```
|
||||
|
||||
|
||||
## Shameless advertising
|
||||
# Shameless advertising
|
||||
|
||||
Hi, please check out some of my other projects that are way cooler than qrcodes!
|
||||
|
||||
- [js-qrcode](https://github.com/chillerlan/js-qrcode) - a javascript port of this library
|
||||
- [php-authenticator](https://github.com/chillerlan/php-authenticator) - a Google Authenticator implementation (see [authenticator example](https://github.com/chillerlan/php-qrcode/blob/v5.0.x/examples/authenticator.php))
|
||||
- [php-httpinterface](https://github.com/chillerlan/php-httpinterface) - a PSR-7/15/17/18 implemetation
|
||||
- [php-oauth-core](https://github.com/chillerlan/php-oauth-core) - an OAuth 1/2 client library along with a bunch of [providers](https://github.com/chillerlan/php-oauth-providers)
|
||||
- [php-oauth](https://github.com/chillerlan/php-oauth) - an OAuth 1/2 client library, fully PSR-7/PSR-17/PSR-18 compatible
|
||||
- [php-database](https://github.com/chillerlan/php-database) - a database client & querybuilder for MySQL, Postgres, SQLite, MSSQL, Firebird
|
||||
- [php-tootbot](https://github.com/php-tootbot/tootbot-template) - a Mastodon bot library (see [@dwil](https://github.com/php-tootbot/dwil))
|
||||
|
||||
|
||||
## Disclaimer!
|
||||
# Disclaimer!
|
||||
|
||||
I don't take responsibility for molten CPUs, misled applications, failed log-ins etc.. Use at your own risk!
|
||||
|
||||
|
||||
### License notice
|
||||
## License notice
|
||||
|
||||
- Parts of this code are [ported to PHP](https://github.com/codemasher/php-qrcode-decoder) from the [ZXing project](https://github.com/zxing/zxing) and licensed under the [Apache License, Version 2.0](./NOTICE).
|
||||
- [The documentation](https://github.com/chillerlan/php-qrcode/tree/v5.0.x/docs) is licensed under the [Creative Commons Attribution 4.0 International (CC BY 4.0) License](https://creativecommons.org/licenses/by/4.0/).
|
||||
|
||||
|
||||
### Trademark Notice
|
||||
## Trademark Notice
|
||||
|
||||
The word "QR Code" is a registered trademark of *DENSO WAVE INCORPORATED*<br>
|
||||
https://www.qrcode.com/en/faq.html#patentH2Title
|
||||
|
||||
35
vendor/chillerlan/php-qrcode/composer.json
vendored
35
vendor/chillerlan/php-qrcode/composer.json
vendored
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"$schema": "https://getcomposer.org/schema.json",
|
||||
"name": "chillerlan/php-qrcode",
|
||||
"description": "A QR code generator and reader with a user friendly API. PHP 7.4+",
|
||||
"description": "A QR Code generator and reader with a user-friendly API. PHP 7.4+",
|
||||
"homepage": "https://github.com/chillerlan/php-qrcode",
|
||||
"license": [
|
||||
"MIT", "Apache-2.0"
|
||||
@@ -32,6 +33,12 @@
|
||||
"homepage":"https://github.com/chillerlan/php-qrcode/graphs/contributors"
|
||||
}
|
||||
],
|
||||
"funding": [
|
||||
{
|
||||
"type": "Ko-Fi",
|
||||
"url": "https://ko-fi.com/codemasher"
|
||||
}
|
||||
],
|
||||
"support": {
|
||||
"docs": "https://php-qrcode.readthedocs.io",
|
||||
"issues": "https://github.com/chillerlan/php-qrcode/issues",
|
||||
@@ -42,15 +49,18 @@
|
||||
"require": {
|
||||
"php": "^7.4 || ^8.0",
|
||||
"ext-mbstring": "*",
|
||||
"chillerlan/php-settings-container": "^2.1.4 || ^3.1"
|
||||
"chillerlan/php-settings-container": "^2.1.6 || ^3.2.1"
|
||||
},
|
||||
"require-dev": {
|
||||
"chillerlan/php-authenticator": "^4.1 || ^5.1",
|
||||
"phan/phan": "^5.4",
|
||||
"ext-fileinfo": "*",
|
||||
"chillerlan/php-authenticator": "^4.3.1 || ^5.2.1",
|
||||
"phan/phan": "^5.5.1",
|
||||
"phpcompatibility/php-compatibility": "10.x-dev",
|
||||
"phpunit/phpunit": "^9.6",
|
||||
"phpmd/phpmd": "^2.15",
|
||||
"setasign/fpdf": "^1.8.2",
|
||||
"squizlabs/php_codesniffer": "^3.8"
|
||||
"slevomat/coding-standard": "^8.23.0",
|
||||
"squizlabs/php_codesniffer": "^4.0.0"
|
||||
},
|
||||
"suggest": {
|
||||
"chillerlan/php-authenticator": "Yet another Google authenticator! Also creates URIs for mobile apps.",
|
||||
@@ -59,21 +69,26 @@
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"chillerlan\\QRCode\\": "src/"
|
||||
"chillerlan\\QRCode\\": "src"
|
||||
}
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"chillerlan\\QRCodeTest\\": "tests/"
|
||||
"chillerlan\\QRCodeTest\\": "tests"
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
"phpunit": "@php vendor/bin/phpunit",
|
||||
"phan": "@php vendor/bin/phan"
|
||||
"phan": "@php vendor/bin/phan",
|
||||
"phpcs": "@php vendor/bin/phpcs",
|
||||
"phpmd": "@php vendor/bin/phpmd src text ./phpmd.xml.dist",
|
||||
"phpunit": "@php vendor/bin/phpunit"
|
||||
},
|
||||
"config": {
|
||||
"lock": false,
|
||||
"sort-packages": true,
|
||||
"platform-check": true
|
||||
"platform-check": true,
|
||||
"allow-plugins": {
|
||||
"dealerdirect/phpcodesniffer-composer-installer": true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ class GDLuminanceSource extends LuminanceSourceAbstract{
|
||||
*
|
||||
* @throws \chillerlan\QRCode\Decoder\QRCodeDecoderException
|
||||
*/
|
||||
public function __construct($gdImage, SettingsContainerInterface $options = null){
|
||||
public function __construct($gdImage, ?SettingsContainerInterface $options = null){
|
||||
|
||||
/** @noinspection PhpFullyQualifiedNameUsageInspection */
|
||||
if(
|
||||
@@ -85,12 +85,12 @@ class GDLuminanceSource extends LuminanceSourceAbstract{
|
||||
}
|
||||
|
||||
/** @inheritDoc */
|
||||
public static function fromFile(string $path, SettingsContainerInterface $options = null):self{
|
||||
public static function fromFile(string $path, ?SettingsContainerInterface $options = null):self{
|
||||
return new self(imagecreatefromstring(file_get_contents(self::checkFile($path))), $options);
|
||||
}
|
||||
|
||||
/** @inheritDoc */
|
||||
public static function fromBlob(string $blob, SettingsContainerInterface $options = null):self{
|
||||
public static function fromBlob(string $blob, ?SettingsContainerInterface $options = null):self{
|
||||
return new self(imagecreatefromstring($blob), $options);
|
||||
}
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ final class GenericGFPoly{
|
||||
* @throws \chillerlan\QRCode\QRCodeException if argument is null or empty, or if leading coefficient is 0 and this
|
||||
* is not a constant polynomial (that is, it is not the monomial "0")
|
||||
*/
|
||||
public function __construct(array $coefficients, int $degree = null){
|
||||
public function __construct(array $coefficients, ?int $degree = null){
|
||||
$degree ??= 0;
|
||||
|
||||
if(empty($coefficients)){
|
||||
|
||||
@@ -28,7 +28,7 @@ class IMagickLuminanceSource extends LuminanceSourceAbstract{
|
||||
/**
|
||||
* IMagickLuminanceSource constructor.
|
||||
*/
|
||||
public function __construct(Imagick $imagick, SettingsContainerInterface $options = null){
|
||||
public function __construct(Imagick $imagick, ?SettingsContainerInterface $options = null){
|
||||
parent::__construct($imagick->getImageWidth(), $imagick->getImageHeight(), $options);
|
||||
|
||||
$this->imagick = $imagick;
|
||||
@@ -63,12 +63,12 @@ class IMagickLuminanceSource extends LuminanceSourceAbstract{
|
||||
}
|
||||
|
||||
/** @inheritDoc */
|
||||
public static function fromFile(string $path, SettingsContainerInterface $options = null):self{
|
||||
public static function fromFile(string $path, ?SettingsContainerInterface $options = null):self{
|
||||
return new self(new Imagick(self::checkFile($path)), $options);
|
||||
}
|
||||
|
||||
/** @inheritDoc */
|
||||
public static function fromBlob(string $blob, SettingsContainerInterface $options = null):self{
|
||||
public static function fromBlob(string $blob, ?SettingsContainerInterface $options = null):self{
|
||||
$im = new Imagick;
|
||||
$im->readImageBlob($blob);
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@ abstract class LuminanceSourceAbstract implements LuminanceSourceInterface{
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public function __construct(int $width, int $height, SettingsContainerInterface $options = null){
|
||||
public function __construct(int $width, int $height, ?SettingsContainerInterface $options = null){
|
||||
$this->width = $width;
|
||||
$this->height = $height;
|
||||
$this->options = ($options ?? new QROptions);
|
||||
@@ -57,7 +57,10 @@ abstract class LuminanceSourceAbstract implements LuminanceSourceInterface{
|
||||
return $this->height;
|
||||
}
|
||||
|
||||
/** @inheritDoc */
|
||||
/**
|
||||
* @inheritDoc
|
||||
* @throws \chillerlan\QRCode\Decoder\QRCodeDecoderException
|
||||
*/
|
||||
public function getRow(int $y):array{
|
||||
|
||||
if($y < 0 || $y >= $this->getHeight()){
|
||||
|
||||
@@ -77,7 +77,7 @@ final class MaskPattern{
|
||||
*/
|
||||
public function __construct(int $maskPattern){
|
||||
|
||||
if((0b111 & $maskPattern) !== $maskPattern){
|
||||
if(($maskPattern & 0b111) !== $maskPattern){
|
||||
throw new QRCodeException('invalid mask pattern');
|
||||
}
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
namespace chillerlan\QRCode\Data;
|
||||
|
||||
use chillerlan\QRCode\Common\{BitBuffer, Mode};
|
||||
use function array_flip, ceil, intdiv, str_split;
|
||||
use function ceil, intdiv, preg_match, strpos;
|
||||
|
||||
/**
|
||||
* Alphanumeric mode: 0 to 9, A to Z, space, $ % * + - . / :
|
||||
@@ -24,16 +24,9 @@ final class AlphaNum extends QRDataModeAbstract{
|
||||
/**
|
||||
* ISO/IEC 18004:2000 Table 5
|
||||
*
|
||||
* @var int[]
|
||||
* @var string
|
||||
*/
|
||||
private const CHAR_TO_ORD = [
|
||||
'0' => 0, '1' => 1, '2' => 2, '3' => 3, '4' => 4, '5' => 5, '6' => 6, '7' => 7,
|
||||
'8' => 8, '9' => 9, 'A' => 10, 'B' => 11, 'C' => 12, 'D' => 13, 'E' => 14, 'F' => 15,
|
||||
'G' => 16, 'H' => 17, 'I' => 18, 'J' => 19, 'K' => 20, 'L' => 21, 'M' => 22, 'N' => 23,
|
||||
'O' => 24, 'P' => 25, 'Q' => 26, 'R' => 27, 'S' => 28, 'T' => 29, 'U' => 30, 'V' => 31,
|
||||
'W' => 32, 'X' => 33, 'Y' => 34, 'Z' => 35, ' ' => 36, '$' => 37, '%' => 38, '*' => 39,
|
||||
'+' => 40, '-' => 41, '.' => 42, '/' => 43, ':' => 44,
|
||||
];
|
||||
private const CHAR_MAP = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:';
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
@@ -51,18 +44,7 @@ final class AlphaNum extends QRDataModeAbstract{
|
||||
* @inheritDoc
|
||||
*/
|
||||
public static function validateString(string $string):bool{
|
||||
|
||||
if($string === ''){
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach(str_split($string) as $chr){
|
||||
if(!isset(self::CHAR_TO_ORD[$chr])){
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
return (bool)preg_match('/^[A-Z\d %$*+\-.:\/]+$/', $string);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -78,12 +60,15 @@ final class AlphaNum extends QRDataModeAbstract{
|
||||
|
||||
// encode 2 characters in 11 bits
|
||||
for($i = 0; ($i + 1) < $len; $i += 2){
|
||||
$bitBuffer->put((self::CHAR_TO_ORD[$this->data[$i]] * 45 + self::CHAR_TO_ORD[$this->data[($i + 1)]]), 11);
|
||||
$bitBuffer->put(
|
||||
($this->ord($this->data[$i]) * 45 + $this->ord($this->data[($i + 1)])),
|
||||
11,
|
||||
);
|
||||
}
|
||||
|
||||
// encode a remaining character in 6 bits
|
||||
if($i < $len){
|
||||
$bitBuffer->put(self::CHAR_TO_ORD[$this->data[$i]], 6);
|
||||
$bitBuffer->put($this->ord($this->data[$i]), 6);
|
||||
}
|
||||
|
||||
return $this;
|
||||
@@ -95,19 +80,7 @@ final class AlphaNum extends QRDataModeAbstract{
|
||||
* @throws \chillerlan\QRCode\Data\QRCodeDataException
|
||||
*/
|
||||
public static function decodeSegment(BitBuffer $bitBuffer, int $versionNumber):string{
|
||||
$length = $bitBuffer->read(self::getLengthBits($versionNumber));
|
||||
$charmap = array_flip(self::CHAR_TO_ORD);
|
||||
|
||||
// @todo
|
||||
$toAlphaNumericChar = function(int $ord) use ($charmap):string{
|
||||
|
||||
if(isset($charmap[$ord])){
|
||||
return $charmap[$ord];
|
||||
}
|
||||
|
||||
throw new QRCodeDataException('invalid character value: '.$ord);
|
||||
};
|
||||
|
||||
$length = $bitBuffer->read(self::getLengthBits($versionNumber));
|
||||
$result = '';
|
||||
// Read two characters at a time
|
||||
while($length > 1){
|
||||
@@ -116,9 +89,9 @@ final class AlphaNum extends QRDataModeAbstract{
|
||||
throw new QRCodeDataException('not enough bits available'); // @codeCoverageIgnore
|
||||
}
|
||||
|
||||
$nextTwoCharsBits = $bitBuffer->read(11);
|
||||
$result .= $toAlphaNumericChar(intdiv($nextTwoCharsBits, 45));
|
||||
$result .= $toAlphaNumericChar($nextTwoCharsBits % 45);
|
||||
$nextTwoCharsBits = $bitBuffer->read(11);
|
||||
$result .= self::chr(intdiv($nextTwoCharsBits, 45));
|
||||
$result .= self::chr($nextTwoCharsBits % 45);
|
||||
$length -= 2;
|
||||
}
|
||||
|
||||
@@ -128,10 +101,36 @@ final class AlphaNum extends QRDataModeAbstract{
|
||||
throw new QRCodeDataException('not enough bits available'); // @codeCoverageIgnore
|
||||
}
|
||||
|
||||
$result .= $toAlphaNumericChar($bitBuffer->read(6));
|
||||
$result .= self::chr($bitBuffer->read(6));
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws \chillerlan\QRCode\Data\QRCodeDataException
|
||||
*/
|
||||
private function ord(string $chr):int{
|
||||
/** @phan-suppress-next-line PhanParamSuspiciousOrder */
|
||||
$ord = strpos(self::CHAR_MAP, $chr);
|
||||
|
||||
if($ord === false){
|
||||
throw new QRCodeDataException('invalid character'); // @codeCoverageIgnore
|
||||
}
|
||||
|
||||
return $ord;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws \chillerlan\QRCode\Data\QRCodeDataException
|
||||
*/
|
||||
private static function chr(int $ord):string{
|
||||
|
||||
if($ord < 0 || $ord > 44){
|
||||
throw new QRCodeDataException('invalid character code'); // @codeCoverageIgnore
|
||||
}
|
||||
|
||||
return self::CHAR_MAP[$ord];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
28
vendor/chillerlan/php-qrcode/src/Data/ECI.php
vendored
28
vendor/chillerlan/php-qrcode/src/Data/ECI.php
vendored
@@ -34,6 +34,7 @@ final class ECI extends QRDataModeAbstract{
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
* @throws \chillerlan\QRCode\Data\QRCodeDataException
|
||||
* @noinspection PhpMissingParentConstructorInspection
|
||||
*/
|
||||
public function __construct(int $encoding){
|
||||
@@ -107,7 +108,7 @@ final class ECI extends QRDataModeAbstract{
|
||||
$id = ((($firstByte & 0b00011111) << 16) | $bitBuffer->read(16));
|
||||
}
|
||||
else{
|
||||
throw new QRCodeDataException(sprintf('error decoding ECI value first byte: %08b', $firstByte)); // @codeCoverageIgnore
|
||||
throw new QRCodeDataException(sprintf('error decoding ECI value first byte: %08b', $firstByte));// @codeCoverageIgnore
|
||||
}
|
||||
|
||||
return new ECICharset($id);
|
||||
@@ -128,17 +129,12 @@ final class ECI extends QRDataModeAbstract{
|
||||
public static function decodeSegment(BitBuffer $bitBuffer, int $versionNumber):string{
|
||||
$eciCharset = self::parseValue($bitBuffer);
|
||||
$nextMode = $bitBuffer->read(4);
|
||||
|
||||
if($nextMode !== Mode::BYTE){
|
||||
throw new QRCodeDataException(sprintf('ECI designator followed by invalid mode: "%04b"', $nextMode));
|
||||
}
|
||||
|
||||
$data = Byte::decodeSegment($bitBuffer, $versionNumber);
|
||||
$encoding = $eciCharset->getName();
|
||||
$data = self::decodeModeSegment($nextMode, $bitBuffer, $versionNumber);
|
||||
$encoding = $eciCharset->getName();
|
||||
|
||||
if($encoding === null){
|
||||
// The spec isn't clear on this mode; see
|
||||
// section 6.4.5: t does not say which encoding to assuming
|
||||
// section 6.4.5: it does not say which encoding to assuming
|
||||
// upon decoding. I have seen ISO-8859-1 used as well as
|
||||
// Shift_JIS -- without anything like an ECI designator to
|
||||
// give a hint.
|
||||
@@ -152,4 +148,18 @@ final class ECI extends QRDataModeAbstract{
|
||||
return mb_convert_encoding($data, mb_internal_encoding(), $encoding);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws \chillerlan\QRCode\Data\QRCodeDataException
|
||||
*/
|
||||
private static function decodeModeSegment(int $mode, BitBuffer $bitBuffer, int $versionNumber):string{
|
||||
|
||||
switch(true){
|
||||
case $mode === Mode::NUMBER: return Number::decodeSegment($bitBuffer, $versionNumber);
|
||||
case $mode === Mode::ALPHANUM: return AlphaNum::decodeSegment($bitBuffer, $versionNumber);
|
||||
case $mode === Mode::BYTE: return Byte::decodeSegment($bitBuffer, $versionNumber);
|
||||
}
|
||||
|
||||
throw new QRCodeDataException(sprintf('ECI designator followed by invalid mode: "%04b"', $mode));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
12
vendor/chillerlan/php-qrcode/src/Data/Hanzi.php
vendored
12
vendor/chillerlan/php-qrcode/src/Data/Hanzi.php
vendored
@@ -13,7 +13,7 @@ namespace chillerlan\QRCode\Data;
|
||||
use chillerlan\QRCode\Common\{BitBuffer, Mode};
|
||||
use Throwable;
|
||||
use function chr, implode, intdiv, is_string, mb_convert_encoding, mb_detect_encoding,
|
||||
mb_detect_order, mb_internal_encoding, mb_strlen, ord, sprintf, strlen;
|
||||
mb_internal_encoding, mb_strlen, ord, sprintf, strlen;
|
||||
|
||||
/**
|
||||
* Hanzi (simplified Chinese) mode, GBT18284-2000: 13-bit double-byte characters from the GB2312/GB18030 character set
|
||||
@@ -64,11 +64,15 @@ final class Hanzi extends QRDataModeAbstract{
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
* @throws \chillerlan\QRCode\Data\QRCodeDataException
|
||||
*/
|
||||
public static function convertEncoding(string $string):string{
|
||||
mb_detect_order([mb_internal_encoding(), 'UTF-8', 'GB2312', 'GB18030', 'CP936', 'EUC-CN', 'HZ']);
|
||||
|
||||
$detected = mb_detect_encoding($string, null, true);
|
||||
$detected = mb_detect_encoding(
|
||||
$string,
|
||||
[mb_internal_encoding(), 'UTF-8', 'GB2312', 'GB18030', 'CP936', 'EUC-CN', 'HZ'],
|
||||
true,
|
||||
);
|
||||
|
||||
if($detected === false){
|
||||
throw new QRCodeDataException('mb_detect_encoding error');
|
||||
@@ -199,7 +203,7 @@ final class Hanzi extends QRDataModeAbstract{
|
||||
$length--;
|
||||
}
|
||||
|
||||
return mb_convert_encoding(implode($buffer), mb_internal_encoding(), self::ENCODING);
|
||||
return mb_convert_encoding(implode('', $buffer), mb_internal_encoding(), self::ENCODING);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ namespace chillerlan\QRCode\Data;
|
||||
use chillerlan\QRCode\Common\{BitBuffer, Mode};
|
||||
use Throwable;
|
||||
use function chr, implode, intdiv, is_string, mb_convert_encoding, mb_detect_encoding,
|
||||
mb_detect_order, mb_internal_encoding, mb_strlen, ord, sprintf, strlen;
|
||||
mb_internal_encoding, mb_strlen, ord, sprintf, strlen;
|
||||
|
||||
/**
|
||||
* Kanji mode: 13-bit double-byte characters from the Shift-JIS character set
|
||||
@@ -57,11 +57,10 @@ final class Kanji extends QRDataModeAbstract{
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
* @throws \chillerlan\QRCode\Data\QRCodeDataException
|
||||
*/
|
||||
public static function convertEncoding(string $string):string{
|
||||
mb_detect_order([mb_internal_encoding(), 'UTF-8', 'SJIS', 'SJIS-2004']);
|
||||
|
||||
$detected = mb_detect_encoding($string, null, true);
|
||||
$detected = mb_detect_encoding($string, [mb_internal_encoding(), 'UTF-8', 'SJIS', 'SJIS-2004'], true);
|
||||
|
||||
if($detected === false){
|
||||
throw new QRCodeDataException('mb_detect_encoding error');
|
||||
@@ -185,7 +184,7 @@ final class Kanji extends QRDataModeAbstract{
|
||||
$length--;
|
||||
}
|
||||
|
||||
return mb_convert_encoding(implode($buffer), mb_internal_encoding(), self::ENCODING);
|
||||
return mb_convert_encoding(implode('', $buffer), mb_internal_encoding(), self::ENCODING);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
60
vendor/chillerlan/php-qrcode/src/Data/Number.php
vendored
60
vendor/chillerlan/php-qrcode/src/Data/Number.php
vendored
@@ -11,7 +11,7 @@
|
||||
namespace chillerlan\QRCode\Data;
|
||||
|
||||
use chillerlan\QRCode\Common\{BitBuffer, Mode};
|
||||
use function array_flip, ceil, intdiv, str_split, substr, unpack;
|
||||
use function ceil, intdiv, substr, unpack;
|
||||
|
||||
/**
|
||||
* Numeric mode: decimal digits 0 to 9
|
||||
@@ -21,13 +21,6 @@ use function array_flip, ceil, intdiv, str_split, substr, unpack;
|
||||
*/
|
||||
final class Number extends QRDataModeAbstract{
|
||||
|
||||
/**
|
||||
* @var int[]
|
||||
*/
|
||||
private const NUMBER_TO_ORD = [
|
||||
'0' => 0, '1' => 1, '2' => 2, '3' => 3, '4' => 4, '5' => 5, '6' => 6, '7' => 7, '8' => 8, '9' => 9,
|
||||
];
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
@@ -44,18 +37,7 @@ final class Number extends QRDataModeAbstract{
|
||||
* @inheritDoc
|
||||
*/
|
||||
public static function validateString(string $string):bool{
|
||||
|
||||
if($string === ''){
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach(str_split($string) as $chr){
|
||||
if(!isset(self::NUMBER_TO_ORD[$chr])){
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
return (bool)preg_match('/^\d+$/', $string);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -95,12 +77,20 @@ final class Number extends QRDataModeAbstract{
|
||||
|
||||
/**
|
||||
* get the code for the given numeric string
|
||||
*
|
||||
* @throws \chillerlan\QRCode\Data\QRCodeDataException
|
||||
*/
|
||||
private function parseInt(string $string):int{
|
||||
$num = 0;
|
||||
|
||||
foreach(unpack('C*', $string) as $chr){
|
||||
$num = ($num * 10 + $chr - 48);
|
||||
$ords = unpack('C*', $string);
|
||||
|
||||
if($ords === false){
|
||||
throw new QRCodeDataException('unpack() error');
|
||||
}
|
||||
|
||||
foreach($ords as $ord){
|
||||
$num = ($num * 10 + $ord - 48);
|
||||
}
|
||||
|
||||
return $num;
|
||||
@@ -112,19 +102,7 @@ final class Number extends QRDataModeAbstract{
|
||||
* @throws \chillerlan\QRCode\Data\QRCodeDataException
|
||||
*/
|
||||
public static function decodeSegment(BitBuffer $bitBuffer, int $versionNumber):string{
|
||||
$length = $bitBuffer->read(self::getLengthBits($versionNumber));
|
||||
$charmap = array_flip(self::NUMBER_TO_ORD);
|
||||
|
||||
// @todo
|
||||
$toNumericChar = function(int $ord) use ($charmap):string{
|
||||
|
||||
if(isset($charmap[$ord])){
|
||||
return $charmap[$ord];
|
||||
}
|
||||
|
||||
throw new QRCodeDataException('invalid character value: '.$ord);
|
||||
};
|
||||
|
||||
$length = $bitBuffer->read(self::getLengthBits($versionNumber));
|
||||
$result = '';
|
||||
// Read three digits at a time
|
||||
while($length >= 3){
|
||||
@@ -139,9 +117,9 @@ final class Number extends QRDataModeAbstract{
|
||||
throw new QRCodeDataException('error decoding numeric value');
|
||||
}
|
||||
|
||||
$result .= $toNumericChar(intdiv($threeDigitsBits, 100));
|
||||
$result .= $toNumericChar(intdiv($threeDigitsBits, 10) % 10);
|
||||
$result .= $toNumericChar($threeDigitsBits % 10);
|
||||
$result .= intdiv($threeDigitsBits, 100);
|
||||
$result .= (intdiv($threeDigitsBits, 10) % 10);
|
||||
$result .= ($threeDigitsBits % 10);
|
||||
|
||||
$length -= 3;
|
||||
}
|
||||
@@ -158,8 +136,8 @@ final class Number extends QRDataModeAbstract{
|
||||
throw new QRCodeDataException('error decoding numeric value');
|
||||
}
|
||||
|
||||
$result .= $toNumericChar(intdiv($twoDigitsBits, 10));
|
||||
$result .= $toNumericChar($twoDigitsBits % 10);
|
||||
$result .= intdiv($twoDigitsBits, 10);
|
||||
$result .= ($twoDigitsBits % 10);
|
||||
}
|
||||
elseif($length === 1){
|
||||
// One digit left over to read
|
||||
@@ -173,7 +151,7 @@ final class Number extends QRDataModeAbstract{
|
||||
throw new QRCodeDataException('error decoding numeric value');
|
||||
}
|
||||
|
||||
$result .= $toNumericChar($digitBits);
|
||||
$result .= $digitBits;
|
||||
}
|
||||
|
||||
return $result;
|
||||
|
||||
@@ -195,7 +195,7 @@ final class QRData{
|
||||
|
||||
// guess the version number within the given range
|
||||
for($version = $this->options->versionMin; $version <= $this->options->versionMax; $version++){
|
||||
if($total <= $this->maxBitsForEcc[$version]){
|
||||
if($total <= ($this->maxBitsForEcc[$version] - 4)){
|
||||
return new Version($version);
|
||||
}
|
||||
}
|
||||
@@ -207,7 +207,7 @@ final class QRData{
|
||||
/**
|
||||
* creates a BitBuffer and writes the string data to it
|
||||
*
|
||||
* @throws \chillerlan\QRCode\QRCodeException on data overflow
|
||||
* @throws \chillerlan\QRCode\Data\QRCodeDataException on data overflow
|
||||
*/
|
||||
private function writeBitBuffer():void{
|
||||
$MAX_BITS = $this->eccLevel->getMaxBitsForVersion($this->version);
|
||||
|
||||
@@ -175,7 +175,7 @@ class QRMatrix{
|
||||
*
|
||||
* @return int[][]|bool[][]
|
||||
*/
|
||||
public function getMatrix(bool $boolean = null):array{
|
||||
public function getMatrix(?bool $boolean = null):array{
|
||||
|
||||
if($boolean !== true){
|
||||
return $this->matrix;
|
||||
@@ -195,7 +195,7 @@ class QRMatrix{
|
||||
* @see \chillerlan\QRCode\Data\QRMatrix::getMatrix()
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
public function matrix(bool $boolean = null):array{
|
||||
public function matrix(?bool $boolean = null):array{
|
||||
return $this->getMatrix($boolean);
|
||||
}
|
||||
|
||||
@@ -387,7 +387,7 @@ class QRMatrix{
|
||||
* 7 # 3
|
||||
* 6 5 4
|
||||
*/
|
||||
public function checkNeighbours(int $x, int $y, int $M_TYPE = null):int{
|
||||
public function checkNeighbours(int $x, int $y, ?int $M_TYPE = null):int{
|
||||
$bits = 0;
|
||||
|
||||
foreach($this::neighbours as $bit => [$ix, $iy]){
|
||||
@@ -463,6 +463,7 @@ class QRMatrix{
|
||||
|
||||
for($c = 0; $c < 3; $c++){
|
||||
for($i = 0; $i < 8; $i++){
|
||||
// phpcs:ignore
|
||||
$this->set( $h[$c][0] , ($h[$c][1] + $i), false, $this::M_SEPARATOR);
|
||||
$this->set(($v[$c][0] - $i), $v[$c][1] , false, $this::M_SEPARATOR);
|
||||
}
|
||||
@@ -552,7 +553,7 @@ class QRMatrix{
|
||||
*
|
||||
* ISO/IEC 18004:2000 Section 8.9
|
||||
*/
|
||||
public function setFormatInfo(MaskPattern $maskPattern = null):self{
|
||||
public function setFormatInfo(?MaskPattern $maskPattern = null):self{
|
||||
$this->maskPattern = $maskPattern;
|
||||
$bits = 0; // sets all format fields to false (test mode)
|
||||
|
||||
@@ -678,7 +679,7 @@ class QRMatrix{
|
||||
*
|
||||
* @throws \chillerlan\QRCode\Data\QRCodeDataException
|
||||
*/
|
||||
public function setLogoSpace(int $width, int $height = null, int $startX = null, int $startY = null):self{
|
||||
public function setLogoSpace(int $width, ?int $height = null, ?int $startX = null, ?int $startY = null):self{
|
||||
$height ??= $width;
|
||||
|
||||
// if width and height happen to be negative or 0 (default value), just return - nothing to do
|
||||
|
||||
@@ -415,7 +415,7 @@ final class BitMatrix extends QRMatrix{
|
||||
* @codeCoverageIgnore
|
||||
* @throws \chillerlan\QRCode\Data\QRCodeDataException
|
||||
*/
|
||||
public function setQuietZone(int $quietZoneSize = null):self{
|
||||
public function setQuietZone(?int $quietZoneSize = null):self{
|
||||
throw new QRCodeDataException('not supported');
|
||||
}
|
||||
|
||||
@@ -423,7 +423,7 @@ final class BitMatrix extends QRMatrix{
|
||||
* @codeCoverageIgnore
|
||||
* @throws \chillerlan\QRCode\Data\QRCodeDataException
|
||||
*/
|
||||
public function setLogoSpace(int $width, int $height = null, int $startX = null, int $startY = null):self{
|
||||
public function setLogoSpace(int $width, ?int $height = null, ?int $startX = null, ?int $startY = null):self{
|
||||
throw new QRCodeDataException('not supported');
|
||||
}
|
||||
|
||||
|
||||
@@ -29,6 +29,7 @@ final class Decoder{
|
||||
private ?EccLevel $eccLevel = null;
|
||||
private ?MaskPattern $maskPattern = null;
|
||||
private BitBuffer $bitBuffer;
|
||||
private Detector $detector;
|
||||
|
||||
/**
|
||||
* Decodes a QR Code represented as a BitMatrix.
|
||||
@@ -37,7 +38,8 @@ final class Decoder{
|
||||
* @throws \Throwable|\chillerlan\QRCode\Decoder\QRCodeDecoderException
|
||||
*/
|
||||
public function decode(LuminanceSourceInterface $source):DecoderResult{
|
||||
$matrix = (new Detector($source))->detect();
|
||||
$this->detector = new Detector($source);
|
||||
$matrix = $this->detector->detect();
|
||||
|
||||
try{
|
||||
// clone the BitMatrix to avoid errors in case we run into mirroring
|
||||
@@ -148,6 +150,7 @@ final class Decoder{
|
||||
'data' => $result,
|
||||
'version' => $this->version,
|
||||
'eccLevel' => $this->eccLevel,
|
||||
'finderPatterns' => $this->detector->getFinderPatterns(),
|
||||
'maskPattern' => $this->maskPattern,
|
||||
'structuredAppendParity' => $parityData,
|
||||
'structuredAppendSequence' => $symbolSequence,
|
||||
|
||||
@@ -20,13 +20,14 @@ use function property_exists;
|
||||
* applies to 2D barcode formats. For now, it contains the raw bytes obtained
|
||||
* as well as a String interpretation of those bytes, if applicable.
|
||||
*
|
||||
* @property \chillerlan\QRCode\Common\BitBuffer $rawBytes
|
||||
* @property string $data
|
||||
* @property \chillerlan\QRCode\Common\Version $version
|
||||
* @property \chillerlan\QRCode\Common\EccLevel $eccLevel
|
||||
* @property \chillerlan\QRCode\Common\MaskPattern $maskPattern
|
||||
* @property int $structuredAppendParity
|
||||
* @property int $structuredAppendSequence
|
||||
* @property \chillerlan\QRCode\Common\BitBuffer $rawBytes
|
||||
* @property string $data
|
||||
* @property \chillerlan\QRCode\Common\Version $version
|
||||
* @property \chillerlan\QRCode\Common\EccLevel $eccLevel
|
||||
* @property \chillerlan\QRCode\Common\MaskPattern $maskPattern
|
||||
* @property int $structuredAppendParity
|
||||
* @property int $structuredAppendSequence
|
||||
* @property \chillerlan\QRCode\Detector\FinderPattern[] $finderPatterns
|
||||
*/
|
||||
final class DecoderResult{
|
||||
|
||||
@@ -37,11 +38,13 @@ final class DecoderResult{
|
||||
private string $data = '';
|
||||
private int $structuredAppendParity = -1;
|
||||
private int $structuredAppendSequence = -1;
|
||||
/** @var \chillerlan\QRCode\Detector\FinderPattern[] */
|
||||
private array $finderPatterns = [];
|
||||
|
||||
/**
|
||||
* DecoderResult constructor.
|
||||
*/
|
||||
public function __construct(iterable $properties = null){
|
||||
public function __construct(?iterable $properties = null){
|
||||
|
||||
if(!empty($properties)){
|
||||
|
||||
|
||||
@@ -94,7 +94,7 @@ final class ReedSolomonDecoder{
|
||||
while($longerBlocksStartAt >= 0){
|
||||
$numCodewords = count($result[$longerBlocksStartAt][1]);
|
||||
|
||||
if($numCodewords == $shorterBlocksTotalCodewords){
|
||||
if($numCodewords === $shorterBlocksTotalCodewords){
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
@@ -256,7 +256,7 @@ final class AlignmentPatternFinder{
|
||||
$i++;
|
||||
}
|
||||
|
||||
if($i == $maxI || $stateCount[1] > $maxCount){
|
||||
if($i === $maxI || $stateCount[1] > $maxCount){
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -269,6 +269,7 @@ final class AlignmentPatternFinder{
|
||||
return null;
|
||||
}
|
||||
|
||||
// phpcs:ignore
|
||||
if((5 * abs(($stateCount[0] + $stateCount[1] + $stateCount[2]) - $originalStateCountTotal)) >= (2 * $originalStateCountTotal)){
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -25,6 +25,8 @@ use const NAN;
|
||||
final class Detector{
|
||||
|
||||
private BitMatrix $matrix;
|
||||
/** @var \chillerlan\QRCode\Detector\FinderPattern[] */
|
||||
private array $finderPatterns = [];
|
||||
|
||||
/**
|
||||
* Detector constructor.
|
||||
@@ -33,11 +35,20 @@ final class Detector{
|
||||
$this->matrix = (new Binarizer($source))->getBlackMatrix();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \chillerlan\QRCode\Detector\FinderPattern[]
|
||||
*/
|
||||
public function getFinderPatterns():array{
|
||||
return $this->finderPatterns;
|
||||
}
|
||||
|
||||
/**
|
||||
* Detects a QR Code in an image.
|
||||
*/
|
||||
public function detect():BitMatrix{
|
||||
[$bottomLeft, $topLeft, $topRight] = (new FinderPatternFinder($this->matrix))->find();
|
||||
$this->finderPatterns = (new FinderPatternFinder($this->matrix))->find();
|
||||
|
||||
[$bottomLeft, $topLeft, $topRight] = $this->finderPatterns;
|
||||
|
||||
$moduleSize = $this->calculateModuleSize($topLeft, $topRight, $bottomLeft);
|
||||
$dimension = $this->computeDimension($topLeft, $topRight, $bottomLeft, $moduleSize);
|
||||
@@ -305,11 +316,11 @@ final class Detector{
|
||||
*
|
||||
*/
|
||||
private function createTransform(
|
||||
FinderPattern $nw,
|
||||
FinderPattern $ne,
|
||||
FinderPattern $sw,
|
||||
int $size,
|
||||
AlignmentPattern $ap = null
|
||||
FinderPattern $nw,
|
||||
FinderPattern $ne,
|
||||
FinderPattern $sw,
|
||||
int $size,
|
||||
?AlignmentPattern $ap = null
|
||||
):PerspectiveTransform{
|
||||
$dimMinusThree = ($size - 3.5);
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ final class FinderPattern extends ResultPoint{
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public function __construct(float $posX, float $posY, float $estimatedModuleSize, int $count = null){
|
||||
public function __construct(float $posX, float $posY, float $estimatedModuleSize, ?int $count = null){
|
||||
parent::__construct($posX, $posY, $estimatedModuleSize);
|
||||
|
||||
$this->count = ($count ?? 1);
|
||||
|
||||
@@ -290,11 +290,13 @@ final class FinderPatternFinder{
|
||||
|
||||
// Now also count down, right from center
|
||||
$i = 1;
|
||||
// phpcs:ignore
|
||||
while(($centerI + $i) < $dimension && ($centerJ + $i) < $dimension && $this->matrix->check(($centerJ + $i), ($centerI + $i))){
|
||||
$stateCount[2]++;
|
||||
$i++;
|
||||
}
|
||||
|
||||
// phpcs:ignore
|
||||
while(($centerI + $i) < $dimension && ($centerJ + $i) < $dimension && !$this->matrix->check(($centerJ + $i), ($centerI + $i))){
|
||||
$stateCount[3]++;
|
||||
$i++;
|
||||
@@ -304,6 +306,7 @@ final class FinderPatternFinder{
|
||||
return false;
|
||||
}
|
||||
|
||||
// phpcs:ignore
|
||||
while(($centerI + $i) < $dimension && ($centerJ + $i) < $dimension && $this->matrix->check(($centerJ + $i), ($centerI + $i))){
|
||||
$stateCount[4]++;
|
||||
$i++;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user