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); -}; + } +}