From 11e17bd978ec3351c49fe07acb36a699cc20a814 Mon Sep 17 00:00:00 2001 From: Siau Pheng Date: Mon, 2 Nov 2026 16:10:36 +0700 Subject: [PATCH] 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. --- assets/js/download.js | 150 ++++++------ assets/js/fonia3.js | 9 +- assets/js/pdf.js | 182 ++++++++++----- assets/js/pole-display.js | 16 +- assets/js/print.js | 480 ++++++++++++++++++++------------------ assets/js/simulator.js | 163 +++++++------ 6 files changed, 560 insertions(+), 440 deletions(-) diff --git a/assets/js/download.js b/assets/js/download.js index 7e91e65..2d2d581 100644 --- a/assets/js/download.js +++ b/assets/js/download.js @@ -4,82 +4,86 @@ Desc. : Fungsi-fungsi download data & laporan Created By : Siau Pheng (siaupheng@3fonia.com). 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 + * + * @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); + } +} diff --git a/assets/js/fonia3.js b/assets/js/fonia3.js index f8b199b..e65b7b8 100644 --- a/assets/js/fonia3.js +++ b/assets/js/fonia3.js @@ -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({ diff --git a/assets/js/pdf.js b/assets/js/pdf.js index 45e594f..a2c0b0a 100644 --- a/assets/js/pdf.js +++ b/assets/js/pdf.js @@ -1,66 +1,126 @@ /******************************************************************* - Module : /assets/js/pdf.js - Desc. : JS - Cetak Laporan PDF - Created By : Siau Pheng (siaupheng@3fonia.com). - Created Date : April 6th, 2015. - Last Modified : October 18th, 2020. + Module : /assets/js/pdf.js + Desc. : JS - Cetak Laporan PDF + Created By : Siau Pheng (siaupheng@3fonia.com). + 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 + * + * @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 = - ''; - 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 = ` + + `; + + $('#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)); + } +} diff --git a/assets/js/pole-display.js b/assets/js/pole-display.js index 2f7ba96..8f6de81 100644 --- a/assets/js/pole-display.js +++ b/assets/js/pole-display.js @@ -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); } } diff --git a/assets/js/print.js b/assets/js/print.js index ad69609..70984b7 100644 --- a/assets/js/print.js +++ b/assets/js/print.js @@ -4,233 +4,265 @@ Desc. : JS Class Printer & Printer POS Created By : Siau Pheng (siaupheng@3fonia.com). 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 + * + * @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 = $('
', {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 = - ''; - if ($('#winAPPPrint')) $('#winAPPPrint').modal('dispose').remove(); - $(_wtxt).on('shown.bs.modal', (e) => { - $('#winAPPPrint').find('#tombCetak').click(() => { - var $printData = $('#dataPrint'); - if (!$printData.length) { - $printData = $('
', {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 = $('
', { + 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 = ` + + `; + + $('#winAPPPrint').modal('dispose').remove(); + $(modalHtml) + .on('shown.bs.modal', () => { + $('#winAPPPrint #tombCetak').off('click').on('click', () => { + let $printData = $('#dataPrint'); + if (!$printData.length) { + $printData = $('
', { + 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 + * + * @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; + } +} diff --git a/assets/js/simulator.js b/assets/js/simulator.js index 2da8e25..01897e9 100644 --- a/assets/js/simulator.js +++ b/assets/js/simulator.js @@ -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 + * + * @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 += '
' + data[no] + '
'; - } - var _wtxt = - ''; - 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 => `
${item}
`) + .join(''); + + const modalHtml = ` + `; + + $('#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); -}; + } +}