/** * HVAC Trainer Communication Templates JavaScript * * Handles read-only communication templates functionality: * - Accordion expand/collapse * - Copy to clipboard * - Search and filtering * - Modal preview * - AJAX template loading * * @package HVAC_Community_Events * @since 2.0.0 */ jQuery(document).ready(function($) { 'use strict'; // Initialize the communication templates interface const HVACTemplates = { // Configuration config: { copyTimeout: 2000, searchDelay: 500, loadMoreCount: 6, }, // State state: { currentPage: 1, isLoading: false, searchTimeout: null, copiedTimeout: null, }, // Initialize all functionality init: function() { this.bindEvents(); this.initializeTooltips(); this.handleInitialLoad(); }, // Bind all event handlers bindEvents: function() { // Template expand/collapse $(document).on('click', '.hvac-template-expand', this.toggleTemplate.bind(this)); // Copy to clipboard $(document).on('click', '.hvac-template-copy', this.copyTemplate.bind(this)); // Preview modal $(document).on('click', '.hvac-template-preview-btn', this.showPreview.bind(this)); // Modal controls $(document).on('click', '.hvac-modal-close', this.closeModal.bind(this)); $(document).on('click', '.hvac-modal-overlay', function(e) { if (e.target === this) { HVACTemplates.closeModal(); } }); // Search and filters $('#hvac-template-search').on('input', this.debounceSearch.bind(this)); $('#hvac-template-category, #hvac-template-channel').on('change', this.performSearch.bind(this)); $('.hvac-search-button').on('click', this.performSearch.bind(this)); // Load more functionality $(document).on('click', '.hvac-load-more', this.loadMoreTemplates.bind(this)); // Keyboard accessibility $(document).on('keydown', this.handleKeyboard.bind(this)); // Token click to copy $(document).on('click', '.hvac-token', this.copyToken.bind(this)); }, // Handle initial page load handleInitialLoad: function() { // Hide templates beyond the initial count for load more functionality const $templates = $('.hvac-template-card'); if ($templates.length > this.config.loadMoreCount) { $templates.slice(this.config.loadMoreCount).hide(); $('.hvac-load-more').show(); } // Apply any URL-based filters this.applyUrlFilters(); }, // Toggle template expand/collapse toggleTemplate: function(e) { e.preventDefault(); const $button = $(e.currentTarget); const $card = $button.closest('.hvac-template-card'); const $preview = $card.find('.hvac-template-preview'); const $fullContent = $card.find('.hvac-template-full-content'); const $expandText = $button.find('.hvac-expand-text'); const $collapseText = $button.find('.hvac-collapse-text'); const $icon = $button.find('.dashicons'); if ($fullContent.is(':visible')) { // Collapse $fullContent.slideUp(300); $preview.slideDown(300); $expandText.show(); $collapseText.hide(); $icon.removeClass('dashicons-minus').addClass('dashicons-plus-alt2'); $button.attr('aria-expanded', 'false'); } else { // Expand $preview.slideUp(300); $fullContent.slideDown(300); $expandText.hide(); $collapseText.show(); $icon.removeClass('dashicons-plus-alt2').addClass('dashicons-minus'); $button.attr('aria-expanded', 'true'); } }, // Copy template to clipboard copyTemplate: function(e) { e.preventDefault(); const $button = $(e.currentTarget); const $card = $button.closest('.hvac-template-card'); const templateId = $button.data('template-id'); // Get template content (prefer full content if expanded) const $fullContent = $card.find('.hvac-template-full-content'); let content; if ($fullContent.is(':visible')) { content = $fullContent.text().trim(); } else { // Get content from data or make AJAX call content = this.getTemplateContent(templateId); } if (content) { this.copyToClipboard(content, $button); } }, // Get template content (from DOM or AJAX) getTemplateContent: function(templateId) { const $card = $('[data-template-id="' + templateId + '"]'); const $fullContent = $card.find('.hvac-template-full-content'); if ($fullContent.length) { return $fullContent.text().trim(); } // Fallback to preview content const $preview = $card.find('.hvac-template-preview'); return $preview.text().trim(); }, // Copy text to clipboard with fallback copyToClipboard: function(text, $button) { const $copyText = $button.find('.hvac-copy-text'); const $copiedText = $button.find('.hvac-copied-text'); // Modern clipboard API if (navigator.clipboard && navigator.clipboard.writeText) { navigator.clipboard.writeText(text).then(() => { this.showCopySuccess($copyText, $copiedText); }).catch(() => { this.fallbackCopyToClipboard(text, $copyText, $copiedText); }); } else { this.fallbackCopyToClipboard(text, $copyText, $copiedText); } }, // Fallback copy method fallbackCopyToClipboard: function(text, $copyText, $copiedText) { const textArea = document.createElement('textarea'); textArea.value = text; textArea.style.position = 'fixed'; textArea.style.left = '-999999px'; textArea.style.top = '-999999px'; document.body.appendChild(textArea); textArea.focus(); textArea.select(); try { const successful = document.execCommand('copy'); if (successful) { this.showCopySuccess($copyText, $copiedText); } else { this.showCopyError(); } } catch (err) { console.error('Copy failed:', err); this.showCopyError(); } finally { document.body.removeChild(textArea); } }, // Show copy success state showCopySuccess: function($copyText, $copiedText) { $copyText.hide(); $copiedText.show(); // Clear any existing timeout if (this.state.copiedTimeout) { clearTimeout(this.state.copiedTimeout); } // Reset after timeout this.state.copiedTimeout = setTimeout(() => { $copiedText.hide(); $copyText.show(); }, this.config.copyTimeout); }, // Show copy error showCopyError: function() { const message = hvacTrainerTemplates.strings.copyError || 'Copy failed. Please select and copy manually.'; this.showNotification(message, 'error'); }, // Show template preview modal showPreview: function(e) { e.preventDefault(); const templateId = $(e.currentTarget).data('template-id'); const $modal = $('#hvac-template-modal'); const $content = $('#hvac-modal-content'); if (!templateId) return; // Show loading state $content.html('

' + (hvacTrainerTemplates.strings.loading || 'Loading...') + '

'); $modal.show().attr('aria-hidden', 'false'); $('body').addClass('hvac-modal-open'); // Focus modal for accessibility $modal.find('.hvac-modal-container').focus(); // Get template data via AJAX $.ajax({ url: hvacTrainerTemplates.ajaxUrl, type: 'POST', data: { action: 'hvac_get_template_preview', template_id: templateId, nonce: hvacTrainerTemplates.nonce }, success: (response) => { if (response.success && response.data) { this.renderPreviewContent(response.data, $content); } else { $content.html('
Failed to load template preview.
'); } }, error: () => { $content.html('
Network error occurred while loading template.
'); } }); }, // Render preview content in modal renderPreviewContent: function(template, $content) { let html = '
'; html += '

' + $('
').text(template.title).html() + '

'; // Template meta html += '
'; if (template.categories && template.categories.length) { html += 'Category: ' + template.categories.map(cat => cat.name).join(', ') + ''; } if (template.channels && template.channels.length) { html += 'Channel: ' + template.channels.map(ch => ch.name).join(', ') + ''; } html += '
'; // Template content html += '
'; html += '
' + $('
').text(template.content).html() + '
'; html += '
'; // Allowed tokens if (template.allowed_tokens) { html += '
'; html += '

Available Tokens:

'; html += '
'; const tokens = template.allowed_tokens.split(', '); tokens.forEach(token => { html += '' + $('
').text(token).html() + ''; }); html += '
'; } html += '
'; $content.html(html); // Update copy button $('.hvac-copy-template').data('template-content', template.content); }, // Close modal closeModal: function() { const $modal = $('#hvac-template-modal'); $modal.hide().attr('aria-hidden', 'true'); $('body').removeClass('hvac-modal-open'); }, // Debounced search debounceSearch: function() { if (this.state.searchTimeout) { clearTimeout(this.state.searchTimeout); } this.state.searchTimeout = setTimeout(() => { this.performSearch(); }, this.config.searchDelay); }, // Perform search and filtering performSearch: function() { if (this.state.isLoading) return; this.state.isLoading = true; const $loading = $('#hvac-templates-loading'); const $list = $('#hvac-templates-list'); const $empty = $('#hvac-templates-empty'); // Show loading state $loading.show(); $list.hide(); $empty.hide(); const searchData = { action: 'hvac_search_templates', search: $('#hvac-template-search').val(), category: $('#hvac-template-category').val(), channel: $('#hvac-template-channel').val(), nonce: hvacTrainerTemplates.nonce }; $.ajax({ url: hvacTrainerTemplates.ajaxUrl, type: 'POST', data: searchData, success: (response) => { if (response.success) { this.updateTemplatesList(response.data.templates); } else { this.showError('Search failed. Please try again.'); } }, error: () => { this.showError('Network error occurred during search.'); }, complete: () => { this.state.isLoading = false; $loading.hide(); $list.show(); } }); }, // Update templates list with search results updateTemplatesList: function(templates) { const $list = $('#hvac-templates-list'); const $empty = $('#hvac-templates-empty'); if (templates.length === 0) { $list.hide(); $empty.show(); return; } // Re-render templates (simplified version) let html = '
'; templates.forEach(template => { html += this.renderTemplateCard(template); }); html += '
'; $list.html(html).show(); $empty.hide(); // Reset pagination this.state.currentPage = 1; }, // Render a single template card renderTemplateCard: function(template) { let html = '
'; // Header html += '
'; html += '

' + $('
').text(template.title).html() + '

'; // Meta information html += '
'; if (template.channels && template.channels.length) { const channel = template.channels[0]; const icon = channel.slug === 'email' ? 'email' : 'smartphone'; html += ''; html += '' + channel.name; html += ''; } if (template.categories && template.categories.length) { html += '' + template.categories.map(cat => cat.name).join(', ') + ''; } html += '
'; html += '
'; // Excerpt if (template.excerpt) { html += '

' + $('

').text(template.excerpt).html() + '

'; } // Content html += '
'; html += '
' + this.truncateText(template.content, 20) + '
'; html += ''; // Actions html += '
'; html += ''; html += ''; html += '
'; // Tokens if (template.allowed_tokens) { html += '
'; html += '

Available Tokens:

'; html += '
'; const tokens = template.allowed_tokens.split(', '); tokens.forEach(token => { html += '' + $('
').text(token).html() + ''; }); html += '
'; } html += '
'; return html; }, // Load more templates loadMoreTemplates: function(e) { e.preventDefault(); const $button = $(e.currentTarget); const $hiddenTemplates = $('.hvac-template-card:hidden'); const nextBatch = $hiddenTemplates.slice(0, this.config.loadMoreCount); if (nextBatch.length === 0) { $button.hide(); return; } nextBatch.fadeIn(300); this.state.currentPage++; // Hide button if no more templates if (nextBatch.length < this.config.loadMoreCount || $hiddenTemplates.length <= this.config.loadMoreCount) { $button.hide(); } }, // Copy individual token copyToken: function(e) { e.preventDefault(); const token = $(e.currentTarget).text(); if (navigator.clipboard && navigator.clipboard.writeText) { navigator.clipboard.writeText(token).then(() => { this.showNotification('Token copied: ' + token, 'success'); }).catch(() => { this.showNotification('Failed to copy token', 'error'); }); } }, // Handle keyboard navigation handleKeyboard: function(e) { // Close modal with Escape if (e.key === 'Escape' && $('#hvac-template-modal').is(':visible')) { this.closeModal(); e.preventDefault(); } // Quick search with / if (e.key === '/' && !$(e.target).is(':input')) { $('#hvac-template-search').focus(); e.preventDefault(); } }, // Apply URL-based filters applyUrlFilters: function() { const urlParams = new URLSearchParams(window.location.search); const category = urlParams.get('category'); const channel = urlParams.get('channel'); const search = urlParams.get('search'); if (category) $('#hvac-template-category').val(category); if (channel) $('#hvac-template-channel').val(channel); if (search) $('#hvac-template-search').val(search); if (category || channel || search) { this.performSearch(); } }, // Initialize tooltips (if tooltip library is available) initializeTooltips: function() { // Implementation depends on available tooltip library }, // Show notification showNotification: function(message, type) { // Create notification element const $notification = $('
' + message + '
'); $('body').append($notification); $notification.fadeIn(300).delay(3000).fadeOut(300, function() { $(this).remove(); }); }, // Show error message showError: function(message) { this.showNotification(message, 'error'); }, // Truncate text helper truncateText: function(text, wordLimit) { const words = text.split(' '); if (words.length <= wordLimit) return text; return words.slice(0, wordLimit).join(' ') + '...'; } }; // Initialize when DOM is ready HVACTemplates.init(); // Handle modal copy button $(document).on('click', '.hvac-copy-template', function(e) { if ($(this).closest('#hvac-template-modal').length) { e.preventDefault(); const content = $(this).data('template-content'); if (content) { HVACTemplates.copyToClipboard(content, $(this)); } } }); });