5 Commits
Author SHA1 Message Date
siaupheng 11e17bd978 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. 2026-11-02 16:10:36 +07:00
siaupheng 929f3af79a Refactor PoleDisplay class and update Auth user_log method
- Converted PoleDisplay from a function-based structure to a class-based structure for improved readability and maintainability.
- Simplified the Open and Close methods to handle port state more clearly.
- Enhanced the Send method to support both string and array inputs for sending data.
- Updated the cd_Text method to use modern JavaScript features for padding calculations.
- Modified the user_log method in Auth class to streamline database fetch and update logic.
2026-11-01 15:55:28 +07:00
siaupheng cf100a86e2 update last modified date and refactor HTTP host retrieval in Menu class 2026-10-30 16:47:21 +07:00
siaupheng a538cc0cc5 update dependencies in composer.json for compatibility 2026-10-29 13:08:14 +07:00
siaupheng 4c8399cf64 add gen_uuidv4 and gen_uuidv7 2026-10-29 12:21:42 +07:00
11 changed files with 1584 additions and 1629 deletions
+226 -340
View File
@@ -1,40 +1,8 @@
var __extends = (undefined && undefined.__extends) || (function () {
var extendStatics = function (d, b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
return extendStatics(d, b);
};
return function (d, b) {
if (typeof b !== "function" && b !== null)
throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
var BaseResolver = (function () {
function BaseResolver(options) {
var AjaxResolver = class {
constructor(options) {
this._settings = $.extend(true, {}, this.getDefaults(), options);
}
BaseResolver.prototype.getDefaults = function () {
return {};
};
BaseResolver.prototype.getResults = function (limit, start, end) {
return this.results;
};
BaseResolver.prototype.search = function (q, cbk) {
cbk(this.getResults());
};
return BaseResolver;
}());
var AjaxResolver = (function (_super) {
__extends(AjaxResolver, _super);
function AjaxResolver(options) {
return _super.call(this, options) || this;
}
AjaxResolver.prototype.getDefaults = function () {
getDefaults() {
return {
url: '',
method: 'post',
@@ -43,42 +11,42 @@ var AjaxResolver = (function (_super) {
timeout: 0,
requestThrottling: 500
};
};
AjaxResolver.prototype.search = function (q, cbk) {
var _this = this;
if (this.jqXHR != null) {
this.jqXHR.abort();
}
search(q, cbk) {
if (this.jqXHR != null) this.jqXHR.abort();
var data = {};
data[this._settings.queryKey] = q;
if (this._settings.param.length > 0) $.extend(data, FoniaJS.RequestParam(this._settings.param));
if (this.requestTID) {
window.clearTimeout(this.requestTID);
if (this._settings.param.length > 0) {
$.extend(data, FoniaJS.RequestParam(this._settings.param));
}
this.requestTID = window.setTimeout(function () {
_this.jqXHR = $.ajax(_this._settings.url, {
method: _this._settings.method,
if (this.requestTID) window.clearTimeout(this.requestTID);
this.requestTID = window.setTimeout(() => {
this.jqXHR = $.ajax(this._settings.url, {
method: this._settings.method,
data: data,
timeout: _this._settings.timeout
timeout: this._settings.timeout
});
_this.jqXHR.done(function (result) {
//gnips: result in object
cbk((undefined !== result.autodata) ? result.autodata : result);
this.jqXHR.done(result => {
cbk(result.autodata !== undefined ? result.autodata : result);
});
_this.jqXHR.fail(function (err) {
var _a;
(_a = _this._settings) === null || _a === void 0 ? void 0 : _a.fail(err);
this.jqXHR.fail(err => {
this._settings.fail?.(err);
});
_this.jqXHR.always(function () {
_this.jqXHR = null;
this.jqXHR.always(() => {
this.jqXHR = null;
});
}, this._settings.requestThrottling);
}
};
return AjaxResolver;
}(BaseResolver));
var DropdownV5 = (function () {
function DropdownV5(e, formatItemCbk, autoSelect, noResultsText) {
var DropdownV5 = class {
constructor(e, formatItemCbk, autoSelect, noResultsText) {
this.initialized = false;
this.shown = false;
this.items = [];
@@ -88,84 +56,67 @@ var DropdownV5 = (function () {
this.autoSelect = autoSelect;
this.noResultsText = noResultsText;
}
DropdownV5.prototype.getElPos = function () {
var pos = $.extend({}, this._$el.position(), {
height: this._$el[0].offsetHeight
});
return pos;
};
DropdownV5.prototype.init = function () {
var _this = this;
getElPos() {
return $.extend({}, this._$el.position(), { height: this._$el[0].offsetHeight });
}
init() {
var pos = this.getElPos();
var fontsize = _this._$el.css('font-size');
this._dd = $('<div />');
this._dd.addClass('bootstrap-autocomplete dropdown-menu');
this._dd.insertAfter(this._$el);
this._dd.css({ top: pos.top + this._$el.outerHeight(), left: pos.left, width: this._$el.outerWidth(), 'font-size': fontsize });
this._dd.on('click', '.dropdown-item', function (evt) {
var fontsize = this._$el.css('font-size');
this._dd = $('<div />')
.addClass('bootstrap-autocomplete dropdown-menu')
.insertAfter(this._$el)
.css({
top: pos.top + this._$el.outerHeight(),
left: pos.left,
width: this._$el.outerWidth(),
'font-size': fontsize
});
this._dd.on('click', '.dropdown-item', evt => {
var item = $(evt.currentTarget).data('item');
_this.itemSelectedLaunchEvent(item);
this.itemSelectedLaunchEvent(item);
evt.preventDefault();
});
this._dd.on('keyup', function (evt) {
if (_this.shown) {
switch (evt.which) {
case 27:
_this.hide();
_this._$el.focus();
break;
}
this._dd.on('keyup', evt => {
if (this.shown && evt.which === 27) {
this.hide();
this._$el.focus();
return false;
}
});
this._dd.on('mouseenter', function (evt) {
_this.ddMouseover = true;
});
this._dd.on('mouseleave', function (evt) {
_this.ddMouseover = false;
});
this._dd.on('mouseenter', '.dropdown-item', function (evt) {
if (_this.haveResults) {
this._dd.on('mouseenter', () => { this.ddMouseover = true; });
this._dd.on('mouseleave', () => { this.ddMouseover = false; });
this._dd.on('mouseenter', '.dropdown-item', evt => {
if (this.haveResults) {
$(evt.currentTarget).closest('div').find('.dropdown-item.active').removeClass('active');
$(evt.currentTarget).addClass('active');
_this.mouseover = true;
this.mouseover = true;
}
});
this._dd.on('mouseleave', '.dropdown-item', function (evt) {
_this.mouseover = false;
});
this._dd.on('mouseleave', '.dropdown-item', () => { this.mouseover = false; });
this.initialized = true;
};
DropdownV5.prototype.checkInitialized = function () {
if (!this.initialized) {
this.init();
}
};
Object.defineProperty(DropdownV5.prototype, "isMouseOver", {
get: function () {
return this.mouseover;
},
enumerable: false,
configurable: true
});
Object.defineProperty(DropdownV5.prototype, "isDdMouseOver", {
get: function () {
return this.ddMouseover;
},
enumerable: false,
configurable: true
});
Object.defineProperty(DropdownV5.prototype, "haveResults", {
get: function () {
return (this.items.length > 0);
},
enumerable: false,
configurable: true
});
DropdownV5.prototype.focusNextItem = function (reversed) {
if (this.haveResults) {
checkInitialized() {
if (!this.initialized) this.init();
}
get isMouseOver() { return this.mouseover; }
get isDdMouseOver() { return this.ddMouseover; }
get haveResults() { return this.items.length > 0; }
focusNextItem(reversed) {
if (!this.haveResults) return;
var currElem = this._dd.find('.dropdown-item.active');
var nextElem = reversed ? currElem.prev() : currElem.next();
if (nextElem.length === 0) {
nextElem = reversed ? this._dd.find('.dropdown-item').last() : this._dd.find('.dropdown-item').first();
}
@@ -173,109 +124,91 @@ var DropdownV5 = (function () {
nextElem.addClass('active');
this.scrollTo(nextElem);
}
};
DropdownV5.prototype.focusPreviousItem = function () {
this.focusNextItem(true);
};
DropdownV5.prototype.selectFocusItem = function () {
this._dd.find('.dropdown-item.active').trigger('click');
};
Object.defineProperty(DropdownV5.prototype, "isItemFocused", {
get: function () {
if (this._dd && this.isShown() && (this._dd.find('.dropdown-item.active').length > 0)) {
return true;
focusPreviousItem() { this.focusNextItem(true); }
selectFocusItem() { this._dd.find('.dropdown-item.active').trigger('click'); }
get isItemFocused() {
return !!(this._dd && this.isShown() && this._dd.find('.dropdown-item.active').length > 0);
}
return false;
},
enumerable: false,
configurable: true
});
DropdownV5.prototype.show = function () {
show() {
if (!this.shown) {
this._dd.addClass('show');
this.shown = true;
this._$el.trigger('autocomplete.dd.shown');
}
};
DropdownV5.prototype.isShown = function () {
return this.shown;
};
DropdownV5.prototype.hide = function () {
}
isShown() { return this.shown; }
hide() {
if (this.shown) {
this._dd.removeClass('show');
this.shown = false;
this._$el.trigger('autocomplete.dd.hidden');
}
};
DropdownV5.prototype.updateItems = function (items, searchText) {
}
updateItems(items, searchText) {
this.items = items;
this.searchText = searchText;
this.refreshItemList();
};
DropdownV5.prototype.showMatchedText = function (text, qry) {
}
showMatchedText(text, qry) {
var startIndex = text.toLowerCase().indexOf(qry.toLowerCase());
if (startIndex > -1) {
var endIndex = startIndex + qry.length;
return text.slice(0, startIndex) + '<b>'
+ text.slice(startIndex, endIndex) + '</b>'
+ text.slice(endIndex);
return text.slice(0, startIndex) + '<b>' + text.slice(startIndex, endIndex) + '</b>' + text.slice(endIndex);
}
return text;
};
DropdownV5.prototype.refreshItemList = function () {
var _this = this;
}
refreshItemList() {
this.checkInitialized();
this._dd.empty();
var liList = [];
if (this.items.length > 0) {
this.items.forEach(function (item) {
var itemFormatted = _this.formatItem(item);
if (typeof itemFormatted === 'string') {
itemFormatted = { text: itemFormatted };
}
var itemText;
var itemHtml;
itemText = _this.showMatchedText(itemFormatted.text, _this.searchText);
if (itemFormatted.html !== undefined) {
itemHtml = itemFormatted.html;
} else {
itemHtml = itemText;
}
var disabledItem = itemFormatted.disabled;
var li = $('<a >');
li.addClass('dropdown-item')
//css bisa dimasukkan ke style default + cursor select
//.css({ 'overflow': 'hidden', 'text-overflow': 'ellipsis' })
this.items.forEach(item => {
var itemFormatted = this.formatItem(item);
if (typeof itemFormatted === 'string') itemFormatted = { text: itemFormatted };
var itemText = this.showMatchedText(itemFormatted.text, this.searchText);
var itemHtml = itemFormatted.html !== undefined ? itemFormatted.html : itemText;
var li = $('<a />')
.addClass('dropdown-item')
.html(itemHtml)
.data('item', item);
if (disabledItem) {
li.addClass('disabled');
}
if (itemFormatted.disabled) li.addClass('disabled');
liList.push(li);
});
this._dd.append(liList);
this.show();
}
else {
if (this.noResultsText === '') {
this.hide();
}
else {
var li = $('<a >');
li.addClass('dropdown-item disabled')
.html(this.noResultsText);
} else if (this.noResultsText !== '') {
var li = $('<a />').addClass('dropdown-item disabled').html(this.noResultsText);
liList.push(li);
this._dd.append(liList);
this.show();
} else {
this.hide();
}
}
};
DropdownV5.prototype.itemSelectedLaunchEvent = function (item) {
itemSelectedLaunchEvent(item) {
this._$el.trigger('autocomplete.select', item);
};
DropdownV5.prototype.scrollTo = function (el) {
var _he = this._dd.height(), _to = this._dd.scrollTop(), _ito = el.position().top, _ihe = el.outerHeight(),
}
scrollTo(el) {
var _he = this._dd.height(),
_to = this._dd.scrollTop(),
_ito = el.position().top,
_ihe = el.outerHeight(),
_nto = _to, _wra = false;
if (_ito < 0) {
_wra = el.is(':first-child');
_nto = _wra ? 0 : _to - _he / 2;
@@ -284,12 +217,11 @@ var DropdownV5 = (function () {
_nto = _wra ? (_ito + _ihe - _he) : _to + _he / 2;
}
this._dd.scrollTop(_nto);
}
};
return DropdownV5;
}());
var AutoComplete = (function () {
function AutoComplete(element, options) {
var AutoComplete = class {
constructor(element, options) {
this._selectedItem = null;
this._defaultValue = null;
this._defaultText = null;
@@ -302,230 +234,185 @@ var AutoComplete = (function () {
autoSelect: true,
noResultsText: 'Pencarian Tidak Ketemu',
preventEnter: false,
events: {
typed: null,
searchPre: null,
search: null,
searchPost: null,
select: null,
focus: null,
},
//gnips: adding callback, keyfunc and breakvalue
selectFunc: function(){},
keyFunc: function(){},
events: { typed: null, searchPre: null, search: null, searchPost: null, select: null, focus: null },
selectFunc: () => {},
keyFunc: () => {},
breakFocus: false,
breakValue: []
};
this._el = element;
this._$el = $(this._el);
this.manageInlineDataAttributes();
if (typeof options === 'object') {
this._settings = $.extend(true, {}, this.getSettings(), options);
}
this.init();
}
AutoComplete.prototype.manageInlineDataAttributes = function () {
manageInlineDataAttributes() {
var s = this.getSettings();
if (this._$el.data('url')) {
s.resolverSettings.url = this._$el.data('url');
if (this._$el.data('url')) s.resolverSettings.url = this._$el.data('url');
if (this._$el.data('default-value')) this._defaultValue = this._$el.data('default-value');
if (this._$el.data('default-text')) this._defaultText = this._$el.data('default-text');
if (this._$el.data('noresults-text') !== undefined) s.noResultsText = this._$el.data('noresults-text');
}
if (this._$el.data('default-value')) {
this._defaultValue = this._$el.data('default-value');
}
if (this._$el.data('default-text')) {
this._defaultText = this._$el.data('default-text');
}
if (this._$el.data('noresults-text') !== undefined) {
s.noResultsText = this._$el.data('noresults-text');
}
};
AutoComplete.prototype.getSettings = function () {
return this._settings;
};
AutoComplete.prototype.init = function () {
getSettings() { return this._settings; }
init() {
this.bindDefaultEventListeners();
if (this._settings.resolver === 'ajax') {
this.resolver = new AjaxResolver(this._settings.resolverSettings);
}
this._dd = new DropdownV5(this._$el, this._settings.formatResult, this._settings.autoSelect, this._settings.noResultsText);
};
AutoComplete.prototype.bindDefaultEventListeners = function () {
var _this = this;
this._$el.on('keydown', function (evt) {
//gnips: check breakValue and call keydown function
(('' == _this._$el.val() || ((_this._settings.breakValue.length > 0) && (_this._settings.breakValue.indexOf(_this._$el.val()) != -1))) && _this._settings.keyFunc(evt.which, _this._$el.val()));
}
bindDefaultEventListeners() {
this._$el.on('keydown', evt => {
var val = this._$el.val();
if ((val === '' || (this._settings.breakValue.length > 0 && this._settings.breakValue.indexOf(val) !== -1))) {
this._settings.keyFunc(evt.which, val);
}
switch (evt.which) {
case 9:
if (_this._dd.isItemFocused) {
_this._dd.selectFocusItem();
if (!this._settings.breakFocus) {
if (this._dd.isItemFocused) this._dd.selectFocusItem();
this._dd.hide();
}
_this._dd.hide();
break;
case 13:
if (_this._dd.isItemFocused) {
_this._dd.selectFocusItem();
}
_this._dd.hide();
if (_this._settings.preventEnter) {
evt.preventDefault();
}
if (this._dd.isItemFocused) this._dd.selectFocusItem();
this._dd.hide();
if (this._settings.preventEnter) evt.preventDefault();
break;
case 40:
_this._dd.focusNextItem();
this._dd.focusNextItem();
break;
case 38:
_this._dd.focusPreviousItem();
this._dd.focusPreviousItem();
break;
}
});
this._$el.on('keyup', function (evt) {
this._$el.on('keyup', evt => {
switch (evt.which) {
case 16:
case 17:
case 18:
case 39:
case 37:
case 36:
case 35:
case 16: case 17: case 18: case 39: case 37: case 36: case 35: case 40: case 38:
break;
case 13:
_this._dd.hide();
break;
case 27:
_this._dd.hide();
break;
case 40:
break;
case 38:
case 13: case 27:
this._dd.hide();
break;
default:
_this._selectedItem = null;
var newValue = _this._$el.val();
_this.handlerTyped(newValue);
this._selectedItem = null;
this.handlerTyped(this._$el.val());
}
});
this._$el.on('blur', function (evt) {
if (!_this._dd.isMouseOver && _this._dd.isDdMouseOver && _this._dd.isShown()) {
setTimeout(function () { _this._$el.focus(); });
_this._$el.focus();
}
else if (!_this._dd.isMouseOver) {
_this._dd.hide();
this._$el.on('blur', () => {
if (this._settings.breakFocus) return;
if (!this._dd.isMouseOver && this._dd.isDdMouseOver && this._dd.isShown()) {
setTimeout(() => { this._$el.focus(); });
this._$el.focus();
} else if (!this._dd.isMouseOver) {
this._dd.hide();
}
});
this._$el.on('autocomplete.select', function (evt, item) {
_this._selectedItem = item;
_this.itemSelectedDefaultHandler(item);
this._$el.on('autocomplete.select', (evt, item) => {
this._selectedItem = item;
this.itemSelectedDefaultHandler(item);
});
this._$el.on('paste', function (evt) {
setTimeout(function () {
_this._$el.trigger('keyup', evt);
}, 0);
this._$el.on('paste', evt => {
setTimeout(() => { this._$el.trigger('keyup', evt); }, 0);
});
};
AutoComplete.prototype.handlerTyped = function (newValue) {
}
handlerTyped(newValue) {
if (this._settings.events.typed !== null) {
newValue = this._settings.events.typed(newValue, this._$el);
if (!newValue) {
return;
}
if (!newValue) return;
}
if (newValue.length >= this._settings.minLength) {
this._searchText = newValue;
this.handlerPreSearch();
}
else {
} else {
this._dd.hide();
}
};
AutoComplete.prototype.handlerPreSearch = function () {
}
handlerPreSearch() {
if (this._settings.events.searchPre !== null) {
var newValue = this._settings.events.searchPre(this._searchText, this._$el);
if (!newValue)
return;
if (!newValue) return;
this._searchText = newValue;
}
this.handlerDoSearch();
};
AutoComplete.prototype.handlerDoSearch = function () {
var _this = this;
if (this._settings.events.search !== null) {
this._settings.events.search(this._searchText, function (results) {
_this.postSearchCallback(results);
}, this._$el);
}
else {
if (this.resolver) {
this.resolver.search(this._searchText, function (results) {
_this.postSearchCallback(results);
handlerDoSearch() {
if (this._settings.events.search !== null) {
this._settings.events.search(this._searchText, results => {
this.postSearchCallback(results);
}, this._$el);
} else if (this.resolver) {
this.resolver.search(this._searchText, results => {
this.postSearchCallback(results);
});
}
}
};
AutoComplete.prototype.postSearchCallback = function (results) {
postSearchCallback(results) {
if (this._settings.events.searchPost) {
results = this._settings.events.searchPost(results, this._$el);
if ((typeof results === 'boolean') && !results)
return;
if (typeof results === 'boolean' && !results) return;
}
this.handlerStartShow(results);
};
AutoComplete.prototype.handlerStartShow = function (results) {
//gnips: adding auto select when barcode result of 1 item only
if (1 == results.length && 1 == (void 0 !== results[0].bc ? results[0].bc : 0)) {
this._$el.trigger('autocomplete.select', results[0]),
}
handlerStartShow(results) {
var bcVal = results[0]?.bc ?? 0;
if (results.length === 1 && bcVal === 1) {
this._$el.trigger('autocomplete.select', results[0]);
this._dd.hide();
} else this._dd.updateItems(results, this._searchText);
};
AutoComplete.prototype.itemSelectedDefaultHandler = function (item) {
} else {
this._dd.updateItems(results, this._searchText);
}
}
itemSelectedDefaultHandler(item) {
if (item != null) {
var itemFormatted = this._settings.formatResult(item);
if (typeof itemFormatted === 'string') {
itemFormatted = { text: itemFormatted };
}
if (typeof itemFormatted === 'string') itemFormatted = { text: itemFormatted };
this._$el.val(itemFormatted.text);
}
else {
} else {
this._$el.val('');
}
this._selectedItem = item;
//gnips: callback with selected item
this._settings.selectFunc(item);
this._dd.hide();
};
AutoComplete.prototype.defaultFormatResult = function (item) {
if (typeof item === 'string') {
return { text: item };
}
else if (item.text) {
return item;
}
else {
defaultFormatResult(item) {
if (typeof item === 'string') return { text: item };
if (item.text) return item;
return { text: item.toString() };
}
};
AutoComplete.prototype.manageAPI = function (APICmd, params) {
if (APICmd === 'set') {
this.itemSelectedDefaultHandler(params);
}
else if (APICmd === 'clear') {
this.itemSelectedDefaultHandler(null);
}
else if (APICmd === 'show') {
this._$el.trigger('keyup');
}
else if (APICmd === 'updateResolver') {
this.resolver = new AjaxResolver(params);
manageAPI(APICmd, params) {
if (APICmd === 'set') this.itemSelectedDefaultHandler(params);
else if (APICmd === 'clear') this.itemSelectedDefaultHandler(null);
else if (APICmd === 'show') this._$el.trigger('keyup');
else if (APICmd === 'updateResolver') this.resolver = new AjaxResolver(params);
}
};
AutoComplete.NAME = 'autoComplete';
return AutoComplete;
}());
(function ($, window, document) {
// jQuery plugin wrapper
$.fn[AutoComplete.NAME] = function (optionsOrAPI, optionalParams) {
return this.each(function () {
var pluginClass;
pluginClass = $(this).data(AutoComplete.NAME);
var pluginClass = $(this).data(AutoComplete.NAME);
if (!pluginClass) {
pluginClass = new AutoComplete(this, optionsOrAPI);
$(this).data(AutoComplete.NAME, pluginClass);
@@ -533,4 +420,3 @@ var AutoComplete = (function () {
pluginClass.manageAPI(optionsOrAPI, optionalParams);
});
};
})(jQuery, window, document);
+76 -72
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 = [];
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();
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 || [];
}
} 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);
}
}
+610 -698
View File
File diff suppressed because it is too large Load Diff
+113 -53
View File
@@ -4,63 +4,123 @@
Desc. : JS - Cetak Laporan PDF
Created By : Siau Pheng ([email protected]).
Created Date : April 6th, 2015.
Last Modified : October 18th, 2020.
Last Modified : November 2nd, 2026.
(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;
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 || [];
}
$.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);
addParam(id, val) {
this.params.push(val !== undefined ? `${id}=${encodeURIComponent(val)}` : id);
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){
}
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');
};
this.construct(options);
};
})
.modal('show');
})
.fail((xhr, status, err) => console.error('Gagal proses preview PDF karena : ', err));
}
}
+57 -61
View File
@@ -4,90 +4,86 @@
Desc. : JS Class Tampil Data di Pole Display POS
Created By : Siau Pheng ([email protected]).
Created Date : October 12th, 2025.
Last Modified : November 1st, 2026.
(c) 3FONIA Software; WWW.3FONIA.COM.
*******************************************************************/
var PoleDisplay = function(port, options){
var _cd_port = '';
var _cd_pole = '';
var _options = {
class PoleDisplay {
constructor(port, options = {}) {
this.port = port;
this.pole = '';
this.options = Object.assign({
rx: { start: '\x02', end: '\x0D', width: null },
baudRate: 9600,
dataBits: 8,
stopBits: 1,
parity: 'NONE',
flowControl: 'NONE'
};
this.construct = function (port, options){
$.extend(_options, options);
_cd_port = port;
};
this.Open = function (){
if (_cd_pole === '') {
qz.serial.openPort(_cd_port, _options).then(() => {
_cd_pole = _cd_port;
this.cd_Error(_cd_port+' opened');
}).then(() => {
qz.serial.sendData(_cd_pole, {type:'HEX',data:'x1Bx40'}, _options).catch(this.cd_Error);
}).catch(this.cd_Error);
}, options);
}
};
this.Close = function (){
qz.serial.closePort(_cd_port).then(() => {
_cd_pole = '';
this.cd_Error(_cd_port+' closed');
}).catch(this.cd_Error);
};
Open() {
if (this.pole) return;
this.Send = function (txt){
if (_cd_pole === '') return false;
var ascii_to_hexa = function(str) {
var arr1 = [];
for (var n = 0, l = str.length; n < l; n++) {
var hex = Number(str.charCodeAt(n)).toString(16);
if (hex.length==1) hex = '0' + hex;
arr1.push(hex.toUpperCase());
qz.serial.openPort(this.port, this.options)
.then(() => {
this.pole = this.port;
this._conerr(`${this.port} opened`);
return qz.serial.sendData(this.pole, { type: 'HEX', data: 'x1Bx40' }, this.options);
})
.catch(this._conerr);
}
return 'x' + arr1.join('x');
};
var _txt = txt;
Close() {
if (!this.pole) return;
qz.serial.closePort(this.port)
.then(() => {
this.pole = '';
this._conerr(`${this.port} closed`);
})
.catch(this._conerr);
}
Send(txt) {
if (!this.pole) return false;
// Convert string to ASCII hex format
const asciiToHex = str =>
Array.from(str, char => char.charCodeAt(0).toString(16).padStart(2, '0').toUpperCase())
.join('x');
let textToSend = txt;
if (Array.isArray(txt)) {
_txt = '';
txt.forEach((ele) => {
_txt += this.cd_Text(ele['t'], ele['a']);
});
textToSend = txt.map(ele => this._aligntext(ele.t, ele.a)).join('');
}
qz.serial.sendData(_cd_pole, {type:'HEX',data:'x10C'+ascii_to_hexa(_txt)}, _options).catch(this.cd_Error);
};
this.cd_Text = function (text, align){
const totWidth = 20;
const txtLength = text.length;
if (txtLength >= totWidth) {
return text.substring(0, totWidth);
const hexData = `x10C${asciiToHex(textToSend)}`;
qz.serial.sendData(this.pole, { type: 'HEX', data: hexData }, this.options)
.catch(this._conerr);
}
const padding = totWidth - txtLength;
var lPadding = 0;
var rPadding = padding;
if (align === null) align = 'L';
if (align == 'C'){
_aligntext(text, align = 'L') {
const totWidth = 20;
if (text.length >= totWidth) return text.substring(0, totWidth);
const padding = totWidth - text.length;
let lPadding = 0, rPadding = padding;
if (align === 'C') {
lPadding = Math.floor(padding / 2);
rPadding = Math.ceil(padding / 2);
} else if (align == 'R'){
} else if (align === 'R') {
lPadding = padding;
rPadding = 0;
}
return " ".repeat(lPadding) + text + " ".repeat(rPadding);
};
this.cd_Error = function (err){
return ' '.repeat(lPadding) + text + ' '.repeat(rPadding);
}
_conerr(err) {
console.error(err);
};
this.construct(port, options);
};
}
}
+201 -169
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;
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;
}
doPrint() {
if (!this.url) {
console.warn('Url Laporan dibutuhkan!');
return;
}
$.ajax({
type: 'POST',
url: vars.url,
data: FoniaJS.RequestParam(vars.param),
url: this.url,
data: FoniaJS.RequestParam(this.params),
success: (txt) => {
var $printData = $('#dataPrint');
let $printData = $('#dataPrint');
if (!$printData.length) {
$printData = $('<div/>', {id:'dataPrint','class':'d-none d-print-block print-body'}).appendTo('body');
$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,
error: typeof this.onFailure === 'function' ? this.onFailure : null
});
return this;
};
this.doPreview = function(){
if (!vars.url) console.warn(vars.param + ", cek url php!");
}
doPreview() {
if (!this.url) {
console.warn('Url Laporan dibutuhkan!');
return;
}
$.ajax({
type: 'POST',
url: vars.url,
data: FoniaJS.RequestParam(vars.param),
url: this.url,
data: FoniaJS.RequestParam(this.params),
success: (txt) => {
if ('' == 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');
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 = $('<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) => {
})
.on('hidden.bs.modal', function () {
$(this).remove();
}).modal('show');
})
.modal('show');
},
error: (typeof vars.onFailure === "function") ? vars.onFailure : null,
error: typeof this.onFailure === 'function' ? this.onFailure : null
});
};
this.construct(options);
};
return this;
}
}
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()) {
/**
* 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;
};
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';
setPrinter(nprint) {
if (nprint != null) this.printer = nprint;
return this;
}
_options.param.push('Printer='+_nprinter);
this.do_print(_options.printer, _options.url, _options.param);
addParam(id, val) {
this.params.push(val !== undefined ? `${id}=${encodeURIComponent(val)}` : id);
return this;
}
};
this.do_print = function(nama_p, url, params) {
var qzcfg = qz.configs.create(nama_p);
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: 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);
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();
};
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";
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 (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);
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);
});
}
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");
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);
});
}
var notaout = [];
if (adata.config.opendraw) {
notaout.push(_options.open_draw);
let notaout = [];
if (adata.config?.opendraw) {
notaout.push(this.open_draw);
}
for (i = 0; i < adata.config.times; i++) {
const times = adata.config?.times || 1;
for (let i = 0; i < 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++) {
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;
};
this.construct(options);
};
}
}
+80 -53
View File
@@ -1,60 +1,87 @@
/*******************************************************************
Module : /assets/js/simulator.js
Desc. : JS Class Simulator Data dalam Code
Created By : Siau Pheng ([email protected]).
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 <[email protected]>
*
* @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(){
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(function(i, ele) {
var range = document.createRange();
$('.sim-code').each((_, ele) => {
const range = document.createRange();
range.selectNode(ele);
window.getSelection().addRange(range);
});
@@ -62,23 +89,23 @@ var Simulator = function(options){
window.getSelection().removeAllRanges();
FoniaJS.showInfo('Sukses salin semua Kode !');
});
$('#winAPPSim').find('.sim-code').click(function(){
var range = document.createRange();
// 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('bg-success');
$(this).addClass('d-none');
});
}).on('hidden.bs.modal', function(e){
})
.on('hidden.bs.modal', function () {
$(this).remove();
}).modal('show');
},
error: (typeof vars.onFailure === "function") ? vars.onFailure : null,
dataType: 'json'
})
.modal('show');
}
});
};
this.construct(options);
};
}
}
+5 -5
View File
@@ -18,12 +18,12 @@
],
"require": {
"php": ">=8.3",
"flightphp/core": "^3.18",
"flightphp/core": "^3.19",
"flightphp/session": "^0.3",
"mpdf/mpdf": ">=8.0",
"avadim/fast-excel-writer": "^6.17",
"matthiasmullie/minify": ">=1.3",
"endroid/qr-code": ">=4.4"
"mpdf/mpdf": "^8.3",
"avadim/fast-excel-writer": "^6.19",
"matthiasmullie/minify": "^1.3",
"endroid/qr-code": "^6.1"
},
"autoload": {
"classmap": ["src/"],
+3 -3
View File
@@ -5,7 +5,7 @@
Module : /App/Auth.php
Created By : Siau Pheng ([email protected]).
Created Date : March 16th, 2008.
Last Modified : October 21st, 2026.
Last Modified : October 31st, 2026.
(c) 3FONIA Software; WWW.3FONIA.COM.
@@ -86,8 +86,8 @@ final class Auth {
public function user_log(): void {
$_user_id = $this->session->get('WEB_USER')['UserID'] ?? 0;
if ($_user_id === 0) return;
$a_hasil = $this->db->fetchRow('SELECT * FROM app_logs WHERE UserID=? ORDER BY Last_Date DESC LIMIT 1', [$_user_id])->getData();
if (count($a_hasil) > 0) {
$a_hasil = $this->db->fetchRow('SELECT * FROM app_logs WHERE UserID=? ORDER BY Last_Date DESC LIMIT 1', [$_user_id]);
if ($a_hasil) {
$this->db->update('app_operator', [ 'Last_IP' => $a_hasil['Last_IP'], 'Last_Date' => $a_hasil['Last_Date'] ], 'UserID=?', [$_user_id]);
}
$this->db->runQuery('INSERT IGNORE INTO app_logs SELECT UserID,Token,IP,DateStamp FROM app_token WHERE Token=?', [$this->session->get('APP_TOKEN')]);
+39 -1
View File
@@ -6,7 +6,7 @@
Desc. : Fungsi Custom Lain Lain
Created By : Siau Pheng ([email protected]).
Created Date : January 27th, 2008.
Last Modified : August 21st, 2026.
Last Modified : October 27th, 2026.
(c) 3FONIA Software; WWW.3FONIA.COM.
@@ -362,3 +362,41 @@ if (!function_exists('random_color')) {
return "rgba(".$col_r.",".$col_g.",".$col_b.",".$trans.")";
}
}
if (!function_exists('gen_uuidv4')) {
function gen_uuidv4(): string {
$data = random_bytes(16);
// Set version to 0100 (UUID v4)
$data[6] = chr(ord($data[6]) & 0x0f | 0x40);
// Set bits 6-7 to 10
$data[8] = chr(ord($data[8]) & 0x3f | 0x80);
return vsprintf('%s%s-%s-%s-%s-%s%s%s', str_split(bin2hex($data), 4));
}
}
if (!function_exists('gen_uuidv7')) {
function gen_uuidv7(int $time=0): string {
// Get timestamp in milliseconds (48 bits)
if (empty($time)) $time = (int) floor(microtime(true) * 1000);
// Generate 16 bytes of random data
$randomBytes = random_bytes(16);
// Pack the 48-bit timestamp into the first 6 bytes
$timeHex = str_pad(dechex($time), 12, '0', STR_PAD_LEFT);
$bytes = hex2bin($timeHex);
// Append 10 bytes of randomness (we need 16 total bytes)
$bytes .= substr($randomBytes, 0, 10);
// Apply the version bits (Version 7 -> 0b0111) onto byte 6
$bytes[6] = chr((ord($bytes[6]) & 0x0f) | 0x70);
// Apply the variant bits (Variant 1 -> 0b10xx) onto byte 8
$bytes[8] = chr((ord($bytes[8]) & 0x3f) | 0x80);
// Unpack to hex string and format with hyphens
$hex = bin2hex($bytes);
return sprintf('%08s-%04s-%04s-%04s-%012s',
substr($hex, 0, 8),
substr($hex, 8, 4),
substr($hex, 12, 4),
substr($hex, 16, 4),
substr($hex, 20, 12)
);
}
}
+2 -2
View File
@@ -5,7 +5,7 @@
Module : /Web/Menu.php
Created By : Siau Pheng ([email protected]).
Created Date : January 16th, 2008.
Last Modified : October 21st, 2026.
Last Modified : October 30th, 2026.
(c) 3FONIA Software; WWW.3FONIA.COM.
@@ -141,7 +141,7 @@ final class Menu {
$href1 = '//127.0.0.1/';
$href2 = "";
} else {
$_host = $this->app->request()->getVar('HTTP_ORIGIN') ?? $this->app->request()->getVar('HTTP_HOST') ?? $this->web_app['PARENT_SITE'];
$_host = $_SERVER['HTTP_ORIGIN'] ?? $_SERVER['HTTP_HOST'] ?? $this->web_app['PARENT_SITE'];
$_host = str_replace(['http://','https://'], "", $_host);
if ($this->web_app['PARENT_SUB'] === true) {
$_subs = explode('.', $_host);