Base64 = {
// private property
_keyStr: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",
// public method for encoding
encode: function (input) {
var output = "";
var chr1, chr2, chr3, enc1, enc2, enc3, enc4;
var i = 0;
input = Base64._utf8_encode(input);
while (i < input.length) {
chr1 = input.charCodeAt(i++);
chr2 = input.charCodeAt(i++);
chr3 = input.charCodeAt(i++);
enc1 = chr1 >> 2;
enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
enc4 = chr3 & 63;
if (isNaN(chr2)) {
enc3 = enc4 = 64;
} else if (isNaN(chr3)) {
enc4 = 64;
}
output = output +
this._keyStr.charAt(enc1) + this._keyStr.charAt(enc2) +
this._keyStr.charAt(enc3) + this._keyStr.charAt(enc4);
}
return output;
},
// public method for decoding
decode: function (input) {
var output = "";
var chr1, chr2, chr3;
var enc1, enc2, enc3, enc4;
var i = 0;
input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");
while (i < input.length) {
enc1 = this._keyStr.indexOf(input.charAt(i++));
enc2 = this._keyStr.indexOf(input.charAt(i++));
enc3 = this._keyStr.indexOf(input.charAt(i++));
enc4 = this._keyStr.indexOf(input.charAt(i++));
chr1 = (enc1 << 2) | (enc2 >> 4);
chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
chr3 = ((enc3 & 3) << 6) | enc4;
output = output + String.fromCharCode(chr1);
if (enc3 != 64) {
output = output + String.fromCharCode(chr2);
}
if (enc4 != 64) {
output = output + String.fromCharCode(chr3);
}
}
output = Base64._utf8_decode(output);
return output;
},
// private method for UTF-8 encoding
_utf8_encode: function (string) {
string = string.replace(/\r\n/g, "\n");
var utftext = "";
for (var n = 0; n < string.length; n++) {
var c = string.charCodeAt(n);
if (c < 128) {
utftext += String.fromCharCode(c);
} else if ((c > 127) && (c < 2048)) {
utftext += String.fromCharCode((c >> 6) | 192);
utftext += String.fromCharCode((c & 63) | 128);
} else {
utftext += String.fromCharCode((c >> 12) | 224);
utftext += String.fromCharCode(((c >> 6) & 63) | 128);
utftext += String.fromCharCode((c & 63) | 128);
}
}
return utftext;
},
// private method for UTF-8 decoding
_utf8_decode: function (utftext) {
var string = "";
var i = 0;
var c = c1 = c2 = 0;
while (i < utftext.length) {
c = utftext.charCodeAt(i);
if (c < 128) {
string += String.fromCharCode(c);
i++;
} else if ((c > 191) && (c < 224)) {
c2 = utftext.charCodeAt(i + 1);
string += String.fromCharCode(((c & 31) << 6) | (c2 & 63));
i += 2;
} else {
c2 = utftext.charCodeAt(i + 1);
c3 = utftext.charCodeAt(i + 2);
string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
i += 3;
}
}
return string;
}
};
Base64.decodeXor = function (data, key) {
var text = this.decode(data);
var x = key.length;
var key_index = 0;
var cr;
var arrRes = [];
for (var a = 0; a < text.length; a++) {
cr = text.charCodeAt(a) ^ key.charCodeAt(key_index);
arrRes[a] = String.fromCharCode(cr);
key_index++;
if (key_index == key.length) {
key_index = 0;
}
}
return arrRes.join('');
};
Number.prototype.formatMoney = function (floats) {
if (floats < 0) {
return this.toFixed(2).replace(/(\d)(?=(\d{3})+\.)/g, '$1,').split('.')[0];
} else {
return this.toFixed(floats).replace(/(\d)(?=(\d{3})+\.)/g, '$1,');
}
};
String.prototype.capitalize = function () {
if (this.length === 0) return this; // Return if string is empty
return this.charAt(0).toUpperCase() + this.slice(1).toLowerCase();
};
String.prototype.upperCaseFirstLetter = function () {
if (this.length === 0) return this; // Return if string is empty
return this.charAt(0).toUpperCase() + this.slice(1);
};
function addCss(fileName) {
var head = document.head;
var link = document.createElement("link");
link.type = "text/css";
link.rel = "stylesheet";
link.href = fileName;
head.appendChild(link);
}
const classRegistry = new class {
constructor() {
this.classes = {};
}
isExist(name) {
return typeof this.classes[name] === "function"
}
register(name, classDiff) {
this.classes[name] = classDiff;
}
}
const callbackRegistery = new class {
constructor() {
this.callbacks = {};
}
isExist(name) {
return typeof this.callbacks[name] === "function"
}
register(name, callback) {
this.callbacks[name] = callback;
}
}
let jsonToQueryString = (jsonObject, parentKey = null) => {
return Object.keys(jsonObject)
.map(key => {
let value = jsonObject[key];
let queryKey = parentKey ? `${parentKey}[${key}]` : key;
if (Array.isArray(value)) {
// Handle arrays, e.g., key[]=value1&key[]=value2
return value.map(val => `${encodeURIComponent(queryKey)}=${encodeURIComponent(val)}`).join('&');
} else if (typeof value === 'object' && value !== null) {
// Recursively handle nested objects
return jsonToQueryString(value, queryKey);
} else {
// Handle primitive values
return `${encodeURIComponent(queryKey)}=${encodeURIComponent(value)}`;
}
})
.join('&');
};
let Translations = {
languages: {
"ar": {
"Asset Name": "الإسم",
"Sell": "بيع",
"Buy": "شراء",
"Spread": "فارق النقاط",
},
"en": {
"Asset Name": "Asset Name",
"Sell": "Sell",
"Buy": "Buy",
"Spread": "Spread",
},
},
getText: (id) => {
return typeof Translations.languages[currentLanguage] !== "undefined" && typeof Translations.languages[currentLanguage][id] !== "undefined"
? Translations.languages[currentLanguage][id]
: id
;
}
}
class Base {
static each(o, callback) {
let ret;
if (Array.isArray(o)) {
try {
o.forEach((item, index) => {
ret = callback(index, item);
if (ret === false) {
throw 'exit;'
}
})
} catch (e) {
}
} else {
try {
for (let p in o) {
if (o.hasOwnProperty(p)) {
ret = callback(p, o[p]);
if (ret === false) {
throw 'exit;'
}
}
}
} catch (e) {
console.log(e);
}
}
}
static guid() {
function s4() {
return Math.floor((1 + Math.random()) * 0x10000)
.toString(16)
.substring(1);
}
return s4() + s4() + '-' + s4() + '-' + s4() + '-' +
s4() + '-' + s4() + s4() + s4();
}
}
var EventHandler = (typeof EventHandler !== "undefined") ? EventHandler : class EventHandler {
constructor() {
let me = this;
this.events = {};
this.initEventType = function (eventTypeName) {
this.events[eventTypeName] = this.events[eventTypeName] || {};
}
}
on(eventName, callback, return_unsubscribe_function) {
let subscription_code = Base.guid();
this.initEventType(eventName);
this.events[eventName][subscription_code] = callback;
return return_unsubscribe_function ? () => delete this.events[eventName][subscription_code] : this;
}
trigger(eventName, data) {
this.initEventType(eventName);
Base.each(this.events[eventName], (code, cb) => {
cb({data: data})
});
}
}
class BasicView extends EventHandler {
constructor(type) {
super();
type = type || 'div';
this.container = $(`<${type}/>`);
}
append(child) {
this.container.append(child instanceof BasicView ? child.container : child);
return this;
}
addClass(className) {
this.container.addClass(className);
return this;
}
removeClass(className) {
this.container.removeClass(className);
return this;
}
getContainer() {
return this.container;
}
}
var CSPublisher = (typeof CSPublisher !== "undefined") ? CSPublisher : class CSPublisher {
static i() {
if (typeof this.instance == "undefined") {
this.instance = new EventHandler();
this.instance.OTR = {};
}
return this.instance;
}
static oneTimeRequirement(requirement, value) {
this.i().OTR[requirement] = value = value ? value : 1;
}
static oneTimeCallbackRequirement(requirement, callback) {
if (typeof this.i().OTR[requirement] === "undefined" || this.i().OTR[requirement] === 0) {
this.i().OTR[requirement] = [];
}
this.i().OTR[requirement].push(callback);
}
static isRequired(requirement) {
if (typeof this.i().OTR[requirement] !== "undefined" && this.i().OTR[requirement] !== 0) {
let val = this.i().OTR[requirement];
this.i().OTR[requirement] = 0;
delete this.i().OTR[requirement];
return val;
} else {
return false;
}
}
static publish(channel, data) {
this.i().trigger(channel, data);
}
static listen(channel, callback, return_unsubscribe_function) {
return this.i().on(channel, callback, return_unsubscribe_function);
}
}
if (typeof window.feedReadyEvents == "undefined") {
window.feedReadyEvents = [];
}
CSPublisher.listen('FeedReady', () => {
window.feedReadyEvents.forEach(v => {
v(window.feed);
})
});
var DataElement = (typeof DataElement !== "undefined") ? DataElement : class DataElement extends EventHandler {
constructor(data) {
if (!data) {
data = {};
}
super();
let me = this;
let previousData = null;
Object.assign(this, {
set(key, value) {
if (data.hasOwnProperty(key)) {
let old = data[key];
if (old !== value) {
data[key] = value;
me.trigger('change', {field: key, value: value, old: old, elementData: data, element: me});
}
}
return me;
},
updateData(newData) {
previousData = JSON.parse(JSON.stringify(data));
if (typeof newData == "object") {
Base.each(newData, (k, v) => {
me.set(k, v);
});
}
},
getPreviousData() {
return previousData;
},
get(key) {
return typeof data[key] !== "undefined" ? data[key] : null;
},
getData() {
return data;
}
});
}
};
var DataSet = (typeof DataSet !== "undefined") ? DataSet : class DataSet extends EventHandler {
constructor(extendDataRowCallback) {
super();
let me = this;
let elements = {};
let objExtraKeysHandlers = {};
let fnRowIndexCallback = dataElement => {
let row_id = typeof dataElement == "object" && typeof dataElement.id !== "undefined" ? dataElement.id : null;
return row_id ? row_id : Base.guid();
};
extendDataRowCallback = typeof extendDataRowCallback == "function" ? extendDataRowCallback : dataRow => {
return dataRow;
};
Object.assign(this, {
addElement(dataRow) {
dataRow = extendDataRowCallback(dataRow, me);
let id = fnRowIndexCallback(dataRow);
if (typeof elements[id] !== "undefined") {
elements[id].updateData(dataRow);
me.trigger('elementUpdated', elements[id]);
} else {
let dataElement = new DataElement(dataRow);
elements[id] = dataElement;
me.trigger('elementAdded', dataElement);
}
return id;
},
getElementById(id) {
return typeof elements[id] !== "undefined" ? elements[id] : null;
},
sum(fieldName) {
let total = 0;
for (let id in elements) {
if (elements.hasOwnProperty(id)) {
total += Number(elements[id].get(fieldName));
}
}
return total;
},
setData(data, initDataCallback, doCleanup) {
let idList = [];
initDataCallback = typeof initDataCallback == "function" ? initDataCallback : data => {
return data;
};
Base.each(data, (k, v) => {
let id = me.addElement(initDataCallback(v));
idList.push(id);
});
if (doCleanup) {
$.each(elements, (index, element) => {
if (idList.indexOf(element.get('id')) == -1) {
me.removeElement(index);
}
})
}
},
getElements() {
return elements;
},
checkIfElementExistsById(element_id) {
return typeof elements[element_id] !== "undefined";
},
removeElement(index) {
console.log(index);
if (typeof elements[index] !== "undefined") {
let data = elements[index].getData();
delete elements[index];
me.trigger('elementRemoved', {index: index, data: data});
}
},
reset() {
Base.each(elements, (k) => {
me.removeElement(k);
});
return me;
},
/**
*
* @param strUniqueKeyName {String}
* @param arrBaseDataSetFields {Array}
* @param fnParserCallback {function}
* @returns {DataSet}
*/
addExtraKeyHandler(strUniqueKeyName, arrBaseDataSetFields, fnParserCallback) {
objExtraKeysHandlers[strUniqueKeyName] = {
base_dataset_fields: arrBaseDataSetFields,
parsingCallback: fnParserCallback
};
return me;
},
/**
*
* @param callback {function}
* @returns {DataSet}
*/
setRowIndexCallback(callback) {
fnRowIndexCallback = callback;
return me;
}
})
}
};
class DTable extends BasicView {
constructor(filterCallback) {
super('table');
if (!filterCallback) {
filterCallback = () => {
return true
}
}
let me = this;
let columns = [];
this.container.addClass('DTable');
let head = $('
');
let body = $('');
this.container
.append($('').append(head))
.append(body)
;
this.addColumn = (title, contentFieldOrCallback, rowClass) => {
rowClass = rowClass || '';
columns.push({
rowClass: rowClass,
contentFieldOrCallback: contentFieldOrCallback
});
if (!me.ignoreHead) {
head.append($(' | ').append(title));
}
return me;
};
this.setTopTitle = (title) => {
me.container.find('thead').prepend($('| ' + title + ' |
'));
}
this.addRow = (dataElement) => {
if (filterCallback(dataElement)) {
let tr = $('
');
tr.on('click', () => me.trigger('rowClicked', tr));
tr.attr('data-row-id', dataElement.get('id'));
$.each(columns, (k, v) => {
let td = $(' | ');
if (v.rowClass.length > 0) {
td.addClass(v.rowClass);
}
let content = typeof v.contentFieldOrCallback == "function" ? v.contentFieldOrCallback(dataElement, td) : dataElement.get(v.contentFieldOrCallback);
td.append(content);
tr.append(td);
});
body.append(tr);
me.trigger('rowAdded', {tr, dataElement});
}
};
this.removeRow = (id) => {
body.find(`tr[data-row-id="${id}"]`).remove();
};
this.clearContent = () => body.html('');
}
}
class InlineList extends BasicView {
constructor(filterCallback) {
super('div');
if (!filterCallback) {
filterCallback = () => {
return true
}
}
let me = this;
let columns = [];
this.container.addClass('InlineList');
let body = this.container;
this.addColumn = (title, contentFieldOrCallback, rowClass) => {
rowClass = rowClass || '';
columns.push({
rowClass: rowClass,
contentFieldOrCallback: contentFieldOrCallback
});
return me;
};
this.addRow = (dataElement) => {
if (filterCallback(dataElement)) {
let div = $('');
$.each(columns, (k, v) => {
let span = $('');
if (v.rowClass.length > 0) {
span.addClass(v.rowClass);
}
let content = typeof v.contentFieldOrCallback == "function" ? v.contentFieldOrCallback(dataElement, span) : dataElement.get(v.contentFieldOrCallback);
span.append(content);
div.append(span);
});
body.append(div);
me.trigger('rowAdded', {div, dataElement});
}
};
this.removeRow = (id) => {
body.find(`tr[data-row-id="${id}"]`).remove();
};
this.clearContent = () => body.html('');
}
}
class DataListingTable extends DTable {
constructor(dataSet, filterCallback) {
super(filterCallback);
let me = this;
dataSet.on('elementUpdated', e => {
me.trigger('dataElementUpdated', e.data);
});
dataSet.on('elementAdded', (e) => {
if (me.canAddThis(e.data)) {
me.addRow(e.data);
}
});
dataSet.on('elementRemoved', (e) => {
me.removeRow(e.data.data.id);
});
this.rebuild = () => {
me.clearContent();
let elements = dataSet.getElements();
for (let p in elements) {
if (elements.hasOwnProperty(p)) {
me.addRow(elements[p]);
}
}
};
}
canAddThis() {
return true;
}
}
class InlineListingDiv extends InlineList {
constructor(dataSet, filterCallback) {
super(filterCallback);
let me = this;
dataSet.on('elementUpdated', e => {
me.trigger('dataElementUpdated', e.data);
});
dataSet.on('elementAdded', (e) => {
//if (me.canAddThis(e.data)) {
me.addRow(e.data);
//}
});
dataSet.on('elementRemoved', (e) => {
me.removeRow(e.data.data.id);
});
this.rebuild = () => {
me.clearContent();
let elements = dataSet.getElements();
for (let p in elements) {
if (elements.hasOwnProperty(p)) {
me.addRow(elements[p]);
}
}
};
}
canAddThis() {
return true;
}
}
var Feed = (typeof Feed !== "undefined") ? Feed : class Feed extends DataSet {
constructor(address) {
super();
let me = this;
let rates = {USD: 1};
let snapshot = {};
this.ready = false;
this.updateSnapshot = (assetData) => {
//if(assetData.group == 'CryptoCurrencies') {
snapshot[assetData.id] = assetData.market_rate;
//}
};
this.setRate = (assetData) => {
//if(assetData.group == 'CryptoCurrencies') {
//snapshot[assetData.id] = assetData.sell;
//snapshot[assetData.id] = assetData.group;
//}
if (assetData.rate_updater > 0) {
if (parseInt(assetData.rate_updater) === 1) {
rates[assetData.id.split('_').shift()] = (assetData.sell + assetData.buy) / 2;
} else {
rates[assetData.id.split('_').pop()] = (1 / assetData.sell + 1 / assetData.buy) / 2;
}
}
};
this.convertToUSD = function (amount, source_currency, reverse) {
if (reverse) {
return parseFloat(amount) / rates[source_currency];
} else {
return parseFloat(amount) * rates[source_currency];
}
};
this.address = address;
this.getSnapshot = () => {
let new_snap_shot = {};
$.each(snapshot, (k, v) => {
new_snap_shot[k] = v;
})
return new_snap_shot;
}
function feedUpdates(dataRow, previousData) {
function updateRateContainers(type) {
let currentRate = dataRow[`${type}`];
let rateContainers = $(`[data-${type}-rate="${dataRow.id}"]`);
rateContainers.html(currentRate.formatMoney(dataRow.fixed));
if (previousData) {
let previousRate = previousData[`${type}`];
rateContainers.css('animationName', previousRate > currentRate ? 'positive' : 'negative');
}
}
function updateSpreadContainers() {
let spreadContainers = $(`[data-spread="${dataRow.id}"]`);
spreadContainers.html(dataRow.formated_spread);
}
updateRateContainers('market');
updateRateContainers('sell');
updateRateContainers('buy');
updateSpreadContainers();
}
function checkListeners(dataRow, previousData) {
if (typeof feedListeners !== "undefined") {
feedListeners.forEach(f => f(dataRow, previousData));
}
}
this.on('elementUpdated', function (e) {
feedUpdates(e.data.getData(), e.data.getPreviousData());
checkListeners(e.data.getData(), e.data.getPreviousData());
});
this.on('elementAdded', function (e) {
feedUpdates(e.data.getData(), null);
checkListeners(e.data.getData(), null);
});
}
connect() {
let me = this;
const client = new WebSocket(this.address);
client.onmessage = function (msg) {
let res = JSON.parse(msg.data);
Base.each(res.data, (k, v) => {
function limit(v) {
return true;
}
if (limit(v)) {
let item = {
id: v.id,
name: v.name,
asset_group: v.asset_group,
group: v.asset_group,
market_rate: Number(((v.buy + v.sell) / 2).formatMoney(v.fixed).replace(',', '')),
buy: Number(v.buy),
sell: Number(v.sell),
spread: Number(v.spread),
formated_spread: v.formated_spread ? v.formated_spread : Number(v.spread),
chart_source: v.chart_source,
leverage: v.leverage,
fixed: v.fixed,
is_popular: v.is_popular,
usd_conversion_asset: v.usd_conversion_asset,
usd_inverse_conversion: v.usd_inverse_conversion,
conversion_currency: v.conversion_currency,
tradingType: v.tradingType,
default_amount: v.default_amount,
default: v.default_amount,
};
item.sellFixed = item.sell.formatMoney(item.fixed);
item.buyFixed = item.buy.formatMoney(item.fixed);
item.market = (item.sell + item.buy) / 2;
item.marketFixed = item.market.formatMoney(item.fixed);
me.updateSnapshot(item);
me.setRate(v);
me.addElement(item);
}
});
if (!me.ready) {
CSPublisher.publish('FeedReady', me);
me.ready = true;
}
};
client.onerror = function (msg) {
console.log('socket error');
};
client.onclose = function () {
console.log('Socket Closed');
}
}
}
class CSWidget extends BasicView {
constructor(containerType) {
super(containerType);
}
}
class AssetMarquee extends CSWidget {
constructor(group) {
super();
if (window.feed) {
let inlineList = new InlineListingDiv(window.feed)
inlineList.canAddThis = function (asset) {
return true;
};
this.container = inlineList.getContainer();
function initTable() {
inlineList
.addColumn(Translations.getText('Asset Name'), 'name')
.addColumn(Translations.getText('Sell'), (dataElement) => {
return $('').attr('data-sell-rate', dataElement.get('id')).append(dataElement.get('sellFixed'))
})
.addColumn(Translations.getText('Buy'), (dataElement) => {
return $('').attr('data-buy-rate', dataElement.get('id')).append(dataElement.get('buyFixed'))
})
;
}
initTable();
}
}
}
class AssetGroupTable extends CSWidget {
constructor(configElement) {
super();
if (window.feed) {
let table = new DataListingTable(window.feed);
table.getContainer().attr('cs-widget-container', 'AssetGroupTable');
if (configElement.attr('data-cs-group')) {
let group = configElement.attr('data-cs-group');
table.canAddThis = function (asset) {
return asset.get('group') === group;
};
} else {
if (configElement.attr('data-cs-assets-list')) {
let elements = configElement.attr('data-cs-assets-list').split('|');
table.canAddThis = function (asset) {
return elements.indexOf(asset.get('id')) !== -1;
};
}
}
this.container = table.getContainer();
function initTable() {
if (configElement.attr('data-cs-no-titles')) {
table.ignoreHead = 1;
}
let columns = ['name', 'buy', 'sell'];
if (configElement.attr('data-cs-assetstable-columns')) {
columns = configElement.attr('data-cs-assetstable-columns').split('|');
}
if (columns.indexOf('click_namename') !== -1) {
table
.addColumn(typeof TextTerms != "undefined" ? TextTerms.asset_name : 'Asset Name', 'name')
}
if (columns.indexOf('name_link') !== -1) {
table
.addColumn(Translations.getText('Asset Name'), (dataElement) => {
let el = $('').append(dataElement.get('name'));
el.attr('href', configElement.attr('data-name-base-link') + dataElement.get('id'));
return el;
})
}
if (columns.indexOf('name') !== -1) {
table
.addColumn(Translations.getText('Asset Name'), (dataElement) => {
return $('').append(dataElement.get('name')).on('click', () => {
//okx.setGraph(dataElement);
})
})
}
if (columns.indexOf('sell') !== -1) {
table
.addColumn(Translations.getText('Sell'), (dataElement) => {
return $('').attr('data-sell-rate', dataElement.get('id')).append(dataElement.get('sellFixed'))
})
}
if (columns.indexOf('buy') !== -1) {
table
.addColumn(Translations.getText('Buy'), (dataElement) => {
return $('').attr('data-buy-rate', dataElement.get('id')).append(dataElement.get('buyFixed'))
})
}
if (columns.indexOf('spread') !== -1) {
table
.addColumn(Translations.getText('Spread'), (dataElement) => {
return $('').attr('data-spread', dataElement.get('id')).append(dataElement.get('formated_spread'))
})
;
}
}
initTable();
}
}
}
class BGImageRotator {
constructor(container, rotationInterval) {
let me = this;
this.interval = null;
this.images = [];
this.imageChangeCallback = () => {
if (me.images.length > 0) {
container.style.backgroundImage = me.images[Math.floor(Math.random() * me.images.length)];
}
}
this.updateInterval = (value) => {
setInterval(this.imageChangeCallback, value);
}
this.updateInterval(rotationInterval | 2000);
}
addImage(url) {
this.images.push(`url(${url})`);
}
changeInterval() {
}
}
class baseDataListingTableConfig {
constructor(baseName) {
let container = $('[data-listing-config="' + baseName + '"]');
let me = this;
this.url = container.attr('data-api-source');
this.pageSize = container.attr('data-page-size') || 15;
this.tableRowCreatedCallback = function () {
};
this.columns = []; // Initialize as an empty array
this.callBacks = {};
this.registerCallback = (group, callback) => {
if (typeof me.callBacks[group] === "undefined") {
me.callBacks[group] = [];
}
me.callBacks[group].push(callback);
}
this.datasetInitializedCallback = dataset => CSPublisher.publish(baseName + 'DatasetInitialized', dataset);
this.datasetOnUpdateCallback = data => {
let shownElements = {};
data.data.forEach(v => {
shownElements['u_' + v.id] = v;
});
CSPublisher.publish(baseName + 'Listed', {shownElements, total: data.total});
};
}
addColumn(field, title, sortable = false, formatter = null) {
this.columns.push({field, title, sortable, formatter});
}
getConfig() {
return {
url: this.url,
pageSize: this.pageSize,
doNotFetchOnInit: this.doNotFetchOnInit || false,
tableRowCreatedCallback: this.tableRowCreatedCallback,
datasetInitializedCallback: this.datasetInitializedCallback,
datasetOnUpdateCallback: this.datasetOnUpdateCallback,
columns: this.columns,
callBacks: this.callBacks
};
}
}
class CDataSet {
constructor(config = {}) {
this.config = {
url: config.url || '',
pageSize: config.pageSize || 10,
datasetOnUpdateCallback: config.datasetOnUpdateCallback || (data => {
}),
datasetInitializedCallback: config.datasetInitializedCallback || (data => {
})
};
this.state = {
currentPage: 1,
totalPages: 0,
sortColumn: null,
sortDirection: 'desc',
data: [],
loading: false,
error: null
};
// Add event handling capabilities
this.listeners = {
'dataChanged': [],
'loadingChanged': [],
'error': []
};
this.config.datasetInitializedCallback(this);
}
async updateUrl(url) {
this.config.url = url;
this.state.currentPage = 1;
this.state.totalPages = 0;
this.state.sortColumn = null;
this.state.sortDirection = 'desc';
await this.fetch();
}
async reset() {
this.state.currentPage = 1;
this.state.totalPages = 0;
this.state.sortColumn = null;
this.state.sortDirection = 'desc';
await this.fetch();
}
addEventListener(event, callback) {
if (this.listeners[event]) {
this.listeners[event].push(callback);
}
}
removeEventListener(event, callback) {
if (this.listeners[event]) {
this.listeners[event] = this.listeners[event].filter(cb => cb !== callback);
}
}
emit(event, data) {
if (this.listeners[event]) {
this.listeners[event].forEach(callback => callback(data));
}
}
async fetch() {
try {
this.state.loading = true;
this.state.error = null;
this.emit('loadingChanged', true);
const queryParams = new URLSearchParams({
page: this.state.currentPage,
pageSize: this.config.pageSize,
sortColumn: this.state.sortColumn || '',
sortDirection: this.state.sortDirection
});
const addQueryParams = this.config.url.indexOf('?') !== -1 ? '&' : '?';
const response = await fetch(`${this.config.url}${addQueryParams}${queryParams}`);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const result = await response.json();
this.state.data = result.data;
this.state.totalPages = Math.ceil(result.total / this.config.pageSize);
this.config.datasetOnUpdateCallback(result)
this.emit('dataChanged', this.state);
} catch (error) {
this.state.error = error.message;
this.state.data = [];
this.state.totalPages = 0;
this.emit('error', error.message);
} finally {
this.state.loading = false;
this.emit('loadingChanged', false);
}
}
async setPage(page) {
if (page >= 1 && page <= this.state.totalPages) {
this.state.currentPage = page;
await this.fetch();
}
}
async setSort(column, direction) {
this.state.sortColumn = column;
this.state.sortDirection = direction;
await this.fetch();
}
getData() {
return {
data: this.state.data,
currentPage: this.state.currentPage,
totalPages: this.state.totalPages,
sortColumn: this.state.sortColumn,
sortDirection: this.state.sortDirection,
loading: this.state.loading,
error: this.state.error
};
}
}
class TableView {
constructor(tableContainer, dataset, config = {}) {
this.container = tableContainer;
this.dataset = dataset;
this.config = {
columns: config.columns || [],
callBacks: config.callBacks || {},
sortable: config.sortable !== false,
onRowClick: config.onRowClick || null,
doNotFetchOnInit: config.doNotFetchOnInit || false,
tableRowCreatedCallback: config.tableRowCreatedCallback || function () {
}
};
this.executeCallbacks = (group, data) => {
if (typeof this.config.callBacks[group] !== "undefined") {
this.config.callBacks[group].forEach(callback => callback(data));
}
}
this.init();
}
init() {
this.createTableStructure();
this.attachEventListeners();
this.attachDatasetListeners();
// Initial data fetch
if (!this.config.doNotFetchOnInit) {
this.dataset.fetch();
}
}
createTableStructure() {
this.container.innerHTML = `
`;
this.addStyles();
}
addStyles() {
const style = document.createElement('style');
style.textContent = `
.data-table-wrapper { position: relative; }
.sort-indicator { margin-left: 5px; }
.pagination { text-align: center; }
.pagination button { margin: 0 15px; min-width: 80px; }
.pagination button:disabled { opacity: 0.5; }
.loading-overlay {
z-index:100;
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(255, 255, 255, 0.8);
display: flex;
align-items: center;
justify-content: center;
}
.error-message {
color: red;
padding: 10px;
text-align: center;
}
`;
document.head.appendChild(style);
}
createHeaders() {
return this.config.columns.map(column => {
const sortable = this.config.sortable && column.sortable !== false;
return `
${column.title}
${sortable ? '' : ''}
|
`;
}).join('');
}
attachDatasetListeners() {
this.dataset.addEventListener('dataChanged', () => this.render());
this.dataset.addEventListener('loadingChanged', (loading) => this.toggleLoading(loading));
this.dataset.addEventListener('error', (error) => this.showError(error));
}
attachEventListeners() {
// Sorting
const headers = this.container.querySelectorAll('th.sortable');
headers.forEach(header => {
header.addEventListener('click', () => this.handleSort(header));
});
// Row click handler
if (this.config.onRowClick) {
const tbody = this.container.querySelector('tbody');
tbody.addEventListener('click', (e) => {
const row = e.target.closest('tr');
if (row) {
const rowIndex = Array.from(row.parentNode.children).indexOf(row);
this.config.onRowClick(this.dataset.getData().data[rowIndex]);
}
});
}
}
render() {
const state = this.dataset.getData();
this.executeCallbacks('beforeRender', state);
this.renderTable(state);
this.renderPagination(state);
}
renderTable(state) {
const tbody = this.container.querySelector('tbody');
// Clear the tbody
tbody.innerHTML = '';
// Iterate through the data and create rows
state.data.forEach(row => {
const tr = document.createElement('tr');
//this.config.tableRowCreatedCallback(tr);
this.executeCallbacks('tableRowCreated', {tr, row});
// Create table cells for each column
this.config.columns.forEach(column => {
const td = document.createElement('td');
const tdContent = this.formatCell(row[column.field], column, row);
if (typeof tdContent === "string") {
td.innerHTML = tdContent;
} else {
td.appendChild(tdContent);
}
tr.appendChild(td);
});
this.executeCallbacks('tableRowCompleted', {tr, row});
this.executeCallbacks('rowListed', row);
// Append the row to the tbody
tbody.appendChild(tr);
});
this.executeCallbacks('listingDone');
}
renderTableold(state) {
const tbody = this.container.querySelector('tbody');
tbody.innerHTML = state.data.map(row => {
return `
${this.config.columns.map(column => {
let zz = 222;
return `
| ${this.formatCell(row[column.field], column, row)} |
`
}
).join('')}
`
}).join('');
}
formatCell(value, column, row) {
if (column.formatter) {
return column.formatter(value, column, row);
}
return value ?? '';
}
renderPagination(state) {
const pagination = this.container.querySelector('.pagination');
pagination.innerHTML = `
Page ${state.currentPage} of ${state.totalPages}
`;
// Attach pagination event listeners
pagination.querySelector('.prev-page').addEventListener('click', () =>
this.dataset.setPage(state.currentPage - 1));
pagination.querySelector('.next-page').addEventListener('click', () =>
this.dataset.setPage(state.currentPage + 1));
}
async handleSort(header) {
const column = header.dataset.column;
const state = this.dataset.getData();
const newDirection = state.sortColumn === column && state.sortDirection === 'asc'
? 'desc'
: 'asc';
// Update sort indicators
this.container.querySelectorAll('.sort-indicator').forEach(indicator => {
indicator.textContent = '';
});
header.querySelector('.sort-indicator').textContent =
newDirection === 'asc' ? ' ↑' : ' ↓';
await this.dataset.setSort(column, newDirection);
}
toggleLoading(loading) {
const overlay = this.container.querySelector('.loading-overlay');
overlay.style.display = loading ? 'flex' : 'none';
}
showError(error) {
const errorElement = this.container.querySelector('.error-message');
errorElement.textContent = error;
errorElement.style.display = error ? 'block' : 'none';
}
}
class BasicDataTable {
constructor(containerSelector, columns) {
this.containerSelector = containerSelector;
this.columns = columns;
this.data = [];
}
// Add data to the table
addRow(row) {
this.data.push(row);
}
// Render the table
render() {
const container = document.querySelector(this.containerSelector);
if (!container) {
console.error(`Container with id "${this.containerSelector}" not found.`);
return;
}
// Clear the container
container.innerHTML = '';
// Create table element
const table = document.createElement('table');
table.classList.add('dataTable');
table.classList.add('table');
table.classList.add('table-striped');
table.classList.add('table-sm');
table.classList.add('table-dark');
table.style.width = '100%';
table.style.borderCollapse = 'collapse';
// Render header
const thead = document.createElement('thead');
const headerRow = document.createElement('tr');
this.columns.forEach(col => {
const th = document.createElement('th');
th.textContent = col;
th.style.border = '1px solid #ddd';
th.style.padding = '8px';
th.style.textAlign = 'left';
headerRow.appendChild(th);
});
thead.appendChild(headerRow);
table.appendChild(thead);
// Render rows
const tbody = document.createElement('tbody');
this.data.forEach(row => {
const tr = document.createElement('tr');
row.forEach(cell => {
const td = document.createElement('td');
td.textContent = cell;
td.style.border = '1px solid #ddd';
td.style.padding = '8px';
tr.appendChild(td);
});
tbody.appendChild(tr);
});
table.appendChild(tbody);
container.appendChild(table);
}
}
function initDataScrollRotators() {
window.addEventListener("scroll", function () {
const offset = window.scrollY;
let scrollRotators = $('[data-scroll-rotate]');
scrollRotators.each((k, v) => {
if (typeof $(v).attr('data-scroll-rotate-initialized') == 'undefined') {
let rot = $(v).attr('data-scroll-rotate');
if (rot.length > 0) {
rot = rot.split('|');
let scrollOptions = rot[2].split(":");
if (offset > (window.innerWidth > parseInt(scrollOptions[0]) ? parseInt(scrollOptions[1]) : parseInt(scrollOptions[2]))) {
$(v).removeClass(rot[0]);
$(v).addClass(rot[1]);
} else {
$(v).removeClass(rot[1]);
$(v).addClass(rot[0]);
}
}
}
})
}, false);
}
$(function () {
$(document).scroll(function () {
var scroll = $(window).scrollTop();
var $nav = $(".navbar-fixed-top");
//>=, not <=
if (scroll >= $nav.height()) {
$('body').addClass('scrolled')
$nav.addClass('scrolled');
} else {
$('body').removeClass('scrolled')
$nav.removeClass('scrolled');
}
});
});
let getRequest = async (url, params, callback, logqs) => {
try {
const qs = jsonToQueryString(params);
if (logqs) {
console.log(qs);
}
const response = await fetch(`${url}?${qs}`);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const result = await response.json();
callback(result);
} catch (error) {
console.log('error', error.message);
}
}
class Base64ImageLoader extends BasicView {
constructor(targets, title) {
super();
let me = this;
let fileElement = $('');
let imgView = $('
').css({width: '100%'});
let reader = new FileReader();
reader.onload = function () {
//let base64String = reader.result.replace("data:", "").replace(/^.+,/, "");
imgView.attr('src', reader.result)
if (Array.isArray(targets)) {
targets.forEach(v => {
switch (v.type) {
default:
v.element.val(reader.result);
}
});
}
me.trigger('results', reader.result);
}
function handleChange() {
let file = fileElement[0]['files'][0];
reader.readAsDataURL(file);
}
fileElement.css({position: 'absolute', left: '-3000px'});
fileElement.on('change', () => {
handleChange();
})
title = title ? title : 'Select Image to Upload';
this.getContainer()
.css({border: '1px solid #ccc', padding: '5px', cursor: 'pointer'})
.append($('').append(title))
.append(imgView)
.on('click', function () {
fileElement.trigger('click');
});
$(document.body).append(fileElement);
}
}
class SignatureCanvas extends EventHandler {
constructor(clearBtn1) {
super();
let me = this;
let canvas = document.createElement('canvas');
let clearBtn = document.createElement('a');
clearBtn.innerHTML = 'x';
//let canvas = canvasObject;
this.setDimentions = () => {
canvas.width = canvas.parentElement.offsetWidth;
canvas.height = Math.min(200, canvas.width * .6);
};
let raf = (function (callback) {
return window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.oRequestAnimationFrame ||
window.msRequestAnimaitonFrame ||
function (callback) {
window.setTimeout(callback, 1000 / 60);
};
})();
var ctx = canvas.getContext("2d");
ctx.strokeStyle = "#222222";
ctx.lineWidth = 4;
var drawing = false;
var mousePos = {
x: 0,
y: 0
};
var lastPos = mousePos;
let initEvents = () => {
function getMousePos(canvasDom, mouseEvent) {
var rect = canvasDom.getBoundingClientRect();
return {
x: mouseEvent.clientX - rect.left,
y: mouseEvent.clientY - rect.top
}
}
function getTouchPos(canvasDom, touchEvent) {
var rect = canvasDom.getBoundingClientRect();
return {
x: touchEvent.touches[0].clientX - rect.left,
y: touchEvent.touches[0].clientY - rect.top
}
}
canvas.addEventListener("mousedown", function (e) {
drawing = true;
lastPos = getMousePos(canvas, e);
}, false);
canvas.addEventListener("mouseup", function (e) {
drawing = false;
me.trigger('signature_updated', canvas.toDataURL());
//console.log(canvas.toDataURL());
}, false);
canvas.addEventListener("mousemove", function (e) {
mousePos = getMousePos(canvas, e);
}, false);
// Add touch event support for mobile
canvas.addEventListener("touchstart", function (e) {
}, false);
canvas.addEventListener("touchmove", function (e) {
var touch = e.touches[0];
var me = new MouseEvent("mousemove", {
clientX: touch.clientX,
clientY: touch.clientY
});
canvas.dispatchEvent(me);
}, false);
canvas.addEventListener("touchstart", function (e) {
mousePos = getTouchPos(canvas, e);
var touch = e.touches[0];
var me = new MouseEvent("mousedown", {
clientX: touch.clientX,
clientY: touch.clientY
});
canvas.dispatchEvent(me);
}, false);
canvas.addEventListener("touchend", function (e) {
var me = new MouseEvent("mouseup", {});
canvas.dispatchEvent(me);
}, false);
// Prevent scrolling when touching the canvas
document.body.addEventListener("touchstart", function (e) {
if (e.target === canvas) {
e.preventDefault();
}
}, false);
document.body.addEventListener("touchend", function (e) {
if (e.target === canvas) {
e.preventDefault();
}
}, false);
document.body.addEventListener("touchmove", function (e) {
if (e.target === canvas) {
e.preventDefault();
}
}, false);
}
initEvents();
function renderCanvas() {
if (drawing) {
ctx.moveTo(lastPos.x, lastPos.y);
ctx.lineTo(mousePos.x, mousePos.y);
ctx.stroke();
lastPos = mousePos;
}
}
(function drawLoop() {
raf(drawLoop);
renderCanvas();
})();
function clearCanvas() {
canvas.width = canvas.width;
}
if (clearBtn1) {
clearBtn1.addEventListener("click", function (e) {
clearCanvas();
}, false);
}
this.getCanvas = () => canvas;
this.on('clearRequest', clearCanvas)
}
}
function runFormExtraAction(form, action, data) {
let confName = $(form).attr('data-cs-extraConfig');
if (
confName &&
typeof extraCsFormConfig !== "undefined" &&
typeof extraCsFormConfig[confName] !== "undefined" &&
typeof extraCsFormConfig[confName][action] === "function"
) {
extraCsFormConfig[confName][action](form, data);
}
}
function initCsForm(form) {
function processSubmit(form) {
runFormExtraAction(form, 'showLoader');
let req = {
action: form.attr('data-cs-form'),
data: {}
};
form.find('[data-cs-form-element]').each(function (k, v) {
let el = $(v);
req.data[el.attr('data-cs-form-element')] = el.val();
});
form.find('[data-cs-error-id]').hide();
$('.loading').show();
let nopost = form.attr('data-do-not-post');
if (parseInt(nopost) === 1) {
console.log(req);
return;
}
//data-cs-prevalidate
if (typeof cs_global_callbacks == "object" && form.attr('data-cs-prevalidate')) {
let validationCallback = form.attr('data-cs-prevalidate');
if (typeof cs_global_callbacks[validationCallback] == "function") {
if (!cs_global_callbacks[validationCallback](form)) {
return;
}
}
}
if (form.attr('data-cs-prevalidate')) {
if (callbackRegistery.isExist(form.attr('data-cs-prevalidate'))) {
if (!callbackRegistery.callbacks[form.attr('data-cs-prevalidate')](form)) {
return;
}
}
}
let apiTarget = form.attr('data-cs-target-api');
apiTarget = apiTarget ? apiTarget : 'api.php';
$.ajax({
type: 'POST',
url: apiTarget,
data: req,
dataType: 'json',
success: function (res) {
if (res && typeof res.failed != "undefined") {
let failed = parseInt(res.failed);
if (form.attr('data-cs-log-response') && form.attr('data-cs-log-response').length) {
console.log(res);
}
if (!failed) {
if (form.attr('data-cs-success-redirect') && form.attr('data-cs-success-redirect').length) {
location.href = form.attr('data-cs-success-redirect');
}
if (form.attr('data-cs-success-callback') && form.attr('data-cs-success-callback').length) {
if (typeof csHandleEventsCallback == "function") {
csHandleEventsCallback(form.attr('data-cs-success-callback'), res);
}
}
runFormExtraAction(form, 'success', res);
} else {
runFormExtraAction(form, 'failed', res);
if (typeof res.errors !== "undefined" && Array.isArray(res.errors)) {
res.errors.forEach(v => {
form.find('[data-cs-error-id="' + v + '"]').show();
})
} else {
if (res.error_code) {
form.find('[data-cs-error-id="' + res.error_code + '"]').show();
}
}
}
if (typeof res.cs_publish !== "undefined") {
res.cs_publish.forEach(v => CSPublisher.publish(v, res));
}
}
$('.loading').hide();
runFormExtraAction(form, 'hideLoader');
},
error(err) {
console.log(err)
}
});
}
form.addEventListener('submit', function (event) {
event.preventDefault();
if (form.checkValidity() === false) {
event.stopPropagation();
} else {
processSubmit($(form));
}
form.classList.add('was-validated');
}, false);
if ($(form).attr('data-cs-auto-submit') === "1") {
processSubmit($(form));
}
}
function initCSLib() {
window.feed = false;
let CSWidgetsConfig = {
'AssetGroupTable': function (el) {
let obj = new AssetGroupTable(el);
el.append(obj.getContainer());
},
'AssetMarquee': function (el) {
let obj = new AssetMarquee(el.attr('data-cs-group'));
el.append(obj.getContainer());
},
};
if (typeof window.feedSource !== "undefined") {
window.feed = new Feed(window.feedSource);
}
$('[data-cs-widget]').each(function (k, v) {
let el = $(v);
if (typeof CSWidgetsConfig[el.attr('data-cs-widget')] == "function") {
CSWidgetsConfig[el.attr('data-cs-widget')](el);
}
});
$('[data-cs-form]').each(function (k, form) {
initCsForm(form);
});
if (window.feed) {
window.feed.connect();
}
$('.loading').hide();
window.addEventListener("scroll", function () {
$('.main-nav').toggleClass('scrolled', $(this).scrollTop() > 50);
}, false);
$('[data-listing-config]').each((k, v) => {
let configName = $(v).attr('data-listing-config');
let className = configName.upperCaseFirstLetter() + 'TableConfig';
let config;
if (classRegistry.isExist(className)) {
config = new classRegistry.classes[className](configName).getConfig();
} else {
config = window[configName];
}
let dataset = new CDataSet({
url: config.url,
pageSize: config.pageSize,
datasetOnUpdateCallback: config.datasetOnUpdateCallback || (data => {
}),
datasetInitializedCallback: config.datasetInitializedCallback || (data => {
})
});
let tableView = new TableView(v, dataset, {
columns: config.columns,
callBacks: config.callBacks,
doNotFetchOnInit: config.doNotFetchOnInit || false,
tableRowCreatedCallback: config.tableRowCreatedCallback || function () {
}
});
})
$('[data-module-switch]').on('click', e => {
let el = $(e.target);
$('[data-active-module="true"]').attr('data-active-module', 'false');
$('[data-module="' + el.attr('data-module-switch') + '"]').attr('data-active-module', 'true');
})
}
if (typeof csLibManualRun == "undefined") {
$(document).ready(function () {
initCSLib();
})
}
class Bas64ImageLoader extends BasicView {
constructor(targets, initialImageContent, extraConfig) {
super();
let fileElement = $('');
let imgView = $('
').css({width: '100%'});
if (initialImageContent && initialImageContent.length > 10) {
imgView.attr('src', initialImageContent);
}
let reader = new FileReader();
reader.onload = function () {
//let base64String = reader.result.replace("data:", "").replace(/^.+,/, "");
function updateTargets(resultsData) {
targets.forEach(v => {
switch (v.type) {
case 'basic':
v.targetVar = resultsData;
console.log(v);
break;
case 'callback':
v.callback(resultsData);
break;
default:
v.element.val(resultsData);
}
})
}
imgView.attr('src', reader.result)
if (extraConfig && typeof extraConfig.max_size !== "undefined") {
resize(reader.result, extraConfig.max_size, updateTargets);
} else {
updateTargets(reader.result);
}
}
function resize(readerData, max_size, updateTargets) {
var image = new Image();
image.onload = function (imageEvent) {
// Resize the image
var canvas = document.createElement('canvas'),
//max_size = 544,// TODO : pull max size from a site config
width = image.width,
height = image.height;
if (width > height) {
if (width > max_size) {
height *= max_size / width;
width = max_size;
}
} else {
if (height > max_size) {
width *= max_size / height;
height = max_size;
}
}
canvas.width = width;
canvas.height = height;
canvas.getContext('2d').drawImage(image, 0, 0, width, height);
var dataUrl = canvas.toDataURL('image/jpeg');
/*
var resizedImage = dataURLToBlob(dataUrl);
$.event.trigger({
type: "imageResized",
blob: resizedImage,
url: dataUrl
});
*/
updateTargets(dataUrl);
}
image.src = readerData;
}
function handleChange() {
let file = fileElement[0]['files'][0];
reader.readAsDataURL(file);
}
//fileElement.css({position: 'absolute', left: '-3000px'});
fileElement.addClass('Bas64ImageLoader')
fileElement.on('change', () => {
handleChange();
})
this.getContainer()
.css({padding: '5px', cursor: 'pointer'})
.addClass('b64uploader')
.append($('Select Image to Upload
'))
.append(imgView)
.on('click', function () {
fileElement.trigger('click');
});
$(document.body).append(fileElement);
}
}