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.createHeaders()}
`; 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); } }