/** * Ticket Tailor Admin Scripts * Version: 3.0 - Enhanced with Color Picker Improvements */ (function ($) { "use strict"; $(document).ready(function () { // =========================== // EXISTING FUNCTIONALITY // =========================== // Confirm actions $(".tt-confirm-action").on("click", function (e) { if (!confirm(ticketTailorAdmin.confirmMessage || "Are you sure?")) { e.preventDefault(); return false; } }); // Test API connection $("#tt-test-api").on("click", function (e) { e.preventDefault(); const $button = $(this); const $status = $("#tt-api-status"); $button.prop("disabled", true).text("Testing..."); $status.removeClass("success error").text(""); $.ajax({ url: ticketTailorAdmin.ajaxUrl, type: "POST", data: { action: "ticket_tailor_test_api", nonce: ticketTailorAdmin.nonce, }, success: function (response) { if (response.success) { $status.addClass("success").text("✔ API connection successful!"); } else { $status .addClass("error") .text("✗ API connection failed: " + response.data); } }, error: function () { $status.addClass("error").text("✗ Connection error"); }, complete: function () { $button.prop("disabled", false).text("Test Connection"); }, }); }); // Auto-dismiss notices setTimeout(function () { $(".notice.is-dismissible").fadeOut(); }, 5000); // Sync progress indicators $(".tt-sync-button").on("click", function () { $(this) .prop("disabled", true) .html( ' Syncing...', ); }); // Table row actions $(".tt-row-actions a").on("click", function (e) { const action = $(this).data("action"); if ( action === "delete" && !confirm("Are you sure you want to delete this item?") ) { e.preventDefault(); } }); // =========================== // ENHANCED COLOR PICKER // =========================== // Function to calculate relative luminance for contrast ratio function getLuminance(hexColor) { // Convert hex to RGB const hex = hexColor.replace("#", ""); const r = parseInt(hex.substr(0, 2), 16) / 255; const g = parseInt(hex.substr(2, 2), 16) / 255; const b = parseInt(hex.substr(4, 2), 16) / 255; // Apply gamma correction const gammaCorrect = (channel) => { return channel <= 0.03928 ? channel / 12.92 : Math.pow((channel + 0.055) / 1.055, 2.4); }; const rLinear = gammaCorrect(r); const gLinear = gammaCorrect(g); const bLinear = gammaCorrect(b); // Calculate relative luminance return 0.2126 * rLinear + 0.7152 * gLinear + 0.0722 * bLinear; } // Function to determine if white or black text is more readable function getContrastColor(hexColor) { const luminance = getLuminance(hexColor); // Use white text for dark colors, black for light colors return luminance > 0.5 ? "#000000" : "#ffffff"; } // Function to format and display hex value with proper contrast function updateHexDisplay($input, color) { const $container = $input.closest(".wp-picker-container"); let $hexDisplay = $container.find(".tt-hex-display"); // Create hex display if it doesn't exist if ($hexDisplay.length === 0) { $hexDisplay = $(''); // Find the color picker button and insert after it const $colorButton = $container.find(".wp-color-result"); $hexDisplay.insertAfter($colorButton); } // Update hex value and text color $hexDisplay.text(color.toUpperCase()); const contrastColor = getContrastColor(color); $hexDisplay.css("color", contrastColor); // Update background to match the selected color for visual context $hexDisplay.css("background-color", color); } // Initialize color pickers with enhanced functionality if ($(".tt-color-picker").length > 0) { $(".tt-color-picker").each(function () { const $this = $(this); const currentColor = $this.val() || $this.data("default-color"); // Initialize WordPress color picker $this.wpColorPicker({ change: function (event, ui) { const color = ui.color.toString(); const field = $(this).attr("id"); // Update hex display updateHexDisplay($(this), color); // Update preview in real-time if (field === "text_color") { $(".preview-title, .preview-description, .preview-venue").css( "color", color, ); } else if (field === "border_color") { $(".tt-preview-card").css("border-color", color); } else if (field === "button_bg") { $(".tt-preview-button").css("background-color", color); } else if (field === "button_text") { $(".tt-preview-button").css("color", color); } else if (field === "button_hover") { // Store hover color for later use $(".tt-preview-button").data("hover-color", color); } }, clear: function () { const field = $(this).attr("id"); const defaultColor = $(this).data("default-color"); // Update hex display with default color updateHexDisplay($(this), defaultColor); // Reset preview to default colors if (field === "text_color") { $(".preview-title, .preview-description, .preview-venue").css( "color", defaultColor, ); } else if (field === "border_color") { $(".tt-preview-card").css("border-color", defaultColor); } else if (field === "button_bg") { $(".tt-preview-button").css("background-color", defaultColor); } else if (field === "button_text") { $(".tt-preview-button").css("color", defaultColor); } else if (field === "button_hover") { $(".tt-preview-button").data("hover-color", defaultColor); } }, }); // Initialize hex display for existing colors if (currentColor) { updateHexDisplay($this, currentColor); } }); // Handle border radius preview in real-time $("#border_radius").on("input", function () { const radius = $(this).val(); $(".tt-preview-card").css("border-radius", radius + "px"); }); // Button hover effect for preview let originalBg = $("#button_bg").val() || $("#button_bg").data("default-color"); let hoverBg = $("#button_hover").val() || $("#button_hover").data("default-color"); $(".tt-preview-button").hover( function () { const currentHover = $("#button_hover").val() || hoverBg; $(this).css("background-color", currentHover); }, function () { const currentBg = $("#button_bg").val() || originalBg; $(this).css("background-color", currentBg); }, ); // Update hover colors when changed $("#button_hover").on("change", function () { hoverBg = $(this).val(); updateHexDisplay($(this), hoverBg); }); $("#button_bg").on("change", function () { originalBg = $(this).val(); updateHexDisplay($(this), originalBg); }); // Also update hex display when color picker is opened/closed $(".wp-color-result").on("click", function () { const $input = $(this) .closest(".wp-picker-container") .find(".tt-color-picker"); const currentColor = $input.val() || $input.data("default-color"); if (currentColor) { updateHexDisplay($input, currentColor); } }); } // =========================== // DASHBOARD ENHANCEMENTS // =========================== // Add smooth scrolling for anchor links $('a[href^="#"]').on("click", function (e) { const target = $(this.getAttribute("href")); if (target.length) { e.preventDefault(); $("html, body") .stop() .animate( { scrollTop: target.offset().top - 40, }, 800, ); } }); // Enhanced loading states $(".button").on("click", function () { const $btn = $(this); if ($btn.hasClass("tt-ajax-button")) { $btn.addClass("tt-loading"); } }); // AJAX complete handler to remove loading states $(document).ajaxComplete(function (event, xhr, settings) { $(".tt-loading").removeClass("tt-loading"); }); // =========================== // ACCESSIBILITY ENHANCEMENTS // =========================== // Add keyboard navigation support for custom elements $(".tt-stat-card, .tt-help-section").attr("tabindex", "0"); // PERFORMANCE FIX: Use event delegation instead of direct binding // This prevents memory leaks when elements are dynamically added/removed $(document) .on("focus", "a, button, input, select, textarea", function () { $(this).addClass("has-focus"); }) .on("blur", "a, button, input, select, textarea", function () { $(this).removeClass("has-focus"); }); // =========================== // RESPONSIVE IMPROVEMENTS // =========================== // Handle responsive table display function checkTableResponsive() { $(".wp-list-table").each(function () { const $table = $(this); const tableWidth = $table.width(); const containerWidth = $table.parent().width(); if (tableWidth > containerWidth) { $table.addClass("tt-responsive-table"); } else { $table.removeClass("tt-responsive-table"); } }); } // Check on load and resize checkTableResponsive(); $(window).on("resize debounce", checkTableResponsive); // =========================== // FORM VALIDATION // =========================== // Basic form validation for settings $("form").on("submit", function () { let isValid = true; // Check required fields $(this) .find("[required]") .each(function () { if (!$(this).val()) { $(this).addClass("error"); isValid = false; } else { $(this).removeClass("error"); } }); // Validate API key format (if present) const $apiKey = $("#api_key"); if ($apiKey.length && $apiKey.val()) { // Basic validation - ensure it's not just whitespace if ($apiKey.val().trim().length < 10) { $apiKey.addClass("error"); isValid = false; } } if (!isValid) { // Show error message if (!$(".tt-validation-error").length) { $( '

Please fill in all required fields correctly.

', ) .insertBefore(this) .delay(3000) .fadeOut(function () { $(this).remove(); }); } return false; } }); // Remove error class on input $("input, select, textarea").on("input change", function () { $(this).removeClass("error"); }); // =========================== // UTILITY FUNCTIONS // =========================== // Debounce function for performance function debounce(func, wait, immediate) { let timeout; return function () { const context = this, args = arguments; const later = function () { timeout = null; if (!immediate) func.apply(context, args); }; const callNow = immediate && !timeout; clearTimeout(timeout); timeout = setTimeout(later, wait); if (callNow) func.apply(context, args); }; } // Add debounced resize event $(window).on( "resize", debounce(function () { $(window).trigger("resize.debounce"); }, 250), ); }); })(jQuery);