Refactor multiple JavaScript classes to use ES6 syntax, including Download, Pdf, Printer, PrinterPos, PoleDisplay, and Simulator. Update method names for consistency and improve error handling. Modify AJAX requests to enhance functionality and user experience.

This commit is contained in:
2026-11-02 16:10:36 +07:00
parent 929f3af79a
commit 11e17bd978
6 changed files with 560 additions and 440 deletions
+77 -73
View File
@@ -4,82 +4,86 @@
Desc. : Fungsi-fungsi download data & laporan
Created By : Siau Pheng ([email protected]).
Created Date : April 1st, 2011.
Last Modified : October 2nd, 2026.
Last Modified : November 2nd, 2026.
(c) 3FONIA Software; WWW.3FONIA.COM.
*******************************************************************/
*******************************************************************
*
* Class Download
*
* Download atau Share file.
*
* @license MIT, https://www.3fonia.com
* @copyright Copyright (c) 2008-2026, Siau Pheng <[email protected]>
*
* @param {object} options
* @param {string} options.url - Endpoint URL File
* @param {string} [options.filename='file.bin'] - Default file name for download is header is missing
* @param {string} [options.title] - Title for share file
* @param {string} [options.text] - Text for share file
* @param {Array} [options.params] - Request payload
*/
var Download = function(options) {
var _options = {
'url': '',
'filename': 'file.bin',
'title': '',
'text': 'share dari aplikasi'
};
var _params = [];
class Download {
constructor(options = {}) {
this.url = typeof options === 'string' ? options : options.url || '';
this.filename = options.filename || 'file.bin';
this.title = options.title || '';
this.text = options.text || '';
this.params = options.params || [];
}
this.construct = function(options){
if (typeof options == 'object') {
$.extend(_options, options);
} else _options.url = options;
};
this.set_url = function (url){
_options.url = url;
};
this.add_param = function(par_id, par_val) {
var _getvalue = (par_val) ? par_id + '=' + encodeURIComponent(par_val) : par_id;
_params.push(_getvalue);
};
this._exec = async function(is_share) {
if (!_options.url) {
console.warn('cek url !');
return false;
}
try {
const blob = await $.ajax({
type: 'POST',
url: _options.url,
data: FoniaJS.RequestParam(_params),
xhrFields: {
responseType: 'blob'
},
success: async function (data, status, xhr) {
const disposition = xhr.getResponseHeader('Content-Disposition');
if (disposition && disposition.indexOf('attachment') !== -1) {
const filenameRegex = /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/;
const matches = filenameRegex.exec(disposition);
if (matches != null && matches[1]) {
_options.filename = matches[1].replace(/['"]/g, '');
}
}
}
});
const file = new File([blob], _options.filename, { type: blob.type });
if (is_share === true && navigator.canShare && navigator.canShare({ files: [file] })) {
await navigator.share({
files: [file],
title: _options.title ?? _options.filename,
text: _options.text
});
} else {
const link = document.createElement('a');
link.href = URL.createObjectURL(blob);
link.download = _options.filename;
link.click();
}
} catch (error) {
console.error('Download / sharing gagal :', error);
};
addParam(id, val) {
this.params.push(val !== undefined ? `${id}=${encodeURIComponent(val)}` : id);
return this;
};
this.share = async function() {
await this._exec(true);
};
this.download = async function() {
await this._exec(false);
};
this.construct(options);
};
}
async _exec(isShare = false) {
if (!this.url) {
console.warn('Url File Download dibutuhkan!');
return;
}
await $.ajax({
type: 'POST',
url: this.url,
data: FoniaJS.RequestParam(this.params),
xhrFields: { responseType: 'blob' }
})
.done((blob, status, xhr) => {
let filename = this.filename;
const disposition = xhr.getResponseHeader('Content-Disposition');
const match = disposition?.match(/filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/);
if (match?.[1]) filename = match[1].replace(/['"]/g, '');
if (!isShare) {
const url = URL.createObjectURL(blob);
const link = Object.assign(document.createElement('a'), {
href: url,
download: filename
});
link.click();
URL.revokeObjectURL(url);
} else {
const file = new File([blob], filename, { type: blob.type });
if (isShare && navigator.canShare?.({ files: [file] })) {
navigator.share({
files: [file],
title: this.title || filename,
text: this.text
});
}
file = null;
}
})
.fail((xhr, status, err) => console.error('Gagal download File karena : ', err));
}
share() {
return this._exec(true);
}
download() {
return this._exec(false);
}
}
+3 -6
View File
@@ -784,7 +784,7 @@ class Request_JSF extends Request {
const opsi = {
responsive: true,
columnDefs: [{ responsivePriority: 1, targets: 0 }, { responsivePriority: 2, targets: 1 }, { responsivePriority: 0, targets: -1 }],
columnDefs: [{ responsivePriority: 1, targets: 0 }, { responsivePriority: 2, targets: 1 }],
lengthMenu: [],
dom: '<"row"<"col-sm-12"tr>><"row"<"col-sm-12 col-lg-auto"<"toolbar">><"col-sm-3 col-lg-auto ms-lg-auto"l><"col-sm-9 col-lg-auto"p>>',
autoWidth: false,
@@ -866,7 +866,7 @@ class Request_JSF extends Request {
};
}
if (ini.option.length > 0) opsi.columnDefs.push({ responsivePriority: 3, targets: -1 }, { orderable: false, targets: -1 });
if (ini.option.length > 0) opsi.columnDefs.push({ responsivePriority: 0, targets: -1 }, { orderable: false, targets: -1 });
if (ini.navi.no_navi === true) opsi.paging = false;
if (ini.config.scrollbar === true) {
@@ -900,7 +900,7 @@ class Request_JSF extends Request {
}
};
opsi.drawCallback = () => {
opsi.drawCallback = function() {
$(`#${ini.id}Tabel tbody td:last-child:has(span.rowcss)`).each((i, el) => {
const span = $(el).find('span').attr('class').replace('rowcss ', '');
$(el).parent().addClass(span);
@@ -1014,9 +1014,6 @@ class Request_JSF extends Request {
}
}
//const Request = (options) => new _Request(options);
//RequestF = (options) => new _Request_JSONF(options);
// jQuery Validator setup
if (jQuery.validator) {
jQuery.validator.setDefaults({
+121 -61
View File
@@ -1,66 +1,126 @@
/*******************************************************************
Module : /assets/js/pdf.js
Desc. : JS - Cetak Laporan PDF
Created By : Siau Pheng ([email protected]).
Created Date : April 6th, 2015.
Last Modified : October 18th, 2020.
Module : /assets/js/pdf.js
Desc. : JS - Cetak Laporan PDF
Created By : Siau Pheng ([email protected]).
Created Date : April 6th, 2015.
Last Modified : November 2nd, 2026.
(c) 3FONIA Software; WWW.3FONIA.COM.
(c) 3FONIA Software; WWW.3FONIA.COM.
*******************************************************************/
*******************************************************************
*
* Class Pdf
*
* Preview dan download file pdf.
*
* @license MIT, https://www.3fonia.com
* @copyright Copyright (c) 2008-2026, Siau Pheng <[email protected]>
*
* @param {object} options
* @param {string} options.url - Endpoint PDF File URL
* @param {number} [options.zoom=80] - Zoom percentage in PDF viewer
* @param {string} [options.filename='report.pdf'] - Default file name for download is header is missing
* @param {Array} [options.params] - Request payload
*/
var Pdf = function(options){
var vars = {
'url': '',
'zoom': 80,
'param': []
};
this.construct = function(options){
if (typeof options == 'object') {
$.extend(vars, options);
} else vars.url = options;
};
this.set_url = function (url){
vars.url = url;
};
this.set_zoom = function (zoom){
if (zoom) vars.zoom = zoom;
},
this.add_param = function (par_id, par_val){
var _getvalue = (par_val) ? par_id + "=" + escape(par_val) : par_id;
vars.param.push(_getvalue);
},
this.downloadPDF = function(){
if (!vars.url) {
console.warn(vars.param + ", cek url php!");
return false;
}
$.ajax({
type: 'POST',
url: vars.url,
data: FoniaJS.RequestParam(vars.param),
success: (typeof vars.onSuccess === "function") ? vars.onSuccess : null,
error: (typeof vars.onFailure === "function") ? vars.onFailure : null,
}).always(FoniaJS.FormDecorated);
return this;
};
this.showPDF = function(){
var _proto = window.location.protocol + '//';
var _src = _proto + FoniaJS.WEB_HOST + vars.url + '?' + $.param(FoniaJS.RequestParam(vars.param)) + '#zoom='+vars.zoom+',0,0&page=1';
var _wtxt =
'<div class="modal fade" id="winAPPPDF" tabindex="-1" role="dialog" data-bs-keyboard="false" data-bs-show="false" data-bs-backdrop="static" aria-labelledby="winAPPPDFLabel" aria-hidden="true">'+
'<div class="modal-dialog modal-dialog-centered modal-fs" role="document">'+
'<div class="modal-content">'+
'<div class="modal-header"><h5 class="modal-title" id="winAPPPDFLabel">Preview PDF</h5><button class="btn-close" type="button" data-bs-dismiss="modal" aria-label="Close"></button></div>'+
'<div class="modal-body mb-0 p-0"><div class="ratio ratio-21x9"><object type="application/pdf" data="'+_src+'"><p>Tidak Mendukung Preview PDF</p></object></div></div>'+
'</div></div></div>';
if ($('#winAPPPDF')) $('#winAPPPDF').modal('dispose').remove();
$(_wtxt).on('shown.bs.modal', function(e){
FoniaJS.FormDecorated();
}).on('hidden.bs.modal', function(e){
$(this).remove();
}).modal('show');
};
this.construct(options);
};
class Pdf {
constructor(options = {}) {
this.url = typeof options === 'string' ? options : options.url || '';
this.zoom = options.zoom || 80;
this.filename = options.filename || 'report.pdf';
this.params = options.params || [];
}
addParam(id, val) {
this.params.push(val !== undefined ? `${id}=${encodeURIComponent(val)}` : id);
return this;
}
async downloadPdf() {
if (!this.url) {
console.warn('Url PDF dibutuhkan!');
return;
}
await $.ajax({
type: 'POST',
url: this.url,
data: FoniaJS.RequestParam(this.params),
xhrFields: { responseType: 'blob' }
})
.done((blob, status, xhr) => {
let filename = this.filename;
const disposition = xhr.getResponseHeader('Content-Disposition');
const match = disposition?.match(/filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/);
if (match?.[1]) filename = match[1].replace(/['"]/g, '');
const url = URL.createObjectURL(blob);
const link = Object.assign(document.createElement('a'), {
href: url,
download: filename
});
link.click();
URL.revokeObjectURL(url);
})
.fail((xhr, status, err) => console.error('Gagal proses download PDF karena : ', err));
}
async showPdf() {
if (!this.url) {
console.warn('Url PDF dibutuhkan!');
return;
}
await $.ajax({
type: 'POST',
url: this.url,
data: FoniaJS.RequestParam(this.params),
xhrFields: { responseType: 'blob' }
})
.done((blob, status, xhr) => {
let filename = this.filename;
const disposition = xhr.getResponseHeader('Content-Disposition');
const match = disposition?.match(/filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/);
if (match?.[1]) filename = match[1].replace(/['"]/g, '');
const objectUrl = URL.createObjectURL(blob);
const pdfSrc = `${objectUrl}#zoom=${zoom}&page=1`;
const modalHtml = `
<div class="modal fade" id="winAPPPDF" tabindex="-1" data-bs-backdrop="static" aria-labelledby="winAPPPDFLabel" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered modal-xl" style="height: 90vh;">
<div class="modal-content h-100">
<div class="modal-header">
<h5 class="modal-title" id="winAPPPDFLabel">${filename}</h5>
<div class="ms-auto d-flex align-items-center gap-2">
<a href="${objectUrl}" download="${filename}" class="btn btn-sm btn-outline-primary">
<i class="bi bi-download"></i> Download
</a>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
</div>
<div class="modal-body p-0 h-100">
<object data="${pdfSrc}" type="application/pdf" class="w-100 h-100">
<iframe src="${pdfSrc}" class="w-100 h-100" style="border: none;">
<p class="p-3">Ada kendala dalam Preview PDF di browser Anda.
<a href="${objectUrl}" download="${filename}">Unduh ${filename}</a> instead.
</p>
</iframe>
</object>
</div>
</div>
</div>
</div>
`;
$('#winAPPPDF').modal('dispose').remove();
$(modalHtml)
.appendTo('body')
//.on('shown.bs.modal', () => FoniaJS.FormDecorated())
.on('hidden.bs.modal', function () {
URL.revokeObjectURL(objectUrl);
$(this).remove();
})
.modal('show');
})
.fail((xhr, status, err) => console.error('Gagal proses preview PDF karena : ', err));
}
}
+8 -8
View File
@@ -30,10 +30,10 @@ class PoleDisplay {
qz.serial.openPort(this.port, this.options)
.then(() => {
this.pole = this.port;
this.cd_Error(`${this.port} opened`);
this._conerr(`${this.port} opened`);
return qz.serial.sendData(this.pole, { type: 'HEX', data: 'x1Bx40' }, this.options);
})
.catch(this.cd_Error);
.catch(this._conerr);
}
Close() {
@@ -42,9 +42,9 @@ class PoleDisplay {
qz.serial.closePort(this.port)
.then(() => {
this.pole = '';
this.cd_Error(`${this.port} closed`);
this._conerr(`${this.port} closed`);
})
.catch(this.cd_Error);
.catch(this._conerr);
}
Send(txt) {
@@ -57,15 +57,15 @@ class PoleDisplay {
let textToSend = txt;
if (Array.isArray(txt)) {
textToSend = txt.map(ele => this.cd_Text(ele.t, ele.a)).join('');
textToSend = txt.map(ele => this._aligntext(ele.t, ele.a)).join('');
}
const hexData = `x10C${asciiToHex(textToSend)}`;
qz.serial.sendData(this.pole, { type: 'HEX', data: hexData }, this.options)
.catch(this.cd_Error);
.catch(this._conerr);
}
cd_Text(text, align = 'L') {
_aligntext(text, align = 'L') {
const totWidth = 20;
if (text.length >= totWidth) return text.substring(0, totWidth);
@@ -83,7 +83,7 @@ class PoleDisplay {
return ' '.repeat(lPadding) + text + ' '.repeat(rPadding);
}
cd_Error(err) {
_conerr(err) {
console.error(err);
}
}
+256 -224
View File
@@ -4,233 +4,265 @@
Desc. : JS Class Printer & Printer POS
Created By : Siau Pheng ([email protected]).
Created Date : May 27th, 2007.
Last Modified : September 21st, 2026.
Last Modified : November 2nd, 2026.
(c) 3FONIA Software; WWW.3FONIA.COM.
*******************************************************************/
*******************************************************************
*
* Class Printer
*
* Standard Web Document Printing & Report Preview Class
*
* @license MIT, https://www.3fonia.com
* @copyright Copyright (c) 2008-2026, Siau Pheng <[email protected]>
*
* @param {object} options
* @param {string} options.url - Endpoint URL Print Page
* @param {number} [options.zoom=90] - Zoom percentage in viewer
* @param {Array} [options.params] - Request payload
*/
var Printer = function(options){
var vars = {
'url': '',
'zoom': 90,
'param': []
};
this.construct = function(options){
if (typeof options == 'object') {
$.extend(vars, options);
} else vars.url = options;
};
this.set_url = function (url){
vars.url = url;
};
this.set_zoom = function (zoom){
if (zoom) vars.zoom = zoom;
},
this.add_param = function (par_id, par_val){
var _getvalue = (par_val) ? par_id + "=" + escape(par_val) : par_id;
vars.param.push(_getvalue);
},
this.doPrint = function(){
if (!vars.url) {
console.warn(vars.param + ", cek url php!");
return false;
}
$.ajax({
type: 'POST',
url: vars.url,
data: FoniaJS.RequestParam(vars.param),
success: (txt) => {
var $printData = $('#dataPrint');
if (!$printData.length) {
$printData = $('<div/>', {id:'dataPrint','class':'d-none d-print-block print-body'}).appendTo('body');
}
$printData.html(txt);
window.print();
},
error: (typeof vars.onFailure === "function") ? vars.onFailure : null,
});
class Printer {
constructor(options = {}) {
this.url = typeof options === 'string' ? options : options.url || '';
this.zoom = options.zoom || 90;
this.params = options.params || [];
}
addParam(id, val) {
this.params.push(val !== undefined ? `${id}=${encodeURIComponent(val)}` : id);
return this;
};
this.doPreview = function(){
if (!vars.url) console.warn(vars.param + ", cek url php!");
$.ajax({
type: 'POST',
url: vars.url,
data: FoniaJS.RequestParam(vars.param),
success: (txt) => {
if ('' == txt) {
FoniaJS.showInfo('Tidak ada data untuk pembuatan laporan !');
return false;
}
var _wtxt =
'<div class="modal fade" id="winAPPPrint" tabindex="-1" role="dialog" data-bs-keyboard="false" data-bs-show="false" data-bs-backdrop="static" aria-labelledby="winAPPPrintLabel" aria-hidden="true">'+
'<div class="modal-dialog modal-dialog-centered modal-dialog-scrollable modal-fs" role="document">'+
'<div class="modal-content">'+
'<div class="modal-header"><h5 class="modal-title" id="winAPPPrintLabel">Preview Laporan</h5><div class="ms-auto"><button class="btn me-2" id="tombCetak" type="button" aria-label="Cetak"><i class="bi-printer me-2"></i><span aria-hidden="true">Cetak</span></button><button class="btn" type="button" data-bs-dismiss="modal" aria-label="Tutup"><i class="bi-x-lg me-2"></i><span aria-hidden="true">Tutup</span></button></div></div>'+
'<div class="modal-body print-body" id="divPrintP">'+txt+'</div>'+
'</div></div></div>';
if ($('#winAPPPrint')) $('#winAPPPrint').modal('dispose').remove();
$(_wtxt).on('shown.bs.modal', (e) => {
$('#winAPPPrint').find('#tombCetak').click(() => {
var $printData = $('#dataPrint');
if (!$printData.length) {
$printData = $('<div/>', {id:'dataPrint','class':'d-none d-print-block print-body'}).appendTo('body');
}
$printData.html($('#divPrintP').html());
FoniaJS.closeWindow($('#winAPPPrint'));
window.print();
});
}).on('hidden.bs.modal', (e) => {
$(this).remove();
}).modal('show');
},
error: (typeof vars.onFailure === "function") ? vars.onFailure : null,
});
};
this.construct(options);
};
}
var PrinterPos = function(options) {
var b_default = false;
var _options = {
'url': '',
'param': [],
'printer': null,
'cut_paper': "\x1D\x56\x41",
'open_draw': "\x1B\x70\x30\x19\x19",
'chr_norm': "\x1B\x21\x01",
'chr_bold': "\x1B\x21\x10",
'chr_sbold': "\x1B\x21\x31",
'chr_uline': "\x1B\x21\x80"
};
this.construct = function(options) {
if (typeof options == 'object') {
$.extend(_options, options);
} else _options.url = options;
if (FoniaJS._def_printer) _options.printer = FoniaJS._def_printer;
};
this.detectPrinter = function() {
if (!qz.websocket.isActive()) {
FoniaJS.showInfo('Status Printer belum Active !');
return false;
}
return true;
};
this.getPrinters = function() {
return [];
};
this.setPrinter = function(nprint) {
(nprint != null) && (_options.printer = nprint);
};
this.useDefaultPrinter = function() {
b_default = true;
};
this.set_url = function(url) {
_options.url = url;
};
this.add_param = function(par_id, par_val) {
var _getvalue = (par_val) ? par_id + '=' + escape(par_val) : par_id;
_options.param.push(_getvalue);
};
this.exec = function() {
if (!this.detectPrinter()) return;
if (this.b_default) {
var parent = this;
qz.printers.getDefault().then(function(data) {
var nama_p = data;
if (data.name != undefined) {
nama_p = data.name;
}
parent._options.param.push('Printer='+nama_p);
return parent.do_print(nama_p, parent._options.url, parent._options.param);
}, function(data) {
FoniaJS.showInfo(data);
});
} else {
var _nprinter = _options.printer;
if (typeof _nprinter === 'object' && _nprinter.name == undefined) {
if (_nprinter.file != undefined) _nprinter = _nprinter.file;
if (_nprinter.host != undefined) _nprinter = _nprinter.host + ":" + _nprinter.port;
} else {
if (_nprinter.name != undefined) _nprinter = _nprinter.name;
if (_nprinter== undefined) _nprinter = 'None';
}
_options.param.push('Printer='+_nprinter);
this.do_print(_options.printer, _options.url, _options.param);
}
};
this.do_print = function(nama_p, url, params) {
var qzcfg = qz.configs.create(nama_p);
new Request({
url: url,
sendParam: params,
onSuccess: (ajson) => {
if (typeof ajson === "object") {
var hasil_cetak = (ajson.type == "escp") ? this.gen_escp(ajson) : this.gen_epl(ajson);
qz.print(qzcfg, hasil_cetak);
} else {
FoniaJS.showInfo('Error dalam proses Cetak.');
}
}
}).send();
};
this.gen_escp = function(adata) {
var ohead = [];
var osheet = [], ocetak = [];
ohead.push("\x1B\x40");
ohead.push("\x1B\x61\x01");
if (adata.config.logo != null) {
//ohead.push({type:'raw',format:'image',data:adata.config.logo,options:{language:'ESCPOS',dotDensity:'double'}});
//ohead.push("\x1B\x32");
ohead.push({type:'raw',format:'image',flavor:'base64',data:adata.config.logo,options:{language:'ESCPOS',dotDensity:'double'}});
ohead.push("\x1B\x32");
}
if (parseInt(adata.config.fontsize) == 2) {
_options.chr_norm = "\x1B\x21\x02";
}
if (adata.header.length > 0) {
for (i = 0; i < adata.header.length; i++) {
if (i==0 && adata.config.logo==null) ohead.push(_options.chr_bold);
ohead.push(adata.header[i] + "\x0A");
if (i==0 && adata.config.logo==null) ohead.push(_options.chr_norm);
}
}
for (s = 0; s < adata.content.length; s++) {
if (adata.content[s].length == 0) continue;
osheet = ohead.slice(0);
for (i = 0; i < adata.content[s].length; i++) {
var tmp_cont = adata.content[s][i];
tmp_cont = tmp_cont.replace('_SBOLD_', _options.chr_sbold)
.replace('_BOLD_', _options.chr_bold)
.replace('_NORM_', _options.chr_norm)
.replace('_ULINE_', _options.chr_uline);
osheet.push(tmp_cont + "\x0A");
}
if (adata.config.cutpaper) {
osheet.push(_options.cut_paper + "\x0A");
}
ocetak = ocetak.concat(osheet);
}
var notaout = [];
if (adata.config.opendraw) {
notaout.push(_options.open_draw);
}
for (i = 0; i < adata.config.times; i++) {
notaout = notaout.concat(ocetak);
}
return notaout;
};
this.gen_epl = function(adata) {
var cetak = [];
for (i = 0; i < adata.content.length; i++) {
cetak.push(adata.content[i] + "\r\n");
}
var notaout = [];
for (i = 0; i < adata.config.times; i++) {
notaout = notaout.concat(cetak);
}
return notaout;
};
this.construct(options);
};
doPrint() {
if (!this.url) {
console.warn('Url Laporan dibutuhkan!');
return;
}
$.ajax({
type: 'POST',
url: this.url,
data: FoniaJS.RequestParam(this.params),
success: (txt) => {
let $printData = $('#dataPrint');
if (!$printData.length) {
$printData = $('<div/>', {
id: 'dataPrint',
class: 'd-none d-print-block print-body'
}).appendTo('body');
}
$printData.html(txt);
window.print();
},
error: typeof this.onFailure === 'function' ? this.onFailure : null
});
return this;
}
doPreview() {
if (!this.url) {
console.warn('Url Laporan dibutuhkan!');
return;
}
$.ajax({
type: 'POST',
url: this.url,
data: FoniaJS.RequestParam(this.params),
success: (txt) => {
if (!txt) {
FoniaJS.showInfo('Tidak ada data untuk pembuatan laporan !');
return false;
}
const modalHtml = `
<div class="modal fade" id="winAPPPrint" tabindex="-1" role="dialog" data-bs-keyboard="false" data-bs-show="false" data-bs-backdrop="static" aria-labelledby="winAPPPrintLabel" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered modal-dialog-scrollable modal-fs" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="winAPPPrintLabel">Preview Laporan</h5>
<div class="ms-auto">
<button class="btn me-2" id="tombCetak" type="button" aria-label="Cetak">
<i class="bi-printer me-2"></i><span aria-hidden="true">Cetak</span>
</button>
<button class="btn" type="button" data-bs-dismiss="modal" aria-label="Tutup">
<i class="bi-x-lg me-2"></i><span aria-hidden="true">Tutup</span>
</button>
</div>
</div>
<div class="modal-body print-body" id="divPrintP">${txt}</div>
</div>
</div>
</div>
`;
$('#winAPPPrint').modal('dispose').remove();
$(modalHtml)
.on('shown.bs.modal', () => {
$('#winAPPPrint #tombCetak').off('click').on('click', () => {
let $printData = $('#dataPrint');
if (!$printData.length) {
$printData = $('<div/>', {
id: 'dataPrint',
class: 'd-none d-print-block print-body'
}).appendTo('body');
}
$printData.html($('#divPrintP').html());
FoniaJS.closeWindow($('#winAPPPrint'));
window.print();
});
})
.on('hidden.bs.modal', function () {
$(this).remove();
})
.modal('show');
},
error: typeof this.onFailure === 'function' ? this.onFailure : null
});
return this;
}
}
/**
* Class PrinterPos
*
* Direct Thermal POS Printer Class (via QZ Tray API)
*
* @license MIT, https://www.3fonia.com
* @copyright Copyright (c) 2008-2026, Siau Pheng <[email protected]>
*
* @param {object} options
* @param {string} options.url - Endpoint URL Print Page
* @param {number} [options.printer=FoniaJS._def_printer] - Printer Name
* @param {Array} [options.params] - Request payload
*/
class PrinterPos {
constructor(options = {}) {
this.url = typeof options === 'string' ? options : options.url || '';
this.printer = options.printer || (FoniaJS._def_printer ?? null);
this.cut_paper = options.cut_paper || '\x1D\x56\x41';
this.open_draw = options.open_draw || '\x1B\x70\x30\x19\x19';
this.chr_norm = options.chr_norm || '\x1B\x21\x01';
this.chr_bold = options.chr_bold || '\x1B\x21\x10';
this.chr_sbold = options.chr_sbold || '\x1B\x21\x31';
this.chr_uline = options.chr_uline || '\x1B\x21\x80';
this.params = options.params || [];
}
_detectPrinter() {
if (!qz?.websocket?.isActive?.()) {
FoniaJS.showInfo('Status Printer belum Active !');
return false;
}
return true;
}
setPrinter(nprint) {
if (nprint != null) this.printer = nprint;
return this;
}
addParam(id, val) {
this.params.push(val !== undefined ? `${id}=${encodeURIComponent(val)}` : id);
return this;
}
async exec() {
if (!this._detectPrinter()) return;
let printerName = this.printer;
if (typeof printerName === 'object' && printerName !== null) {
if (printerName.name !== undefined) {
printerName = printerName.name;
} else if (printerName.file !== undefined) {
printerName = printerName.file;
} else if (printerName.host !== undefined) {
printerName = `${printerName.host}:${printerName.port}`;
}
}
printerName = printerName ?? 'None';
this.params.push(`Printer=${printerName}`);
this.doPrint(this.printer, this.url, this.params);
}
doPrint(printerName, url, params) {
const qzcfg = qz.configs.create(printerName);
new Request({
url,
sendParam: params,
onSuccess: (ajson) => {
if (typeof ajson === 'object' && ajson !== null) {
const printPayload = ajson.type === 'escp' ? this.genEscp(ajson) : this.genEpl(ajson);
qz.print(qzcfg, printPayload);
} else {
FoniaJS.showInfo('Error dalam proses Cetak.');
}
}
}).send();
}
genEscp(adata) {
const ohead = ['\x1B\x40', '\x1B\x61\x01'];
if (adata.config?.logo) {
ohead.push({
type: 'raw',
format: 'image',
flavor: 'base64',
data: adata.config.logo,
options: { language: 'ESCPOS', dotDensity: 'double' }
});
ohead.push('\x1B\x32');
}
if (parseInt(adata.config?.fontsize, 10) === 2) {
this.chr_norm = '\x1B\x21\x02';
}
if (Array.isArray(adata.header)) {
adata.header.forEach((headerLine, index) => {
if (index === 0 && !adata.config?.logo) ohead.push(this.chr_bold);
ohead.push(`${headerLine}\x0A`);
if (index === 0 && !adata.config?.logo) ohead.push(this.chr_norm);
});
}
let ocetak = [];
if (Array.isArray(adata.content)) {
adata.content.forEach((sheetContent) => {
if (!sheetContent?.length) return;
const osheet = [...ohead];
sheetContent.forEach((line) => {
const formattedLine = line
.replace('_SBOLD_', this.chr_sbold)
.replace('_BOLD_', this.chr_bold)
.replace('_NORM_', this.chr_norm)
.replace('_ULINE_', this.chr_uline);
osheet.push(`${formattedLine}\x0A`);
});
if (adata.config?.cutpaper) {
osheet.push(`${this.cut_paper}\x0A`);
}
ocetak = ocetak.concat(osheet);
});
}
let notaout = [];
if (adata.config?.opendraw) {
notaout.push(this.open_draw);
}
const times = adata.config?.times || 1;
for (let i = 0; i < times; i++) {
notaout = notaout.concat(ocetak);
}
return notaout;
}
genEpl(adata) {
const cetak = (adata.content || []).map((line) => `${line}\r\n`);
let notaout = [];
const times = adata.config?.times || 1;
for (let i = 0; i < times; i++) {
notaout = notaout.concat(cetak);
}
return notaout;
}
}
+95 -68
View File
@@ -1,84 +1,111 @@
/*******************************************************************
Module : /assets/js/simulator.js
Desc. : JS Class Simulator Data dalam Code
Created By : Siau Pheng (siaupheng@3fonia.com).
Created Date : March 18th, 2021.
Last Modified : December 26th, 2025.
Last Modified : November 2nd, 2026.
(c) 3FONIA Software; WWW.3FONIA.COM.
*******************************************************************
*
* Class Simulator
*
* Simulator Data untuk sinkron data dengan JSON
*
* @license MIT, https://www.3fonia.com
* @copyright Copyright (c) 2008-2026, Siau Pheng <siaupheng@3fonia.com>
*
* @param {object} options
* @param {string} options.url - Endpoint URL Print Page
* @param {Array} [options.params] - Request payload
*/
*******************************************************************/
class Simulator {
constructor(options = {}) {
this.url = typeof options === 'string' ? options : options.url || '';
this.params = options.params || [];
}
var Simulator = function(options){
var vars = {
'url': '',
'param': []
};
this.construct = function(options){
if (typeof options == 'object') {
$.extend(vars, options);
} else vars.url = options;
};
this.set_url = function (url){
vars.url = url;
};
this.add_param = function (par_id, par_val){
var _getvalue = (par_val) ? par_id + "=" + escape(par_val) : par_id;
vars.param.push(_getvalue);
},
this.doPreview = function(){
if (!vars.url) console.warn(vars.param + ", cek url php!");
addParam(id, val) {
this.params.push(val !== undefined ? `${id}=${encodeURIComponent(val)}` : id);
return this;
}
doPreview() {
if (!this.url) {
console.warn('Url Simulator dibutuhkan!');
return;
}
$.ajax({
type: 'POST',
url: vars.url,
data: FoniaJS.RequestParam(vars.param),
success: function(data){
if (data.length == 0) {
url: this.url,
data: FoniaJS.RequestParam(this.params),
dataType: 'json',
error: typeof onFailure === 'function' ? onFailure : null,
success: (data) => {
if (!data || data.length === 0) {
FoniaJS.showInfo('Tidak ada data !');
return false;
}
var _divd = '';
for (var no = 0; no < data.length; no++) {
_divd += '<div class="sim-code px-2 py-2 mb-1 border border-info">' + data[no] + '</div>';
}
var _wtxt =
'<div class="modal fade" id="winAPPSim" tabindex="-1" role="dialog" data-bs-keyboard="false" data-bs-show="false" data-bs-backdrop="static" aria-labelledby="winAPPSimLabel" aria-hidden="true">'+
'<div class="modal-dialog modal-dialog-centered modal-dialog-scrollable modal-fs" role="document">'+
'<div class="modal-content">'+
'<div class="modal-header"><h5 class="modal-title" id="winAPPSimLabel">Code Simulator</h5><div class="ms-auto"><button class="btn me-2" id="tombSalin" type="button" aria-label="Salin Semua"><i class="bi-copy me-2"></i><span aria-hidden="true">Salin Semua</span></button><button class="btn" type="button" data-bs-dismiss="modal" aria-label="Tutup"><i class="bi-x-lg me-2"></i><span aria-hidden="true">Tutup</span></button></div></div>'+
'<div class="modal-body">'+_divd+'</div>'+
'</div></div></div>';
if ($('#winAPPSim')) $('#winAPPSim').modal('dispose').remove();
$(_wtxt).on('shown.bs.modal', function(e){
$('#winAPPSim').find('#tombSalin').click(function(){
window.getSelection().removeAllRanges();
$('.sim-code').each(function(i, ele) {
var range = document.createRange();
range.selectNode(ele);
window.getSelection().addRange(range);
const divContent = data
.map(item => `<div class="sim-code px-2 py-2 mb-1 border border-info">${item}</div>`)
.join('');
const modalHtml = `
<div class="modal fade" id="winAPPSim" tabindex="-1" role="dialog" data-bs-keyboard="false" data-bs-show="false" data-bs-backdrop="static" aria-labelledby="winAPPSimLabel" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered modal-dialog-scrollable modal-fs" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="winAPPSimLabel">Code Simulator</h5>
<div class="ms-auto">
<button class="btn me-2" id="tombSalin" type="button" aria-label="Salin Semua">
<i class="bi-copy me-2"></i><span aria-hidden="true">Salin Semua</span>
</button>
<button class="btn" type="button" data-bs-dismiss="modal" aria-label="Tutup">
<i class="bi-x-lg me-2"></i><span aria-hidden="true">Tutup</span>
</button>
</div>
</div>
<div class="modal-body">${divContent}</div>
</div>
</div>
</div>`;
$('#winAPPSim').modal('dispose').remove();
$(modalHtml)
.on('shown.bs.modal', function () {
const $modal = $(this);
// Copy all code blocks
$modal.find('#tombSalin').on('click', () => {
window.getSelection().removeAllRanges();
$('.sim-code').each((_, ele) => {
const range = document.createRange();
range.selectNode(ele);
window.getSelection().addRange(range);
});
document.execCommand('copy');
window.getSelection().removeAllRanges();
FoniaJS.showInfo('Sukses salin semua Kode !');
});
document.execCommand('copy');
window.getSelection().removeAllRanges();
FoniaJS.showInfo('Sukses salin semua Kode !');
});
$('#winAPPSim').find('.sim-code').click(function(){
var range = document.createRange();
range.selectNode(this);
window.getSelection().removeAllRanges();
window.getSelection().addRange(range);
document.execCommand('copy');
window.getSelection().removeAllRanges();
//$(this).addClass('bg-success');
$(this).addClass('d-none');
});
}).on('hidden.bs.modal', function(e){
$(this).remove();
}).modal('show');
},
error: (typeof vars.onFailure === "function") ? vars.onFailure : null,
dataType: 'json'
// Copy individual code block and hide it
$modal.find('.sim-code').on('click', function () {
const range = document.createRange();
range.selectNode(this);
window.getSelection().removeAllRanges();
window.getSelection().addRange(range);
document.execCommand('copy');
window.getSelection().removeAllRanges();
$(this).addClass('d-none');
});
})
.on('hidden.bs.modal', function () {
$(this).remove();
})
.modal('show');
}
});
};
this.construct(options);
};
}
}