🏠 Root
/
home
/
artorgp
/
www
/
wp-content
/
plugins
/
eli-php-compatibility-scanner
/
assets
/
src
/
js
/
classes
/
Editing: TargetsManager.js
/** * TargetsManager - Handles loading and managing scan targets (plugins and themes) */ import jQuery from 'jquery'; export class TargetsManager { constructor(ajaxClient) { this.ajaxClient = ajaxClient; this.targets = { plugins: [], themes: [] }; } /** * Initialize targets manager */ async init() { await this.loadTargets(); this.bindEvents(); this.updateOverviewDisplay(); } /** * Load available scan targets from the backend */ async loadTargets() { try { const $targets = jQuery('#phpcompat-targets'); $targets.html('<div class="phpcompat-loader">Loading targets...</div>'); const response = await this.ajaxClient.getTargets(); if (response && response.success && response.data) { this.targets = { plugins: response.data.plugins || [], themes: response.data.themes || [] }; this.renderTargets(); } } catch (error) { console.error('Failed to load targets:', error); jQuery('#phpcompat-targets').html('<div class="phpcompat-error">Failed to load targets</div>'); } } /** * Render targets in the UI */ renderTargets() { const $targets = jQuery('#phpcompat-targets'); let html = ''; if (this.targets.plugins.length > 0) { html += '<div class="phpcompat-target-group"><strong>Plugins</strong></div>'; this.targets.plugins.forEach(plugin => { html += this.renderTargetItem(plugin, 'plugin'); }); } if (this.targets.themes.length > 0) { html += '<div class="phpcompat-target-group"><strong>Themes</strong></div>'; this.targets.themes.forEach(theme => { html += this.renderTargetItem(theme, 'theme'); }); } $targets.html(html); } /** * Render a single target item */ renderTargetItem(item, type) { const escapedSlug = item.slug.replace(/"/g, '"'); const escapedName = jQuery('<div/>').text(item.name).html(); const activeBadge = item.active ? ' <span class="phpcompat-target-active">Active</span>' : ''; const activeAttr = item.active ? ' data-active="1"' : ''; return `<label class="phpcompat-target-item" data-name="${escapedName.replace(/"/g, '"')}"${activeAttr}> <input type="checkbox" class="phpcompat-target" data-type="${type}" data-slug="${escapedSlug}"> ${escapedName}${activeBadge} </label>`; } /** * Bind event handlers for target interactions */ bindEvents() { jQuery(document).on('change', '.phpcompat-target', () => { this.updateOverviewDisplay(); }); jQuery('#phpcompat-select-all').on('click', () => { const $all = jQuery('.phpcompat-target'); const allChecked = $all.length > 0 && $all.length === $all.filter(':checked').length; $all.prop('checked', !allChecked); this.updateOverviewDisplay(); }); jQuery('#phpcompat-select-active').on('click', () => { jQuery('.phpcompat-target').prop('checked', false); jQuery('.phpcompat-target-item[data-active="1"]').find('.phpcompat-target').prop('checked', true); this.updateOverviewDisplay(); }); jQuery(document).on('click', '.phpcompat-overview-tag', function () { const slug = jQuery(this).data('slug'); const type = jQuery(this).data('type'); const $target = jQuery(`.phpcompat-target[data-slug="${slug}"][data-type="${type}"]`).closest('.phpcompat-target-item'); if (!$target.length) return; const adminBarHeight = jQuery('#wpadminbar').outerHeight() || 32; const $sticky = jQuery('.phpcompat-sticky-controls'); const offset = adminBarHeight + ($sticky.hasClass('pinned') ? $sticky.outerHeight() + 10 : 10); jQuery('html, body').animate({ scrollTop: $target.offset().top - offset }, 400); $target.addClass('phpcompat-highlight'); setTimeout(() => $target.removeClass('phpcompat-highlight'), 1500); }); } /** * Build a unique key for a target */ _targetKey(type, slug) { return `${type}--${slug}`; } /** * Update the unified overview display. * Shows every selected target as a tag whose appearance reflects its scan state. * Previously completed scan results are preserved across re-renders. */ updateOverviewDisplay() { const $list = jQuery('#phpcompat-overview-list'); const $selected = jQuery('.phpcompat-target:checked'); if ($selected.length === 0) { $list.html('<span class="phpcompat-overview-empty">No targets selected</span>'); return; } const existing = {}; $list.find('.phpcompat-overview-tag').each(function () { existing[jQuery(this).data('key')] = jQuery(this); }); let html = ''; const newKeys = new Set(); $selected.each((_, el) => { const $el = jQuery(el); const type = $el.data('type'); const slug = $el.data('slug'); const key = `${type}--${slug}`; const name = $el.closest('.phpcompat-target-item').data('name') || $el.closest('.phpcompat-target-item').text().trim(); newKeys.add(key); if (existing[key]) { return; // keep the existing DOM node (preserves state) } html += `<span class="phpcompat-overview-tag pending" data-key="${key}" data-type="${type}" data-slug="${slug}">` + `<span class="phpcompat-overview-tag-name">${name}</span>` + `</span>`; }); // Remove tags for targets that were unselected for (const key in existing) { if (!newKeys.has(key)) { existing[key].remove(); } } if (html) { $list.find('.phpcompat-overview-empty').remove(); $list.append(html); } if ($list.find('.phpcompat-overview-tag').length === 0) { $list.html('<span class="phpcompat-overview-empty">No targets selected</span>'); } } /** * Mark a target as currently scanning (blue + spinner) */ setTargetScanning(type, slug) { const key = this._targetKey(type, slug); const $tag = jQuery(`#phpcompat-overview-list .phpcompat-overview-tag[data-key="${key}"]`); if (!$tag.length) return; $tag.removeClass('pending success errors warnings').addClass('scanning'); const name = $tag.find('.phpcompat-overview-tag-name').text(); $tag.html( `<span class="phpcompat-overview-tag-name">${name}</span>` + `<span class="phpcompat-spinner"></span>` ); } /** * Mark a target scan as complete. * @param {string} type - 'plugin' or 'theme' * @param {string} slug * @param {number} totalErrors * @param {number} totalWarnings */ setTargetComplete(type, slug, totalErrors, totalWarnings) { const key = this._targetKey(type, slug); const $tag = jQuery(`#phpcompat-overview-list .phpcompat-overview-tag[data-key="${key}"]`); if (!$tag.length) return; $tag.removeClass('pending scanning'); const name = $tag.find('.phpcompat-overview-tag-name').text(); let stateClass, icon, detail; if (totalErrors > 0) { stateClass = 'errors'; icon = '❌'; detail = `${totalErrors} error${totalErrors > 1 ? 's' : ''}`; if (totalWarnings > 0) { detail += `, ${totalWarnings} warning${totalWarnings > 1 ? 's' : ''}`; } } else if (totalWarnings > 0) { stateClass = 'warnings'; icon = '⚠️'; detail = `${totalWarnings} warning${totalWarnings > 1 ? 's' : ''}`; } else { stateClass = 'success'; icon = '✅'; detail = 'No issues'; } $tag.addClass(stateClass); $tag.html( `<span class="phpcompat-overview-tag-name">${name}</span>` + `<span class="phpcompat-overview-tag-detail">${icon} ${detail}</span>` ); } /** * Get selected targets */ getSelectedTargets() { const $items = jQuery('.phpcompat-target:checked'); return $items.toArray().map(item => { const $item = jQuery(item); const $label = $item.closest('.phpcompat-target-item'); return { element: item, type: $item.data('type'), slug: $item.data('slug'), name: $label.data('name') || $label.text().trim() }; }); } /** * Check if any targets are selected */ hasSelectedTargets() { return jQuery('.phpcompat-target:checked').length > 0; } /** * Uncheck all selected targets */ uncheckAllTargets() { jQuery('.phpcompat-target:checked').prop('checked', false); this.updateOverviewDisplay(); } }
Save
Cancel