/** * 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...') + '
' + $('').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 += '';
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));
}
}
});
});