"use strict"; // Used for all script file to call Indicator /** * * @param {*} button | 设置按钮 * @param {*} status | 状态 * @returns */ function controlIndicator(button, status) { if (!button) return if (status) { // Show loading indication button.setAttribute('data-kt-indicator', 'on'); // Disable button to avoid multiple click button.disabled = true; } else { // Hide loading indication button.removeAttribute('data-kt-indicator'); // Enable button button.disabled = false; } } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ // Used for all script file to call BlockUI let globalBlockUI; // 全局的 blockUI 实例 /** * 初始化或获取全局的 blockUI 实例。 * 如果不存在全局 blockUI 实例,则会创建一个新的实例。 * @param {string} targetSelector - 元素的选择器,用于触发 blockUI 操作。 * @param {object} options - 配置选项对象。 * @returns {Promise} - 返回一个 Promise,在 blockUI 完全初始化后解决。 */ async function initializeBlockUI(targetSelector, options = {}) { // 获取匹配 targetSelector 的 DOM 元素 const target = document.querySelector(`[data-blockUI-target="${targetSelector}"]`); // 如果全局 blockUI 实例不存在,创建它 if (!globalBlockUI) { // 合并默认选项和用户提供的选项 const mergedOptions = Object.assign( { message: '
Loading...
', }, options ); // 创建新的 blockUI 实例 globalBlockUI = new KTBlockUI(target, mergedOptions); // 阻止界面 globalBlockUI.block(); // 当 blockUI 被释放时,自动销毁实例 globalBlockUI.on("kt.blockui.release", function () { if (globalBlockUI) { globalBlockUI.destroy(); globalBlockUI = null; // 将全局 blockUI 实例置为 null,以便下次重新创建 } }); // 返回一个 Promise,以便异步调用时等待 blockUI 完全初始化 return new Promise(resolve => { globalBlockUI.on("init", function () { resolve(); }); }); } else { // 如果全局 blockUI 实例已存在,直接返回 resolved 的 Promise return Promise.resolve(); } } // 存储已经初始化的 blockUI 实例的数组 const initializedBlockUIs = []; /** * 初始化或获取指定目标的 blockUI 实例。 * 如果尚未初始化该目标的 blockUI 实例,则创建一个新的实例。 * @param {string} targetSelector - 元素的选择器,用于触发 blockUI 操作。 * @param {object} options - 配置选项对象。 * @returns {Promise} - 返回一个 Promise,在 blockUI 完全初始化后解决。 */ async function initializeOrGetBlockUI(targetSelector, options = {}) { // 检查该目标是否已经存在初始化的 blockUI 实例,如果存在,则直接返回已解决的 Promise if (initializedBlockUIs.includes(targetSelector)) { return Promise.resolve(); } // 获取匹配 targetSelector 的 DOM 元素 const target = document.querySelector(`[data-blockUI-target="${targetSelector}"]`); // 合并默认选项和用户提供的选项 const mergedOptions = Object.assign( { message: '
Loading...
', }, options ); // 创建新的 blockUI 实例 const blockUI = new KTBlockUI(target, mergedOptions); // 阻止界面 blockUI.block(); // 当 blockUI 被释放时,从已初始化列表中移除,并销毁实例 blockUI.on("kt.blockui.release", function () { const index = initializedBlockUIs.indexOf(targetSelector); if (index !== -1) { initializedBlockUIs.splice(index, 1); } blockUI.destroy(); }); // 将目标添加到已初始化列表中 initializedBlockUIs.push(targetSelector); // 返回一个 Promise,以便异步调用时等待 blockUI 完全初始化 return new Promise(resolve => { blockUI.on("init", function () { resolve(); }); }); } /** * 销毁指定目标的 blockUI 实例。 * @param {string} targetSelector - 要销毁的 blockUI 实例的目标选择器。 */ function destroyBlockUI(targetSelector) { // 获取匹配 targetSelector 的 DOM 元素 const target = document.querySelector(`[data-blockUI-target="${targetSelector}"]`); // 查找关联的 blockUI 实例并释放它 const blockUI = KTBlockUI.getInstance(target); if (blockUI) { blockUI.release(); } } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ // Used for all script file to call sweet alert /** * * @param {*} title | 标题 * @param {*} text | 信息 * @param {*} type | 状态 * @param {*} showConfirm | 确认按钮 false / true * @param {*} confirmText | 文字 * @param {*} confirmCallback | 处理功能 * @param {*} showCancel | 关闭按钮 false / true * @param {*} cancelText | 文字 * @param {*} cancelCallback | 处理功能 * @param {*} showCustom | 客制化按钮 false / true * @param {*} customButtonText | 文字 * @param {*} customCallback | 处理功能 */ function sweetAlert(title, text, type, showConfirm, confirmText, confirmCallback, showCancel, cancelText, cancelCallback, showCustom, customButtonText, customCallback) { Swal.fire({ title: title, text: text, icon: type, allowOutsideClick: false, showCancelButton: showCancel, cancelButtonText: cancelText || 'Cancel', showDenyButton: showCustom, denyButtonText: customButtonText || 'Custom', showConfirmButton: showConfirm, confirmButtonText: confirmText || 'OK', buttonsStyling: false, customClass: { confirmButton: "btn btn-primary", cancelButton: "btn btn-danger", denyButton: "btn btn-info" }, }).then((result) => { if (result.isConfirmed) { // 用户点击 "Confirm" 按钮 if (typeof confirmCallback === 'function') { confirmCallback(); } } else if (result.isDismissed && result.dismiss === Swal.DismissReason.cancel) { // 用户点击 "Cancel" 按钮或关闭对话框 if (typeof cancelCallback === 'function') { cancelCallback(); } } else if (result.isDenied) { // 用户点击自定义按钮 if (typeof customCallback === 'function') { customCallback(); } } }); } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ // Used for all script file to call ajax let isRequesting = false; // 标志用于防止双击触发多次请求 let requestQueue = []; // Queue to store pending requests /** * 添加请求到队列 * @param {function} requestFunction - 要添加到队列的请求函数 */ function addToQueue(requestFunction) { requestQueue.push(requestFunction); // 如果没有正在进行的请求,立即执行队列中的下一个请求 if (!isRequesting) { executeNextInQueue(); } } /** * 执行队列中的下一个请求 */ function executeNextInQueue() { // 从队列中取出并执行下一个请求函数 const nextRequest = requestQueue.shift(); if (nextRequest) { nextRequest(); } } /** * 发起 Ajax 请求 * @param {string} url - 请求的 URL * @param {string} method - HTTP 方法 (GET, POST, PUT, DELETE) * @param {*} data - 要发送的数据 * @param {number} timeout - 请求超时时间(毫秒) * @param {HTMLElement} button - 触发请求的按钮元素(可选) * @param {function} successFunc - 成功时的回调函数(可选) * @param {function} errorFunc - 失败时的回调函数(可选) */ function ajaxRequest(url, method = "GET", data = null, timeout = 5000, button = null, successFunc = () => { }, errorFunc = () => { }) { // 参数检查 if (typeof url !== "string" || !url.trim() || !/^(get|post|put|delete)$/i.test(method) || (timeout && (!Number.isInteger(+timeout) || timeout <= 0))) { errorCallback("Invalid URL, HTTP method, or timeout"); return; } // 如果正在进行请求,不执行新的请求 if (isRequesting) { return; } // 控制按钮的加载指示器,如果提供了 button 参数 if (button) { controlIndicator(button, true); } // 设置标志为 true,表示正在进行请求 isRequesting = true; // 构造 Ajax 选项 var options = { url, method, dataType: "json", processData: false, contentType: false, timeout, data, }; // 发起 Ajax 请求 setTimeout(() => { $.ajax(options) .done(function (responseData) { // 如果提供了 button 参数,隐藏加载指示器 if (button) { controlIndicator(button, false); } // Close blockUI on error if (globalBlockUI && globalBlockUI.isBlocked()) { globalBlockUI.release(); } // 处理成功回调 handleSuccessCallback(responseData); successFunc(responseData); }) .fail(function (jqXHR) { // 如果提供了 button 参数,隐藏加载指示器 if (button) { controlIndicator(button, false); } // Close blockUI on error if (globalBlockUI && globalBlockUI.isBlocked()) { globalBlockUI.release(); } // 处理失败回调 const errorMessage = jqXHR.status === 500 ? language[ISO].error_500 : jqXHR.status === 0 ? language[ISO].error_0 : language[ISO].error_unknown; handleFailureCallback(jqXHR.status, errorMessage); errorFunc(jqXHR.status, errorMessage); }) .always(function () { // 请求完成后,将标志设置为 false isRequesting = false; // 执行队列中的下一个请求 executeNextInQueue(); }); }, AJAX_TIME); } /** * Converts an object into a URL-encoded query string for a GET request. * @param {Object} obj - The object to be converted. * @returns {string} - The URL-encoded query string. */ function objectToQueryString(obj) { // Use Object.keys to get an array of object keys. return Object.keys(obj) // Use map to create an array of key-value pairs in the form 'key=value'. .map(key => `${encodeURIComponent(key)}=${encodeURIComponent(obj[key])}`) // Use join to concatenate the array elements with '&' to create the final query string. .join('&'); } /** * 处理成功回调函数 * @param {*} data - 返回数据 */ function handleSuccessCallback(data) { switch (data.code) { case 100: // 处理返回信息 sweetAlert('', data.message, 'success', true, language[ISO].ok); break; case 101: // 显示成功消息并刷新页面 sweetAlert('', data.message, 'success', true, language[ISO].ok, () => { location.reload(); }); break; case 102: // 显示成功消息并在新标签页打开指定链接 sweetAlert('', data.message, 'success', true, language[ISO].ok, () => { window.open(data.redirectUrl, '_blank'); }); break; case 103: // 显示成功消息并跳转到指定页面 sweetAlert('', data.message, 'success', true, language[ISO].ok, () => { window.location.href = data.redirectUrl; }); break; case 104: // 不进行处理 break; case 105: // 不进行处理 break; case 106: // 显示成功消息并跳转到指定页面 window.location.href = data.redirectUrl; break; default: // 处理其他情况,显示错误消息 sweetAlert(`[ERROR-${data.code}]`, data.message, 'error', true, language[ISO].ok); break; } } /** * 处理错误回调函数 * @param {*} code - 响应代码 * @param {*} text - 文字 */ function handleFailureCallback(code, text) { // 显示错误消息并提供刷新页面选项(可根据需要调整) sweetAlert(`[ERROR-${code}]`, text, 'error', true, language[ISO].ok, () => { // 可以考虑提供更友好的错误信息,而不是直接刷新页面 // location.reload(); }); } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /** * Initializes the date range picker for a specified element. * * @param {string} selector - The CSS selector for the target element. * @param {Object} options - The options object for configuring the date range picker. * @param {function} successCallback - The callback function to be executed on successful initialization. * @param {function} errorCallback - The callback function to be executed if initialization fails. * @param {function} clickCallback - The callback function to be executed on date range picker click. * @param {function} changeCallback - The callback function to be executed when the date range changes. */ function initializeDateRangePicker(selector, options, successCallback, errorCallback, clickCallback, changeCallback) { try { // Check if the required libraries are loaded if (typeof $ === 'undefined' || typeof $.fn.daterangepicker === 'undefined') { throw new Error('jQuery or daterangepicker plugin not found. Make sure to include them.'); } // Check if the selector exists const targetElement = $(`[data-daterange-picker='${selector}']`); if (targetElement.length === 0) { throw new Error(`Element with selector "${selector}" not found.`); } // Initialize the date range picker without initial dates targetElement.daterangepicker(options); // Attach click event handler targetElement.on('show.daterangepicker', function (ev, picker) { if (typeof clickCallback === 'function') { clickCallback(ev, picker); } }); // Attach change event handler targetElement.on('apply.daterangepicker', function (ev, picker) { if (typeof changeCallback === 'function') { // Default setup const fromSelector = `[data-daterange-picker-target='${selector}-from']`; const toSelector = `[data-daterange-picker-target='${selector}-to']`; const from = $(fromSelector); const to = $(toSelector); // Check if the elements exist before updating their values if (from.length) { // Format startDate using Moment.js const formattedStartDate = moment(picker.startDate).format('YYYY-MM-DD HH:mm'); from.val(formattedStartDate); } if (to.length) { // Format endDate using Moment.js const formattedEndDate = moment(picker.endDate).format('YYYY-MM-DD HH:mm'); to.val(formattedEndDate); } changeCallback(picker.startDate, picker.endDate); } }); // Execute the success callback if (typeof successCallback === 'function') { targetElement.val(null); successCallback(); } } catch (error) { // Execute the error callback if (typeof errorCallback === 'function') { errorCallback(error); } else { // Log the error to the console if no error callback is provided console.error(error.message); } } } /** * Initializes the Flatpickr date picker for a specified input element. * * @param {string} selector - The CSS selector for the target input element. * @param {Object} options - The options object for configuring the Flatpickr date picker. * @returns {Object} - The initialized Flatpickr instance. */ function initializeDatepicker(selector, options = {}) { // Check if a valid selector is provided if (!selector) { console.error("Error: Please provide a valid selector for the datepicker."); return; } // Default configuration options for Flatpickr const defaultOptions = { dateFormat: "Y-m-d", // Default date format // Additional Flatpickr configuration options can be added here }; // Merge default options with user-provided options const mergedOptions = Object.assign({}, defaultOptions, options); // Get the target input element const targetElement = document.querySelector(selector); // Check if the target element is found if (!targetElement) { console.error(`Error: Element with selector "${selector}" not found.`); return; } // Check if the target element is an input element if (targetElement.tagName.toLowerCase() !== "input") { console.error("Error: Datepicker can only be applied to input elements."); return; } // Initialize the Flatpickr date picker const datePickerInstance = flatpickr(targetElement, mergedOptions); // Return the initialized Flatpickr instance for further operations if needed return datePickerInstance; } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /** * Initializes DataTable with custom options and callbacks. * @param {string} tableSelector - The CSS selector for the target table element. * @param {Array} customColumns - Custom columns definition for DataTable, including the footer. * @param {Object} customAjax - Custom ajax configuration for DataTable. * @param {Object} columnDefs - Custom columnDefs configuration for DataTable. * @param {function} customCreatedRow - Custom function for createdRow. * @param {function} customFooterCallback - Custom function for footerCallback. * @param {Object} customOptions - Custom options to override default DataTable options. * @param {function} onDrawCallback - The callback function to be executed on every table re-draw. */ function initializeDataTable(tableSelector, customColumns = null, customAjax = null, columnDefs = {}, customCreatedRow = null, customFooterCallback = null, customOptions = {}, onDrawCallback) { try { const defaultOptions = { // Default options here language: { info: language[ISO].showing, emptyTable: language[ISO].empty, infoEmpty: language[ISO].empty_showing, processing: language[ISO].processing_loading, zeroRecords: language[ISO].empty_searching, }, }; // Use DataTable's default behavior if no custom Ajax is provided const mergedOptions = $.extend(true, {}, defaultOptions, { // Additional DataTable options paging: true, // Enable pagination processing: true, // Show loading indicator // stateSave: true, // Enable state saving // Custom options here columns: customColumns || undefined, ajax: customAjax || undefined, // Use undefined to let DataTable handle default behavior createdRow: customCreatedRow || undefined, // Include the provided custom function or undefined footerCallback: customFooterCallback || undefined, // Rest of the configuration... columnDefs: $.extend(true, [], columnDefs), ...customOptions, // 将 customOptions 直接合并到 mergedOptions 中 }); const dataTable = $(tableSelector).DataTable(mergedOptions); // Auto startup for Metronic menu instances KTMenu.createInstances(); dataTable.on('draw', () => { if (typeof onDrawCallback === 'function') { onDrawCallback(); } }); return dataTable; } catch (error) { console.error(error.message); return null; } } /** * Build a URL with GET parameters. * @param {string} baseUrl - The base URL. * @param {Object} params - GET parameters as key-value pairs. * @returns {string} The complete URL with GET parameters. */ function buildUrlWithParams(baseUrl, params) { const queryString = Object.entries(params) .map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(value)}`) .join('&'); return baseUrl + (queryString ? `?${queryString}` : ''); } /** * Get HTML for a status badge. * * @param {string} type - Type, such as 'player', 'deposit', etc. * @param {string} data - Specific status, e.g., 'Approved', 'Rejected', 'Active'. * @returns {string} Returns HTML string containing the status badge. */ function getStatusBadge(type, data) { // Initialize badge color, defaulting to 'primary'. let badgeColor = 'primary'; // Use switch statement to set badge color based on type. switch (type) { case 'player': badgeColor = data === 'Active' ? 'success' : data === 'Disabled' ? 'danger' : data === 'Validation' ? 'primary' : 'info'; break; case 'User': badgeColor = data === 'Active' ? 'success' : data === 'Disabled' ? 'danger' : 'info'; break; case 'Bank': case 'Promotion': badgeColor = data === 'Enabled' ? 'success' : data === 'Disabled' ? 'danger' : 'info'; break; case 'Deposit': badgeColor = data === 'Completed' ? 'success' : data === 'Failed' ? 'danger' : data === 'Pending' ? 'primary' : 'info'; break; case 'Category': badgeColor = data === 'Active' ? 'success' : data === 'Disabled' ? 'danger' : data === 'Maintenance' ? 'primary' : 'info'; break; case 'API': badgeColor = data === 'Active' ? 'success' : data === 'Disabled' ? 'danger' : data === 'Maintenance' ? 'primary' : 'info'; break; case 'API-LOG': badgeColor = data === 'SUCCESS' ? 'success' : data === 'FAILED' ? 'danger' : 'warning'; break; case 'Kiosk': badgeColor = data === 'Active' ? 'success' : data === 'Disabled' ? 'danger' : data === 'Maintenance' ? 'primary' : 'info'; break; case 'KIOSK-LOG': badgeColor = data === 'SUCCESS' ? 'success' : data === 'FAILED' ? 'danger' : 'warning'; break; case 'HUB': badgeColor = data === 'SUCCESS' ? 'success' : data === 'FAILED' ? 'danger' : 'warning'; break; case 'HUB-LOG': badgeColor = data === 'SUCCESS' ? 'success' : data === 'FAILED' ? 'danger' : 'warning'; break; case 'HUB-PROVIDER': badgeColor = data === 'Active' ? 'success' : data === 'Disabled' ? 'danger' : data === 'Maintenance' ? 'primary' : 'info'; break; // Add other type handling logic if needed. default: // Handling for the default case break; } // Return HTML string containing the status badge. return `
${data}
`; } /** * Get HTML for a type badge. * * @param {string} type - Type, such as 'player', 'deposit', etc. * @param {string} data - Specific status, e.g., 'Approved', 'Rejected', 'Active'. * @returns {string} Returns HTML string containing the status badge. */ function getTypeBadge(type, data) { // Initialize badge color, defaulting to 'primary'. let badgeColor = 'primary'; // Use switch statement to set badge color based on type. switch (type) { case 'User': badgeColor = data === 'Administrator' ? 'primary' : 'info'; break; case 'Session': badgeColor = data === 'SUCCESS' ? 'success' : 'danger'; break; // Add other type handling logic if needed. default: // Handling for the default case break; } // Return HTML string containing the status badge. return `
${data}
`; } /** * Get HTML for a type badge. * * @param {string} type - Type, such as 'player', 'deposit', etc. * @param {string} data - Specific status, e.g., 'Approved', 'Rejected', 'Active'. * @returns {string} Returns HTML string containing the status badge. */ function getCustomBadge(type, data) { // Initialize badge color, defaulting to 'primary'. let message = ''; let badge = ''; // Use switch statement to set badge color based on type. switch (type) { case 'Log': badge = `badge badge-light-${data == '200' ? 'success' : 'danger'}`; message = `${data} ${data == '200' ? 'OK' : 'ERR'}`; break; // Add other type handling logic if needed. default: // Handling for the default case break; } // Return HTML string containing the status badge. return `
${message}
`; } /** * Get HTML links for authorization badges. * * @param {string} type - Type, such as 'player', 'deposit', etc. * @param {string[]} values - Array of values. * @returns {string} Returns HTML string containing authorization badges. */ function getArrayBadges(type, values) { // Create an empty string to store generated HTML links. let htmlLinks = ''; // Use forEach loop to generate HTML links for each authorization. values.forEach(function (value) { // Initialize badge color, defaulting to 'primary'. let badgeColor = 'primary'; // Use switch statement to set badge color based on authorization. switch (type) { case 'Authorization': badgeColor = value == 'READ' ? 'primary' : value == 'WRITE' ? 'danger' : value == 'CREATE' ? 'success' : value == 'EXPORT' ? 'info' : 'warning'; break; case 'Role': badgeColor = value == 'Administrator' ? 'primary' : 'success'; break; case 'Category': badgeColor = 'info'; break; // Add other authorization handling logic if needed. default: // Handling for the default case break; } // Append HTML link for each authorization to the string. htmlLinks += `
${value}
`; }); // Return HTML string containing authorization badges. return htmlLinks; } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /** * Parse a JSON string or return a default value. * * @param {string} str - The JSON string to be parsed. * @param {any} defaultValue - The default value to return if parsing fails. * @param {function} errorCallback - Optional callback function to handle parsing errors. * @returns {any} Returns the parsed JSON or the default value in case of an error. */ function parseJSONOrDefault(str, defaultValue = false, errorCallback = null) { try { // Attempt to parse the JSON string. const parsedJSON = JSON.parse(str); return parsedJSON; // Return the parsed JSON if successful. } catch (error) { // If an error occurs during parsing: // - Call the errorCallback if provided and it is a function. if (errorCallback && typeof errorCallback === 'function') { errorCallback(error); } // - Return the default value if errorCallback is not provided or not a function. return defaultValue; } } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /** * Initializes the FormValidation for a given form. * @param {HTMLFormElement} form - The form element to apply validation. * @param {object} fields - The fields configuration for validation. * @returns {object} - The FormValidation instance. */ function initializeFormValidation(form, fields) { // Initialize FormValidation rules const formValidation = FormValidation.formValidation( form, { fields: fields, plugins: { trigger: new FormValidation.plugins.Trigger(), submitButton: new FormValidation.plugins.SubmitButton(), bootstrap: new FormValidation.plugins.Bootstrap5({ rowSelector: '.fv-row', eleInvalidClass: '', eleValidClass: '' }) } } ); return formValidation; } /** * Validates a form using the provided FormValidation object. * @param {object} formValidation - The FormValidation object to perform validation. * @param {function} callbackSuccess - The callback function to execute on successful validation. * @param {function} callbackFailed - The callback function to execute on failed validation. * @param {boolean} [isScroll=true] - Optional. Specifies whether to scroll to the first error element on failed validation. Default is true. */ function formValidationValidate(formValidation, callbackSuccess, callbackFailed, isScroll = true) { // Validate the form using the validate() method of the formValidation object var validationResult = formValidation.validate(); // Check if the validation result is not undefined or null if (validationResult) { // Asynchronous validation using the validate() method validationResult.then(function (status) { // Check the validation result status if (status === "Valid") { // If the form is valid, execute the callbackSuccess function (if provided) if (typeof callbackSuccess === 'function') { callbackSuccess(); } } else { if (isScroll) { // If the form is invalid and scrolling is enabled, scroll to the first error element var firstError = document.querySelector('.fv-plugins-bootstrap5-row-invalid'); if (firstError) { firstError.scrollIntoView({ behavior: 'smooth', block: 'center' }); // Scroll to the error position } } // Execute the callbackFailed function (if provided) if (typeof callbackFailed === 'function') { callbackFailed(); } } }); } } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ // Function to initialize custom Tagify function initCustomTagify(element, hostUrl, options, formatDataAsSuggestions, maxTag) { const { tagTextProp, dropdown, templates } = options; // Initialize Tagify on the input element const tagify = new Tagify(element, { tagTextProp: tagTextProp, maxTags: maxTag ?? null, enforceWhitelist: true, skipInvalid: true, whitelist: [], // Empty whitelist to allow dynamic suggestions dropdown: dropdown, templates: templates, callbacks: { input: function (e) { const inputValue = e.detail.value.toLowerCase().trim(); if (!inputValue) return; // Fetch data based on the input value fetchData(inputValue) .then(data => { if (data) { // Format fetched data as Tagify suggestion items using the provided function const suggestions = formatDataAsSuggestions(data); // Update Tagify's whitelist with the fetched suggestions tagify.settings.whitelist = suggestions; // Show the dropdown with the updated suggestions tagify.dropdown.show.call(tagify, inputValue); } else { // No data or unexpected data returned, do nothing console.log('No valid data returned from server'); } }) .catch(error => console.error('Error handling data:', error)); } } }); // Function to fetch data based on user input const fetchData = (searchInput) => { // Fetch data from the server using AJAX or any other method // For demonstration purposes, returning a mock response return fetch(`${hostUrl}?search=${searchInput}`) .then(response => { if (response.ok) { // Check if the response is successful (status 200-299) return response.json(); // Parse the JSON data } else { // If response is not successful, throw an error console.log('Error fetching data: Server responded with status ' + response.status); } }) .then(data => { if (data.code === 104) { return data.record; // Return the data if the code is 104 } else { console.log('Unexpected code value: ' + data.code); } }) .catch(error => console.error('Error fetching data:', error)); }; return tagify; } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /** * Initializes a Quill editor. * @param {string} elementId The ID of the HTML element containing the Quill editor. */ function initQuill(elementId) { // Check if the element with the given ID exists const quillElement = document.querySelector('[data-quill-target="' + elementId + '"]'); const contentElement = document.querySelector('[data-quill-body="' + elementId + '"]'); if (!quillElement || !contentElement) { console.error('Quill element or content element not found.'); return; } // Create a Quill editor instance var quill = new Quill(quillElement, { modules: { toolbar: [ // Define toolbar buttons [{ header: [1, 2, false] }], // Headers ['bold', 'italic', 'underline'], // Bold, Italic, Underline ['image', 'code-block'] // Insert Image, Code Block ] }, placeholder: 'Type your text here...', // Placeholder text in the editor theme: 'snow', // Theme used, either 'snow' or 'bubble' }); // Customize the editor toolbar style const toolbar = quillElement.querySelector('.ql-toolbar'); if (toolbar) { // Add custom style classes const toolbarClasses = ['px-5', 'border-top-0', 'border-start-0', 'border-end-0']; toolbar.classList.add(...toolbarClasses); } // Save the HTML content of the Quill editor to the hidden input quill.on('text-change', function () { contentElement.value = quill.root.innerHTML; }); }