PHP Classes

File: public/assets/js/main.js

Recommend this page to a friend!
  Classes of Francisco Núñez   Catalyst   public/assets/js/main.js   Download  
File: public/assets/js/main.js
Role: Auxiliary data
Content type: text/plain
Description: Auxiliary data
Class: Catalyst
Framework to develop MVC-based PHP applications
Author: By
Last change:
Date: 2 days ago
Size: 13,913 bytes
 

Contents

Class file image Download
/************************************************************************************** * * Catalyst PHP Framework - JavaScript Component * ES6+/ES7 Standard * * @package Catalyst * @subpackage Js * @see https://github.com/arcanisgk/catalyst * * @author Walter Nuñez (arcanisgk/original founder) <[email protected]> * @copyright 2023 - 2025 * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License * * @note This program is distributed in the hope that it will be useful * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. * * @category Framework * @filesource * * @link https://catalyst.dock Local development URL * * Main component for the Catalyst Framework * */ /** * Handle OAuth credential operations with specialized UI updates * * @param {string} action - The action to perform ('save' or 'clear') * @param {HTMLFormElement} form - The credentials form * @param {string} serviceKey - The service key identifier * @param {bootstrap.Modal} modal - The Bootstrap modal to hide on success * @returns {Promise} - Promise that resolves when operation is complete */ async function handleOAuthCredentials(action, form, serviceKey, modal) { try { let url, data; if (action === 'save') { url = '/configure/oauth/save'; const formData = new FormData(form); data = new URLSearchParams(formData).toString(); } else if (action === 'clear') { url = '/configure/oauth/clear'; data = `service_key=${serviceKey}`; } else { throw new Error('Invalid action'); } const response = await fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded', 'X-Requested-With': 'XMLHttpRequest' }, body: data }); const result = await response.json(); if (result.success) { // Get service checkbox for UI updates const serviceCheckbox = document.querySelector(`.service-checkbox[data-service="${serviceKey}"]`); if (serviceCheckbox) { const serviceItem = serviceCheckbox.closest('.service-item'); if (serviceItem) { if (action === 'save') { // Add success badge if saving const badgeParent = serviceItem.querySelector('.form-check').parentElement; if (!badgeParent.querySelector('.text-success')) { const badge = document.createElement('span'); badge.className = 'ms-1 text-success'; badge.title = 'Credentials configured'; badge.innerHTML = '<i class="bi bi-check-circle"></i>'; badgeParent.appendChild(badge); } } else if (action === 'clear') { // Remove badge if clearing const badge = serviceItem.querySelector('.text-success'); if (badge) badge.remove(); } } } // Reset form and hide modal if (form) form.reset(); if (modal) modal.hide(); // Show success toast window.toasts.success(`Service credentials ${action === 'save' ? 'saved' : 'cleared'} successfully`); } else { window.toasts.error(`Error: ${result.message}`); } return result; } catch (error) { console.error(`Error ${action}ing credentials:`, error); window.toasts.error(`An error occurred while ${action}ing credentials`); throw error; } } /** * Handle configuration form submissions with standard UI feedback * * @param {HTMLFormElement|string} form - Form element or form selector * @param {string} endpoint - API endpoint to submit data to * @param {Object} options - Additional options * @returns {Promise} - Promise that resolves when submission is complete */ async function handleConfigSubmit(form, endpoint, options = {}) { // Default options const defaults = { redirectDelay: 1000, loadingText: '<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span> Saving...', submitSelector: 'button[type="submit"]', collectFromForms: null, // Optional selector for multiple forms to collect data from preProcess: null, // Optional function to process data before submission }; options = {...defaults, ...options}; // Get the form element if a selector was provided if (typeof form === 'string') { form = document.querySelector(form); } if (!form) { console.error('Form not found'); return false; } // Find submit button const submitButton = options.submitSelector instanceof HTMLElement ? options.submitSelector : form.querySelector(options.submitSelector); // Store original button content const originalButtonContent = submitButton ? submitButton.innerHTML : ''; try { // Disable submit button and show loading indicator if (submitButton) { submitButton.disabled = true; submitButton.innerHTML = options.loadingText; } // Collect form data let formData = new FormData(form); // If we need to collect data from multiple forms if (options.collectFromForms) { const additionalForms = document.querySelectorAll(options.collectFromForms); additionalForms.forEach(additionalForm => { const additionalFormData = new FormData(additionalForm); for (const [key, value] of additionalFormData.entries()) { formData.append(key, value); } }); } // Get CSRF token from the form or document const csrfToken = form.querySelector('input[name="csrf_token"]')?.value || document.querySelector('input[name="csrf_token"]')?.value; // Ensure CSRF token is included if (csrfToken && !formData.has('csrf_token')) { formData.append('csrf_token', csrfToken); } // Allow pre-processing of form data if needed if (typeof options.preProcess === 'function') { formData = options.preProcess(formData); } // Send the request const response = await fetch(endpoint, { method: 'POST', body: formData, headers: { 'X-CSRF-TOKEN': csrfToken || '', 'X-Requested-With': 'XMLHttpRequest' } }); // Parse the response const result = await response.json(); // Handle the result if (result.success) { window.toasts.success(result.message || 'Configuration saved successfully'); // Redirect if specified if (result.redirect) { setTimeout(() => { window.location.href = result.redirect; }, options.redirectDelay); } return true; } else { window.toasts.error(result.message || 'Failed to save configuration'); console.error('Form submission error:', result); return false; } } catch (error) { console.error('Form submission error:', error); window.toasts.error('An unexpected error occurred'); return false; } finally { // Restore submit button if (submitButton) { submitButton.disabled = false; submitButton.innerHTML = originalButtonContent; } } } /** * Función utilitaria para peticiones AJAX * @param {string} url - URL del endpoint * @param {Object} data - Datos a enviar * @param {Object} options - Opciones adicionales * @returns {Promise} - Promesa con la respuesta JSON */ async function apiPost(url, data, options = {redirectDelay: 1000, handleRedirect: true}) { try { // Add CSRF token to the data if it doesn't already exist if (typeof data === 'object' && data !== null) { // Get CSRF token from any form or meta tag on the page const csrfTokenElement = document.querySelector('input[name="csrf_token"]'); const csrfToken = csrfTokenElement ? csrfTokenElement.value : null; if (csrfToken && !data.csrf_token) { data.csrf_token = csrfToken; } } const response = await fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-Requested-With': 'XMLHttpRequest', 'Accept': 'application/json', 'X-CSRF-TOKEN': document.querySelector('input[name="csrf_token"]')?.value || '' }, body: JSON.stringify(data) }); const result = await response.json(); // Manejo estandarizado de respuestas if (result.success) { window.toasts.success(result.message); // Handle redirect if present and not disabled in options if (result.redirect && options.handleRedirect !== false) { // Optional delay to allow toast to be seen if (options.redirectDelay) { await new Promise(resolve => setTimeout(resolve, options.redirectDelay)); } window.location.href = result.redirect; } } else { window.toasts.error(result.message || 'Error en la operación'); } return result; } catch (error) { console.error('Error en petición API:', error); window.toasts.error('Error de conexión'); throw error; } } // Generic password toggle functionality /** * Password Toggle Utility * * A standalone utility that adds toggle functionality to password fields * Works with both static and dynamically added elements */ (function () { 'use strict'; /** * Initialize password toggle functionality */ function initPasswordToggle() { // Use event delegation for all toggle password buttons document.addEventListener('click', function (event) { // Find if a toggle-password button was clicked or any of its children const toggleButton = event.target.closest('.toggle-password'); if (!toggleButton) return; // Find the associated input field (should be a sibling in the same input-group) const inputGroup = toggleButton.closest('.input-group'); if (!inputGroup) return; const passwordInput = inputGroup.querySelector('input[type="password"], input[type="text"]'); if (!passwordInput) return; // Find the icon element const iconElement = toggleButton.querySelector('i, .bi'); // Toggle the password visibility if (passwordInput.type === 'password') { passwordInput.type = 'text'; if (iconElement) { iconElement.classList.remove('bi-eye'); iconElement.classList.add('bi-eye-slash'); } } else { passwordInput.type = 'password'; if (iconElement) { iconElement.classList.remove('bi-eye-slash'); iconElement.classList.add('bi-eye'); } } // Prevent the default button action event.preventDefault(); }); } // Initialize when the DOM is fully loaded if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', initPasswordToggle); } else { initPasswordToggle(); } })(); // Add global CSRF protection for all fetch requests (function () { // Store the original fetch function const originalFetch = window.fetch; // Override fetch with our version that adds CSRF tokens window.fetch = function (url, options = {}) { // Don't modify GET requests or requests that already have a body if (options.method && options.method.toUpperCase() !== 'GET') { const csrfToken = document.querySelector('input[name="csrf_token"]')?.value; if (csrfToken) { // If it's a FormData object, append the token if (options.body instanceof FormData) { if (!options.body.has('csrf_token')) { options.body.append('csrf_token', csrfToken); } } else if (typeof options.body === 'string' && options.headers?.['Content-Type'] === 'application/json') { // If it's JSON, parse and add token try { const bodyData = JSON.parse(options.body); if (!bodyData.csrf_token) { bodyData.csrf_token = csrfToken; options.body = JSON.stringify(bodyData); } } catch (e) { // Not valid JSON, leave as is } } // Add CSRF header for all non-GET requests options.headers = options.headers || {}; if (!options.headers['X-CSRF-TOKEN']) { options.headers['X-CSRF-TOKEN'] = csrfToken; } } } // Call the original fetch with our modified options return originalFetch(url, options); }; })();