1631 lines
59 KiB
HTML
1631 lines
59 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="theme-color" content="#0066cc">
|
||
<meta name="description" content="Roll Diameter Calculator - Internal Naue Tool">
|
||
<meta name="apple-mobile-web-app-capable" content="yes">
|
||
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>Roll Diameter Calculator</title>
|
||
<style>
|
||
/* ---- NAUE BASE STYLES ---- */
|
||
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
||
body {
|
||
font-family: 'Segoe UI', Arial, sans-serif;
|
||
background: #f4f6f9;
|
||
color: #333;
|
||
min-height: 100vh;
|
||
}
|
||
|
||
/* ---- DISCLAIMER MODAL ---- */
|
||
#disclaimerOverlay {
|
||
position: fixed; inset: 0;
|
||
background: rgba(0,0,0,0.7);
|
||
z-index: 9999;
|
||
display: flex; align-items: center; justify-content: center;
|
||
padding: 20px;
|
||
}
|
||
#disclaimerBox {
|
||
background: #fff;
|
||
border-radius: 10px;
|
||
max-width: 680px;
|
||
width: 100%;
|
||
padding: 32px;
|
||
box-shadow: 0 8px 40px rgba(0,0,0,0.3);
|
||
}
|
||
.disclaimer-badge {
|
||
display: inline-block;
|
||
background: #ff6600;
|
||
color: #fff;
|
||
font-size: 11px;
|
||
font-weight: 700;
|
||
padding: 3px 10px;
|
||
border-radius: 4px;
|
||
letter-spacing: 1px;
|
||
margin-bottom: 12px;
|
||
}
|
||
#disclaimerBox h2 {
|
||
font-size: 18px;
|
||
color: #003366;
|
||
margin-bottom: 20px;
|
||
}
|
||
.disclaimer-section {
|
||
background: #f8f9fa;
|
||
border-left: 4px solid #0066cc;
|
||
border-radius: 4px;
|
||
padding: 14px 16px;
|
||
margin-bottom: 14px;
|
||
font-size: 13px;
|
||
line-height: 1.6;
|
||
}
|
||
.disclaimer-section h4 {
|
||
font-size: 13px;
|
||
margin-bottom: 6px;
|
||
color: #333;
|
||
}
|
||
.disclaimer-confirm {
|
||
margin: 18px 0 20px;
|
||
font-size: 13px;
|
||
display: flex;
|
||
align-items: flex-start;
|
||
gap: 10px;
|
||
}
|
||
.disclaimer-confirm input[type=checkbox] {
|
||
margin-top: 2px;
|
||
width: 16px; height: 16px;
|
||
flex-shrink: 0;
|
||
}
|
||
.disclaimer-buttons {
|
||
display: flex;
|
||
gap: 12px;
|
||
justify-content: flex-end;
|
||
}
|
||
#btnCancel {
|
||
padding: 10px 22px;
|
||
border: 2px solid #ccc;
|
||
background: #fff;
|
||
color: #666;
|
||
border-radius: 5px;
|
||
cursor: pointer;
|
||
font-size: 13px;
|
||
}
|
||
#btnAccept {
|
||
padding: 10px 22px;
|
||
border: none;
|
||
background: #0066cc;
|
||
color: #fff;
|
||
border-radius: 5px;
|
||
cursor: pointer;
|
||
font-size: 13px;
|
||
font-weight: 600;
|
||
}
|
||
#btnAccept:disabled {
|
||
background: #aaa;
|
||
cursor: not-allowed;
|
||
}
|
||
#btnAccept:not(:disabled):hover { background: #0052a3; }
|
||
|
||
/* ---- APP HEADER ---- */
|
||
.app-header {
|
||
background: linear-gradient(135deg, #003366 0%, #0066cc 100%);
|
||
color: #fff;
|
||
padding: 18px 32px;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 16px;
|
||
box-shadow: 0 2px 8px rgba(0,0,0,0.2);
|
||
}
|
||
.app-header .logo-text {
|
||
font-size: 20px;
|
||
font-weight: 700;
|
||
letter-spacing: 1px;
|
||
}
|
||
.app-header .subtitle {
|
||
font-size: 13px;
|
||
opacity: 0.85;
|
||
}
|
||
.beta-pill {
|
||
background: #ff6600;
|
||
color: #fff;
|
||
font-size: 10px;
|
||
font-weight: 700;
|
||
padding: 2px 8px;
|
||
border-radius: 20px;
|
||
letter-spacing: 1px;
|
||
margin-left: 8px;
|
||
}
|
||
.internal-pill {
|
||
background: rgba(255,255,255,0.2);
|
||
color: #fff;
|
||
font-size: 10px;
|
||
font-weight: 600;
|
||
padding: 2px 8px;
|
||
border-radius: 20px;
|
||
letter-spacing: 0.5px;
|
||
margin-left: 4px;
|
||
}
|
||
|
||
/* ---- MAIN LAYOUT ---- */
|
||
.app-main {
|
||
max-width: 920px;
|
||
margin: 30px auto;
|
||
padding: 0 16px 60px;
|
||
}
|
||
|
||
/* ---- CARD ---- */
|
||
.card {
|
||
background: #fff;
|
||
border-radius: 10px;
|
||
box-shadow: 0 2px 12px rgba(0,0,0,0.07);
|
||
overflow: hidden;
|
||
margin-bottom: 24px;
|
||
}
|
||
.card-header {
|
||
background: #f0f4fa;
|
||
padding: 14px 22px;
|
||
border-bottom: 1px solid #e0e8f0;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 10px;
|
||
}
|
||
.card-header h2 {
|
||
font-size: 15px;
|
||
color: #003366;
|
||
font-weight: 700;
|
||
}
|
||
.card-body {
|
||
padding: 22px;
|
||
}
|
||
|
||
/* ---- TABS ---- */
|
||
.tab-bar {
|
||
display: flex;
|
||
border-bottom: 2px solid #e0e8f0;
|
||
margin-bottom: 24px;
|
||
}
|
||
.tab-btn {
|
||
padding: 10px 20px;
|
||
border: none;
|
||
background: none;
|
||
cursor: pointer;
|
||
font-size: 14px;
|
||
color: #666;
|
||
border-bottom: 3px solid transparent;
|
||
margin-bottom: -2px;
|
||
transition: all 0.2s;
|
||
font-weight: 500;
|
||
}
|
||
.tab-btn.active {
|
||
color: #0066cc;
|
||
border-bottom-color: #0066cc;
|
||
font-weight: 700;
|
||
}
|
||
.tab-btn:hover:not(.active) { color: #333; }
|
||
.tab-pane { display: none; }
|
||
.tab-pane.active { display: block; }
|
||
|
||
/* ---- FORM ELEMENTS ---- */
|
||
.form-row {
|
||
display: grid;
|
||
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
||
gap: 16px;
|
||
margin-bottom: 16px;
|
||
}
|
||
.form-group {
|
||
display: flex;
|
||
flex-direction: column;
|
||
}
|
||
.form-group label {
|
||
font-size: 12px;
|
||
font-weight: 600;
|
||
color: #555;
|
||
margin-bottom: 5px;
|
||
text-transform: uppercase;
|
||
letter-spacing: 0.4px;
|
||
}
|
||
.form-group input,
|
||
.form-group select {
|
||
padding: 9px 11px;
|
||
border: 1px solid #ccc;
|
||
border-radius: 5px;
|
||
font-size: 14px;
|
||
transition: border 0.2s;
|
||
}
|
||
.form-group input:focus,
|
||
.form-group select:focus {
|
||
outline: none;
|
||
border-color: #0066cc;
|
||
box-shadow: 0 0 0 3px rgba(0,102,204,0.1);
|
||
}
|
||
.input-with-unit {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 6px;
|
||
}
|
||
.input-with-unit input { flex: 1; }
|
||
.unit-label {
|
||
font-size: 12px;
|
||
color: #888;
|
||
white-space: nowrap;
|
||
}
|
||
.tol-row {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
}
|
||
.tol-row input { width: 80px; }
|
||
|
||
/* ---- MODE SELECTOR ---- */
|
||
.mode-selector {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
gap: 8px;
|
||
margin-bottom: 20px;
|
||
}
|
||
.mode-btn {
|
||
padding: 7px 16px;
|
||
border: 2px solid #0066cc;
|
||
background: #fff;
|
||
color: #0066cc;
|
||
border-radius: 5px;
|
||
cursor: pointer;
|
||
font-size: 13px;
|
||
font-weight: 600;
|
||
transition: all 0.2s;
|
||
}
|
||
.mode-btn.active {
|
||
background: #0066cc;
|
||
color: #fff;
|
||
}
|
||
.mode-btn:hover:not(.active) {
|
||
background: #e8f0fb;
|
||
}
|
||
|
||
/* ---- FORMULA BOX ---- */
|
||
.formula-box {
|
||
background: #f8f9fa;
|
||
border: 1px solid #e0e0e0;
|
||
border-radius: 6px;
|
||
padding: 10px 14px;
|
||
font-size: 12px;
|
||
color: #666;
|
||
margin-top: 18px;
|
||
font-family: 'Courier New', monospace;
|
||
}
|
||
|
||
/* ---- RESULT DISPLAY ---- */
|
||
.result-box {
|
||
background: linear-gradient(135deg, #e8f4fd 0%, #d0e8fa 100%);
|
||
border: 2px solid #0066cc;
|
||
border-radius: 8px;
|
||
padding: 16px 20px;
|
||
margin-bottom: 18px;
|
||
}
|
||
.result-box .result-label {
|
||
font-size: 12px;
|
||
color: #555;
|
||
font-weight: 600;
|
||
text-transform: uppercase;
|
||
letter-spacing: 0.4px;
|
||
}
|
||
.result-box .result-value {
|
||
font-size: 28px;
|
||
font-weight: 700;
|
||
color: #003366;
|
||
line-height: 1.2;
|
||
}
|
||
.result-box .result-range {
|
||
font-size: 12px;
|
||
color: #666;
|
||
margin-top: 4px;
|
||
}
|
||
|
||
/* ---- DIAGRAM ---- */
|
||
.diagram-row {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
gap: 24px;
|
||
margin: 18px 0;
|
||
}
|
||
.diagram-svg-wrap {
|
||
flex-shrink: 0;
|
||
}
|
||
.diagram-legend {
|
||
font-size: 12px;
|
||
color: #555;
|
||
line-height: 1.8;
|
||
}
|
||
|
||
/* ---- BUTTONS ---- */
|
||
.btn-calc {
|
||
background: #0066cc;
|
||
color: #fff;
|
||
border: none;
|
||
border-radius: 6px;
|
||
padding: 11px 28px;
|
||
font-size: 14px;
|
||
font-weight: 700;
|
||
cursor: pointer;
|
||
transition: background 0.2s, box-shadow 0.2s;
|
||
}
|
||
.btn-calc:hover { background: #0052a3; box-shadow: 0 2px 8px rgba(0,102,204,0.3); }
|
||
.btn-reset {
|
||
background: #e0e0e0;
|
||
color: #444;
|
||
border: none;
|
||
border-radius: 6px;
|
||
padding: 11px 20px;
|
||
font-size: 14px;
|
||
cursor: pointer;
|
||
transition: background 0.2s;
|
||
margin-left: 10px;
|
||
}
|
||
.btn-reset:hover { background: #d0d0d0; }
|
||
|
||
/* ---- INFO NOTE ---- */
|
||
.info-note {
|
||
background: #fffbea;
|
||
border-left: 4px solid #f0b429;
|
||
padding: 12px 16px;
|
||
border-radius: 4px;
|
||
font-size: 12px;
|
||
color: #666;
|
||
line-height: 1.6;
|
||
margin-top: 18px;
|
||
}
|
||
|
||
/* ---- SECTION SEPARATOR ---- */
|
||
.section-sep {
|
||
border: none;
|
||
border-top: 1px solid #e8ecf0;
|
||
margin: 20px 0;
|
||
}
|
||
|
||
/* ---- WEIGHT SECTION ---- */
|
||
.weight-section {
|
||
background: #f8fafa;
|
||
border: 1px dashed #aac;
|
||
border-radius: 6px;
|
||
padding: 14px 18px;
|
||
margin-top: 18px;
|
||
}
|
||
.weight-section h4 {
|
||
font-size: 13px;
|
||
color: #555;
|
||
margin-bottom: 12px;
|
||
}
|
||
|
||
/* ---- APP FOOTER ---- */
|
||
.app-footer {
|
||
text-align: center;
|
||
font-size: 11px;
|
||
color: #aaa;
|
||
padding: 18px;
|
||
border-top: 1px solid #e8ecf0;
|
||
margin-top: 10px;
|
||
}
|
||
|
||
/* ============================================
|
||
LOAD OPTIMIZER STYLES
|
||
============================================ */
|
||
.load-optimizer-section {
|
||
margin-top: 30px;
|
||
border: 2px solid #0066cc;
|
||
border-radius: 8px;
|
||
background-color: #f0f7ff;
|
||
overflow: hidden;
|
||
box-shadow: 0 2px 8px rgba(0,102,204,0.1);
|
||
}
|
||
.lo-accordion-header {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 12px;
|
||
padding: 16px 20px;
|
||
background: linear-gradient(135deg, #0066cc 0%, #004499 100%);
|
||
color: white;
|
||
cursor: pointer;
|
||
user-select: none;
|
||
transition: background 0.3s ease;
|
||
}
|
||
.lo-accordion-header:hover {
|
||
background: linear-gradient(135deg, #0052a3 0%, #003d7a 100%);
|
||
}
|
||
.lo-accordion-header h3 {
|
||
margin: 0;
|
||
flex: 1;
|
||
font-size: 16px;
|
||
font-weight: 600;
|
||
}
|
||
.lo-accordion-icon {
|
||
font-size: 14px;
|
||
transition: transform 0.3s ease;
|
||
display: inline-block;
|
||
}
|
||
.lo-accordion-header.active .lo-accordion-icon {
|
||
transform: rotate(180deg);
|
||
}
|
||
.badge-beta-lo {
|
||
display: inline-block;
|
||
padding: 4px 8px;
|
||
border-radius: 4px;
|
||
font-size: 11px;
|
||
font-weight: bold;
|
||
background-color: rgba(255,255,255,0.3);
|
||
color: white;
|
||
}
|
||
.lo-accordion-content {
|
||
padding: 20px;
|
||
}
|
||
@keyframes loSlideDown {
|
||
from { opacity: 0; }
|
||
to { opacity: 1; }
|
||
}
|
||
.lo-accordion-content { animation: loSlideDown 0.3s ease; }
|
||
.lo-inputs {
|
||
display: grid;
|
||
gap: 20px;
|
||
margin-bottom: 30px;
|
||
}
|
||
.lo-presets-section, .lo-custom-section {
|
||
background: white;
|
||
padding: 16px;
|
||
border-radius: 6px;
|
||
border: 1px solid #ddd;
|
||
}
|
||
.lo-presets-section h4, .lo-custom-section h4 {
|
||
margin-top: 0;
|
||
color: #333;
|
||
font-size: 14px;
|
||
margin-bottom: 12px;
|
||
}
|
||
.lo-preset-buttons {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
gap: 10px;
|
||
}
|
||
.lo-preset-btn {
|
||
padding: 8px 16px;
|
||
border: 2px solid #0066cc;
|
||
background: white;
|
||
color: #0066cc;
|
||
border-radius: 4px;
|
||
cursor: pointer;
|
||
font-weight: 500;
|
||
transition: all 0.2s;
|
||
}
|
||
.lo-preset-btn:hover { background: #0066cc; color: white; }
|
||
.lo-preset-btn.active {
|
||
background: #0066cc;
|
||
color: white;
|
||
box-shadow: 0 0 0 3px rgba(0,102,204,0.2);
|
||
}
|
||
.lo-input-grid {
|
||
display: grid;
|
||
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
|
||
gap: 12px;
|
||
}
|
||
.lo-input-field { display: flex; flex-direction: column; }
|
||
.lo-input-field label {
|
||
font-size: 12px;
|
||
font-weight: 600;
|
||
color: #666;
|
||
margin-bottom: 4px;
|
||
}
|
||
.lo-input-field input {
|
||
padding: 8px;
|
||
border: 1px solid #ccc;
|
||
border-radius: 4px;
|
||
font-size: 13px;
|
||
}
|
||
.lo-input-field input:focus {
|
||
outline: none;
|
||
border-color: #0066cc;
|
||
box-shadow: 0 0 0 2px rgba(0,102,204,0.1);
|
||
}
|
||
.lo-button-group { display: flex; gap: 12px; }
|
||
.lo-btn-primary, .lo-btn-secondary {
|
||
flex: 1;
|
||
padding: 12px 16px;
|
||
border: none;
|
||
border-radius: 4px;
|
||
font-size: 14px;
|
||
font-weight: 600;
|
||
cursor: pointer;
|
||
transition: all 0.2s;
|
||
}
|
||
.lo-btn-primary { background: #0066cc; color: white; }
|
||
.lo-btn-primary:hover { background: #0052a3; box-shadow: 0 2px 6px rgba(0,102,204,0.3); }
|
||
.lo-btn-secondary { background: #e0e0e0; color: #333; }
|
||
.lo-btn-secondary:hover { background: #d0d0d0; }
|
||
.lo-results { display: grid; gap: 20px; }
|
||
.lo-results-summary {
|
||
background: white;
|
||
padding: 16px;
|
||
border-radius: 6px;
|
||
border-left: 4px solid #28a745;
|
||
}
|
||
.lo-results-summary h4 { margin-top: 0; color: #333; font-size: 14px; margin-bottom: 12px; }
|
||
.lo-summary-item {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: flex-start;
|
||
margin-bottom: 12px;
|
||
font-size: 13px;
|
||
}
|
||
.lo-summary-item:last-child { margin-bottom: 0; }
|
||
.lo-value-highlight { color: #0066cc; font-weight: bold; font-size: 16px; }
|
||
.lo-value-number { color: #28a745; font-weight: bold; }
|
||
.lo-util-group { display: flex; flex-direction: column; gap: 4px; text-align: right; }
|
||
.lo-results-table {
|
||
background: white;
|
||
padding: 16px;
|
||
border-radius: 6px;
|
||
overflow-x: auto;
|
||
}
|
||
.lo-results-table h4 { margin-top: 0; margin-bottom: 12px; }
|
||
.lo-results-table table { width: 100%; border-collapse: collapse; font-size: 12px; }
|
||
.lo-results-table thead { background: #f5f5f5; }
|
||
.lo-results-table th {
|
||
padding: 8px;
|
||
text-align: left;
|
||
font-weight: 600;
|
||
color: #333;
|
||
border-bottom: 2px solid #0066cc;
|
||
}
|
||
.lo-results-table td { padding: 8px; border-bottom: 1px solid #e0e0e0; }
|
||
.lo-results-table tbody tr:hover { background: #f9f9f9; }
|
||
.lo-results-table tbody tr.lo-recommended { background: #fffacd; font-weight: bold; }
|
||
.lo-warning {
|
||
background: #fff8e1;
|
||
border: 1px solid #f0b429;
|
||
border-radius: 5px;
|
||
padding: 10px 14px;
|
||
font-size: 12px;
|
||
color: #7a5c00;
|
||
margin-bottom: 12px;
|
||
}
|
||
|
||
@media (max-width: 768px) {
|
||
.lo-accordion-header h3 { font-size: 14px; }
|
||
.lo-preset-buttons { flex-direction: column; }
|
||
.lo-preset-btn { width: 100%; }
|
||
.lo-input-grid { grid-template-columns: 1fr; }
|
||
.lo-button-group { flex-direction: column; }
|
||
.form-row { grid-template-columns: 1fr; }
|
||
}
|
||
/* ===== SIDE VIEW ===== */
|
||
.lo-sideview-section {
|
||
background: white; padding: 16px; border-radius: 6px; border: 1px solid #ddd;
|
||
}
|
||
.lo-sideview-section h4 { margin: 0 0 12px 0; font-size: 14px; color: #333; }
|
||
.lo-sideview-wrap {
|
||
background: #f8f9fa; border: 1px solid #e0e0e0; border-radius: 4px; overflow: hidden;
|
||
}
|
||
.lo-sideview-legend { display:flex; gap:20px; margin-top:10px; font-size:12px; color:#555; }
|
||
.lo-legend-item { display:flex; align-items:center; gap:6px; }
|
||
.lo-legend-box { width:14px; height:14px; border-radius:2px; display:inline-block; border:1px solid #aaa; }
|
||
.roll-color { background:#a8d4f5; border-color:#0066cc; }
|
||
.container-color { background:#e8e8e8; border-color:#999; }
|
||
.core-presets { display:flex; gap:5px; margin-top:6px; flex-wrap:wrap; }
|
||
.core-preset-btn { padding:3px 9px; font-size:11px; font-weight:600; border:1px solid #0066cc; background:#fff; color:#0066cc; border-radius:4px; cursor:pointer; transition:all 0.15s; }
|
||
.core-preset-btn:hover, .core-preset-btn.active { background:#0066cc; color:#fff; }
|
||
</style>
|
||
</head>
|
||
<body>
|
||
|
||
<!-- ============================================================
|
||
DISCLAIMER MODAL
|
||
============================================================ -->
|
||
<div id="disclaimerOverlay">
|
||
<div id="disclaimerBox">
|
||
<div class="disclaimer-badge">⚠ BETA VERSION · INTERNAL USE ONLY</div>
|
||
<h2>Roll Diameter Calculator — Naue GmbH & Co. KG</h2>
|
||
|
||
<div class="disclaimer-section">
|
||
<h4>🔒 Authorized Departments Only / Nur autorisierte Abteilungen</h4>
|
||
This tool is exclusively intended for internal use by:<br>
|
||
<strong>Production Planning · Logistics · Product Management</strong><br><br>
|
||
Dieses Tool ist ausschließlich für den internen Gebrauch in:<br>
|
||
<strong>Produktionsplanung · Logistik · Produktmanagement</strong>
|
||
</div>
|
||
|
||
<div class="disclaimer-section">
|
||
<h4>🧪 Beta-Status</h4>
|
||
This application is in Beta. Results should be verified before operational use.<br>
|
||
Diese Anwendung befindet sich im Beta-Stadium. Ergebnisse sind vor dem produktiven Einsatz zu überprüfen.
|
||
</div>
|
||
|
||
<div class="disclaimer-section">
|
||
<h4>📊 Product Thickness Values</h4>
|
||
Pre-filled product thickness values are derived from an automated data evaluation and serve as a starting point only.
|
||
They must be independently verified before use in any calculation.<br>
|
||
Die vorgeschlagenen Produktdicken stammen aus einer automatischen Datenauswertung und müssen vor Verwendung in Berechnungen eigenständig überprüft werden.
|
||
</div>
|
||
|
||
<div class="disclaimer-confirm">
|
||
<input type="checkbox" id="confirmCheck">
|
||
<label for="confirmCheck">
|
||
Ich bestätige, dass ich zur Nutzung dieses Tools berechtigt bin und die obigen Hinweise gelesen und verstanden habe.<br>
|
||
<em>I confirm that I am authorised to use this tool and have read and understood the notices above.</em>
|
||
</label>
|
||
</div>
|
||
|
||
<div class="disclaimer-buttons">
|
||
<button id="btnCancel">✕ Abbrechen / Cancel</button>
|
||
<button id="btnAccept" disabled>✓ Akzeptieren & Weiter / Accept & Continue</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ============================================================
|
||
APP HEADER
|
||
============================================================ -->
|
||
<div class="app-header">
|
||
<div>
|
||
<div class="logo-text">
|
||
Naue
|
||
<span class="beta-pill">BETA</span>
|
||
<span class="internal-pill">Internal</span>
|
||
</div>
|
||
<div class="subtitle">Roll Diameter Calculator · Roll Diameter · Product Length · Roll Weight</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ============================================================
|
||
MAIN
|
||
============================================================ -->
|
||
<div class="app-main">
|
||
|
||
<div class="card">
|
||
<div class="card-header">
|
||
<h2>🧮 Roll Diameter Calculator</h2>
|
||
</div>
|
||
<div class="card-body">
|
||
|
||
<!-- TABS -->
|
||
<div class="tab-bar">
|
||
<button class="tab-btn active" data-tab="direct">🧮 Direct Calculation</button>
|
||
<button class="tab-btn" data-tab="extrap">📐 Extrapolation</button>
|
||
</div>
|
||
|
||
<!-- ===================== TAB: DIRECT CALCULATION ===================== -->
|
||
<div class="tab-pane active" id="tab-direct">
|
||
|
||
<!-- Product Category + Site -->
|
||
<div class="form-row">
|
||
<div class="form-group">
|
||
<label>Product Category</label>
|
||
<select id="d-cat">
|
||
<option value="">— Select —</option>
|
||
<option value="bentofix">Bentofix</option>
|
||
<option value="secutex">Secutex</option>
|
||
<option value="carbofol">Carbofol</option>
|
||
<option value="secumat">Secumat</option>
|
||
<option value="secudrain">Secudrain</option>
|
||
</select>
|
||
</div>
|
||
<div class="form-group">
|
||
<label>Production Site</label>
|
||
<select id="d-site">
|
||
<option value="">— Select —</option>
|
||
<option value="espelkamp">Espelkamp</option>
|
||
<option value="malaysia">Malaysia</option>
|
||
</select>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Calculation Mode -->
|
||
<div class="form-group" style="margin-bottom:14px;">
|
||
<label>Calculation Mode</label>
|
||
<div class="mode-selector">
|
||
<button class="mode-btn active" data-mode="diameter">→ Roll Diameter</button>
|
||
<button class="mode-btn" data-mode="length">→ Product Length</button>
|
||
<button class="mode-btn" data-mode="target">🎯 Target Diameter</button>
|
||
<button class="mode-btn" data-mode="thickness">→ Product Thickness</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Diagram -->
|
||
<div class="diagram-row">
|
||
<div class="diagram-svg-wrap">
|
||
<svg width="120" height="120" viewBox="0 0 120 120">
|
||
<circle cx="60" cy="60" r="50" fill="#d0e8fa" stroke="#0066cc" stroke-width="2"/>
|
||
<circle cx="60" cy="60" r="20" fill="#fff" stroke="#666" stroke-width="2" stroke-dasharray="4,3"/>
|
||
<line x1="60" y1="60" x2="110" y2="60" stroke="#0066cc" stroke-width="1.5"/>
|
||
<line x1="60" y1="60" x2="80" y2="60" stroke="#666" stroke-width="1.5"/>
|
||
<text x="82" y="56" font-size="9" fill="#0066cc">D</text>
|
||
<text x="68" y="56" font-size="9" fill="#666">d</text>
|
||
</svg>
|
||
</div>
|
||
<div class="diagram-legend">
|
||
<div><strong>D</strong> = Roll outer diameter</div>
|
||
<div><strong>d</strong> = Core outer diameter</div>
|
||
<div><strong>t</strong> = Product thickness</div>
|
||
<div><strong>L</strong> = Product length</div>
|
||
</div>
|
||
</div>
|
||
|
||
<hr class="section-sep">
|
||
|
||
<!-- Input Parameters -->
|
||
<h4 style="font-size:13px;color:#555;margin-bottom:14px;">Input Parameters</h4>
|
||
|
||
<div class="form-row">
|
||
<div class="form-group">
|
||
<label>Core Outer Diameter (d)</label>
|
||
<div class="input-with-unit">
|
||
<input type="number" id="d-co" value="150" min="0" step="1">
|
||
<span class="unit-label">mm</span>
|
||
</div>
|
||
<div class="core-presets">
|
||
<button class="core-preset-btn" data-core="76">76</button>
|
||
<button class="core-preset-btn" data-core="150">150</button>
|
||
<button class="core-preset-btn" data-core="168">168</button>
|
||
<button class="core-preset-btn" data-core="200">200</button>
|
||
<button class="core-preset-btn" data-core="300">300</button>
|
||
</div>
|
||
</div>
|
||
<div class="form-group">
|
||
<label>Article Number (optional)</label>
|
||
<input type="text" id="d-art" list="d-art-list" placeholder="Search article no. or name..." autocomplete="off" style="width:100%;">
|
||
<datalist id="d-art-list"></datalist>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="form-row">
|
||
<div class="form-group">
|
||
<label>Product Thickness (t)</label>
|
||
<div class="tol-row">
|
||
<input type="number" id="d-th" placeholder="mm" min="0" step="0.01">
|
||
<span class="unit-label">±</span>
|
||
<input type="number" id="d-tol" placeholder="mm" min="0" step="0.01" style="width:80px;">
|
||
<span class="unit-label">mm</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="form-row">
|
||
<div class="form-group">
|
||
<label>Product Length (L)</label>
|
||
<div class="input-with-unit">
|
||
<input type="number" id="d-le" placeholder="m" min="0" step="0.1">
|
||
<span class="unit-label">m</span>
|
||
</div>
|
||
</div>
|
||
<div class="form-group">
|
||
<label>Roll Diameter (D)</label>
|
||
<div class="input-with-unit">
|
||
<input type="number" id="d-di" placeholder="mm" min="0" step="1">
|
||
<span class="unit-label">mm</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Weight (optional) -->
|
||
<div class="weight-section">
|
||
<h4>Roll Weight (optional)</h4>
|
||
<div class="form-row">
|
||
<div class="form-group">
|
||
<label>Roll Width</label>
|
||
<div class="input-with-unit">
|
||
<input type="number" id="de-wi" placeholder="m" min="0" step="0.01">
|
||
<span class="unit-label">m</span>
|
||
</div>
|
||
</div>
|
||
<div class="form-group">
|
||
<label>Area Weight</label>
|
||
<div class="input-with-unit">
|
||
<input type="number" id="d-aw" placeholder="g/m²" min="0" step="1">
|
||
<span class="unit-label">g/m²</span>
|
||
</div>
|
||
</div>
|
||
<div class="form-group">
|
||
<label>Calculated Weight</label>
|
||
<div class="input-with-unit">
|
||
<input type="text" id="d-wo" readonly placeholder="—" style="background:#f0f0f0;">
|
||
<span class="unit-label">kg</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div style="margin-top:18px;">
|
||
<button class="btn-calc" id="d-calc">► Calculate</button>
|
||
<button class="btn-reset" id="d-reset">Reset</button>
|
||
</div>
|
||
|
||
<!-- Result -->
|
||
<div id="d-result" style="display:none; margin-top:20px;">
|
||
<div class="result-box">
|
||
<div class="result-label" id="d-res-label">Result</div>
|
||
<div class="result-value" id="d-res-val">—</div>
|
||
<div class="result-range" id="d-res-range"></div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="formula-box">
|
||
Geometry: π/4 × (D² − d²) = L × t | Weight: G [kg] = Area Weight [g/m²] × L [m] × Width [m] / 1000
|
||
</div>
|
||
|
||
</div>
|
||
<!-- END TAB DIRECT -->
|
||
|
||
<!-- ===================== TAB: EXTRAPOLATION ===================== -->
|
||
<div class="tab-pane" id="tab-extrap">
|
||
|
||
<h4 style="font-size:13px;color:#555;margin-bottom:14px;">Known Reference Roll</h4>
|
||
|
||
<div class="form-row">
|
||
<div class="form-group">
|
||
<label>Article Number (optional)</label>
|
||
<input type="text" id="e-art" list="e-art-list" placeholder="Search article no. or name..." autocomplete="off" style="width:100%;">
|
||
<datalist id="e-art-list"></datalist>
|
||
</div>
|
||
<div class="form-group">
|
||
<label>Core Outer Diameter (d)</label>
|
||
<div class="input-with-unit">
|
||
<input type="number" id="e-co" value="150" min="0" step="1">
|
||
<span class="unit-label">mm</span>
|
||
</div>
|
||
<div class="core-presets">
|
||
<button class="core-preset-btn" data-core="76">76</button>
|
||
<button class="core-preset-btn" data-core="150">150</button>
|
||
<button class="core-preset-btn" data-core="168">168</button>
|
||
<button class="core-preset-btn" data-core="200">200</button>
|
||
<button class="core-preset-btn" data-core="300">300</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="form-row">
|
||
<div class="form-group">
|
||
<label>Known Roll Diameter (D₀)</label>
|
||
<div class="input-with-unit">
|
||
<input type="number" id="e-d0" placeholder="mm" min="0" step="1">
|
||
<span class="unit-label">mm</span>
|
||
</div>
|
||
</div>
|
||
<div class="form-group">
|
||
<label>Known Roll Length (L₀)</label>
|
||
<div class="input-with-unit">
|
||
<input type="number" id="e-l0" placeholder="m" min="0" step="0.1">
|
||
<span class="unit-label">m</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<hr class="section-sep">
|
||
<h4 style="font-size:13px;color:#555;margin-bottom:14px;">New Roll Length</h4>
|
||
|
||
<div class="form-row">
|
||
<div class="form-group">
|
||
<label>New Roll Length (L₁)</label>
|
||
<div class="input-with-unit">
|
||
<input type="number" id="e-l1" placeholder="m" min="0" step="0.1">
|
||
<span class="unit-label">m</span>
|
||
</div>
|
||
</div>
|
||
<div class="form-group">
|
||
<label>Tolerance (optional)</label>
|
||
<div class="tol-row">
|
||
<span class="unit-label">±</span>
|
||
<input type="number" id="e-tol" placeholder="mm" min="0" step="0.1" style="width:100px;">
|
||
<span class="unit-label">mm</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Weight (optional) -->
|
||
<div class="weight-section">
|
||
<h4>Roll Weight (optional)</h4>
|
||
<div class="form-row">
|
||
<div class="form-group">
|
||
<label>Roll Width</label>
|
||
<div class="input-with-unit">
|
||
<input type="number" id="e-wi" placeholder="m" min="0" step="0.01">
|
||
<span class="unit-label">m</span>
|
||
</div>
|
||
</div>
|
||
<div class="form-group">
|
||
<label>Area Weight</label>
|
||
<div class="input-with-unit">
|
||
<input type="number" id="e-aw" placeholder="g/m²" min="0" step="1">
|
||
<span class="unit-label">g/m²</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div style="margin-top:18px;">
|
||
<button class="btn-calc" id="e-calc">► Calculate Roll Diameter</button>
|
||
<button class="btn-reset" id="e-reset">Reset</button>
|
||
</div>
|
||
|
||
<!-- Result -->
|
||
<div id="e-result" style="display:none; margin-top:20px;">
|
||
<div class="result-box">
|
||
<div class="result-label">Extrapolated Roll Diameter (D₁)</div>
|
||
<div class="result-value" id="e-res-val">—</div>
|
||
<div class="result-range" id="e-res-range"></div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="formula-box">
|
||
Geometry: D₁ = √(d² + L₁/L₀ × (D₀² − d²)) | Weight: G [kg] = Area Weight × L₁ × Width / 1000
|
||
</div>
|
||
|
||
</div>
|
||
<!-- END TAB EXTRAP -->
|
||
|
||
</div><!-- card-body -->
|
||
</div><!-- card -->
|
||
|
||
<!-- INFO NOTE -->
|
||
<div class="info-note">
|
||
<strong>ⓘ Calculation Notes</strong><br>
|
||
The calculated roll diameters assume a perfectly cylindrical roll with uniform, gap-free winding.<br><br>
|
||
The product thickness can vary during production within the permitted manufacturing tolerance.
|
||
The actual roll diameter may therefore differ from the calculated values.<br><br>
|
||
Use the <em>± Tolerance</em> field next to the product thickness to estimate this effect — the result will then show the minimum, nominal and maximum roll diameter.
|
||
</div>
|
||
|
||
<!-- ============================================================
|
||
LOAD OPTIMIZER SECTION
|
||
============================================================ -->
|
||
<div id="loadOptimizerContainer" class="load-optimizer-section">
|
||
|
||
<div class="lo-accordion-header" id="loAccordionToggle">
|
||
<span class="lo-accordion-icon">▼</span>
|
||
<h3>🎯 Load Optimizer — Transport Capacity Calculator</h3>
|
||
<span class="badge-beta-lo">BETA</span>
|
||
</div>
|
||
|
||
<div class="lo-accordion-content" id="loAccordionContent" style="display:none;">
|
||
|
||
<div class="lo-warning">
|
||
⚠ The Load Optimizer uses roll data from the <strong>Direct Calculation</strong> tab above.
|
||
Please complete a roll calculation first (Roll Diameter + Roll Width + Roll Weight must be filled).
|
||
</div>
|
||
|
||
<div class="lo-inputs">
|
||
|
||
<div class="lo-presets-section">
|
||
<h4>📘 Preset Transport Type:</h4>
|
||
<div class="lo-preset-buttons">
|
||
<button class="lo-preset-btn" data-preset="container_20ft">20ft Container</button>
|
||
<button class="lo-preset-btn" data-preset="container_40ft">40ft Container</button>
|
||
<button class="lo-preset-btn" data-preset="container_40ft_hc">40ft High Cube</button>
|
||
<button class="lo-preset-btn" data-preset="lkw_sattelzug">LKW Sattelzug</button>
|
||
<button class="lo-preset-btn" data-preset="lkw_tandem">LKW Tandem</button>
|
||
<button class="lo-preset-btn" data-preset="custom">Custom</button>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="lo-custom-section">
|
||
<h4>📐 Dimensions & Weight Limit:</h4>
|
||
<div class="lo-input-grid">
|
||
<div class="lo-input-field">
|
||
<label>Length (m)</label>
|
||
<input type="number" id="customLength" placeholder="e.g. 12.19" step="0.01" min="0">
|
||
</div>
|
||
<div class="lo-input-field">
|
||
<label>Width (m)</label>
|
||
<input type="number" id="customWidth" placeholder="e.g. 2.35" step="0.01" min="0">
|
||
</div>
|
||
<div class="lo-input-field">
|
||
<label>Height (m)</label>
|
||
<input type="number" id="customHeight" placeholder="e.g. 2.39" step="0.01" min="0">
|
||
</div>
|
||
<div class="lo-input-field">
|
||
<label>Max. Load (kg)</label>
|
||
<input type="number" id="customMaxWeight" placeholder="e.g. 30480" step="100" min="0">
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="lo-button-group">
|
||
<button class="lo-btn-primary" id="calculateLoad">► Calculate</button>
|
||
<button class="lo-btn-secondary" id="resetLoad">↺ Reset</button>
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<div class="lo-results" id="resultsContainer" style="display:none;">
|
||
|
||
<div class="lo-results-summary">
|
||
<h4>Results:</h4>
|
||
<div class="lo-summary-item">
|
||
<span><strong>Limiting Factor:</strong></span>
|
||
<span id="limitingFactorValue" class="lo-value-highlight">—</span>
|
||
</div>
|
||
<div class="lo-summary-item">
|
||
<span><strong>Total Rolls:</strong></span>
|
||
<div style="text-align:right;">
|
||
<span id="totalRollsValue" class="lo-value-highlight">—</span>
|
||
<small id="rollsDetailValue" style="display:block;color:#888;"></small>
|
||
</div>
|
||
</div>
|
||
<div class="lo-summary-item">
|
||
<span><strong>Utilization:</strong></span>
|
||
<div class="lo-util-group">
|
||
<div>Weight: <span id="weightUtilValue" class="lo-value-number">—</span> %</div>
|
||
<div>Volume: <span id="volumeUtilValue" class="lo-value-number">—</span> %</div>
|
||
<div>Product: <span id="productM2Value" class="lo-value-number">—</span> m²</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="lo-results-table">
|
||
<h4>Comparison Table:</h4>
|
||
<table id="comparisonTable">
|
||
<thead>
|
||
<tr>
|
||
<th>Scenario</th>
|
||
<th>Rolls</th>
|
||
<th>kg</th>
|
||
<th>m²</th>
|
||
<th>Util. %</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody></tbody>
|
||
</table>
|
||
</div>
|
||
|
||
<div class="lo-sideview-section" id="sideViewSection" style="display:none;">
|
||
<h4>📐 Front View — Cross-Section</h4>
|
||
<div class="lo-sideview-wrap">
|
||
<svg id="sideViewSVG" width="100%" viewBox="0 0 800 300" xmlns="http://www.w3.org/2000/svg">
|
||
<text x="400" y="130" text-anchor="middle" fill="#aaa" font-size="14">Run a calculation to see the side view.</text>
|
||
</svg>
|
||
</div>
|
||
<div class="lo-sideview-legend">
|
||
<span class="lo-legend-item"><span class="lo-legend-box roll-color"></span> Roll (cross-section, Ø = diameter)</span>
|
||
<span class="lo-legend-item"><span class="lo-legend-box container-color"></span> Container / Vehicle</span>
|
||
</div>
|
||
</div>
|
||
|
||
</div>
|
||
|
||
</div>
|
||
</div>
|
||
<!-- END LOAD OPTIMIZER -->
|
||
|
||
</div><!-- app-main -->
|
||
|
||
<div class="app-footer">
|
||
■ Naue GmbH & Co. KG · Roll Diameter Calculator · Internal Application
|
||
</div>
|
||
|
||
|
||
<!-- ============================================================
|
||
ARTICLE DATA (JSON)
|
||
============================================================ -->
|
||
<script>
|
||
window.ARTICLE_DATA = [];
|
||
|
||
// Load article data from external JSON
|
||
(async function() {
|
||
try {
|
||
const response = await fetch('article-data.json');
|
||
if (response.ok) {
|
||
window.ARTICLE_DATA = await response.json();
|
||
console.log('Article data loaded:', window.ARTICLE_DATA.length, 'items');
|
||
// Populate dropdowns
|
||
populateArticleDropdowns();
|
||
} else {
|
||
console.error('Failed to load article data:', response.status);
|
||
}
|
||
} catch (error) {
|
||
console.error('Error loading article data:', error);
|
||
}
|
||
})();
|
||
</script>
|
||
|
||
<!-- ============================================================
|
||
LOAD OPTIMIZER CORE
|
||
============================================================ -->
|
||
<script>
|
||
class LoadOptimizer {
|
||
constructor() {
|
||
this.state = {
|
||
rollDiameter: 0, coreDiameter: 150, rollWidth: 0,
|
||
rollWeight: 0, m2PerRoll: 0, productLength: 0
|
||
};
|
||
this.presets = {
|
||
container_20ft: { name:"20ft Container", length:5.90, width:2.35, height:2.39, maxWeight:21600 },
|
||
container_40ft: { name:"40ft Container", length:12.19, width:2.35, height:2.39, maxWeight:30480 },
|
||
container_40ft_hc:{ name:"40ft High Cube", length:12.19, width:2.35, height:2.69, maxWeight:30480 },
|
||
lkw_sattelzug: { name:"LKW Sattelzug", length:13.50, width:2.50, height:2.70, maxWeight:24000 },
|
||
lkw_tandem: { name:"LKW Tandem", length:13.50, width:2.50, height:2.70, maxWeight:25000 }
|
||
};
|
||
}
|
||
setRollData(data) {
|
||
this.state = {
|
||
rollDiameter: data.rollDiameter || 0,
|
||
coreDiameter: data.coreDiameter || 150,
|
||
rollWidth: data.rollWidth || 0,
|
||
rollWeight: data.rollWeight || 0,
|
||
m2PerRoll: data.m2PerRoll || 0,
|
||
productLength: data.productLength || 0
|
||
};
|
||
}
|
||
calculateLoadUtilization(dim, maxLoadWeight) {
|
||
const { rollDiameter, coreDiameter, rollWidth, rollWeight, m2PerRoll } = this.state;
|
||
const D = rollDiameter / 1000, d = coreDiameter / 1000;
|
||
// Rollen liegen auf der Seite: Länge der Rolle = rollWidth, Durchmesser = D
|
||
const rollsAlongLength = Math.floor(dim.length / rollWidth); // entlang Containerlänge
|
||
const rollsAlongWidth = Math.floor(dim.width / D); // quer (Durchmesser)
|
||
const rollsPerLayer = rollsAlongLength * rollsAlongWidth;
|
||
const maxStackHeight = Math.floor(dim.height / D); // Stapellagen
|
||
const maxByGeometry = rollsPerLayer * maxStackHeight;
|
||
const maxByWeight = rollWeight > 0 ? Math.floor(maxLoadWeight / rollWeight) : Infinity;
|
||
const volPerRoll = (Math.PI / 4) * (D*D - d*d) * rollWidth;
|
||
const containerVol = dim.length * dim.width * dim.height;
|
||
const maxByVolume = volPerRoll > 0 ? Math.floor(containerVol / volPerRoll) : Infinity;
|
||
const scenarios = [
|
||
{ name:'Weight', rolls: maxByWeight, weight: maxByWeight * rollWeight, type:'weight' },
|
||
{ name:'Volume', rolls: maxByVolume, weight: maxByVolume * rollWeight, type:'volume' },
|
||
{ name:'Geometry', rolls: maxByGeometry, weight: maxByGeometry * rollWeight, type:'geometry' }
|
||
];
|
||
const limiting = scenarios.reduce((mn, c) => c.rolls < mn.rolls ? c : mn);
|
||
const finalRolls = limiting.rolls === Infinity ? 0 : limiting.rolls;
|
||
const finalWeight = finalRolls * rollWeight;
|
||
const finalM2 = finalRolls * m2PerRoll;
|
||
return {
|
||
scenarios, limiting, finalRolls, finalWeight, finalM2,
|
||
utilizationWeight: maxLoadWeight > 0 ? (finalWeight / maxLoadWeight) * 100 : 0,
|
||
utilizationVolume: containerVol > 0 ? (finalRolls * volPerRoll / containerVol) * 100 : 0,
|
||
rollsPerLayer, rollsAlongLength, rollsAlongWidth, maxStackHeight, containerVol, volPerRoll
|
||
};
|
||
}
|
||
fmt(n, dec=1) { return isFinite(n) ? n.toFixed(dec) : '—'; }
|
||
}
|
||
window.LoadOptimizer = LoadOptimizer;
|
||
</script>
|
||
|
||
<!-- ============================================================
|
||
LOAD OPTIMIZER UI
|
||
============================================================ -->
|
||
<script>
|
||
class LoadOptimizerUI {
|
||
constructor() {
|
||
this.optimizer = new window.LoadOptimizer();
|
||
this.currentPreset = null;
|
||
this.init();
|
||
}
|
||
init() {
|
||
document.getElementById('loAccordionToggle').addEventListener('click', () => this.toggleAccordion());
|
||
document.querySelectorAll('.lo-preset-btn').forEach(btn =>
|
||
btn.addEventListener('click', e => this.loadPreset(e.target.dataset.preset))
|
||
);
|
||
document.getElementById('calculateLoad').addEventListener('click', () => this.calculate());
|
||
document.getElementById('resetLoad').addEventListener('click', () => this.resetForm());
|
||
}
|
||
toggleAccordion() {
|
||
const header = document.getElementById('loAccordionToggle');
|
||
const content = document.getElementById('loAccordionContent');
|
||
const isOpen = content.style.display !== 'none';
|
||
content.style.display = isOpen ? 'none' : 'block';
|
||
header.classList.toggle('active');
|
||
}
|
||
loadPreset(id) {
|
||
if (id === 'custom') { this.clearPresets(); return; }
|
||
const p = this.optimizer.presets[id];
|
||
if (!p) return;
|
||
document.getElementById('customLength').value = p.length;
|
||
document.getElementById('customWidth').value = p.width;
|
||
document.getElementById('customHeight').value = p.height;
|
||
document.getElementById('customMaxWeight').value = p.maxWeight;
|
||
this.updatePresetBtns(id);
|
||
this.currentPreset = id;
|
||
}
|
||
updatePresetBtns(activeId) {
|
||
document.querySelectorAll('.lo-preset-btn').forEach(b => b.classList.remove('active'));
|
||
if (activeId) document.querySelector('[data-preset="'+activeId+'"]')?.classList.add('active');
|
||
}
|
||
clearPresets() { this.updatePresetBtns(null); this.currentPreset = null; }
|
||
calculate() {
|
||
const L = parseFloat(document.getElementById('customLength').value);
|
||
const W = parseFloat(document.getElementById('customWidth').value);
|
||
const H = parseFloat(document.getElementById('customHeight').value);
|
||
const MW= parseFloat(document.getElementById('customMaxWeight').value);
|
||
if ([L,W,H,MW].some(v => isNaN(v) || v <= 0)) {
|
||
alert('Please fill in all transport dimensions!');
|
||
return;
|
||
}
|
||
const rollData = this.getRollData();
|
||
if (!rollData) {
|
||
alert('Please complete a roll calculation first (Direct Calculation tab).\nRoll Diameter, Roll Width and Roll Weight are required.');
|
||
return;
|
||
}
|
||
this.optimizer.setRollData(rollData);
|
||
const res = this.optimizer.calculateLoadUtilization({ length:L, width:W, height:H }, MW);
|
||
this.showResults(res, MW);
|
||
this.drawSideView(res, { length:L, width:W, height:H });
|
||
}
|
||
getRollData() {
|
||
const D = parseFloat(document.getElementById('d-di')?.value || 0);
|
||
const W = parseFloat(document.getElementById('de-wi')?.value || 0);
|
||
const GW = parseFloat(document.getElementById('d-wo')?.value || 0);
|
||
const L = parseFloat(document.getElementById('d-le')?.value || 0);
|
||
const d = parseFloat(document.getElementById('d-co')?.value || 150);
|
||
if (!D || !W || !GW) return null;
|
||
return { rollDiameter:D, coreDiameter:d, rollWidth:W, rollWeight:GW, m2PerRoll:L*W, productLength:L };
|
||
}
|
||
showResults(res, maxW) {
|
||
const m2PerRoll = this.optimizer.state.m2PerRoll || 0;
|
||
document.getElementById('limitingFactorValue').textContent = res.limiting.name;
|
||
document.getElementById('totalRollsValue').textContent = res.finalRolls;
|
||
document.getElementById('rollsDetailValue').textContent =
|
||
'(' + res.rollsAlongLength + ' along length × ' + res.rollsAlongWidth + ' across, ' + res.maxStackHeight + ' layers)';
|
||
document.getElementById('weightUtilValue').textContent = this.optimizer.fmt(res.utilizationWeight);
|
||
document.getElementById('volumeUtilValue').textContent = this.optimizer.fmt(res.utilizationVolume);
|
||
document.getElementById('productM2Value').textContent = this.optimizer.fmt(res.finalM2, 0);
|
||
const tbody = document.querySelector('#comparisonTable tbody');
|
||
tbody.innerHTML = '';
|
||
res.scenarios.forEach(sc => {
|
||
const tr = document.createElement('tr');
|
||
const lim = (sc.type === res.limiting.type);
|
||
if (lim) tr.classList.add('lo-recommended');
|
||
const util = maxW > 0 ? (sc.weight / maxW * 100).toFixed(1) : '—';
|
||
const scM2 = isFinite(sc.rolls) && m2PerRoll > 0 ? Math.round(sc.rolls * m2PerRoll) : '—';
|
||
tr.innerHTML =
|
||
'<td>'+ sc.name +(lim?' ★':'')+'</td>'+
|
||
'<td>'+ (sc.rolls === Infinity ? '—' : sc.rolls) +'</td>'+
|
||
'<td>'+ (isFinite(sc.weight) ? Math.round(sc.weight) : '—') +'</td>'+
|
||
'<td>'+ scM2 +'</td>'+
|
||
'<td>'+ util +'%</td>';
|
||
tbody.appendChild(tr);
|
||
});
|
||
document.getElementById('resultsContainer').style.display = 'block';
|
||
}
|
||
|
||
drawSideView(res, dim) {
|
||
const svg = document.getElementById('sideViewSVG');
|
||
const section = document.getElementById('sideViewSection');
|
||
if (!svg || !res || res.finalRolls === 0) return;
|
||
|
||
const D_m = this.optimizer.state.rollDiameter / 1000;
|
||
const aW = res.rollsAlongWidth;
|
||
const aL = res.rollsAlongLength;
|
||
const layers = res.maxStackHeight;
|
||
const total = res.finalRolls;
|
||
|
||
const PAD = 52, W_SVG = 800, H_SVG = 300;
|
||
const scaleX = (W_SVG - 2*PAD) / dim.width;
|
||
const scaleY = (H_SVG - 2*PAD) / dim.height;
|
||
const scale = Math.min(scaleX, scaleY);
|
||
|
||
const cW = dim.width * scale;
|
||
const cH = dim.height * scale;
|
||
const cX = PAD + ((W_SVG - 2*PAD) - cW) / 2;
|
||
const cY = PAD + ((H_SVG - 2*PAD) - cH) / 2;
|
||
const rD = D_m * scale;
|
||
|
||
let s = '';
|
||
s += '<defs>';
|
||
s += '<marker id="arr" markerWidth="6" markerHeight="6" refX="3" refY="3" orient="auto"><path d="M0,0 L0,6 L6,3 z" fill="#666"/></marker>';
|
||
s += '<marker id="arr2" markerWidth="6" markerHeight="6" refX="3" refY="3" orient="auto-start-reverse"><path d="M0,0 L0,6 L6,3 z" fill="#666"/></marker>';
|
||
s += '</defs>';
|
||
s += '<rect x="0" y="0" width="800" height="300" fill="#f8f9fa"/>';
|
||
s += '<rect x="'+cX.toFixed(1)+'" y="'+cY.toFixed(1)+'" width="'+cW.toFixed(1)+'" height="'+cH.toFixed(1)+'" fill="#e8e8e8" stroke="#888" stroke-width="2.5" rx="3"/>';
|
||
s += '<rect x="'+cX.toFixed(1)+'" y="'+(cY+cH-5).toFixed(1)+'" width="'+cW.toFixed(1)+'" height="5" fill="#bbb"/>';
|
||
|
||
let drawn = 0;
|
||
for (let layer = 0; layer < layers && drawn < total; layer++) {
|
||
for (let col = 0; col < aW && drawn < total; col++) {
|
||
const cx = cX + col * rD + rD / 2;
|
||
const cy = cY + cH - layer * rD - rD / 2 - 5;
|
||
const r = Math.max(1, rD / 2 - 1.0);
|
||
s += '<circle cx="'+(cx+1.5).toFixed(1)+'" cy="'+(cy+1.5).toFixed(1)+'" r="'+r.toFixed(1)+'" fill="rgba(0,0,0,0.08)"/>';
|
||
s += '<circle cx="'+cx.toFixed(1)+'" cy="'+cy.toFixed(1)+'" r="'+r.toFixed(1)+'" fill="#a8d4f5" stroke="#0066cc" stroke-width="1.2"/>';
|
||
if (rD > 16) {
|
||
const kr = Math.max(1.5, rD * 0.13);
|
||
s += '<circle cx="'+cx.toFixed(1)+'" cy="'+cy.toFixed(1)+'" r="'+kr.toFixed(1)+'" fill="white" stroke="#0052a3" stroke-width="1" opacity="0.8"/>';
|
||
}
|
||
drawn++;
|
||
}
|
||
}
|
||
|
||
const dimY = cY + cH + 20;
|
||
s += '<line x1="'+cX.toFixed(1)+'" y1="'+dimY.toFixed(1)+'" x2="'+(cX+cW).toFixed(1)+'" y2="'+dimY.toFixed(1)+'" stroke="#666" stroke-width="1" marker-start="url(#arr2)" marker-end="url(#arr)"/>';
|
||
s += '<text x="'+(cX+cW/2).toFixed(1)+'" y="'+(dimY+13).toFixed(1)+'" text-anchor="middle" font-size="11" fill="#555">'+dim.width+' m</text>';
|
||
|
||
const dimX = cX - 22;
|
||
const midY = (cY + cH/2).toFixed(1);
|
||
s += '<line x1="'+dimX.toFixed(1)+'" y1="'+cY.toFixed(1)+'" x2="'+dimX.toFixed(1)+'" y2="'+(cY+cH).toFixed(1)+'" stroke="#666" stroke-width="1" marker-start="url(#arr2)" marker-end="url(#arr)"/>';
|
||
s += '<text x="'+(dimX-5).toFixed(1)+'" y="'+midY+'" text-anchor="middle" font-size="11" fill="#555" transform="rotate(-90,'+(dimX-5).toFixed(1)+','+midY+')">'+dim.height+' m</text>';
|
||
|
||
s += '<text x="'+(cX+cW/2).toFixed(1)+'" y="'+(cY-8).toFixed(1)+'" text-anchor="middle" font-size="11" fill="#666" font-style="italic">'+aL+' rows deep (into page)</text>';
|
||
s += '<text x="'+(cX+cW-4).toFixed(1)+'" y="'+(cY+15).toFixed(1)+'" text-anchor="end" font-size="10" fill="#555" font-style="italic">'+aW+' wide \u00d7 '+layers+' high \u00d7 '+aL+' deep \u00b7 '+total+' rolls</text>';
|
||
|
||
if (rD > 30 && aW > 0) {
|
||
const rx1 = cX, rx2 = cX + rD, ry = cY + cH - rD/2 - 5;
|
||
s += '<line x1="'+rx1.toFixed(1)+'" y1="'+(ry+rD/2+10).toFixed(1)+'" x2="'+rx2.toFixed(1)+'" y2="'+(ry+rD/2+10).toFixed(1)+'" stroke="#0066cc" stroke-width="0.8" stroke-dasharray="3,2" marker-start="url(#arr2)" marker-end="url(#arr)"/>';
|
||
s += '<text x="'+(cX+rD/2).toFixed(1)+'" y="'+(ry+rD/2+22).toFixed(1)+'" text-anchor="middle" font-size="10" fill="#0066cc">\u00d8 '+(D_m*1000).toFixed(0)+' mm</text>';
|
||
}
|
||
|
||
svg.setAttribute('viewBox', '0 0 800 300');
|
||
svg.innerHTML = s;
|
||
section.style.display = 'block';
|
||
}
|
||
|
||
resetForm() {
|
||
['customLength','customWidth','customHeight','customMaxWeight'].forEach(id =>
|
||
document.getElementById(id).value = ''
|
||
);
|
||
document.getElementById('resultsContainer').style.display = 'none';
|
||
document.getElementById('sideViewSection').style.display = 'none';
|
||
this.clearPresets();
|
||
}
|
||
}
|
||
</script>
|
||
|
||
<!-- ============================================================
|
||
ROLL CALCULATOR LOGIC
|
||
============================================================ -->
|
||
<script>
|
||
// --- Article dropdown population ---
|
||
function populateArticleDropdowns() {
|
||
const data = window.ARTICLE_DATA || [];
|
||
|
||
// Datalists befuellen
|
||
['d-art','e-art'].forEach(function(inputId) {
|
||
var dl = document.getElementById(inputId + '-list');
|
||
if (!dl) return;
|
||
data.forEach(function(a) {
|
||
var opt = document.createElement('option');
|
||
opt.value = a.nr + ' – ' + a.name;
|
||
dl.appendChild(opt);
|
||
});
|
||
});
|
||
|
||
// Auto-fill Dicke bei Artikelauswahl (d-art)
|
||
var dArt = document.getElementById('d-art');
|
||
if (dArt) {
|
||
dArt.addEventListener('input', function() {
|
||
var val = dArt.value;
|
||
var match = data.find(function(a) { return val.startsWith(a.nr + ' –') || val === a.nr; });
|
||
if (match) {
|
||
document.getElementById('d-th').value = match.thickness;
|
||
}
|
||
});
|
||
}
|
||
|
||
// Auto-fill Dicke bei Artikelauswahl (e-art) - kein Dicken-Feld im Extrap-Tab
|
||
// (kann spaeter ergaenzt werden)
|
||
|
||
// Kern-Preset Buttons
|
||
document.querySelectorAll('.core-preset-btn').forEach(function(btn) {
|
||
btn.addEventListener('click', function() {
|
||
var val = btn.dataset.core;
|
||
var pane = btn.closest('.tab-pane');
|
||
if (pane) {
|
||
var inp = pane.querySelector('input[id$="-co"]');
|
||
if (inp) {
|
||
inp.value = val;
|
||
pane.querySelectorAll('.core-preset-btn').forEach(function(b) { b.classList.remove('active'); });
|
||
btn.classList.add('active');
|
||
}
|
||
}
|
||
});
|
||
});
|
||
}
|
||
|
||
// --- Tab switching ---
|
||
document.querySelectorAll('.tab-btn').forEach(btn => {
|
||
btn.addEventListener('click', () => {
|
||
document.querySelectorAll('.tab-btn').forEach(b => b.classList.remove('active'));
|
||
document.querySelectorAll('.tab-pane').forEach(p => p.classList.remove('active'));
|
||
btn.classList.add('active');
|
||
document.getElementById('tab-' + btn.dataset.tab).classList.add('active');
|
||
});
|
||
});
|
||
|
||
// --- Mode switching (Direct Calc) ---
|
||
document.querySelectorAll('.mode-btn').forEach(btn => {
|
||
btn.addEventListener('click', () => {
|
||
document.querySelectorAll('.mode-btn').forEach(b => b.classList.remove('active'));
|
||
btn.classList.add('active');
|
||
});
|
||
});
|
||
|
||
// --- DIRECT CALCULATION ---
|
||
document.getElementById('d-calc').addEventListener('click', () => {
|
||
const mode = document.querySelector('.mode-btn.active')?.dataset.mode || 'diameter';
|
||
const d_mm = parseFloat(document.getElementById('d-co').value) || 0;
|
||
const t_mm = parseFloat(document.getElementById('d-th').value) || 0;
|
||
const tol = parseFloat(document.getElementById('d-tol').value) || 0;
|
||
const L_m = parseFloat(document.getElementById('d-le').value) || 0;
|
||
const D_mm = parseFloat(document.getElementById('d-di').value) || 0;
|
||
|
||
const resBox = document.getElementById('d-result');
|
||
const resLabel = document.getElementById('d-res-label');
|
||
const resVal = document.getElementById('d-res-val');
|
||
const resRange = document.getElementById('d-res-range');
|
||
|
||
// Weight calc helper
|
||
function calcWeight(length) {
|
||
const wi = parseFloat(document.getElementById('de-wi').value) || 0;
|
||
const aw = parseFloat(document.getElementById('d-aw').value) || 0;
|
||
if (wi > 0 && aw > 0 && length > 0) {
|
||
const w = (aw * length * wi / 1000).toFixed(1);
|
||
document.getElementById('d-wo').value = w;
|
||
return parseFloat(w);
|
||
}
|
||
return null;
|
||
}
|
||
|
||
if (mode === 'diameter') {
|
||
if (!d_mm || !t_mm || !L_m) { alert('Please enter d, t and L.'); return; }
|
||
const D_nom = Math.sqrt(d_mm*d_mm + (4*L_m*1000*t_mm)/Math.PI);
|
||
resLabel.textContent = 'Roll Diameter (D)';
|
||
resVal.textContent = D_nom.toFixed(1) + ' mm';
|
||
if (tol) {
|
||
const D_min = Math.sqrt(d_mm*d_mm + (4*L_m*1000*(t_mm-tol))/Math.PI);
|
||
const D_max = Math.sqrt(d_mm*d_mm + (4*L_m*1000*(t_mm+tol))/Math.PI);
|
||
resRange.textContent = 'Min: ' + D_min.toFixed(1) + ' mm | Max: ' + D_max.toFixed(1) + ' mm';
|
||
} else {
|
||
resRange.textContent = '';
|
||
}
|
||
// write result back to d-di for Load Optimizer
|
||
document.getElementById('d-di').value = D_nom.toFixed(1);
|
||
calcWeight(L_m);
|
||
|
||
} else if (mode === 'length') {
|
||
if (!d_mm || !t_mm || !D_mm) { alert('Please enter d, t and D.'); return; }
|
||
const L = (Math.PI / (4 * t_mm)) * (D_mm*D_mm - d_mm*d_mm) / 1000;
|
||
resLabel.textContent = 'Product Length (L)';
|
||
resVal.textContent = L.toFixed(2) + ' m';
|
||
if (tol) {
|
||
const L_min = (Math.PI / (4*(t_mm+tol))) * (D_mm*D_mm - d_mm*d_mm) / 1000;
|
||
const L_max = (Math.PI / (4*(t_mm-tol))) * (D_mm*D_mm - d_mm*d_mm) / 1000;
|
||
resRange.textContent = 'Min: ' + L_min.toFixed(2) + ' m | Max: ' + L_max.toFixed(2) + ' m';
|
||
} else {
|
||
resRange.textContent = '';
|
||
}
|
||
document.getElementById('d-le').value = L.toFixed(2);
|
||
calcWeight(L);
|
||
|
||
} else if (mode === 'target') {
|
||
if (!d_mm || !t_mm) { alert('Please enter d and t.'); return; }
|
||
const D_target = D_mm || 800;
|
||
const L = (Math.PI / (4 * t_mm)) * (D_target*D_target - d_mm*d_mm) / 1000;
|
||
resLabel.textContent = 'Length for Target Diameter ' + D_target + ' mm';
|
||
resVal.textContent = L.toFixed(2) + ' m';
|
||
resRange.textContent = '';
|
||
document.getElementById('d-le').value = L.toFixed(2);
|
||
calcWeight(L);
|
||
|
||
} else if (mode === 'thickness') {
|
||
if (!d_mm || !D_mm || !L_m) { alert('Please enter d, D and L.'); return; }
|
||
const t = (Math.PI / (4 * L_m * 1000)) * (D_mm*D_mm - d_mm*d_mm);
|
||
resLabel.textContent = 'Product Thickness (t)';
|
||
resVal.textContent = t.toFixed(3) + ' mm';
|
||
resRange.textContent = '';
|
||
document.getElementById('d-th').value = t.toFixed(3);
|
||
calcWeight(L_m);
|
||
}
|
||
|
||
resBox.style.display = 'block';
|
||
});
|
||
|
||
// --- DIRECT RESET ---
|
||
document.getElementById('d-reset').addEventListener('click', () => {
|
||
['d-co','d-th','d-tol','d-le','d-di','de-wi','d-aw','d-wo'].forEach(id => {
|
||
const el = document.getElementById(id);
|
||
if (el) el.value = id === 'd-co' ? '150' : '';
|
||
});
|
||
document.getElementById('d-art').selectedIndex = 0;
|
||
document.getElementById('d-result').style.display = 'none';
|
||
});
|
||
|
||
// --- EXTRAPOLATION CALCULATION ---
|
||
document.getElementById('e-calc').addEventListener('click', () => {
|
||
const d = parseFloat(document.getElementById('e-co').value) || 0;
|
||
const D0 = parseFloat(document.getElementById('e-d0').value) || 0;
|
||
const L0 = parseFloat(document.getElementById('e-l0').value) || 0;
|
||
const L1 = parseFloat(document.getElementById('e-l1').value) || 0;
|
||
const tol= parseFloat(document.getElementById('e-tol').value)|| 0;
|
||
|
||
if (!d || !D0 || !L0 || !L1) { alert('Please enter d, D₀, L₀ and L₁.'); return; }
|
||
|
||
const D1 = Math.sqrt(d*d + (L1/L0) * (D0*D0 - d*d));
|
||
document.getElementById('e-res-val').textContent = D1.toFixed(1) + ' mm';
|
||
|
||
if (tol) {
|
||
const D1_min = D1 - tol, D1_max = D1 + tol;
|
||
document.getElementById('e-res-range').textContent =
|
||
'Min: ' + D1_min.toFixed(1) + ' mm | Max: ' + D1_max.toFixed(1) + ' mm';
|
||
} else {
|
||
document.getElementById('e-res-range').textContent = '';
|
||
}
|
||
|
||
const wi = parseFloat(document.getElementById('e-wi').value) || 0;
|
||
const aw = parseFloat(document.getElementById('e-aw').value) || 0;
|
||
if (wi > 0 && aw > 0) {
|
||
// no weight output field on extrap tab in this version
|
||
}
|
||
|
||
document.getElementById('e-result').style.display = 'block';
|
||
});
|
||
|
||
// --- EXTRAPOLATION RESET ---
|
||
document.getElementById('e-reset').addEventListener('click', () => {
|
||
['e-co','e-d0','e-l0','e-l1','e-tol','e-wi','e-aw'].forEach(id => {
|
||
const el = document.getElementById(id);
|
||
if (el) el.value = id === 'e-co' ? '150' : '';
|
||
});
|
||
document.getElementById('e-art').selectedIndex = 0;
|
||
document.getElementById('e-result').style.display = 'none';
|
||
});
|
||
|
||
// --- DISCLAIMER ---
|
||
document.getElementById('confirmCheck').addEventListener('change', function() {
|
||
document.getElementById('btnAccept').disabled = !this.checked;
|
||
});
|
||
document.getElementById('btnAccept').addEventListener('click', () => {
|
||
document.getElementById('disclaimerOverlay').style.display = 'none';
|
||
});
|
||
document.getElementById('btnCancel').addEventListener('click', () => {
|
||
window.location.href = 'about:blank';
|
||
});
|
||
|
||
// --- INIT ---
|
||
document.addEventListener('DOMContentLoaded', () => {
|
||
// Populate wird jetzt async von der ARTICLE_DATA fetch() aufgerufen
|
||
// Falls die Daten schnell geladen sind, wird es dort aufgerufen
|
||
// Falls DOMContentLoaded vor dem fetch fertig ist, rufen wir es hier auf:
|
||
if (window.ARTICLE_DATA && window.ARTICLE_DATA.length > 0) {
|
||
populateArticleDropdowns();
|
||
}
|
||
window.loadOptimizerUI = new LoadOptimizerUI();
|
||
});
|
||
</script>
|
||
|
||
|
||
<!-- ============================================================
|
||
SERVICE WORKER REGISTRATION
|
||
============================================================ -->
|
||
|
||
<!-- Offline Indicator -->
|
||
<div id="offlineIndicator" style="display:none; position:fixed; top:0; left:0; right:0; background:#ff6600; color:#fff; padding:10px; text-align:center; font-weight:bold; z-index:10000;">
|
||
📡 You are offline - using cached data
|
||
</div>
|
||
<script>
|
||
window.addEventListener('offline', () => {
|
||
document.getElementById('offlineIndicator').style.display = 'block';
|
||
console.log('[App] Offline mode activated');
|
||
});
|
||
window.addEventListener('online', () => {
|
||
document.getElementById('offlineIndicator').style.display = 'none';
|
||
console.log('[App] Online mode activated');
|
||
});
|
||
// Check initial state
|
||
if (!navigator.onLine) {
|
||
document.getElementById('offlineIndicator').style.display = 'block';
|
||
}
|
||
</script>
|
||
|
||
<script>
|
||
// Register Service Worker für Offline-Funktionalität
|
||
if ('serviceWorker' in navigator) {
|
||
window.addEventListener('load', () => {
|
||
navigator.serviceWorker.register('service-worker.js')
|
||
.then(reg => {
|
||
console.log('[App] ServiceWorker registered:', reg.scope);
|
||
})
|
||
.catch(err => {
|
||
console.warn('[App] ServiceWorker registration failed:', err);
|
||
});
|
||
});
|
||
} else {
|
||
console.warn('[App] ServiceWorker not supported in this browser');
|
||
}
|
||
</script>
|
||
|
||
</body>
|
||
</html>
|