api_g / spoller.html
DmitrMakeev's picture
Create spoller.html
a558069 verified
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Hydroponic Profile Generator (HPG)</title>
<style>
* {
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Arial', sans-serif;
background: #c3c3c3;
margin: 0;
padding: 0;
color: #202020;
}
.container {
max-width: 720px;
margin: 14px auto;
background: #d6d6d6;
border: 1px solid #9c9c9c;
box-shadow: inset 0 1px 0 rgba(255,255,255,0.6);
}
.header {
background: linear-gradient(to bottom, #e6e6e6, #cccccc);
padding: 8px 12px;
border-bottom: 1px solid #8f8f8f;
font-weight: bold;
text-shadow: 0 1px 0 #f4f4f4;
}
.tab-bar {
display: flex;
background: #dcdcdc;
border-bottom: 1px solid #ccc;
}
.tab {
padding: 4px 12px;
cursor: pointer;
border-right: 1px solid #999;
font-size: 13px;
color: #1f1f1f;
user-select: none;
}
.tab:hover {
background: #e8e8e8;
}
.tab.active {
background: #f0f0f0;
border-bottom: 0.5px solid #f0f0f0;
margin-bottom: -0.5px;
font-weight: bold;
}
.tab-content {
display: none;
padding: 8px 12px;
background: #d3d3d3;
min-height: 625px;
max-height: 625px;
overflow-y: auto;
}
.tab-content.active {
display: block;
}
.section-title {
font-weight: bold;
margin: 8px 0 6px 0;
padding: 2px 0;
color: #8B0000;
font-size: 13px;
text-shadow: 0 1px 0 #f0f0f0;
}
.input-group {
display: flex;
align-items: center;
margin: 3px 0;
}
.input-group label {
min-width: 35px;
font-weight: bold;
font-size: 11px;
margin-right: 3px;
}
input[type="number"] {
width: 52px;
padding: 2px 3px;
border: 1px solid #7c7c7c;
border-radius: 2px;
font-size: 11px;
text-align: right;
font-family: Arial, sans-serif;
background: #e9e9e9;
box-shadow: inset 0 1px 0 rgba(255,255,255,0.6);
}
input[type="number"]:focus {
outline: 1px solid #4a90e2;
border-color: #4a90e2;
}
input[type="number"].highlight-green {
background-color: #d5efd0 !important;
}
input[type="number"].highlight-blue {
background: #d7e5fe;
}
input[type="number"].wide {
width: 90px;
}
input[type="number"].narrow {
width: 60px;
}
.macro-table {
width: 100%;
border-collapse: collapse;
font-size: 11px;
margin: 4px 0;
}
.macro-table th,
.macro-table td {
padding: 2px 3px;
}
.macro-table th:not(:first-child),
.macro-table td:not(:first-child) {
width: 56px;
}
.macro-table th:first-child,
.macro-table td:first-child {
width: 30px;
}
.macro-table input[type="number"] {
width: 50px;
}
.macro-summary {
width: auto !important;
}
.salts-table {
width: 100%;
border-collapse: collapse;
font-size: 11px;
background: #d6d6d6;
margin: 8px 0;
}
.salts-table td,
.salts-table th {
padding: 2px 4px;
}
.salts-table td:first-child {
width: 200px;
}
.salts-table input[type="number"] {
width: 52px;
}
.salts-table .checkbox-cell {
width: 40px;
}
.salts-table input[readonly] {
width: 54px;
}
.matrix-table {
width: 100%;
border-collapse: collapse;
margin: 8px 0;
font-size: 11px;
background: #d6d6d6;
}
.matrix-table th,
.matrix-table td {
border: 1px solid #9c9c9c;
padding: 2px 4px;
text-align: center;
}
.matrix-table th {
background: linear-gradient(to bottom, #e8e8e8, #d2d2d2);
font-weight: bold;
padding: 3px 5px;
}
.matrix-table td input {
width: 100%;
text-align: center;
padding: 1px 2px;
font-size: 11px;
border: 1px solid #7c7c7c;
background: #e9e9e9;
box-sizing: border-box;
}
.matrix-table .diagonal {
background: #d6d6d6;
font-weight: bold;
}
.matrix-table .highlight-green-cell {
background: #d5efd0;
}
.matrix-table .highlight-blue-cell {
background: #d7e5fe;
}
.salts-table {
width: 100%;
border-collapse: collapse;
margin: 8px 0;
font-size: 11px;
}
.salts-table thead th {
padding: 2px 4px;
background: transparent;
border: none;
font-weight: normal;
}
.salts-table td {
padding: 2px 4px;
border-bottom: 1px solid #c1c1c1;
font-size: 11px;
}
.salts-table td:first-child {
width: 35%;
font-size: 11px;
color: #111;
}
.salts-table td[style*="font-weight: bold"] {
color: #8B0000;
}
.salts-table input[type="number"] {
width: 100%;
font-size: 11px;
padding: 1px 2px;
border: 1px solid #7c7c7c;
background: #e9e9e9;
box-sizing: border-box;
}
.salts-table input[readonly] {
width: 100%;
background: #f5f5f5;
box-sizing: border-box;
}
.salts-table .checkbox-cell {
width: 25px;
text-align: center;
}
.salts-table label {
font-weight: bold;
margin-right: 2px;
font-size: 10px;
}
.salts-table td {
white-space: nowrap;
}
.info-panel {
background: transparent;
padding: 0;
margin: 0;
border: none;
font-size: 11px;
line-height: 1.45;
color: #8B0000;
}
.info-panel .info-line {
margin: 1px 0;
}
.bottom-panel {
background: #d3d3d3;
padding: 8px 10px;
border-top: 1px solid #999;
display: flex;
justify-content: space-between;
align-items: flex-start;
gap: 20px;
font-size: 11px;
}
.bottom-panel .bottom-left {
display: flex;
flex-direction: column;
gap: 4px;
}
.bottom-panel .bottom-row {
display: grid;
grid-template-columns: 140px 80px 130px 130px;
column-gap: 8px;
align-items: center;
}
.bottom-panel .bottom-row label {
min-width: 140px;
}
.bottom-panel input {
width: 80px;
font-size: 11px;
}
.bottom-panel button {
padding: 4px 12px;
width: 130px;
background: linear-gradient(to bottom, #e8e8e8, #cfcfcf);
border: 1px solid #868686;
cursor: pointer;
border-radius: 2px;
font-size: 11px;
box-shadow: inset 0 1px 0 rgba(255,255,255,0.7);
}
.bottom-panel button:hover {
background: linear-gradient(to bottom, #f0f0f0, #d4d4d4);
}
.bottom-panel button:active {
background: linear-gradient(to bottom, #c9c9c9, #b2b2b2);
}
.bottom-panel label {
font-size: 11px;
margin-right: 3px;
}
.bottom-panel .weights {
display: flex;
gap: 8px;
align-items: center;
}
.bottom-panel .cost-info {
color: #8B0000;
font-weight: bold;
font-size: 16px;
}
.bottom-panel .bottom-right {
display: flex;
flex-direction: column;
align-items: flex-end;
gap: 6px;
text-align: right;
position: relative;
min-height: 90px;
}
.bottom-panel .bottom-right::before {
content: "";
position: absolute;
inset: 10px 0 0 0;
opacity: 0.5;
background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAKIAAABGCAYAAABPJ0+PAAAAxXpUWHRSYXcgcHJvZmlsZSB0eXBlIGV4aWYAAHjabVBbDsMgDPvnFDtCXkA4Dh2dtBvs+AskndpqljBgRyYk7Z/3Kz0mCCVJrlpaKWCQJo26HRQcfTGCLHaJwsOrnqSEQSax7exXDR0PHX8BvnU75VOQPsPYrkaTyNdbUDzEs6PZ3YigFkFMbmAEdP8WlKb1/IVthyvUV5o0Kq4ybO7d71JteiPbO0y0MzIYMxdvgOfKifs0jIErudynYZxXKfpA/s3pQPoCaaFZvSULF9cAAAGDaUNDUElDQyBwcm9maWxlAAB4nH2RPUjDUBSFT1OlUioOZlBxyFCd7KIijrUKRagQaoVWHUxe+gdNWpIUF0fBteDgz2LVwcVZVwdXQRD8AXEXnBRdpMT7kkKLGB9c3sd57xzuuw8QmhWmWz1xQDdsM51MSNncqhR6RRghqmGICrNqc7Kcgu/6ukeA73cxnuV/78/Vr+UtBgQk4jirmTbxBvHMpl3jvE8sspKiEZ8TT5jUIPEj11WP3zgXXRZ4pmhm0vPEIrFU7GK1i1nJ1ImniaOablC+kPVY47zFWa/UWbtP/sJI3lhZ5jrVKJJYxBJkSFBRRxkV2IjRbpBiIU3nCR//iOuXyaWSqwxGjgVUoUNx/eB/8Hu2VmFq0kuKJIDeF8f5GANCu0Cr4Tjfx47TOgGCz8CV0fFXm8DsJ+mNjhY9Aga2gYvrjqbuAZc7wNBTTTEVVwpSCYUC8H5G35QDBm+B8Jo3t/Y5Th+ADM0qdQMcHALjRcpe93l3X/fc/r3Tnt8PVC5ymoM5BWcAAA14aVRYdFhNTDpjb20uYWRvYmUueG1wAAAAAAA8P3hwYWNrZXQgYmVnaW49Iu+7vyIgaWQ9Ilc1TTBNcENlaGlIenJlU3pOVGN6a2M5ZCI/Pgo8eDp4bXBtZXRhIHhtbG5zOng9ImFkb2JlOm5zOm1ldGEvIiB4OnhtcHRrPSJYTVAgQ29yZSA0LjQuMC1FeGl2MiI+CiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPgogIDxyZGY6RGVzY3JpcHRpb24gcmRmOmFib3V0PSIiCiAgICB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIKICAgIHhtbG5zOnN0RXZ0PSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VFdmVudCMiCiAgICB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iCiAgICB4bWxuczpHSU1QPSJodHRwOi8vd3d3LmdpbXAub3JnL3htcC8iCiAgICB4bWxuczp0aWZmPSJodHRwOi8vbnMuYWRvYmUuY29tL3RpZmYvMS4wLyIKICAgIHhtbG5zOnhtcD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLyIKICAgeG1wTU06RG9jdW1lbnRJRD0iZ2ltcDpkb2NpZDpnaW1wOjYxYzFlNTI4LTM4NWQtNDYzNC05MzM1LTJjOTI4NDNjODM3NyIKICAgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDo5ZTczZDE0OC0zOTZlLTQ4MTctYjFlZi0wMWRmMzU5YjI1NWYiCiAgIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDoxNjFkMDhiYS00MWMxLTQ3NDQtODhiZS1kZjg5YTdlMmNlODciCiAgIGRjOkZvcm1hdD0iaW1hZ2UvcG5nIgogICBHSU1QOkFQST0iMi4wIgogICBHSU1QOlBsYXRmb3JtPSJMaW51eCIKICAgR0lNUDpUaW1lU3RhbXA9IjE3NTk4MzI3NTY1MzI2NjYiCiAgIEdJTVA6VmVyc2lvbj0iMi4xMC4zNiIKICAgdGlmZjpPcmllbnRhdGlvbj0iMSIKICAgeG1wOkNyZWF0b3JUb29sPSJHSU1QIDIuMTAiCiAgIHhtcDpNZXRhZGF0YURhdGU9IjIwMjU6MTA6MDdUMjA6MjU6NTYrMTA6MDAiCiAgIHhtcDpNb2RpZnlEYXRlPSIyMDI1OjEwOjA3VDIwOjI1OjU2KzEwOjAwIj4KICAgPHhtcE1NOkhpc3Rvcnk+CiAgICA8cmRmOlNlcT4KICAgICA8cmRmOmxpCiAgICAgIHN0RXZ0OmFjdGlvbj0ic2F2ZWQiCiAgICAgIHN0RXZ0OmNoYW5nZWQ9Ii8iCiAgICAgIHN0RXZ0Omluc3RhbmNlSUQ9InhtcC5paWQ6NmU1MDYxYzQtZGYwYS00MzgyLWEwZTMtODMxNGFjMjcxNDcwIgogICAgICBzdEV2dDpzb2Z0d2FyZUFnZW50PSJHaW1wIDIuMTAgKExpbnV4KSIKICAgICAgc3RFdnQ6d2hlbj0iMjAyNS0xMC0wN1QyMDoyNTo1NisxMDowMCIvPgogICAgPC9yZGY6U2VxPgogICA8L3htcE1NOkhpc3Rvcnk+CiAgPC9yZGY6RGVzY3JpcHRpb24+CiA8L3JkZjpSREY+CjwveDp4bXBtZXRhPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgCjw/eHBhY2tldCBlbmQ9InciPz7Wt35cAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAEdcAABHXAFzXhzCAAAAB3RJTUUH6QoHChk4lG1UogAAB5dJREFUeNrtXduW5CAItHL6/3+ZfeptYwRBQZOJmZfZnW7jpSwKRE3pyQ8lSvv5E8+xu2A/d3iwnMXQWQdK1P3d/dzu+Sw3pd//8wBVy1Rv4L6cES1argcs0eXv5w8Asceh0IKl11nZYHyZsxLp1W6PeQMxmKMRDsIN4g3E2zwbjC/SiBEazhtAkXoxr+vWpYuAOAqY78Dl4Z1IFhsBirZeG4wPBOJd9emsiMDWiA8weU/WmFubLtCIT+v4GR47Jz9ezpT3W1nR6MVIINbeEaVPc/C9HIw7+6YGji8g8t+fMGFfCcS3re9Gt+HlpvkYnsH5j5ZpekG/crAi2Go7LkaNWNMvI3mF1gEotdQMVloJkhey46ECCtcxvSxn+c4KcGymmv58ugeqxpDeACvLWwHK6BWdrRONGrEcjHIdtRVikQYzHwBkPxGDxpXNlTeTHV/MxFAzXi/zRQ2OJzvOTLLYrGhgxK85Kr3hEoSrTJbHYOWxwv3cjBEtYRIPndgqk8u8yf89supSq+tq83itEw1HPR4BxN+A1htYA8N1aercbb17TsB0/ffdNUDWBoxM089DPkSLpbklLGREunRsjWk4BoGxM2psBuPgaiZDfd04QsemG8KnP0Zsa61LzT/ZwNWLbjktP7byNNEyA2u6/CkeKAZBjqEJdJtEC7BNJkVIo8Vo2pAIx4ZlmlSNnWtsaGlx9NbVPgD1hZIkaRPL/fDrDquJrTkWHCBazgVE4ucdoj6T3gdETzDCgd/W7OmeAMSW1uL1oR4UaLyPA8f1PX1saAV9BBBhHMgZOZg+SngIjJ9m0d+OqIGxNJckVEcXEkrsqkeLgSOfWCbUrSBFAbJvpyG84wVtL7hlvkr9ptWKEoNKLCxp2jFVzLP2KhB6TIje9qBhO5w9aD1ALMtgOiDrgGQF8SgQtYNnXWHCIAg9JgUGnSDZtRwC48GYQV/vUEqY0HS+1sue+eT1gtkHng9CqQwp0WSS/LGZPsuMgjFUZDEtI0D0YkQ9++j7cabDol/G1Th3w6x4dPtIPY3yMS/r2bCXyTV/m1m/EZpybsehBheSLtfwt9pSb5QVSK19MTNWBrze8exsQ7AmnMZbepg6DopqWmYhmbuCr9/T0rlWreh4TZvWeBvrfZg7S8tKEit6dVOeU+ixbuoN5r+T6YgmIIlxUpWAtAtWOeyjzc7xYUPupAR5W8I87mitoa90VuR6jFAIKZwbIyOOsmKk0bhjUusK1l0bWbGSB8uQH8PAt6smnRtjXRO2dsWs83FWgdBrRTi+BJzKqOWFVuKvH3UHanIOuZdCrKoPG7bkQavLsSjIHMVfZGgD/JfsTm9QkIQt7ciaJiZ9Ssqilt+gT01rtYEcmMqmSTGNqfRLniO2qo/PK4kWH0NRfZnYEAnc3rlw76J+YOvYlJxaAFcnDFOXBs7trjDkMYxz27qxjy7iVmlyhq19xsp83Lbapzs6d3GYMkD26SJNdg2fxaMHJxz1HBdemHMppb/2Gk0E0aV5eTs+A+Gblg5C0iU9/DThGBvOPl74rqyiPZplbd3V7zlMlf+ZOzsQuJMVoIo/2U4eu8M5Nu0WzZsUqOizeDY0ibPDrZvPWwrk19eaLgFthjaaD9I5rJ0vf0Zlnzuw4jGtG1uiX9pqEAsH2zEr/QDGVDDKLJiW9LXwlsMV95akVVJrTbiDbmRwy8lkq99cMK7oo05WHHNWyHmGSd+/SzJpbu78BoUWAJLEvp7Mip8hdhid0VJ4t3b+TvQFjpqTLbRH86GRvdlWz7jFpPNlxcElPgkIvWle0hbUGhBhSD2b7SRYdiDOM80wvBOTMqpYG3qoAdjj0aKzq0gttu+1QtCnt5AiFi6tn52TVtcZ0NZWjhpAyquAdD0LZ/bhwPc0Y3AApfb7GBrvgHGA+4Bc9eMvWYKy+Vf+3co8Ld2Yn0CBIPCl1M6DjDmJa/6F78HWJ9q0UZZqxQMxYuaV7gAG9DAXrtFtybSZ7chw1ejJZ4FgnKGxKOVRQhTiGIU3VXrSUojIvleklA0kerJULQ9Vpud8RFQmna72XN1wgQuYiW0PY/OWLBiMM28nxYkNcWnUb6EvFZ+rQVu3PRyKwDk/8FfY+LJh3o7z7+XfiQWw7Mjhos45/l+sGe9+Te75ulpdkup3ANsev2wZ0DTF3DbaVh5l7Rre8+/nVkuakU5QHtHz+g0cAWD83ByItmMxKKDDNHmVdeY4u2FI8UcI038boJ+waEoq9Qaov8KIXx4jUaGVRoaSfoGs/jmLoS+N+XVwJNYbswzn/iAHUPTenRjAjjMYESelAxZgeailtc8Dyqt7+RKsjlvuwiBp1mZJZNUYO0HObMVvxaiFyh7DiLaLHuXgrHSxIwrWgBpkct2ul1dey0dISKa++WjsnS1N2HZgHK/Du7uz0juHiTGJV3BTku9p0Zika+Jp7rFSE1g906N2IhelkX2CZN6COnIA6+014lUz6tVeusTUKNNufKdeP0UX77t8JzJQ2zZRWe4+oKRfse7dw0JZcApqueJ8UecsIKJwQWCYe+h6nxSGSUwnnk0N3+Ekeur6OunagEZiCVzGRbp7TAv+gbr8A6402iXlSyL4AAAAAElFTkSuQmCC');
background-repeat: no-repeat;
background-position: right top;
background-size: 150px auto;
pointer-events: none;
}
.bottom-panel .version-text {
color: #0b5fa4;
font-weight: bold;
font-size: 12px;
}
.profile-string {
width: 100%;
padding: 5px;
font-family: monospace;
font-size: 8px;
border: 1px solid #999;
background: #fff;
margin: 5px 0;
white-space: nowrap;
overflow-x: auto;
letter-spacing: -0.5px;
}
.help-button {
background: #e8e8e8;
border: 1px solid #999;
padding: 3px 8px;
cursor: pointer;
border-radius: 2px;
font-size: 11px;
}
.help-button:hover {
background: #d8d8d8;
}
h3 {
font-size: 13px;
margin: 3px 0;
color: #000;
font-weight: bold;
}
/* Старые стили micro-inputs удалены - теперь используем абсолютное позиционирование как в легаси версии */
.concentrates-section {
margin: 15px 0;
}
.preparation-section {
margin: 0;
}
#preparation h3 {
margin-top: 2px;
margin-bottom: 3px;
font-size: 13px;
}
.preparation-complex {
margin: 2px 0;
padding: 1px 0;
}
.prep-complex-text {
font-size: 11px;
color: #000;
font-weight: normal;
line-height: 1.2;
}
.preparation-header {
display: flex;
align-items: center;
padding: 0;
gap: 6px;
font-weight: bold;
font-size: 10px;
color: #333;
border-bottom: 1px solid #999;
margin-bottom: 1px;
height: 12px;
}
.prep-checkbox-header {
width: 18px;
text-align: center;
flex-shrink: 0;
font-size: 10px;
font-weight: bold;
}
.prep-input-header {
width: 54px;
text-align: center;
flex-shrink: 0;
font-size: 10px;
font-weight: bold;
}
.prep-value-header {
width: 54px;
text-align: center;
flex-shrink: 0;
font-size: 10px;
font-weight: bold;
}
.prep-label-header {
flex: 1;
text-align: left;
flex-shrink: 0;
font-size: 10px;
font-weight: bold;
}
.preparation-section h4 {
color: #000;
margin-bottom: 1px;
margin-top: 1px;
font-size: 11px;
font-weight: bold;
height: 12px;
line-height: 1;
}
.preparation-list {
background: #f8f9fa;
border-radius: 6px;
padding: 10px;
margin-bottom: 15px;
}
.preparation-item {
display: flex;
align-items: center;
padding: 0;
gap: 6px;
height: 18px;
margin: 0;
}
.prep-label {
font-weight: normal;
color: #000;
flex: 1;
height: 14px;
font-size: 10px;
margin: 0;
display: flex;
align-items: center;
flex-shrink: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
line-height: 1.2;
}
.prep-value {
font-weight: normal;
color: #0000FF;
background: transparent;
padding: 0;
border-radius: 0;
font-size: 10px;
width: 54px;
height: 18px;
text-align: right;
display: flex;
align-items: center;
justify-content: flex-end;
padding-right: 2px;
box-sizing: border-box;
flex-shrink: 0;
line-height: 1.2;
}
.preparation-subgroup {
margin-bottom: 4px;
padding-left: 0;
}
.preparation-subgroup h5 {
margin: 1px 0 0 0;
color: #000;
font-size: 10px;
font-weight: bold;
height: 16px;
line-height: 1.2;
}
.prep-input {
width: 54px;
height: 18px;
padding: 1px 2px;
border: 1px solid #808080;
border-radius: 0;
font-size: 10px;
text-align: right;
box-sizing: border-box;
background: #ffffff;
color: #000;
}
.prep-checkbox {
width: 18px;
height: 18px;
margin: 0;
flex-shrink: 0;
accent-color: #000;
}
.mixer-input {
width: 240px;
height: 21px;
padding: 1px 2px;
border: 1px solid #808080;
border-radius: 0;
font-size: 11px;
box-sizing: border-box;
background: #ffffff;
color: #000;
}
.mixer-number {
width: 68px;
height: 21px;
padding: 1px 2px;
border: 1px solid #808080;
border-radius: 0;
font-size: 11px;
text-align: right;
box-sizing: border-box;
background: #ffffff;
color: #000;
}
.preparation-buttons {
display: flex;
flex-direction: row;
gap: 8px;
margin-top: 4px;
align-items: center;
}
.prep-button {
height: 23px;
padding: 2px 8px;
border: 1px solid #808080;
cursor: pointer;
box-sizing: border-box;
font-size: 11px;
white-space: nowrap;
}
.prep-button:hover {
background: #e0e0e0;
}
.prep-button:active {
background: #d0d0d0;
}
.price-section {
margin: 10px 0;
}
.price-section h4 {
color: #000;
margin: 6px 0;
font-size: 13px;
font-weight: bold;
}
.price-inputs {
padding: 0;
margin: 0 0 10px 0;
background: none;
border-radius: 0;
}
.price-item {
display: flex;
align-items: center;
gap: 8px;
font-size: 12px;
margin: 2px 0;
}
.price-item input[type="number"],
.price-item input[type="text"].price-input-custom {
width: 70px;
padding: 2px 20px 2px 4px;
border: 1px solid #808080;
text-align: right;
font-size: 12px;
box-sizing: border-box;
}
/* Контейнер для поля ввода с кастомными крутилками */
.price-input-wrapper {
position: relative;
display: inline-block;
}
/* Кастомные крутилки */
.price-spinner {
position: absolute;
right: 1px;
top: 1px;
bottom: 1px;
width: 16px;
display: flex;
flex-direction: column;
background: #f0f0f0;
border-left: 1px solid #808080;
}
.price-spinner button {
flex: 1;
margin: 0;
padding: 0;
border: none;
background: #f0f0f0;
cursor: pointer;
font-size: 8px;
line-height: 1;
color: #333;
display: flex;
align-items: center;
justify-content: center;
}
.price-spinner button:hover {
background: #e0e0e0;
}
.price-spinner button:active {
background: #d0d0d0;
}
.price-spinner button:first-child {
border-bottom: 1px solid #808080;
}
.price-name {
flex: 1;
text-align: left;
}
.price-result {
min-width: 130px;
text-align: left;
color: #000;
font-weight: normal;
}
.price-total {
margin-top: 10px;
font-size: 13px;
font-weight: bold;
color: #a40000;
}
.concentrate-header {
font-weight: bold;
font-size: 14px;
margin: 8px 0 4px 0;
padding: 4px;
background: #e8e8e8;
}
.concentrate-table {
width: 100%;
border-collapse: collapse;
font-size: 11px;
margin-bottom: 8px;
}
.concentrate-table th {
background: #e0e0e0;
padding: 3px 4px;
border: 1px solid #999;
font-weight: bold;
font-size: 11px;
}
.concentrate-table td {
padding: 2px 4px;
border: 1px solid #ddd;
}
.concentrate-table input {
width: 65px;
font-size: 11px;
padding: 1px 3px;
}
/* Корректор - легаси стиль с масштабированием /2 * 1.7 */
.corrector-container {
position: relative;
min-height: 571px;
max-height: 571px;
background: #f0f0f0;
font-family: Arial, sans-serif;
}
.corrector-header {
position: absolute;
top: 7px;
font-size: 11px;
}
.corrector-btn-fill {
position: absolute;
top: 27px;
width: 71px;
height: 27px;
font-size: 11px;
background: #e0e0e0;
border: 1px solid #999;
cursor: pointer;
box-sizing: border-box;
padding: 0 2px;
}
.corrector-btn-fill:hover {
background: #d0d0d0;
}
.corrector-label {
position: absolute;
font-size: 15px;
font-weight: bold;
}
.corrector-input {
position: absolute;
width: 71px !important;
height: 24px;
font-size: 13px;
font-family: Arial, sans-serif;
text-align: right;
padding: 2px 3px;
border: 1px solid #999;
background: white;
box-sizing: border-box;
}
.corrector-input:read-only {
background: #e8e8e8;
color: #666;
}
.corrector-volume {
position: absolute;
top: 340px;
width: 71px !important;
height: 24px;
font-size: 13px;
font-family: Arial, sans-serif;
text-align: right;
padding: 2px 3px;
border: 1px solid #999;
background: white;
box-sizing: border-box;
}
.corrector-btn-calc {
position: absolute;
top: 372px;
width: 71px;
height: 27px;
font-size: 11px;
background: #e0e0e0;
border: 1px solid #999;
cursor: pointer;
box-sizing: border-box;
padding: 0 2px;
}
.corrector-btn-calc:hover {
background: #d0d0d0;
}
.corrector-btn-journal {
position: absolute;
top: 401px;
left: 321px;
width: 71px;
height: 27px;
font-size: 11px;
background: #e0e0e0;
border: 1px solid #999;
cursor: pointer;
box-sizing: border-box;
padding: 0 2px;
}
.corrector-btn-journal:hover {
background: #d0d0d0;
}
.corrector-protocol {
position: absolute;
top: 27px;
left: 394px;
width: 297px;
height: 539px;
font-size: 10px;
font-family: Arial, sans-serif;
padding: 3px;
border: 1px solid #999;
background: white;
overflow-y: auto;
white-space: pre-wrap;
line-height: 1.4;
}
.corrector-protocol-header {
position: absolute;
top: 7px;
left: 442px;
font-size: 11px;
}
.corrector-btn-help {
position: absolute;
top: 0px;
left: 598px;
width: 43px;
height: 17px;
font-size: 10px;
background: #e0e0e0;
border: 1px solid #999;
cursor: pointer;
}
.corrector-btn-help:hover {
background: #d0d0d0;
}
.tabs-inner {
display: flex;
background: #e8e8e8;
border-bottom: 1px solid #ccc;
margin: -5px 0 2px 0;
}
.tab-inner {
padding: 5px 12px;
cursor: pointer;
border-right: 1px solid #999;
background: #d8d8d8;
user-select: none;
font-size: 12px;
border-bottom: 0.25px solid #999;
position: relative;
}
.tab-inner:hover {
background: #e0e0e0;
}
.tab-inner.active {
background: #f0f0f0;
border-bottom: 0.5px solid #f0f0f0;
margin-bottom: -0.5px;
z-index: 1;
}
.inner-tab-content {
display: none;
border-top: 1px solid #ccc;
padding: 15px;
background: #f0f0f0;
}
.inner-tab-content.active {
display: block;
}
.volume-inputs {
display: flex;
gap: 8px;
align-items: center;
margin: 8px 0;
}
.volume-inputs label {
font-weight: bold;
font-size: 11px;
}
.volume-inputs input {
width: 80px;
font-size: 11px;
}
.volume-inputs span {
font-size: 11px;
}
/* ========== МОБИЛЬНАЯ ВЕРСИЯ (полностью отдельная) ========== */
/* Скрываем десктопную версию на мобильных */
@media only screen and (max-width: 768px) {
.container {
display: none;
}
}
/* Показываем мобильную версию только на мобильных */
.mobile-version {
display: none;
}
@media only screen and (max-width: 768px) {
.mobile-version {
display: block;
padding: 10px;
background: #f0f0f0;
font-family: Arial, sans-serif;
}
.mobile-header {
background: #4a90e2;
color: white;
padding: 15px;
text-align: center;
font-size: 18px;
font-weight: bold;
margin: -10px -10px 15px -10px;
}
.mobile-notice {
background: #fff3cd;
border: 1px solid #ffc107;
padding: 15px;
border-radius: 5px;
margin-bottom: 15px;
}
.mobile-notice h3 {
margin: 0 0 10px 0;
color: #856404;
}
.mobile-notice p {
margin: 5px 0;
line-height: 1.6;
}
.mobile-link {
display: inline-block;
background: #4a90e2;
color: white;
padding: 12px 20px;
text-decoration: none;
border-radius: 5px;
margin-top: 10px;
}
.mobile-features {
background: white;
padding: 15px;
border-radius: 5px;
margin-top: 15px;
}
.mobile-features h4 {
margin-top: 0;
}
.mobile-features ul {
padding-left: 20px;
}
.mobile-features li {
margin: 8px 0;
}
.mobile-input-group {
margin-bottom: 12px;
}
.mobile-input-group label {
display: block;
margin-bottom: 5px;
font-weight: bold;
color: #333;
}
.mobile-input-group input {
width: 100%;
padding: 10px;
font-size: 16px;
border: 2px solid #ddd;
border-radius: 5px;
box-sizing: border-box;
}
.mobile-input-group input:focus {
outline: none;
border-color: #4a90e2;
}
/* Спойлеры (аккордеоны) */
.mobile-accordion {
background: white;
border-radius: 8px;
margin-bottom: 10px;
overflow: hidden;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
.mobile-accordion-header {
padding: 15px;
background: linear-gradient(to right, #4a90e2, #357abd);
color: white;
cursor: pointer;
display: flex;
justify-content: space-between;
align-items: center;
font-weight: bold;
font-size: 16px;
user-select: none;
}
.mobile-accordion-header:active {
background: linear-gradient(to right, #357abd, #2868a8);
}
.mobile-accordion-icon {
transition: transform 0.3s;
font-size: 20px;
}
.mobile-accordion-icon.open {
transform: rotate(180deg);
}
.mobile-accordion-content {
max-height: 0;
overflow: hidden;
transition: max-height 0.3s ease-out;
padding: 0 15px;
}
.mobile-accordion-content.open {
max-height: 2000px;
padding: 15px;
}
/* Компактные поля для соотношений */
.ratio-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 8px;
margin-top: 10px;
}
.ratio-item {
padding: 8px;
background: #f8f9fa;
border-radius: 5px;
text-align: center;
font-size: 13px;
}
.ratio-item.optimal {
background: #d4edda;
border: 2px solid #28a745;
font-weight: bold;
}
.ratio-label {
font-size: 11px;
color: #666;
margin-bottom: 3px;
}
.ratio-value {
font-size: 15px;
font-weight: bold;
color: #333;
}
}
</style>
</head>
<body>
<div class="container">
<div class="header">Hydroponic Profile Generator (HPG)</div>
<div class="tab-bar">
<div class="tab active" onclick="switchTab('macro', this)">Макро</div>
<div class="tab" onclick="switchTab('micro', this)">Микро</div>
<div class="tab" onclick="switchTab('concentrates', this)">Концентраты</div>
<div class="tab" onclick="switchTab('corrector', this)">Корректор</div>
<div class="tab" onclick="switchTab('file', this)">Файл</div>
<div class="tab" onclick="switchTab('help', this)">Справка</div>
</div>
<!-- МАКРО -->
<div id="macro" class="tab-content active">
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 6px;">
<h3>Макропрофиль в мг/л (ppm)</h3>
<button class="help-button" onclick="showMacroHelp()">help</button>
</div>
<div class="section-title">Расчет макропрофиля и навесок солей</div>
<table style="width: 100%; border-collapse: collapse; font-size: 11px; margin: 5px 0;">
<tr>
<td style="width: 45px; font-weight: bold; text-align: right; padding: 2px 5px 2px 0;"></td>
<td style="width: 75px; font-weight: bold; text-align: center; padding: 2px;">N</td>
<td style="width: 75px; font-weight: bold; text-align: center; padding: 2px;">P</td>
<td style="width: 75px; font-weight: bold; text-align: center; padding: 2px;">K</td>
<td style="width: 75px; font-weight: bold; text-align: center; padding: 2px;">Ca</td>
<td style="width: 75px; font-weight: bold; text-align: center; padding: 2px;">Mg</td>
<td style="width: 75px; font-weight: bold; text-align: center; padding: 2px;">S</td>
<td style="width: 75px; font-weight: bold; text-align: center; padding: 2px;">Cl</td>
<td style="width: 75px; font-weight: bold; text-align: center; padding: 2px;">EC</td>
</tr>
<tr>
<td></td>
<td style="text-align: center; padding: 2px;"><input type="number" id="N" value="220.000" step="1" style="width: 68px;" oninput="onNChange()"></td>
<td style="text-align: center; padding: 2px;"><input type="number" id="P" value="40.000" step="1" class="highlight-green" style="width: 68px;" oninput="onPChange()"></td>
<td style="text-align: center; padding: 2px;"><input type="number" id="K" value="180.000" step="1" style="width: 68px;" oninput="onMacroChange()"></td>
<td style="text-align: center; padding: 2px;"><input type="number" id="Ca" value="200.000" step="1" style="width: 68px;" oninput="onMacroChange()"></td>
<td style="text-align: center; padding: 2px;"><input type="number" id="Mg" value="50.000" step="1" style="width: 68px;" oninput="onMacroChange()"></td>
<td style="text-align: center; padding: 2px;"><input type="number" id="S" value="73.049" step="1" style="width: 68px;" oninput="onSChange()"></td>
<td style="text-align: center; padding: 2px;"><input type="number" id="Cl" value="0.00" step="1" class="highlight-green" style="width: 68px;" oninput="onClChange()"></td>
<td style="text-align: center; padding: 2px;"><input type="number" id="EC" value="2.102" step="0.1" class="highlight-green" style="width: 68px;" oninput="onECChange()"></td>
</tr>
<tr>
<td style="font-weight: bold; text-align: right; padding: 2px 5px 2px 0;">NO3</td>
<td style="text-align: center; padding: 2px;"><input type="number" id="NO3" value="200.00" step="1" style="width: 68px;" oninput="onNO3Change()"></td>
<td colspan="2" style="padding: 2px 0 2px 10px;"><span id="nh4no3-info">NH4:NO3 1:10</span></td>
<td rowspan="2" colspan="5" style="text-align: center; vertical-align: middle; padding: 5px;">
<div style="display: inline-block; text-align: left; font-size: 11px; color: #8B0000;">
<div id="ratio-info" style="margin: 2px 0;">N:1 : 0.18 : 0.82 : 0.91 : 0.23 : 0.33 : 0 sPPM=763.05</div>
<div id="npk-info" style="margin: 2px 0;">NPK: 22-9-22 CaO=28% MgO=8.3% SO3=18.2%</div>
</div>
</td>
</tr>
<tr>
<td style="font-weight: bold; text-align: right; padding: 2px 5px 2px 0;">NH4</td>
<td style="text-align: center; padding: 2px;"><input type="number" id="NH4" value="20.00" step="1" style="width: 68px;" oninput="onNH4Change()"></td>
<td style="text-align: center; padding: 2px;"><input type="number" id="NH4_ratio" value="0.100" step="0.001" class="highlight-green" style="width: 68px;" oninput="onNH4RatioChange()"></td>
<td></td>
</tr>
</table>
<div class="section-title">Матрица соотношений элементов</div>
<table class="matrix-table">
<thead>
<tr>
<th></th>
<th>N</th>
<th>P</th>
<th>K</th>
<th>Ca</th>
<th>Mg</th>
<th>S</th>
</tr>
</thead>
<tbody>
<tr>
<th>N</th>
<td class="diagonal">1</td>
<td><input type="number" id="m_P_N" value="5.500" step="0.001" oninput="onMatrixChange('P','N')"></td>
<td><input type="number" id="m_K_N" value="1.222" step="0.001" class="highlight-green" oninput="onMatrixChange('K','N')"></td>
<td><input type="number" id="m_Ca_N" value="1.100" step="0.001" oninput="onMatrixChange('Ca','N')"></td>
<td><input type="number" id="m_Mg_N" value="4.400" step="0.001" oninput="onMatrixChange('Mg','N')"></td>
<td><input type="number" id="m_S_N" value="3.012" step="0.001" oninput="onMatrixChange('S','N')"></td>
</tr>
<tr>
<th>P</th>
<td><input type="number" id="m_N_P" value="0.182" step="0.001" oninput="onMatrixChange('N','P')"></td>
<td class="diagonal">1</td>
<td><input type="number" id="m_K_P" value="0.222" step="0.001" oninput="onMatrixChange('K','P')"></td>
<td><input type="number" id="m_Ca_P" value="0.200" step="0.001" class="highlight-blue" oninput="onMatrixChange('P','Ca')"></td>
<td><input type="number" id="m_Mg_P" value="0.800" step="0.001" oninput="onMatrixChange('Mg','P')"></td>
<td><input type="number" id="m_S_P" value="0.548" step="0.001" oninput="onMatrixChange('S','P')"></td>
</tr>
<tr>
<th>K</th>
<td><input type="number" id="m_N_K" value="0.818" step="0.001" oninput="onMatrixChange('N','K')"></td>
<td><input type="number" id="m_P_K" value="4.500" step="0.001" oninput="onMatrixChange('P','K')"></td>
<td class="diagonal">1</td>
<td><input type="number" id="m_Ca_K" value="0.900" step="0.001" oninput="onMatrixChange('Ca','K')"></td>
<td><input type="number" id="m_Mg_K" value="3.600" step="0.001" oninput="onMatrixChange('Mg','K')"></td>
<td><input type="number" id="m_S_K" value="2.464" step="0.001" oninput="onMatrixChange('S','K')"></td>
</tr>
<tr>
<th>Ca</th>
<td><input type="number" id="m_N_Ca" value="0.909" step="0.001" oninput="onMatrixChange('N','Ca')"></td>
<td><input type="number" id="m_P_Ca" value="5.000" step="0.001" oninput="onMatrixChange('P','Ca')"></td>
<td><input type="number" id="m_K_Ca" value="1.111" step="0.001" class="highlight-green" oninput="onMatrixChange('K','Ca')"></td>
<td class="diagonal">1</td>
<td><input type="number" id="m_Mg_Ca" value="4.000" step="0.001" oninput="onMatrixChange('Mg','Ca')"></td>
<td><input type="number" id="m_S_Ca" value="2.738" step="0.001" oninput="onMatrixChange('S','Ca')"></td>
</tr>
<tr>
<th>Mg</th>
<td><input type="number" id="m_N_Mg" value="0.227" step="0.001" oninput="onMatrixChange('N','Mg')"></td>
<td><input type="number" id="m_P_Mg" value="1.250" step="0.001" oninput="onMatrixChange('P','Mg')"></td>
<td><input type="number" id="m_K_Mg" value="0.278" step="0.001" class="highlight-green" oninput="onMatrixChange('K','Mg')"></td>
<td><input type="number" id="m_Ca_Mg" value="0.250" step="0.001" oninput="onMatrixChange('Ca','Mg')"></td>
<td class="diagonal">1</td>
<td><input type="number" id="m_S_Mg" value="0.684" step="0.001" oninput="onMatrixChange('S','Mg')"></td>
</tr>
<tr>
<th>S</th>
<td><input type="number" id="m_N_S" value="0.332" step="0.001" oninput="onMatrixChange('N','S')"></td>
<td><input type="number" id="m_P_S" value="1.826" step="0.001" oninput="onMatrixChange('P','S')"></td>
<td><input type="number" id="m_K_S" value="0.406" step="0.001" oninput="onMatrixChange('K','S')"></td>
<td><input type="number" id="m_Ca_S" value="0.365" step="0.001" oninput="onMatrixChange('Ca','S')"></td>
<td><input type="number" id="m_Mg_S" value="1.461" step="0.001" oninput="onMatrixChange('Mg','S')"></td>
<td class="diagonal">1</td>
</tr>
</tbody>
</table>
<div class="section-title">Составы солей</div>
<table class="salts-table">
<thead>
<tr style="border-bottom: 1px solid #999;">
<th style="text-align: left; font-weight: normal;"></th>
<th colspan="2" style="text-align: center; font-weight: normal; font-size: 11px; color: #8B0000;">%</th>
<th colspan="2" style="text-align: center; font-weight: normal; font-size: 11px; color: #8B0000;">%</th>
<th colspan="2" style="text-align: center; font-weight: normal; font-size: 11px; color: #8B0000;">%</th>
<th style="text-align: center; font-weight: normal; font-size: 11px; color: #8B0000;">граммы</th>
</tr>
</thead>
<tbody>
<tr>
<td><span id="label_CaNO3">Кальций азотнокислый Ca(NO3)2*4H2O</span></td>
<td style="width: 30px; font-weight: bold;">Ca</td>
<td style="width: 70px;"><input type="number" id="salt_CaNO3_Ca" value="16.972" step="0.001" oninput="onSaltCompositionChange('CaNO3','Ca')"></td>
<td style="width: 40px; font-weight: bold;">NO3</td>
<td style="width: 70px;"><input type="number" id="salt_CaNO3_NO3" value="11.863" step="0.001" oninput="onSaltCompositionChange('CaNO3','NO3')"></td>
<td style="width: 40px; font-weight: bold;">NH4</td>
<td style="width: 70px;"><input type="number" id="salt_CaNO3_NH4" value="0.000" step="0.001" oninput="onSaltCompositionChange('CaNO3','NH4')"></td>
<td style="width: 70px;"><input type="number" id="g_CaNO3" value="11.78" step="0.01" oninput="onSaltWeightChange()"></td>
</tr>
<tr>
<td><span id="label_KNO3">Калий азотнокислый KNO3</span></td>
<td style="font-weight: bold;">K</td>
<td><input type="number" id="salt_KNO3_K" value="38.672" step="0.001" oninput="onSaltCompositionChange('KNO3','K')"></td>
<td style="font-weight: bold;">NO3</td>
<td><input type="number" id="salt_KNO3_NO3" value="13.854" step="0.001" oninput="onSaltCompositionChange('KNO3','NO3')"></td>
<td></td>
<td></td>
<td><input type="number" id="g_KNO3" value="2.90" step="0.01" oninput="onSaltWeightChange()"></td>
</tr>
<tr>
<td><span id="label_NH4NO3">Аммоний азотнокислый NH4NO3</span></td>
<td style="font-weight: bold;">NH4</td>
<td><input type="number" id="salt_NH4NO3_NH4" value="17.499" step="0.001" oninput="onSaltCompositionChange('NH4NO3','NH4')"></td>
<td style="font-weight: bold;">NO3</td>
<td><input type="number" id="salt_NH4NO3_NO3" value="17.499" step="0.001" oninput="onSaltCompositionChange('NH4NO3','NO3')"></td>
<td></td>
<td></td>
<td><input type="number" id="g_NH4NO3" value="1.14" step="0.01" oninput="onSaltWeightChange()"></td>
</tr>
<tr>
<td><span id="label_MgSO4">Магний сернокислый MgSO4*7H2O</span></td>
<td style="font-weight: bold;">Mg</td>
<td><input type="number" id="salt_MgSO4_Mg" value="9.861" step="0.001" oninput="onSaltCompositionChange('MgSO4','Mg')"></td>
<td style="font-weight: bold;">S</td>
<td><input type="number" id="salt_MgSO4_S" value="13.010" step="0.001" oninput="onSaltCompositionChange('MgSO4','S')"></td>
<td></td>
<td></td>
<td><input type="number" id="g_MgSO4" value="5.07" step="0.01" oninput="onSaltWeightChange()"></td>
</tr>
<tr>
<td><span id="label_KH2PO4">Калий фосфорнокислый KH2PO4</span></td>
<td style="font-weight: bold;">K</td>
<td><input type="number" id="salt_KH2PO4_K" value="28.731" step="0.001" oninput="onSaltCompositionChange('KH2PO4','K')"></td>
<td style="font-weight: bold;">P</td>
<td><input type="number" id="salt_KH2PO4_P" value="22.761" step="0.001" oninput="onSaltCompositionChange('KH2PO4','P')"></td>
<td></td>
<td></td>
<td><input type="number" id="g_KH2PO4" value="1.76" step="0.01" oninput="onSaltWeightChange()"></td>
</tr>
<tr>
<td><span id="label_K2SO4">Калий сернокислый K2SO4</span></td>
<td style="font-weight: bold;">K</td>
<td><input type="number" id="salt_K2SO4_K" value="44.874" step="0.001" oninput="onSaltCompositionChange('K2SO4','K')"></td>
<td style="font-weight: bold;">S</td>
<td><input type="number" id="salt_K2SO4_S" value="18.401" step="0.001" oninput="onSaltCompositionChange('K2SO4','S')"></td>
<td></td>
<td class="checkbox-cell"><input type="checkbox" id="use_K2SO4" checked onchange="onSaltSelectionChange('K2SO4')"></td>
<td><input type="number" id="g_K2SO4" value="0.38" step="0.01" oninput="onSaltWeightChange()"></td>
</tr>
<tr>
<td><span id="label_MgNO3">Магний азотнокислый Mg(NO3)2*6H2O</span></td>
<td style="font-weight: bold;">Mg</td>
<td><input type="number" id="salt_MgNO3_Mg" value="9.479" step="0.001" oninput="onSaltCompositionChange('MgNO3','Mg')"></td>
<td style="font-weight: bold;">NO3</td>
<td><input type="number" id="salt_MgNO3_NO3" value="10.925" step="0.001" oninput="onSaltCompositionChange('MgNO3','NO3')"></td>
<td></td>
<td class="checkbox-cell"><input type="checkbox" id="use_MgNO3" onchange="onSaltSelectionChange('MgNO3')"></td>
<td><input type="number" id="g_MgNO3" value="0.00" step="0.01" oninput="onSaltWeightChange()"></td>
</tr>
<tr>
<td><span id="label_CaCl2">Хлорид кальция 6-водный CaCl2*6H2O</span></td>
<td style="font-weight: bold;">Ca</td>
<td><input type="number" id="salt_CaCl2_Ca" value="18.294" step="0.001" oninput="onSaltCompositionChange('CaCl2','Ca')"></td>
<td style="font-weight: bold;">Cl</td>
<td><input type="number" id="salt_CaCl2_Cl" value="32.366" step="0.001" oninput="onSaltCompositionChange('CaCl2','Cl')"></td>
<td></td>
<td></td>
<td><input type="number" id="g_CaCl2" value="0.00" step="0.01" oninput="onSaltWeightChange()"></td>
</tr>
</tbody>
</table>
</div>
<!-- МИКРО -->
<div id="micro" class="tab-content">
<!-- Верхняя строка с заголовками -->
<div style="position: relative; height: 30px; margin-bottom: 10px;">
<span style="position: absolute; left: 4px; top: 2px; font-size: 15px;">Микропрофиль в мкг/л</span>
<span style="position: absolute; left: 360px; top: 2px; font-size: 15px; font-weight: bold;">Расчет навесок микроэлементов</span>
<button class="help-button" onclick="showMicroHelp()" style="position: absolute; right: 3px; top: 0; width: 43px; height: 21px; font-size: 11px;">help</button>
</div>
<!-- Метки микроэлементов -->
<div style="position: relative; height: 20px; margin-top: 10px;">
<label style="position: absolute; left: 25px; top: 0; font-weight: bold;">Fe</label>
<label style="position: absolute; left: 95px; top: 0; font-weight: bold;">Mn</label>
<label style="position: absolute; left: 170px; top: 0; font-weight: bold;">B</label>
<label style="position: absolute; left: 245px; top: 0; font-weight: bold;">Zn</label>
<label style="position: absolute; left: 315px; top: 0; font-weight: bold;">Cu</label>
<label style="position: absolute; left: 390px; top: 0; font-weight: bold;">Mo</label>
<label style="position: absolute; left: 460px; top: 0; font-weight: bold;">Co</label>
<label style="position: absolute; left: 535px; top: 0; font-weight: bold;">Si</label>
</div>
<!-- Поля ввода микроэлементов -->
<div style="position: relative; height: 35px; margin-bottom: 15px;">
<input type="number" id="Fe" value="2000" step="1" oninput="onMicroChange()"
style="position: absolute; left: 10px; top: 0; width: 70px; height: 27px;">
<input type="number" id="Mn" value="550" step="1" oninput="onMicroChange()"
style="position: absolute; left: 82px; top: 0; width: 70px; height: 27px;">
<input type="number" id="B" value="500" step="1" oninput="onMicroChange()"
style="position: absolute; left: 154px; top: 0; width: 70px; height: 27px;">
<input type="number" id="Zn" value="330" step="1" oninput="onMicroChange()"
style="position: absolute; left: 226px; top: 0; width: 70px; height: 27px;">
<input type="number" id="Cu" value="63" step="1" oninput="onMicroChange()"
style="position: absolute; left: 298px; top: 0; width: 70px; height: 27px;">
<input type="number" id="Mo" value="63" step="1" oninput="onMicroChange()"
style="position: absolute; left: 370px; top: 0; width: 70px; height: 27px;">
<input type="number" id="Co" value="0" step="1" oninput="onMicroChange()"
style="position: absolute; left: 442px; top: 0; width: 70px; height: 27px;">
<input type="number" id="Si" value="0" step="1" oninput="onMicroChange()"
style="position: absolute; left: 514px; top: 0; width: 70px; height: 27px;">
</div>
<!-- Составы солей -->
<div id="salts-header" style="position: relative; height: 30px; margin-top: 10px;">
<span style="position: absolute; left: 8px; top: 0; font-size: 15px;">Составы солей</span>
<span style="position: absolute; left: 125px; top: 0; font-size: 15px;">доля (%)</span>
<span style="position: absolute; left: 215px; top: 0; font-size: 15px;">граммы</span>
</div>
<!-- Таблица солей с абсолютным позиционированием -->
<div id="salts-table" style="position: relative;">
<!-- Железо -->
<div style="position: relative; height: 28px;">
<label id="micro_Fe_label" style="position: absolute; left: 8px; top: 5px; font-size: 14px;">Железо Fe=20.1%</label>
<input type="number" id="micro_Fe_percent" value="20.1000" step="0.0001" oninput="onMicroCompositionChange()"
style="position: absolute; left: 120px; top: 2px; width: 80px; height: 23px;">
<input type="number" id="g_Fe" value="0.09950" step="0.00001" readonly
style="position: absolute; left: 205px; top: 2px; width: 80px; height: 23px;">
</div>
<!-- Марганец -->
<div style="position: relative; height: 28px;">
<label id="micro_Mn_label" style="position: absolute; left: 8px; top: 5px; font-size: 14px;">Марганец Mn=36.4%</label>
<input type="number" id="micro_Mn_percent" value="36.4000" step="0.0001" oninput="onMicroCompositionChange()"
style="position: absolute; left: 120px; top: 2px; width: 80px; height: 23px;">
<input type="number" id="g_Mn" value="0.01511" step="0.00001" readonly
style="position: absolute; left: 205px; top: 2px; width: 80px; height: 23px;">
</div>
<!-- Бор -->
<div style="position: relative; height: 28px;">
<label id="micro_B_label" style="position: absolute; left: 8px; top: 5px; font-size: 14px;">Бор B=17.5%</label>
<input type="number" id="micro_B_percent" value="17.5000" step="0.0001" oninput="onMicroCompositionChange()"
style="position: absolute; left: 120px; top: 2px; width: 80px; height: 23px;">
<input type="number" id="g_B" value="0.02857" step="0.00001" readonly
style="position: absolute; left: 205px; top: 2px; width: 80px; height: 23px;">
</div>
<!-- Цинк -->
<div style="position: relative; height: 28px;">
<label id="micro_Zn_label" style="position: absolute; left: 8px; top: 5px; font-size: 14px;">Цинк Zn=22.7%</label>
<input type="number" id="micro_Zn_percent" value="22.7000" step="0.0001" oninput="onMicroCompositionChange()"
style="position: absolute; left: 120px; top: 2px; width: 80px; height: 23px;">
<input type="number" id="g_Zn" value="0.01454" step="0.00001" readonly
style="position: absolute; left: 205px; top: 2px; width: 80px; height: 23px;">
</div>
<!-- Медь -->
<div style="position: relative; height: 28px;">
<label id="micro_Cu_label" style="position: absolute; left: 8px; top: 5px; font-size: 14px;">Медь Cu=25.5%</label>
<input type="number" id="micro_Cu_percent" value="25.5000" step="0.0001" oninput="onMicroCompositionChange()"
style="position: absolute; left: 120px; top: 2px; width: 80px; height: 23px;">
<input type="number" id="g_Cu" value="0.00247" step="0.00001" readonly
style="position: absolute; left: 205px; top: 2px; width: 80px; height: 23px;">
</div>
<!-- Молибден -->
<div style="position: relative; height: 28px;">
<label id="micro_Mo_label" style="position: absolute; left: 8px; top: 5px; font-size: 14px;">Молибден Mo=54.3%</label>
<input type="number" id="micro_Mo_percent" value="54.3000" step="0.0001" oninput="onMicroCompositionChange()"
style="position: absolute; left: 120px; top: 2px; width: 80px; height: 23px;">
<input type="number" id="g_Mo" value="0.00116" step="0.00001" readonly
style="position: absolute; left: 205px; top: 2px; width: 80px; height: 23px;">
</div>
<!-- Кобальт -->
<div style="position: relative; height: 28px;">
<label id="micro_Co_label" style="position: absolute; left: 8px; top: 5px; font-size: 14px;">Кобальт Co=13.0%</label>
<input type="number" id="micro_Co_percent" value="13.0000" step="0.0001" oninput="onMicroCompositionChange()"
style="position: absolute; left: 120px; top: 2px; width: 80px; height: 23px;">
<input type="number" id="g_Co" value="0.00000" step="0.00001" readonly
style="position: absolute; left: 205px; top: 2px; width: 80px; height: 23px;">
</div>
<!-- Кремний -->
<div style="position: relative; height: 28px;">
<label id="micro_Si_label" style="position: absolute; left: 8px; top: 5px; font-size: 14px;">Кремний Si=7.0%</label>
<input type="number" id="micro_Si_percent" value="7.0000" step="0.0001" oninput="onMicroCompositionChange()"
style="position: absolute; left: 120px; top: 2px; width: 80px; height: 23px;">
<input type="number" id="g_Si" value="0.00000" step="0.00001" readonly
style="position: absolute; left: 205px; top: 2px; width: 80px; height: 23px;">
</div>
</div>
<!-- Комплекс по бору -->
<div style="position: relative; height: 30px; margin-top: 5px;">
<input type="checkbox" id="use_complex" onchange="onComplexChange()"
style="position: absolute; left: 8px; top: 6px;">
<label for="use_complex" style="position: absolute; left: 28px; top: 5px; font-size: 14px;">Комплекс по бору</label>
<span id="total_micro_text" style="position: absolute; left: 205px; top: 5px; font-size: 14px; width: 80px; text-align: right;">0.16135</span>
<input type="number" id="total_micro_grams" value="0.16135" step="0.00001" onchange="onTotalMicroGramsChange()"
style="position: absolute; left: 205px; top: 2px; width: 80px; height: 23px; display: none;">
</div>
<!-- Информация о составе -->
<div style="position: relative; height: 25px; margin-top: 5px;">
<div id="micro-composition" style="position: absolute; left: 8px; top: 0; width: 670px;
background: white; border: 1px solid #999; padding: 2px 4px; font-size: 13px;">
Состав: Fe=12,395% Mn=3,409% B=3,099% Zn=2,045% Cu=0,39% Mo=0,39% Co=0% Si=0%
</div>
</div>
<!-- Заголовок разведения микроэлементов -->
<div style="position: relative; height: 25px; margin-top: 20px;">
<span style="position: absolute; left: 120px; top: 0; font-size: 15px; font-weight: bold;">
Разведение микроэлементов в воде (жидкий концентрат микроэлементов)
</span>
</div>
<!-- Объем -->
<div style="position: relative; height: 55px; margin-top: 5px;">
<label style="position: absolute; left: 8px; top: 3px; font-size: 15px;">Объем до (мл):</label>
<input type="number" id="micro_volume" value="500" step="1" oninput="onMicroVolumeChange()"
style="position: absolute; left: 8px; top: 22px; width: 85px; height: 23px;">
<div id="micro-dilution-info" style="position: absolute; left: 100px; top: 22px; width: 480px;
border: none; border-style: none; padding: 2px 0; font-size: 13px;">
Концентрация: 0,32 г/л, Кратность: 20:1, Расход: 50 мл/л раствора
</div>
</div>
</div>
<!-- КОНЦЕНТРАТЫ -->
<div id="concentrates" class="tab-content">
<div class="tabs-inner">
<div class="tab-inner active" onclick="switchInnerTab('calc', this)">Расчет</div>
<div class="tab-inner" onclick="switchInnerTab('preparation', this)">Изготовление</div>
<div class="tab-inner" onclick="switchInnerTab('price', this)">Цена</div>
</div>
<div id="calc" class="inner-tab-content active">
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 6px;">
<h3 style="margin: 0;">Расчет монорастворов и концентратов</h3>
<button class="help-button" onclick="showCalcHelp()">help</button>
</div>
<div class="concentrate-header">
Раствор A
<span id="sumA" style="margin-left: 20px; font-weight: normal; font-size: 11px;">Объем: 42,63 мл, вес: 42,63 гр, плотность: 1 г/мл.</span>
</div>
<table class="concentrate-table">
<thead>
<tr>
<th></th>
<th>г/л</th>
<th>г/мл</th>
<th>мл</th>
<th>гр. жидк.</th>
</tr>
</thead>
<tbody>
<tr>
<td>Кальций азотнокислый Ca(NO3)2*4H2O</td>
<td><input type="number" id="conc_glCaNO3" value="600.0" step="0.1" oninput="calcConcentrates()"></td>
<td><input type="number" id="conc_gmlCaNO3" value="1.0000" step="0.0001" oninput="calcConcentrates()"></td>
<td><input type="number" id="conc_mlCaNO3" value="19.63" step="0.01" readonly></td>
<td><input type="number" id="conc_ggCaNO3" value="19.63" step="0.01" readonly></td>
</tr>
<tr>
<td>Калий азотнокислый KNO3</td>
<td><input type="number" id="conc_glKNO3" value="250.0" step="0.1" oninput="calcConcentrates()"></td>
<td><input type="number" id="conc_gmlKNO3" value="1.0000" step="0.0001" oninput="calcConcentrates()"></td>
<td><input type="number" id="conc_mlKNO3" value="11.60" step="0.01" readonly></td>
<td><input type="number" id="conc_ggKNO3" value="11.60" step="0.01" readonly></td>
</tr>
<tr>
<td>Аммоний азотнокислый NH4NO3</td>
<td><input type="number" id="conc_glNH4NO3" value="100.0" step="0.1" oninput="calcConcentrates()"></td>
<td><input type="number" id="conc_gmlNH4NO3" value="1.0000" step="0.0001" oninput="calcConcentrates()"></td>
<td><input type="number" id="conc_mlNH4NO3" value="11.40" step="0.01" readonly></td>
<td><input type="number" id="conc_ggNH4NO3" value="11.40" step="0.01" readonly></td>
</tr>
<tr>
<td>Магний азотнокислый Mg(NO3)2*6H2O</td>
<td><input type="number" id="conc_glMgNO3" value="500.0" step="0.1" oninput="calcConcentrates()"></td>
<td><input type="number" id="conc_gmlMgNO3" value="1.0000" step="0.0001" oninput="calcConcentrates()"></td>
<td><input type="number" id="conc_mlMgNO3" value="0.00" step="0.01" readonly></td>
<td><input type="number" id="conc_ggMgNO3" value="0.00" step="0.01" readonly></td>
</tr>
<tr>
<td>Хлорид кальция 6-водный CaCl2*6H2O</td>
<td><input type="number" id="conc_glCaCl2" value="100.0" step="0.1" oninput="calcConcentrates()"></td>
<td><input type="number" id="conc_gmlCaCl2" value="1.0000" step="0.0001" oninput="calcConcentrates()"></td>
<td><input type="number" id="conc_mlCaCl2" value="0.00" step="0.01" readonly></td>
<td><input type="number" id="conc_ggCaCl2" value="0.00" step="0.01" readonly></td>
</tr>
</tbody>
</table>
<div class="concentrate-header">
Раствор B
<span id="sumB" style="margin-left: 20px; font-weight: normal; font-size: 11px;">Объем: 40,1 мл, вес: 39,87 гр, плотность: 0,99 г/мл</span>
</div>
<table class="concentrate-table">
<thead>
<tr>
<th></th>
<th>г/л</th>
<th>г/мл</th>
<th>мл</th>
<th>гр. жидк.</th>
</tr>
</thead>
<tbody>
<tr>
<td>Магний сернокислый MgSO4*7H2O</td>
<td><input type="number" id="conc_glMgSO4" value="600.0" step="0.1" oninput="calcConcentrates()"></td>
<td><input type="number" id="conc_gmlMgSO4" value="1.0000" step="0.0001" oninput="calcConcentrates()"></td>
<td><input type="number" value="8.45" step="0.01" readonly></td>
<td><input type="number" value="8.45" step="0.01" readonly></td>
</tr>
<tr>
<td>Калий фосфорнокислый KH2PO4</td>
<td><input type="number" id="conc_glKH2PO4" value="150.0" step="0.1" oninput="calcConcentrates()"></td>
<td><input type="number" id="conc_gmlKH2PO4" value="1.0000" step="0.0001" oninput="calcConcentrates()"></td>
<td><input type="number" value="11.73" step="0.01" readonly></td>
<td><input type="number" value="11.73" step="0.01" readonly></td>
</tr>
<tr>
<td>Калий сернокислый K2SO4</td>
<td><input type="number" id="conc_glK2SO4" value="100.0" step="0.1" oninput="calcConcentrates()"></td>
<td><input type="number" id="conc_gmlK2SO4" value="1.0000" step="0.0001" oninput="calcConcentrates()"></td>
<td><input type="number" value="3.80" step="0.01" readonly></td>
<td><input type="number" value="3.80" step="0.01" readonly></td>
</tr>
<tr style="height: 10px;"><td colspan="5"></td></tr>
<tr>
<td>Железо Fe=20,1%</td>
<td><input type="number" id="conc_glFe" value="10.00" step="0.1" oninput="calcConcentrates()"></td>
<td><input type="number" id="conc_gmlFe" value="1.000" step="0.001" oninput="calcConcentrates()"></td>
<td><input type="number" value="9.95" step="0.01" readonly></td>
<td><input type="number" value="9.95" step="0.01" readonly></td>
</tr>
<tr>
<td>Марганец Mn=36,4%</td>
<td><input type="number" id="conc_glMn" value="10.00" step="0.1" oninput="calcConcentrates()"></td>
<td><input type="number" id="conc_gmlMn" value="1.000" step="0.001" oninput="calcConcentrates()"></td>
<td><input type="number" value="1.51" step="0.01" readonly></td>
<td><input type="number" value="1.51" step="0.01" readonly></td>
</tr>
<tr>
<td>Бор B=17,5%</td>
<td><input type="number" id="conc_glB" value="10.00" step="0.1" oninput="calcConcentrates()"></td>
<td><input type="number" id="conc_gmlB" value="1.000" step="0.001" oninput="calcConcentrates()"></td>
<td><input type="number" value="2.86" step="0.01" readonly></td>
<td><input type="number" value="2.86" step="0.01" readonly></td>
</tr>
<tr>
<td>Цинк Zn=22,7%</td>
<td><input type="number" id="conc_glZn" value="10.00" step="0.1" oninput="calcConcentrates()"></td>
<td><input type="number" id="conc_gmlZn" value="1.000" step="0.001" oninput="calcConcentrates()"></td>
<td><input type="number" value="1.45" step="0.01" readonly></td>
<td><input type="number" value="1.45" step="0.01" readonly></td>
</tr>
<tr>
<td>Медь Cu=25,5%</td>
<td><input type="number" id="conc_glCu" value="10.00" step="0.1" oninput="calcConcentrates()"></td>
<td><input type="number" id="conc_gmlCu" value="1.000" step="0.001" oninput="calcConcentrates()"></td>
<td><input type="number" value="0.25" step="0.01" readonly></td>
<td><input type="number" value="0.25" step="0.01" readonly></td>
</tr>
<tr>
<td>Молибден Mo=54,3%</td>
<td><input type="number" id="conc_glMo" value="10.00" step="0.1" oninput="calcConcentrates()"></td>
<td><input type="number" id="conc_gmlMo" value="1.000" step="0.001" oninput="calcConcentrates()"></td>
<td><input type="number" value="0.12" step="0.01" readonly></td>
<td><input type="number" value="0.12" step="0.01" readonly></td>
</tr>
<tr>
<td>Кобальт Co=13%</td>
<td><input type="number" id="conc_glCo" value="10.00" step="0.1" oninput="calcConcentrates()"></td>
<td><input type="number" id="conc_gmlCo" value="1.000" step="0.001" oninput="calcConcentrates()"></td>
<td><input type="number" value="0.00" step="0.01" readonly></td>
<td><input type="number" value="0.00" step="0.01" readonly></td>
</tr>
<tr>
<td>Кремний Si=7%</td>
<td><input type="number" id="conc_glSi" value="10.00" step="0.1" oninput="calcConcentrates()"></td>
<td><input type="number" id="conc_gmlSi" value="1.000" step="0.001" oninput="calcConcentrates()"></td>
<td><input type="number" value="0.00" step="0.01" readonly></td>
<td><input type="number" value="0.00" step="0.01" readonly></td>
</tr>
</tbody>
</table>
</div>
<div id="preparation" class="inner-tab-content">
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 6px;">
<h3 style="margin: 0;">Изготовление</h3>
<button class="help-button" onclick="showPreparationHelp()">help</button>
</div>
<!-- Макроэлементы -->
<div class="preparation-section">
<h4>Макроэлементы</h4>
<!-- Заголовки колонок -->
<div class="preparation-header">
<div class="prep-input-header">Помпа</div>
<div class="prep-checkbox-header">Ручн.</div>
<div class="prep-value-header">жидк (гр.)</div>
<div class="prep-label-header">Описание</div>
</div>
<!-- Раствор A -->
<div class="preparation-subgroup">
<h5>Раствор A</h5>
<div class="preparation-item">
<input type="text" id="mCaNO3" value="p1" class="prep-input">
<input type="checkbox" id="cbCaNO3" class="prep-checkbox">
<input type="text" id="g2gCaNO3" value="0.00" class="prep-input">
<label for="k2nCaNO3" class="prep-label">Кальций азотнокислый 4-водный (ч.)</label>
</div>
<div class="preparation-item">
<input type="text" id="mKNO3" value="p2" class="prep-input">
<input type="checkbox" id="cbKNO3" class="prep-checkbox">
<input type="text" id="g2gKNO3" value="0.00" class="prep-input">
<label for="k2nKNO3" class="prep-label">Калий азотнокислый (ч.)</label>
</div>
<div class="preparation-item">
<input type="text" id="mNH4NO3" value="p3" class="prep-input">
<input type="checkbox" id="cbNH4NO3" class="prep-checkbox">
<input type="text" id="g2gNH4NO3" value="0.00" class="prep-input">
<label for="k2nNH4NO3" class="prep-label">Аммоний азотнокислый (ч.)</label>
</div>
<div class="preparation-item">
<input type="text" id="mMgNO3" value="" class="prep-input">
<input type="checkbox" id="cbMgNO3" class="prep-checkbox">
<input type="text" id="g2gMgNO3" value="0.00" class="prep-input">
<label for="k2nMgNO3" class="prep-label">Магний азотнокислый 6-водный (ч.)</label>
</div>
<div class="preparation-item">
<input type="text" id="mCaCl2" value="" class="prep-input">
<input type="checkbox" id="cbCaCl2" class="prep-checkbox">
<input type="text" id="g2gCaCl2" value="0.00" class="prep-input">
<label for="k2nCaCl2" class="prep-label">Хлорид кальция 6-водный (ч.)</label>
</div>
</div>
<!-- Раствор B -->
<div class="preparation-subgroup">
<h5>Раствор B</h5>
<div class="preparation-item">
<input type="text" id="mMgSO4" value="p4" class="prep-input">
<input type="checkbox" id="cbMgSO4" class="prep-checkbox">
<input type="text" id="g2gMgSO4" value="0.00" class="prep-input">
<label for="k2nMgSO4" class="prep-label">Магний сернокислый 7-водный (ч.)</label>
</div>
<div class="preparation-item">
<input type="text" id="mKH2PO4" value="p5" class="prep-input">
<input type="checkbox" id="cbKH2PO4" class="prep-checkbox">
<input type="text" id="g2gKH2PO4" value="0.00" class="prep-input">
<label for="k2nKH2PO4" class="prep-label">Калий фосфорнокислый 1-замещенный (ч.)</label>
</div>
<div class="preparation-item">
<input type="text" id="mK2SO4" value="p6" class="prep-input">
<input type="checkbox" id="cbK2SO4" class="prep-checkbox">
<input type="text" id="g2gK2SO4" value="0.00" class="prep-input">
<label for="k2nK2SO4" class="prep-label">Калий сернокислый безводный (ч.)</label>
</div>
</div>
</div>
<!-- Микроэлементы -->
<div class="preparation-section">
<h4>Микроэлементы</h4>
<!-- Заголовки колонок -->
<div class="preparation-header">
<div class="prep-input-header">Помпа</div>
<div class="prep-checkbox-header">Ручн.</div>
<div class="prep-value-header">жидк (гр.)</div>
<div class="prep-label-header">Описание</div>
</div>
<!-- Комплекс микроэлементов -->
<div class="preparation-complex">
<span class="prep-complex-text">Комплекс микроэлементов 2285.520 г. (10 л)</span>
</div>
<!-- Отдельные микроэлементы -->
<div class="preparation-item">
<input type="text" id="mFe" value="" class="prep-input">
<input type="checkbox" id="cbFe" class="prep-checkbox">
<input type="text" id="g2gFe" value="0.00" class="prep-input">
<label for="l2Fe" class="prep-label">Железо (%)</label>
</div>
<div class="preparation-item">
<input type="text" id="mMn" value="" class="prep-input">
<input type="checkbox" id="cbMn" class="prep-checkbox">
<input type="text" id="g2gMn" value="0.00" class="prep-input">
<label for="l2Mn" class="prep-label">Марганец (%)</label>
</div>
<div class="preparation-item">
<input type="text" id="mB" value="" class="prep-input">
<input type="checkbox" id="cbB" class="prep-checkbox">
<input type="text" id="g2gB" value="0.00" class="prep-input">
<label for="l2B" class="prep-label">Бор (%)</label>
</div>
<div class="preparation-item">
<input type="text" id="mZn" value="" class="prep-input">
<input type="checkbox" id="cbZn" class="prep-checkbox">
<input type="text" id="g2gZn" value="0.00" class="prep-input">
<label for="l2Zn" class="prep-label">Цинк (%)</label>
</div>
<div class="preparation-item">
<input type="text" id="mCu" value="" class="prep-input">
<input type="checkbox" id="cbCu" class="prep-checkbox">
<input type="text" id="g2gCu" value="0.00" class="prep-input">
<label for="l2Cu" class="prep-label">Медь (%)</label>
</div>
<div class="preparation-item">
<input type="text" id="mMo" value="" class="prep-input">
<input type="checkbox" id="cbMo" class="prep-checkbox">
<input type="text" id="g2gMo" value="0.00" class="prep-input">
<label for="l2Mo" class="prep-label">Молибден (%)</label>
</div>
<div class="preparation-item">
<input type="text" id="mCo" value="" class="prep-input">
<input type="checkbox" id="cbCo" class="prep-checkbox">
<input type="text" id="g2gCo" value="0.00" class="prep-input">
<label for="l2Co" class="prep-label">Кобальт (%)</label>
</div>
<div class="preparation-item">
<input type="text" id="mSi" value="" class="prep-input">
<input type="checkbox" id="cbSi" class="prep-checkbox">
<input type="text" id="g2gSi" value="0.00" class="prep-input">
<label for="l2Si" class="prep-label">Кремний (%)</label>
</div>
</div>
<!-- Настройки миксера и кнопки управления -->
<div class="preparation-section">
<h4>Настройки миксера</h4>
<div style="display: flex; align-items: center; gap: 8px;">
<label for="addrMixer" style="font-size: 11px;">Адрес:</label>
<input type="text" id="addrMixer" value="mixer.local" class="prep-input mixer-input">
<label for="nmix" style="font-size: 11px;">Номер:</label>
<input type="number" id="nmix" value="1" class="prep-input mixer-number">
<button id="btnManufacture" class="prep-button">Изготовить</button>
<button id="btnToProfile" class="prep-button">Перевести в профиль</button>
<button id="btnToJournal" class="prep-button">Внести в журнал</button>
</div>
</div>
</div>
<div id="price" class="inner-tab-content">
<h3>Цена</h3>
<div class="price-section">
<h4>Цена за 1 грамм</h4>
<div class="price-inputs">
<div class="price-item">
<div class="price-input-wrapper">
<input type="text" id="price_CaNO3" class="price-input-custom" value="0.4000" oninput="updatePriceResults()">
<div class="price-spinner">
<button data-input="price_CaNO3" data-delta="0.0001"></button>
<button data-input="price_CaNO3" data-delta="-0.0001"></button>
</div>
</div>
<span class="price-name">Кальций азотнокислый 4-водный (ч.)</span>
<span class="price-result" id="price_result_CaNO3">0.00 г. цена: 0.00</span>
</div>
<div class="price-item">
<div class="price-input-wrapper">
<input type="text" id="price_KNO3" class="price-input-custom" value="0.3500" oninput="updatePriceResults()">
<div class="price-spinner">
<button data-input="price_KNO3" data-delta="0.0001"></button>
<button data-input="price_KNO3" data-delta="-0.0001"></button>
</div>
</div>
<span class="price-name">Калий азотнокислый (ч.)</span>
<span class="price-result" id="price_result_KNO3">0.00 г. цена: 0.00</span>
</div>
<div class="price-item">
<div class="price-input-wrapper">
<input type="text" id="price_NH4NO3" class="price-input-custom" value="0.2500" oninput="updatePriceResults()">
<div class="price-spinner">
<button data-input="price_NH4NO3" data-delta="0.0001"></button>
<button data-input="price_NH4NO3" data-delta="-0.0001"></button>
</div>
</div>
<span class="price-name">Аммоний азотнокислый (ч.)</span>
<span class="price-result" id="price_result_NH4NO3">0.00 г. цена: 0.00</span>
</div>
<div class="price-item">
<div class="price-input-wrapper">
<input type="text" id="price_MgNO3" class="price-input-custom" value="0.3500" oninput="updatePriceResults()">
<div class="price-spinner">
<button data-input="price_MgNO3" data-delta="0.0001"></button>
<button data-input="price_MgNO3" data-delta="-0.0001"></button>
</div>
</div>
<span class="price-name">Магний азотнокислый 6-водный (ч.)</span>
<span class="price-result" id="price_result_MgNO3">0.00 г. цена: 0.00</span>
</div>
<div class="price-item">
<div class="price-input-wrapper">
<input type="text" id="price_MgSO4" class="price-input-custom" value="0.1200" oninput="updatePriceResults()">
<div class="price-spinner">
<button data-input="price_MgSO4" data-delta="0.0001"></button>
<button data-input="price_MgSO4" data-delta="-0.0001"></button>
</div>
</div>
<span class="price-name">Магний сернокислый 7-водный (ч.)</span>
<span class="price-result" id="price_result_MgSO4">0.00 г. цена: 0.00</span>
</div>
<div class="price-item">
<div class="price-input-wrapper">
<input type="text" id="price_KH2PO4" class="price-input-custom" value="0.4000" oninput="updatePriceResults()">
<div class="price-spinner">
<button data-input="price_KH2PO4" data-delta="0.0001"></button>
<button data-input="price_KH2PO4" data-delta="-0.0001"></button>
</div>
</div>
<span class="price-name">Калий фосфорнокислый 1-замещенный (ч.)</span>
<span class="price-result" id="price_result_KH2PO4">0.00 г. цена: 0.00</span>
</div>
<div class="price-item">
<div class="price-input-wrapper">
<input type="text" id="price_K2SO4" class="price-input-custom" value="0.3200" oninput="updatePriceResults()">
<div class="price-spinner">
<button data-input="price_K2SO4" data-delta="0.0001"></button>
<button data-input="price_K2SO4" data-delta="-0.0001"></button>
</div>
</div>
<span class="price-name">Калий сернокислый безводный (ч.)</span>
<span class="price-result" id="price_result_K2SO4">0.00 г. цена: 0.00</span>
</div>
<div class="price-item">
<div class="price-input-wrapper">
<input type="text" id="price_CaCl2" class="price-input-custom" value="7.7000" oninput="updatePriceResults()">
<div class="price-spinner">
<button data-input="price_CaCl2" data-delta="0.0001"></button>
<button data-input="price_CaCl2" data-delta="-0.0001"></button>
</div>
</div>
<span class="price-name">Хлорид кальция 6-водный (ч.)</span>
<span class="price-result" id="price_result_CaCl2">0.00 г. цена: 0.00</span>
</div>
</div>
</div>
<div class="price-section">
<h4>Микроэлементы</h4>
<div class="price-inputs">
<div class="price-item">
<div class="price-input-wrapper">
<input type="text" id="price_Fe" class="price-input-custom" value="3.0000" oninput="updatePriceResults()">
<div class="price-spinner">
<button data-input="price_Fe" data-delta="0.0001"></button>
<button data-input="price_Fe" data-delta="-0.0001"></button>
</div>
</div>
<span class="price-name">Железо (%)</span>
<span class="price-result" id="price_result_Fe">0.00 г. цена: 0.00</span>
</div>
<div class="price-item">
<div class="price-input-wrapper">
<input type="text" id="price_Mn" class="price-input-custom" value="0.3000" oninput="updatePriceResults()">
<div class="price-spinner">
<button data-input="price_Mn" data-delta="0.0001"></button>
<button data-input="price_Mn" data-delta="-0.0001"></button>
</div>
</div>
<span class="price-name">Марганец (%)</span>
<span class="price-result" id="price_result_Mn">0.00 г. цена: 0.00</span>
</div>
<div class="price-item">
<div class="price-input-wrapper">
<input type="text" id="price_B" class="price-input-custom" value="0.4000" oninput="updatePriceResults()">
<div class="price-spinner">
<button data-input="price_B" data-delta="0.0001"></button>
<button data-input="price_B" data-delta="-0.0001"></button>
</div>
</div>
<span class="price-name">Бор (%)</span>
<span class="price-result" id="price_result_B">0.00 г. цена: 0.00</span>
</div>
<div class="price-item">
<div class="price-input-wrapper">
<input type="text" id="price_Zn" class="price-input-custom" value="0.1500" oninput="updatePriceResults()">
<div class="price-spinner">
<button data-input="price_Zn" data-delta="0.0001"></button>
<button data-input="price_Zn" data-delta="-0.0001"></button>
</div>
</div>
<span class="price-name">Цинк (%)</span>
<span class="price-result" id="price_result_Zn">0.00 г. цена: 0.00</span>
</div>
<div class="price-item">
<div class="price-input-wrapper">
<input type="text" id="price_Cu" class="price-input-custom" value="0.4000" oninput="updatePriceResults()">
<div class="price-spinner">
<button data-input="price_Cu" data-delta="0.0001"></button>
<button data-input="price_Cu" data-delta="-0.0001"></button>
</div>
</div>
<span class="price-name">Медь (%)</span>
<span class="price-result" id="price_result_Cu">0.00 г. цена: 0.00</span>
</div>
<div class="price-item">
<div class="price-input-wrapper">
<input type="text" id="price_Mo" class="price-input-custom" value="3.0000" oninput="updatePriceResults()">
<div class="price-spinner">
<button data-input="price_Mo" data-delta="0.0001"></button>
<button data-input="price_Mo" data-delta="-0.0001"></button>
</div>
</div>
<span class="price-name">Молибден (%)</span>
<span class="price-result" id="price_result_Mo">0.00 г. цена: 0.00</span>
</div>
<div class="price-item">
<div class="price-input-wrapper">
<input type="text" id="price_Co" class="price-input-custom" value="2.3000" oninput="updatePriceResults()">
<div class="price-spinner">
<button data-input="price_Co" data-delta="0.0001"></button>
<button data-input="price_Co" data-delta="-0.0001"></button>
</div>
</div>
<span class="price-name">Кобальт (%)</span>
<span class="price-result" id="price_result_Co">0.00 г. цена: 0.00</span>
</div>
<div class="price-item">
<div class="price-input-wrapper">
<input type="text" id="price_Si" class="price-input-custom" value="0.2700" oninput="updatePriceResults()">
<div class="price-spinner">
<button data-input="price_Si" data-delta="0.0001"></button>
<button data-input="price_Si" data-delta="-0.0001"></button>
</div>
</div>
<span class="price-name">Кремний (%)</span>
<span class="price-result" id="price_result_Si">0.00 г. цена: 0.00</span>
</div>
<div class="price-item" style="display: none;">
<div class="price-input-wrapper">
<input type="text" id="price_Cmplx" class="price-input-custom" value="0.0500" oninput="updatePriceResults()">
<div class="price-spinner">
<button data-input="price_Cmplx" data-delta="0.0001"></button>
<button data-input="price_Cmplx" data-delta="-0.0001"></button>
</div>
</div>
<span class="price-name">Комплекс микроэлементов</span>
<span class="price-result" id="price_result_Cmplx">0.00 г. цена: 0.00</span>
</div>
</div>
</div>
<div class="price-total">
Стоимость: <span id="total_price">0.00</span> за 1 литр: <span id="price_per_liter">0.00</span>
</div>
</div>
<!-- Общий блок для всех подвкладок Концентратов -->
<div class="volume-inputs">
<label>Тара A (мл)</label>
<input type="number" id="tank_A" value="500" step="1" oninput="calcConcentrates()">
<span id="lVolA">Концентрат A (20:1) . Долить воды: 457мл. По 50 мл на 1л.</span>
</div>
<div class="volume-inputs">
<label>Тара B (мл)</label>
<input type="number" id="tank_B" value="500" step="1" oninput="calcConcentrates()">
<span id="lVolB">Концентрат B (20:1) . Долить воды: 460мл. По 50 мл на 1л.</span>
</div>
</div>
<!-- КОРРЕКТОР -->
<div id="corrector" class="tab-content">
<div class="corrector-container">
<!-- Заголовки колонок -->
<div class="corrector-header" style="left: 48px;">Исходный</div>
<div class="corrector-header" style="left: 136px;">Текущий</div>
<div class="corrector-header" style="left: 223px;">Корректирующий</div>
<div class="corrector-header" style="left: 321px;">Итоговый</div>
<div class="corrector-protocol-header">Протокол коррекции</div>
<!-- Кнопки "Заполнить" -->
<button class="corrector-btn-fill" style="left: 48px;" onclick="fillColumn('initial')">Заполнить</button>
<button class="corrector-btn-fill" style="left: 223px;" onclick="fillColumn('correcting')">Заполнить</button>
<button class="corrector-btn-fill" style="left: 321px;" onclick="fillColumn('final')">Заполнить</button>
<!-- Метки элементов слева -->
<label class="corrector-label" style="top: 68px; left: 20px;">N</label>
<label class="corrector-label" style="top: 95px; left: 3px;">NO3</label>
<label class="corrector-label" style="top: 121px; left: 3px;">NH4</label>
<label class="corrector-label" style="top: 146px; left: 20px;">P</label>
<label class="corrector-label" style="top: 173px; left: 20px;">K</label>
<label class="corrector-label" style="top: 199px; left: 14px;">Ca</label>
<label class="corrector-label" style="top: 226px; left: 14px;">Mg</label>
<label class="corrector-label" style="top: 253px; left: 22px;">S</label>
<label class="corrector-label" style="top: 279px; left: 19px;">Cl</label>
<label class="corrector-label" style="top: 306px; left: 15px;">EC</label>
<label class="corrector-label" style="top: 343px; left: 68px;">Объем</label>
<!-- Колонка 1: Исходный (Left=48) -->
<input type="number" class="corrector-input" style="top: 63px; left: 48px;" id="corr_init_N" value="220.00" step="0.01" readonly>
<input type="number" class="corrector-input" style="top: 90px; left: 48px;" id="corr_init_NO3" value="200.00" step="0.01" oninput="onCorrectorChange()">
<input type="number" class="corrector-input" style="top: 116px; left: 48px;" id="corr_init_NH4" value="20.00" step="0.01" oninput="onCorrectorChange()">
<input type="number" class="corrector-input" style="top: 143px; left: 48px;" id="corr_init_P" value="40.000" step="0.001" oninput="onCorrectorChange()">
<input type="number" class="corrector-input" style="top: 168px; left: 48px;" id="corr_init_K" value="180.000" step="0.001" oninput="onCorrectorChange()">
<input type="number" class="corrector-input" style="top: 196px; left: 48px;" id="corr_init_Ca" value="200.000" step="0.001" oninput="onCorrectorChange()">
<input type="number" class="corrector-input" style="top: 221px; left: 48px;" id="corr_init_Mg" value="50.000" step="0.001" oninput="onCorrectorChange()">
<input type="number" class="corrector-input" style="top: 248px; left: 48px;" id="corr_init_S" value="73.049" step="0.001" readonly>
<input type="number" class="corrector-input" style="top: 274px; left: 48px;" id="corr_init_Cl" value="0.00" step="0.01" oninput="onCorrectorChange()">
<input type="number" class="corrector-input" style="top: 301px; left: 48px;" id="corr_init_EC" value="2.102" step="0.001" readonly>
<input type="number" class="corrector-volume" style="left: 136px;" id="corr_init_vol" value="10.0" step="0.1" oninput="onCorrectorChange()">
<button class="corrector-btn-calc" style="left: 48px;" onclick="calcCorrection('initial')">В расчет</button>
<!-- Колонка 2: Текущий (Left=136) -->
<input type="number" class="corrector-input" style="top: 63px; left: 136px;" id="corr_curr_N" value="220.000" step="0.001" readonly>
<input type="number" class="corrector-input" style="top: 90px; left: 136px;" id="corr_curr_NO3" value="200.00" step="0.01" readonly>
<input type="number" class="corrector-input" style="top: 116px; left: 136px;" id="corr_curr_NH4" value="20.00" step="0.01" readonly>
<input type="number" class="corrector-input" style="top: 143px; left: 136px;" id="corr_curr_P" value="40.000" step="0.001" readonly>
<input type="number" class="corrector-input" style="top: 168px; left: 136px;" id="corr_curr_K" value="180.000" step="0.001" readonly>
<input type="number" class="corrector-input" style="top: 196px; left: 136px;" id="corr_curr_Ca" value="200.000" step="0.001" readonly>
<input type="number" class="corrector-input" style="top: 221px; left: 136px;" id="corr_curr_Mg" value="50.000" step="0.001" readonly>
<input type="number" class="corrector-input" style="top: 248px; left: 136px;" id="corr_curr_S" value="73.049" step="0.001" readonly>
<input type="number" class="corrector-input" style="top: 274px; left: 136px;" id="corr_curr_Cl" value="0.00" step="0.01" readonly>
<input type="number" class="corrector-input" style="top: 301px; left: 136px;" id="corr_curr_EC" value="2.102" step="0.001" oninput="onCorrectorChange()">
<button class="corrector-btn-calc" style="left: 136px;" onclick="calcCorrection('current')">В расчет</button>
<!-- Колонка 3: Корректирующий (Left=223) -->
<input type="number" class="corrector-input" style="top: 63px; left: 223px;" id="corr_corr_N" value="220.000" step="0.001" readonly>
<input type="number" class="corrector-input" style="top: 90px; left: 223px;" id="corr_corr_NO3" value="200.00" step="0.01" oninput="onCorrectorChange()">
<input type="number" class="corrector-input" style="top: 116px; left: 223px;" id="corr_corr_NH4" value="20.00" step="0.01" oninput="onCorrectorChange()">
<input type="number" class="corrector-input" style="top: 143px; left: 223px;" id="corr_corr_P" value="40.000" step="0.001" oninput="onCorrectorChange()">
<input type="number" class="corrector-input" style="top: 168px; left: 223px;" id="corr_corr_K" value="180.000" step="0.001" oninput="onCorrectorChange()">
<input type="number" class="corrector-input" style="top: 196px; left: 223px;" id="corr_corr_Ca" value="200.000" step="0.001" oninput="onCorrectorChange()">
<input type="number" class="corrector-input" style="top: 221px; left: 223px;" id="corr_corr_Mg" value="50.000" step="0.001" oninput="onCorrectorChange()">
<input type="number" class="corrector-input" style="top: 248px; left: 223px;" id="corr_corr_S" value="73.049" step="0.001" readonly>
<input type="number" class="corrector-input" style="top: 274px; left: 223px;" id="corr_corr_Cl" value="0.00" step="0.01" oninput="onCorrectorChange()">
<input type="number" class="corrector-input" style="top: 301px; left: 223px;" id="corr_corr_EC" value="2.102" step="0.001" readonly>
<input type="number" class="corrector-volume" style="left: 223px;" id="corr_corr_vol" value="20.0" step="0.1" readonly>
<button class="corrector-btn-calc" style="left: 223px;" onclick="calcCorrection('correcting')">В расчет</button>
<!-- Колонка 4: Итоговый (Left=321) -->
<input type="number" class="corrector-input" style="top: 63px; left: 321px;" id="corr_final_N" value="220.000" step="0.001" readonly>
<input type="number" class="corrector-input" style="top: 90px; left: 321px;" id="corr_final_NO3" value="200.00" step="0.01" oninput="onCorrectorChange()">
<input type="number" class="corrector-input" style="top: 116px; left: 321px;" id="corr_final_NH4" value="20.00" step="0.01" oninput="onCorrectorChange()">
<input type="number" class="corrector-input" style="top: 143px; left: 321px;" id="corr_final_P" value="40.000" step="0.001" oninput="onCorrectorChange()">
<input type="number" class="corrector-input" style="top: 168px; left: 321px;" id="corr_final_K" value="180.000" step="0.001" oninput="onCorrectorChange()">
<input type="number" class="corrector-input" style="top: 196px; left: 321px;" id="corr_final_Ca" value="200.000" step="0.001" oninput="onCorrectorChange()">
<input type="number" class="corrector-input" style="top: 221px; left: 321px;" id="corr_final_Mg" value="50.000" step="0.001" oninput="onCorrectorChange()">
<input type="number" class="corrector-input" style="top: 248px; left: 321px;" id="corr_final_S" value="73.049" step="0.001" readonly>
<input type="number" class="corrector-input" style="top: 274px; left: 321px;" id="corr_final_Cl" value="0.00" step="0.01" oninput="onCorrectorChange()">
<input type="number" class="corrector-input" style="top: 301px; left: 321px;" id="corr_final_EC" value="2.102" step="0.001" readonly>
<input type="number" class="corrector-volume" style="left: 321px;" id="corr_final_vol" value="30.0" step="0.1" oninput="onCorrectorChange()">
<button class="corrector-btn-calc" style="left: 321px;" onclick="calcCorrection('final')">В расчет</button>
<!-- Кнопка "В журнал" -->
<button class="corrector-btn-journal" onclick="saveCorrectorToJournal()">В журнал</button>
<!-- Протокол коррекции -->
<textarea class="corrector-protocol" id="corrector_protocol" readonly>ОСНОВНОЕ:
Изменение объема на: 200%
Доля старого раствора: 33%
Изменение EC на: 0%
Изменение N общий на: 0%
ПРОФИЛЬ:
Коррекция NO3 на: 0%
Коррекция NH4 на: 0%
Коррекция P на: 0%
Коррекция K на: 0%
Коррекция Ca на: 0%
Коррекция Mg на: 0%
Коррекция S на: 0 ppm
Коррекция Cl на: 0 ppm
СООТНОШЕНИЯ:
NH4:NO3 до 0,1 после 0,1
K:N до 0,818 после 0,818
K:Ca до 0,9 после 0,9
K:Mg до 3,6 после 3,6</textarea>
<!-- Кнопка help -->
<button class="corrector-btn-help" onclick="showCorrectorHelp()">help</button>
</div>
</div>
<!-- ФАЙЛ -->
<div id="file" class="tab-content" style="position: relative; max-height: 470px; overflow: hidden; font-family: Arial; font-size: 15px;">
<!-- Кнопка help справа вверху -->
<button onclick="showFileHelp()" style="position: absolute; left: 673px; top: 3px; width: 49px; height: 21px; font-size: 14px; padding: 0;">help</button>
<!-- Параметры сохранения -->
<div style="position: absolute; left: 8px; top: 3px; font-weight: bold; font-size: 15px;">Параметры сохранения</div>
<label style="position: absolute; left: 19px; top: 36px; font-size: 15px;">Имя файла:</label>
<input type="text" id="file_filename" value="default.hpg" style="position: absolute; left: 120px; top: 30px; width: 582px; height: 24px; font-family: Arial; font-size: 15px;">
<label style="position: absolute; left: 19px; top: 65px; font-size: 15px;">Описание:</label>
<input type="text" id="file_comment" value="По умолчанию" style="position: absolute; left: 120px; top: 62px; width: 582px; height: 24px; font-family: Arial; font-size: 15px;">
<!-- Кнопки управления файлами -->
<button onclick="fileOpen()" style="position: absolute; left: 11px; top: 95px; width: 106px; height: 30px; font-size: 15px;">Открыть</button>
<button onclick="fileSaveAs()" style="position: absolute; left: 123px; top: 95px; width: 138px; height: 30px; font-size: 15px;">Сохранить как</button>
<button onclick="fileLoadFertilizers()" style="position: absolute; left: 267px; top: 95px; width: 208px; height: 30px; font-size: 15px;">Подгрузить удобрения</button>
<button onclick="fileLoadProfile()" style="position: absolute; left: 481px; top: 95px; width: 208px; height: 30px; font-size: 15px;">Подгрузить профиль</button>
<!-- Журнал действий -->
<div style="position: absolute; left: 8px; top: 145px; font-weight: bold; font-size: 15px;">Журнал действий</div>
<select id="file_journal_list" size="6" style="position: absolute; left: 8px; top: 171px; width: 691px; height: 127px; font-family: Arial; font-size: 15px;">
</select>
<label style="position: absolute; left: 8px; top: 306px; font-size: 15px;">Описание:</label>
<label style="position: absolute; left: 134px; top: 306px; font-size: 15px;">Дата:</label>
<input type="date" id="file_journal_date" value="" style="position: absolute; left: 169px; top: 301px; width: 138px; height: 24px; font-family: Arial; font-size: 15px;">
<button onclick="journalAdd()" style="position: absolute; left: 315px; top: 301px; width: 101px; height: 24px; font-size: 15px;">Добавить</button>
<button onclick="journalUpdate()" style="position: absolute; left: 423px; top: 301px; width: 66px; height: 24px; font-size: 15px;">Обнов.</button>
<button onclick="journalDelete()" style="position: absolute; left: 494px; top: 301px; width: 80px; height: 24px; font-size: 15px;">Удалить</button>
<textarea id="file_journal_memo" style="position: absolute; left: 8px; top: 329px; width: 691px; height: 105px; font-family: Arial; font-size: 15px; resize: none;"></textarea>
<!-- Профиль в журнале -->
<div style="position: absolute; left: 8px; top: 446px; font-size: 15px;">Профиль в журнале:</div>
<!-- Полная строка профиля (как pr2 в легаси) -->
<input type="text" id="file_profile_info" readonly style="position: absolute; left: 8px; top: 464px; width: 691px; height: 13px; font-family: Arial; font-size: 12px; border: none; background: transparent; color: #000000;">
<button onclick="acceptProfileFromJournal()" style="position: absolute; left: 253px; top: 482px; width: 237px; height: 23px; font-size: 15px;">Принять профиль из журнала</button>
<!-- Детали профиля с процентами (позиции увеличены на 40%) -->
<!-- Строка 1 -->
<span id="rN" style="position: absolute; left: 6px; top: 519px; font-family: Arial; font-size: 10px; color: #000000;"></span>
<span id="rNO3" style="position: absolute; left: 62px; top: 519px; font-family: Arial; font-size: 10px; color: #000000;"></span>
<span id="rNH4" style="position: absolute; left: 123px; top: 519px; font-family: Arial; font-size: 10px; color: #000000;"></span>
<span id="rFe" style="position: absolute; left: 197px; top: 519px; font-family: Arial; font-size: 10px; color: #000000;"></span>
<span id="rCo" style="position: absolute; left: 274px; top: 519px; font-family: Arial; font-size: 10px; color: #000000;"></span>
<!-- Строка 2 -->
<span id="rP" style="position: absolute; left: 6px; top: 533px; font-family: Arial; font-size: 10px; color: #000000;"></span>
<span id="rMn" style="position: absolute; left: 197px; top: 533px; font-family: Arial; font-size: 10px; color: #000000;"></span>
<span id="rSi" style="position: absolute; left: 274px; top: 533px; font-family: Arial; font-size: 10px; color: #000000;"></span>
<!-- Строка 2.5 (Cl) -->
<span id="rCl" style="position: absolute; left: 62px; top: 547px; font-family: Arial; font-size: 10px; color: #000000;"></span>
<!-- Строка 3 -->
<span id="rK" style="position: absolute; left: 6px; top: 547px; font-family: Arial; font-size: 10px; color: #000000;"></span>
<span id="rB" style="position: absolute; left: 197px; top: 547px; font-family: Arial; font-size: 10px; color: #000000;"></span>
<!-- Строка 4 -->
<span id="rCa" style="position: absolute; left: 6px; top: 561px; font-family: Arial; font-size: 10px; color: #000000;"></span>
<span id="rZn" style="position: absolute; left: 197px; top: 561px; font-family: Arial; font-size: 10px; color: #000000;"></span>
<!-- Строка 5 -->
<span id="rMg" style="position: absolute; left: 6px; top: 575px; font-family: Arial; font-size: 10px; color: #000000;"></span>
<span id="rCu" style="position: absolute; left: 197px; top: 575px; font-family: Arial; font-size: 10px; color: #000000;"></span>
<!-- Строка 6 -->
<span id="rS" style="position: absolute; left: 6px; top: 589px; font-family: Arial; font-size: 10px; color: #000000;"></span>
<span id="rMo" style="position: absolute; left: 197px; top: 589px; font-family: Arial; font-size: 10px; color: #000000;"></span>
</div>
<!-- СПРАВКА -->
<div id="help" class="tab-content">
<h3>Справка - Hydroponic Profile Generator</h3>
<div style="line-height: 1.6; padding: 10px;">
<p><strong>Версия:</strong> 0.221 (HTML5 порт - 10.10.2025)</p>
<p><strong>Описание:</strong> Калькулятор для расчета профилей минерального питания растений при гидропонном методе выращивания.</p>
<div style="background: #fff3cd; border: 1px solid #ffc107; border-radius: 4px; padding: 12px; margin: 15px 0;">
<h4 style="margin-top: 0; color: #856404;">⚠️ О портировании</h4>
<p style="margin: 8px 0;">Это HTML5 версия оригинального приложения WEGA-HPG, портированная с помощью ИИ.</p>
<p style="margin: 8px 0;"><strong>Цель портирования:</strong> Максимально точно воспроизвести функционал оригинальной десктопной версии (Lazarus/Free Pascal) в веб-формате с сохранением всех расчетов, алгоритмов и пользовательского интерфейса.</p>
<p style="margin: 8px 0;"><strong>Важно:</strong></p>
<ul style="margin: 8px 0 8px 20px;">
<li>Функционал может частично отличаться от оригинала</li>
<li>Возможны ошибки и неточности в расчетах</li>
<li>Порт находится в активной разработке и постоянно обновляется</li>
<li>Формат файлов .hpg совместим с оригинальной версией</li>
<li>Для критически важных расчетов рекомендуется сверяться с оригинальной версией</li>
</ul>
<p style="margin: 8px 0; font-size: 13px; color: #856404;">
<strong>Если поведение редактора различается с оригиналом</strong>, убедитесь что используете последнюю версию порта, затем сообщите об этом в канале проекта
<a href="https://t.me/WEGA_SERVER/20744" target="_blank" style="color: #0066cc;">https://t.me/WEGA_SERVER/20744</a>
и приложите:
</p>
<ul style="margin: 4px 0 8px 20px; font-size: 12px; color: #856404;">
<li>Файл .hpg с которым возникла проблема</li>
<li>Скриншот из оригинального HPG</li>
<li>Скриншот того, что показывает HTML5 порт</li>
</ul>
</div>
<h4 style="margin-top: 20px;">Основные возможности:</h4>
<ul style="margin-left: 20px;">
<li>Расчет на основе ионного баланса</li>
<li>Создание профиля питания через NPKCaMgSCl</li>
<li>Получение расчетного EC</li>
<li>Матрица соотношений элементов</li>
<li>Составление микрокомплекса</li>
<li>Разделение концентратов на А и Б</li>
<li>Расчет стоимости удобрений</li>
<li>Корректировка раствора</li>
<li>Загрузка и обмен профилями</li>
<li>Журнал действий</li>
</ul>
<h4 style="margin-top: 20px;">Контакты:</h4>
<p>Оригинальный проект: <a href="https://github.com/WEGA-project/WEGA-HPG" target="_blank">WEGA-project/WEGA-HPG</a></p>
<p>HTML5 порт: <a href="https://github.com/WEGA-project/wega-hpg" target="_blank">WEGA-project/wega-hpg</a></p>
<p>Wiki: <a href="https://github.com/WEGA-project/WEGA-HPG/wiki" target="_blank">Документация</a></p>
</div>
</div>
<!-- Строка профиля - общая для всех вкладок -->
<div style="background: #d3d3d3; padding: 8px 12px;">
<div style="display: flex; gap: 5px; align-items: center;">
<input type="text" class="profile-string" id="profile-string" value="N=220 NO3=200 NH4=20 P=40 K=180 Ca=200 Mg=50 S=73 Cl=0 Fe=2 Mn=0.55 B=0.5 Zn=0.33 Cu=0.063 Mo=0.063 Co=0 Si=0" style="flex: 1;">
<button id="parse-button" onclick="parseProfile()" style="background: #c0ffc0; border: 1px solid #999; padding: 5px 10px; cursor: pointer; font-weight: bold;">OK</button>
</div>
</div>
<!-- НИЖНЯЯ ПАНЕЛЬ -->
<div class="bottom-panel">
<div class="bottom-left">
<div class="bottom-row">
<label>Объем в литрах</label>
<input type="number" id="volume" value="10.0" step="0.1" oninput="onVolumeChange()">
<button onclick="saveFullState()">Сохранить</button>
<button onclick="loadCompositions()">Вернуть составы</button>
</div>
<div class="bottom-row">
<label>Солей в граммах:</label>
<input type="number" id="total-salts" value="23.20" step="0.01" readonly>
<button onclick="recalculate()">по умолчанию</button>
<button onclick="loadProfileOnly()">Вернуть профиль</button>
</div>
<div class="cost-info">Стоимость: <span id="bottom_total_price">0.00</span> за 1 литр: <span id="bottom_price_per_liter">0.00</span></div>
</div>
<div class="bottom-right">
<div class="version-text">Hydroponic Profile Generator 0.221</div>
</div>
</div>
</div>
<script>
// Молярные массы элементов
const molN = 14.0067;
const molP = 30.973762;
const molK = 39.0983;
const molCa = 40.078;
const molMg = 24.305;
const molS = 32.065;
const molCl = 35.453;
// Глобальные переменные для хранения значений
let currentProfile = {
N: 220, NO3: 200, NH4: 20, P: 40, K: 180, Ca: 200, Mg: 50, S: 73.049, Cl: 0, EC: 2.102,
Fe: 2000, Mn: 550, B: 500, Zn: 330, Cu: 63, Mo: 63, Co: 0, Si: 0,
volume: 10.0
};
// Составы солей (процентовки)
let saltCompositions = {
CaNO3: { Ca: 16.972, NO3: 11.863, NH4: 0.000 },
KNO3: { K: 38.672, NO3: 13.854 },
NH4NO3: { NH4: 17.499, NO3: 17.499 },
MgSO4: { Mg: 9.861, S: 13.010 },
KH2PO4: { K: 28.731, P: 22.761 },
K2SO4: { K: 44.874, S: 18.401 },
MgNO3: { Mg: 9.479, NO3: 10.925 },
CaCl2: { Ca: 18.294, Cl: 32.366 }
};
// Флаги использования солей
let useK2SO4 = false;
let useMgNO3 = false;
// Флаг для предотвращения рекурсивных вызовов
let isUpdating = false;
const roundTo = (value, decimals) => {
if (!Number.isFinite(value)) return NaN;
const factor = Math.pow(10, decimals);
return Math.round(value * factor) / factor;
};
const setInputValue = (id, value, decimals, { force = false } = {}) => {
const input = document.getElementById(id);
if (!input || !Number.isFinite(value)) return;
if (document.activeElement === input && !force) return;
if (typeof decimals === 'number') {
input.value = roundTo(value, decimals).toFixed(decimals);
} else {
input.value = value;
}
};
const parseInput = (id, fallback = 0) => {
const el = document.getElementById(id);
if (!el) return fallback;
const value = parseFloat(String(el.value).replace(',', '.'));
return Number.isFinite(value) ? value : fallback;
};
const parseSaltValue = (id) => {
const el = document.getElementById(id);
if (!el) return 0;
const value = parseFloat(String(el.value).replace(',', '.'));
return Number.isFinite(value) ? value : 0;
};
const getSaltPercent = (salt, ion) => parseSaltValue(`salt_${salt}_${ion}`);
const setSaltPercent = (salt, ion, value) => {
setInputValue(`salt_${salt}_${ion}`, value, 3, { force: true });
};
const equalsRounded = (value, target, decimals = 1) => roundTo(value, decimals) === target;
const nearZero = (value, epsilon = 1e-6) => Math.abs(value) < epsilon;
const formatPercent = (value) => roundTo(value, 1).toFixed(1);
const getSaltWeight = (salt) => parseSaltValue(`g_${salt}`);
function getValues() {
const data = {
NO3: parseInput('NO3'),
NH4: parseInput('NH4'),
NH4_ratio: parseInput('NH4_ratio'),
P: parseInput('P'),
K: parseInput('K'),
Ca: parseInput('Ca'),
Mg: parseInput('Mg'),
S: parseInput('S'),
Cl: parseInput('Cl'),
EC: parseInput('EC'),
volume: parseInput('volume', 10),
Fe: parseInput('Fe'),
Mn: parseInput('Mn'),
B: parseInput('B'),
Zn: parseInput('Zn'),
Cu: parseInput('Cu'),
Mo: parseInput('Mo'),
Co: parseInput('Co'),
Si: parseInput('Si')
};
// N всегда вычисляется как сумма NO3 + NH4 (как в оригинале getVar)
data.N = data.NO3 + data.NH4;
// Обновляем N только если поле не в фокусе И не во время обработки onNChange
const nInput = document.getElementById('N');
if (!isUpdating && document.activeElement !== nInput) {
setInputValue('N', data.N, 3);
}
currentProfile = { ...currentProfile, ...data };
return data;
}
function calculateCa(values) {
const data = values || getValues();
const vNH4 = data.NH4;
const vP = data.P;
const vMg = data.Mg;
const vK = data.K;
const vNO3 = data.NO3;
const vS = data.S;
const vCl = data.Cl;
const numerator = -molCa * (vNH4 * molP * molMg * molK * molS * molCl -
vP * molN * molMg * molK * molS * molCl +
2 * vMg * molN * molP * molK * molS * molCl +
vK * molN * molP * molMg * molS * molCl -
vNO3 * molP * molMg * molK * molS * molCl -
2 * vS * molN * molP * molMg * molK * molCl -
vCl * molN * molP * molMg * molK * molS);
const denominator = 2 * molN * molP * molMg * molK * molS * molCl;
const vCa = numerator / denominator;
if (Number.isFinite(vCa)) {
data.Ca = vCa;
currentProfile.Ca = vCa;
setInputValue('Ca', vCa, 3, { force: true });
}
return data;
}
function calcAll(values, options = {}) {
const opts = { skipCalculateS: false, ...options };
const data = values || getValues();
if (!opts.skipCalculateS) {
calculateS(data);
}
calcEC(data);
calcWeight(data);
calcRatios(data);
genNH4NO3event(data);
updateInfo(data);
updateProfileString(data);
// Расчет концентратов
calcConcentrates();
// Обновление цен
updatePriceResults();
Object.assign(currentProfile, data);
return data;
}
// Полный пересчет включая Ca (для зеленых полей P, Cl)
function calcAllWithCa(values, options = {}) {
const data = values || getValues();
if (!options.skipCalculateS) {
calculateS(data);
}
calculateCa(data);
calcEC(data);
calcWeight(data);
calcRatios(data);
genNH4NO3event(data);
updateInfo(data);
updateProfileString(data);
// Расчет концентратов
calcConcentrates();
// Обновление цен
updatePriceResults();
Object.assign(currentProfile, data);
return data;
}
// Переключение вкладок
function switchTab(tabName, tabElement) {
// Скрыть все вкладки
document.querySelectorAll('.tab-content').forEach(tab => {
tab.classList.remove('active');
});
document.querySelectorAll('.tab').forEach(tab => {
tab.classList.remove('active');
});
// Показать выбранную вкладку
document.getElementById(tabName).classList.add('active');
// Если передан элемент таба (при клике), активировать его
// Иначе (при восстановлении) найти таб по тексту вкладки
if (tabElement) {
tabElement.classList.add('active');
} else {
// Найти и активировать соответствующий таб в панели
document.querySelectorAll('.tab').forEach(tab => {
if (tab.getAttribute('onclick') && tab.getAttribute('onclick').includes(`'${tabName}'`)) {
tab.classList.add('active');
}
});
}
// Сохранить активную вкладку в localStorage
localStorage.setItem('activeTab', tabName);
}
// Переключение внутренних вкладок (в концентратах)
function switchInnerTab(tabName, tabElement) {
document.querySelectorAll('.inner-tab-content').forEach(tab => {
tab.classList.remove('active');
});
document.querySelectorAll('.tab-inner').forEach(tab => {
tab.classList.remove('active');
});
document.getElementById(tabName).classList.add('active');
if (tabElement) {
tabElement.classList.add('active');
} else {
// Найти и активировать соответствующий внутренний таб
document.querySelectorAll('.tab-inner').forEach(tab => {
if (tab.getAttribute('onclick') && tab.getAttribute('onclick').includes(`'${tabName}'`)) {
tab.classList.add('active');
}
});
}
// Пересчитать концентраты при переключении на вкладки "calc" или "preparation"
if (tabName === 'calc' || tabName === 'preparation') {
calcConcentrates();
}
// Сохранить активную внутреннюю вкладку в localStorage
localStorage.setItem('activeInnerTab', tabName);
}
// Расчет EC
function calcEC(values) {
const data = values || getValues();
const vNH4 = data.NH4;
const vCa = data.Ca;
const vMg = data.Mg;
const vK = data.K;
const vN = data.N;
const vEC = 0.095 * (vNH4 * molCa * molMg * molK + 2 * vCa * molN * molMg * molK +
2 * vMg * molN * molCa * molK + vK * molN * molCa * molMg +
2 * molN * molCa * molMg * molK) / (molN * molCa * molMg * molK);
data.EC = vEC;
currentProfile.EC = vEC;
setInputValue('EC', vEC, 3, { force: true });
return data;
}
// Расчет серы
function calculateS(values) {
const data = values || getValues();
const vNH4 = data.NH4;
const vCa = data.Ca;
const vMg = data.Mg;
const vK = data.K;
const vNO3 = data.NO3;
const vP = data.P;
const vCl = data.Cl;
const vS = (-molS * (-vNH4 * molCa * molMg * molK * molP * molCl -
2 * vCa * molN * molMg * molK * molP * molCl -
2 * vMg * molN * molCa * molK * molP * molCl -
vK * molN * molCa * molMg * molP * molCl +
vNO3 * molCa * molMg * molK * molP * molCl +
vP * molN * molCa * molMg * molK * molCl +
vCl * molN * molCa * molMg * molK * molP)) /
(2 * (molN * molCa * molMg * molK * molP * molCl));
if (Number.isFinite(vS)) {
data.S = vS;
currentProfile.S = vS;
setInputValue('S', vS, 3, { force: true });
}
return data;
}
// Расчет навесок солей
function calcWeight(values) {
const data = values || getValues();
const V = data.volume;
const vP = data.P;
const vK = data.K;
const vCa = data.Ca;
const vMg = data.Mg;
const vS = data.S;
const vNH4 = data.NH4;
const vCl = data.Cl;
const vKH2PO4_P = saltCompositions.KH2PO4.P;
const vKH2PO4_K = saltCompositions.KH2PO4.K;
const vCaNO3_Ca = saltCompositions.CaNO3.Ca;
const vCaNO3_NH4 = saltCompositions.CaNO3.NH4;
const vCaCl2_Ca = saltCompositions.CaCl2.Ca;
const vCaCl2_Cl = saltCompositions.CaCl2.Cl;
const vNH4NO3_NH4 = saltCompositions.NH4NO3.NH4;
const vMgSO4_Mg = saltCompositions.MgSO4.Mg;
const vMgSO4_S = saltCompositions.MgSO4.S;
const vKNO3_K = saltCompositions.KNO3.K;
const vK2SO4_K = saltCompositions.K2SO4.K;
const vK2SO4_S = saltCompositions.K2SO4.S;
const vMgNO3_Mg = saltCompositions.MgNO3.Mg;
let sKH2PO4 = 0, sKNO3 = 0, sCaNO3 = 0, sMgNO3 = 0, sMgSO4 = 0, sK2SO4 = 0, sNH4NO3 = 0, sCaCl2 = 0;
const safe = (value) => Number.isFinite(value) ? value : 0;
if (useK2SO4 && !useMgNO3) {
sKH2PO4 = safe(vP / vKH2PO4_P);
sKNO3 = safe(-(-vK * vKH2PO4_P * vK2SO4_S * vMgSO4_Mg +
vP * vKH2PO4_K * vK2SO4_S * vMgSO4_Mg +
vK2SO4_K * vKH2PO4_P * vS * vMgSO4_Mg -
vK2SO4_K * vKH2PO4_P * vMg * vMgSO4_S) /
(vKNO3_K * vKH2PO4_P * vK2SO4_S * vMgSO4_Mg));
sCaNO3 = safe((vCa * vCaCl2_Cl - vCl * vCaCl2_Ca) / (vCaNO3_Ca * vCaCl2_Cl));
sMgNO3 = 0;
sMgSO4 = safe(vMg / vMgSO4_Mg);
sK2SO4 = safe((vS * vMgSO4_Mg - vMg * vMgSO4_S) / (vK2SO4_S * vMgSO4_Mg));
sNH4NO3 = safe(-(-vNH4 * vCaNO3_Ca * vCaCl2_Cl +
vCaNO3_NH4 * vCa * vCaCl2_Cl -
vCaNO3_NH4 * vCl * vCaCl2_Ca) /
(vNH4NO3_NH4 * vCaNO3_Ca * vCaCl2_Cl));
sCaCl2 = safe(vCl / vCaCl2_Cl);
} else if (!useK2SO4 && useMgNO3) {
sKH2PO4 = safe(vP / vKH2PO4_P);
sKNO3 = safe((vK * vKH2PO4_P - vP * vKH2PO4_K) / (vKNO3_K * vKH2PO4_P));
sCaNO3 = safe((vCa * vCaCl2_Cl - vCl * vCaCl2_Ca) / (vCaNO3_Ca * vCaCl2_Cl));
sMgNO3 = safe((vMg * vMgSO4_S - vMgSO4_Mg * vS) / (vMgNO3_Mg * vMgSO4_S));
sMgSO4 = safe(vS / vMgSO4_S);
sK2SO4 = 0;
sNH4NO3 = safe((vNH4 * vCaNO3_Ca * vCaCl2_Cl -
vCaNO3_NH4 * vCa * vCaCl2_Cl +
vCaNO3_NH4 * vCl * vCaCl2_Ca) /
(vNH4NO3_NH4 * vCaNO3_Ca * vCaCl2_Cl));
sCaCl2 = safe(vCl / vCaCl2_Cl);
} else {
sKH2PO4 = safe(vP / vKH2PO4_P);
sKNO3 = safe((vK * vKH2PO4_P - vP * vKH2PO4_K) / (vKNO3_K * vKH2PO4_P));
sCaNO3 = safe((vCa * vCaCl2_Cl - vCl * vCaCl2_Ca) / (vCaNO3_Ca * vCaCl2_Cl));
sMgNO3 = 0;
sMgSO4 = safe(vMg / vMgSO4_Mg);
sK2SO4 = 0;
sNH4NO3 = safe(-(-vNH4 * vCaNO3_Ca * vCaCl2_Cl +
vCaNO3_NH4 * vCa * vCaCl2_Cl -
vCaNO3_NH4 * vCl * vCaCl2_Ca) /
(vNH4NO3_NH4 * vCaNO3_Ca * vCaCl2_Cl));
sCaCl2 = safe(vCl / vCaCl2_Cl);
}
// Динамическая точность в зависимости от объема (как в легаси)
let vDecimalPlaces = 3;
let vStep = 0.001;
if (V >= 1) { vDecimalPlaces = 3; vStep = 0.001; }
if (V >= 10) { vDecimalPlaces = 2; vStep = 0.01; }
if (V >= 100) { vDecimalPlaces = 1; vStep = 0.1; }
if (V >= 1000) { vDecimalPlaces = 0; vStep = 1; }
// Устанавливаем step для всех полей с весами
['g_CaNO3', 'g_KNO3', 'g_NH4NO3', 'g_MgSO4', 'g_KH2PO4', 'g_K2SO4', 'g_MgNO3', 'g_CaCl2'].forEach(id => {
const input = document.getElementById(id);
if (input) input.step = vStep;
});
const grams = (value) => roundTo(value * V / 10, vDecimalPlaces);
setInputValue('g_CaNO3', grams(sCaNO3), vDecimalPlaces, { force: true });
setInputValue('g_KNO3', grams(sKNO3), vDecimalPlaces, { force: true });
setInputValue('g_NH4NO3', grams(sNH4NO3), vDecimalPlaces, { force: true });
setInputValue('g_MgSO4', grams(sMgSO4), vDecimalPlaces, { force: true });
setInputValue('g_KH2PO4', grams(sKH2PO4), vDecimalPlaces, { force: true });
setInputValue('g_K2SO4', grams(sK2SO4), vDecimalPlaces, { force: true });
setInputValue('g_MgNO3', grams(sMgNO3), vDecimalPlaces, { force: true });
setInputValue('g_CaCl2', grams(sCaCl2), vDecimalPlaces, { force: true });
const totalSalts = grams(sCaNO3 + sKNO3 + sNH4NO3 + sMgSO4 + sKH2PO4 + sK2SO4 + sMgNO3 + sCaCl2);
setInputValue('total-salts', totalSalts, vDecimalPlaces, { force: true });
return data;
}
function calcRatios(values) {
const data = values || getValues();
const { N, P, K, Ca, Mg, S, Cl, NH4, NO3 } = data;
if (N <= 0 || P <= 0 || K <= 0 || Ca <= 0 || Mg <= 0 || S <= 0 || NH4 <= 0 || NO3 <= 0) {
return data;
}
const ratio = (a, b) => (b !== 0 ? a / b : NaN);
const setRatio = (id, value, decimals = 3) => {
if (Number.isFinite(value)) setInputValue(id, value, decimals);
};
setRatio('m_N_P', ratio(N, P));
setRatio('m_N_K', ratio(N, K));
setRatio('m_N_Ca', ratio(N, Ca));
setRatio('m_N_Mg', ratio(N, Mg));
setRatio('m_N_S', ratio(N, S));
setRatio('m_P_N', ratio(P, N));
setRatio('m_P_K', ratio(P, K));
setRatio('m_P_Ca', ratio(P, Ca));
setRatio('m_P_Mg', ratio(P, Mg));
setRatio('m_P_S', ratio(P, S));
setRatio('m_K_N', ratio(K, N));
setRatio('m_K_P', ratio(K, P));
setRatio('m_K_Ca', ratio(K, Ca));
setRatio('m_K_Mg', ratio(K, Mg));
setRatio('m_K_S', ratio(K, S));
setRatio('m_Ca_N', ratio(Ca, N));
setRatio('m_Ca_P', ratio(Ca, P));
setRatio('m_Ca_K', ratio(Ca, K));
setRatio('m_Ca_Mg', ratio(Ca, Mg));
setRatio('m_Ca_S', ratio(Ca, S));
setRatio('m_Mg_N', ratio(Mg, N));
setRatio('m_Mg_P', ratio(Mg, P));
setRatio('m_Mg_K', ratio(Mg, K));
setRatio('m_Mg_Ca', ratio(Mg, Ca));
setRatio('m_Mg_S', ratio(Mg, S));
setRatio('m_S_N', ratio(S, N));
setRatio('m_S_P', ratio(S, P));
setRatio('m_S_K', ratio(S, K));
setRatio('m_S_Ca', ratio(S, Ca));
setRatio('m_S_Mg', ratio(S, Mg));
const nh4ratio = ratio(NH4, NO3);
if (Number.isFinite(nh4ratio)) setInputValue('NH4_ratio', nh4ratio, 3);
return data;
}
function genNH4NO3event(values) {
const data = values || getValues();
const ratioLabel = document.getElementById('nh4no3-info');
if (!ratioLabel) return;
if (data.NH4 > 0) {
ratioLabel.textContent = `NH4:NO3 1:${roundTo(data.NO3 / data.NH4, 0)}`;
} else {
ratioLabel.textContent = 'NO3=100%';
}
return data;
}
// Обновление информационных панелей
function updateInfo(values) {
const data = values || getValues();
const vN = data.N;
const vP = data.P;
const vK = data.K;
const vCa = data.Ca;
const vMg = data.Mg;
const vS = data.S;
const vCl = data.Cl;
const vSUM = vN + vP + vK + vCa + vMg + vS + vCl;
const ratioLine = vN > 0
? `N:1 : ${roundTo(vP / vN, 2)} : ${roundTo(vK / vN, 2)} : ${roundTo(vCa / vN, 2)} : ${roundTo(vMg / vN, 2)} : ${roundTo(vS / vN, 2)} : ${roundTo(vCl / vN, 0)} sPPM=${roundTo(vSUM, 2)}`
: 'N: --';
document.getElementById('ratio-info').textContent = ratioLine;
const npkN = Math.round(vN / 10);
const npkP = Math.round(vP / 0.436421 / 10);
const npkK = Math.round(vK / 0.830148 / 10);
const CaO = roundTo(vCa / 0.714691 / 10 * 10, 1) / 10;
const MgO = roundTo(vMg / 0.603036 / 10 * 10, 1) / 10;
const SO3 = roundTo(vS / 0.400496 / 10 * 10, 1) / 10;
const npkInfo = `NPK: ${npkN}-${npkP}-${npkK} CaO=${CaO.toFixed(1)}% MgO=${MgO.toFixed(1)}% SO3=${SO3.toFixed(1)}%`;
document.getElementById('npk-info').textContent = npkInfo;
return data;
}
// Обновление строки профиля
function updateProfileString(values) {
const data = values || currentProfile;
const Fe = parseFloat(document.getElementById('Fe').value) || 0;
const Mn = parseFloat(document.getElementById('Mn').value) || 0;
const B = parseFloat(document.getElementById('B').value) || 0;
const Zn = parseFloat(document.getElementById('Zn').value) || 0;
const Cu = parseFloat(document.getElementById('Cu').value) || 0;
const Mo = parseFloat(document.getElementById('Mo').value) || 0;
const Co = parseFloat(document.getElementById('Co').value) || 0;
const Si = parseFloat(document.getElementById('Si').value) || 0;
const profileStr = `N=${Math.round(data.N)} NO3=${roundTo(data.NO3, 2)} NH4=${roundTo(data.NH4, 2)} P=${roundTo(data.P, 2)} K=${roundTo(data.K, 2)} Ca=${roundTo(data.Ca, 2)} Mg=${roundTo(data.Mg, 2)} S=${roundTo(data.S, 1)} Cl=${roundTo(data.Cl, 1)} Fe=${roundTo(Fe / 1000, 3)} Mn=${roundTo(Mn / 1000, 3)} B=${roundTo(B / 1000, 3)} Zn=${roundTo(Zn / 1000, 3)} Cu=${roundTo(Cu / 1000, 3)} Mo=${roundTo(Mo / 1000, 3)} Co=${roundTo(Co / 1000, 3)} Si=${roundTo(Si / 1000, 3)}`;
document.getElementById('profile-string').value = profileStr;
return data;
}
// Обработчики событий изменения макроэлементов
function onMacroChange() {
calcAll();
}
function onNChange() {
if (isUpdating) return;
isUpdating = true;
const N = parseInput('N');
const ratio = parseInput('NH4_ratio');
if (N !== 0) {
const NO3 = N / (ratio + 1);
const NH4 = ratio * N / (ratio + 1);
// Устанавливаем значения напрямую без генерации событий
const no3Input = document.getElementById('NO3');
const nh4Input = document.getElementById('NH4');
if (no3Input) no3Input.value = NO3.toFixed(2);
if (nh4Input) nh4Input.value = NH4.toFixed(2);
}
// Используем setTimeout чтобы дать браузеру время обработать
setTimeout(() => {
calcAll();
isUpdating = false;
}, 0);
}
function onNO3Change() {
if (isUpdating) return;
calcAll();
}
function onNH4Change() {
if (isUpdating) return;
calcAll();
}
function onNH4RatioChange() {
const ratio = parseInput('NH4_ratio');
const N = parseInput('N');
const NO3 = N / (ratio + 1);
const NH4 = ratio * N / (ratio + 1);
setInputValue('NO3', NO3, 2, { force: true });
setInputValue('NH4', NH4, 2, { force: true });
calcECtoVal();
calcAll();
}
function onPChange() {
calcAllWithCa();
}
function onClChange() {
calcAllWithCa();
}
function onSChange() {
const values = getValues();
calculateCa(values);
calcEC(values);
calcWeight(values);
calcRatios(values);
genNH4NO3event(values);
updateInfo(values);
updateProfileString(values);
Object.assign(currentProfile, values);
}
function calcECtoVal() {
const vEC = parseInput('EC');
const vKN = parseInput('m_K_N');
const vKCa = parseInput('m_K_Ca');
const vKMg = parseInput('m_K_Mg');
const vNH4NO3 = parseInput('NH4_ratio');
const vP = parseInput('P');
const vCl = parseInput('Cl');
// Расчет соотношений элементов
const rN = (vKMg * vKCa) / (vKCa * vKN + vKMg * vKN + vKMg * vKCa + vKMg * vKCa * vKN);
const rK = (vKN * vKMg * vKCa) / (vKCa * vKN + vKMg * vKN + vKMg * vKCa + vKMg * vKCa * vKN);
const rCa = (vKMg * vKN) / (vKCa * vKN + vKMg * vKN + vKMg * vKCa + vKMg * vKCa * vKN);
const rMg = (vKCa * vKN) / (vKCa * vKN + vKMg * vKN + vKMg * vKCa + vKMg * vKCa * vKN);
const rNH4 = (rN * vNH4NO3) / (1 + vNH4NO3);
// Расчет масштабирующего коэффициента
const r = (0.10526315789473684211 * molN * molCa * molMg * molK * (100 * vEC - 19)) /
(rNH4 * molCa * molMg * molK + 2 * rCa * molN * molMg * molK +
2 * rMg * molN * molCa * molK + rK * molN * molCa * molMg);
// Пересчет элементов
const vN = rN * r;
const vK = rK * r;
const vCa = rCa * r;
const vMg = rMg * r;
const vNH4 = rNH4 * r;
const vNO3 = vN - vNH4;
// Расчет серы через ионный баланс
const vS = (molS * (vNH4 * molCa * molMg * molK * molP + 2 * vCa * molN * molMg * molK * molP +
2 * vMg * molN * molCa * molK * molP + vK * molN * molCa * molMg * molP -
vNO3 * molCa * molMg * molK * molP - vP * molN * molCa * molMg * molK)) /
(2 * (molN * molCa * molMg * molK * molP));
// Обновление полей
setInputValue('NO3', vNO3, 2, { force: true });
setInputValue('NH4', vNH4, 2, { force: true });
setInputValue('K', vK, 3, { force: true });
setInputValue('Ca', vCa, 3, { force: true });
setInputValue('Mg', vMg, 3, { force: true });
setInputValue('S', vS, 3, { force: true });
setInputValue('N', vN, 3, { force: true });
// Обновить остальные расчеты
calcAll();
}
function onECChange() {
calcECtoVal();
}
function onVolumeChange() {
calcAll();
microToWeght();
calcConcentrates(); // Обновляем концентраты и изготовление
}
function parseProfile() {
const profileStr = document.getElementById('profile-string').value.trim();
const parts = profileStr.split(/\s+/);
const parseButton = document.getElementById('parse-button');
if (parts.length !== 17) {
parseButton.style.background = '#ff8080';
parseButton.textContent = 'NO';
return;
}
try {
const values = {};
parts.forEach(part => {
const [key, val] = part.split('=');
if (key && val !== undefined) {
values[key] = parseFloat(val.replace(',', '.'));
}
});
if (values.N !== undefined) setInputValue('N', values.N, 3, { force: true });
if (values.NO3 !== undefined) setInputValue('NO3', values.NO3, 2, { force: true });
if (values.NH4 !== undefined) setInputValue('NH4', values.NH4, 2, { force: true });
if (values.P !== undefined) setInputValue('P', values.P, 3, { force: true });
if (values.K !== undefined) setInputValue('K', values.K, 3, { force: true });
if (values.Ca !== undefined) setInputValue('Ca', values.Ca, 3, { force: true });
if (values.Mg !== undefined) setInputValue('Mg', values.Mg, 3, { force: true });
if (values.S !== undefined) setInputValue('S', values.S, 3, { force: true });
if (values.Cl !== undefined) setInputValue('Cl', values.Cl, 3, { force: true });
if (values.Fe !== undefined) setInputValue('Fe', values.Fe * 1000, 0, { force: true });
if (values.Mn !== undefined) setInputValue('Mn', values.Mn * 1000, 0, { force: true });
if (values.B !== undefined) setInputValue('B', values.B * 1000, 0, { force: true });
if (values.Zn !== undefined) setInputValue('Zn', values.Zn * 1000, 0, { force: true });
if (values.Cu !== undefined) setInputValue('Cu', values.Cu * 1000, 0, { force: true });
if (values.Mo !== undefined) setInputValue('Mo', values.Mo * 1000, 0, { force: true });
if (values.Co !== undefined) setInputValue('Co', values.Co * 1000, 0, { force: true });
if (values.Si !== undefined) setInputValue('Si', values.Si * 1000, 0, { force: true });
calculateS();
calcEC();
calcAll();
parseButton.style.background = '#c0ffc0';
parseButton.textContent = 'OK';
} catch (e) {
parseButton.style.background = '#ff8080';
parseButton.textContent = 'NO';
}
}
function onMatrixChange(row, col) {
const fieldId = `m_${row}_${col}`;
const inverseId = `m_${col}_${row}`;
const value = parseFloat(document.getElementById(fieldId).value.replace(',', '.'));
if (!Number.isFinite(value) || value === 0) return;
const updateInverse = () => {
const inverseField = document.getElementById(inverseId);
if (inverseField && document.activeElement !== inverseField) {
inverseField.value = (1 / value).toFixed(3);
}
};
if (fieldId === 'm_K_N' || fieldId === 'm_K_Ca' || fieldId === 'm_K_Mg') {
const values = getValues();
if (fieldId === 'm_K_N') {
values.K = values.N * value;
} else if (fieldId === 'm_K_Ca') {
values.K = values.Ca * value;
} else if (fieldId === 'm_K_Mg') {
values.K = values.Mg * value;
}
setInputValue('K', values.K, 3, { force: true });
updateInverse();
calcECtoVal();
calcAll();
return;
}
const values = getValues();
let needsCaRecalc = false;
const applyRatio = (target, source, ratio) => {
const newValue = source * ratio;
if (Number.isFinite(newValue)) {
values[target] = newValue;
setInputValue(target, newValue, 3, { force: true });
if (target === 'S') needsCaRecalc = true;
}
};
if (row === 'N') {
if (col === 'P') applyRatio('N', values.P, value);
if (col === 'K') applyRatio('N', values.K, value);
if (col === 'Ca') applyRatio('N', values.Ca, value);
if (col === 'Mg') applyRatio('N', values.Mg, value);
if (col === 'S') applyRatio('N', values.S, value);
values.NO3 = values.N / (values.NH4_ratio + 1);
values.NH4 = values.NH4_ratio * values.N / (values.NH4_ratio + 1);
} else if (row === 'P') {
if (col === 'N') applyRatio('P', values.N, value);
if (col === 'K') applyRatio('P', values.K, value);
if (col === 'Ca') applyRatio('P', values.Ca, value);
if (col === 'Mg') applyRatio('P', values.Mg, value);
if (col === 'S') applyRatio('P', values.S, value);
} else if (row === 'K') {
if (col === 'N') applyRatio('K', values.N, value);
if (col === 'P') applyRatio('K', values.P, value);
if (col === 'Ca') applyRatio('K', values.Ca, value);
if (col === 'Mg') applyRatio('K', values.Mg, value);
if (col === 'S') applyRatio('K', values.S, value);
} else if (row === 'Ca') {
if (col === 'N') applyRatio('Ca', values.N, value);
if (col === 'P') applyRatio('Ca', values.P, value);
if (col === 'K') applyRatio('Ca', values.K, value);
if (col === 'Mg') applyRatio('Ca', values.Mg, value);
if (col === 'S') applyRatio('Ca', values.S, value);
} else if (row === 'Mg') {
if (col === 'N') applyRatio('Mg', values.N, value);
if (col === 'P') applyRatio('Mg', values.P, value);
if (col === 'K') applyRatio('Mg', values.K, value);
if (col === 'Ca') applyRatio('Mg', values.Ca, value);
if (col === 'S') applyRatio('Mg', values.S, value);
} else if (row === 'S') {
if (col === 'N') applyRatio('S', values.N, value);
if (col === 'P') applyRatio('S', values.P, value);
if (col === 'K') applyRatio('S', values.K, value);
if (col === 'Ca') applyRatio('S', values.Ca, value);
if (col === 'Mg') applyRatio('S', values.Mg, value);
}
updateInverse();
setInputValue('N', values.N, 3, { force: true });
setInputValue('P', values.P, 3, { force: true });
setInputValue('K', values.K, 3, { force: true });
setInputValue('Ca', values.Ca, 3, { force: true });
setInputValue('Mg', values.Mg, 3, { force: true });
setInputValue('S', values.S, 3, { force: true });
setInputValue('NO3', values.NO3, 3, { force: true });
setInputValue('NH4', values.NH4, 3, { force: true });
if (needsCaRecalc) {
calculateCa(values);
}
calcAll();
}
function syncSaltCompositions() {
saltCompositions.CaNO3.Ca = getSaltPercent('CaNO3', 'Ca');
saltCompositions.CaNO3.NO3 = getSaltPercent('CaNO3', 'NO3');
saltCompositions.CaNO3.NH4 = getSaltPercent('CaNO3', 'NH4');
saltCompositions.KNO3.K = getSaltPercent('KNO3', 'K');
saltCompositions.KNO3.NO3 = getSaltPercent('KNO3', 'NO3');
saltCompositions.NH4NO3.NH4 = getSaltPercent('NH4NO3', 'NH4');
saltCompositions.NH4NO3.NO3 = getSaltPercent('NH4NO3', 'NO3');
saltCompositions.MgSO4.Mg = getSaltPercent('MgSO4', 'Mg');
saltCompositions.MgSO4.S = getSaltPercent('MgSO4', 'S');
saltCompositions.KH2PO4.K = getSaltPercent('KH2PO4', 'K');
saltCompositions.KH2PO4.P = getSaltPercent('KH2PO4', 'P');
saltCompositions.K2SO4.K = getSaltPercent('K2SO4', 'K');
saltCompositions.K2SO4.S = getSaltPercent('K2SO4', 'S');
saltCompositions.MgNO3.Mg = getSaltPercent('MgNO3', 'Mg');
saltCompositions.MgNO3.NO3 = getSaltPercent('MgNO3', 'NO3');
saltCompositions.CaCl2.Ca = getSaltPercent('CaCl2', 'Ca');
saltCompositions.CaCl2.Cl = getSaltPercent('CaCl2', 'Cl');
}
function updateSaltLabels() {
const labelCaNO3 = document.getElementById('label_CaNO3');
if (labelCaNO3) {
const ca = getSaltPercent('CaNO3', 'Ca');
const no3 = getSaltPercent('CaNO3', 'NO3');
const nh4 = getSaltPercent('CaNO3', 'NH4');
let text;
if (nearZero(nh4) && equalsRounded(ca, 17, 0)) {
text = 'Кальций азотнокислый Ca(NO3)2*4H2O';
} else if (nearZero(nh4) && equalsRounded(ca, 20, 0)) {
text = 'Кальций азотнокислый Ca(NO3)2*2H2O';
} else if (nearZero(nh4) && equalsRounded(ca, 24.4, 1)) {
text = 'Кальций азотнокислый Ca(NO3)2';
} else {
text = `Селитра кальциевая CaO-${formatPercent(ca / 0.714691)}% N-${formatPercent(nh4 + no3)}%`;
}
labelCaNO3.textContent = text;
}
const labelKNO3 = document.getElementById('label_KNO3');
if (labelKNO3) {
const k = getSaltPercent('KNO3', 'K');
const no3 = getSaltPercent('KNO3', 'NO3');
const text = equalsRounded(k, 38.7, 1)
? 'Калий азотнокислый KNO3'
: `Селитра калиевая K2O-${formatPercent(k / 0.830148)}% N-${formatPercent(no3)}%`;
labelKNO3.textContent = text;
}
const labelNH4NO3 = document.getElementById('label_NH4NO3');
if (labelNH4NO3) {
const nh4 = getSaltPercent('NH4NO3', 'NH4');
const no3 = getSaltPercent('NH4NO3', 'NO3');
const text = equalsRounded(no3, 17.5, 1)
? 'Аммоний азотнокислый NH4NO3'
: `Селитра аммиачная N-${formatPercent(nh4 + no3)}%`;
labelNH4NO3.textContent = text;
}
const labelMgSO4 = document.getElementById('label_MgSO4');
if (labelMgSO4) {
const mg = getSaltPercent('MgSO4', 'Mg');
const s = getSaltPercent('MgSO4', 'S');
let text;
if (equalsRounded(mg, 9.9, 1)) {
text = 'Магний сернокислый MgSO4*7H2O';
} else if (equalsRounded(mg, 20.2, 1)) {
text = 'Магний сернокислый MgSO4';
} else {
text = `Сульфат магния MgO-${formatPercent(mg / 0.603036)}% SO3-${formatPercent(s / 0.400496)}%`;
}
labelMgSO4.textContent = text;
}
const labelKH2PO4 = document.getElementById('label_KH2PO4');
if (labelKH2PO4) {
const k = getSaltPercent('KH2PO4', 'K');
const p = getSaltPercent('KH2PO4', 'P');
const text = equalsRounded(k, 28.7, 1)
? 'Калий фосфорнокислый KH2PO4'
: `Монофосфат калия K2O-${formatPercent(k / 0.830148)}% P2O5-${formatPercent(p / 0.436421)}%`;
labelKH2PO4.textContent = text;
}
const labelK2SO4 = document.getElementById('label_K2SO4');
if (labelK2SO4) {
const k = getSaltPercent('K2SO4', 'K');
const s = getSaltPercent('K2SO4', 'S');
const text = equalsRounded(k, 44.9, 1)
? 'Калий сернокислый K2SO4'
: `Сульфат калия K2O-${formatPercent(k / 0.830148)}% SO3-${formatPercent(s / 0.400496)}%`;
labelK2SO4.textContent = text;
}
const labelMgNO3 = document.getElementById('label_MgNO3');
if (labelMgNO3) {
const mg = getSaltPercent('MgNO3', 'Mg');
const no3 = getSaltPercent('MgNO3', 'NO3');
let text;
if (equalsRounded(mg, 9.5, 1)) {
text = 'Магний азотнокислый Mg(NO3)2*6H2O';
} else if (equalsRounded(mg, 16.4, 1)) {
text = 'Магний азотнокислый Mg(NO3)2';
} else {
text = `Селитра магниевая MgO-${formatPercent(mg / 0.603036)}% N-${formatPercent(no3)}%`;
}
labelMgNO3.textContent = text;
}
const labelCaCl2 = document.getElementById('label_CaCl2');
if (labelCaCl2) {
const ca = getSaltPercent('CaCl2', 'Ca');
const cl = getSaltPercent('CaCl2', 'Cl');
let text;
if (equalsRounded(ca, 18.3, 1)) {
text = 'Хлорид кальция 6-водный CaCl2*6H2O';
} else if (equalsRounded(ca, 36.1, 1)) {
text = 'Хлорид кальция безводный CaCl2';
} else {
text = `Кальций хлористый CaO-${formatPercent(ca / 0.714691)}% Cl-${formatPercent(cl)}%`;
}
labelCaCl2.textContent = text;
}
}
function onSaltCompositionChange(salt, changedIon) {
if (isUpdating) return;
isUpdating = true;
try {
if (salt) {
switch (salt) {
case 'CaNO3': {
let ca = getSaltPercent('CaNO3', 'Ca');
let no3 = getSaltPercent('CaNO3', 'NO3');
let nh4 = getSaltPercent('CaNO3', 'NH4');
if (changedIon === 'Ca') {
no3 = (2 * ca * molN + nh4 * molCa) / molCa;
setSaltPercent('CaNO3', 'NO3', no3);
} else if (changedIon === 'NH4') {
no3 = (2 * ca * molN + nh4 * molCa) / molCa;
setSaltPercent('CaNO3', 'NO3', no3);
ca = -molCa * (nh4 - no3) / (2 * molN);
setSaltPercent('CaNO3', 'Ca', ca);
} else if (changedIon === 'NO3') {
ca = -molCa * (nh4 - no3) / (2 * molN);
setSaltPercent('CaNO3', 'Ca', ca);
}
break;
}
case 'KNO3': {
let k = getSaltPercent('KNO3', 'K');
let no3 = getSaltPercent('KNO3', 'NO3');
if (changedIon === 'K') {
no3 = (k * molN) / molK;
setSaltPercent('KNO3', 'NO3', no3);
} else if (changedIon === 'NO3') {
k = (no3 * molK) / molN;
setSaltPercent('KNO3', 'K', k);
}
break;
}
case 'NH4NO3': {
const value = getSaltPercent('NH4NO3', changedIon);
setSaltPercent('NH4NO3', changedIon === 'NH4' ? 'NO3' : 'NH4', value);
break;
}
case 'MgSO4': {
let mg = getSaltPercent('MgSO4', 'Mg');
let s = getSaltPercent('MgSO4', 'S');
if (changedIon === 'Mg') {
s = (mg * molS) / molMg;
setSaltPercent('MgSO4', 'S', s);
} else if (changedIon === 'S') {
mg = (s * molMg) / molS;
setSaltPercent('MgSO4', 'Mg', mg);
}
break;
}
case 'KH2PO4': {
let k = getSaltPercent('KH2PO4', 'K');
let p = getSaltPercent('KH2PO4', 'P');
if (changedIon === 'K') {
p = (k * molP) / molK;
setSaltPercent('KH2PO4', 'P', p);
} else if (changedIon === 'P') {
k = (p * molK) / molP;
setSaltPercent('KH2PO4', 'K', k);
}
break;
}
case 'K2SO4': {
let k = getSaltPercent('K2SO4', 'K');
let s = getSaltPercent('K2SO4', 'S');
if (changedIon === 'K') {
s = (k * molS) / (2 * molK);
setSaltPercent('K2SO4', 'S', s);
} else if (changedIon === 'S') {
k = (s * 2 * molK) / molS;
setSaltPercent('K2SO4', 'K', k);
}
break;
}
case 'MgNO3': {
let mg = getSaltPercent('MgNO3', 'Mg');
let no3 = getSaltPercent('MgNO3', 'NO3');
if (changedIon === 'Mg') {
no3 = (2 * mg * molN) / molMg;
setSaltPercent('MgNO3', 'NO3', no3);
} else if (changedIon === 'NO3') {
mg = 0.5 * (no3 / molN) * molMg;
setSaltPercent('MgNO3', 'Mg', mg);
}
break;
}
case 'CaCl2': {
let ca = getSaltPercent('CaCl2', 'Ca');
let cl = getSaltPercent('CaCl2', 'Cl');
if (changedIon === 'Ca') {
cl = (2 * ca / molCa) * molCl;
setSaltPercent('CaCl2', 'Cl', cl);
} else if (changedIon === 'Cl') {
ca = 0.5 * (cl / molCl) * molCa;
setSaltPercent('CaCl2', 'Ca', ca);
}
break;
}
}
}
syncSaltCompositions();
updateSaltLabels();
updateSaltNames(); // Обновляем названия солей (как SoilName в легаси)
calcWeight(getValues());
applyWeightsToProfile({ sync: false });
calcConcentrates(); // Обновляем концентраты и изготовление (как CalcConc в легаси)
} finally {
isUpdating = false;
}
}
function applyWeightsToProfile({ sync = true } = {}) {
const V = parseInput('volume', 10);
if (!Number.isFinite(V) || V <= 0) return;
if (sync) syncSaltCompositions();
const factor = 0.1 * V;
if (factor === 0) return;
const sCaNO3 = getSaltWeight('CaNO3');
const sKNO3 = getSaltWeight('KNO3');
const sNH4NO3 = getSaltWeight('NH4NO3');
const sMgSO4 = getSaltWeight('MgSO4');
const sKH2PO4 = getSaltWeight('KH2PO4');
const sK2SO4 = useK2SO4 ? getSaltWeight('K2SO4') : 0;
const sMgNO3 = useMgNO3 ? getSaltWeight('MgNO3') : 0;
const sCaCl2 = getSaltWeight('CaCl2');
const comps = saltCompositions;
const vNO3 = (sCaNO3 * comps.CaNO3.NO3 +
sNH4NO3 * comps.NH4NO3.NO3 +
sKNO3 * comps.KNO3.NO3 +
sMgNO3 * comps.MgNO3.NO3) / factor;
const vNH4 = (sCaNO3 * comps.CaNO3.NH4 +
sNH4NO3 * comps.NH4NO3.NH4) / factor;
const vN = vNO3 + vNH4;
const vP = (sKH2PO4 * comps.KH2PO4.P) / factor;
const vK = (sKNO3 * comps.KNO3.K +
sKH2PO4 * comps.KH2PO4.K +
sK2SO4 * comps.K2SO4.K) / factor;
const vCa = (sCaNO3 * comps.CaNO3.Ca + sCaCl2 * comps.CaCl2.Ca) / factor;
const vMg = (sMgSO4 * comps.MgSO4.Mg + sMgNO3 * comps.MgNO3.Mg) / factor;
const vCl = (sCaCl2 * comps.CaCl2.Cl) / factor;
const vNH4NO3ratio = vNO3 !== 0 ? vNH4 / vNO3 : 0;
setInputValue('NO3', vNO3, 3, { force: true });
setInputValue('NH4', vNH4, 3, { force: true });
setInputValue('N', vN, 3, { force: true });
setInputValue('P', vP, 3, { force: true });
setInputValue('K', vK, 3, { force: true });
setInputValue('Ca', vCa, 3, { force: true });
setInputValue('Mg', vMg, 3, { force: true });
setInputValue('Cl', vCl, 3, { force: true });
setInputValue('NH4_ratio', vNH4NO3ratio, 3, { force: true });
let values = getValues();
values.N = vN;
values.NO3 = vNO3;
values.NH4 = vNH4;
values.P = vP;
values.K = vK;
values.Ca = vCa;
values.Mg = vMg;
values.Cl = vCl;
values.NH4_ratio = vNH4NO3ratio;
values = calculateS(values);
values = calculateCa(values);
calcAll(values, { skipCalculateS: true });
}
function onSaltWeightChange() {
if (isUpdating) return;
isUpdating = true;
try {
applyWeightsToProfile({ sync: true });
calcConcentrates(); // Обновляем концентраты и изготовление (как CalcConc в легаси)
} finally {
isUpdating = false;
}
}
function onSaltSelectionChange(changedCheckbox) {
const k2so4Checkbox = document.getElementById('use_K2SO4');
const mgno3Checkbox = document.getElementById('use_MgNO3');
// Взаимоисключающая логика как в оригинале
if (changedCheckbox === 'K2SO4') {
if (k2so4Checkbox.checked) {
mgno3Checkbox.checked = false;
} else {
mgno3Checkbox.checked = true;
}
} else if (changedCheckbox === 'MgNO3') {
if (mgno3Checkbox.checked) {
k2so4Checkbox.checked = false;
} else {
k2so4Checkbox.checked = true;
}
}
useK2SO4 = k2so4Checkbox.checked;
useMgNO3 = mgno3Checkbox.checked;
calcAll();
}
// Глобальные переменные для микро как в легаси
let agFe = 20.1, agMn = 36.4, agB = 17.5, agZn = 22.7, agCu = 25.5, agMo = 54.3, agCo = 13.0, agSi = 7.0;
let vdFe = 20.1, vdMn = 36.4, vdB = 17.5, vdZn = 22.7, vdCu = 25.5, vdMo = 54.3, vdCo = 13.0, vdSi = 7.0;
let gmSUM = 0;
// Микроэлементы
function onMicroChange() {
updateProfileString();
microToWeght();
updateMicroNames(); // Обновляем названия микроэлементов
calcConcentrates();
}
function onMicroCompositionChange() {
microToWeght();
updateMicroNames(); // Обновляем названия микроэлементов
calcConcentrates();
}
function toMicrocomplex() {
// Генерация строки состава микрокомплекса из составляющих (как в легаси)
const gFe = parseFloat(document.getElementById('g_Fe').value) || 0;
const gMn = parseFloat(document.getElementById('g_Mn').value) || 0;
const gB = parseFloat(document.getElementById('g_B').value) || 0;
const gZn = parseFloat(document.getElementById('g_Zn').value) || 0;
const gCu = parseFloat(document.getElementById('g_Cu').value) || 0;
const gMo = parseFloat(document.getElementById('g_Mo').value) || 0;
const gCo = parseFloat(document.getElementById('g_Co').value) || 0;
const gSi = parseFloat(document.getElementById('g_Si').value) || 0;
gmSUM = gFe + gMn + gB + gZn + gCu + gMo + gCo + gSi;
const V = parseFloat(document.getElementById('volume').value) || 10;
const Fe = parseFloat(document.getElementById('Fe').value) || 0;
const Mn = parseFloat(document.getElementById('Mn').value) || 0;
const B = parseFloat(document.getElementById('B').value) || 0;
const Zn = parseFloat(document.getElementById('Zn').value) || 0;
const Cu = parseFloat(document.getElementById('Cu').value) || 0;
const Mo = parseFloat(document.getElementById('Mo').value) || 0;
const Co = parseFloat(document.getElementById('Co').value) || 0;
const Si = parseFloat(document.getElementById('Si').value) || 0;
if (gmSUM > 0) {
agFe = (Fe * V) / (gmSUM * 10000);
agMn = (Mn * V) / (gmSUM * 10000);
agB = (B * V) / (gmSUM * 10000);
agZn = (Zn * V) / (gmSUM * 10000);
agCu = (Cu * V) / (gmSUM * 10000);
agMo = (Mo * V) / (gmSUM * 10000);
agCo = (Co * V) / (gmSUM * 10000);
agSi = (Si * V) / (gmSUM * 10000);
}
}
function onComplexChange() {
const checkbox = document.getElementById('use_complex');
const complexField = document.getElementById('complex_value');
// Элементы таблицы солей
const saltsTable = document.getElementById('salts-table');
const saltsHeader = document.getElementById('salts-header');
// Поля микроэлементов (БЕЗ бора! Бор остается редактируемым)
const microFields = ['Fe', 'Mn', 'Zn', 'Cu', 'Mo', 'Co', 'Si'];
if (checkbox.checked) {
// Сохраняем текущие проценты перед включением комплекса
vdFe = parseFloat(document.getElementById('micro_Fe_percent').value) || 20.1;
vdMn = parseFloat(document.getElementById('micro_Mn_percent').value) || 36.4;
vdB = parseFloat(document.getElementById('micro_B_percent').value) || 17.5;
vdZn = parseFloat(document.getElementById('micro_Zn_percent').value) || 22.7;
vdCu = parseFloat(document.getElementById('micro_Cu_percent').value) || 25.5;
vdMo = parseFloat(document.getElementById('micro_Mo_percent').value) || 54.3;
vdCo = parseFloat(document.getElementById('micro_Co_percent').value) || 13.0;
vdSi = parseFloat(document.getElementById('micro_Si_percent').value) || 7.0;
// Заполняем проценты вычисленными значениями (как в легаси chkComplexClick)
document.getElementById('micro_Fe_percent').value = agFe.toFixed(4);
document.getElementById('micro_Mn_percent').value = agMn.toFixed(4);
document.getElementById('micro_B_percent').value = agB.toFixed(4);
document.getElementById('micro_Zn_percent').value = agZn.toFixed(4);
document.getElementById('micro_Cu_percent').value = agCu.toFixed(4);
document.getElementById('micro_Mo_percent').value = agMo.toFixed(4);
document.getElementById('micro_Co_percent').value = agCo.toFixed(4);
document.getElementById('micro_Si_percent').value = agSi.toFixed(4);
toMicrocomplex();
// В режиме комплекса - скрываем колонку граммов в таблице, оставляем проценты
// Скрываем заголовок "граммы"
if (saltsHeader) {
const gramsLabel = saltsHeader.querySelector('span[style*="left: 215px"]');
if (gramsLabel) gramsLabel.style.display = 'none';
}
// Скрываем все поля граммов в таблице
['g_Fe', 'g_Mn', 'g_B', 'g_Zn', 'g_Cu', 'g_Mo', 'g_Co', 'g_Si'].forEach(id => {
const field = document.getElementById(id);
if (field) field.style.display = 'none';
});
// Делаем микроэлементы readonly (КРОМЕ B - он остается редактируемым!)
microFields.forEach(id => {
const field = document.getElementById(id);
if (field) {
field.readOnly = true;
field.style.background = '#e8e8e8';
}
});
// B остается активным
document.getElementById('B').readOnly = false;
document.getElementById('B').style.background = '';
// Показываем поле ввода, скрываем текст
document.getElementById('total_micro_grams').style.display = 'block';
document.getElementById('total_micro_text').style.display = 'none';
document.getElementById('total_micro_grams').value = gmSUM.toFixed(5);
} else {
// Восстанавливаем сохраненные проценты при выключении
document.getElementById('micro_Fe_percent').value = vdFe.toFixed(4);
document.getElementById('micro_Mn_percent').value = vdMn.toFixed(4);
document.getElementById('micro_B_percent').value = vdB.toFixed(4);
document.getElementById('micro_Zn_percent').value = vdZn.toFixed(4);
document.getElementById('micro_Cu_percent').value = vdCu.toFixed(4);
document.getElementById('micro_Mo_percent').value = vdMo.toFixed(4);
document.getElementById('micro_Co_percent').value = vdCo.toFixed(4);
document.getElementById('micro_Si_percent').value = vdSi.toFixed(4);
// При выключенном комплексе - показываем колонку граммов
// Показываем заголовок "граммы"
if (saltsHeader) {
const gramsLabel = saltsHeader.querySelector('span[style*="left: 215px"]');
if (gramsLabel) gramsLabel.style.display = '';
}
// Показываем все поля граммов
['g_Fe', 'g_Mn', 'g_B', 'g_Zn', 'g_Cu', 'g_Mo', 'g_Co', 'g_Si'].forEach(id => {
const field = document.getElementById(id);
if (field) field.style.display = '';
});
// Убираем readonly со всех микроэлементов
microFields.forEach(id => {
const field = document.getElementById(id);
if (field) {
field.readOnly = false;
field.style.background = '';
}
});
// Скрываем поле ввода, показываем текст
document.getElementById('total_micro_grams').style.display = 'none';
document.getElementById('total_micro_text').style.display = 'block';
document.getElementById('total_micro_text').textContent = gmSUM.toFixed(5);
}
microToWeght();
updateMicroNames(); // Обновляем названия микроэлементов
// Обновляем видимость элементов цен (аналог price() из легаси строки 1096-1144)
const priceItemsCmplx = document.querySelector('#price_result_Cmplx')?.closest('.price-item');
const priceItemsFe = document.querySelector('#price_result_Fe')?.closest('.price-item');
const priceItemsMn = document.querySelector('#price_result_Mn')?.closest('.price-item');
const priceItemsB = document.querySelector('#price_result_B')?.closest('.price-item');
const priceItemsZn = document.querySelector('#price_result_Zn')?.closest('.price-item');
const priceItemsCu = document.querySelector('#price_result_Cu')?.closest('.price-item');
const priceItemsMo = document.querySelector('#price_result_Mo')?.closest('.price-item');
const priceItemsCo = document.querySelector('#price_result_Co')?.closest('.price-item');
const priceItemsSi = document.querySelector('#price_result_Si')?.closest('.price-item');
if (checkbox.checked) {
// Показываем комплекс, скрываем отдельные микроэлементы
if (priceItemsCmplx) priceItemsCmplx.style.display = 'flex';
if (priceItemsFe) priceItemsFe.style.display = 'none';
if (priceItemsMn) priceItemsMn.style.display = 'none';
if (priceItemsB) priceItemsB.style.display = 'none';
if (priceItemsZn) priceItemsZn.style.display = 'none';
if (priceItemsCu) priceItemsCu.style.display = 'none';
if (priceItemsMo) priceItemsMo.style.display = 'none';
if (priceItemsCo) priceItemsCo.style.display = 'none';
if (priceItemsSi) priceItemsSi.style.display = 'none';
} else {
// Скрываем комплекс, показываем отдельные микроэлементы
if (priceItemsCmplx) priceItemsCmplx.style.display = 'none';
if (priceItemsFe) priceItemsFe.style.display = 'flex';
if (priceItemsMn) priceItemsMn.style.display = 'flex';
if (priceItemsB) priceItemsB.style.display = 'flex';
if (priceItemsZn) priceItemsZn.style.display = 'flex';
if (priceItemsCu) priceItemsCu.style.display = 'flex';
if (priceItemsMo) priceItemsMo.style.display = 'flex';
if (priceItemsCo) priceItemsCo.style.display = 'flex';
if (priceItemsSi) priceItemsSi.style.display = 'flex';
}
// Обновляем цены после изменения видимости
updatePriceResults();
}
function onMicroVolumeChange() {
microToWeght();
}
function onTotalMicroGramsChange() {
// При изменении общих граммов в режиме комплекса пересчитываем B
if (document.getElementById('use_complex').checked) {
const totalGrams = parseFloat(document.getElementById('total_micro_grams').value) || 0;
const V = parseFloat(document.getElementById('volume').value) || 10;
const pB = parseFloat(document.getElementById('micro_B_percent').value) || 1;
if (totalGrams > 0 && V > 0 && pB > 0) {
// Пересчитываем B из общих граммов: B = gCmplx * dB * 10000 / V
const B = (totalGrams * pB * 10000) / V;
document.getElementById('B').value = Math.round(B);
// Обновляем остальные расчеты
microToWeght();
updateProfileString();
updateMicroNames(); // Обновляем названия микроэлементов
calcConcentrates(); // Обновляем концентраты и изготовление
}
}
}
function microToWeght() {
// Точная копия логики из легаси procedure microToWeght
const V = parseFloat(document.getElementById('volume').value) || 10;
const Fe = parseFloat(document.getElementById('Fe').value) || 0;
const Mn = parseFloat(document.getElementById('Mn').value) || 0;
const B = parseFloat(document.getElementById('B').value) || 0;
const Zn = parseFloat(document.getElementById('Zn').value) || 0;
const Cu = parseFloat(document.getElementById('Cu').value) || 0;
const Mo = parseFloat(document.getElementById('Mo').value) || 0;
const Co = parseFloat(document.getElementById('Co').value) || 0;
const Si = parseFloat(document.getElementById('Si').value) || 0;
const pFe = parseFloat(document.getElementById('micro_Fe_percent').value) || 0;
const pMn = parseFloat(document.getElementById('micro_Mn_percent').value) || 0;
const pB = parseFloat(document.getElementById('micro_B_percent').value) || 0;
const pZn = parseFloat(document.getElementById('micro_Zn_percent').value) || 0;
const pCu = parseFloat(document.getElementById('micro_Cu_percent').value) || 0;
const pMo = parseFloat(document.getElementById('micro_Mo_percent').value) || 0;
const pCo = parseFloat(document.getElementById('micro_Co_percent').value) || 0;
const pSi = parseFloat(document.getElementById('micro_Si_percent').value) || 0;
const isComplexMode = document.getElementById('use_complex').checked;
if (!isComplexMode) {
// Обычный режим - как в легаси if (kF.chKComplex.Checked = False)
// if Kf.dFe.value >0 then Kf.gFe.value:=Kf.Fe.value/Kf.dFe.value*Kf.V.value/10000
document.getElementById('g_Fe').value = (pFe > 0 ? (Fe / pFe * V / 10000).toFixed(5) : '0.00000');
document.getElementById('g_Mn').value = (pMn > 0 ? (Mn / pMn * V / 10000).toFixed(5) : '0.00000');
document.getElementById('g_B').value = (pB > 0 ? (B / pB * V / 10000).toFixed(5) : '0.00000');
document.getElementById('g_Zn').value = (pZn > 0 ? (Zn / pZn * V / 10000).toFixed(5) : '0.00000');
document.getElementById('g_Cu').value = (pCu > 0 ? (Cu / pCu * V / 10000).toFixed(5) : '0.00000');
document.getElementById('g_Mo').value = (pMo > 0 ? (Mo / pMo * V / 10000).toFixed(5) : '0.00000');
document.getElementById('g_Co').value = (pCo > 0 ? (Co / pCo * V / 10000).toFixed(5) : '0.00000');
document.getElementById('g_Si').value = (pSi > 0 ? (Si / pSi * V / 10000).toFixed(5) : '0.00000');
toMicrocomplex();
} else {
// Режим комплекса - как в легаси else
// if Kf.dB.value >0 then Kf.gCmplx.value:=Kf.B.value/Kf.dB.value*Kf.V.value/10000
const complexValue = pB > 0 ? (B / pB * V / 10000) : 0;
gmSUM = complexValue;
document.getElementById('total_micro_grams').value = complexValue.toFixed(5);
// Пересчитываем микроэлементы из комплекса
// Kf.Fe.value:=10000*Kf.gCmplx.value* (Kf.dFe.value/Kf.V.value)
if (complexValue > 0) {
document.getElementById('Fe').value = Math.round(10000 * complexValue * (pFe / V));
document.getElementById('Mn').value = Math.round(10000 * complexValue * (pMn / V));
// B остается как есть
document.getElementById('Zn').value = Math.round(10000 * complexValue * (pZn / V));
document.getElementById('Cu').value = Math.round(10000 * complexValue * (pCu / V));
document.getElementById('Mo').value = Math.round(10000 * complexValue * (pMo / V));
document.getElementById('Co').value = Math.round(10000 * complexValue * (pCo / V));
document.getElementById('Si').value = Math.round(10000 * complexValue * (pSi / V));
}
}
// Всегда обновляем отображение общих граммов
if (isComplexMode) {
document.getElementById('total_micro_grams').value = gmSUM.toFixed(5);
} else {
document.getElementById('total_micro_text').textContent = gmSUM.toFixed(5);
}
// Обновление состава - используем глобальные agXX значения как в легаси
// Формула из легаси: round(agFe*1000)/1000 - округление до 3 знаков
const composition = `Состав: Fe=${(Math.round(agFe*1000)/1000).toFixed(3)}% Mn=${(Math.round(agMn*1000)/1000).toFixed(3)}% B=${(Math.round(agB*1000)/1000).toFixed(3)}% Zn=${(Math.round(agZn*1000)/1000).toFixed(3)}% Cu=${(Math.round(agCu*1000)/1000).toFixed(3)}% Mo=${(Math.round(agMo*1000)/1000).toFixed(3)}% Co=${(Math.round(agCo*1000)/1000).toFixed(3)}% Si=${(Math.round(agSi*1000)/1000).toFixed(3)}%`;
document.getElementById('micro-composition').textContent = composition;
// Разведение
const microVol = parseFloat(document.getElementById('micro_volume').value) || 500;
if (microVol > 0 && gmSUM > 0) {
const concentration = (gmSUM * 1000 / microVol).toFixed(2);
const dilution = Math.round(1000 * V / microVol);
const usage = Math.round(microVol / V);
document.getElementById('micro-dilution-info').textContent =
`Концентрация: ${concentration} г/л, Кратность: ${dilution}:1, Расход: ${usage} мл/л раствора`;
}
}
// Обратная функция: пересчет из граммов солей В микроэлементы (аналог WeghtTomicro из легаси строки 1960-1987)
function weightToMicro() {
const V = parseFloat(document.getElementById('volume').value) || 10;
const gFe = parseFloat(document.getElementById('g_Fe').value) || 0;
const gMn = parseFloat(document.getElementById('g_Mn').value) || 0;
const gB = parseFloat(document.getElementById('g_B').value) || 0;
const gZn = parseFloat(document.getElementById('g_Zn').value) || 0;
const gCu = parseFloat(document.getElementById('g_Cu').value) || 0;
const gMo = parseFloat(document.getElementById('g_Mo').value) || 0;
const gCo = parseFloat(document.getElementById('g_Co').value) || 0;
const gSi = parseFloat(document.getElementById('g_Si').value) || 0;
const dFe = parseFloat(document.getElementById('micro_Fe_percent').value) || 0;
const dMn = parseFloat(document.getElementById('micro_Mn_percent').value) || 0;
const dB = parseFloat(document.getElementById('micro_B_percent').value) || 0;
const dZn = parseFloat(document.getElementById('micro_Zn_percent').value) || 0;
const dCu = parseFloat(document.getElementById('micro_Cu_percent').value) || 0;
const dMo = parseFloat(document.getElementById('micro_Mo_percent').value) || 0;
const dCo = parseFloat(document.getElementById('micro_Co_percent').value) || 0;
const dSi = parseFloat(document.getElementById('micro_Si_percent').value) || 0;
const isComplexMode = document.getElementById('use_complex').checked;
if (!isComplexMode) {
// Обычный режим: Kf.Fe.value:=10000*Kf.gFe.value* (Kf.dFe.value/Kf.V.value)
document.getElementById('Fe').value = Math.round(10000 * gFe * (dFe / V));
document.getElementById('Mn').value = Math.round(10000 * gMn * (dMn / V));
document.getElementById('B').value = Math.round(10000 * gB * (dB / V));
document.getElementById('Zn').value = Math.round(10000 * gZn * (dZn / V));
document.getElementById('Cu').value = Math.round(10000 * gCu * (dCu / V));
document.getElementById('Mo').value = Math.round(10000 * gMo * (dMo / V));
document.getElementById('Co').value = Math.round(10000 * gCo * (dCo / V));
document.getElementById('Si').value = Math.round(10000 * gSi * (dSi / V));
} else {
// Режим комплекса: используем gCmplx
const gCmplx = parseFloat(document.getElementById('total_micro_grams').value) || 0;
document.getElementById('Fe').value = Math.round(10000 * gCmplx * (dFe / V));
document.getElementById('Mn').value = Math.round(10000 * gCmplx * (dMn / V));
document.getElementById('B').value = Math.round(10000 * gCmplx * (dB / V));
document.getElementById('Zn').value = Math.round(10000 * gCmplx * (dZn / V));
document.getElementById('Cu').value = Math.round(10000 * gCmplx * (dCu / V));
document.getElementById('Mo').value = Math.round(10000 * gCmplx * (dMo / V));
document.getElementById('Co').value = Math.round(10000 * gCmplx * (dCo / V));
document.getElementById('Si').value = Math.round(10000 * gCmplx * (dSi / V));
}
// Вызываем genProfile для обновления строки профиля (как в легаси строка 1986)
updateProfileString();
}
// Работа с профилями
function saveProfile() {
getValues();
const profileData = JSON.stringify(currentProfile, null, 2);
document.getElementById('profile-data').value = profileData;
// Сохранение в localStorage
localStorage.setItem('hpg_profile', profileData);
alert('Профиль сохранен в браузере');
}
function loadProfile() {
const savedProfile = localStorage.getItem('hpg_profile');
if (savedProfile) {
try {
currentProfile = JSON.parse(savedProfile);
applyProfile();
alert('Профиль загружен');
} catch (e) {
alert('Ошибка загрузки профиля');
}
} else {
alert('Сохраненный профиль не найден');
}
}
// ========== РАБОТА С ПОЛНЫМ СОСТОЯНИЕМ (как в легаси SaveFile/LoadFirt/loadPrf) ==========
// Сохранение полного состояния (профиль + составы + настройки)
// Аналог SaveFile в легаси - сохраняет ВСЁ в localStorage['hpg_full_state']
function saveFullState() {
const fullState = {
// Профиль
profile: {
N: parseFloat(document.getElementById('N')?.value) || 0,
NO3: parseFloat(document.getElementById('NO3')?.value) || 0,
NH4: parseFloat(document.getElementById('NH4')?.value) || 0,
P: parseFloat(document.getElementById('P')?.value) || 0,
K: parseFloat(document.getElementById('K')?.value) || 0,
Ca: parseFloat(document.getElementById('Ca')?.value) || 0,
Mg: parseFloat(document.getElementById('Mg')?.value) || 0,
S: parseFloat(document.getElementById('S')?.value) || 0,
Cl: parseFloat(document.getElementById('Cl')?.value) || 0,
EC: parseFloat(document.getElementById('EC')?.value) || 0,
Fe: parseFloat(document.getElementById('Fe')?.value) || 0,
Mn: parseFloat(document.getElementById('Mn')?.value) || 0,
B: parseFloat(document.getElementById('B')?.value) || 0,
Zn: parseFloat(document.getElementById('Zn')?.value) || 0,
Cu: parseFloat(document.getElementById('Cu')?.value) || 0,
Mo: parseFloat(document.getElementById('Mo')?.value) || 0,
Co: parseFloat(document.getElementById('Co')?.value) || 0,
Si: parseFloat(document.getElementById('Si')?.value) || 0,
volume: parseFloat(document.getElementById('volume')?.value) || 10
},
// Составы солей (%)
saltCompositions: {
CaNO3: {
Ca: parseFloat(document.getElementById('salt_CaNO3_Ca')?.value) || 0,
NO3: parseFloat(document.getElementById('salt_CaNO3_NO3')?.value) || 0,
NH4: parseFloat(document.getElementById('salt_CaNO3_NH4')?.value) || 0
},
KNO3: {
K: parseFloat(document.getElementById('salt_KNO3_K')?.value) || 0,
NO3: parseFloat(document.getElementById('salt_KNO3_NO3')?.value) || 0
},
NH4NO3: {
NH4: parseFloat(document.getElementById('salt_NH4NO3_NH4')?.value) || 0,
NO3: parseFloat(document.getElementById('salt_NH4NO3_NO3')?.value) || 0
},
MgSO4: {
Mg: parseFloat(document.getElementById('salt_MgSO4_Mg')?.value) || 0,
S: parseFloat(document.getElementById('salt_MgSO4_S')?.value) || 0
},
KH2PO4: {
K: parseFloat(document.getElementById('salt_KH2PO4_K')?.value) || 0,
P: parseFloat(document.getElementById('salt_KH2PO4_P')?.value) || 0
},
K2SO4: {
K: parseFloat(document.getElementById('salt_K2SO4_K')?.value) || 0,
S: parseFloat(document.getElementById('salt_K2SO4_S')?.value) || 0
},
MgNO3: {
Mg: parseFloat(document.getElementById('salt_MgNO3_Mg')?.value) || 0,
NO3: parseFloat(document.getElementById('salt_MgNO3_NO3')?.value) || 0
},
CaCl2: {
Ca: parseFloat(document.getElementById('salt_CaCl2_Ca')?.value) || 0,
Cl: parseFloat(document.getElementById('salt_CaCl2_Cl')?.value) || 0
}
},
// Доли микроэлементов (%)
microCompositions: {
Fe: parseFloat(document.getElementById('micro_Fe_percent')?.value) || 0,
Mn: parseFloat(document.getElementById('micro_Mn_percent')?.value) || 0,
B: parseFloat(document.getElementById('micro_B_percent')?.value) || 0,
Zn: parseFloat(document.getElementById('micro_Zn_percent')?.value) || 0,
Cu: parseFloat(document.getElementById('micro_Cu_percent')?.value) || 0,
Mo: parseFloat(document.getElementById('micro_Mo_percent')?.value) || 0,
Co: parseFloat(document.getElementById('micro_Co_percent')?.value) || 0,
Si: parseFloat(document.getElementById('micro_Si_percent')?.value) || 0
},
// Концентрации (г/л)
concentrations: {
glCaNO3: parseFloat(document.getElementById('conc_glCaNO3')?.value) || 0,
glKNO3: parseFloat(document.getElementById('conc_glKNO3')?.value) || 0,
glNH4NO3: parseFloat(document.getElementById('conc_glNH4NO3')?.value) || 0,
glMgNO3: parseFloat(document.getElementById('conc_glMgNO3')?.value) || 0,
glMgSO4: parseFloat(document.getElementById('conc_glMgSO4')?.value) || 0,
glKH2PO4: parseFloat(document.getElementById('conc_glKH2PO4')?.value) || 0,
glK2SO4: parseFloat(document.getElementById('conc_glK2SO4')?.value) || 0,
glCaCl2: parseFloat(document.getElementById('conc_glCaCl2')?.value) || 0,
glCmplx: parseFloat(document.getElementById('conc_glCmplx')?.value) || 0,
glFe: parseFloat(document.getElementById('conc_glFe')?.value) || 0,
glMn: parseFloat(document.getElementById('conc_glMn')?.value) || 0,
glB: parseFloat(document.getElementById('conc_glB')?.value) || 0,
glZn: parseFloat(document.getElementById('conc_glZn')?.value) || 0,
glCu: parseFloat(document.getElementById('conc_glCu')?.value) || 0,
glMo: parseFloat(document.getElementById('conc_glMo')?.value) || 0,
glCo: parseFloat(document.getElementById('conc_glCo')?.value) || 0,
glSi: parseFloat(document.getElementById('conc_glSi')?.value) || 0
},
// Плотности (г/мл)
densities: {
gmlCaNO3: parseFloat(document.getElementById('conc_gmlCaNO3')?.value) || 0,
gmlKNO3: parseFloat(document.getElementById('conc_gmlKNO3')?.value) || 0,
gmlNH4NO3: parseFloat(document.getElementById('conc_gmlNH4NO3')?.value) || 0,
gmlMgNO3: parseFloat(document.getElementById('conc_gmlMgNO3')?.value) || 0,
gmlMgSO4: parseFloat(document.getElementById('conc_gmlMgSO4')?.value) || 0,
gmlKH2PO4: parseFloat(document.getElementById('conc_gmlKH2PO4')?.value) || 0,
gmlK2SO4: parseFloat(document.getElementById('conc_gmlK2SO4')?.value) || 0,
gmlCaCl2: parseFloat(document.getElementById('conc_gmlCaCl2')?.value) || 0,
gmlCmplx: parseFloat(document.getElementById('conc_gmlCmplx')?.value) || 0,
gmlFe: parseFloat(document.getElementById('conc_gmlFe')?.value) || 0,
gmlMn: parseFloat(document.getElementById('conc_gmlMn')?.value) || 0,
gmlB: parseFloat(document.getElementById('conc_gmlB')?.value) || 0,
gmlZn: parseFloat(document.getElementById('conc_gmlZn')?.value) || 0,
gmlCu: parseFloat(document.getElementById('conc_gmlCu')?.value) || 0,
gmlMo: parseFloat(document.getElementById('conc_gmlMo')?.value) || 0,
gmlCo: parseFloat(document.getElementById('conc_gmlCo')?.value) || 0,
gmlSi: parseFloat(document.getElementById('conc_gmlSi')?.value) || 0
},
// Цены
prices: {
CaNO3: parseFloat(document.getElementById('price_CaNO3')?.value) || 0,
KNO3: parseFloat(document.getElementById('price_KNO3')?.value) || 0,
NH4NO3: parseFloat(document.getElementById('price_NH4NO3')?.value) || 0,
MgNO3: parseFloat(document.getElementById('price_MgNO3')?.value) || 0,
MgSO4: parseFloat(document.getElementById('price_MgSO4')?.value) || 0,
KH2PO4: parseFloat(document.getElementById('price_KH2PO4')?.value) || 0,
K2SO4: parseFloat(document.getElementById('price_K2SO4')?.value) || 0,
CaCl2: parseFloat(document.getElementById('price_CaCl2')?.value) || 0,
Fe: parseFloat(document.getElementById('price_Fe')?.value) || 0,
Mn: parseFloat(document.getElementById('price_Mn')?.value) || 0,
B: parseFloat(document.getElementById('price_B')?.value) || 0,
Zn: parseFloat(document.getElementById('price_Zn')?.value) || 0,
Cu: parseFloat(document.getElementById('price_Cu')?.value) || 0,
Mo: parseFloat(document.getElementById('price_Mo')?.value) || 0,
Co: parseFloat(document.getElementById('price_Co')?.value) || 0,
Si: parseFloat(document.getElementById('price_Si')?.value) || 0,
Cmplx: parseFloat(document.getElementById('price_Cmplx')?.value) || 0
},
// Чекбоксы
flags: {
use_K2SO4: document.getElementById('use_K2SO4')?.checked || false,
use_MgNO3: document.getElementById('use_MgNO3')?.checked || false,
use_complex: document.getElementById('use_complex')?.checked || false
},
// Названия помп
pumpNames: {
mCaNO3: document.getElementById('mCaNO3')?.value || '',
mKNO3: document.getElementById('mKNO3')?.value || '',
mNH4NO3: document.getElementById('mNH4NO3')?.value || '',
mMgNO3: document.getElementById('mMgNO3')?.value || '',
mCaCl2: document.getElementById('mCaCl2')?.value || '',
mMgSO4: document.getElementById('mMgSO4')?.value || '',
mKH2PO4: document.getElementById('mKH2PO4')?.value || '',
mK2SO4: document.getElementById('mK2SO4')?.value || '',
mFe: document.getElementById('mFe')?.value || '',
mMn: document.getElementById('mMn')?.value || '',
mB: document.getElementById('mB')?.value || '',
mZn: document.getElementById('mZn')?.value || '',
mCu: document.getElementById('mCu')?.value || '',
mMo: document.getElementById('mMo')?.value || '',
mCo: document.getElementById('mCo')?.value || '',
mSi: document.getElementById('mSi')?.value || '',
mCmplx: document.getElementById('mCmplx')?.value || ''
},
// Настройки миксера
mixer: {
addrMixer: document.getElementById('addrMixer')?.value || 'mixer.local',
nmix: document.getElementById('nmix')?.value || '1'
},
// Объемы баков
tanks: {
tank_A: parseFloat(document.getElementById('tank_A')?.value) || 500,
tank_B: parseFloat(document.getElementById('tank_B')?.value) || 500
},
// Метаданные
meta: {
filename: document.getElementById('file_filename')?.value || 'default.hpg',
comment: document.getElementById('file_comment')?.value || 'По умолчанию'
}
};
// Сохраняем в localStorage
localStorage.setItem('hpg_full_state', JSON.stringify(fullState));
alert('Все настройки сохранены в браузере');
}
// Загрузка только составов и настроек (НЕ трогая профиль)
// Аналог LoadFirt в легаси - загружает составы солей, концентрации, цены, чекбоксы и т.д.
// НЕ трогает целевой профиль (N, P, K, Fe и т.д.)
function loadCompositions() {
const savedState = localStorage.getItem('hpg_full_state');
if (!savedState) {
alert('Сохраненные настройки не найдены. Сначала нажмите "Сохранить"');
return;
}
try {
const state = JSON.parse(savedState);
// Загружаем составы солей
if (state.saltCompositions) {
Object.keys(state.saltCompositions).forEach(salt => {
Object.keys(state.saltCompositions[salt]).forEach(element => {
const id = `salt_${salt}_${element}`;
const el = document.getElementById(id);
if (el) el.value = state.saltCompositions[salt][element];
});
});
}
// Загружаем доли микроэлементов
if (state.microCompositions) {
Object.keys(state.microCompositions).forEach(element => {
const el = document.getElementById(`micro_${element}_percent`);
if (el) el.value = state.microCompositions[element];
});
}
// Загружаем концентрации
if (state.concentrations) {
Object.keys(state.concentrations).forEach(key => {
const el = document.getElementById(`conc_${key}`);
if (el) el.value = state.concentrations[key];
});
}
// Загружаем плотности
if (state.densities) {
Object.keys(state.densities).forEach(key => {
const el = document.getElementById(`conc_${key}`);
if (el) el.value = state.densities[key];
});
}
// Загружаем цены
if (state.prices) {
Object.keys(state.prices).forEach(key => {
const el = document.getElementById(`price_${key}`);
if (el) el.value = state.prices[key];
});
}
// Загружаем чекбоксы
if (state.flags) {
if (state.flags.use_K2SO4 !== undefined) {
const el = document.getElementById('use_K2SO4');
if (el) {
el.checked = state.flags.use_K2SO4;
useK2SO4 = state.flags.use_K2SO4;
}
}
if (state.flags.use_MgNO3 !== undefined) {
const el = document.getElementById('use_MgNO3');
if (el) {
el.checked = state.flags.use_MgNO3;
useMgNO3 = state.flags.use_MgNO3;
}
}
if (state.flags.use_complex !== undefined) {
const el = document.getElementById('use_complex');
if (el) el.checked = state.flags.use_complex;
}
}
// Загружаем названия помп
if (state.pumpNames) {
Object.keys(state.pumpNames).forEach(key => {
const el = document.getElementById(key);
if (el) el.value = state.pumpNames[key];
});
}
// Загружаем настройки миксера
if (state.mixer) {
if (state.mixer.addrMixer) {
const el = document.getElementById('addrMixer');
if (el) el.value = state.mixer.addrMixer;
}
if (state.mixer.nmix) {
const el = document.getElementById('nmix');
if (el) el.value = state.mixer.nmix;
}
}
// Загружаем объемы баков
if (state.tanks) {
if (state.tanks.tank_A) {
const el = document.getElementById('tank_A');
if (el) el.value = state.tanks.tank_A;
}
if (state.tanks.tank_B) {
const el = document.getElementById('tank_B');
if (el) el.value = state.tanks.tank_B;
}
}
// Синхронизируем составы солей с глобальной переменной
syncSaltCompositions();
updateSaltLabels();
updateSaltNames();
updateMicroNames();
// Пересчитываем (как в легаси LoadFirt)
calcAll();
onMicroChange();
calcConcentrates();
updatePriceResults();
alert('Составы и настройки загружены');
} catch (e) {
console.error('Ошибка загрузки составов:', e);
alert('Ошибка загрузки составов');
}
}
// Загрузка только профиля (НЕ трогая составы и настройки)
// Аналог loadPrf в легаси - загружает только целевой профиль (N, P, K, Fe и т.д.)
// НЕ трогает составы солей, концентрации, цены и другие настройки
function loadProfileOnly() {
const savedState = localStorage.getItem('hpg_full_state');
if (!savedState) {
alert('Сохраненный профиль не найден. Сначала нажмите "Сохранить"');
return;
}
try {
const state = JSON.parse(savedState);
if (!state.profile) {
alert('Профиль не найден в сохраненных данных');
return;
}
// Загружаем только профиль
const profile = state.profile;
// Макропрофиль
if (profile.N !== undefined) document.getElementById('N').value = profile.N;
if (profile.NO3 !== undefined) document.getElementById('NO3').value = profile.NO3;
if (profile.NH4 !== undefined) document.getElementById('NH4').value = profile.NH4;
if (profile.P !== undefined) document.getElementById('P').value = profile.P;
if (profile.K !== undefined) document.getElementById('K').value = profile.K;
if (profile.Ca !== undefined) document.getElementById('Ca').value = profile.Ca;
if (profile.Mg !== undefined) document.getElementById('Mg').value = profile.Mg;
if (profile.S !== undefined) document.getElementById('S').value = profile.S;
if (profile.Cl !== undefined) document.getElementById('Cl').value = profile.Cl;
// Микропрофиль
if (profile.Fe !== undefined) document.getElementById('Fe').value = profile.Fe;
if (profile.Mn !== undefined) document.getElementById('Mn').value = profile.Mn;
if (profile.B !== undefined) document.getElementById('B').value = profile.B;
if (profile.Zn !== undefined) document.getElementById('Zn').value = profile.Zn;
if (profile.Cu !== undefined) document.getElementById('Cu').value = profile.Cu;
if (profile.Mo !== undefined) document.getElementById('Mo').value = profile.Mo;
if (profile.Co !== undefined) document.getElementById('Co').value = profile.Co;
if (profile.Si !== undefined) document.getElementById('Si').value = profile.Si;
// Объем
if (profile.volume !== undefined) document.getElementById('volume').value = profile.volume;
// Пересчитываем (как в легаси loadPrf)
calcAll();
onMicroChange();
calcConcentrates();
updatePriceResults();
alert('Профиль загружен');
} catch (e) {
console.error('Ошибка загрузки профиля:', e);
alert('Ошибка загрузки профиля');
}
}
function exportProfile() {
getValues();
updateProfileString();
const profileStr = document.getElementById('profile-string').textContent;
const blob = new Blob([profileStr], { type: 'text/plain' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = document.getElementById('filename').value || 'profile.hpg';
a.click();
URL.revokeObjectURL(url);
}
function importProfile() {
const input = document.createElement('input');
input.type = 'file';
input.accept = '.hpg,.txt';
input.onchange = (e) => {
const file = e.target.files[0];
const reader = new FileReader();
reader.onload = (event) => {
const content = event.target.result;
parseProfileString(content);
};
reader.readAsText(file);
};
input.click();
}
function parseProfileString(str) {
// Парсинг строки профиля: N=220 NO3=200 NH4=20 P=40 ...
const parts = str.trim().split(/\s+/);
parts.forEach(part => {
const [key, value] = part.split('=');
const numValue = parseFloat(value);
if (!isNaN(numValue)) {
if (key === 'N') currentProfile.N = numValue;
else if (key === 'NO3') currentProfile.NO3 = numValue;
else if (key === 'NH4') currentProfile.NH4 = numValue;
else if (key === 'P') currentProfile.P = numValue;
else if (key === 'K') currentProfile.K = numValue;
else if (key === 'Ca') currentProfile.Ca = numValue;
else if (key === 'Mg') currentProfile.Mg = numValue;
else if (key === 'S') currentProfile.S = numValue;
else if (key === 'Cl') currentProfile.Cl = numValue;
else if (key === 'Fe') currentProfile.Fe = numValue * 1000;
else if (key === 'Mn') currentProfile.Mn = numValue * 1000;
else if (key === 'B') currentProfile.B = numValue * 1000;
else if (key === 'Zn') currentProfile.Zn = numValue * 1000;
else if (key === 'Cu') currentProfile.Cu = numValue * 1000;
else if (key === 'Mo') currentProfile.Mo = numValue * 1000;
else if (key === 'Co') currentProfile.Co = numValue * 1000;
else if (key === 'Si') currentProfile.Si = numValue * 1000;
}
});
applyProfile();
alert('Профиль импортирован');
}
function applyProfile() {
document.getElementById('N').value = currentProfile.N.toFixed(3);
document.getElementById('P').value = currentProfile.P.toFixed(3);
document.getElementById('K').value = currentProfile.K.toFixed(3);
document.getElementById('Ca').value = currentProfile.Ca.toFixed(3);
document.getElementById('Mg').value = currentProfile.Mg.toFixed(3);
document.getElementById('S').value = currentProfile.S.toFixed(3);
document.getElementById('Cl').value = currentProfile.Cl.toFixed(2);
document.getElementById('EC').value = currentProfile.EC.toFixed(3);
document.getElementById('NO3').value = currentProfile.NO3.toFixed(2);
document.getElementById('NH4').value = currentProfile.NH4.toFixed(2);
document.getElementById('Fe').value = currentProfile.Fe || 2000;
document.getElementById('Mn').value = currentProfile.Mn || 550;
document.getElementById('B').value = currentProfile.B || 500;
document.getElementById('Zn').value = currentProfile.Zn || 330;
document.getElementById('Cu').value = currentProfile.Cu || 63;
document.getElementById('Mo').value = currentProfile.Mo || 63;
document.getElementById('Co').value = currentProfile.Co || 0;
document.getElementById('Si').value = currentProfile.Si || 0;
document.getElementById('volume').value = currentProfile.volume || 10;
onMacroChange();
onMicroChange();
}
// ===== ФУНКЦИИ ВКЛАДКИ "ФАЙЛ" =====
// Массив для хранения записей журнала
let journalEntries = [];
function fileOpen() {
const input = document.createElement('input');
input.type = 'file';
input.accept = '.hpg,.txt';
input.onchange = (e) => {
const file = e.target.files[0];
if (file) {
document.getElementById('file_filename').value = file.name;
const reader = new FileReader();
reader.onload = (event) => {
const content = event.target.result;
parseFullFile(content);
};
reader.readAsText(file);
}
};
input.click();
}
// Парсинг ТОЛЬКО составов из файла (аналог LoadFirt в легаси)
// НЕ трогает профиль (N, P, K, Fe и т.д.)
function parseCompositionsOnly(content) {
const lines = content.split('\n');
lines.forEach(line => {
line = line.trim();
if (!line || !line.includes('=') || line.startsWith('#') || line.startsWith('date=')) return;
const [key, value] = line.split('=');
if (!value) return;
const trimmedValue = value.trim();
const numValue = parseFloat(trimmedValue);
// Составы солей (проценты макроэлементов)
if (key === 'CaNO3_Ca' && !isNaN(numValue)) setSaltPercent('CaNO3', 'Ca', numValue);
else if (key === 'CaNO3_NO3' && !isNaN(numValue)) setSaltPercent('CaNO3', 'NO3', numValue);
else if (key === 'CaNO3_NH4' && !isNaN(numValue)) setSaltPercent('CaNO3', 'NH4', numValue);
else if (key === 'KNO3_K' && !isNaN(numValue)) setSaltPercent('KNO3', 'K', numValue);
else if (key === 'KNO3_NO3' && !isNaN(numValue)) setSaltPercent('KNO3', 'NO3', numValue);
else if (key === 'NH4NO3_NH4' && !isNaN(numValue)) setSaltPercent('NH4NO3', 'NH4', numValue);
else if (key === 'NH4NO3_NO3' && !isNaN(numValue)) setSaltPercent('NH4NO3', 'NO3', numValue);
else if (key === 'MgSO4_Mg' && !isNaN(numValue)) setSaltPercent('MgSO4', 'Mg', numValue);
else if (key === 'MgSO4_S' && !isNaN(numValue)) setSaltPercent('MgSO4', 'S', numValue);
else if (key === 'KH2PO4_K' && !isNaN(numValue)) setSaltPercent('KH2PO4', 'K', numValue);
else if (key === 'KH2PO4_P' && !isNaN(numValue)) setSaltPercent('KH2PO4', 'P', numValue);
else if (key === 'K2SO4_K' && !isNaN(numValue)) setSaltPercent('K2SO4', 'K', numValue);
else if (key === 'K2SO4_S' && !isNaN(numValue)) setSaltPercent('K2SO4', 'S', numValue);
else if (key === 'MgNO3_Mg' && !isNaN(numValue)) setSaltPercent('MgNO3', 'Mg', numValue);
else if (key === 'MgNO3_NO3' && !isNaN(numValue)) setSaltPercent('MgNO3', 'NO3', numValue);
else if (key === 'CaCl2_Ca' && !isNaN(numValue)) setSaltPercent('CaCl2', 'Ca', numValue);
else if (key === 'CaCl2_Cl' && !isNaN(numValue)) setSaltPercent('CaCl2', 'Cl', numValue);
// Доли микроэлементов в солях
else if (key === 'dFe' && !isNaN(numValue)) setInputValue('micro_Fe_percent', numValue, 4);
else if (key === 'dMn' && !isNaN(numValue)) setInputValue('micro_Mn_percent', numValue, 4);
else if (key === 'dB' && !isNaN(numValue)) setInputValue('micro_B_percent', numValue, 4);
else if (key === 'dZn' && !isNaN(numValue)) setInputValue('micro_Zn_percent', numValue, 4);
else if (key === 'dCu' && !isNaN(numValue)) setInputValue('micro_Cu_percent', numValue, 4);
else if (key === 'dMo' && !isNaN(numValue)) setInputValue('micro_Mo_percent', numValue, 4);
else if (key === 'dCo' && !isNaN(numValue)) setInputValue('micro_Co_percent', numValue, 4);
else if (key === 'dSi' && !isNaN(numValue)) setInputValue('micro_Si_percent', numValue, 4);
// Концентрации (г/л)
else if (key === 'glCaNO3' && !isNaN(numValue)) setInputValue('conc_glCaNO3', numValue, 1);
else if (key === 'glKNO3' && !isNaN(numValue)) setInputValue('conc_glKNO3', numValue, 1);
else if (key === 'glNH4NO3' && !isNaN(numValue)) setInputValue('conc_glNH4NO3', numValue, 1);
else if (key === 'glMgNO3' && !isNaN(numValue)) setInputValue('conc_glMgNO3', numValue, 1);
else if (key === 'glMgSO4' && !isNaN(numValue)) setInputValue('conc_glMgSO4', numValue, 1);
else if (key === 'glK2SO4' && !isNaN(numValue)) setInputValue('conc_glK2SO4', numValue, 1);
else if (key === 'glKH2PO4' && !isNaN(numValue)) setInputValue('conc_glKH2PO4', numValue, 1);
else if (key === 'glCaCl2' && !isNaN(numValue)) setInputValue('conc_glCaCl2', numValue, 1);
else if (key === 'glCmplx' && !isNaN(numValue)) setInputValue('conc_glCmplx', numValue, 2);
else if (key === 'glFe' && !isNaN(numValue)) setInputValue('conc_glFe', numValue, 2);
else if (key === 'glMn' && !isNaN(numValue)) setInputValue('conc_glMn', numValue, 2);
else if (key === 'glB' && !isNaN(numValue)) setInputValue('conc_glB', numValue, 2);
else if (key === 'glZn' && !isNaN(numValue)) setInputValue('conc_glZn', numValue, 2);
else if (key === 'glCu' && !isNaN(numValue)) setInputValue('conc_glCu', numValue, 2);
else if (key === 'glMo' && !isNaN(numValue)) setInputValue('conc_glMo', numValue, 2);
else if (key === 'glCo' && !isNaN(numValue)) setInputValue('conc_glCo', numValue, 2);
else if (key === 'glSi' && !isNaN(numValue)) setInputValue('conc_glSi', numValue, 2);
// Плотности (г/мл)
else if (key === 'gmlCaNO3' && !isNaN(numValue)) setInputValue('conc_gmlCaNO3', numValue, 4);
else if (key === 'gmlKNO3' && !isNaN(numValue)) setInputValue('conc_gmlKNO3', numValue, 4);
else if (key === 'gmlNH4NO3' && !isNaN(numValue)) setInputValue('conc_gmlNH4NO3', numValue, 4);
else if (key === 'gmlMgNO3' && !isNaN(numValue)) setInputValue('conc_gmlMgNO3', numValue, 4);
else if (key === 'gmlMgSO4' && !isNaN(numValue)) setInputValue('conc_gmlMgSO4', numValue, 4);
else if (key === 'gmlK2SO4' && !isNaN(numValue)) setInputValue('conc_gmlK2SO4', numValue, 4);
else if (key === 'gmlKH2PO4' && !isNaN(numValue)) setInputValue('conc_gmlKH2PO4', numValue, 4);
else if (key === 'gmlCaCl2' && !isNaN(numValue)) setInputValue('conc_gmlCaCl2', numValue, 4);
else if (key === 'gmlCmplx' && !isNaN(numValue)) setInputValue('conc_gmlCmplx', numValue, 3);
else if (key === 'gmlFe' && !isNaN(numValue)) setInputValue('conc_gmlFe', numValue, 3);
else if (key === 'gmlMn' && !isNaN(numValue)) setInputValue('conc_gmlMn', numValue, 3);
else if (key === 'gmlB' && !isNaN(numValue)) setInputValue('conc_gmlB', numValue, 3);
else if (key === 'gmlZn' && !isNaN(numValue)) setInputValue('conc_gmlZn', numValue, 3);
else if (key === 'gmlCu' && !isNaN(numValue)) setInputValue('conc_gmlCu', numValue, 3);
else if (key === 'gmlMo' && !isNaN(numValue)) setInputValue('conc_gmlMo', numValue, 3);
else if (key === 'gmlCo' && !isNaN(numValue)) setInputValue('conc_gmlCo', numValue, 3);
else if (key === 'gmlSi' && !isNaN(numValue)) setInputValue('conc_gmlSi', numValue, 3);
// Чекбоксы
else if (key === 'chK2SO4') {
const el = document.getElementById('use_K2SO4');
if (el) {
el.checked = (trimmedValue.toLowerCase() === 'true');
useK2SO4 = el.checked;
}
}
else if (key === 'chMgNO3') {
const el = document.getElementById('use_MgNO3');
if (el) {
el.checked = (trimmedValue.toLowerCase() === 'true');
useMgNO3 = el.checked;
}
}
else if (key === 'chkComplex') {
const el = document.getElementById('use_complex');
if (el) el.checked = (trimmedValue.toLowerCase() === 'true');
}
// Названия солей (m)
else if (key === 'mCaNO3') { const el = document.getElementById('mCaNO3'); if (el) el.value = trimmedValue; }
else if (key === 'mKNO3') { const el = document.getElementById('mKNO3'); if (el) el.value = trimmedValue; }
else if (key === 'mNH4NO3') { const el = document.getElementById('mNH4NO3'); if (el) el.value = trimmedValue; }
else if (key === 'mMgNO3') { const el = document.getElementById('mMgNO3'); if (el) el.value = trimmedValue; }
else if (key === 'mCaCl2') { const el = document.getElementById('mCaCl2'); if (el) el.value = trimmedValue; }
else if (key === 'mMgSO4') { const el = document.getElementById('mMgSO4'); if (el) el.value = trimmedValue; }
else if (key === 'mKH2PO4') { const el = document.getElementById('mKH2PO4'); if (el) el.value = trimmedValue; }
else if (key === 'mK2SO4') { const el = document.getElementById('mK2SO4'); if (el) el.value = trimmedValue; }
else if (key === 'mFe') { const el = document.getElementById('mFe'); if (el) el.value = trimmedValue; }
else if (key === 'mMn') { const el = document.getElementById('mMn'); if (el) el.value = trimmedValue; }
else if (key === 'mB') { const el = document.getElementById('mB'); if (el) el.value = trimmedValue; }
else if (key === 'mZn') { const el = document.getElementById('mZn'); if (el) el.value = trimmedValue; }
else if (key === 'mCu') { const el = document.getElementById('mCu'); if (el) el.value = trimmedValue; }
else if (key === 'mMo') { const el = document.getElementById('mMo'); if (el) el.value = trimmedValue; }
else if (key === 'mCo') { const el = document.getElementById('mCo'); if (el) el.value = trimmedValue; }
else if (key === 'mSi') { const el = document.getElementById('mSi'); if (el) el.value = trimmedValue; }
else if (key === 'mCmplx') { const el = document.getElementById('mCmplx'); if (el) el.value = trimmedValue; }
// Цены
else if (key === 'pCaNO3' && !isNaN(numValue)) setInputValue('price_CaNO3', numValue, 4);
else if (key === 'pKNO3' && !isNaN(numValue)) setInputValue('price_KNO3', numValue, 4);
else if (key === 'pNH4NO3' && !isNaN(numValue)) setInputValue('price_NH4NO3', numValue, 4);
else if (key === 'pMgNO3' && !isNaN(numValue)) setInputValue('price_MgNO3', numValue, 4);
else if (key === 'pMgSO4' && !isNaN(numValue)) setInputValue('price_MgSO4', numValue, 4);
else if (key === 'pKH2PO4' && !isNaN(numValue)) setInputValue('price_KH2PO4', numValue, 4);
else if (key === 'pK2SO4' && !isNaN(numValue)) setInputValue('price_K2SO4', numValue, 4);
else if (key === 'pCaCl2' && !isNaN(numValue)) setInputValue('price_CaCl2', numValue, 4);
else if (key === 'pFe' && !isNaN(numValue)) setInputValue('price_Fe', numValue, 4);
else if (key === 'pMn' && !isNaN(numValue)) setInputValue('price_Mn', numValue, 4);
else if (key === 'pB' && !isNaN(numValue)) setInputValue('price_B', numValue, 4);
else if (key === 'pZn' && !isNaN(numValue)) setInputValue('price_Zn', numValue, 4);
else if (key === 'pCu' && !isNaN(numValue)) setInputValue('price_Cu', numValue, 4);
else if (key === 'pMo' && !isNaN(numValue)) setInputValue('price_Mo', numValue, 4);
else if (key === 'pCo' && !isNaN(numValue)) setInputValue('price_Co', numValue, 4);
else if (key === 'pSi' && !isNaN(numValue)) setInputValue('price_Si', numValue, 4);
else if (key === 'pCmplx' && !isNaN(numValue)) setInputValue('price_Cmplx', numValue, 4);
// Настройки миксера
else if (key === 'addrMixer') { const el = document.getElementById('addrMixer'); if (el) el.value = trimmedValue; }
else if (key === 'nmix') { const el = document.getElementById('nmix'); if (el) el.value = trimmedValue; }
// Объемы баков
else if (key === 'tank_A' && !isNaN(numValue)) setInputValue('tank_A', numValue, 0);
else if (key === 'tank_B' && !isNaN(numValue)) setInputValue('tank_B', numValue, 0);
});
// Синхронизируем и пересчитываем (как в легаси LoadFirt)
syncSaltCompositions();
updateSaltLabels();
updateSaltNames();
updateMicroNames();
calcAll();
onMicroChange();
calcConcentrates();
updatePriceResults();
}
// Парсинг ТОЛЬКО профиля из файла (аналог loadPrf в легаси)
// НЕ трогает составы, концентрации, цены и другие настройки
function parseProfileOnly(content) {
const lines = content.split('\n');
lines.forEach(line => {
line = line.trim();
if (!line || !line.includes('=') || line.startsWith('#') || line.startsWith('date=')) return;
const [key, value] = line.split('=');
if (!value) return;
const numValue = parseFloat(value.trim());
// Макроэлементы
if (key === 'N' && !isNaN(numValue)) setInputValue('N', numValue, 3);
else if (key === 'NO3' && !isNaN(numValue)) setInputValue('NO3', numValue, 2);
else if (key === 'NH4' && !isNaN(numValue)) setInputValue('NH4', numValue, 2);
else if (key === 'P' && !isNaN(numValue)) setInputValue('P', numValue, 3);
else if (key === 'K' && !isNaN(numValue)) setInputValue('K', numValue, 3);
else if (key === 'Ca' && !isNaN(numValue)) setInputValue('Ca', numValue, 3);
else if (key === 'Mg' && !isNaN(numValue)) setInputValue('Mg', numValue, 3);
else if (key === 'S' && !isNaN(numValue)) setInputValue('S', numValue, 3);
else if (key === 'Cl' && !isNaN(numValue)) setInputValue('Cl', numValue, 1);
// Микроэлементы
else if (key === 'Fe' && !isNaN(numValue)) setInputValue('Fe', numValue, 3);
else if (key === 'Mn' && !isNaN(numValue)) setInputValue('Mn', numValue, 3);
else if (key === 'B' && !isNaN(numValue)) setInputValue('B', numValue, 3);
else if (key === 'Zn' && !isNaN(numValue)) setInputValue('Zn', numValue, 3);
else if (key === 'Cu' && !isNaN(numValue)) setInputValue('Cu', numValue, 3);
else if (key === 'Mo' && !isNaN(numValue)) setInputValue('Mo', numValue, 3);
else if (key === 'Co' && !isNaN(numValue)) setInputValue('Co', numValue, 3);
else if (key === 'Si' && !isNaN(numValue)) setInputValue('Si', numValue, 3);
// Объем
else if (key === 'V' && !isNaN(numValue)) setInputValue('volume', numValue, 1);
});
// Пересчитываем (как в легаси loadPrf)
calcAll();
onMicroChange();
calcConcentrates();
updatePriceResults();
}
// Парсинг полного файла .hpg включая журнал
function parseFullFile(content) {
const lines = content.split('\n');
let comment = '';
const tempJournal = [];
lines.forEach(line => {
line = line.trim();
if (!line) return;
// Парсинг комментария
if (line.startsWith('Comment=')) {
comment = line.substring(8);
document.getElementById('file_comment').value = comment;
}
// Парсинг журнала
else if (line.startsWith('date=')) {
// Формат: date=2024-10-10;Описание;N=220 NO3=200...
const parts = line.substring(5).split(';');
if (parts.length >= 3) {
const date = parts[0];
const desc = parts[1];
const profileStr = parts[2];
// Парсим профиль из строки
const profile = {};
const profileParts = profileStr.trim().split(/\s+/);
profileParts.forEach(part => {
const [key, value] = part.split('=');
const numValue = parseFloat(value);
if (!isNaN(numValue)) {
profile[key] = numValue;
}
});
// Добавляем в временный журнал
tempJournal.push({
date: date,
desc: desc,
memo: '', // В легаси формате нет отдельного memo
profile: profileStr,
fullProfile: profile
});
}
}
// Парсинг параметров профиля
else if (line.includes('=') && !line.startsWith('#')) {
const [key, value] = line.split('=');
if (!value) return; // Пропускаем если нет значения
const trimmedValue = value.trim();
const numValue = parseFloat(trimmedValue);
// Макроэлементы
if (key === 'N' && !isNaN(numValue)) currentProfile.N = numValue;
else if (key === 'NO3' && !isNaN(numValue)) currentProfile.NO3 = numValue;
else if (key === 'NH4' && !isNaN(numValue)) currentProfile.NH4 = numValue;
else if (key === 'P' && !isNaN(numValue)) currentProfile.P = numValue;
else if (key === 'K' && !isNaN(numValue)) currentProfile.K = numValue;
else if (key === 'Ca' && !isNaN(numValue)) currentProfile.Ca = numValue;
else if (key === 'Mg' && !isNaN(numValue)) currentProfile.Mg = numValue;
else if (key === 'S' && !isNaN(numValue)) currentProfile.S = numValue;
else if (key === 'Cl' && !isNaN(numValue)) currentProfile.Cl = numValue;
// Микроэлементы
else if (key === 'Fe' && !isNaN(numValue)) currentProfile.Fe = numValue;
else if (key === 'Mn' && !isNaN(numValue)) currentProfile.Mn = numValue;
else if (key === 'B' && !isNaN(numValue)) currentProfile.B = numValue;
else if (key === 'Zn' && !isNaN(numValue)) currentProfile.Zn = numValue;
else if (key === 'Cu' && !isNaN(numValue)) currentProfile.Cu = numValue;
else if (key === 'Mo' && !isNaN(numValue)) currentProfile.Mo = numValue;
else if (key === 'Co' && !isNaN(numValue)) currentProfile.Co = numValue;
else if (key === 'Si' && !isNaN(numValue)) currentProfile.Si = numValue;
else if (key === 'V' && !isNaN(numValue)) currentProfile.volume = numValue;
// Составы солей (проценты макроэлементов)
else if (key === 'CaNO3_Ca' && !isNaN(numValue)) setSaltPercent('CaNO3', 'Ca', numValue);
else if (key === 'CaNO3_NO3' && !isNaN(numValue)) setSaltPercent('CaNO3', 'NO3', numValue);
else if (key === 'CaNO3_NH4' && !isNaN(numValue)) setSaltPercent('CaNO3', 'NH4', numValue);
else if (key === 'KNO3_K' && !isNaN(numValue)) setSaltPercent('KNO3', 'K', numValue);
else if (key === 'KNO3_NO3' && !isNaN(numValue)) setSaltPercent('KNO3', 'NO3', numValue);
else if (key === 'NH4NO3_NH4' && !isNaN(numValue)) setSaltPercent('NH4NO3', 'NH4', numValue);
else if (key === 'NH4NO3_NO3' && !isNaN(numValue)) setSaltPercent('NH4NO3', 'NO3', numValue);
else if (key === 'MgSO4_Mg' && !isNaN(numValue)) setSaltPercent('MgSO4', 'Mg', numValue);
else if (key === 'MgSO4_S' && !isNaN(numValue)) setSaltPercent('MgSO4', 'S', numValue);
else if (key === 'KH2PO4_K' && !isNaN(numValue)) setSaltPercent('KH2PO4', 'K', numValue);
else if (key === 'KH2PO4_P' && !isNaN(numValue)) setSaltPercent('KH2PO4', 'P', numValue);
else if (key === 'K2SO4_K' && !isNaN(numValue)) setSaltPercent('K2SO4', 'K', numValue);
else if (key === 'K2SO4_S' && !isNaN(numValue)) setSaltPercent('K2SO4', 'S', numValue);
else if (key === 'MgNO3_Mg' && !isNaN(numValue)) setSaltPercent('MgNO3', 'Mg', numValue);
else if (key === 'MgNO3_NO3' && !isNaN(numValue)) setSaltPercent('MgNO3', 'NO3', numValue);
else if (key === 'CaCl2_Ca' && !isNaN(numValue)) setSaltPercent('CaCl2', 'Ca', numValue);
else if (key === 'CaCl2_Cl' && !isNaN(numValue)) setSaltPercent('CaCl2', 'Cl', numValue);
// Доли микроэлементов в солях
else if (key === 'dFe' && !isNaN(numValue)) setInputValue('micro_Fe_percent', numValue, 4);
else if (key === 'dMn' && !isNaN(numValue)) setInputValue('micro_Mn_percent', numValue, 4);
else if (key === 'dB' && !isNaN(numValue)) setInputValue('micro_B_percent', numValue, 4);
else if (key === 'dZn' && !isNaN(numValue)) setInputValue('micro_Zn_percent', numValue, 4);
else if (key === 'dCu' && !isNaN(numValue)) setInputValue('micro_Cu_percent', numValue, 4);
else if (key === 'dMo' && !isNaN(numValue)) setInputValue('micro_Mo_percent', numValue, 4);
else if (key === 'dCo' && !isNaN(numValue)) setInputValue('micro_Co_percent', numValue, 4);
else if (key === 'dSi' && !isNaN(numValue)) setInputValue('micro_Si_percent', numValue, 4);
// Концентрации растворов (gl)
else if (key === 'glCaNO3' && !isNaN(numValue)) setInputValue('conc_glCaNO3', numValue, 1);
else if (key === 'glKNO3' && !isNaN(numValue)) setInputValue('conc_glKNO3', numValue, 1);
else if (key === 'glNH4NO3' && !isNaN(numValue)) setInputValue('conc_glNH4NO3', numValue, 1);
else if (key === 'glMgNO3' && !isNaN(numValue)) setInputValue('conc_glMgNO3', numValue, 1);
else if (key === 'glMgSO4' && !isNaN(numValue)) setInputValue('conc_glMgSO4', numValue, 1);
else if (key === 'glK2SO4' && !isNaN(numValue)) setInputValue('conc_glK2SO4', numValue, 1);
else if (key === 'glKH2PO4' && !isNaN(numValue)) setInputValue('conc_glKH2PO4', numValue, 1);
else if (key === 'glCaCl2' && !isNaN(numValue)) setInputValue('conc_glCaCl2', numValue, 1);
else if (key === 'glCmplx' && !isNaN(numValue)) setInputValue('conc_glCmplx', numValue, 2);
else if (key === 'glFe' && !isNaN(numValue)) setInputValue('conc_glFe', numValue, 2);
else if (key === 'glMn' && !isNaN(numValue)) setInputValue('conc_glMn', numValue, 2);
else if (key === 'glB' && !isNaN(numValue)) setInputValue('conc_glB', numValue, 2);
else if (key === 'glZn' && !isNaN(numValue)) setInputValue('conc_glZn', numValue, 2);
else if (key === 'glCu' && !isNaN(numValue)) setInputValue('conc_glCu', numValue, 2);
else if (key === 'glMo' && !isNaN(numValue)) setInputValue('conc_glMo', numValue, 2);
else if (key === 'glCo' && !isNaN(numValue)) setInputValue('conc_glCo', numValue, 2);
else if (key === 'glSi' && !isNaN(numValue)) setInputValue('conc_glSi', numValue, 2);
// Плотности растворов (gml)
else if (key === 'gmlCaNO3' && !isNaN(numValue)) setInputValue('conc_gmlCaNO3', numValue, 4);
else if (key === 'gmlKNO3' && !isNaN(numValue)) setInputValue('conc_gmlKNO3', numValue, 4);
else if (key === 'gmlNH4NO3' && !isNaN(numValue)) setInputValue('conc_gmlNH4NO3', numValue, 4);
else if (key === 'gmlMgNO3' && !isNaN(numValue)) setInputValue('conc_gmlMgNO3', numValue, 4);
else if (key === 'gmlMgSO4' && !isNaN(numValue)) setInputValue('conc_gmlMgSO4', numValue, 4);
else if (key === 'gmlK2SO4' && !isNaN(numValue)) setInputValue('conc_gmlK2SO4', numValue, 4);
else if (key === 'gmlKH2PO4' && !isNaN(numValue)) setInputValue('conc_gmlKH2PO4', numValue, 4);
else if (key === 'gmlCaCl2' && !isNaN(numValue)) setInputValue('conc_gmlCaCl2', numValue, 4);
else if (key === 'gmlCmplx' && !isNaN(numValue)) setInputValue('conc_gmlCmplx', numValue, 3);
else if (key === 'gmlFe' && !isNaN(numValue)) setInputValue('conc_gmlFe', numValue, 3);
else if (key === 'gmlMn' && !isNaN(numValue)) setInputValue('conc_gmlMn', numValue, 3);
else if (key === 'gmlB' && !isNaN(numValue)) setInputValue('conc_gmlB', numValue, 3);
else if (key === 'gmlZn' && !isNaN(numValue)) setInputValue('conc_gmlZn', numValue, 3);
else if (key === 'gmlCu' && !isNaN(numValue)) setInputValue('conc_gmlCu', numValue, 3);
else if (key === 'gmlMo' && !isNaN(numValue)) setInputValue('conc_gmlMo', numValue, 3);
else if (key === 'gmlCo' && !isNaN(numValue)) setInputValue('conc_gmlCo', numValue, 3);
else if (key === 'gmlSi' && !isNaN(numValue)) setInputValue('conc_gmlSi', numValue, 3);
// Чекбоксы (как в легаси StrToBool - регистронезависимо)
else if (key === 'chkComplex') {
const checkbox = document.getElementById('use_complex');
if (checkbox) {
const lowerValue = trimmedValue.toLowerCase();
checkbox.checked = (lowerValue === 'true' || lowerValue === '1' || lowerValue === 'yes');
console.log('Загружен chkComplex:', trimmedValue, '→', checkbox.checked);
}
}
else if (key === 'chK2SO4') {
const checkbox = document.getElementById('use_K2SO4'); // Правильный ID с большой буквы
if (checkbox) {
const lowerValue = trimmedValue.toLowerCase();
checkbox.checked = (lowerValue === 'true' || lowerValue === '1' || lowerValue === 'yes');
useK2SO4 = checkbox.checked; // Обновляем глобальную переменную
}
}
else if (key === 'chMgNO3') {
const checkbox = document.getElementById('use_MgNO3'); // Правильный ID с большой буквы
if (checkbox) {
const lowerValue = trimmedValue.toLowerCase();
checkbox.checked = (lowerValue === 'true' || lowerValue === '1' || lowerValue === 'yes');
useMgNO3 = checkbox.checked; // Обновляем глобальную переменную
}
}
// Названия солей (m) - текстовые поля
else if (key === 'mCaNO3') { const el = document.getElementById('mCaNO3'); if (el) el.value = trimmedValue; }
else if (key === 'mKNO3') { const el = document.getElementById('mKNO3'); if (el) el.value = trimmedValue; }
else if (key === 'mNH4NO3') { const el = document.getElementById('mNH4NO3'); if (el) el.value = trimmedValue; }
else if (key === 'mMgNO3') { const el = document.getElementById('mMgNO3'); if (el) el.value = trimmedValue; }
else if (key === 'mMgSO4') { const el = document.getElementById('mMgSO4'); if (el) el.value = trimmedValue; }
else if (key === 'mKH2PO4') { const el = document.getElementById('mKH2PO4'); if (el) el.value = trimmedValue; }
else if (key === 'mK2SO4') { const el = document.getElementById('mK2SO4'); if (el) el.value = trimmedValue; }
else if (key === 'mCaCl2') { const el = document.getElementById('mCaCl2'); if (el) el.value = trimmedValue; }
else if (key === 'mCmplx') { const el = document.getElementById('mCmplx'); if (el) el.value = trimmedValue; }
else if (key === 'mFe') { const el = document.getElementById('mFe'); if (el) el.value = trimmedValue; }
else if (key === 'mMn') { const el = document.getElementById('mMn'); if (el) el.value = trimmedValue; }
else if (key === 'mB') { const el = document.getElementById('mB'); if (el) el.value = trimmedValue; }
else if (key === 'mZn') { const el = document.getElementById('mZn'); if (el) el.value = trimmedValue; }
else if (key === 'mCu') { const el = document.getElementById('mCu'); if (el) el.value = trimmedValue; }
else if (key === 'mMo') { const el = document.getElementById('mMo'); if (el) el.value = trimmedValue; }
else if (key === 'mCo') { const el = document.getElementById('mCo'); if (el) el.value = trimmedValue; }
else if (key === 'mSi') { const el = document.getElementById('mSi'); if (el) el.value = trimmedValue; }
else if (key === 'addrMixer') { const el = document.getElementById('addrMixer'); if (el) el.value = trimmedValue; }
else if (key === 'nmix' && !isNaN(numValue)) setInputValue('nmix', numValue, 0);
// Объемы баков
else if (key === 'tAml' && !isNaN(numValue)) setInputValue('tank_A', numValue, 0);
else if (key === 'tBml' && !isNaN(numValue)) setInputValue('tank_B', numValue, 0);
// Цены (cg)
else if (key === 'cgCaNO3' && !isNaN(numValue)) setInputValue('price_CaNO3', numValue, 4);
else if (key === 'cgKNO3' && !isNaN(numValue)) setInputValue('price_KNO3', numValue, 4);
else if (key === 'cgNH4NO3' && !isNaN(numValue)) setInputValue('price_NH4NO3', numValue, 4);
else if (key === 'cgMgNO3' && !isNaN(numValue)) setInputValue('price_MgNO3', numValue, 4);
else if (key === 'cgMgSO4' && !isNaN(numValue)) setInputValue('price_MgSO4', numValue, 4);
else if (key === 'cgK2SO4' && !isNaN(numValue)) setInputValue('price_K2SO4', numValue, 4);
else if (key === 'cgKH2PO4' && !isNaN(numValue)) setInputValue('price_KH2PO4', numValue, 4);
else if (key === 'cgCaCl2' && !isNaN(numValue)) setInputValue('price_CaCl2', numValue, 4);
else if (key === 'cgCmplx' && !isNaN(numValue)) setInputValue('price_Cmplx', numValue, 4);
else if (key === 'cgFe' && !isNaN(numValue)) setInputValue('price_Fe', numValue, 4);
else if (key === 'cgMn' && !isNaN(numValue)) setInputValue('price_Mn', numValue, 4);
else if (key === 'cgB' && !isNaN(numValue)) setInputValue('price_B', numValue, 4);
else if (key === 'cgZn' && !isNaN(numValue)) setInputValue('price_Zn', numValue, 4);
else if (key === 'cgCu' && !isNaN(numValue)) setInputValue('price_Cu', numValue, 4);
else if (key === 'cgMo' && !isNaN(numValue)) setInputValue('price_Mo', numValue, 4);
else if (key === 'cgCo' && !isNaN(numValue)) setInputValue('price_Co', numValue, 4);
else if (key === 'cgSi' && !isNaN(numValue)) setInputValue('price_Si', numValue, 4);
}
});
// Обновляем журнал
journalEntries = tempJournal;
updateJournalList();
// Применяем профиль
applyProfile();
alert('Файл загружен. Профиль и журнал (' + tempJournal.length + ' записей) импортированы.');
}
// Вспомогательные функции для сохранения файла (дополнительные)
function getMicroPercent(element) {
const id = 'micro_' + element + '_percent';
const input = document.getElementById(id);
return input ? (parseFloat(input.value) || 0) : 0;
}
function getConcentration(salt) {
const id = 'conc_gl' + salt;
const input = document.getElementById(id);
return input ? (parseFloat(input.value) || 0) : 0;
}
function getDensity(salt) {
const id = 'conc_gml' + salt;
const input = document.getElementById(id);
return input ? (parseFloat(input.value) || 0) : 0;
}
function getSaltName(salt) {
const id = 'm' + salt;
const input = document.getElementById(id);
return input ? input.value : '';
}
function getTankVolume(tank) {
const id = 'tank_' + tank;
const input = document.getElementById(id);
return input ? (parseFloat(input.value) || 0) : 0;
}
function getPrice(salt) {
const id = 'price_' + salt;
const input = document.getElementById(id);
return input ? (parseFloat(input.value) || 0) : 0;
}
function fileSaveAs() {
getValues();
const comment = document.getElementById('file_comment').value;
// Формируем содержимое файла в формате .hpg (как в легаси SaveFile)
let content = '';
content += 'version=Hydroponic Profile Generator HTML5 https://github.com/siv237/HPG\n';
content += 'Comment=' + (comment || '') + '\n';
// Макроэлементы профиля
content += 'N=' + currentProfile.N + '\n';
content += 'NH4=' + currentProfile.NH4 + '\n';
content += 'NO3=' + currentProfile.NO3 + '\n';
content += 'P=' + currentProfile.P + '\n';
content += 'K=' + currentProfile.K + '\n';
content += 'Ca=' + currentProfile.Ca + '\n';
content += 'Mg=' + currentProfile.Mg + '\n';
content += 'S=' + currentProfile.S + '\n';
content += 'Cl=' + currentProfile.Cl + '\n';
// Составы солей - проценты макроэлементов (Macro %)
content += 'CaNO3_Ca=' + getSaltPercent('CaNO3', 'Ca') + '\n';
content += 'CaNO3_NO3=' + getSaltPercent('CaNO3', 'NO3') + '\n';
content += 'CaNO3_NH4=' + getSaltPercent('CaNO3', 'NH4') + '\n';
content += 'KNO3_K=' + getSaltPercent('KNO3', 'K') + '\n';
content += 'KNO3_NO3=' + getSaltPercent('KNO3', 'NO3') + '\n';
content += 'NH4NO3_NH4=' + getSaltPercent('NH4NO3', 'NH4') + '\n';
content += 'NH4NO3_NO3=' + getSaltPercent('NH4NO3', 'NO3') + '\n';
content += 'MgSO4_Mg=' + getSaltPercent('MgSO4', 'Mg') + '\n';
content += 'MgSO4_S=' + getSaltPercent('MgSO4', 'S') + '\n';
content += 'KH2PO4_K=' + getSaltPercent('KH2PO4', 'K') + '\n';
content += 'KH2PO4_P=' + getSaltPercent('KH2PO4', 'P') + '\n';
content += 'K2SO4_K=' + getSaltPercent('K2SO4', 'K') + '\n';
content += 'K2SO4_S=' + getSaltPercent('K2SO4', 'S') + '\n';
content += 'MgNO3_Mg=' + getSaltPercent('MgNO3', 'Mg') + '\n';
content += 'MgNO3_NO3=' + getSaltPercent('MgNO3', 'NO3') + '\n';
content += 'CaCl2_Ca=' + getSaltPercent('CaCl2', 'Ca') + '\n';
content += 'CaCl2_Cl=' + getSaltPercent('CaCl2', 'Cl') + '\n';
// Микроэлементы профиля
content += 'Fe=' + currentProfile.Fe + '\n';
content += 'Mn=' + currentProfile.Mn + '\n';
content += 'B=' + currentProfile.B + '\n';
content += 'Zn=' + currentProfile.Zn + '\n';
content += 'Cu=' + currentProfile.Cu + '\n';
content += 'Mo=' + currentProfile.Mo + '\n';
content += 'Co=' + currentProfile.Co + '\n';
content += 'Si=' + currentProfile.Si + '\n';
// Доли микроэлементов в солях (Micro %)
content += 'dFe=' + getMicroPercent('Fe') + '\n';
content += 'dMn=' + getMicroPercent('Mn') + '\n';
content += 'dB=' + getMicroPercent('B') + '\n';
content += 'dZn=' + getMicroPercent('Zn') + '\n';
content += 'dCu=' + getMicroPercent('Cu') + '\n';
content += 'dMo=' + getMicroPercent('Mo') + '\n';
content += 'dCo=' + getMicroPercent('Co') + '\n';
content += 'dSi=' + getMicroPercent('Si') + '\n';
// Концентрации растворов (gl)
content += 'glCaNO3=' + getConcentration('CaNO3') + '\n';
content += 'glKNO3=' + getConcentration('KNO3') + '\n';
content += 'glNH4NO3=' + getConcentration('NH4NO3') + '\n';
content += 'glMgNO3=' + getConcentration('MgNO3') + '\n';
content += 'glMgSO4=' + getConcentration('MgSO4') + '\n';
content += 'glK2SO4=' + getConcentration('K2SO4') + '\n';
content += 'glKH2PO4=' + getConcentration('KH2PO4') + '\n';
content += 'glCaCl2=' + getConcentration('CaCl2') + '\n';
content += 'glCmplx=' + getConcentration('Cmplx') + '\n';
content += 'glFe=' + getConcentration('Fe') + '\n';
content += 'glMn=' + getConcentration('Mn') + '\n';
content += 'glB=' + getConcentration('B') + '\n';
content += 'glZn=' + getConcentration('Zn') + '\n';
content += 'glCu=' + getConcentration('Cu') + '\n';
content += 'glMo=' + getConcentration('Mo') + '\n';
content += 'glCo=' + getConcentration('Co') + '\n';
content += 'glSi=' + getConcentration('Si') + '\n';
// Плотности растворов (gml)
content += 'gmlCaNO3=' + getDensity('CaNO3') + '\n';
content += 'gmlKNO3=' + getDensity('KNO3') + '\n';
content += 'gmlNH4NO3=' + getDensity('NH4NO3') + '\n';
content += 'gmlMgNO3=' + getDensity('MgNO3') + '\n';
content += 'gmlMgSO4=' + getDensity('MgSO4') + '\n';
content += 'gmlK2SO4=' + getDensity('K2SO4') + '\n';
content += 'gmlKH2PO4=' + getDensity('KH2PO4') + '\n';
content += 'gmlCaCl2=' + getDensity('CaCl2') + '\n';
content += 'gmlCmplx=' + getDensity('Cmplx') + '\n';
content += 'gmlFe=' + getDensity('Fe') + '\n';
content += 'gmlMn=' + getDensity('Mn') + '\n';
content += 'gmlB=' + getDensity('B') + '\n';
content += 'gmlZn=' + getDensity('Zn') + '\n';
content += 'gmlCu=' + getDensity('Cu') + '\n';
content += 'gmlMo=' + getDensity('Mo') + '\n';
content += 'gmlCo=' + getDensity('Co') + '\n';
content += 'gmlSi=' + getDensity('Si') + '\n';
// Чекбоксы (как в легаси - True/False с большой буквы)
content += 'chkComplex=' + (document.getElementById('use_complex')?.checked ? 'True' : 'False') + '\n';
content += 'chK2SO4=' + (document.getElementById('use_K2SO4')?.checked ? 'True' : 'False') + '\n';
content += 'chMgNO3=' + (document.getElementById('use_MgNO3')?.checked ? 'True' : 'False') + '\n';
// Объем
content += 'V=' + (currentProfile.volume || 10) + '\n';
// Названия солей (m)
content += 'mCaNO3=' + getSaltName('CaNO3') + '\n';
content += 'mKNO3=' + getSaltName('KNO3') + '\n';
content += 'mNH4NO3=' + getSaltName('NH4NO3') + '\n';
content += 'mMgNO3=' + getSaltName('MgNO3') + '\n';
content += 'mMgSO4=' + getSaltName('MgSO4') + '\n';
content += 'mKH2PO4=' + getSaltName('KH2PO4') + '\n';
content += 'mK2SO4=' + getSaltName('K2SO4') + '\n';
content += 'mCaCl2=' + getSaltName('CaCl2') + '\n';
content += 'mCmplx=' + getSaltName('Cmplx') + '\n';
content += 'mFe=' + getSaltName('Fe') + '\n';
content += 'mMn=' + getSaltName('Mn') + '\n';
content += 'mB=' + getSaltName('B') + '\n';
content += 'mZn=' + getSaltName('Zn') + '\n';
content += 'mCu=' + getSaltName('Cu') + '\n';
content += 'mMo=' + getSaltName('Mo') + '\n';
content += 'mCo=' + getSaltName('Co') + '\n';
content += 'mSi=' + getSaltName('Si') + '\n';
content += 'addrMixer=' + (document.getElementById('addrMixer')?.value || '') + '\n';
content += 'nmix=' + (document.getElementById('nmix')?.value || '') + '\n';
// Объемы баков
content += 'tAml=' + getTankVolume('A') + '\n';
content += 'tBml=' + getTankVolume('B') + '\n';
// Цены (cg)
content += 'cgCaNO3=' + getPrice('CaNO3') + '\n';
content += 'cgKNO3=' + getPrice('KNO3') + '\n';
content += 'cgNH4NO3=' + getPrice('NH4NO3') + '\n';
content += 'cgMgNO3=' + getPrice('MgNO3') + '\n';
content += 'cgMgSO4=' + getPrice('MgSO4') + '\n';
content += 'cgK2SO4=' + getPrice('K2SO4') + '\n';
content += 'cgKH2PO4=' + getPrice('KH2PO4') + '\n';
content += 'cgCaCl2=' + getPrice('CaCl2') + '\n';
content += 'cgCmplx=' + getPrice('Cmplx') + '\n';
content += 'cgFe=' + getPrice('Fe') + '\n';
content += 'cgMn=' + getPrice('Mn') + '\n';
content += 'cgB=' + getPrice('B') + '\n';
content += 'cgZn=' + getPrice('Zn') + '\n';
content += 'cgCu=' + getPrice('Cu') + '\n';
content += 'cgMo=' + getPrice('Mo') + '\n';
content += 'cgCo=' + getPrice('Co') + '\n';
content += 'cgSi=' + getPrice('Si') + '\n';
// Журнал (в конце файла)
journalEntries.forEach(entry => {
// Формат: date=2024-10-10;Описание;N=220 NO3=200...
content += 'date=' + entry.date + ';' + entry.desc + ';' + entry.profile + '\n';
});
const blob = new Blob([content], { type: 'text/plain' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = document.getElementById('file_filename').value || 'profile.hpg';
a.click();
URL.revokeObjectURL(url);
}
// Подгрузить удобрения из файла (аналог Button4Click в легаси)
// Загружает ТОЛЬКО составы, концентрации, цены и настройки, НЕ трогая профиль
function fileLoadFertilizers() {
const input = document.createElement('input');
input.type = 'file';
input.accept = '.hpg,.txt';
input.onchange = (e) => {
const file = e.target.files[0];
if (file) {
const reader = new FileReader();
reader.onload = (event) => {
try {
const content = event.target.result;
parseCompositionsOnly(content);
alert(`Составы и настройки загружены из файла: ${file.name}`);
} catch (err) {
console.error('Ошибка загрузки удобрений:', err);
alert('Ошибка загрузки файла удобрений');
}
};
reader.readAsText(file);
}
};
input.click();
}
// Подгрузить профиль из файла (аналог Button5Click в легаси)
// Загружает ТОЛЬКО профиль (N, P, K, Fe и т.д.), НЕ трогая составы и настройки
function fileLoadProfile() {
const input = document.createElement('input');
input.type = 'file';
input.accept = '.hpg,.txt';
input.onchange = (e) => {
const file = e.target.files[0];
if (file) {
const reader = new FileReader();
reader.onload = (event) => {
try {
const content = event.target.result;
parseProfileOnly(content);
alert(`Профиль загружен из файла: ${file.name}`);
} catch (err) {
console.error('Ошибка загрузки профиля:', err);
alert('Ошибка загрузки файла профиля');
}
};
reader.readAsText(file);
}
};
input.click();
}
function showMacroHelp() {
window.open('https://github.com/siv237/HPG/wiki/Macro', '_blank');
}
function showMicroHelp() {
window.open('https://github.com/siv237/HPG/wiki/Micro', '_blank');
}
function showCalcHelp() {
window.open('https://github.com/siv237/HPG/wiki/raschet', '_blank');
}
function showPreparationHelp() {
window.open('https://github.com/siv237/HPG/wiki/izgotovlenie', '_blank');
}
function showCorrectorHelp() {
window.open('https://github.com/siv237/HPG/wiki/correction', '_blank');
}
function showFileHelp() {
window.open('https://github.com/siv237/HPG/wiki/file', '_blank');
}
function journalAdd() {
const memo = document.getElementById('file_journal_memo').value;
const date = document.getElementById('file_journal_date').value;
if (!memo || !date) {
alert('Заполните описание и дату');
return;
}
getValues();
updateProfileString();
const profileStr = document.getElementById('profile-string').value;
const entry = {
desc: memo, // Описание берётся из memo
date: date,
memo: memo,
profile: profileStr,
fullProfile: {...currentProfile}
};
journalEntries.push(entry);
updateJournalList();
// Очистка полей
document.getElementById('file_journal_date').value = '';
document.getElementById('file_journal_memo').value = '';
}
function journalUpdate() {
const select = document.getElementById('file_journal_list');
const selectedIndex = select.selectedIndex;
if (selectedIndex < 0) {
alert('Выберите запись для обновления');
return;
}
const memo = document.getElementById('file_journal_memo').value;
const date = document.getElementById('file_journal_date').value;
if (!memo || !date) {
alert('Заполните описание и дату');
return;
}
getValues();
updateProfileString();
const profileStr = document.getElementById('profile-string').value;
journalEntries[selectedIndex] = {
desc: memo, // Описание берётся из memo
date: date,
memo: memo,
profile: profileStr,
fullProfile: {...currentProfile}
};
updateJournalList();
}
function journalDelete() {
const select = document.getElementById('file_journal_list');
const selectedIndex = select.selectedIndex;
if (selectedIndex < 0) {
alert('Выберите запись для удаления');
return;
}
if (confirm('Удалить выбранную запись?')) {
journalEntries.splice(selectedIndex, 1);
updateJournalList();
document.getElementById('file_journal_date').value = '';
document.getElementById('file_journal_memo').value = '';
clearFileProfileDetails();
}
}
function updateJournalList() {
const select = document.getElementById('file_journal_list');
select.innerHTML = '';
journalEntries.forEach((entry, index) => {
const option = document.createElement('option');
option.value = index;
option.textContent = `${entry.date} - ${entry.desc}`;
select.appendChild(option);
});
// Если журнал пустой, очищаем поля
if (journalEntries.length === 0) {
document.getElementById('file_journal_date').value = '';
document.getElementById('file_journal_memo').value = '';
document.getElementById('file_profile_info').value = ''; // Используем .value для input
clearFileProfileDetails();
}
// Сохранение в localStorage
localStorage.setItem('hpg_journal', JSON.stringify(journalEntries));
}
function acceptProfileFromJournal() {
const select = document.getElementById('file_journal_list');
const selectedIndex = select.selectedIndex;
if (selectedIndex < 0) {
alert('Выберите запись из журнала');
return;
}
// Копируем строку профиля из file_profile_info в profile-string (как в легаси: profile.Caption := pr2.Caption)
const profileFromJournal = document.getElementById('file_profile_info').value;
document.getElementById('profile-string').value = profileFromJournal;
// Вызываем парсер профиля (как в легаси: LoadProfile)
parseProfile();
}
function clearFileProfileDetails() {
document.getElementById('rN').textContent = '';
document.getElementById('rNO3').textContent = '';
document.getElementById('rNH4').textContent = '';
document.getElementById('rP').textContent = '';
document.getElementById('rK').textContent = '';
document.getElementById('rCa').textContent = '';
document.getElementById('rMg').textContent = '';
document.getElementById('rS').textContent = '';
document.getElementById('rCl').textContent = '';
document.getElementById('rFe').textContent = '';
document.getElementById('rMn').textContent = '';
document.getElementById('rB').textContent = '';
document.getElementById('rZn').textContent = '';
document.getElementById('rCu').textContent = '';
document.getElementById('rMo').textContent = '';
document.getElementById('rCo').textContent = '';
document.getElementById('rSi').textContent = '';
}
// Обработчик выбора записи в журнале
document.addEventListener('DOMContentLoaded', function() {
const select = document.getElementById('file_journal_list');
if (select) {
select.addEventListener('change', function() {
const selectedIndex = this.selectedIndex;
if (selectedIndex >= 0 && selectedIndex < journalEntries.length) {
const entry = journalEntries[selectedIndex];
document.getElementById('file_journal_date').value = entry.date;
document.getElementById('file_journal_memo').value = entry.memo || entry.desc; // Совместимость со старым форматом
document.getElementById('file_profile_info').value = entry.profile;
// Получаем текущие значения профиля
getValues();
// Отображаем детали профиля с процентами отклонения (как в легаси)
const p = entry.fullProfile;
// Функция расчета процента отклонения
const calcPercent = (current, journal) => {
if (journal > 0) {
return Math.round((current - journal) / journal * 100);
}
return 0;
};
// Макроэлементы
const vN = currentProfile.N || 0;
const vNO3 = currentProfile.NO3 || 0;
const vNH4 = currentProfile.NH4 || 0;
const vP = currentProfile.P || 0;
const vK = currentProfile.K || 0;
const vCa = currentProfile.Ca || 0;
const vMg = currentProfile.Mg || 0;
const vS = currentProfile.S || 0;
const vCl = currentProfile.Cl || 0;
// Микроэлементы
const vFe = currentProfile.Fe || 0;
const vMn = currentProfile.Mn || 0;
const vB = currentProfile.B || 0;
const vZn = currentProfile.Zn || 0;
const vCu = currentProfile.Cu || 0;
const vMo = currentProfile.Mo || 0;
const vCo = currentProfile.Co || 0;
const vSi = currentProfile.Si || 0;
// Отображаем с процентами
document.getElementById('rN').textContent = p.N > 0 ? `N:(${calcPercent(vN, p.N)}%)` : '';
document.getElementById('rNO3').textContent = p.NO3 > 0 ? `NO3:(${calcPercent(vNO3, p.NO3)}%)` : '';
document.getElementById('rNH4').textContent = p.NH4 > 0 ? `NH4:(${calcPercent(vNH4, p.NH4)}%)` : '';
document.getElementById('rP').textContent = p.P > 0 ? `P:(${calcPercent(vP, p.P)}%)` : '';
document.getElementById('rK').textContent = p.K > 0 ? `K:(${calcPercent(vK, p.K)}%)` : '';
document.getElementById('rCa').textContent = p.Ca > 0 ? `Ca:(${calcPercent(vCa, p.Ca)}%)` : '';
document.getElementById('rMg').textContent = p.Mg > 0 ? `Mg:(${calcPercent(vMg, p.Mg)}%)` : '';
document.getElementById('rS').textContent = p.S > 0 ? `S:(${calcPercent(vS, p.S)}%)` : '';
document.getElementById('rCl').textContent = p.Cl > 0 ? `Cl:(${calcPercent(vCl, p.Cl)}%)` : '-';
// Для микроэлементов делим текущее значение на 1000 (мкг/л -> мг/л)
document.getElementById('rFe').textContent = p.Fe > 0 ? `Fe:(${calcPercent(vFe/1000, p.Fe)}%)` : 'Fe: -';
document.getElementById('rMn').textContent = p.Mn > 0 ? `Mn:(${calcPercent(vMn/1000, p.Mn)}%)` : 'Mn: -';
document.getElementById('rB').textContent = p.B > 0 ? `B:(${calcPercent(vB/1000, p.B)}%)` : 'B: -';
document.getElementById('rZn').textContent = p.Zn > 0 ? `Zn:(${calcPercent(vZn/1000, p.Zn)}%)` : 'Zn: -';
document.getElementById('rCu').textContent = p.Cu > 0 ? `Cu:(${calcPercent(vCu/1000, p.Cu)}%)` : 'Cu: -';
document.getElementById('rMo').textContent = p.Mo > 0 ? `Mo:(${calcPercent(vMo/1000, p.Mo)}%)` : 'Mo: -';
document.getElementById('rCo').textContent = p.Co > 0 ? `Co:(${calcPercent(vCo/1000, p.Co)}%)` : 'Co: -';
document.getElementById('rSi').textContent = p.Si > 0 ? `Si:(${calcPercent(vSi/1000, p.Si)}%)` : 'Si: -';
}
});
}
// Загрузка журнала из localStorage
const savedJournal = localStorage.getItem('hpg_journal');
if (savedJournal) {
try {
journalEntries = JSON.parse(savedJournal);
updateJournalList();
} catch (e) {
console.error('Ошибка загрузки журнала:', e);
}
}
});
// Корректор
function fillColumn(columnName) {
getValues();
const prefix = columnName === 'initial' ? 'init' :
columnName === 'current' ? 'curr' :
columnName === 'correcting' ? 'corr' : 'final';
document.getElementById(`corr_${prefix}_N`).value = currentProfile.N.toFixed(3);
document.getElementById(`corr_${prefix}_NO3`).value = currentProfile.NO3.toFixed(2);
document.getElementById(`corr_${prefix}_NH4`).value = currentProfile.NH4.toFixed(2);
document.getElementById(`corr_${prefix}_P`).value = currentProfile.P.toFixed(3);
document.getElementById(`corr_${prefix}_K`).value = currentProfile.K.toFixed(3);
document.getElementById(`corr_${prefix}_Ca`).value = currentProfile.Ca.toFixed(3);
document.getElementById(`corr_${prefix}_Mg`).value = currentProfile.Mg.toFixed(3);
document.getElementById(`corr_${prefix}_S`).value = currentProfile.S.toFixed(3);
document.getElementById(`corr_${prefix}_Cl`).value = currentProfile.Cl.toFixed(1);
document.getElementById(`corr_${prefix}_EC`).value = currentProfile.EC.toFixed(3);
}
// ===== ФУНКЦИИ КОРРЕКТОРА =====
// Текущая активная колонка для корректора
let activeCorrectColumn = null;
// Обработчик изменения полей корректора
function onCorrectorChange(event) {
// Запускаем полный пересчет корректора
korrection();
}
// Главная функция расчета корректора (аналог korrection из легаси строка 2580)
function korrection() {
// Исходный (0) - колонка init
const V_0 = parseFloat(document.getElementById('corr_init_vol').value) || 10;
const V_2 = parseFloat(document.getElementById('corr_final_vol').value) || 30;
// Текущий (1) - колонка curr
let V_1 = 10; // текущий всегда 10 литров (условно)
// Автоматически устанавливаем V_2 если он меньше V_1
if (V_0 >= V_2) {
document.getElementById('corr_final_vol').value = (V_0 + 10).toFixed(1);
}
// Пересчет S и EC для исходного
calculateCorrectorS('init');
calculateCorrectorEC('init');
const N_0 = parseFloat(document.getElementById('corr_init_NO3').value) +
parseFloat(document.getElementById('corr_init_NH4').value);
document.getElementById('corr_init_N').value = N_0.toFixed(3);
// Текущий = исходный × (EC_1 / EC_0) - учет испарения воды
const EC_0 = parseFloat(document.getElementById('corr_init_EC').value) || 1;
const EC_1 = parseFloat(document.getElementById('corr_curr_EC').value) || EC_0;
const kEC = EC_0 !== 0 ? EC_1 / EC_0 : 1;
const elements = ['NO3', 'NH4', 'P', 'K', 'Ca', 'Mg', 'Cl'];
elements.forEach(elem => {
const val_0 = parseFloat(document.getElementById(`corr_init_${elem}`).value) || 0;
document.getElementById(`corr_curr_${elem}`).value = (val_0 * kEC).toFixed(elem === 'NO3' || elem === 'NH4' ? 2 : 3);
});
// S и N для текущего
calculateCorrectorS('curr');
const N_1 = parseFloat(document.getElementById('corr_curr_NO3').value) +
parseFloat(document.getElementById('corr_curr_NH4').value);
document.getElementById('corr_curr_N').value = N_1.toFixed(3);
document.getElementById('corr_curr_EC').value = EC_1.toFixed(3);
// Объем корректирующего
const V_k = V_2 - V_1;
document.getElementById('corr_corr_vol').value = V_k.toFixed(1);
// Корректирующий рассчитывается по формуле баланса масс:
// C_k = (C_2 * V_2 - C_1 * V_1) / V_k
elements.forEach(elem => {
const C_1 = parseFloat(document.getElementById(`corr_curr_${elem}`).value) || 0;
const C_2 = parseFloat(document.getElementById(`corr_final_${elem}`).value) || 0;
const C_k = V_k !== 0 ? (C_2 * V_2 - C_1 * V_1) / V_k : 0;
document.getElementById(`corr_corr_${elem}`).value = C_k.toFixed(elem === 'NO3' || elem === 'NH4' ? 2 : 3);
});
// S, EC и N для корректирующего
calculateCorrectorS('corr');
calculateCorrectorEC('corr');
const N_k = parseFloat(document.getElementById('corr_corr_NO3').value) +
parseFloat(document.getElementById('corr_corr_NH4').value);
document.getElementById('corr_corr_N').value = N_k.toFixed(3);
// S, EC и N для итогового
calculateCorrectorS('final');
calculateCorrectorEC('final');
const N_2 = parseFloat(document.getElementById('corr_final_NO3').value) +
parseFloat(document.getElementById('corr_final_NH4').value);
document.getElementById('corr_final_N').value = N_2.toFixed(3);
// Обновляем протокол коррекции
updateCorrectionProtocol();
}
// Обновление протокола коррекции (аналог mkorr из легаси строки 2667-2689)
function updateCorrectionProtocol() {
const protocol = document.getElementById('corrector_protocol');
if (!protocol) return;
const V_1 = 10;
const V_2 = parseFloat(document.getElementById('corr_final_vol').value) || 30;
const EC_1 = parseFloat(document.getElementById('corr_curr_EC').value) || 1;
const EC_2 = parseFloat(document.getElementById('corr_final_EC').value) || 1;
const N_1 = parseFloat(document.getElementById('corr_curr_N').value) || 0;
const N_2 = parseFloat(document.getElementById('corr_final_N').value) || 0;
const NO3_1 = parseFloat(document.getElementById('corr_curr_NO3').value) || 1;
const NO3_2 = parseFloat(document.getElementById('corr_final_NO3').value) || 1;
const NH4_1 = parseFloat(document.getElementById('corr_curr_NH4').value) || 1;
const NH4_2 = parseFloat(document.getElementById('corr_final_NH4').value) || 1;
const P_1 = parseFloat(document.getElementById('corr_curr_P').value) || 1;
const P_2 = parseFloat(document.getElementById('corr_final_P').value) || 1;
const K_1 = parseFloat(document.getElementById('corr_curr_K').value) || 1;
const K_2 = parseFloat(document.getElementById('corr_final_K').value) || 1;
const Ca_1 = parseFloat(document.getElementById('corr_curr_Ca').value) || 1;
const Ca_2 = parseFloat(document.getElementById('corr_final_Ca').value) || 1;
const Mg_1 = parseFloat(document.getElementById('corr_curr_Mg').value) || 1;
const Mg_2 = parseFloat(document.getElementById('corr_final_Mg').value) || 1;
const S_1 = parseFloat(document.getElementById('corr_curr_S').value) || 0;
const S_2 = parseFloat(document.getElementById('corr_final_S').value) || 0;
const Cl_1 = parseFloat(document.getElementById('corr_curr_Cl').value) || 0;
const Cl_2 = parseFloat(document.getElementById('corr_final_Cl').value) || 0;
let text = 'ОСНОВНОЕ:\n';
text += `Изменение объема на: ${Math.round((V_2 - V_1) / V_1 * 100)}%\n`;
text += `Доля старого раствора: ${Math.round(V_1 / V_2 * 100)}%\n`;
text += `Изменение EC на: ${Math.round((EC_2 - EC_1) / EC_1 * 100)}%\n`;
text += `Изменение N общий на: ${Math.round((N_2 - N_1) / N_1 * 100)}%\n\n`;
text += 'ПРОФИЛЬ:\n';
text += `Коррекция NO3 на: ${Math.round((NO3_2 - NO3_1) / NO3_1 * 100)}%\n`;
text += `Коррекция NH4 на: ${Math.round((NH4_2 - NH4_1) / NH4_1 * 100)}%\n`;
text += `Коррекция P на: ${Math.round((P_2 - P_1) / P_1 * 100)}%\n`;
text += `Коррекция K на: ${Math.round((K_2 - K_1) / K_1 * 100)}%\n`;
text += `Коррекция Ca на: ${Math.round((Ca_2 - Ca_1) / Ca_1 * 100)}%\n`;
text += `Коррекция Mg на: ${Math.round((Mg_2 - Mg_1) / Mg_1 * 100)}%\n`;
text += `Коррекция S на: ${Math.round(S_2 - S_1)} ppm\n`;
text += `Коррекция Cl на: ${Math.round(Cl_2 - Cl_1)} ppm\n\n`;
text += 'СООТНОШЕНИЯ:\n';
text += `NH4:NO3 до ${(NH4_1/NO3_1).toFixed(3)} после ${(NH4_2/NO3_2).toFixed(3)}\n`;
text += `K:N до ${(K_1/N_1).toFixed(3)} после ${(K_2/N_2).toFixed(3)}\n`;
text += `K:Ca до ${(K_1/Ca_1).toFixed(3)} после ${(K_2/Ca_2).toFixed(3)}\n`;
text += `K:Mg до ${(K_1/Mg_1).toFixed(3)} после ${(K_2/Mg_2).toFixed(3)}`;
protocol.value = text;
}
// Расчет серы для корректора (аналог CalculateS)
function calculateCorrectorS(prefix) {
const molN = 14.0067;
const molP = 30.973762;
const molK = 39.0983;
const molCa = 40.078;
const molMg = 24.305;
const molS = 32.065;
const molCl = 35.453;
const NO3 = parseFloat(document.getElementById(`corr_${prefix}_NO3`).value) || 0;
const NH4 = parseFloat(document.getElementById(`corr_${prefix}_NH4`).value) || 0;
const P = parseFloat(document.getElementById(`corr_${prefix}_P`).value) || 0;
const K = parseFloat(document.getElementById(`corr_${prefix}_K`).value) || 0;
const Ca = parseFloat(document.getElementById(`corr_${prefix}_Ca`).value) || 0;
const Mg = parseFloat(document.getElementById(`corr_${prefix}_Mg`).value) || 0;
const Cl = parseFloat(document.getElementById(`corr_${prefix}_Cl`).value) || 0;
// Формула из легаси (строка 1029)
const S = (-molS * (-NH4*molCa*molMg*molK*molP*molCl - 2*Ca*molN*molMg*molK*molP*molCl -
2*Mg*molN*molCa*molK*molP*molCl - K*molN*molCa*molMg*molP*molCl +
NO3*molCa*molMg*molK*molP*molCl + P*molN*molCa*molMg*molK*molCl +
Cl*molN*molCa*molMg*molK*molP)) / (2*molN*molCa*molMg*molK*molP*molCl);
const sField = document.getElementById(`corr_${prefix}_S`);
if (sField) {
// Программно обновляем значение даже для readonly полей
sField.value = S.toFixed(3);
}
}
// Расчет EC для корректора (аналог CalcEC)
function calculateCorrectorEC(prefix) {
const molN = 14.0067;
const molK = 39.0983;
const molCa = 40.078;
const molMg = 24.305;
const NH4 = parseFloat(document.getElementById(`corr_${prefix}_NH4`).value) || 0;
const K = parseFloat(document.getElementById(`corr_${prefix}_K`).value) || 0;
const Ca = parseFloat(document.getElementById(`corr_${prefix}_Ca`).value) || 0;
const Mg = parseFloat(document.getElementById(`corr_${prefix}_Mg`).value) || 0;
// Формула из легаси (строка 1017)
const EC = 0.095 * (NH4*molCa*molMg*molK + 2*Ca*molN*molMg*molK +
2*Mg*molN*molCa*molK + K*molN*molCa*molMg + 2*molN*molCa*molMg*molK) /
(molN*molCa*molMg*molK);
const ecField = document.getElementById(`corr_${prefix}_EC`);
if (ecField) {
// Программно обновляем значение даже для readonly полей
ecField.value = EC.toFixed(3);
}
}
// Кнопка "В расчет" - перенос значений в основной профиль
function calcCorrection(columnName) {
const prefix = columnName === 'initial' ? 'init' :
columnName === 'current' ? 'curr' :
columnName === 'correcting' ? 'corr' : 'final';
// Переносим значения в основные поля макроэлементов
document.getElementById('N').value = document.getElementById(`corr_${prefix}_N`).value;
document.getElementById('NO3').value = document.getElementById(`corr_${prefix}_NO3`).value;
document.getElementById('NH4').value = document.getElementById(`corr_${prefix}_NH4`).value;
document.getElementById('P').value = document.getElementById(`corr_${prefix}_P`).value;
document.getElementById('K').value = document.getElementById(`corr_${prefix}_K`).value;
document.getElementById('Ca').value = document.getElementById(`corr_${prefix}_Ca`).value;
document.getElementById('Mg').value = document.getElementById(`corr_${prefix}_Mg`).value;
document.getElementById('S').value = document.getElementById(`corr_${prefix}_S`).value;
document.getElementById('Cl').value = document.getElementById(`corr_${prefix}_Cl`).value;
document.getElementById('EC').value = document.getElementById(`corr_${prefix}_EC`).value;
// Обновляем объем
const vol = document.getElementById(`corr_${prefix}_vol`);
if (vol && vol.value) {
document.getElementById('volume').value = vol.value;
}
// Запускаем полный пересчет
calcAll();
}
// Инициализация корректора при загрузке
function initCorrector() {
// Устанавливаем обработчики на все поля корректора
const correctorInputs = document.querySelectorAll('.corrector-input');
correctorInputs.forEach(input => {
input.addEventListener('input', onCorrectorChange);
input.addEventListener('focus', function(e) {
const id = e.target.id;
if (id.includes('_init_')) activeCorrectColumn = 'init';
else if (id.includes('_corr_')) activeCorrectColumn = 'corr';
else if (id.includes('_final_')) activeCorrectColumn = 'final';
});
});
// Начальный расчет корректора
korrection();
}
// Сохранение в журнал коррекций (как в легаси tojrnlClick)
function saveCorrectorToJournal() {
// Получаем текущую дату в формате yyyy-mm-dd
const now = new Date();
const dateStr = now.toISOString().split('T')[0];
// Получаем итоговый объем
const finalVolume = parseFloat(document.getElementById('corr_final_vol')?.value) || 30;
// Формируем описание (как в легаси: m1.Text)
const desc = `Корректор. Скорректирован раствор до ${finalVolume} литров.`;
// Получаем значения из Итогового профиля (N_2, NO3_2 и т.д. в легаси)
const N = parseFloat(document.getElementById('corr_final_N')?.value) || 0;
const NO3 = parseFloat(document.getElementById('corr_final_NO3')?.value) || 0;
const NH4 = parseFloat(document.getElementById('corr_final_NH4')?.value) || 0;
const P = parseFloat(document.getElementById('corr_final_P')?.value) || 0;
const K = parseFloat(document.getElementById('corr_final_K')?.value) || 0;
const Ca = parseFloat(document.getElementById('corr_final_Ca')?.value) || 0;
const Mg = parseFloat(document.getElementById('corr_final_Mg')?.value) || 0;
const S = parseFloat(document.getElementById('corr_final_S')?.value) || 0;
const Cl = parseFloat(document.getElementById('corr_final_Cl')?.value) || 0;
// Получаем микроэлементы (из основного профиля, как в легаси)
const Fe = (parseFloat(document.getElementById('Fe')?.value) || 0) / 1000;
const Mn = (parseFloat(document.getElementById('Mn')?.value) || 0) / 1000;
const B = (parseFloat(document.getElementById('B')?.value) || 0) / 1000;
const Zn = (parseFloat(document.getElementById('Zn')?.value) || 0) / 1000;
const Cu = (parseFloat(document.getElementById('Cu')?.value) || 0) / 1000;
const Mo = (parseFloat(document.getElementById('Mo')?.value) || 0) / 1000;
const Co = (parseFloat(document.getElementById('Co')?.value) || 0) / 1000;
const Si = (parseFloat(document.getElementById('Si')?.value) || 0) / 1000;
// Формируем строку профиля (как в легаси str)
const profileStr = `N=${N} NO3=${NO3} NH4=${NH4} P=${P} K=${K} Ca=${Ca} Mg=${Mg} S=${S} Cl=${Cl} Fe=${Fe} Mn=${Mn} B=${B} Zn=${Zn} Cu=${Cu} Mo=${Mo} Co=${Co} Si=${Si}`;
// Создаем запись журнала (как в легаси: date=...;...;...)
const journalEntry = {
date: dateStr,
desc: desc,
memo: desc, // Совместимость
profile: profileStr
};
// Добавляем в журнал
journalEntries.push(journalEntry);
// Сортируем журнал по дате (как в легаси DStr.Sort)
journalEntries.sort((a, b) => {
const dateA = new Date(a.date);
const dateB = new Date(b.date);
return dateB - dateA; // Новые записи сверху
});
// Обновляем список журнала
updateJournalList();
// Переключаемся на вкладку "Файл" чтобы показать результат
switchTab('file', document.querySelector('[onclick*="file"]'));
alert('Данные корректора сохранены в журнал');
}
// Функции для расчета концентратов (аналог CalcConc из легаси)
// В легаси CalcConc вызывается ПОСЛЕ CalcWeight и microToWeght
// и использует уже вычисленные значения gCaNO3, gKNO3, gFe и т.д.
function calcConcentrates() {
const volume = parseFloat(document.getElementById('volume').value) || 10;
// Концентрации растворов (г/л) - читаем из полей или используем дефолтные значения
const glCaNO3 = parseFloat(document.getElementById('conc_glCaNO3')?.value) || 600;
const glKNO3 = parseFloat(document.getElementById('conc_glKNO3')?.value) || 250;
const glNH4NO3 = parseFloat(document.getElementById('conc_glNH4NO3')?.value) || 100;
const glMgNO3 = parseFloat(document.getElementById('conc_glMgNO3')?.value) || 500;
const glCaCl2 = parseFloat(document.getElementById('conc_glCaCl2')?.value) || 100;
const glMgSO4 = parseFloat(document.getElementById('conc_glMgSO4')?.value) || 600;
const glKH2PO4 = parseFloat(document.getElementById('conc_glKH2PO4')?.value) || 150;
const glK2SO4 = parseFloat(document.getElementById('conc_glK2SO4')?.value) || 100;
const glFe = parseFloat(document.getElementById('conc_glFe')?.value) || 10;
const glMn = parseFloat(document.getElementById('conc_glMn')?.value) || 10;
const glB = parseFloat(document.getElementById('conc_glB')?.value) || 10;
const glZn = parseFloat(document.getElementById('conc_glZn')?.value) || 10;
const glCu = parseFloat(document.getElementById('conc_glCu')?.value) || 10;
const glMo = parseFloat(document.getElementById('conc_glMo')?.value) || 10;
const glCo = parseFloat(document.getElementById('conc_glCo')?.value) || 10;
const glSi = parseFloat(document.getElementById('conc_glSi')?.value) || 10;
const glCmplx = parseFloat(document.getElementById('conc_glCmplx')?.value) || 10;
// Плотности растворов (г/мл) - читаем из полей или используем дефолтные значения
const gmlCaNO3 = parseFloat(document.getElementById('conc_gmlCaNO3')?.value) || 1;
const gmlKNO3 = parseFloat(document.getElementById('conc_gmlKNO3')?.value) || 1;
const gmlNH4NO3 = parseFloat(document.getElementById('conc_gmlNH4NO3')?.value) || 1;
const gmlMgNO3 = parseFloat(document.getElementById('conc_gmlMgNO3')?.value) || 1;
const gmlCaCl2 = parseFloat(document.getElementById('conc_gmlCaCl2')?.value) || 1;
const gmlMgSO4 = parseFloat(document.getElementById('conc_gmlMgSO4')?.value) || 1;
const gmlKH2PO4 = parseFloat(document.getElementById('conc_gmlKH2PO4')?.value) || 1;
const gmlK2SO4 = parseFloat(document.getElementById('conc_gmlK2SO4')?.value) || 1;
const gmlFe = parseFloat(document.getElementById('conc_gmlFe')?.value) || 1;
const gmlMn = parseFloat(document.getElementById('conc_gmlMn')?.value) || 1;
const gmlB = parseFloat(document.getElementById('conc_gmlB')?.value) || 1;
const gmlZn = parseFloat(document.getElementById('conc_gmlZn')?.value) || 1;
const gmlCu = parseFloat(document.getElementById('conc_gmlCu')?.value) || 1;
const gmlMo = parseFloat(document.getElementById('conc_gmlMo')?.value) || 1;
const gmlCo = parseFloat(document.getElementById('conc_gmlCo')?.value) || 1;
const gmlSi = parseFloat(document.getElementById('conc_gmlSi')?.value) || 1;
const gmlCmplx = parseFloat(document.getElementById('conc_gmlCmplx')?.value) || 1;
// Читаем граммы солей из полей (уже вычислены в calcWeight)
// В легаси: Kf.mlCaNO3.Value:=Kf.gCaNO3.value/Kf.glCaNO3.value*1000
const gCaNO3 = parseFloat(document.getElementById('g_CaNO3')?.value) || 0;
const gKNO3 = parseFloat(document.getElementById('g_KNO3')?.value) || 0;
const gNH4NO3 = parseFloat(document.getElementById('g_NH4NO3')?.value) || 0;
const gMgNO3 = parseFloat(document.getElementById('g_MgNO3')?.value) || 0;
const gCaCl2 = parseFloat(document.getElementById('g_CaCl2')?.value) || 0;
const gMgSO4 = parseFloat(document.getElementById('g_MgSO4')?.value) || 0;
const gKH2PO4 = parseFloat(document.getElementById('g_KH2PO4')?.value) || 0;
const gK2SO4 = parseFloat(document.getElementById('g_K2SO4')?.value) || 0;
// Читаем граммы микроэлементов из полей (уже вычислены в microToWeght)
// В легаси: if Kf.dFe.value >0 then Kf.gFe.value:=Kf.Fe.value/Kf.dFe.value*Kf.V.value/10000
const gFe = parseFloat(document.getElementById('g_Fe')?.value) || 0;
const gMn = parseFloat(document.getElementById('g_Mn')?.value) || 0;
const gB = parseFloat(document.getElementById('g_B')?.value) || 0;
const gZn = parseFloat(document.getElementById('g_Zn')?.value) || 0;
const gCu = parseFloat(document.getElementById('g_Cu')?.value) || 0;
const gMo = parseFloat(document.getElementById('g_Mo')?.value) || 0;
const gCo = parseFloat(document.getElementById('g_Co')?.value) || 0;
const gSi = parseFloat(document.getElementById('g_Si')?.value) || 0;
// Комплекс микроэлементов (сумма всех)
const gCmplx = gFe + gMn + gB + gZn + gCu + gMo + gCo + gSi;
// Получаем значения Fe, Mn и т.д. для расчёта mlFe
const Fe = parseFloat(document.getElementById('Fe')?.value) || 0;
const Mn = parseFloat(document.getElementById('Mn')?.value) || 0;
const B = parseFloat(document.getElementById('B')?.value) || 0;
const Zn = parseFloat(document.getElementById('Zn')?.value) || 0;
const Cu = parseFloat(document.getElementById('Cu')?.value) || 0;
const Mo = parseFloat(document.getElementById('Mo')?.value) || 0;
const Co = parseFloat(document.getElementById('Co')?.value) || 0;
const Si = parseFloat(document.getElementById('Si')?.value) || 0;
const dFe = parseFloat(document.getElementById('micro_Fe_percent').value) || 20.1;
const dMn = parseFloat(document.getElementById('micro_Mn_percent').value) || 36.4;
const dB = parseFloat(document.getElementById('micro_B_percent').value) || 17.5;
const dZn = parseFloat(document.getElementById('micro_Zn_percent').value) || 22.7;
const dCu = parseFloat(document.getElementById('micro_Cu_percent').value) || 25.5;
const dMo = parseFloat(document.getElementById('micro_Mo_percent').value) || 54.3;
const dCo = parseFloat(document.getElementById('micro_Co_percent').value) || 13.0;
const dSi = parseFloat(document.getElementById('micro_Si_percent').value) || 7.0;
// Расчет миллилитров концентрата
// Для макроэлементов: mlCaNO3 = gCaNO3 / glCaNO3 * 1000
const mlCaNO3 = gCaNO3 / glCaNO3 * 1000;
const mlKNO3 = gKNO3 / glKNO3 * 1000;
const mlNH4NO3 = gNH4NO3 / glNH4NO3 * 1000;
const mlMgNO3 = gMgNO3 / glMgNO3 * 1000;
const mlCaCl2 = gCaCl2 / glCaCl2 * 1000;
const mlMgSO4 = gMgSO4 / glMgSO4 * 1000;
const mlKH2PO4 = gKH2PO4 / glKH2PO4 * 1000;
const mlK2SO4 = gK2SO4 / glK2SO4 * 1000;
// Для микроэлементов: mlFe = (Fe / dFe * V / 10) / glFe (легаси строка 1559)
const mlFe = dFe > 0 ? ((Fe / dFe * volume / 10) / glFe) : 0;
const mlMn = dMn > 0 ? ((Mn / dMn * volume / 10) / glMn) : 0;
const mlB = dB > 0 ? ((B / dB * volume / 10) / glB) : 0;
const mlZn = dZn > 0 ? ((Zn / dZn * volume / 10) / glZn) : 0;
const mlCu = dCu > 0 ? ((Cu / dCu * volume / 10) / glCu) : 0;
const mlMo = dMo > 0 ? ((Mo / dMo * volume / 10) / glMo) : 0;
const mlCo = dCo > 0 ? ((Co / dCo * volume / 10) / glCo) : 0;
const mlSi = dSi > 0 ? ((Si / dSi * volume / 10) / glSi) : 0;
const mlCmplx = dB > 0 ? ((B / dB * volume / 10) / glCmplx) : 0;
// Расчет граммов для изготовления (как в легаси: ggCaNO3 = gmlCaNO3 * mlCaNO3)
const ggCaNO3 = gmlCaNO3 * mlCaNO3;
const ggKNO3 = gmlKNO3 * mlKNO3;
const ggNH4NO3 = gmlNH4NO3 * mlNH4NO3;
const ggMgNO3 = gmlMgNO3 * mlMgNO3;
const ggCaCl2 = gmlCaCl2 * mlCaCl2;
const ggMgSO4 = gmlMgSO4 * mlMgSO4;
const ggKH2PO4 = gmlKH2PO4 * mlKH2PO4;
const ggK2SO4 = gmlK2SO4 * mlK2SO4;
const ggFe = gmlFe * mlFe;
const ggMn = gmlMn * mlMn;
const ggB = gmlB * mlB;
const ggZn = gmlZn * mlZn;
const ggCu = gmlCu * mlCu;
const ggMo = gmlMo * mlMo;
const ggCo = gmlCo * mlCo;
const ggSi = gmlSi * mlSi;
const ggCmplx = gmlCmplx * mlCmplx;
// Расчет сводной информации для растворов A и B (как в легаси CalcConc строки 1672-1712)
const Av = Math.round((mlCaNO3 + mlKNO3 + mlNH4NO3 + mlMgNO3 + mlCaCl2) * 10000) / 10000;
const Am = Math.round((ggCaNO3 + ggKNO3 + ggNH4NO3 + ggMgNO3 + ggCaCl2) * 10000) / 10000;
const Ak = Math.round(Am / Av * 100) / 100;
const useComplex = document.getElementById('use_complex')?.checked;
let Bv, Bm, Bk;
if (useComplex) {
Bv = Math.round((mlMgSO4 + mlKH2PO4 + mlK2SO4 + mlCmplx) * 10000) / 10000;
Bm = Math.round((ggMgSO4 + ggKH2PO4 + ggK2SO4 + ggCmplx) * 10000) / 10000;
Bk = Math.round(Bm / Bv * 1000) / 1000;
} else {
Bv = mlMgSO4 + mlKH2PO4 + mlK2SO4 + mlFe + mlMn + mlB + mlZn + mlMo + mlCu + mlCo + mlSi;
Bm = Math.round((ggMgSO4 + ggKH2PO4 + ggK2SO4 + ggFe + ggMn + ggB + ggZn + ggMo + ggCo + ggSi) * 100) / 100;
Bk = Math.round(Bm / Bv * 100) / 100;
}
// Обновление сводной информации
const sumAElement = document.getElementById('sumA');
const sumBElement = document.getElementById('sumB');
if (sumAElement) {
sumAElement.textContent = `Объем: ${Av.toFixed(2)} мл, вес: ${Am.toFixed(2)} гр, плотность: ${Ak.toFixed(2)} г/мл.`;
}
if (sumBElement) {
sumBElement.textContent = `Объем: ${(Math.round(Bv * 10) / 10).toFixed(1)} мл, вес: ${Bm.toFixed(2)} гр, плотность: ${Bk.toFixed(2)} г/мл`;
}
// Расчёт информации о таре (как в легаси строки 1675-1680, 1708-1712)
const tAml = parseFloat(document.getElementById('tank_A')?.value) || 500;
const tBml = parseFloat(document.getElementById('tank_B')?.value) || 500;
const Ac = tAml !== 0 ? Math.round(volume / tAml * 1000) : 0;
const Aw = Math.round(tAml - Av);
const Aml = Math.round(tAml / volume * 1000) / 1000;
const Bc = tBml !== 0 ? Math.round(volume / tBml * 1000) : 0;
const Bw = Math.round(tBml - Bv);
const Bml = Math.round(tBml / volume * 1000) / 1000;
const lVolAElement = document.getElementById('lVolA');
const lVolBElement = document.getElementById('lVolB');
if (lVolAElement) {
lVolAElement.textContent = `Концентрат A (${Ac}:1) . Долить воды: ${Aw}мл. По ${Aml} мл на 1л.`;
}
if (lVolBElement) {
lVolBElement.textContent = `Концентрат B (${Bc}:1) . Долить воды: ${Bw}мл. По ${Bml} мл на 1л.`;
}
// Обновление таблицы расчета (передаём вычисленные ml* и gg*)
updateConcentrateTable('A', {
CaNO3: { g_l: glCaNO3, g_ml: gmlCaNO3, ml: mlCaNO3, gr: ggCaNO3 },
KNO3: { g_l: glKNO3, g_ml: gmlKNO3, ml: mlKNO3, gr: ggKNO3 },
NH4NO3: { g_l: glNH4NO3, g_ml: gmlNH4NO3, ml: mlNH4NO3, gr: ggNH4NO3 },
MgNO3: { g_l: glMgNO3, g_ml: gmlMgNO3, ml: mlMgNO3, gr: ggMgNO3 },
CaCl2: { g_l: glCaCl2, g_ml: gmlCaCl2, ml: mlCaCl2, gr: ggCaCl2 }
});
updateConcentrateTable('B', {
MgSO4: { g_l: glMgSO4, g_ml: gmlMgSO4, ml: mlMgSO4, gr: ggMgSO4 },
KH2PO4: { g_l: glKH2PO4, g_ml: gmlKH2PO4, ml: mlKH2PO4, gr: ggKH2PO4 },
K2SO4: { g_l: glK2SO4, g_ml: gmlK2SO4, ml: mlK2SO4, gr: ggK2SO4 },
Fe: { g_l: glFe, g_ml: gmlFe, ml: mlFe, gr: ggFe },
Mn: { g_l: glMn, g_ml: gmlMn, ml: mlMn, gr: ggMn },
B: { g_l: glB, g_ml: gmlB, ml: mlB, gr: ggB },
Zn: { g_l: glZn, g_ml: gmlZn, ml: mlZn, gr: ggZn },
Cu: { g_l: glCu, g_ml: gmlCu, ml: mlCu, gr: ggCu },
Mo: { g_l: glMo, g_ml: gmlMo, ml: mlMo, gr: ggMo },
Co: { g_l: glCo, g_ml: gmlCo, ml: mlCo, gr: ggCo },
Si: { g_l: glSi, g_ml: gmlSi, ml: mlSi, gr: ggSi }
});
// Обновляем названия солей перед обновлением изготовления
updateSaltNames();
// Обновление подвкладки изготовления (передаём gg* - граммы для изготовления)
updatePreparationTab({
CaNO3: ggCaNO3, KNO3: ggKNO3, NH4NO3: ggNH4NO3, MgNO3: ggMgNO3, CaCl2: ggCaCl2,
MgSO4: ggMgSO4, KH2PO4: ggKH2PO4, K2SO4: ggK2SO4,
Fe: ggFe, Mn: ggMn, B: ggB, Zn: ggZn, Cu: ggCu, Mo: ggMo, Co: ggCo, Si: ggSi,
Cmplx: ggCmplx
});
// Обновление подвкладки цены - не нужно, так как updatePriceResults() вызывается в calcAll()
// и берет данные напрямую из полей g_CaNO3, g_KNO3 и т.д.
}
// Обновление названий микроэлементов во всех местах (аналог legacy строки 1623-1630)
function updateMicroNames() {
// Получаем проценты микроэлементов
const dFe = parseFloat(document.getElementById('micro_Fe_percent')?.value) || 20.1;
const dMn = parseFloat(document.getElementById('micro_Mn_percent')?.value) || 36.4;
const dB = parseFloat(document.getElementById('micro_B_percent')?.value) || 17.5;
const dZn = parseFloat(document.getElementById('micro_Zn_percent')?.value) || 22.7;
const dCu = parseFloat(document.getElementById('micro_Cu_percent')?.value) || 25.5;
const dMo = parseFloat(document.getElementById('micro_Mo_percent')?.value) || 54.3;
const dCo = parseFloat(document.getElementById('micro_Co_percent')?.value) || 13.0;
const dSi = parseFloat(document.getElementById('micro_Si_percent')?.value) || 7.0;
// Формируем названия микроэлементов (как в legacy: 'Железо Fe=20.1%')
const nFe = `Железо Fe=${dFe}%`;
const nMn = `Марганец Mn=${dMn}%`;
const nB = `Бор B=${dB}%`;
const nZn = `Цинк Zn=${dZn}%`;
const nCu = `Медь Cu=${dCu}%`;
const nMo = `Молибден Mo=${dMo}%`;
const nCo = `Кобальт Co=${dCo}%`;
const nSi = `Кремний Si=${dSi}%`;
// Обновляем метки на вкладке "Микро"
const labelFe = document.getElementById('micro_Fe_label');
const labelMn = document.getElementById('micro_Mn_label');
const labelB = document.getElementById('micro_B_label');
const labelZn = document.getElementById('micro_Zn_label');
const labelCu = document.getElementById('micro_Cu_label');
const labelMo = document.getElementById('micro_Mo_label');
const labelCo = document.getElementById('micro_Co_label');
const labelSi = document.getElementById('micro_Si_label');
if (labelFe) labelFe.textContent = nFe;
if (labelMn) labelMn.textContent = nMn;
if (labelB) labelB.textContent = nB;
if (labelZn) labelZn.textContent = nZn;
if (labelCu) labelCu.textContent = nCu;
if (labelMo) labelMo.textContent = nMo;
if (labelCo) labelCo.textContent = nCo;
if (labelSi) labelSi.textContent = nSi;
// Возвращаем названия для использования в других функциях
return {
Fe: nFe,
Mn: nMn,
B: nB,
Zn: nZn,
Cu: nCu,
Mo: nMo,
Co: nCo,
Si: nSi
};
}
// Обновление названий солей во всех местах (аналог SoilName из легаси строки 1990-2073)
function updateSaltNames() {
const round1 = (val) => Math.round(val * 10) / 10;
// Кальций азотнокислый
const CaNO3_Ca = parseFloat(document.getElementById('salt_CaNO3_Ca')?.value) || 16.972;
const CaNO3_NO3 = parseFloat(document.getElementById('salt_CaNO3_NO3')?.value) || 11.863;
const CaNO3_NH4 = parseFloat(document.getElementById('salt_CaNO3_NH4')?.value) || 0;
let nCaNO3;
if (CaNO3_NH4 === 0) {
const ca = round1(CaNO3_Ca);
if (ca === 17) nCaNO3 = 'Кальций азотнокислый Ca(NO3)2*4H2O';
else if (ca === 20) nCaNO3 = 'Кальций азотнокислый Ca(NO3)2*2H2O';
else if (ca === 24.4) nCaNO3 = 'Кальций азотнокислый Ca(NO3)2';
else nCaNO3 = `Селитра кальциевая CaO-${round1(CaNO3_Ca/0.714691)}% N-${round1(CaNO3_NH4+CaNO3_NO3)}%`;
} else {
nCaNO3 = `Селитра кальциевая CaO-${round1(CaNO3_Ca/0.714691)}% N-${round1(CaNO3_NH4+CaNO3_NO3)}%`;
}
// Калий азотнокислый
const KNO3_K = parseFloat(document.getElementById('salt_KNO3_K')?.value) || 38.672;
const KNO3_NO3 = parseFloat(document.getElementById('salt_KNO3_NO3')?.value) || 13.854;
const nKNO3 = round1(KNO3_K) === 38.7
? 'Калий азотнокислый KNO3'
: `Селитра калиевая K2O-${round1(KNO3_K/0.830148)}% N-${round1(KNO3_NO3)}%`;
// Аммоний азотнокислый
const NH4NO3_NH4 = parseFloat(document.getElementById('salt_NH4NO3_NH4')?.value) || 17.499;
const NH4NO3_NO3 = parseFloat(document.getElementById('salt_NH4NO3_NO3')?.value) || 17.499;
const nNH4NO3 = round1(NH4NO3_NO3) === 17.5
? 'Аммоний азотнокислый NH4NO3'
: `Селитра аммиачная N-${round1(NH4NO3_NH4+NH4NO3_NO3)}%`;
// Магний сернокислый
const MgSO4_Mg = parseFloat(document.getElementById('salt_MgSO4_Mg')?.value) || 9.861;
const MgSO4_S = parseFloat(document.getElementById('salt_MgSO4_S')?.value) || 13.01;
let nMgSO4;
const mg = round1(MgSO4_Mg);
if (mg === 9.9) nMgSO4 = 'Магний сернокислый MgSO4*7H2O';
else if (mg === 20.2) nMgSO4 = 'Магний сернокислый MgSO4';
else nMgSO4 = `Сульфат магния MgO-${round1(MgSO4_Mg/0.603036)}% SO3-${round1(MgSO4_S/0.400496)}%`;
// Калий фосфорнокислый
const KH2PO4_K = parseFloat(document.getElementById('salt_KH2PO4_K')?.value) || 28.731;
const KH2PO4_P = parseFloat(document.getElementById('salt_KH2PO4_P')?.value) || 22.761;
const nKH2PO4 = round1(KH2PO4_K) === 28.7
? 'Калий фосфорнокислый KH2PO4'
: `Монофосфат калия K2O-${round1(KH2PO4_K/0.830148)}% P2O5-${round1(KH2PO4_P/0.436421)}%`;
// Калий сернокислый
const K2SO4_K = parseFloat(document.getElementById('salt_K2SO4_K')?.value) || 44.874;
const K2SO4_S = parseFloat(document.getElementById('salt_K2SO4_S')?.value) || 18.401;
const nK2SO4 = round1(K2SO4_K) === 44.9
? 'Калий сернокислый K2SO4'
: `Сульфат калия K2O-${round1(K2SO4_K/0.830148)}% SO3-${round1(K2SO4_S/0.400496)}%`;
// Магний азотнокислый
const MgNO3_Mg = parseFloat(document.getElementById('salt_MgNO3_Mg')?.value) || 9.479;
const MgNO3_NO3 = parseFloat(document.getElementById('salt_MgNO3_NO3')?.value) || 10.926;
let nMgNO3;
const mgn = round1(MgNO3_Mg);
if (mgn === 9.5) nMgNO3 = 'Магний азотнокислый Mg(NO3)2*6H2O';
else if (mgn === 16.4) nMgNO3 = 'Магний азотнокислый Mg(NO3)2';
else nMgNO3 = `Селитра магниевая MgO-${round1(MgNO3_Mg/0.603036)}% N-${round1(MgNO3_NO3)}%`;
// Хлорид кальция
const CaCl2_Ca = parseFloat(document.getElementById('salt_CaCl2_Ca')?.value) || 18.294;
const CaCl2_Cl = parseFloat(document.getElementById('salt_CaCl2_Cl')?.value) || 32.366;
let nCaCl2;
const cacl = round1(CaCl2_Ca);
if (cacl === 18.3) nCaCl2 = 'Хлорид кальция 6-водный CaCl2*6H2O';
else if (cacl === 36.1) nCaCl2 = 'Хлорид кальция безводный CaCl2';
else nCaCl2 = `Кальций хлористый CaO-${round1(CaCl2_Ca/0.714691)}% Cl-${round1(CaCl2_Cl)}%`;
// Обновляем метки на вкладке "Макро"
const labelCaNO3 = document.getElementById('label_CaNO3');
const labelKNO3 = document.getElementById('label_KNO3');
const labelNH4NO3 = document.getElementById('label_NH4NO3');
const labelMgSO4 = document.getElementById('label_MgSO4');
const labelKH2PO4 = document.getElementById('label_KH2PO4');
const labelK2SO4 = document.getElementById('label_K2SO4');
const labelMgNO3 = document.getElementById('label_MgNO3');
const labelCaCl2 = document.getElementById('label_CaCl2');
if (labelCaNO3) labelCaNO3.textContent = nCaNO3;
if (labelKNO3) labelKNO3.textContent = nKNO3;
if (labelNH4NO3) labelNH4NO3.textContent = nNH4NO3;
if (labelMgSO4) labelMgSO4.textContent = nMgSO4;
if (labelKH2PO4) labelKH2PO4.textContent = nKH2PO4;
if (labelK2SO4) labelK2SO4.textContent = nK2SO4;
if (labelMgNO3) labelMgNO3.textContent = nMgNO3;
if (labelCaCl2) labelCaCl2.textContent = nCaCl2;
// Возвращаем названия для использования в других функциях
return {
CaNO3: nCaNO3,
KNO3: nKNO3,
NH4NO3: nNH4NO3,
MgSO4: nMgSO4,
KH2PO4: nKH2PO4,
K2SO4: nK2SO4,
MgNO3: nMgNO3,
CaCl2: nCaCl2
};
}
function updateConcentrateTable(section, data) {
const saltNames = updateSaltNames();
const tableBodies = document.querySelectorAll('#calc .concentrate-table tbody');
tableBodies.forEach(tableBody => {
const rows = tableBody.querySelectorAll('tr');
rows.forEach(row => {
const firstCell = row.cells[0];
const cellText = firstCell.textContent;
let key = '';
// Определяем ключ по содержимому (более надёжно чем по тексту)
if (cellText.includes('азотнокислый') && cellText.includes('Ca')) key = 'CaNO3';
else if (cellText.includes('азотнокислый') && cellText.includes('K')) key = 'KNO3';
else if (cellText.includes('азотнокислый') && cellText.includes('NH4')) key = 'NH4NO3';
else if (cellText.includes('азотнокислый') && cellText.includes('Mg')) key = 'MgNO3';
else if (cellText.includes('хлорид') || cellText.includes('Хлорид') || cellText.includes('хлористый')) key = 'CaCl2';
else if (cellText.includes('сернокислый') && cellText.includes('Mg')) key = 'MgSO4';
else if (cellText.includes('фосфорнокислый') || cellText.includes('Монофосфат')) key = 'KH2PO4';
else if (cellText.includes('сернокислый') && cellText.includes('K')) key = 'K2SO4';
else if (cellText.includes('Fe=')) key = 'Fe';
else if (cellText.includes('Mn=')) key = 'Mn';
else if (cellText.includes('B=')) key = 'B';
else if (cellText.includes('Zn=')) key = 'Zn';
else if (cellText.includes('Cu=')) key = 'Cu';
else if (cellText.includes('Mo=')) key = 'Mo';
else if (cellText.includes('Co=')) key = 'Co';
else if (cellText.includes('Si=')) key = 'Si';
if (key && data[key]) {
// Обновляем название соли
if (saltNames[key]) {
firstCell.textContent = saltNames[key];
}
// Обновляем значения
row.cells[1].querySelector('input').value = data[key].g_l.toFixed(1);
row.cells[2].querySelector('input').value = data[key].g_ml.toFixed(4);
row.cells[3].querySelector('input').value = data[key].ml.toFixed(2);
row.cells[4].querySelector('input').value = data[key].gr.toFixed(2);
}
});
});
}
function updatePreparationTab(weights) {
// Обновляем весовые значения для макроэлементов
document.getElementById('g2gCaNO3').value = weights.CaNO3.toFixed(2);
document.getElementById('g2gKNO3').value = weights.KNO3.toFixed(2);
document.getElementById('g2gNH4NO3').value = weights.NH4NO3.toFixed(2);
document.getElementById('g2gMgNO3').value = weights.MgNO3.toFixed(2);
document.getElementById('g2gCaCl2').value = weights.CaCl2.toFixed(2);
document.getElementById('g2gMgSO4').value = weights.MgSO4.toFixed(2);
document.getElementById('g2gKH2PO4').value = weights.KH2PO4.toFixed(2);
document.getElementById('g2gK2SO4').value = weights.K2SO4.toFixed(2);
// Обновляем весовые значения для микроэлементов
document.getElementById('g2gFe').value = weights.Fe.toFixed(2);
document.getElementById('g2gMn').value = weights.Mn.toFixed(2);
document.getElementById('g2gB').value = weights.B.toFixed(2);
document.getElementById('g2gZn').value = weights.Zn.toFixed(2);
document.getElementById('g2gCu').value = weights.Cu.toFixed(2);
document.getElementById('g2gMo').value = weights.Mo.toFixed(2);
document.getElementById('g2gCo').value = weights.Co.toFixed(2);
document.getElementById('g2gSi').value = weights.Si.toFixed(2);
// Получаем названия солей из единой системы (аналог SoilName из легаси)
const saltNames = updateSaltNames();
// Получаем названия микроэлементов из единой системы (аналог legacy строки 1623-1630)
const microNames = updateMicroNames();
// Получаем концентрации для изготовления (аналог CalcConc из легаси)
const glCaNO3 = parseFloat(document.getElementById('conc_glCaNO3')?.value) || 600;
const glKNO3 = parseFloat(document.getElementById('conc_glKNO3')?.value) || 250;
const glNH4NO3 = parseFloat(document.getElementById('conc_glNH4NO3')?.value) || 100;
const glMgNO3 = parseFloat(document.getElementById('conc_glMgNO3')?.value) || 500;
const glCaCl2 = parseFloat(document.getElementById('conc_glCaCl2')?.value) || 100;
const glMgSO4 = parseFloat(document.getElementById('conc_glMgSO4')?.value) || 600;
const glKH2PO4 = parseFloat(document.getElementById('conc_glKH2PO4')?.value) || 150;
const glK2SO4 = parseFloat(document.getElementById('conc_glK2SO4')?.value) || 100;
const gmlCaNO3 = parseFloat(document.getElementById('conc_gmlCaNO3')?.value) || 1.0000;
const gmlKNO3 = parseFloat(document.getElementById('conc_gmlKNO3')?.value) || 1.0000;
const gmlNH4NO3 = parseFloat(document.getElementById('conc_gmlNH4NO3')?.value) || 1.0000;
const gmlMgNO3 = parseFloat(document.getElementById('conc_gmlMgNO3')?.value) || 1.0000;
const gmlCaCl2 = parseFloat(document.getElementById('conc_gmlCaCl2')?.value) || 1.0000;
const gmlMgSO4 = parseFloat(document.getElementById('conc_gmlMgSO4')?.value) || 1.0000;
const gmlKH2PO4 = parseFloat(document.getElementById('conc_gmlKH2PO4')?.value) || 1.0000;
const gmlK2SO4 = parseFloat(document.getElementById('conc_gmlK2SO4')?.value) || 1.0000;
// Обновляем названия с весовыми значениями и концентрациями (как в legacy CalcConc строки 1652-1659)
const k2nCaNO3 = document.querySelector('label[for="k2nCaNO3"]');
const k2nKNO3 = document.querySelector('label[for="k2nKNO3"]');
const k2nNH4NO3 = document.querySelector('label[for="k2nNH4NO3"]');
const k2nMgNO3 = document.querySelector('label[for="k2nMgNO3"]');
const k2nMgSO4 = document.querySelector('label[for="k2nMgSO4"]');
const k2nKH2PO4 = document.querySelector('label[for="k2nKH2PO4"]');
const k2nK2SO4 = document.querySelector('label[for="k2nK2SO4"]');
const k2nCaCl2 = document.querySelector('label[for="k2nCaCl2"]');
if (k2nCaNO3) k2nCaNO3.textContent = `${saltNames.CaNO3} ${weights.CaNO3.toFixed(3)} г. (${glCaNO3} г/л, ${gmlCaNO3.toFixed(4)} г/мл)`;
if (k2nKNO3) k2nKNO3.textContent = `${saltNames.KNO3} ${weights.KNO3.toFixed(3)} г. (${glKNO3} г/л, ${gmlKNO3.toFixed(4)} г/мл)`;
if (k2nNH4NO3) k2nNH4NO3.textContent = `${saltNames.NH4NO3} ${weights.NH4NO3.toFixed(3)} г. (${glNH4NO3} г/л, ${gmlNH4NO3.toFixed(4)} г/мл)`;
if (k2nMgNO3) k2nMgNO3.textContent = `${saltNames.MgNO3} ${weights.MgNO3.toFixed(3)} г. (${glMgNO3} г/л, ${gmlMgNO3.toFixed(4)} г/мл)`;
if (k2nMgSO4) k2nMgSO4.textContent = `${saltNames.MgSO4} ${weights.MgSO4.toFixed(3)} г. (${glMgSO4} г/л, ${gmlMgSO4.toFixed(4)} г/мл)`;
if (k2nKH2PO4) k2nKH2PO4.textContent = `${saltNames.KH2PO4} ${weights.KH2PO4.toFixed(3)} г. (${glKH2PO4} г/л, ${gmlKH2PO4.toFixed(4)} г/мл)`;
if (k2nK2SO4) k2nK2SO4.textContent = `${saltNames.K2SO4} ${weights.K2SO4.toFixed(3)} г. (${glK2SO4} г/л, ${gmlK2SO4.toFixed(4)} г/мл)`;
if (k2nCaCl2) k2nCaCl2.textContent = `${saltNames.CaCl2} ${weights.CaCl2.toFixed(3)} г. (${glCaCl2} г/л, ${gmlCaCl2.toFixed(4)} г/мл)`;
// Обновляем названия микроэлементов (аналог CalcConc строки 1662-1669)
const glFe = parseFloat(document.getElementById('conc_glFe')?.value) || 10;
const glMn = parseFloat(document.getElementById('conc_glMn')?.value) || 10;
const glB = parseFloat(document.getElementById('conc_glB')?.value) || 10;
const glZn = parseFloat(document.getElementById('conc_glZn')?.value) || 10;
const glCu = parseFloat(document.getElementById('conc_glCu')?.value) || 10;
const glMo = parseFloat(document.getElementById('conc_glMo')?.value) || 10;
const glCo = parseFloat(document.getElementById('conc_glCo')?.value) || 10;
const glSi = parseFloat(document.getElementById('conc_glSi')?.value) || 10;
const gmlFe = parseFloat(document.getElementById('conc_gmlFe')?.value) || 1.000;
const gmlMn = parseFloat(document.getElementById('conc_gmlMn')?.value) || 1.000;
const gmlB = parseFloat(document.getElementById('conc_gmlB')?.value) || 1.000;
const gmlZn = parseFloat(document.getElementById('conc_gmlZn')?.value) || 1.000;
const gmlCu = parseFloat(document.getElementById('conc_gmlCu')?.value) || 1.000;
const gmlMo = parseFloat(document.getElementById('conc_gmlMo')?.value) || 1.000;
const gmlCo = parseFloat(document.getElementById('conc_gmlCo')?.value) || 1.000;
const gmlSi = parseFloat(document.getElementById('conc_gmlSi')?.value) || 1.000;
const l2Cmplx = document.querySelector('.prep-complex-text');
const l2Fe = document.querySelector('label[for="l2Fe"]');
const l2Mn = document.querySelector('label[for="l2Mn"]');
const l2B = document.querySelector('label[for="l2B"]');
const l2Zn = document.querySelector('label[for="l2Zn"]');
const l2Cu = document.querySelector('label[for="l2Cu"]');
const l2Mo = document.querySelector('label[for="l2Mo"]');
const l2Co = document.querySelector('label[for="l2Co"]');
const l2Si = document.querySelector('label[for="l2Si"]');
if (l2Cmplx) l2Cmplx.textContent = `Комплекс микроэлементов ${weights.Cmplx.toFixed(3)} г. (10 г/л, 1.000 г/мл)`;
if (l2Fe) l2Fe.textContent = `${microNames.Fe} ${weights.Fe.toFixed(3)} г. (${glFe} г/л, ${gmlFe.toFixed(3)} г/мл)`;
if (l2Mn) l2Mn.textContent = `${microNames.Mn} ${weights.Mn.toFixed(3)} г. (${glMn} г/л, ${gmlMn.toFixed(3)} г/мл)`;
if (l2B) l2B.textContent = `${microNames.B} ${weights.B.toFixed(3)} г. (${glB} г/л, ${gmlB.toFixed(3)} г/мл)`;
if (l2Zn) l2Zn.textContent = `${microNames.Zn} ${weights.Zn.toFixed(3)} г. (${glZn} г/л, ${gmlZn.toFixed(3)} г/мл)`;
if (l2Cu) l2Cu.textContent = `${microNames.Cu} ${weights.Cu.toFixed(3)} г. (${glCu} г/л, ${gmlCu.toFixed(3)} г/мл)`;
if (l2Mo) l2Mo.textContent = `${microNames.Mo} ${weights.Mo.toFixed(3)} г. (${glMo} г/л, ${gmlMo.toFixed(3)} г/мл)`;
if (l2Co) l2Co.textContent = `${microNames.Co} ${weights.Co.toFixed(3)} г. (${glCo} г/л, ${gmlCo.toFixed(3)} г/мл)`;
if (l2Si) l2Si.textContent = `${microNames.Si} ${weights.Si.toFixed(3)} г. (${glSi} г/л, ${gmlSi.toFixed(3)} г/мл)`;
}
// Перевод фактических навесок в профиль (аналог btchClick из легаси строки 4269-4301)
function transferToProfile() {
// 1. Пересчитываем граммы из навесок: gSalt = (g2gSalt * glSalt) / (1000 * gmlSalt)
const g2g = (id) => parseFloat(document.getElementById(id)?.value) || 0;
const gl = (id) => parseFloat(document.getElementById(id)?.value) || 0;
const gml = (id) => parseFloat(document.getElementById(id)?.value) || 1;
// Обновляем граммы макроэлементов
document.getElementById('g_CaNO3').value = ((g2g('g2gCaNO3') * gl('conc_glCaNO3')) / (1000 * gml('conc_gmlCaNO3'))).toFixed(5);
document.getElementById('g_KNO3').value = ((g2g('g2gKNO3') * gl('conc_glKNO3')) / (1000 * gml('conc_gmlKNO3'))).toFixed(5);
document.getElementById('g_NH4NO3').value = ((g2g('g2gNH4NO3') * gl('conc_glNH4NO3')) / (1000 * gml('conc_gmlNH4NO3'))).toFixed(5);
document.getElementById('g_MgSO4').value = ((g2g('g2gMgSO4') * gl('conc_glMgSO4')) / (1000 * gml('conc_gmlMgSO4'))).toFixed(5);
document.getElementById('g_KH2PO4').value = ((g2g('g2gKH2PO4') * gl('conc_glKH2PO4')) / (1000 * gml('conc_gmlKH2PO4'))).toFixed(5);
document.getElementById('g_K2SO4').value = ((g2g('g2gK2SO4') * gl('conc_glK2SO4')) / (1000 * gml('conc_gmlK2SO4'))).toFixed(5);
document.getElementById('g_MgNO3').value = ((g2g('g2gMgNO3') * gl('conc_glMgNO3')) / (1000 * gml('conc_gmlMgNO3'))).toFixed(5);
document.getElementById('g_CaCl2').value = ((g2g('g2gCaCl2') * gl('conc_glCaCl2')) / (1000 * gml('conc_gmlCaCl2'))).toFixed(5);
// Обновляем граммы микроэлементов
document.getElementById('g_Fe').value = ((g2g('g2gFe') * gl('conc_glFe')) / (1000 * gml('conc_gmlFe'))).toFixed(5);
document.getElementById('g_Mn').value = ((g2g('g2gMn') * gl('conc_glMn')) / (1000 * gml('conc_gmlMn'))).toFixed(5);
document.getElementById('g_B').value = ((g2g('g2gB') * gl('conc_glB')) / (1000 * gml('conc_gmlB'))).toFixed(5);
document.getElementById('g_Zn').value = ((g2g('g2gZn') * gl('conc_glZn')) / (1000 * gml('conc_gmlZn'))).toFixed(5);
document.getElementById('g_Cu').value = ((g2g('g2gCu') * gl('conc_glCu')) / (1000 * gml('conc_gmlCu'))).toFixed(5);
document.getElementById('g_Mo').value = ((g2g('g2gMo') * gl('conc_glMo')) / (1000 * gml('conc_gmlMo'))).toFixed(5);
document.getElementById('g_Co').value = ((g2g('g2gCo') * gl('conc_glCo')) / (1000 * gml('conc_gmlCo'))).toFixed(5);
document.getElementById('g_Si').value = ((g2g('g2gSi') * gl('conc_glSi')) / (1000 * gml('conc_gmlSi'))).toFixed(5);
// 2. WeghtTomicro - пересчет микроэлементов из граммов
weightToMicro();
// 3. fromWeight - пересчет макроэлементов (вызываем onSaltWeightChange который это делает)
onSaltWeightChange();
// 4. CalculateS - расчет серы (вызывается внутри calcAll)
// 5. CalcKoef - расчет коэффициентов (вызывается внутри calcAll)
// 6. CalcEC - расчет EC (вызывается внутри calcAll)
// 7. genProfile - обновление профиля (вызывается внутри calcAll)
}
// Переменные для автоповтора крутилок
let priceAdjustInterval = null;
let priceAdjustTimeout = null;
// Функция для изменения цены с помощью кастомных крутилок
function adjustPrice(inputId, delta) {
const input = document.getElementById(inputId);
if (!input) return;
const currentValue = parseFloat(input.value) || 0;
const newValue = Math.max(0, currentValue + delta); // Не даем уйти в минус
input.value = newValue.toFixed(4);
updatePriceResults();
}
// Начать автоповтор при удержании кнопки
function startPriceAdjust(inputId, delta) {
// Первое нажатие
adjustPrice(inputId, delta);
// Задержка перед началом автоповтора
priceAdjustTimeout = setTimeout(() => {
// Автоповтор каждые 50ms
priceAdjustInterval = setInterval(() => {
adjustPrice(inputId, delta);
}, 50);
}, 300); // Начинаем автоповтор через 300ms
}
// Остановить автоповтор
function stopPriceAdjust() {
if (priceAdjustTimeout) {
clearTimeout(priceAdjustTimeout);
priceAdjustTimeout = null;
}
if (priceAdjustInterval) {
clearInterval(priceAdjustInterval);
priceAdjustInterval = null;
}
}
// Инициализация обработчиков для кастомных крутилок (делегирование событий)
function initPriceSpinners() {
document.addEventListener('mousedown', (e) => {
const button = e.target.closest('.price-spinner button');
if (!button) return;
const inputId = button.getAttribute('data-input');
const delta = parseFloat(button.getAttribute('data-delta'));
if (inputId && !isNaN(delta)) {
startPriceAdjust(inputId, delta);
}
});
document.addEventListener('mouseup', (e) => {
if (e.target.closest('.price-spinner button')) {
stopPriceAdjust();
}
});
document.addEventListener('mouseleave', (e) => {
if (e.target.closest('.price-spinner button')) {
stopPriceAdjust();
}
});
}
// Функция обновления результатов цен (аналог price() из легаси строки 1073-1147)
function updatePriceResults() {
// Получаем названия солей и микроэлементов (аналог knCaNO3.caption, lFe.caption из легаси)
const saltNames = updateSaltNames();
const microNames = updateMicroNames();
// Получаем веса удобрений из вкладки "Расчет"
const weights = {
CaNO3: parseFloat(document.getElementById('g_CaNO3')?.value) || 0,
KNO3: parseFloat(document.getElementById('g_KNO3')?.value) || 0,
NH4NO3: parseFloat(document.getElementById('g_NH4NO3')?.value) || 0,
MgNO3: parseFloat(document.getElementById('g_MgNO3')?.value) || 0,
CaCl2: parseFloat(document.getElementById('g_CaCl2')?.value) || 0,
MgSO4: parseFloat(document.getElementById('g_MgSO4')?.value) || 0,
KH2PO4: parseFloat(document.getElementById('g_KH2PO4')?.value) || 0,
K2SO4: parseFloat(document.getElementById('g_K2SO4')?.value) || 0,
Fe: parseFloat(document.getElementById('g_Fe')?.value) || 0,
Mn: parseFloat(document.getElementById('g_Mn')?.value) || 0,
B: parseFloat(document.getElementById('g_B')?.value) || 0,
Zn: parseFloat(document.getElementById('g_Zn')?.value) || 0,
Cu: parseFloat(document.getElementById('g_Cu')?.value) || 0,
Mo: parseFloat(document.getElementById('g_Mo')?.value) || 0,
Co: parseFloat(document.getElementById('g_Co')?.value) || 0,
Si: parseFloat(document.getElementById('g_Si')?.value) || 0,
Cmplx: parseFloat(document.getElementById('g_Cmplx')?.value) || 0
};
// Получаем цены за грамм
const prices = {
CaNO3: parseFloat(document.getElementById('price_CaNO3')?.value) || 0,
KNO3: parseFloat(document.getElementById('price_KNO3')?.value) || 0,
NH4NO3: parseFloat(document.getElementById('price_NH4NO3')?.value) || 0,
MgNO3: parseFloat(document.getElementById('price_MgNO3')?.value) || 0,
CaCl2: parseFloat(document.getElementById('price_CaCl2')?.value) || 0,
MgSO4: parseFloat(document.getElementById('price_MgSO4')?.value) || 0,
KH2PO4: parseFloat(document.getElementById('price_KH2PO4')?.value) || 0,
K2SO4: parseFloat(document.getElementById('price_K2SO4')?.value) || 0,
Fe: parseFloat(document.getElementById('price_Fe')?.value) || 0,
Mn: parseFloat(document.getElementById('price_Mn')?.value) || 0,
B: parseFloat(document.getElementById('price_B')?.value) || 0,
Zn: parseFloat(document.getElementById('price_Zn')?.value) || 0,
Cu: parseFloat(document.getElementById('price_Cu')?.value) || 0,
Mo: parseFloat(document.getElementById('price_Mo')?.value) || 0,
Co: parseFloat(document.getElementById('price_Co')?.value) || 0,
Si: parseFloat(document.getElementById('price_Si')?.value) || 0,
Cmplx: parseFloat(document.getElementById('price_Cmplx')?.value) || 0
};
// Проверяем использование комплекса микроэлементов
const useComplex = document.getElementById('use_complex')?.checked || false;
let totalPrice = 0;
// Рассчитываем цены для макроэлементов (аналог строк 1087-1094)
['CaNO3', 'KNO3', 'NH4NO3', 'MgNO3', 'CaCl2', 'MgSO4', 'KH2PO4', 'K2SO4'].forEach(key => {
const price = Math.round(prices[key] * weights[key] * 100) / 100;
totalPrice += price;
const resultEl = document.getElementById(`price_result_${key}`);
const nameEl = resultEl?.previousElementSibling; // элемент с классом price-name
if (resultEl) {
// Обновляем название (аналог knCaNO3.caption)
if (nameEl && nameEl.classList.contains('price-name') && saltNames[key]) {
nameEl.textContent = saltNames[key];
}
// Обновляем результат (вес + цена)
resultEl.textContent = `${(Math.round(weights[key] * 1000) / 1000).toFixed(3)} г. цена: ${price.toFixed(2)}`;
}
});
// Рассчитываем цены для микроэлементов в зависимости от режима
if (useComplex) {
// Комплекс микроэлементов (аналог строки 1108)
const price = Math.round(prices.Cmplx * weights.Cmplx * 100) / 100;
totalPrice += price;
const resultEl = document.getElementById('price_result_Cmplx');
const nameEl = resultEl?.previousElementSibling;
if (resultEl) {
// Название комплекса статичное "Комплекс микроэлементов"
if (nameEl && nameEl.classList.contains('price-name')) {
nameEl.textContent = 'Комплекс микроэлементов';
}
resultEl.textContent = `${(Math.round(weights.Cmplx * 1000) / 1000).toFixed(3)} г. цена: ${price.toFixed(2)}`;
}
} else {
// Отдельные микроэлементы (аналог строк 1133-1140)
['Fe', 'Mn', 'B', 'Zn', 'Cu', 'Mo', 'Co', 'Si'].forEach(key => {
const price = Math.round(prices[key] * weights[key] * 100) / 100;
totalPrice += price;
const resultEl = document.getElementById(`price_result_${key}`);
const nameEl = resultEl?.previousElementSibling;
if (resultEl) {
// Обновляем название (аналог lFe.caption)
if (nameEl && nameEl.classList.contains('price-name') && microNames[key]) {
nameEl.textContent = microNames[key];
}
// Обновляем результат (вес + цена)
resultEl.textContent = `${(Math.round(weights[key] * 1000) / 1000).toFixed(3)} г. цена: ${price.toFixed(2)}`;
}
});
}
// Обновляем общую стоимость и цену за литр
const volume = parseFloat(document.getElementById('volume')?.value) || 1;
const pricePerLiter = volume > 0 ? Math.round(totalPrice / volume * 100) / 100 : 0;
// Обновляем в подвкладке "Цена"
document.getElementById('total_price').textContent = (Math.round(totalPrice * 100) / 100).toFixed(2);
document.getElementById('price_per_liter').textContent = pricePerLiter.toFixed(2);
// Обновляем внизу страницы (аналог lprice из легаси строка 1145)
const bottomTotalPrice = document.getElementById('bottom_total_price');
const bottomPricePerLiter = document.getElementById('bottom_price_per_liter');
if (bottomTotalPrice) bottomTotalPrice.textContent = (Math.round(totalPrice * 100) / 100).toFixed(2);
if (bottomPricePerLiter) bottomPricePerLiter.textContent = pricePerLiter.toFixed(2);
}
// Обработчики событий для полей цен и тары
function addPriceEventListeners() {
const priceInputs = document.querySelectorAll('#price input[type="number"]');
priceInputs.forEach(input => {
input.addEventListener('input', () => {
updatePriceResults();
});
// Форматирование до 4 знаков после запятой при потере фокуса
input.addEventListener('blur', () => {
const value = parseFloat(input.value) || 0;
input.value = value.toFixed(4);
});
});
// Обработчики для полей тары
const tankA = document.getElementById('tank_A');
const tankB = document.getElementById('tank_B');
// Обработчики для полей ввода в подвкладке Изготовление
const preparationInputs = document.querySelectorAll('#preparation input[type="text"]');
preparationInputs.forEach(input => {
input.addEventListener('input', () => {
// Здесь можно добавить логику для обработки изменений полей ввода
console.log(`Field ${input.id} changed to: ${input.value}`);
});
});
// Обработчики для чекбоксов в подвкладке Изготовление
const preparationCheckboxes = document.querySelectorAll('#preparation input[type="checkbox"]');
preparationCheckboxes.forEach(checkbox => {
checkbox.addEventListener('change', () => {
// Здесь можно добавить логику для обработки изменений чекбоксов
console.log(`Checkbox ${checkbox.id} changed to: ${checkbox.checked}`);
});
});
// Обработчик для чекбокса комплекса микроэлементов (как в legacy)
const cbCmplx = document.getElementById('cbCmplx');
if (cbCmplx) {
cbCmplx.addEventListener('change', () => {
toggleMicroElements(cbCmplx.checked);
});
}
// Обработчики для полей миксера
const addrMixer = document.getElementById('addrMixer');
const nmix = document.getElementById('nmix');
if (addrMixer) {
addrMixer.addEventListener('input', () => {
console.log(`Mixer address changed to: ${addrMixer.value}`);
});
}
if (nmix) {
nmix.addEventListener('input', () => {
console.log(`Mix number changed to: ${nmix.value}`);
});
}
// Обработчики для кнопок изготовления
const btnManufacture = document.getElementById('btnManufacture');
const btnToProfile = document.getElementById('btnToProfile');
const btnToJournal = document.getElementById('btnToJournal');
if (btnManufacture) {
btnManufacture.addEventListener('click', () => {
manufactureSolution();
});
}
if (btnToProfile) {
btnToProfile.addEventListener('click', () => {
transferToProfile();
});
}
if (btnToJournal) {
btnToJournal.addEventListener('click', () => {
addToJournal();
});
}
}
// Функции обработки кнопок изготовления
function manufactureSolution() {
// Пересчитываем веса перед отправкой (как в легаси перед Button2Click вызывается CalcAll -> CalcConc)
calcConcentrates();
const addrMixerInput = document.getElementById('addrMixer');
const nmixInput = document.getElementById('nmix');
if (!addrMixerInput || !nmixInput) {
console.warn('Mixer controls not found');
return;
}
const addrMixer = addrMixerInput.value.trim();
const nmix = nmixInput.value.trim();
if (!addrMixer) {
alert('Укажите адрес миксера');
return;
}
if (!nmix) {
alert('Укажите номер раствора (s)');
return;
}
let mixlink = `http://${addrMixer}/?`;
mixlink += `s=${encodeURIComponent(nmix)}&`;
const macroPairs = [
['mCaNO3', 'g2gCaNO3'],
['mKNO3', 'g2gKNO3'],
['mNH4NO3', 'g2gNH4NO3'],
['mMgNO3', 'g2gMgNO3'],
['mCaCl2', 'g2gCaCl2'],
['mMgSO4', 'g2gMgSO4'],
['mKH2PO4', 'g2gKH2PO4'],
['mK2SO4', 'g2gK2SO4']
];
macroPairs.forEach(([pumpId, valueId]) => {
const pumpInput = document.getElementById(pumpId);
const valueInput = document.getElementById(valueId);
if (!pumpInput || !valueInput) {
return;
}
const pump = pumpInput.value.trim();
if (!pump) {
return;
}
const grams = valueInput.value.trim();
if (grams === '' || grams === '0' || grams === '0.00') {
return;
}
mixlink += `${encodeURIComponent(pump)}=${encodeURIComponent(grams)}&`;
});
const useComplex = document.getElementById('use_complex')?.checked;
if (useComplex) {
const pumpInput = document.getElementById('mCmplx');
const valueInput = document.getElementById('g2gCmplx');
if (pumpInput && valueInput) {
const pump = pumpInput.value.trim();
const grams = valueInput.value.trim();
if (pump && grams !== '' && grams !== '0' && grams !== '0.00') {
mixlink += `${encodeURIComponent(pump)}=${encodeURIComponent(grams)}&`;
}
}
} else {
const microPairs = [
['mFe', 'g2gFe'],
['mMn', 'g2gMn'],
['mB', 'g2gB'],
['mZn', 'g2gZn'],
['mCu', 'g2gCu'],
['mMo', 'g2gMo'],
['mCo', 'g2gCo'],
['mSi', 'g2gSi']
];
microPairs.forEach(([pumpId, valueId]) => {
const pumpInput = document.getElementById(pumpId);
const valueInput = document.getElementById(valueId);
if (!pumpInput || !valueInput) {
return;
}
const pump = pumpInput.value.trim();
if (!pump) {
return;
}
const grams = valueInput.value.trim();
if (grams === '' || grams === '0' || grams === '0.00') {
return;
}
mixlink += `${encodeURIComponent(pump)}=${encodeURIComponent(grams)}&`;
});
}
if (mixlink.endsWith('&')) {
mixlink = mixlink.slice(0, -1);
}
window.open(mixlink, '_blank');
}
// Автозапись в журнал при изготовлении (как в легаси Button9Click)
function addToJournal() {
// Получаем текущую дату в формате yyyy-mm-dd
const currentDate = new Date().toISOString().split('T')[0];
// Получаем объем
const volume = parseFloat(document.getElementById('volume')?.value) || 10;
// Формируем описание (как в легаси: m1.Text)
const desc = `Автозапись. Изготовлен раствор на ${volume} литров.`;
// Берем строку профиля из поля profile-string (аналог profile.Caption в легаси)
const profileStr = document.getElementById('profile-string')?.value || '';
// Создаем запись журнала (как в легаси: date=...;...;...)
const journalEntry = {
date: currentDate,
desc: desc,
memo: desc, // Совместимость
profile: profileStr
};
// Добавляем в журнал
journalEntries.push(journalEntry);
// Сортируем журнал по дате (как в легаси DStr.Sort)
journalEntries.sort((a, b) => {
const dateA = new Date(a.date);
const dateB = new Date(b.date);
return dateB - dateA; // Новые записи сверху
});
// Обновляем список журнала
updateJournalList();
// Переключаемся на вкладку "Файл" чтобы показать результат
switchTab('file', document.querySelector('[onclick*="file"]'));
alert('Автозапись добавлена в журнал');
}
// Переключение между комплексом микроэлементов и отдельными элементами (как в legacy)
function toggleMicroElements(useComplex) {
const complexElements = ['mCmplx', 'cbCmplx', 'l2Cmplx', 'g2gCmplx'];
const individualElements = ['mFe', 'cbFe', 'l2Fe', 'g2gFe', 'mMn', 'cbMn', 'l2Mn', 'g2gMn',
'mB', 'cbB', 'l2B', 'g2gB', 'mZn', 'cbZn', 'l2Zn', 'g2gZn',
'mCu', 'cbCu', 'l2Cu', 'g2gCu', 'mMo', 'cbMo', 'l2Mo', 'g2gMo',
'mCo', 'cbCo', 'l2Co', 'g2gCo', 'mSi', 'cbSi', 'l2Si', 'g2gSi'];
if (useComplex) {
// Показываем комплекс, скрываем отдельные элементы
complexElements.forEach(id => {
const element = document.getElementById(id);
if (element) element.style.display = 'block';
});
individualElements.forEach(id => {
const element = document.getElementById(id);
if (element) element.style.display = 'none';
});
} else {
// Показываем отдельные элементы, скрываем комплекс
complexElements.forEach(id => {
const element = document.getElementById(id);
if (element) element.style.display = 'none';
});
individualElements.forEach(id => {
const element = document.getElementById(id);
if (element) element.style.display = '';
});
}
}
function saveData() {
saveProfile();
}
function resetSalts() {
// Возврат к стандартным составам солей
document.getElementById('salt_CaNO3_Ca').value = 16.972;
document.getElementById('salt_CaNO3_NO3').value = 11.863;
document.getElementById('salt_CaNO3_NH4').value = 0.000;
document.getElementById('salt_KNO3_K').value = 38.672;
document.getElementById('salt_KNO3_NO3').value = 13.854;
document.getElementById('salt_NH4NO3_NH4').value = 17.499;
document.getElementById('salt_NH4NO3_NO3').value = 17.499;
document.getElementById('salt_MgSO4_Mg').value = 9.861;
document.getElementById('salt_MgSO4_S').value = 13.010;
document.getElementById('salt_KH2PO4_K').value = 28.731;
document.getElementById('salt_KH2PO4_P').value = 22.761;
document.getElementById('salt_K2SO4_K').value = 44.874;
document.getElementById('salt_K2SO4_S').value = 18.401;
document.getElementById('salt_MgNO3_Mg').value = 9.479;
document.getElementById('salt_MgNO3_NO3').value = 10.925;
document.getElementById('salt_CaCl2_Ca').value = 18.294;
document.getElementById('salt_CaCl2_Cl').value = 32.366;
onSaltCompositionChange();
}
function resetProfile() {
currentProfile = {
N: 220, NO3: 200, NH4: 20, P: 40, K: 180, Ca: 200, Mg: 50, S: 73.049, Cl: 0, EC: 2.102,
Fe: 2000, Mn: 550, B: 500, Zn: 330, Cu: 63, Mo: 63, Co: 0, Si: 0,
volume: 10.0
};
applyProfile();
}
function recalculate() {
// Полный сброс к начальным значениям через имитацию загрузки дефолтного файла
// Очистка localStorage
localStorage.removeItem('hpg_profile');
localStorage.removeItem('hpg_journal');
// Очистка журнала
journalEntries = [];
updateJournalList();
// Создаем ПОЛНЫЙ дефолтный файл со ВСЕМИ параметрами
const defaultFileContent = `version=Hydroponic Profile Generator HTML5
Comment=По умолчанию
N=220
NO3=200
NH4=20
P=40
K=180
Ca=200
Mg=50
S=73.049
Cl=0
Fe=2000
Mn=550
B=500
Zn=330
Cu=63
Mo=63
Co=0
Si=0
CaNO3_Ca=16.972
CaNO3_NO3=11.863
CaNO3_NH4=0.000
KNO3_K=38.672
KNO3_NO3=13.854
NH4NO3_NH4=17.499
NH4NO3_NO3=17.499
MgSO4_Mg=9.861
MgSO4_S=13.010
KH2PO4_K=28.731
KH2PO4_P=22.761
K2SO4_K=44.874
K2SO4_S=18.401
MgNO3_Mg=9.479
MgNO3_NO3=10.926
CaCl2_Ca=18.294
CaCl2_Cl=32.366
dFe=20.1000
dMn=36.4000
dB=17.5000
dZn=22.7000
dCu=25.5000
dMo=54.3000
dCo=13.0000
dSi=7.0000
glCaNO3=600.0
gmlCaNO3=1.0000
glKNO3=250.0
gmlKNO3=1.0000
glNH4NO3=100.0
gmlNH4NO3=1.0000
glMgNO3=500.0
gmlMgNO3=1.0000
glMgSO4=600.0
gmlMgSO4=1.0000
glK2SO4=100.0
gmlK2SO4=1.0000
glKH2PO4=150.0
gmlKH2PO4=1.0000
glCaCl2=100.0
gmlCaCl2=1.0000
glCmplx=10.00
gmlCmplx=1.000
glFe=10.00
gmlFe=1.000
glMn=10.00
gmlMn=1.000
glB=10.00
gmlB=1.000
glZn=10.00
gmlZn=1.000
glCu=10.00
gmlCu=1.000
glMo=10.00
gmlMo=1.000
glCo=10.00
gmlCo=1.000
glSi=10.00
gmlSi=1.000
chkComplex=False
chK2SO4=False
chMgNO3=False
V=10.0
mCaNO3=p1
mKNO3=p2
mNH4NO3=p3
mMgNO3=
mMgSO4=p4
mKH2PO4=p5
mK2SO4=p6
mCaCl2=
mCmplx=
mFe=
mMn=
mB=
mZn=
mCu=
mMo=
mCo=
mSi=
addrMixer=mixer.local
nmix=1
tAml=500
tBml=500
cgCaNO3=0.4000
cgKNO3=0.3500
cgNH4NO3=0.2500
cgMgNO3=0.3500
cgMgSO4=0.1200
cgK2SO4=0.3200
cgKH2PO4=0.4000
cgCaCl2=7.7000
cgCmplx=0.0500
cgFe=3.0000
cgMn=0.3000
cgB=0.4000
cgZn=0.1500
cgCu=0.4000
cgMo=3.0000
cgCo=2.3000
cgSi=0.2700`;
// Парсим дефолтный файл - это установит ВСЕ параметры
parseFullFile(defaultFileContent);
// Сброс полей вкладки "Файл"
document.getElementById('file_filename').value = 'default.hpg';
document.getElementById('file_comment').value = 'По умолчанию';
document.getElementById('file_journal_desc').value = '';
document.getElementById('file_journal_date').value = '';
document.getElementById('file_journal_memo').value = '';
document.getElementById('file_profile_info').value = ''; // Используем .value для input
clearFileProfileDetails();
// Пересчитываем все
calcAll();
calcConcentrates();
updatePriceResults();
}
// Вычисление контрольной суммы файла (SHA-256, последние 3 символа)
async function calculateChecksum() {
try {
const response = await fetch(window.location.href);
const text = await response.text();
const msgBuffer = new TextEncoder().encode(text);
const hashBuffer = await crypto.subtle.digest('SHA-256', msgBuffer);
const hashArray = Array.from(new Uint8Array(hashBuffer));
const hashHex = hashArray.map(b => b.toString(16).padStart(2, '0')).join('');
return hashHex.slice(-3); // Последние 3 символа
} catch (error) {
console.error('Ошибка вычисления контрольной суммы:', error);
return '---';
}
}
// Определение типа устройства
function detectDevice() {
const isMobile = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
const isTablet = /(tablet|ipad|playbook|silk)|(android(?!.*mobi))/i.test(navigator.userAgent);
const screenWidth = window.innerWidth;
let deviceType = 'Desktop';
if (isMobile && !isTablet) {
deviceType = 'Mobile';
} else if (isTablet) {
deviceType = 'Tablet';
}
console.log('=== HPG Device Info ===');
console.log('Device Type:', deviceType);
console.log('Screen Width:', screenWidth + 'px');
console.log('User Agent:', navigator.userAgent);
console.log('Touch Support:', 'ontouchstart' in window);
console.log('======================');
// Добавляем класс к body для дополнительной стилизации
document.body.classList.add(deviceType.toLowerCase());
return {
type: deviceType,
isMobile: isMobile && !isTablet,
isTablet: isTablet,
isDesktop: !isMobile && !isTablet,
screenWidth: screenWidth
};
}
// Инициализация при загрузке
window.onload = async function() {
// Определяем тип устройства
const device = detectDevice();
// Инициализация корректора
initCorrector();
const k2so4Checkbox = document.getElementById('use_K2SO4');
const mgno3Checkbox = document.getElementById('use_MgNO3');
useK2SO4 = k2so4Checkbox.checked;
useMgNO3 = mgno3Checkbox.checked;
syncSaltCompositions();
updateSaltLabels();
updateSaltNames(); // Обновляем названия солей при загрузке
updateMicroNames(); // Обновляем названия микроэлементов при загрузке
calcAll();
onMicroChange();
// Инициализация микро вкладки
onComplexChange();
// Инициализация концентратов
calcConcentrates();
addPriceEventListeners();
initPriceSpinners(); // Инициализация кастомных крутилок
// Инициализация подвкладки Изготовление (по умолчанию отдельные микроэлементы)
toggleMicroElements(false);
// Восстановить активную вкладку из localStorage
const savedTab = localStorage.getItem('activeTab');
if (savedTab && document.getElementById(savedTab)) {
switchTab(savedTab);
// Если активная вкладка "концентраты", восстановить внутреннюю вкладку
if (savedTab === 'concentrates') {
const savedInnerTab = localStorage.getItem('activeInnerTab');
if (savedInnerTab && document.getElementById(savedInnerTab)) {
switchInnerTab(savedInnerTab);
}
}
}
// Вычисляем и добавляем контрольную сумму к версии
const checksum = await calculateChecksum();
// Обновляем версию на вкладке Справка (добавляем после даты)
const helpTab = document.getElementById('help');
if (helpTab) {
const paragraphs = helpTab.querySelectorAll('p');
paragraphs.forEach(p => {
if (p.innerHTML && p.innerHTML.includes('Версия:')) {
// Добавляем контрольную сумму после даты: "10.10.2025)" -> "10.10.2025-XXXXXX)"
p.innerHTML = p.innerHTML.replace('10.10.2025)', `10.10.2025-${checksum})`);
}
});
}
// Обновляем версию внизу страницы
const versionText = document.querySelector('.version-text');
if (versionText) {
versionText.textContent = versionText.textContent.replace('0.221', `0.221-${checksum}`);
}
};
// ========== МОБИЛЬНАЯ ВЕРСИЯ - ФУНКЦИИ ==========
function loadFileMobile() {
const input = document.createElement('input');
input.type = 'file';
input.accept = '.hpg,.txt';
input.onchange = (e) => {
const file = e.target.files[0];
if (file) {
const reader = new FileReader();
reader.onload = (event) => {
try {
const content = event.target.result;
parseMobileProfile(content);
autoCalculateMobile(); // Автоматически пересчитываем
alert(`✅ Профиль загружен: ${file.name}`);
} catch (err) {
console.error('Ошибка загрузки файла:', err);
alert('❌ Ошибка загрузки файла');
}
};
reader.readAsText(file);
}
};
input.click();
}
function parseMobileProfile(content) {
const lines = content.split('\n');
lines.forEach(line => {
line = line.trim();
if (!line || !line.includes('=') || line.startsWith('#') || line.startsWith('date=')) return;
const [key, value] = line.split('=');
if (!value) return;
const numValue = parseFloat(value.trim());
// Макроэлементы
if (key === 'N' && !isNaN(numValue)) document.getElementById('m_N').value = numValue;
else if (key === 'P' && !isNaN(numValue)) document.getElementById('m_P').value = numValue;
else if (key === 'K' && !isNaN(numValue)) document.getElementById('m_K').value = numValue;
else if (key === 'Ca' && !isNaN(numValue)) document.getElementById('m_Ca').value = numValue;
else if (key === 'Mg' && !isNaN(numValue)) document.getElementById('m_Mg').value = numValue;
else if (key === 'S' && !isNaN(numValue)) document.getElementById('m_S').value = numValue;
// Микроэлементы
else if (key === 'Fe' && !isNaN(numValue)) document.getElementById('m_Fe').value = numValue;
else if (key === 'Mn' && !isNaN(numValue)) document.getElementById('m_Mn').value = numValue;
else if (key === 'B' && !isNaN(numValue)) document.getElementById('m_B').value = numValue;
else if (key === 'Zn' && !isNaN(numValue)) document.getElementById('m_Zn').value = numValue;
else if (key === 'Cu' && !isNaN(numValue)) document.getElementById('m_Cu').value = numValue;
else if (key === 'Mo' && !isNaN(numValue)) document.getElementById('m_Mo').value = numValue;
// Объем
else if (key === 'V' && !isNaN(numValue)) document.getElementById('m_volume').value = numValue;
});
}
// Переключение аккордеонов
function toggleAccordion(id) {
const content = document.getElementById('content-' + id);
const icon = document.getElementById('icon-' + id);
if (content.classList.contains('open')) {
content.classList.remove('open');
icon.classList.remove('open');
} else {
content.classList.add('open');
icon.classList.add('open');
}
}
// Автоматический расчет при изменении любого поля
function autoCalculateMobile() {
calculateMobile();
updateMobileRatios();
}
// Обновление соотношений элементов
function updateMobileRatios() {
const N = parseFloat(document.getElementById('m_N').value) || 0;
const P = parseFloat(document.getElementById('m_P').value) || 0;
const K = parseFloat(document.getElementById('m_K').value) || 0;
const Ca = parseFloat(document.getElementById('m_Ca').value) || 0;
const Mg = parseFloat(document.getElementById('m_Mg').value) || 0;
const S = parseFloat(document.getElementById('m_S').value) || 0;
if (P === 0) return; // Избегаем деления на ноль
const ratios = [
{ label: 'N:P', value: (N/P).toFixed(2), optimal: N/P >= 5.0 && N/P <= 6.0 },
{ label: 'N:K', value: (N/K).toFixed(2), optimal: N/K >= 1.1 && N/K <= 1.3 },
{ label: 'K:P', value: (K/P).toFixed(2), optimal: K/P >= 4.0 && K/P <= 5.0 },
{ label: 'K:Ca', value: (K/Ca).toFixed(2), optimal: K/Ca >= 0.8 && K/Ca <= 1.2 },
{ label: 'K:Mg', value: (K/Mg).toFixed(2), optimal: K/Mg >= 3.0 && K/Mg <= 4.0 },
{ label: 'Ca:P', value: (Ca/P).toFixed(2), optimal: Ca/P >= 4.5 && Ca/P <= 5.5 },
{ label: 'Ca:Mg', value: (Ca/Mg).toFixed(2), optimal: Ca/Mg >= 3.5 && Ca/Mg <= 4.5 },
{ label: 'Ca:K', value: (Ca/K).toFixed(2), optimal: Ca/K >= 0.8 && Ca/K <= 1.2 },
{ label: 'Mg:P', value: (Mg/P).toFixed(2), optimal: Mg/P >= 1.0 && Mg/P <= 1.5 },
{ label: 'Mg:S', value: (Mg/S).toFixed(2), optimal: Mg/S >= 0.6 && Mg/S <= 0.8 },
{ label: 'S:P', value: (S/P).toFixed(2), optimal: S/P >= 1.5 && S/P <= 2.0 },
{ label: 'S:K', value: (S/K).toFixed(2), optimal: S/K >= 0.3 && S/K <= 0.5 }
];
let html = '<div class="ratio-grid">';
ratios.forEach(r => {
const className = r.optimal ? 'ratio-item optimal' : 'ratio-item';
html += `<div class="${className}">
<div class="ratio-label">${r.label}</div>
<div class="ratio-value">${r.value}</div>
</div>`;
});
html += '</div>';
document.getElementById('mobile-ratios-content').innerHTML = html;
}
function calculateMobile() {
// Получаем значения из полей
const N = parseFloat(document.getElementById('m_N').value) || 0;
const P = parseFloat(document.getElementById('m_P').value) || 0;
const K = parseFloat(document.getElementById('m_K').value) || 0;
const Ca = parseFloat(document.getElementById('m_Ca').value) || 0;
const Mg = parseFloat(document.getElementById('m_Mg').value) || 0;
const S = parseFloat(document.getElementById('m_S').value) || 0;
const Fe = parseFloat(document.getElementById('m_Fe').value) || 0;
const Mn = parseFloat(document.getElementById('m_Mn').value) || 0;
const B = parseFloat(document.getElementById('m_B').value) || 0;
const Zn = parseFloat(document.getElementById('m_Zn').value) || 0;
const Cu = parseFloat(document.getElementById('m_Cu').value) || 0;
const Mo = parseFloat(document.getElementById('m_Mo').value) || 0;
const volume = parseFloat(document.getElementById('m_volume').value) || 10;
// Используем те же функции расчета что и в десктопной версии
// Устанавливаем значения в основные поля
document.getElementById('N').value = N;
document.getElementById('P').value = P;
document.getElementById('K').value = K;
document.getElementById('Ca').value = Ca;
document.getElementById('Mg').value = Mg;
document.getElementById('S').value = S;
document.getElementById('Fe').value = Fe;
document.getElementById('Mn').value = Mn;
document.getElementById('B').value = B;
document.getElementById('Zn').value = Zn;
document.getElementById('Cu').value = Cu;
document.getElementById('Mo').value = Mo;
document.getElementById('volume').value = volume;
// Вызываем расчет
calcAll();
onMicroChange();
// Получаем результаты
const results = [];
// Макросоли (с подчеркиванием!)
const gCaNO3 = parseFloat(document.getElementById('g_CaNO3')?.value) || 0;
const gKNO3 = parseFloat(document.getElementById('g_KNO3')?.value) || 0;
const gNH4NO3 = parseFloat(document.getElementById('g_NH4NO3')?.value) || 0;
const gMgSO4 = parseFloat(document.getElementById('g_MgSO4')?.value) || 0;
const gKH2PO4 = parseFloat(document.getElementById('g_KH2PO4')?.value) || 0;
const gK2SO4 = parseFloat(document.getElementById('g_K2SO4')?.value) || 0;
const gMgNO3 = parseFloat(document.getElementById('g_MgNO3')?.value) || 0;
const gCaCl2 = parseFloat(document.getElementById('g_CaCl2')?.value) || 0;
// Микроэлементы (с подчеркиванием!)
const gFe = parseFloat(document.getElementById('g_Fe')?.value) || 0;
const gMn = parseFloat(document.getElementById('g_Mn')?.value) || 0;
const gB = parseFloat(document.getElementById('g_B')?.value) || 0;
const gZn = parseFloat(document.getElementById('g_Zn')?.value) || 0;
const gCu = parseFloat(document.getElementById('g_Cu')?.value) || 0;
const gMo = parseFloat(document.getElementById('g_Mo')?.value) || 0;
let html = '<div style="font-size: 14px;">';
html += '<h4 style="margin: 10px 0; color: #2e7d32;">Макросоли:</h4>';
if (gCaNO3 > 0) html += `<div style="padding: 5px 0; border-bottom: 1px solid #ddd;"><strong>Ca(NO3)2·4H2O:</strong> ${gCaNO3.toFixed(2)} г</div>`;
if (gKNO3 > 0) html += `<div style="padding: 5px 0; border-bottom: 1px solid #ddd;"><strong>KNO3:</strong> ${gKNO3.toFixed(2)} г</div>`;
if (gNH4NO3 > 0) html += `<div style="padding: 5px 0; border-bottom: 1px solid #ddd;"><strong>NH4NO3:</strong> ${gNH4NO3.toFixed(2)} г</div>`;
if (gMgSO4 > 0) html += `<div style="padding: 5px 0; border-bottom: 1px solid #ddd;"><strong>MgSO4·7H2O:</strong> ${gMgSO4.toFixed(2)} г</div>`;
if (gKH2PO4 > 0) html += `<div style="padding: 5px 0; border-bottom: 1px solid #ddd;"><strong>KH2PO4:</strong> ${gKH2PO4.toFixed(2)} г</div>`;
if (gK2SO4 > 0) html += `<div style="padding: 5px 0; border-bottom: 1px solid #ddd;"><strong>K2SO4:</strong> ${gK2SO4.toFixed(2)} г</div>`;
if (gMgNO3 > 0) html += `<div style="padding: 5px 0; border-bottom: 1px solid #ddd;"><strong>Mg(NO3)2·6H2O:</strong> ${gMgNO3.toFixed(2)} г</div>`;
if (gCaCl2 > 0) html += `<div style="padding: 5px 0; border-bottom: 1px solid #ddd;"><strong>CaCl2·6H2O:</strong> ${gCaCl2.toFixed(2)} г</div>`;
html += '<h4 style="margin: 15px 0 10px 0; color: #2e7d32;">Микроэлементы:</h4>';
if (gFe > 0) html += `<div style="padding: 5px 0; border-bottom: 1px solid #ddd;"><strong>Fe-DTPA (13%):</strong> ${gFe.toFixed(3)} г</div>`;
if (gMn > 0) html += `<div style="padding: 5px 0; border-bottom: 1px solid #ddd;"><strong>MnSO4·5H2O:</strong> ${gMn.toFixed(3)} г</div>`;
if (gB > 0) html += `<div style="padding: 5px 0; border-bottom: 1px solid #ddd;"><strong>H3BO3:</strong> ${gB.toFixed(3)} г</div>`;
if (gZn > 0) html += `<div style="padding: 5px 0; border-bottom: 1px solid #ddd;"><strong>ZnSO4·7H2O:</strong> ${gZn.toFixed(3)} г</div>`;
if (gCu > 0) html += `<div style="padding: 5px 0; border-bottom: 1px solid #ddd;"><strong>CuSO4·5H2O:</strong> ${gCu.toFixed(3)} г</div>`;
if (gMo > 0) html += `<div style="padding: 5px 0; border-bottom: 1px solid #ddd;"><strong>(NH4)6Mo7O24·4H2O:</strong> ${gMo.toFixed(3)} г</div>`;
const totalSalts = parseFloat(document.getElementById('total-salts')?.value) || 0;
html += `<div style="margin-top: 15px; padding: 10px; background: #fff; border-radius: 5px; text-align: center;">`;
html += `<strong style="font-size: 16px;">Всего солей: ${totalSalts.toFixed(2)} г</strong>`;
html += `</div>`;
html += '</div>';
document.getElementById('mobile-results-content').innerHTML = html;
}
</script>
<!-- МОБИЛЬНАЯ ВЕРСИЯ (показывается только на экранах ≤768px) -->
<div class="mobile-version">
<div class="mobile-header">
🌱 HPG - Hydroponic Profile Generator
</div>
<!-- Информационный баннер -->
<div style="background: #fff3cd; border-left: 4px solid #ffc107; padding: 12px; margin: 10px; border-radius: 5px; font-size: 13px; line-height: 1.5;">
<strong>📱 Упрощенная мобильная версия</strong><br>
Базовый расчет навесок по профилю. Для полного функционала (настройка составов солей, концентраты, корректор, журнал) откройте на компьютере или планшете в горизонтальной ориентации (≥768px).
</div>
<!-- Кнопка загрузки файла -->
<div style="background: white; padding: 15px; border-radius: 8px; margin: 10px; text-align: center; box-shadow: 0 2px 4px rgba(0,0,0,0.1);">
<button onclick="loadFileMobile()" style="width: 100%; padding: 12px; background: #6c757d; color: white; border: none; border-radius: 5px; font-size: 15px; font-weight: bold;">
📂 Загрузить профиль из файла
</button>
<p style="margin: 8px 0 0 0; font-size: 12px; color: #666;">Поддерживаются файлы .hpg и .txt</p>
</div>
<!-- Объем раствора -->
<div style="background: white; padding: 15px; border-radius: 8px; margin: 10px; box-shadow: 0 2px 4px rgba(0,0,0,0.1);">
<div class="mobile-input-group">
<label>Объем раствора (литры):</label>
<input type="number" id="m_volume" value="10" step="0.1" oninput="autoCalculateMobile()">
</div>
</div>
<!-- Аккордеон: Макроэлементы -->
<div class="mobile-accordion">
<div class="mobile-accordion-header" onclick="toggleAccordion('macro')">
<span>🌿 Макроэлементы (мг/л)</span>
<span class="mobile-accordion-icon" id="icon-macro"></span>
</div>
<div class="mobile-accordion-content" id="content-macro">
<div class="mobile-input-group">
<label>N (Азот):</label>
<input type="number" id="m_N" value="220" step="1" oninput="autoCalculateMobile()">
</div>
<div class="mobile-input-group">
<label>P (Фосфор):</label>
<input type="number" id="m_P" value="40" step="1" oninput="autoCalculateMobile()">
</div>
<div class="mobile-input-group">
<label>K (Калий):</label>
<input type="number" id="m_K" value="180" step="1" oninput="autoCalculateMobile()">
</div>
<div class="mobile-input-group">
<label>Ca (Кальций):</label>
<input type="number" id="m_Ca" value="200" step="1" oninput="autoCalculateMobile()">
</div>
<div class="mobile-input-group">
<label>Mg (Магний):</label>
<input type="number" id="m_Mg" value="50" step="1" oninput="autoCalculateMobile()">
</div>
<div class="mobile-input-group">
<label>S (Сера):</label>
<input type="number" id="m_S" value="73" step="1" oninput="autoCalculateMobile()">
</div>
</div>
</div>
<!-- Аккордеон: Микроэлементы -->
<div class="mobile-accordion">
<div class="mobile-accordion-header" onclick="toggleAccordion('micro')">
<span>🔬 Микроэлементы (мкг/л)</span>
<span class="mobile-accordion-icon" id="icon-micro"></span>
</div>
<div class="mobile-accordion-content" id="content-micro">
<div class="mobile-input-group">
<label>Fe (Железо):</label>
<input type="number" id="m_Fe" value="5385" step="10" oninput="autoCalculateMobile()">
</div>
<div class="mobile-input-group">
<label>Mn (Марганец):</label>
<input type="number" id="m_Mn" value="2000" step="10" oninput="autoCalculateMobile()">
</div>
<div class="mobile-input-group">
<label>B (Бор):</label>
<input type="number" id="m_B" value="500" step="10" oninput="autoCalculateMobile()">
</div>
<div class="mobile-input-group">
<label>Zn (Цинк):</label>
<input type="number" id="m_Zn" value="462" step="10" oninput="autoCalculateMobile()">
</div>
<div class="mobile-input-group">
<label>Cu (Медь):</label>
<input type="number" id="m_Cu" value="208" step="10" oninput="autoCalculateMobile()">
</div>
<div class="mobile-input-group">
<label>Mo (Молибден):</label>
<input type="number" id="m_Mo" value="50" step="10" oninput="autoCalculateMobile()">
</div>
</div>
</div>
<!-- Аккордеон: Соотношения элементов -->
<div class="mobile-accordion">
<div class="mobile-accordion-header" onclick="toggleAccordion('ratios')">
<span>📊 Соотношения элементов</span>
<span class="mobile-accordion-icon" id="icon-ratios"></span>
</div>
<div class="mobile-accordion-content" id="content-ratios">
<div id="mobile-ratios-content"></div>
</div>
</div>
<!-- Результат: Навески -->
<div class="mobile-accordion">
<div class="mobile-accordion-header" onclick="toggleAccordion('results')">
<span>⚖️ Навески солей</span>
<span class="mobile-accordion-icon open" id="icon-results"></span>
</div>
<div class="mobile-accordion-content open" id="content-results">
<div id="mobile-results-content"></div>
</div>
</div>
<!-- Информация -->
<div id="mobile-info" style="display: none;">
<div class="mobile-notice">
<h3>⚠️ Упрощенная версия</h3>
<p>Это упрощенный калькулятор для быстрого расчета навесок.</p>
<p>Для полного функционала откройте на десктопе.</p>
<a href="https://wega-project.github.io/wega-hpg/hpg.html" class="mobile-link">
Открыть полную версию
</a>
</div>
<div class="mobile-features">
<h4>✨ Полная версия включает:</h4>
<ul>
<li>Настройка составов солей</li>
<li>Матрица соотношений элементов</li>
<li>Расчет концентратов</li>
<li>Корректор растворов</li>
<li>Журнал действий</li>
<li>Сохранение и загрузка профилей</li>
</ul>
</div>
</div>
<div style="text-align: center; margin-top: 20px; color: #666; font-size: 12px;">
<p>Версия: 0.221 (HTML5 порт)</p>
<p>© WEGA Automation</p>
</div>
</div>
</body>
</html>