// ==UserScript== // @name B1 Kiosk Mode // @namespace http://tampermonkey.net/ // @homepage https://github.com/martynas2200/b1-labels // @version 2.1.4 // @description Simplified kiosk interface for label printing // @author Martynas Miliauskas // @match https://www.b1.lt/* // @match https://site.pro/My-Accounting/* // @match https://site.pro/lt/My-Accounting/* // @icon https://b1.lt/favicon.ico // @connect b1.lt // @connect raw.githubusercontent.com // @downloadURL https://raw.githubusercontent.com/martynas2200/b1-labels/main/dist/kiosk.user.js // @updateURL https://raw.githubusercontent.com/martynas2200/b1-labels/main/dist/kiosk.user.js // @grant GM.setValue // @grant GM.getValue // @grant unsafeWindow // @grant GM_xmlhttpRequest // @license GNU GPLv3 // ==/UserScript== (function () { 'use strict'; var all = ".virtual-keyboard-body{padding:20px}.virtual-keyboard-input{font-size:18px;text-align:center;margin-bottom:15px}.virtual-keyboard-footer{border-top:1px solid #e5e5e5}.keyboard{display:grid;grid-template-columns:repeat(3, 1fr);gap:8px;max-width:300px;margin:0 auto}.keyboard .key{height:50px;font-size:18px;font-weight:bold;border:1px solid #ddd;background:#f8f9fa;border-radius:4px;cursor:pointer;transition:all .1s ease}.keyboard .key:hover{background:#e9ecef;border-color:#adb5bd}.keyboard .key:active{background:#dee2e6;transform:translateY(1px)}.keyboard .key.btn-grey{background:#6c757d;color:#fff;border-color:#6c757d}.keyboard .key.btn-grey:hover{background:#5a6268;border-color:#5a6268}.keyboard .key.btn-danger{background:#dc3545;color:#fff;border-color:#dc3545}.keyboard .key.btn-danger:hover{background:#c82333;border-color:#c82333}.keyboard .backspace{font-size:16px}@media(max-width: 480px){.keyboard{max-width:280px}.keyboard .key{height:45px;font-size:16px}}body.label-print-interface{background-color:#eee}body.label-print-interface tr.sub-grid-row,body.label-print-interface td.sub-grid,body.label-print-interface [ng-if=\"config.subGrid\"],body.label-print-interface [ng-if=\"!config.hideTopPager\"],body.label-print-interface .sidebar{display:none !important;pointer-events:none}body.label-print-interface .input-group{display:flex}body.label-print-interface .modal h5.header.blue.header-temp-files-list{margin-top:0}body.label-print-interface .modal .btn,body.label-print-interface .modal select,body.label-print-interface .modal input[type=text],body.label-print-interface .modal input[type=number],body.label-print-interface .modal input[type=date]{border-radius:4px !important}body.label-print-interface .navbar{background:#2e8b57}body.label-print-interface .navbar .navbar-brand{padding-left:1em}body.label-print-interface .navbar .navbar-shortcuts{margin-right:10px}body.label-print-interface .navbar a.navbar-shortcut{color:#eee}body.label-print-interface .navbar a.navbar-shortcut i{font-size:1.2em;margin:0 2px;text-align:center}body.label-print-interface .navbar li:has(.navbar-shortcut):hover{background:rgba(0,0,0,.3607843137)}body.label-print-interface .summary{background:#fff;font-size:1.75rem;border-top:#000 2px dashed;padding:10px;margin-top:10px}body.label-print-interface .summary .summary-amount{float:right;font-weight:700}body.label-print-interface .summary .summary-label{font-weight:700;color:#333}body.label-print-interface .summary .summary-buttons{display:flex;gap:10px;margin-top:10px}body.label-print-interface .summary .summary-buttons button{flex:1}.item-search-container *:not(.header):not(svg){border-radius:4px}.item-search-container .load-data{padding:10px}@media screen and (max-width: 992px){.item-search-container .load-data{padding:10px 0 !important}}.item-search-container .load-overlay{background-color:rgba(238,238,238,.7490196078)}.item-search-container .form-section{border:1px solid #ddd;margin-top:10px;background-color:#fcfcfc;padding:0 15px}.item-search-container .item-list{max-height:calc(100vh - 70px);overflow:auto}.item-search-container .item-list .item{transition:.5s;animation:highlight .5s ease-out;background-color:#fff;display:grid;grid-template-columns:minmax(0, 1fr) min-content min-content min-content;column-gap:5px;font-size:1.1em;border:1px solid #ddd;padding:8px;margin-bottom:4px;cursor:pointer;position:relative}.item-search-container .item-list .item .item-price,.item-search-container .item-list .item .item-stock{font-weight:bold;background-color:var(--theme-blue--dark-bg);color:#fff;padding:2px 8px;margin-right:4px;transition:.5s}.item-search-container .item-list .item.inactive span.item-price{filter:blur(1px);background-color:darkred;display:inline-block;animation:vibrate .3s linear infinite both}.item-search-container .item-list .item.inactive{color:darkred}.item-search-container .item-list .item .item-stock{background-color:#777}.item-search-container .item-list .item .item-main{grid-row:1;grid-column:1}.item-search-container .item-list .item .item-labels{grid-column:1;grid-row:2;align-self:center}.item-search-container .item-list .item .btn{grid-row:1/span 2;border:none}.item-search-container .item-list .item .btn-yellow{grid-column:-1}.item-search-container .item-list .item .last-row-btn{display:none}.item-search-container .item-list .item:not(.inactive):last-child .last-row-btn{display:block;grid-row:1/span 2;grid-row:2;display:flex;gap:5px;grid-column:2/span 2}.item-search-container .item-list .item:not(.inactive):last-child{margin-bottom:30px;animation:highlight .5s ease-out,emphasizeItem .5s ease-in-out 5s backwards}.item-search-container .item-list .item.inactive{background-color:#f8d7da}.item-search-container .item-list .item.mark{background-color:#fcf8e3}.item-search-container .item-list .item.green{background-color:#e4efc9}.item-search-container .item-list .item:hover{background-color:#f1f1f1}.item-search-container .item-list .item *:empty:not(i){display:none}.item-search-container .item-list .item:not(.inactive):last-child .item-price{animation:emphasizePrice .5s ease-in-out 5s backwards}.item-search-container .item-labels{font-size:.8em;color:#666}.item-search-container .item-labels span{margin-right:10px}.item-search-container .form-group{position:relative}.item-search-container .ready-for-scan{opacity:0;transition:.5s;position:absolute;right:0%;width:80px;text-align:right;color:var(--theme-orange--base-bg);padding-top:35px;text-transform:lowercase}.item-search-container #barcode:focus:placeholder-shown+.ready-for-scan{opacity:1}.item-search-container .recent-items .recent-tab-content{background:#fff;padding:10px;border-radius:0}.item-search-container .recent-items .item-list .item{padding:8px;column-gap:0px}.item-search-container .recent-items .item-list .item .btn{grid-row:unset}.item-search-container .recent-items .item-list .item .item-price,.item-search-container .recent-items .item-list .item .btn-xs{padding:0px 6px;margin-left:5px}.item-search-container .recent-items .item-list .item-main{text-overflow:clip;overflow:hidden;white-space:nowrap}.item-search-container .recent-items .item-list .item-price{margin-right:0px}.item-search-container .recent-items .item-list .item,.item-search-container .recent-items .item-list .item:last-child{animation:none;margin-bottom:4px;padding:6px}.item-search-container .recent-items .item-list .item:last-child .item-price{animation:none}.container.width-auto>.row{padding:5px 0px;border-bottom:1px solid #eee}.keyboard{display:grid;background-color:#f5f5f5;padding:10px;grid-template-columns:repeat(3, 1fr);gap:10px;width:200px;margin:0px auto;border-radius:2px}.keyboard>*{padding:10px;font-size:18px}.modal .list-group{display:grid;grid-template-columns:1fr 1fr;gap:15px;margin-top:15px}.modal .list-group .label p:empty:nth-of-type(1)::before{content:\" \";display:block;background-image:repeating-linear-gradient(to right, black, black 4px, white 5px, white 10px);position:static;height:15px;width:120px}.modal .list-group .label.barcodeOnly p:empty:nth-of-type(1)::before{height:50px}.modal .list-group .label .barcode.dm{background-image:repeating-linear-gradient(to top, black, black 4px, white 4px, white 8px)}.modal .list-group .label.fridge::after{content:\"\";height:20px;width:100%;background-image:url(\"data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIKIHdpZHRoPSI3NjguMDAwMDAwcHQiIGhlaWdodD0iMTI4MC4wMDAwMDBwdCIgdmlld0JveD0iMCAwIDc2OC4wMDAwMDAgMTI4MC4wMDAwMDAiCiBwcmVzZXJ2ZUFzcGVjdFJhdGlvPSJ4TWlkWU1pZCBtZWV0Ij4KPGcgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMC4wMDAwMDAsMTI4MC4wMDAwMDApIHNjYWxlKDAuMTAwMDAwLC0wLjEwMDAwMCkiCmZpbGw9IiMwMDAwMDAiIHN0cm9rZT0ibm9uZSI+CjxwYXRoIGQ9Ik01NzgyIDEyNTIzIGMtMTYgLTQzIC00NTYgLTEyMTYgLTk3NyAtMjYwOCAtNTIxIC0xMzkxIC05NTAgLTI1MzMKLTk1NCAtMjUzNyAtMyAtNCAtMTI3IDMwMiAtMjc1IDY4MCAtMTQ4IDM3OCAtMzAyIDc3MCAtMzQyIDg3MiAtMzkgMTAyIC0xODcKNDc4IC0zMjcgODM1IC0xNDAgMzU4IC0yNzYgNzA0IC0zMDIgNzcwIC00NyAxMjAgLTI3NSA3MDEgLTYwMSAxNTMzIC05NiAyNDQKLTE3NCA0NDcgLTE3NCA0NTIgMCAzMCAtMzAgMTQgLTkyIC00NiAtMjMwIC0yMjYgLTM2NiAtNDkzIC00MzUgLTg1OCAtMjYKLTEzNCAtMjUgLTU4NSAwIC03NDQgNDAgLTI0NyA4NyAtNDA5IDIyNyAtNzc3IDU0IC0xNDMgMTQzIC0zODAgMTk4IC01MjUgNTUKLTE0NiAxMzMgLTM1MCAxNzIgLTQ1NSA0MCAtMTA0IDExMCAtMjkxIDE1NyAtNDE1IDQ3IC0xMjQgMTA5IC0yODggMTM4IC0zNjUKMjkgLTc3IDE0OCAtMzkyIDI2NSAtNzAwIDExNiAtMzA4IDI5NCAtNzc4IDM5NSAtMTA0NSAxMDEgLTI2NyAyMjYgLTU5OCAyNzgKLTczNyBsOTUgLTI1MSAtNDAgLTc3IGMtMTU3IC0yOTkgLTMwMSAtNjkwIC0zOTggLTEwNzQgLTY0IC0yNTQgLTk1IC00MTYKLTE4MCAtOTI2IC03NiAtNDUyIC0xMjQgLTU4MiAtMjI3IC02MTYgLTY4IC0yMiAtMTUzIDMgLTI5NyA4OCAtMTYyIDk1IC0yODIKMTIyIC01NTIgMTIzIC0xOTggMCAtMzAxIC0xMyAtNDIxIC01NCAtNDI4IC0xNDYgLTc2NiAtNTUyIC04NzUgLTEwNDkgLTIwCi04OSAtMjIgLTEzMSAtMjIgLTMyMiAwIC0xODggNCAtMjM1IDIyIC0zMjMgMzMgLTE1MSA2NyAtMjU0IDEyOCAtMzc3IDE4NAotMzc5IDQ4NSAtNjQwIDg0OSAtNzM2IDE1OCAtNDIgNDIxIC00OCA1OTIgLTE0IDI3OSA1NiA1MTUgMTg2IDcyOSAzOTkgMTMyCjEzMyAyMTYgMjQ5IDI4OSA0MDAgODQgMTc1IDExNSAzMDEgMTQxIDU2NiAxNyAxODUgMTYgMzYzIC03IDkyNSAtMzIgNzk0IDc0CjEyOTEgMzU2IDE2NjYgOTIgMTI0IDI2MiAyODMgMzk3IDM3MyA2MyA0MiAxMjAgNzYgMTI3IDc2IDYgMCA1MyAtMjUgMTAzIC01NgoyMzQgLTE0MiA0MTQgLTMxNCA1NTUgLTUzMiAxMzggLTIxMSAyMzMgLTQ4OCAyNzggLTgxMiAyMyAtMTYwIDMwIC01OTcgMTYKLTg4MyAtMTcgLTMyNyAzIC01OTQgNjQgLTg1MiAxNTggLTY2OSA1NTggLTEwOTggMTEzMSAtMTIxMSAxMzIgLTI3IDM3NSAtMjQKNTE0IDUgNDcyIDk5IDgyMiAzODIgOTY0IDc3OSAxNDUgNDA4IDE1MiA3OTMgMTkgMTE3NiAtMjM3IDY4NSAtODg4IDEwNDcKLTE1MzggODU0IC0zOCAtMTEgLTE0OSAtNTIgLTI0NSAtOTEgLTE1NSAtNjIgLTE4NCAtNzAgLTI1NSAtNzQgLTcwIC0zIC04NQotMSAtMTIwIDE5IC03NyA0NSAtOTcgOTQgLTE1MSAzNzMgLTk3IDUwMyAtMTMwIDY1NiAtMTc1IDgzNCAtMTA3IDQyMiAtMjM2Cjc5NiAtNDI1IDEyMzggbC02NiAxNTIgNTAgMTQzIGM1MCAxNDEgNTY2IDE2MTkgODQ5IDI0MjggNzkgMjI4IDE4MCA1MTYgMjIzCjY0MCA0MyAxMjQgMjA2IDU5MiAzNjMgMTA0MCAzNTggMTAyNiAzNzIgMTA4MSAzNzIgMTQ4NSAwIDI3NSAtMTkgNDAxIC05Mgo2MTUgLTgwIDIzNCAtMjYwIDUwNyAtNDM0IDY1OSBsLTI3IDIzIC0zMCAtNzl6IG02MjggLTk4NDggYzExMSAtMjggMjU3IC05MgozMzUgLTE0NSAxMjQgLTg1IDIzNiAtMjE5IDI4OCAtMzQ1IDczIC0xNzggMTAxIC0zMzIgOTQgLTUyNCAtMTEgLTI5NyAtMTEzCi01NDEgLTMwMCAtNzE3IC0xNjUgLTE1NSAtMzM0IC0yMTIgLTU5NyAtMjAxIC0xNTAgNiAtMjU4IDM1IC0zOTEgMTAzIC0yODUKMTQ4IC00OTYgNDEyIC01ODEgNzI5IC0xOCA2NSAtMjIgMTA3IC0yMiAyMzAgMSAxMzQgMyAxNTkgMjggMjM5IDM2IDExOSAxMjUKMjk5IDE4MCAzNjYgMTE4IDE0MiAzMDIgMjQ1IDUwMSAyNzkgMzMgNiA3MSAxMyA4NSAxNSA2OSAxMyAyNzggLTQgMzgwIC0yOXoKbS00NTE4IC0zNCBjMTgyIC00NyAyODAgLTEwMyA0MDggLTIzMSA4MSAtODEgMTAzIC0xMDkgMTQzIC0xOTAgMTE3IC0yMzYgMTQyCi00NTUgODEgLTcxMCAtOTAgLTM3MSAtMzg2IC03MDAgLTcxOSAtNzk5IC0yNDIgLTcyIC01NDQgLTI1IC03NTEgMTE3IC02NyA0NQotMTc1IDE1NCAtMjIzIDIyNCAtMTIxIDE3OSAtMTkwIDQ0MCAtMTc4IDY3MyA4IDE0MyAzNyAyNTYgOTcgMzgxIDE0MCAyOTAKNDExIDQ5MiA3MzggNTUwIDEwNyAxOSAzMDQgMTEgNDA0IC0xNXoiLz4KPC9nPgo8L3N2Zz4=\");background-size:81% 100%;background-position:90% 50%;background-repeat:no-repeat;bottom:.6mm;right:3em;opacity:.7;background-blend-mode:color-dodge;width:2em;height:4em;transform:rotate(90deg)}.modal .list-group .label{border:1px solid #ff9b79}.modal .list-group .label-preview:has(.half){position:relative;display:inline-flex}.modal .list-group .label-preview:has(.half)::after{content:\"\";height:100%;width:20px;background-image:url(\"data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIKIHdpZHRoPSI3NjguMDAwMDAwcHQiIGhlaWdodD0iMTI4MC4wMDAwMDBwdCIgdmlld0JveD0iMCAwIDc2OC4wMDAwMDAgMTI4MC4wMDAwMDAiCiBwcmVzZXJ2ZUFzcGVjdFJhdGlvPSJ4TWlkWU1pZCBtZWV0Ij4KPGcgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMC4wMDAwMDAsMTI4MC4wMDAwMDApIHNjYWxlKDAuMTAwMDAwLC0wLjEwMDAwMCkiCmZpbGw9IiMwMDAwMDAiIHN0cm9rZT0ibm9uZSI+CjxwYXRoIGQ9Ik01NzgyIDEyNTIzIGMtMTYgLTQzIC00NTYgLTEyMTYgLTk3NyAtMjYwOCAtNTIxIC0xMzkxIC05NTAgLTI1MzMKLTk1NCAtMjUzNyAtMyAtNCAtMTI3IDMwMiAtMjc1IDY4MCAtMTQ4IDM3OCAtMzAyIDc3MCAtMzQyIDg3MiAtMzkgMTAyIC0xODcKNDc4IC0zMjcgODM1IC0xNDAgMzU4IC0yNzYgNzA0IC0zMDIgNzcwIC00NyAxMjAgLTI3NSA3MDEgLTYwMSAxNTMzIC05NiAyNDQKLTE3NCA0NDcgLTE3NCA0NTIgMCAzMCAtMzAgMTQgLTkyIC00NiAtMjMwIC0yMjYgLTM2NiAtNDkzIC00MzUgLTg1OCAtMjYKLTEzNCAtMjUgLTU4NSAwIC03NDQgNDAgLTI0NyA4NyAtNDA5IDIyNyAtNzc3IDU0IC0xNDMgMTQzIC0zODAgMTk4IC01MjUgNTUKLTE0NiAxMzMgLTM1MCAxNzIgLTQ1NSA0MCAtMTA0IDExMCAtMjkxIDE1NyAtNDE1IDQ3IC0xMjQgMTA5IC0yODggMTM4IC0zNjUKMjkgLTc3IDE0OCAtMzkyIDI2NSAtNzAwIDExNiAtMzA4IDI5NCAtNzc4IDM5NSAtMTA0NSAxMDEgLTI2NyAyMjYgLTU5OCAyNzgKLTczNyBsOTUgLTI1MSAtNDAgLTc3IGMtMTU3IC0yOTkgLTMwMSAtNjkwIC0zOTggLTEwNzQgLTY0IC0yNTQgLTk1IC00MTYKLTE4MCAtOTI2IC03NiAtNDUyIC0xMjQgLTU4MiAtMjI3IC02MTYgLTY4IC0yMiAtMTUzIDMgLTI5NyA4OCAtMTYyIDk1IC0yODIKMTIyIC01NTIgMTIzIC0xOTggMCAtMzAxIC0xMyAtNDIxIC01NCAtNDI4IC0xNDYgLTc2NiAtNTUyIC04NzUgLTEwNDkgLTIwCi04OSAtMjIgLTEzMSAtMjIgLTMyMiAwIC0xODggNCAtMjM1IDIyIC0zMjMgMzMgLTE1MSA2NyAtMjU0IDEyOCAtMzc3IDE4NAotMzc5IDQ4NSAtNjQwIDg0OSAtNzM2IDE1OCAtNDIgNDIxIC00OCA1OTIgLTE0IDI3OSA1NiA1MTUgMTg2IDcyOSAzOTkgMTMyCjEzMyAyMTYgMjQ5IDI4OSA0MDAgODQgMTc1IDExNSAzMDEgMTQxIDU2NiAxNyAxODUgMTYgMzYzIC03IDkyNSAtMzIgNzk0IDc0CjEyOTEgMzU2IDE2NjYgOTIgMTI0IDI2MiAyODMgMzk3IDM3MyA2MyA0MiAxMjAgNzYgMTI3IDc2IDYgMCA1MyAtMjUgMTAzIC01NgoyMzQgLTE0MiA0MTQgLTMxNCA1NTUgLTUzMiAxMzggLTIxMSAyMzMgLTQ4OCAyNzggLTgxMiAyMyAtMTYwIDMwIC01OTcgMTYKLTg4MyAtMTcgLTMyNyAzIC01OTQgNjQgLTg1MiAxNTggLTY2OSA1NTggLTEwOTggMTEzMSAtMTIxMSAxMzIgLTI3IDM3NSAtMjQKNTE0IDUgNDcyIDk5IDgyMiAzODIgOTY0IDc3OSAxNDUgNDA4IDE1MiA3OTMgMTkgMTE3NiAtMjM3IDY4NSAtODg4IDEwNDcKLTE1MzggODU0IC0zOCAtMTEgLTE0OSAtNTIgLTI0NSAtOTEgLTE1NSAtNjIgLTE4NCAtNzAgLTI1NSAtNzQgLTcwIC0zIC04NQotMSAtMTIwIDE5IC03NyA0NSAtOTcgOTQgLTE1MSAzNzMgLTk3IDUwMyAtMTMwIDY1NiAtMTc1IDgzNCAtMTA3IDQyMiAtMjM2Cjc5NiAtNDI1IDEyMzggbC02NiAxNTIgNTAgMTQzIGM1MCAxNDEgNTY2IDE2MTkgODQ5IDI0MjggNzkgMjI4IDE4MCA1MTYgMjIzCjY0MCA0MyAxMjQgMjA2IDU5MiAzNjMgMTA0MCAzNTggMTAyNiAzNzIgMTA4MSAzNzIgMTQ4NSAwIDI3NSAtMTkgNDAxIC05Mgo2MTUgLTgwIDIzNCAtMjYwIDUwNyAtNDM0IDY1OSBsLTI3IDIzIC0zMCAtNzl6IG02MjggLTk4NDggYzExMSAtMjggMjU3IC05MgozMzUgLTE0NSAxMjQgLTg1IDIzNiAtMjE5IDI4OCAtMzQ1IDczIC0xNzggMTAxIC0zMzIgOTQgLTUyNCAtMTEgLTI5NyAtMTEzCi01NDEgLTMwMCAtNzE3IC0xNjUgLTE1NSAtMzM0IC0yMTIgLTU5NyAtMjAxIC0xNTAgNiAtMjU4IDM1IC0zOTEgMTAzIC0yODUKMTQ4IC00OTYgNDEyIC01ODEgNzI5IC0xOCA2NSAtMjIgMTA3IC0yMiAyMzAgMSAxMzQgMyAxNTkgMjggMjM5IDM2IDExOSAxMjUKMjk5IDE4MCAzNjYgMTE4IDE0MiAzMDIgMjQ1IDUwMSAyNzkgMzMgNiA3MSAxMyA4NSAxNSA2OSAxMyAyNzggLTQgMzgwIC0yOXoKbS00NTE4IC0zNCBjMTgyIC00NyAyODAgLTEwMyA0MDggLTIzMSA4MSAtODEgMTAzIC0xMDkgMTQzIC0xOTAgMTE3IC0yMzYgMTQyCi00NTUgODEgLTcxMCAtOTAgLTM3MSAtMzg2IC03MDAgLTcxOSAtNzk5IC0yNDIgLTcyIC01NDQgLTI1IC03NTEgMTE3IC02NyA0NQotMTc1IDE1NCAtMjIzIDIyNCAtMTIxIDE3OSAtMTkwIDQ0MCAtMTc4IDY3MyA4IDE0MyAzNyAyNTYgOTcgMzgxIDE0MCAyOTAKNDExIDQ5MiA3MzggNTUwIDEwNyAxOSAzMDQgMTEgNDA0IC0xNXoiLz4KPC9nPgo8L3N2Zz4=\");background-size:100% 25%;background-repeat:no-repeat;background-position:50% 86%;position:absolute;left:calc(100% - 10px);margin-right:-10px;z-index:1;opacity:.7}@keyframes emphasizeItem{from{font-size:1.2em}}@keyframes emphasizePrice{from{margin-left:-10px;padding-left:10px;border-radius:0px 5px 5px 0px;background-color:#000}}@keyframes highlight{from{background-color:#1b6aaa;color:#fff;filter:opacity(0.5)}}@keyframes vibrate{0%{transform:translate(0)}20%{transform:translate(-2px, 2px)}40%{transform:translate(-2px, -2px)}60%{transform:translate(2px, 2px)}80%{transform:translate(2px, -2px)}100%{transform:translate(0)}}"; const MINIMAL_TRANSLATIONS = { en: { barcode: 'Barcode', foundXItems: '{1} items found', multipleItemsFound: 'Multiple items found', itemCreated: 'Item created', itemNotFound: 'Item not found!', itemUpdated: 'Item updated', notAllItemsActive: 'Not all items are active; Do you want to proceed?', oddNumberOfItems: 'Odd number of items; Do you want to proceed?', success: 'Success', error: 'Error', failed: 'Failed', loading: 'Loading...', nlabelsToBePrinted: 'labels to be printed', noData: 'No data to print!', invalidId: 'Invalid ID', longTimeAgo: 'a long time ago', twoMonthsAgo: 'two months ago', oneMonthAgo: 'a month ago', threeWeeksAgo: 'three weeks ago', twoWeeksAgo: 'two weeks ago', daysAgo: '{1} days ago', yesterday: 'yesterday', hoursAgo: '{1} hours ago', minutesAgo: '{1} minutes ago', }, lt: { barcode: 'Barkodas', foundXItems: 'Rasta {1} prekių', multipleItemsFound: 'Rasta daugiau negu viena prekė!', itemCreated: 'Prekė sukurta', itemNotFound: 'Prekė nerasta!', itemUpdated: 'Prekė atnaujinta', notAllItemsActive: 'Ne visos prekės aktyvios, ar norite tęsti?', oddNumberOfItems: 'Nelyginis prekių skaičius, ar norite tęsti?', success: 'Sėkmingai atlikta', error: 'Įvyko klaida', failed: 'Nepavyko', loading: 'Kraunama...', nlabelsToBePrinted: 'etiketės spausdinimui', noData: 'Nepakanka duomenų spausdinimui!', invalidId: 'Neteisingas ID', longTimeAgo: 'labai seniai', twoMonthsAgo: 'prieš du mėnesius', oneMonthAgo: 'prieš mėnesį', threeWeeksAgo: 'prieš tris savaites', twoWeeksAgo: 'prieš dvi savaites', daysAgo: 'prieš {1} dienas', yesterday: 'vakar', hoursAgo: 'prieš {1} valandas', minutesAgo: 'prieš {1} minutes', }, }; const FULL_TRANSLATIONS = { en: { ...MINIMAL_TRANSLATIONS.en, normal: 'Normal', half: 'Half', add: 'Add', addManufacturer: 'Add Manufacturer', addPackageFee: 'Add Package Fee', addToList: 'Add to List', ageLimit: 'Age Limit', ago: 'ago', barcodeOnly: 'Barcode only', fridge: 'Fridge', alcohol: 'Alcohol', asMentioned: 'As mentioned', attributeName: 'Attribute Name', autoLogin: 'Instant Login', cancel: 'Cancel', checked: 'Checked', chooseLabelType: 'Choose label type', chooseLabelTypeDescription: 'Choose the type of label you want to print', cleanAll: 'Clean All', close: 'Close', code: 'Code', confirm: 'Confirm', confirmDeleteDraft: 'Are you sure you want to delete the draft?', cost: 'Cost', countryOfOriginName: 'Country of Origin Name', date: 'Date', defaultSaleService: 'Default Sale Service', departmentNumber: 'Department', description: 'Description', discount: 'Discount', discountPointsStatus: 'Discount Points Status', discountRate: 'Discount Rate', discountStatus: 'Discount Status', done: 'Done', draftBarcode: 'Draft Barcode', enterBarcode: 'Enter the barcode', enterName: 'Enter Name', enterNewPrice: 'Enter new price', enterQuantityFor: 'Enter quantity for', expiryDate: 'Expiry Date', files: 'Files', fitRaso: 'Fit Raso', freePrice: 'Free Price', fullBarcode: 'Full Barcode', fullName: 'Full Name', grossWeight: 'Gross Weight', groupName: 'Group Name', help: 'If you have any problems, reach out via GitHub', inactiveItem: 'The item is inactive. Do you want to continue?', isActive: 'Is Active', isCommentRequired: 'Is Comment Required', isQuantitative: 'Is Quantitative', isRefundable: 'Is Refundable', itemAdded: 'Item added', itemCatalog: 'Item Catalog', itemDetails: 'Item Details', itemNotActive: 'Item not active', itemsFound: 'Items found', kiloPrice: 'Price per kilo', label: 'Label', labels: 'Labels', leftover: 'Leftover', logout: 'Logout', manufacturerName: 'Manufacturer Name', markdowns: 'Markdowns', maxDiscount: 'Max Discount', measurementUnitCanBeWeighed: 'Measurement Unit Can Be Weighed', measurementUnitName: 'Measurement Unit Name', minPriceWithVat: 'Min Price With VAT', minQuantity: 'Min Quantity', missingBarcode: 'Missing barcode', missingName: 'Missing name', missingWeight: 'Missing weight', modifiedAt: 'Last modified', name: 'Name', netWeight: 'Net Weight', newItem: 'New Item', nlabelsToBePrinted: ' labels to be printed', no: 'No', noItems: 'No items', noItemsScanned: 'No items scanned yet', noItemsSelected: 'No items selected', notAllItemsActive: 'Not all selected items are active. Do you want to continue?', number: 'Number', oddNumberOfItems: 'Odd number of labels. Do you want to continue?', packageCode: 'Package', packageQuantity: 'Package Quantity', pcs: 'pcs', price: 'Price', priceFrom: 'Price From', priceMinQuantity: 'Price Min Quantity', priceNotSet: 'Price not set', pricePerKg: 'Price per kg', priceUntil: 'Price Until', priceWithoutVat: 'Price Without VAT', priceWithVat: 'Price With VAT', print: 'Print', printJobIsSent: 'Print job is sent', quantity: 'Quantity', readyForScan: 'Ready for scan', recentlyModified: 'Recently modified items', recentlySearched: 'Recently Searched', refresh: 'Refresh', retrievedAt: 'Retrieved at', save: 'Save', sayOutLoud: 'Say out loud', scannerIsNotConnected: 'Scanner is not connected', show: 'Show', showByDate: 'Show by Date', showMore: 'Show More', showStock: 'Show Stock', simplifyForm: 'Simplify Form', stock: 'Stock', type: 'Label type', thisIs: 'This is', toggleKeyboard: 'Toggle Keyboard', tooManyItems: 'Too many items', total: 'Total', totalPrice: 'Total Price', validFrom: 'Valid From', validUntil: 'Valid Until', vatRate: 'VAT Rate', weight: 'Weight', weightedItem: 'Weighted item', weightedItemAdded: 'Weighted item added', weightLabel: 'Weight Label', yes: 'Yes', zero: 'zero', draft: 'Draft', saveDraft: 'Save Draft', savedDrafts: 'Saved Drafts', draftSaved: 'Draft saved', printDraft: 'Print Draft', }, lt: { ...MINIMAL_TRANSLATIONS.lt, normal: 'Normali', half: 'Pusė', fridge: 'Trumpa (šaldytuvui)', barcodeOnly: 'Tik brūkšninis kodas', add: 'Pridėti', ago: '', addManufacturer: 'Pridėti gamintoją', addPackageFee: 'Pridėti fasavimo maišelį', addToList: 'Pridėti į sąrašą', ageLimit: 'Amžiaus limitas', alcohol: 'Alkoholis', asMentioned: 'Kaip minėjau', attributeName: 'Atributo pavadinimas', autoLogin: 'Prisijungti automatiškai', cancel: 'Atšaukti', checked: 'Tikrinta prieš', chooseLabelType: 'Pasirinkite etiketės tipą', chooseLabelTypeDescription: 'Pasirinkite etiketės tipą, kurį norite spausdinti', cleanAll: 'Išvalyti', close: 'Uždaryti', code: 'Kodas', confirm: 'Patvirtinti', confirmDeleteDraft: 'Ar tikrai norite ištrinti juodraštį?', cost: 'Savikaina', countryOfOriginName: 'Kilmės šalies pavadinimas', date: 'Data', defaultSaleService: 'Numatytoji pardavimo paslauga', departmentNumber: 'S', description: 'Aprašymas', discount: 'Nuolaida', discountPointsStatus: 'Nuolaidų taškų statusas', discountRate: 'Nuolaidos dydis', discountStatus: 'Nuolaidos statusas', done: 'Atlikta', draftBarcode: 'Kodas kasai', enterBarcode: 'Įveskite brūkšninį kodą', enterName: 'Įveskite prekės pavadinimą', enterNewPrice: 'Įveskite naują kainą', enterQuantityFor: 'Įveskite kiekį', expiryDate: 'Galiojimo data', files: 'Failai', fitRaso: 'Tinkamas RASO importui', freePrice: 'Laisva kaina', fullBarcode: 'Pilnas brūkšninis kodas', fullName: 'Pilnas pavadinimas', grossWeight: 'Bendras svoris', groupName: 'Grupės pavadinimas', help: 'Jei kyla problemų, kreiptis pas Martyną', inactiveItem: 'Prekė yra neaktyvi. Ar norite tęsti?', isActive: 'Aktyvus', isCommentRequired: 'Komentaro reikalavimas', isQuantitative: 'Kiekinė', isRefundable: 'Grąžinimo galimybė', itemAdded: 'Prekė pridėta', itemCatalog: 'Prekių žinynas', itemDetails: 'Prekės informacija', itemNotActive: 'Prekė neaktyvi', kiloPrice: 'Kilogramo kaina', label: 'Etiketė', labels: 'Etiketės', leftover: 'Liko', logout: 'Atsijungti', manufacturerName: 'Gamintojo pavadinimas', markdowns: 'Nukainavimai', maxDiscount: 'Max nuolaida', measurementUnitCanBeWeighed: 'Prekė gali būti sveriama', measurementUnitName: 'Matavimo vieneto pavadinimas', minPriceWithVat: 'Min kaina su PVM', minQuantity: 'Min kiekis', missingBarcode: 'Trūksta brūkšninio kodo', missingName: 'Trūksta pavadinimo', missingWeight: 'Trūksta svorio', modifiedAt: 'Paskutinis keitimas', name: 'Pavadinimas', netWeight: 'Neto svoris', newItem: 'Nauja prekė', nlabelsToBePrinted: ' etiketės bus spausdinamos', no: 'Ne', noItems: 'Nėra prekių', noItemsScanned: 'Dar nėra nuskaitytų prekių', noItemsSelected: 'Nėra pasirinktų prekių', notAllItemsActive: 'Ne visos pasirinktos prekės yra aktyvios. Ar norite tęsti?', number: 'Numeris', oddNumberOfItems: 'Nelyginis etikečių skaičius. Ar norite tęsti?', packageCode: 'Pakuotė', packageQuantity: 'Pakuotės kiekis', pcs: 'vnt.', price: 'Kaina', priceFrom: 'Kaina nuo', priceMinQuantity: 'Kaina min kiekis', priceNotSet: 'Kaina nėra nustatyta', pricePerKg: 'Kaina už 1 kg', priceUntil: 'Kaina iki', priceWithoutVat: 'Kaina be PVM', priceWithVat: 'Kaina su PVM', print: 'Spausdinti', printJobIsSent: 'Spausdinimo užduotis nusiųsta', quantity: 'Kiekis', readyForScan: 'Pasiruošęs skenavimui', recentlyModified: 'Šiandien pakeista', recentlySearched: 'Neseniai ieškota', refresh: 'Atnaujinti', retrievedAt: 'Gauta', save: 'Išsaugoti', sayOutLoud: 'Sakyti kainas balsu', scannerIsNotConnected: 'Skenavimo įrenginys neprijungtas', show: 'Rodyti', showByDate: 'Rodyti pagal datą', showMore: 'Rodyti daugiau', showStock: 'Rodyti likutį', simplifyForm: 'Supaprastinti formą', stock: 'Likutis', type: 'Etiketės tipas', thisIs: 'Tai', toggleKeyboard: 'Klaviatūra', tooManyItems: 'Per daug prekių', total: 'Bendra suma', totalPrice: 'Apskaičiuota kaina', validFrom: 'Galioja nuo', validUntil: 'Galioja iki', vatRate: 'PVM tarifas', weight: 'Svoris', weightedItem: 'Sveriama prekė', weightedItemAdded: 'Sveriama prekė pridėta', weightLabel: 'Svorio etiketė', yes: 'Taip', zero: 'nulis', draft: 'Juodraštis', saveDraft: 'Išsaugoti juodraštį', savedDrafts: 'Išsaugoti juodraščiai', draftSaved: 'Juodraštis išsaugotas', printDraft: 'Spausdinti juodraštį', }, }; const currentLanguage = navigator.language.split('-')[0] === 'lt' ? 'lt' : 'en'; const i18n = (key, values = []) => { let translation = FULL_TRANSLATIONS[currentLanguage]?.[key] ?? FULL_TRANSLATIONS.en[key] ?? key; values.forEach((value, index) => { translation = translation.replace(`{${index + 1}}`, value); }); return translation; }; function mainHTML(i18n) { return `

{{ 'labels' | i18n }}

{{ 'draft' | i18n }} {{ drafts[currentDraft].title }}

{{ 'scannerIsNotConnected' | i18n }}: {{ error }}
{{ 'total' | i18n }} {{ getTotalPrice() | number:2 }} €

{{ 'savedDrafts' | i18n }}

  • {{ draft.title }} {{ draft.date | date:'short' }} {{ getTotalPrice(draft.items) | number:2 }} €
{{ (item.totalPrice || item.priceWithVat).toFixed(2) }} {{ item.stock }} {{ item.name }}
{{ label | i18n }}: {{ item[label] }} {{ 'kiloPrice' | i18n }}: {{ item.priceWithVat.toFixed(2) }} {{ 'weightedItem' | i18n }} {{ getPricePerUnit(item) }} {{ getFriendlyTime(item.modifiedAt) }}
{{ 'printJobIsSent' | i18n }}. {{ 'noItemsScanned' | i18n }}.
{{ 'noItems' | i18n }}
{{ item.name }}
{{ item.priceWithVat.toFixed(2) }}
{{ 'help' | i18n }}
`; } function calculateTotalPrice(priceWithVat, quantity) { if (priceWithVat == null || quantity == null) { return 0; } const totalPrice = priceWithVat * quantity; return Math.round((totalPrice + Number.EPSILON) * 100) / 100; } function getFriendlyTime(lastChanged) { if (!lastChanged) { return null; } const now = new Date(); const changedDate = new Date(new Date(lastChanged).getTime() - new Date().getTimezoneOffset() * 60000); const diffMs = now.getTime() - changedDate.getTime(); const diffDays = Math.floor(diffMs / (1000 * 60 * 60 * 24)); const diffHours = Math.floor(diffMs / (1000 * 60 * 60)); const diffMinutes = Math.floor(diffMs / (1000 * 60)); if (diffDays > 60) { return null; } else if (diffDays > 30) { return i18n('oneMonthAgo'); } else if (diffDays > 21) { return i18n('threeWeeksAgo'); } else if (diffDays > 14) { return i18n('twoWeeksAgo'); } else if (diffDays > 1) { return i18n('daysAgo', [diffDays.toString()]); } else if (diffDays === 1) { return i18n('yesterday'); } else if (diffHours >= 1) { return i18n('hoursAgo', [diffHours.toString()]); } else { return i18n('minutesAgo', [diffMinutes.toString()]); } } function isItRecent(lastChanged) { const now = new Date(); const changedDate = new Date(lastChanged); const diffMs = now.getTime() - changedDate.getTime(); const diffDays = Math.floor(diffMs / (1000 * 60 * 60 * 24)); return diffDays < 1; } class ItemDetailsModal { modalService; notification; req; weightModal; constructor(modalService, notification, req, weightModal) { this.modalService = modalService; this.notification = notification; this.req = req; this.weightModal = weightModal; } show(item) { const filteredItem = Object.fromEntries(Object.entries(item).filter(([key, value]) => value !== null && !key.toString().toLowerCase().includes('id') && !key.toString().toLowerCase().includes('account'))); const resultString = Object.entries(filteredItem) .map(([key, value]) => `
${i18n(key)}:
${value}
`) .join(''); const modalTemplate = ` `; void this.modalService.showModal({ template: modalTemplate, scopeProperties: { item, changePrice: (item) => { void this.req.quickPriceChange(item); }, tagModal: (item) => { void this.weightModal.show(item); } } }); } } var printStyles = ".is_a_bug{display:none}@media print{body{margin:0;padding:0;max-width:58.3mm;display:inline-flex;flex-wrap:wrap}}.label{all:revert;position:relative;background:#fff;color:#000;height:31.75mm;width:57.15mm;border:.5px solid #ffdfd4;margin:0px;box-sizing:border-box;overflow:hidden}.item{height:19mm;overflow:hidden;padding:4px;font-family:Arial,sans-serif;font-size:initial;line-height:initial}.barcode{white-space:nowrap;position:absolute;bottom:0;z-index:3}.barcode div{font-size:10px;font-family:monospace;margin-left:6px;line-height:1em}.barcode p{margin:0}.dm{width:6mm;height:6mm;transform:rotate(270deg);padding:3px;fill:#000}.subtext{position:absolute;right:2px;bottom:3px;font-family:serif;font-size:18px;font-weight:500;z-index:10;line-height:1em}.price{position:absolute;bottom:21px;font-size:50px;right:0;overflow:hidden;object-position:center;margin-right:3px;line-height:1em;font-family:\"Book Antiqua\",serif;padding:0px 10px}.price-per-unit{color:#777}.barcodeOnly .item{font-size:.9em;margin-right:15px}.barcodeOnly .barcode{left:50%;transform:translateX(-50%) scale(1.4);width:max-content}.barcodeOnly .barcode>div{margin-left:0;font-size:8px;writing-mode:vertical-lr;position:absolute;right:-1.2em;bottom:1.2em}.barcodeOnly .price,.barcodeOnly .price-per-unit,.barcodeOnly .subtext{display:none}.label.fridge::before{content:\"\";display:block;position:absolute;border-top:.5px dashed #ff9b79;width:100%;bottom:7mm}.label.fridge .item{font-size:15px;height:12mm}.label.fridge .barcode p,.label.fridge .price-per-unit{display:none}.label.fridge .subtext{font-family:\"Book Antiqua\",serif;font-size:1em;bottom:11mm;left:0;right:unset;background:#888;color:#fff;padding:0px 3px;margin-left:6px;border-radius:3px;padding-top:2px}.label.fridge .price,.label.fridge .barcode{bottom:8mm;line-height:.9em}.label.half{width:28.575mm;display:block;border-right:.5px dashed #ff9b79}.label.half+.label.half:nth-of-type(even){border-right-style:solid;border-left-style:none}.label.half .item{font-size:.75em;font-size:12px;text-align:center}.label.half .subtext{padding:0px 3px;border-radius:3px;font-size:.95em}.label.half .price{right:0;left:0;margin:0px 3px;padding:0px;text-align:center;font-size:38px}.label.half .barcode.dm{transform:none;bottom:1}.label.half .barcode p{display:none}.label.weight{display:grid;grid-template-columns:min-content 1fr;align-items:center;justify-content:space-between;padding:5px;box-sizing:border-box;writing-mode:vertical-rl;grid-column-gap:2px;grid-row-gap:0px;font-family:\"Arial\"}.label.weight .barcode{grid-column:1;position:static;width:8mm;height:8mm;transform:unset}.label.weight .item{grid-column:span 2;text-align:left;font-size:11pt;font-size:10pt;max-width:60pt;padding:0px;height:unset}.label.weight .price{all:revert;grid-row:2;grid-column:span 2;text-align:center;justify-self:center;align-self:end;font-size:16pt;font-weight:bold;line-height:1em;font-family:\"Book Antiqua\",serif;margin-right:4px}.label.weight .price::after{content:\" €\"}.label.weight .kg-price{grid-column:2;justify-self:center;align-self:end}.label.weight .kg-text{grid-column:2}.label.weight .kg-text,.label.weight .weight-text{justify-self:center;align-self:baseline;color:#999;font-size:.8em;line-height:.6}.label.weight .weight-text{grid-column:1}.label.weight .weight{grid-column:1;justify-self:center;align-self:end}.label.weight .expiry{grid-column:2;text-align:center;justify-self:center;align-self:center;line-height:.9;font-size:.8em;color:#000}.label.weight .expiry::before{content:\"Geriausia iki \";color:#444;display:inline;font-family:Arial}.label.weight.half{display:grid;writing-mode:unset}.label.weight.half .price{font-size:18px}.label.weight.half *:not(.price){font-size:9pt}.label.weight.half .item{max-width:unset;max-height:11mm}.label.weight.half .dm{grid-row:3/span 3}.label.weight.half .weight{grid-column:2}.label.weight.half .expiry::before{content:\" \";display:inline-block;position:static;background-repeat:no-repeat;background-image:url(\"data:image/svg+xml,%3Csvg fill='%23000000' version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' width='15px' height='15px' viewBox='0 0 612 612' xml:space='preserve'%3E%3Cg%3E%3Cg%3E%3Cpath d='M612,463.781c0-70.342-49.018-129.199-114.75-144.379c-10.763-2.482-21.951-3.84-33.469-3.84 c-3.218,0-6.397,0.139-9.562,0.34c-71.829,4.58-129.725,60.291-137.69,131.145c-0.617,5.494-0.966,11.073-0.966,16.734 c0,10.662,1.152,21.052,3.289,31.078C333.139,561.792,392.584,612,463.781,612C545.641,612,612,545.641,612,463.781z M463.781,561.797c-54.133,0-98.016-43.883-98.016-98.016s43.883-98.016,98.016-98.016s98.016,43.883,98.016,98.016 S517.914,561.797,463.781,561.797z'/%3E%3Cpolygon points='482.906,396.844 449.438,396.844 449.438,449.438 396.844,449.438 396.844,482.906 482.906,482.906 482.906,449.438 482.906,449.438 '/%3E%3Cpath d='M109.969,0c-9.228,0-16.734,7.507-16.734,16.734v38.25v40.641c0,9.228,7.506,16.734,16.734,16.734h14.344 c9.228,0,16.734-7.507,16.734-16.734V54.984v-38.25C141.047,7.507,133.541,0,124.312,0H109.969z'/%3E%3Cpath d='M372.938,0c-9.228,0-16.734,7.507-16.734,16.734v38.25v40.641c0,9.228,7.507,16.734,16.734,16.734h14.344 c9.228,0,16.734-7.507,16.734-16.734V54.984v-38.25C404.016,7.507,396.509,0,387.281,0H372.938z'/%3E%3Cpath d='M38.25,494.859h236.672c-2.333-11.6-3.572-23.586-3.572-35.859c0-4.021,0.177-7.999,0.435-11.953H71.719 c-15.845,0-28.688-12.843-28.688-28.688v-229.5h411.188v88.707c3.165-0.163,6.354-0.253,9.562-0.253 c11.437,0,22.61,1.109,33.469,3.141V93.234c0-21.124-17.126-38.25-38.25-38.25h-31.078v40.641c0,22.41-18.23,40.641-40.641,40.641 h-14.344c-22.41,0-40.641-18.231-40.641-40.641V54.984H164.953v40.641c0,22.41-18.231,40.641-40.641,40.641h-14.344 c-22.41,0-40.641-18.231-40.641-40.641V54.984H38.25C17.126,54.984,0,72.111,0,93.234v363.375 C0,477.733,17.126,494.859,38.25,494.859z'/%3E%3Ccircle cx='134.774' cy='260.578' r='37.954'/%3E%3Ccircle cx='248.625' cy='260.578' r='37.954'/%3E%3Ccircle cx='362.477' cy='260.578' r='37.954'/%3E%3Ccircle cx='248.625' cy='375.328' r='37.953'/%3E%3Ccircle cx='134.774' cy='375.328' r='37.953'/%3E%3C/g%3E%3C/g%3E%3C/svg%3E\");height:15px;width:15px}.label.weight.half .expiry{display:flex;align-items:center}.label.weight.half .manufacturer,.label.weight.half .weight-text,.label.weight.half .kg-text,.label.weight.half .package{display:none}.label.weight .manufacturer,.label.weight .description{font-size:.7em;grid-column:span 2}.label.weight .package{font-size:.7em;grid-column:span 2}.label.weight .package{grid-row:3;grid-column:span 2;text-align:center;justify-self:center;align-self:center;margin-right:-5px;margin-left:5px;color:#444;font-size:10px;line-height:.8em}del{color:gray;position:relative;text-decoration:none;position:absolute;bottom:0;right:5px;font-size:1.4em;line-height:1em}del:after{content:\"\";display:block;position:absolute;width:110%;height:2px;border-radius:1px;background:darkred;top:9px;left:-5%;transform:skewY(-14deg)}"; const getDataMatrixMat = (text, rect = false) => { var enc = [], cw = 0, ce = 0; function push(val) { cw = 40 * cw + val; if (ce++ == 2) { enc.push(++cw >> 8); enc.push(cw & 255); ce = cw = 0; } } var cost = [ function (c) { return ((c - 48) & 255) < 10 ? 6 : c < 128 ? 12 : 24; }, function (c) { return ((c - 48) & 255) < 10 || ((c - 65) & 255) < 26 || c == 32 ? 8 : c < 128 ? 16 : 16 + cost[1](c & 127); }, function (c) { return ((c - 48) & 255) < 10 || ((c - 97) & 255) < 26 || c == 32 ? 8 : c < 128 ? 16 : 16 + cost[2](c & 127); }, function (c) { return ((c - 48) & 255) < 10 || ((c - 65) & 255) < 26 || c == 32 || c == 13 || c == 62 || c == 42 ? 8 : 1e9; }, function (c) { return c >= 32 && c < 95 ? 9 : 1e9; }, function (c) { return 12; } ]; var latch = [0, 24, 24, 24, 21, 25]; var count = [0, 12, 12, 12, 12, 25]; var c, i, p, cm = 0, nm = 0; var bytes = []; bytes[text.length] = count.slice(); for (p = text.length; p-- > 0;) { for (c = 1e9, i = 0; i < count.length; i++) { count[i] += cost[i](text.charCodeAt(p)); c = Math.min(c, Math.ceil(count[i] / 12) * 12); } if (cost[0](text.charCodeAt(p)) > 6) count[0] = Math.ceil(count[0] / 12) * 12; for (i = 0; i < count.length; i++) if (c + latch[i] < count[i]) count[i] = c + latch[i]; bytes[p] = count.slice(); } for (p = 0;; cm = nm) { c = bytes[p][cm] - latch[cm]; if (p + [0, 2, 2, 2, 3, 0][cm] >= text.length) nm = 0; else for (i = cost.length; i-- > 0;) if (Math.ceil((bytes[p + 1][i] + cost[i](text.charCodeAt(p))) / 12) * 12 == c) nm = i; if (cm != nm && cm > 0) if (cm < 4) enc.push(254); else if (cm == 4) enc.push(31 | cw & 255); else { if (ce > 249) enc.push((ce / 250 + 250 + (149 * (enc.length + 1)) % 255) & 255); enc.push((ce % 250 + (149 * (enc.length + 1)) % 255 + 1) & 255); for (; ce > 0; ce--) enc.push((text.charCodeAt(p - ce) + (149 * (enc.length + 1)) % 255 + 1) & 255); } if (p >= text.length) break; if (cm != nm) cw = ce = 0; if (cm != nm && nm > 0) enc.push([230, 239, 238, 240, 231][nm - 1]); if (nm == 0) { c = text.charCodeAt(p++); i = (c - 48) & 255; if (i < 10 && p < text.length && ((text.charCodeAt(p) - 48) & 255) < 10) enc.push(i * 10 + text.charCodeAt(p++) - 48 + 130); else { if (c > 127) enc.push(235); enc.push((c & 127) + 1); } if (cm == 4 || ce < 0) ce--; } else if (nm < 4) { var set = [[31, 0, 32, 119, 47, 133, 57, 179, 64, 173, 90, 207, 95, 277, 127, 386, 255, 1], [31, 0, 32, 119, 47, 133, 57, 179, 64, 173, 90, 258, 95, 277, 122, 335, 127, 386, 255, 1], [13, 55, 32, 119, 42, 167, 57, 179, 62, 243, 90, 207, 255, 3]][nm - 1]; do { c = text.charCodeAt(p++); if (c > 127) { push(1); push(30); c &= 127; } for (i = 0; c > set[i]; i += 2) ; if ((set[i + 1] & 3) < 3) push(set[i + 1] & 3); push(c - (set[i + 1] >> 2)); } while (ce > 0); } else if (nm == 4) { if (ce > 0) enc.push(255 & cw + (text.charCodeAt(p++) & 63)); for (cw = ce = 0; ce < 3; ce++) cw = 64 * (cw + (text.charCodeAt(p++) & 63)); enc.push(cw >> 16); enc.push((cw >> 8) & 255); } else { p++; ce++; } } var el = enc.length; var h, w, nc = 1, nr = 1, fw, fh; var j = -1, l, r, s, b = 1, k; if (ce == -1 || (cm && cm < 5)) nm = 1; if (rect && el - nm < 50) { k = [16, 7, 28, 11, 24, 14, 32, 18, 32, 24, 44, 28]; do { w = k[++j]; h = 6 + (j & 12); l = w * h / 8; } while (l - k[++j] < el - nm); if (w > 25) nc = 2; } else { w = h = 6; i = 2; k = [5, 7, 10, 12, 14, 18, 20, 24, 28, 36, 42, 48, 56, 68, 84, 112, 144, 192, 224, 272, 336, 408, 496, 620]; do { if (++j == k.length) return []; if (w > 11 * i) i = 4 + i & 12; w = h += i; l = (w * h) >> 3; } while (l - k[j] < el - nm); if (w > 27) nr = nc = 2 * Math.floor(w / 54) + 2; if (l > 255) b = 2 * (l >> 9) + 2; } s = k[j]; if (l - s + 1 == el && nm > 0) { el--; if (ce == -1) enc[el - 1] ^= 31 ^ (enc[el] - 1) & 63; } fw = w / nc; fh = h / nr; if (el < l - s) enc[el++] = 129; while (el < l - s) enc[el++] = (((149 * el) % 253) + 130) % 254; s /= b; var rs = new Array(70), rc = new Array(70); var lg = new Array(256), ex = new Array(255); for (j = 1, i = 0; i < 255; i++) { ex[i] = j; lg[j] = i; j += j; if (j > 255) j ^= 301; } for (rs[s] = 0, i = 1; i <= s; i++) for (j = s - i, rs[j] = 1; j < s; j++) rs[j] = rs[j + 1] ^ ex[(lg[rs[j]] + i) % 255]; for (c = 0; c < b; c++) { for (i = 0; i <= s; i++) rc[i] = 0; for (i = c; i < el; i += b) for (j = 0, k = rc[0] ^ enc[i]; j < s; j++) rc[j] = rc[j + 1] ^ (k ? ex[(lg[rs[j]] + lg[k]) % 255] : 0); for (i = 0; i < s; i++) enc[el + c + i * b] = rc[i]; } var mat = Array(h + 2 * nr).fill(null).map(function () { return []; }); for (i = 0; i < w + 2 * nc; i += fw + 2) for (j = 0; j < h; j++) { mat[j + (j / fh | 0) * 2 + 1][i] = 1; if ((j & 1) == 1) mat[j + (j / fh | 0) * 2][i + fw + 1] = 1; } for (i = 0; i < h + 2 * nr; i += fh + 2) for (j = 0; j < w + 2 * nc; j++) { mat[i + fh + 1][j] = 1; if ((j & 1) == 0) mat[i][j] = 1; } s = 2; c = 0; r = 4; for (i = 0; i < l; r -= s, c += s) { if (r == h - 3 && c == -1) k = [w, 6 - h, w, 5 - h, w, 4 - h, w, 3 - h, w - 1, 3 - h, 3, 2, 2, 2, 1, 2]; else if (r == h + 1 && c == 1 && (w & 7) == 0 && (h & 7) == 6) k = [w - 2, -h, w - 3, -h, w - 4, -h, w - 2, -1 - h, w - 3, -1 - h, w - 4, -1 - h, w - 2, -2, -1, -2]; else { if (r == 0 && c == w - 2 && (w & 3)) continue; if (r < 0 || c >= w || r >= h || c < 0) { s = -s; r += 2 + s / 2; c += 2 - s / 2; while (r < 0 || c >= w || r >= h || c < 0) { r -= s; c += s; } } if (r == h - 2 && c == 0 && (w & 3)) k = [w - 1, 3 - h, w - 1, 2 - h, w - 2, 2 - h, w - 3, 2 - h, w - 4, 2 - h, 0, 1, 0, 0, 0, -1]; else if (r == h - 2 && c == 0 && (w & 7) == 4) k = [w - 1, 5 - h, w - 1, 4 - h, w - 1, 3 - h, w - 1, 2 - h, w - 2, 2 - h, 0, 1, 0, 0, 0, -1]; else if (r == 1 && c == w - 1 && (w & 7) == 0 && (h & 7) == 6) continue; else k = [0, 0, -1, 0, -2, 0, 0, -1, -1, -1, -2, -1, -1, -2, -2, -2]; } for (el = enc[i++], j = 0; el > 0; j += 2, el >>= 1) { if (el & 1) { var x = c + k[j], y = r + k[j + 1]; if (x < 0) { x += w; y += 4 - ((w + 4) & 7); } if (y < 0) { y += h; x += 4 - ((h + 4) & 7); } mat[y + 2 * (y / fh | 0) + 1][x + 2 * (x / fw | 0) + 1] = 1; } } } for (i = w; i & 3; i--) mat[i][i] = 1; return mat; }; const toPath = (mat) => { var path = "", x, y; mat.forEach(function (y) { y.unshift(0); }); mat.push([]); mat.unshift([]); for (;;) { for (y = 0; y + 2 < mat.length; y++) if ((x = mat[y + 1].indexOf(1) - 1) >= 0 || (x = mat[y + 1].indexOf(5) - 1) >= 0) break; if (y + 2 == mat.length || path.length > 1e7) return path; var c = mat[y + 1][x + 1] >> 2, p = ""; for (var x0 = x, y0 = y, d = 1; p.length < 1e6;) { do x += 2 * d - 1; while ((mat[y][x + d] ^ mat[y + 1][x + d]) & mat[y + d][x + d] & 1); d ^= mat[y + d][x + d] & 1; do mat[d ? ++y : y--][x + 1] ^= 2; while ((mat[y + d][x] ^ mat[y + d][x + 1]) & mat[y + d][x + 1 - d] & 1); if (x == x0 && y == y0) break; d ^= 1 ^ mat[y + d][x + 1 - d] & 1; if (c) p = "V" + y + "H" + x + p; else p += "H" + x + "V" + y; } path += "M" + x + " " + y + p + (c ? "V" + y : "H" + x) + "Z"; for (d = 0, y = 1; y < mat.length - 1; y++) for (x = 1; x < mat[y].length; x++) { d ^= (mat[y][x] >> 1) & 1; mat[y][x] = 5 * d ^ mat[y][x] & 5; } } }; class Code128 { text; constructor(text) { this.text = text; } encode() { var t = 3, enc = [], i, j, c, mat = []; for (i = 0; i < this.text.length; i++) { c = this.text.charCodeAt(i); if (t != 2) { for (j = 0; j + i < this.text.length; j++) if (this.text.charCodeAt(i + j) - 48 >>> 0 > 9) break; if ((j > 1 && i == 0) || (j > 3 && (i + j < this.text.length || (j & 1) == 0))) { enc.push(i == 0 ? 105 : 99); t = 2; } } if (t == 2) if (c - 48 >>> 0 < 10 && i + 1 < this.text.length && this.text.charCodeAt(i + 1) - 48 >>> 0 < 10) enc.push(+this.text.substr(i++, 2)); else t = 3; if (t != 2) { if (t > 2 || ((c & 127) < 32 && t) || ((c & 127) > 95 && !t)) { for (j = t > 2 ? i : i + 1; j < this.text.length; j++) if ((this.text.charCodeAt(j) - 32) & 64) break; j = j == this.text.length || (this.text.charCodeAt(j) & 96) ? 1 : 0; enc.push(i == 0 ? 103 + j : j != t ? 101 - j : 98); t = j; } if (c > 127) enc.push(101 - t); enc.push(((c & 127) + 64) % 96); } } if (i == 0) enc.push(103); j = enc[0]; for (i = 1; i < enc.length; i++) j += i * enc[i]; enc.push(j % 103); enc.push(106); c = [358, 310, 307, 76, 70, 38, 100, 98, 50, 292, 290, 274, 206, 110, 103, 230, 118, 115, 313, 302, 295, 370, 314, 439, 422, 406, 403, 434, 410, 409, 364, 355, 283, 140, 44, 35, 196, 52, 49, 324, 276, 273, 220, 199, 55, 236, 227, 59, 443, 327, 279, 372, 369, 375, 428, 419, 395, 436, 433, 397, 445, 289, 453, 152, 134, 88, 67, 22, 19, 200, 194, 104, 97, 26, 25, 265, 296, 477, 266, 61, 158, 94, 79, 242, 122, 121, 466, 458, 457, 367, 379, 475, 188, 143, 47, 244, 241, 468, 465, 239, 247, 431, 471, 322, 328, 334, 285]; for (t = i = 0; i < enc.length; i++, t++) { mat[t++] = 1; for (j = 256; j > 0; j >>= 1, t++) if (c[enc[i]] & j) mat[t] = 1; } mat[t++] = mat[t] = 1; return [mat]; } toHtml(mat, size = 3, blocks = 5) { if (!Array.isArray(size)) size = [size || 3, size || 3]; let s = "barcode" + size[0] + size[1], b, ss; let html = "
"; for (i = 0; i < mat.length; i++) for (j = 0; j < mat[i].length;) { if (i && !j) html += "
"; for (b = 0; j < mat[i].length; b++, j++) if (!mat[i][j] || b + 1 == blocks) break; for (ss = 0; j < mat[i].length; ss++, j++) if (mat[i][j] || ss + 1 == blocks) break; html += "
"; } return html + "
"; } } class LabelGenerator { items = []; success = false; type; constructor(data = undefined, type = 'normal') { this.type = type; if (data == null) ; else if (data instanceof Promise) { void data.then((data) => { this.items = data; this.print(); }); } else { this.items = data; this.print(); } } print() { this.items = this.items.filter((item) => item.barcode != null); if (!this.isAllItemsActive()) { if (!confirm(i18n('notAllItemsActive'))) { return; } } if (this.type == 'half' && this.items.length % 2 != 0) { if (!confirm(i18n('oddNumberOfItems'))) { return; } } if (this.items.length > 0) { void this.printLabelsUsingBrowser(this.items); } else { alert(i18n('noData')); } } isAllItemsActive() { return this.items.every((item) => item.isActive); } makeUpperCaseBold(text) { const regex = /("[^"]+"|[A-ZŽĄČĘĖĮŠŲŪ]{3,})/g; return text.replace(regex, '$1'); } static getPricePerUnit(item) { const regex = /(?:,?\s*)?(?:(\d+)\s*x\s*)?(\d+(\.\d+)?(?:,\d+)?)[\s]*(k?g|m?l|vnt|pak|rul)\b/i; const match = item.name.match(regex); if (match) { const match2 = item.name.replace(match[0], '').match(regex); if (match2) { return null; } const multiplier = match[1] ? parseInt(match[1]) : 1; const amount = parseFloat(match[2].replace(',', '.')) * multiplier; const unit = match[4].replace('.', '').toLowerCase(); let pricePerUnit; if (unit === 'g' || unit === 'ml') { pricePerUnit = (item.priceWithVat / (amount / 1000)).toFixed(2) + (unit === 'ml' ? ' €/l' : ' €/kg'); } else { pricePerUnit = (item.priceWithVat / amount).toFixed(2) + ' €/' + unit; } if (parseFloat(pricePerUnit) === item.priceWithVat) { return null; } return pricePerUnit; } return null; } generateLabel(data, type = this.type) { const label = document.createElement('div'); label.className = 'label'; if (data.weight != null) { return this.generateWeightLabel(data, type === 'half'); } else if (type !== 'normal') { label.classList.add(type); } label.appendChild(this.createDivWithClass('item', this.makeUpperCaseBold(data.name), true)); if (data.barcode != null && type !== 'half') { label.appendChild(this.createCode123Div(data)); } else if (data.barcode != null) { label.appendChild(this.createDMDiv(data.barcode)); } label.appendChild(this.createDivWithClass('price', this.getItemPrice(data))); if (data.packageCode != null) { const packagePrice = 0.1 * data.packageQuantity; label.appendChild(this.createDivWithClass('subtext', 'Tara +' + packagePrice.toFixed(2))); } else if (data.measurementUnitCanBeWeighed == true) { label.appendChild(this.createDivWithClass('subtext', '/ 1 ' + data.measurementUnitName)); } return label; } getItemPrice(data) { if (data.priceWithVat == null || data.priceWithVat === 0) { return ''; } if (typeof data.priceWithVat === 'number') { return data.priceWithVat.toFixed(2); } else { return parseFloat(data.priceWithVat).toFixed(2).toString(); } } createCode123Div(data) { const barcode = document.createElement('div'); barcode.className = 'barcode'; barcode.appendChild(this.createDivWithClass('barcode-text', data.barcode)); const code128 = new Code128(data.barcode); const p = document.createElement('p'); p.innerHTML = code128.toHtml(code128.encode(), this.type == 'barcodeOnly' ? [1, 50] : [1, 15]); barcode.appendChild(p); return barcode; } generateWeightLabel(data, half = false) { const label = document.createElement('div'); if (data.weight == null || data.totalPrice == null || data.priceWithVat == null || data.barcode == null || data.barcode.length > 13) { return label; } label.className = 'label weight' + (half ? ' half' : ''); const elements = [ { className: 'item', text: data.name }, { className: 'price', text: half && data.addPackageFee ? (data.totalPrice + 0.01).toFixed(2) : data.totalPrice.toFixed(2), }, { className: 'weight', text: (data.measurementUnitCanBeWeighed ? Number(data.weight).toFixed(3) : data.weight.toString()) + (half ? ' ' + data.measurementUnitName : ''), }, { className: 'kg-price', text: data.priceWithVat.toFixed(2) + (half ? ' €/kg' : ''), }, { className: 'weight-text', text: data.measurementUnitName }, { className: 'kg-text', text: '€/' + data.measurementUnitName }, ]; if (data.addPackageFee && !half) { elements.push({ className: 'package', text: '+ 0,01 (fas. maišelis)' }); } elements.forEach(({ className, text }) => { label.appendChild(this.createDivWithClass(className, text)); }); const barcodeString = (data.addPackageFee ? '1102\t1\n' : '') + this.createPackedItemBarcode(data) + '\t1\n\r'; label.appendChild(this.createDMDiv(barcodeString)); if (data.expiryDate != null) { const date = new Date(data.expiryDate).toLocaleDateString('lt-LT', { month: '2-digit', day: '2-digit', }); label.appendChild(this.createDivWithClass('expiry', date)); } if (data.addManufacturer == true && data.manufacturerName != null) { label.appendChild(this.createDivWithClass('manufacturer', data.manufacturerName)); } return label; } createDivWithClass(className, text, raw = false) { const div = document.createElement('div'); div.className = className; if (raw) { div.innerHTML = text; } else { div.textContent = text; } return div; } createPackageBarcode(items) { if (items.length < 1) { throw new Error('No items to create package barcode'); } let barcodeString = ''; items.forEach((item) => { if (item.barcode == null) { throw new Error('Item has no barcode'); } const quantity = item.weight != null ? item.weight : 1; barcodeString += `${item.barcode}\t${quantity}\n`; }); barcodeString += '\r'; return barcodeString; } createPackedItemBarcode(data) { if (data.barcode == null) { throw new Error('Item has no barcode'); } return ('2200' + data.barcode.padStart(13, '0') + data.weight.toFixed(3).replace('.', '').padStart(4, '0')); } createDMDiv(barcodeString, big = false) { if (typeof barcodeString !== 'string') { throw new Error('Barcode string must be a string'); } else if (barcodeString.length < 1) { throw new Error('Barcode string cannot be empty'); } const barcode = document.createElement('div'); barcode.className = 'barcode dm'; const svgNS = 'http://www.w3.org/2000/svg'; const svg = document.createElementNS(svgNS, 'svg'); const path = document.createElementNS(svgNS, 'path'); const matrix = getDataMatrixMat(barcodeString); const actualSize = matrix.length; path.setAttribute('transform', 'scale(1)'); path.setAttribute('d', toPath(matrix)); svg.appendChild(path); svg.setAttribute('class', 'datamatrix'); svg.setAttribute('viewBox', `0 0 ${actualSize} ${actualSize}`); if (big === true) { svg.style.width = '100%'; svg.style.height = '100%'; svg.style.maxWidth = '60px'; svg.style.maxHeight = '60px'; } else { svg.style.width = '100%'; svg.style.height = '100%'; svg.style.maxWidth = '30px'; svg.style.maxHeight = '30px'; } svg.style.imageRendering = 'pixelated'; svg.style.shapeRendering = 'crispEdges'; barcode.appendChild(svg); return barcode; } isItInAppMode() { return (window.matchMedia('(display-mode: standalone)').matches || window.matchMedia('(display-mode: fullscreen)').matches); } async printLabelsUsingBrowser(data) { const labels = data.map((item) => this.generateLabel(item)); const popup = window.open('', '_blank', this.isItInAppMode() ? 'width=250,height=300' : 'width=800,height=600'); if (popup == null) { alert('Please allow popups for this site'); return; } popup.document.title = `${labels.length} ${i18n('nlabelsToBePrinted')}`; popup.document.head.appendChild(this.createStyleElement()); labels.forEach((label) => { popup.document.body.appendChild(label); }); this.success = true; popup.addEventListener('afterprint', () => { popup.close(); }); popup.print(); } createStyleElement() { const style = document.createElement('style'); style.innerHTML = `${printStyles}`; return style; } } class ModalService { $uibModal; $rootScope; modalInstance = null; constructor() { const injector = angular.element(document.body).injector(); this.$uibModal = injector.get('$uibModal'); this.$rootScope = injector.get('$rootScope'); } async showModal(config) { const modalScope = this.$rootScope.$new(true); if (config.scopeProperties) { Object.defineProperties(modalScope, Object.entries(config.scopeProperties).reduce((acc, [key, value]) => ({ ...acc, [key]: { get: () => config.scopeProperties[key], set: (v) => config.scopeProperties[key] = v, enumerable: true, configurable: true } }), {})); } this.modalInstance = this.$uibModal.open({ animation: true, template: config.template, scope: modalScope, size: config.size || 'lg', backdrop: config.backdrop || 'static', windowClass: config.windowClass || '', }); modalScope.closeModal = () => { this.modalInstance.close(); modalScope.$destroy(); config.onClose?.(); }; return this.modalInstance.result; } } class AngularServiceLocator { static injector = null; static getInjector() { if (this.injector) { return this.injector; } const appElement = document.querySelector('[ng-app]'); if (!appElement) { throw new Error('Angular app not found'); } this.injector = angular.element(appElement).injector(); return this.injector; } static getService(serviceName) { return this.getInjector().get(serviceName); } } class Request { notifier; items = {}; baseUrl = 'https://site.pro/lt/My-Accounting'; path = '/reference-book/items/search'; csrfToken; headers; turnstileService; constructor(notifier) { this.notifier = notifier; this.turnstileService = AngularServiceLocator.getService('turnstileService'); const csrfTokenElement = document.querySelector('meta[name="csrf-token"]'); this.csrfToken = csrfTokenElement != null ? csrfTokenElement.content : ''; this.headers = { accept: 'application/json, text/plain, */*', 'accept-language': 'en-GB,en;q=0.9,lt-LT;q=0.8,lt;q=0.7,en-US;q=0.6', 'content-type': 'application/json;charset=UTF-8', origin: this.baseUrl, referer: this.baseUrl, 'sec-fetch-dest': 'empty', 'sec-fetch-mode': 'cors', 'sec-fetch-site': 'same-origin', 'x-requested-with': 'XMLHttpRequest', 'x-csrf-token': this.csrfToken, cookie: '', }; } buildRequestBody(rules, pageSize = 20) { return { pageSize, filters: { groupOp: 'AND', rules, }, allSelected: false, asString: '', page: 1, }; } async fetchData(method, path, body) { if (this.csrfToken === '') { console.error('CSRF token is missing'); this.notifier.error('CSRF token is missing'); return; } const pathParts = path.split('/'); pathParts.pop(); this.headers.referer = `${this.baseUrl}${pathParts.join('/')}`; this.getCookies(); try { const response = await fetch(`${this.baseUrl}${path}`, { method, headers: this.headers, body: JSON.stringify(body), }); if (response.ok) { return await response.json(); } else if ('challenge' === response.headers.get('cf-mitigated') || response.status === 403) { if (await this.handleChallenge()) { console.info('Challenge handled, repeat the request'); return await this.fetchData(method, path, body); } } else { console.error('Request failed with status:', response.status); this.notifier.error({ title: i18n('error'), message: response.statusText, }); } } catch (error) { console.error('Error:', error); this.notifier.error('Error: ' + error); } } getCookies() { const cookies = document.cookie.split(';').map((cookie) => cookie.trim()); cookies.forEach((cookie) => { const [name, value] = cookie.split('='); if ([ 'YII_CSRF_TOKEN', 'b1-device_id', '__cookie_law__', '__SITE_PRO_SERVER__', 'site_language', 'site_currency', 'sp_user_fp', 'SSO_REFRESH_TOKEN', 'b1-session_id', 'isLoggedUser-v1', 'LoggedUserHash', 'cf_clearance', 'PHPSESSID', 'SPDOMAIN', ].includes(name.trim())) { this.headers.cookie = this.headers.cookie.length > 0 ? `${this.headers.cookie}; ${name}=${value}` : `${name}=${value}`; } }); } isItDigits(barcode) { return /^\d+$/.test(barcode); } async getItem(barcode) { if (!this.isItDigits(barcode)) { this.notifier.error('Invalid barcode'); return null; } if (Object.keys(this.items).includes(barcode)) { const retrievedAt = this.items[barcode].retrievedAt; if (retrievedAt != null && barcode.length > 10 && new Date().getTime() - retrievedAt.getTime() < 30000) { return { ...this.items[barcode] }; } else if (retrievedAt != null && barcode.length < 10 && new Date().getTime() - retrievedAt.getTime() < 60000) { return { ...this.items[barcode] }; } } const rules = { barcode: { data: barcode, field: 'barcode', op: barcode[0] === '0' ? 'cn' : 'eq', }, }; const body = this.buildRequestBody(rules); const response = await this.fetchData('POST', this.path, body); if (response == null || response.data[0] == null) { return null; } response.data[0].retrievedAt = new Date(); this.items[barcode] = response.data[0]; if (response.data.length > 1) { this.notifier.warning({ title: i18n('multipleItemsFound'), delay: 20000, message: i18n('foundXItems', [response.records]), }); } return response.data[0]; } async getRecentlyModifiedItems(forced = false) { const today = new Date(); today.setHours(0, 0, 0, 0); const body = this.buildRequestBody({ isActive: { data: true, field: 'isActive', op: 'eq' }, modifiedAt: { data: today.toISOString().split('T')[0], field: 'modifiedAt', op: 'gt', }, }, 200); body.sort = { modifiedAt: 'desc' }; const response = await this.fetchData('POST', this.path, body); const recentlySearched = JSON.parse(localStorage.getItem('items') ?? '[]'); const now = new Date(); if (response && response.data) { response.data.forEach((item) => { item.retrievedAt = now; this.items[item.barcode] = item; const found = recentlySearched.find((i) => i.barcode === item.barcode); if (found && found.printedAt && item.modifiedAt && new Date(item.modifiedAt).getTime() <= new Date(found.printedAt).getTime()) { item.noNeedToPrint = true; } }); } if (forced) { this.notifier.info(i18n('foundXItems', [response.records])); } return response.data || []; } async getItemsByIds(ids) { const rules = { id: { data: ids, field: 'id', op: 'in' }, }; const body = this.buildRequestBody(rules, 20); const response = await this.fetchData('POST', this.path, body); if (response && response.data) { response.data.forEach((item) => { item.retrievedAt = new Date(); this.items[item.barcode] = item; }); } if (response.code === 200) { this.notifier.success({ title: i18n('success'), message: i18n('foundXItems', [response.records]), }); } else { this.notifier.error({ title: i18n('error'), message: response.message, }); } return response.data || []; } async getAllItemsBatch(page = 1, pageSize = 100) { const body = this.buildRequestBody({}, pageSize); body.page = page; const response = await this.fetchData('POST', this.path, body); if (response && response.data) { const hasMore = response.data.length === pageSize && page * pageSize < response.records; return { data: response.data, hasMore }; } return { data: [], hasMore: false }; } async getItemMovements(itemId, dateFrom, warehouseId) { const rules = { itemId: { data: itemId, field: 'itemId', op: 'eq' }, warehouseId: { data: warehouseId, field: 'warehouseId', op: 'eq' }, date: { data: dateFrom, field: 'date', op: 'eq' }, }; const body = this.buildRequestBody(rules, 20); body.sort = {}; const response = await this.fetchData('POST', '/warehouse/item-movement/search', body); if (response.code === 200) { this.notifier.success({ title: i18n('success'), message: i18n('foundXItems', [response.records]), }); } else { this.notifier.error({ title: i18n('error'), message: response.message, }); } return response.data || []; } async saveItem(id, data) { if (!this.isItDigits(id)) { this.notifier.error(i18n('invalidId')); return false; } const response = await this.fetchData('POST', `/reference-book/items/update?id=${id}`, data); if (response.code === 200) { this.notifier.success({ title: i18n('itemUpdated'), message: i18n('newPriceIs') + ' ' + data.priceWithVat, delay: 15000, }); } else { this.notifier.error({ title: i18n('failedToUpdateItem'), message: response.message, }); } return response.code === 200; } async quickPriceChange(item) { const price = prompt(i18n('enterNewPrice'), (item.priceWithVat ?? 0).toString()); if (price == null || item.id == null) { this.notifier.info(i18n('error')); return false; } const data = new Object(); data.isActive = true; data.id = item.id; data.priceWithVat = parseFloat(price.replace(',', '.')); if (data.priceWithVat <= 0) { this.notifier.error(i18n('missingPrice')); return false; } data.priceWithoutVat = (data.priceWithVat / 1.21); data.priceWithoutVat = Math.round((data.priceWithoutVat + Number.EPSILON) * 10000) / 10000; item.priceWithVat = data.priceWithVat; item.priceWithoutVat = data.priceWithoutVat; return this.saveItem(data.id, data); } async createItem(data) { const response = await this.fetchData('POST', '/reference-book/items/create', data); if (response.code === 200) { this.notifier.success(i18n('itemCreated')); setTimeout(() => { void this.saveItem(response.data.id, { isActive: true }); }, 400); } else { this.notifier.error({ title: i18n('failedToCreateItem'), message: response.message, }); } return response.code === 200; } async getSales(operationTypeName) { const rules = { operationTypeName: { data: operationTypeName, field: 'operationTypeName', op: 'cn', }, }; const body = this.buildRequestBody(rules, 20); body.sort = { saleDate: 'desc' }; const path = '/warehouse/light-sales/search'; return this.fetchData('POST', path, body); } getSaleItems(lightSaleId) { const rules = { lightSaleId: { field: 'lightSaleId', op: 'eq', data: lightSaleId }, }; const body = this.buildRequestBody(rules, -1); return this.fetchData('POST', '/warehouse/light-sale-items/search', body); } async handleChallenge() { try { await this.turnstileService.render(); console.info('Turnstile challenge passed!'); return true; } catch (error) { console.error('Turnstile challenge failed.', error); return false; } } } class TextToVoice { language; notifier; apiKey = null; numbers; languages = { 'lt-LT': { units: ['', 'vienas', 'du', 'trys', 'keturi', 'penki', 'šeši', 'septyni', 'aštuoni', 'devyni'], teens: ['dešimt', 'vienuolika', 'dvylika', 'trylika', 'keturiolika', 'penkiolika', 'šešiolika', 'septyniolika', 'aštuoniolika', 'devyniolika'], tens: ['', '', 'dvidešimt', 'trisdešimt', 'keturiasdešimt', 'penkiasdešimt', 'šešiasdešimt', 'septyniasdešimt', 'aštuoniasdešimt', 'devyniasdešimt'], hundreds: ['', 'šimtas', 'du šimtai', 'trys šimtai', 'keturi šimtai', 'penki šimtai', 'šeši šimtai', 'septyni šimtai', 'aštuoni šimtai', 'devyni šimtai'], }, 'en-GB': { units: ['', 'one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine'], teens: ['ten', 'eleven', 'twelve', 'thirteen', 'fourteen', 'fifteen', 'sixteen', 'seventeen', 'eighteen', 'nineteen'], tens: ['', '', 'twenty', 'thirty', 'forty', 'fifty', 'sixty', 'seventy', 'eighty', 'ninety'], hundreds: ['', 'one hundred', 'two hundred', 'three hundred', 'four hundred', 'five hundred', 'six hundred', 'seven hundred', 'eight hundred', 'nine hundred'], } }; constructor(notifier) { this.language = window.navigator.language.split('-')[0]; if (this.language == 'lt') { this.language = 'lt-LT'; } else { this.language = 'en-GB'; } this.numbers = this.languages[this.language]; this.notifier = notifier; void this.checkApiKey(); } async checkApiKey() { this.apiKey = await GM.getValue('api-key', null); if (this.apiKey != null && this.apiKey.length < 20) { this.apiKey = null; this.notifier.error(i18n('invalidApiKey')); } } async speak(text) { const audio = new Audio(await this.getAudioUrl(text)); void audio.play(); } async getAudioUrl(text) { if (this.apiKey == null) { return; } const response = await fetch(`https://texttospeech.googleapis.com/v1/text:synthesize?key=${this.apiKey}`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ input: { text }, voice: { languageCode: this.language, ssmlGender: 'MALE' }, audioConfig: { audioEncoding: 'MP3' } }) }); const data = await response.json(); if (data.audioContent == null) { this.notifier.error({ title: i18n('error'), message: JSON.stringify(data) }); return; } const audioContent = data.audioContent; const audioBlob = new Blob([Uint8Array.from(atob(audioContent), c => c.charCodeAt(0))], { type: 'audio/mp3' }); return URL.createObjectURL(audioBlob); } numberToWords(number) { let words = []; if (number === 0) { words.push(i18n('zero')); } else { const unitsPart = number % 10; const tensPart = Math.floor(number / 10) % 10; const hundredsPart = Math.floor(number / 100); if (hundredsPart > 0) { words.push(this.numbers.hundreds[hundredsPart]); } if (tensPart > 1) { words.push(this.numbers.tens[tensPart]); } if (tensPart === 1) { words.push(this.numbers.teens[unitsPart]); } else { words.push(this.numbers.units[unitsPart]); } } words = words.filter(word => word); return words.join(' '); } digitsToPrice(number) { const integer = Math.floor(number); const decimal = Math.round((number - integer) * 100); let words = []; if (integer > 0) { words.push(this.numberToWords(integer)); } if (decimal > 0) { if (integer !== 0 && this.language == 'lt-LT') { if (integer === 1 || (integer % 10 === 1 && integer % 100 !== 11)) { words.push('euras'); } else if (integer % 10 === 0 || integer % 10 >= 10 || (integer % 100 >= 10 && integer % 100 <= 20)) { words.push('eurų'); } else { words.push('eurai'); } words.push('ir'); } else if (integer > 1 && this.language == 'en-GB') { words.push('euros'); } else if (integer === 1 && this.language == 'en-GB') { words.push('euro'); } words.push(this.numberToWords(decimal)); if (this.language == 'en-GB' && decimal === 1) { words.push('cent'); } else if (this.language == 'en-GB') { words.push('cents'); } else if (decimal === 1 || (decimal % 10 === 1 && decimal % 100 !== 11)) { words.push('centas'); } else if (decimal % 10 === 0 || decimal % 10 >= 10 || (decimal % 100 >= 10 && decimal % 100 <= 20)) { words.push('centų'); } else { words.push('centai'); } } words = words.filter(word => word); return words.join(' '); } } class UINotification { notificationService; constructor() { try { this.notificationService = AngularServiceLocator.getService('Notification'); } catch (error) { console.error('Failed to get Notification service', error); this.notificationService = { info: (options) => alert(options), error: (options) => alert(options), success: (options) => alert(options), warning: (options) => alert(options), primary: (options) => alert(options) }; } } info(options) { this.notificationService.info(options); } error(options) { this.notificationService.error(options); } success(options) { this.notificationService.success(options); } warning(options) { this.notificationService.warning(options); } primary(options) { this.notificationService.primary(options); } } function modalHTML(i18n) { return ` `; } class VirtualKeyboard { options; scope; i18nFunction; constructor(options = {}, i18nFunction) { this.i18nFunction = i18nFunction; this.options = { initialValue: '', placeholder: '', title: 'Enter Value', maxLength: 10, allowDecimal: true, ...options, }; this.scope = { value: this.options.initialValue || '', replaceInitialValue: this.options.initialValue !== undefined && this.options.initialValue !== null, visible: false, title: this.options.title || 'Enter Value', placeholder: this.options.placeholder || '', key: this.handleKey.bind(this), confirm: this.handleConfirm.bind(this), cancel: this.handleCancel.bind(this), hide: this.hide.bind(this), i18n: this.i18nFunction, }; } getScope() { return this.scope; } show() { this.scope.visible = true; this.scope.value = ''; } hide() { this.scope.visible = false; } handleKey(input) { if (input === 'd') { this.scope.value = this.scope.value.slice(0, -1); } else if (input === 'c') { this.scope.value = ''; } else if (input === '.') { if (this.options.allowDecimal && !this.scope.value.includes('.')) { this.scope.value += input; } } else if (/^\d$/.test(input)) { if (this.scope.replaceInitialValue) { this.scope.value = input; this.scope.replaceInitialValue = false; } else if (!this.options.maxLength || this.scope.value.length < this.options.maxLength) { this.scope.value += input; } } if (this.options.onValueChange) { this.options.onValueChange(this.scope.value); } } handleConfirm() { if (this.options.onConfirm) { this.options.onConfirm(this.scope.value); } this.hide(); } handleCancel() { if (this.options.onCancel) { this.options.onCancel(); } this.hide(); } setValue(value) { this.scope.value = value; if (this.options.onValueChange) { this.options.onValueChange(this.scope.value); } } getValue() { return this.scope.value; } updateOptions(newOptions) { this.options = { ...this.options, ...newOptions }; this.scope.title = this.options.title || 'Enter Value'; this.scope.placeholder = this.options.placeholder || ''; } } class WeightLabelModal { modalScope; modalService; notifier; controller; virtualKeyboard; constructor(modalService, notifier, controller) { this.modalService = modalService; this.notifier = notifier; this.controller = controller; this.virtualKeyboard = new VirtualKeyboard({ allowDecimal: true, maxLength: 8, onValueChange: (value) => { if (this.modalScope.item) { this.modalScope.item.weight = value; this.handleWeightChange(); } }, }, i18n); this.modalScope = { item: null, weight: '', virtualKeyboardVisible: this.controller !== undefined, addButton: this.controller !== undefined, hideVirtualKeyboard: this.hideVirtualKeyboard.bind(this), handleWeightChange: this.handleWeightChange.bind(this), key: this.key.bind(this), add: this.add.bind(this), print: this.print.bind(this), picker: this.showPicker.bind(this), i18n: i18n, }; } hideVirtualKeyboard() { this.modalScope.virtualKeyboardVisible = false; } key(input) { if (!this.modalScope.item) { return; } if (input === 'd' && this.modalScope.item.weight) { this.modalScope.item.weight = this.modalScope.item.weight .toString() .slice(0, -1); } else if (input === 'c') { this.modalScope.item.weight = ''; } else if (input !== 'd') { this.modalScope.item.weight = (this.modalScope.item.weight || '') + input; } this.modalScope.handleWeightChange(); } show(item) { if (!item || !item.name || !item.priceWithVat) { this.notifier.error(i18n('noData')); return Promise.reject(); } this.setupModalItem(item); return this.modalService.showModal({ template: modalHTML(), scopeProperties: this.modalScope, size: 'md', windowClass: 'weight-label-modal', }); } setupModalItem(item) { this.modalScope.item = { ...item, weight: '', addManufacturer: false, addPackageFee: true, }; } getWeightItem() { const item = { ...this.modalScope.item }; if (!item || !item.weight) { this.notifier.error(i18n('missingWeight')); return null; } item.weight = item.measurementUnitCanBeWeighed ? this.gToKg(parseFloat(item.weight.toString())) : parseFloat(item.weight.toString()); if (item.weight > 9.999) { this.notifier.error(i18n('maxWeight')); return null; } item.addManufacturer = !!item.addManufacturer && !!item.manufacturerName?.length; return item; } add() { if (this.controller !== undefined) { const item = this.getWeightItem(); if (item) { void this.controller?.processItem(item, true); } } } print() { const item = this.getWeightItem(); if (item) { this.notifier.success({ title: i18n('printJobIsSent'), message: `${item.weight}${item.measurementUnitName}`, }); new LabelGenerator([item]); } } handleWeightChange() { if (!this.modalScope.item) { return; } this.modalScope.item.totalPrice = calculateTotalPrice(this.modalScope.item.priceWithVat, this.modalScope.item.measurementUnitCanBeWeighed ? this.gToKg(Number(this.modalScope.item.weight || 0)) : Number(this.modalScope.item.weight || 0)); } gToKg(weight) { return weight / 1000; } showPicker(event) { const target = event.target; if (target?.showPicker) { target.showPicker(); } } } class LabelTypeModal { type; modal; lg; labelTypes = ['normal', 'fridge', 'half', 'barcodeOnly']; fakeItems = [ { name: 'Whiskey BLACK RAM, 40%, 0,7 l', barcode: '3800032070302', measurementUnitName: 'vnt.', measurementUnitCanBeWeighed: false, priceWithVat: 14.29, isActive: true, isRefundable: true, }, { name: 'Cookies BARNI Milk, 30 g', barcode: '5906747318345', measurementUnitName: 'vnt.', measurementUnitCanBeWeighed: false, priceWithVat: 0.5, isActive: true, isRefundable: true, }, { name: 'Coca-Cola, 0,5 l', barcode: '4779036770016', measurementUnitName: 'vnt.', measurementUnitCanBeWeighed: false, priceWithVat: 1.99, isActive: true, packageCode: '1100', isRefundable: true, }, ]; constructor(type) { this.type = type; this.modal = new ModalService(); this.lg = new LabelGenerator(); } open() { return new Promise((resolve, reject) => { void this.modal.showModal({ template: this.lg.createStyleElement().outerHTML + `