PluginProbe
Search Atlas SEO – OTTO AI SEO Automation for WordPress / 2.6.7
Search Atlas SEO – OTTO AI SEO Automation for WordPress v2.6.7
2.7.1 2.7.2 2.7.0 2.6.26 2.6.25 2.6.24 2.6.23 2.6.22 2.6.21 2.6.20 2.6.19 2.6.18 2.6.17 2.6.16 2.6.15 2.6.14 2.6.13 2.6.12 2.6.11 2.6.10 2.6.9 2.6.8 2.6.7 2.6.6 2.6.5 All 141 releases
metasync / views / js / metasync-redirections.js

metasync-redirections.js in Search Atlas SEO – OTTO AI SEO Automation for WordPress 2.6.7, at views/js/metasync-redirections.js

187 lines 6.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 /**
2 * MetaSync redirections — filter auto-submit and pagination tab fix.
3 *
4 * Extracted from views/metasync-redirection.php (Phase 5, #887).
5 */
6 document.addEventListener('DOMContentLoaded', function () {
7 // Auto-submit filters when changed
8 var filterSelects = document.querySelectorAll('#status-filter, #pattern-filter, #http-code-filter');
9 var form = document.getElementById('redirection-form');
10 filterSelects.forEach(function (select) {
11 select.addEventListener('change', function () {
12 var formAction = form.getAttribute('action') || window.location.href;
13 var url = new URL(formAction, window.location.origin);
14 url.searchParams.delete('paged_redir');
15 form.setAttribute('action', url.pathname + url.search);
16
17 // Also add hidden field to ensure pagination resets
18 var pagedInput = form.querySelector('input[name="paged_redir"]');
19 if (!pagedInput) {
20 pagedInput = document.createElement('input');
21 pagedInput.type = 'hidden';
22 pagedInput.name = 'paged_redir';
23 form.appendChild(pagedInput);
24 }
25 pagedInput.value = '1';
26
27 form.submit();
28 });
29 });
30
31 // Add tab parameter to all pagination links in redirections tab
32 function addTabToPaginationLinks() {
33 // Find all pagination links within redirections-content
34 var redirectionsContent = document.getElementById('redirections-content');
35 if (redirectionsContent) {
36 var paginationLinks = redirectionsContent.querySelectorAll('.tablenav-pages a');
37 paginationLinks.forEach(function (link) {
38 var url = new URL(link.href);
39 url.searchParams.set('tab', 'redirections');
40 link.href = url.toString();
41 });
42 }
43 }
44
45 // Run immediately and after a short delay
46 addTabToPaginationLinks();
47 setTimeout(addTabToPaginationLinks, 100);
48 setTimeout(addTabToPaginationLinks, 500);
49
50 // --- Health Check ---
51 var healthBtn = document.getElementById('metasync-check-health-btn');
52 if (!healthBtn || typeof metasyncHealthCheck === 'undefined') {
53 return;
54 }
55
56 var healthBtnLabel = healthBtn.textContent;
57
58 // Single reusable tooltip appended to body — escapes all stacking contexts
59 var floatingTip = document.createElement('div');
60 floatingTip.className = 'health-tooltip-floating';
61 floatingTip.style.display = 'none';
62 document.body.appendChild(floatingTip);
63
64 // Hide tooltip on scroll to prevent detachment from icon
65 window.addEventListener('scroll', function () {
66 floatingTip.style.display = 'none';
67 }, true);
68
69 // Delegated hover handlers — work for any health cells added now or later
70 document.addEventListener('mouseover', function (e) {
71 var cell = e.target.closest('.health-status-cell[data-health-tip]');
72 if (!cell) {
73 return;
74 }
75 var rect = cell.getBoundingClientRect();
76 floatingTip.textContent = cell.getAttribute('data-health-tip');
77 floatingTip.style.display = 'block';
78 var tipRect = floatingTip.getBoundingClientRect();
79 floatingTip.style.left = (rect.left + rect.width / 2 - tipRect.width / 2) + 'px';
80 floatingTip.style.top = (rect.top - tipRect.height - 6) + 'px';
81 });
82
83 document.addEventListener('mouseout', function (e) {
84 var cell = e.target.closest('.health-status-cell[data-health-tip]');
85 if (!cell) {
86 return;
87 }
88 floatingTip.style.display = 'none';
89 });
90
91 healthBtn.addEventListener('click', function () {
92 healthBtn.disabled = true;
93 healthBtn.innerHTML = healthBtnLabel + ' <span class="spinner is-active" style="float:none;margin:0 0 0 6px;vertical-align:middle;"></span>';
94
95 var data = new FormData();
96 data.append('action', 'metasync_check_redirects_health');
97 data.append('nonce', metasyncHealthCheck.healthNonce);
98
99 fetch(metasyncHealthCheck.ajaxUrl, { method: 'POST', body: data, credentials: 'same-origin' })
100 .then(function (r) {
101 return r.json();
102 })
103 .then(function (resp) {
104 healthBtn.disabled = false;
105 healthBtn.textContent = healthBtnLabel;
106
107 if (!resp.success || !resp.data || !resp.data.results) {
108 return;
109 }
110
111 var results = resp.data.results;
112 var map = {};
113 results.forEach(function (r) {
114 map[r.id] = r;
115 });
116
117 var statusIcons = { ok: '�
118 ', loop: '🔴', chain_too_long: '⚠️', dead_end: '💀' };
119 var statusLabels = { ok: 'OK', loop: 'Loop detected', chain_too_long: 'Chain too long', dead_end: 'Dead end' };
120
121 // Insert header column
122 var table = document.querySelector('.wp-list-table');
123 if (!table) {
124 return;
125 }
126
127 // Remove existing health columns (re-run support)
128 table.querySelectorAll('.column-health_status').forEach(function (el) {
129 el.remove();
130 });
131
132 var headerRows = table.querySelectorAll('thead tr, tfoot tr');
133 headerRows.forEach(function (tr) {
134 var statusTh = tr.querySelector('.column-status');
135 if (!statusTh) {
136 // Fallback: insert at end
137 statusTh = tr.lastElementChild;
138 }
139 var th = document.createElement('th');
140 th.className = 'manage-column column-health_status';
141 th.textContent = 'Health';
142 statusTh.parentNode.insertBefore(th, statusTh.nextSibling);
143 });
144
145 // Insert cell per row
146 var bodyRows = table.querySelectorAll('tbody tr');
147 bodyRows.forEach(function (tr) {
148 var cb = tr.querySelector('input[type="checkbox"]');
149 var id = cb ? parseInt(cb.value, 10) : 0;
150 var info = map[id];
151
152 var td = document.createElement('td');
153 td.className = 'column-health_status';
154
155 if (info) {
156 var icon = statusIcons[info.status] || '❓';
157 var label = statusLabels[info.status] || info.status;
158 var tipText = label;
159 if (info.status !== 'ok' && info.chain && info.chain.length > 0) {
160 tipText += ': ' + info.chain.join(' \u2192 ');
161 }
162 td.innerHTML = '<span class="health-status-cell" data-health-tip="' +
163 tipText.replace(/&/g, '&amp;').replace(/"/g, '&quot;').replace(/</g, '&lt;').replace(/>/g, '&gt;') +
164 '">' + icon + '</span>';
165 } else {
166 td.textContent = '\u2014';
167 }
168
169 var statusTd = tr.querySelector('.column-status');
170 if (statusTd) {
171 statusTd.parentNode.insertBefore(td, statusTd.nextSibling);
172 } else {
173 tr.appendChild(td);
174 }
175 });
176
177
178 // Scroll to the table so results are visible
179 table.scrollIntoView({ behavior: 'smooth', block: 'start' });
180 })
181 .catch(function () {
182 healthBtn.disabled = false;
183 healthBtn.textContent = healthBtnLabel;
184 });
185 });
186 });
187