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 / media-optimization / assets / js / media-optimization-library.js

media-optimization-library.js in Search Atlas SEO – OTTO AI SEO Automation for WordPress 2.6.7, at media-optimization/assets/js/media-optimization-library.js

408 lines 15.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 /**
2 * Media Optimization Image Library Scripts
3 *
4 * @package Search Atlas SEO
5 * @copyright Copyright (C) 2021-2025, Search Atlas Group - [email protected]
6 * @since 2.6.0
7 *
8 * Localized data expected via metasyncMediaLib:
9 * - ajaxUrl: string
10 * - nonce: string
11 * - batchRunning: bool
12 * - i18n.optimizing: string
13 * - i18n.optimize: string
14 * - i18n.revert: string
15 * - i18n.revertConfirm: string
16 * - i18n.optimizeFailed:string
17 * - i18n.revertFailed: string
18 * - i18n.startBatch: string
19 * - i18n.batchFailed: string
20 * - i18n.batchComplete: string
21 * - i18n.imagesProcessed: string
22 * - i18n.failed: string
23 * - i18n.optimizingOf: string (e.g. "Optimizing")
24 * - i18n.of: string
25 * - i18n.images: string (e.g. "images...")
26 * - i18n.unoptimized: string
27 * - i18n.selectImages: string
28 * - i18n.bulkFailed: string
29 * - i18n.apply: string
30 */
31 (function() {
32 'use strict';
33
34 var config = window.metasyncMediaLib || {};
35 var nonce = config.nonce || '';
36 var ajaxUrl = config.ajaxUrl || window.ajaxurl;
37 var i18n = config.i18n || {};
38 var batchActive = false;
39 var fallbackPoll = null;
40
41 function sanitizeHtml(str) {
42 var div = document.createElement('div');
43 div.textContent = str;
44 return div.innerHTML;
45 }
46
47 function sanitizeTrustedHtml(html) {
48 var parser = new DOMParser();
49 var doc = parser.parseFromString(html, 'text/html');
50 doc.querySelectorAll('script,iframe,object,embed,form,link[rel="import"]').forEach(function(el) { el.remove(); });
51 return doc.body.innerHTML;
52 }
53
54 // ── Single Optimize ──
55 document.addEventListener('click', function(e) {
56 var btn = e.target.closest('.metasync-optimize-btn');
57 if (!btn) return;
58
59 var id = btn.dataset.id;
60 btn.classList.add('loading');
61 btn.disabled = true;
62 btn.innerHTML = '<span class="metasync-batch-spinner" style="width:14px;height:14px;display:inline-block;"></span> ' + sanitizeHtml(i18n.optimizing || 'Optimizing...');
63
64 fetch(ajaxUrl, {
65 method: 'POST',
66 headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
67 body: 'action=metasync_optimize_single_image&nonce=' + nonce + '&attachment_id=' + encodeURIComponent(id)
68 })
69 .then(function(r) { return r.json(); })
70 .then(function(data) {
71 if (data.success) {
72 var row = btn.closest('tr');
73 var statusCell = row.querySelector('.column-status');
74 var parser = new DOMParser();
75 var statusDoc = parser.parseFromString(data.data.status_html || '', 'text/html');
76 statusDoc.querySelectorAll('script,iframe,object,embed,form').forEach(function(el) { el.remove(); });
77 while (statusCell.firstChild) { statusCell.removeChild(statusCell.firstChild); }
78 Array.prototype.forEach.call(statusDoc.body.childNodes, function(node) {
79 statusCell.appendChild(node.cloneNode(true));
80 });
81
82 var actionsCell = row.querySelector('.column-actions');
83 actionsCell.innerHTML = '<button type="button" class="button button-small metasync-revert-btn" data-id="' + sanitizeHtml(String(id)) + '">' +
84 '<span class="dashicons dashicons-undo" style="margin-top:3px;"></span> ' + sanitizeHtml(i18n.revert || 'Revert') + '</button>';
85
86 updateStats();
87 } else {
88 btn.classList.remove('loading');
89 btn.disabled = false;
90 btn.innerHTML = '<span class="dashicons dashicons-performance" style="margin-top:3px;"></span> ' + sanitizeHtml(i18n.optimize || 'Optimize');
91 alert(data.data || (i18n.optimizeFailed || 'Optimization failed.'));
92 }
93 })
94 .catch(function() {
95 btn.classList.remove('loading');
96 btn.disabled = false;
97 btn.innerHTML = '<span class="dashicons dashicons-performance" style="margin-top:3px;"></span> ' + sanitizeHtml(i18n.optimize || 'Optimize');
98 });
99 });
100
101 // ── Single Revert ──
102 document.addEventListener('click', function(e) {
103 var btn = e.target.closest('.metasync-revert-btn');
104 if (!btn) return;
105
106 if (!confirm(i18n.revertConfirm || 'Revert this image to its original format?')) {
107 return;
108 }
109
110 var id = btn.dataset.id;
111 btn.classList.add('loading');
112 btn.disabled = true;
113
114 fetch(ajaxUrl, {
115 method: 'POST',
116 headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
117 body: 'action=metasync_revert_single_image&nonce=' + nonce + '&attachment_id=' + id
118 })
119 .then(function(r) { return r.json(); })
120 .then(function(data) {
121 if (data.success) {
122 var row = btn.closest('tr');
123 var statusCell = row.querySelector('.column-status');
124 statusCell.innerHTML = '<span class="metasync-status-badge metasync-status-unoptimized">' + (i18n.unoptimized || 'Unoptimized') + '</span>';
125
126 var actionsCell = row.querySelector('.column-actions');
127 actionsCell.innerHTML = '<button type="button" class="button button-small button-primary metasync-optimize-btn" data-id="' + id + '">' +
128 '<span class="dashicons dashicons-performance" style="margin-top:3px;"></span> ' + (i18n.optimize || 'Optimize') + '</button>';
129
130 updateStats();
131 } else {
132 btn.classList.remove('loading');
133 btn.disabled = false;
134 alert(data.data || (i18n.revertFailed || 'Revert failed.'));
135 }
136 })
137 .catch(function() {
138 btn.classList.remove('loading');
139 btn.disabled = false;
140 });
141 });
142
143 // ── Optimize All (Start Batch) ──
144 var optimizeAllBtn = document.getElementById('metasync-optimize-all');
145 if (optimizeAllBtn) {
146 optimizeAllBtn.addEventListener('click', function() {
147 if (!confirm(i18n.startBatch || 'Start optimizing all unoptimized images?')) {
148 return;
149 }
150
151 optimizeAllBtn.disabled = true;
152
153 fetch(ajaxUrl, {
154 method: 'POST',
155 headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
156 body: 'action=metasync_start_batch_optimize&nonce=' + nonce
157 })
158 .then(function(r) { return r.json(); })
159 .then(function(data) {
160 if (data.success) {
161 showBatchProgress(data.data);
162 startBatchProcessing();
163 } else {
164 optimizeAllBtn.disabled = false;
165 alert(data.data || (i18n.batchFailed || 'Failed to start batch optimization.'));
166 }
167 })
168 .catch(function() {
169 optimizeAllBtn.disabled = false;
170 });
171 });
172 }
173
174 // ── Cancel Batch ──
175 var cancelBtn = document.getElementById('metasync-cancel-batch');
176 if (cancelBtn) {
177 cancelBtn.addEventListener('click', function() {
178 batchActive = false;
179 stopFallbackPoll();
180
181 fetch(ajaxUrl, {
182 method: 'POST',
183 headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
184 body: 'action=metasync_cancel_batch_optimize&nonce=' + nonce
185 })
186 .then(function(r) { return r.json(); })
187 .then(function() {
188 document.getElementById('metasync-batch-progress').style.display = 'none';
189 if (optimizeAllBtn) optimizeAllBtn.disabled = false;
190 location.reload();
191 });
192 });
193 }
194
195 // ── Copy URL ──
196 document.addEventListener('click', function(e) {
197 var btn = e.target.closest('.metasync-copy-btn');
198 if (!btn) return;
199
200 var url = btn.dataset.url;
201 if (!url) return;
202
203 navigator.clipboard.writeText(url).then(function() {
204 btn.classList.add('copied');
205 var icon = btn.querySelector('.dashicons');
206 if (icon) {
207 icon.className = 'dashicons dashicons-yes';
208 setTimeout(function() {
209 icon.className = 'dashicons dashicons-clipboard';
210 btn.classList.remove('copied');
211 }, 1500);
212 }
213 });
214 });
215
216 // ── Dismiss Complete Notice ──
217 var dismissBtn = document.getElementById('metasync-dismiss-complete');
218 if (dismissBtn) {
219 dismissBtn.addEventListener('click', function() {
220 document.getElementById('metasync-batch-complete').style.display = 'none';
221 });
222 }
223
224 // ── AJAX-Driven Batch Processing ──
225
226 function startBatchProcessing() {
227 batchActive = true;
228 startFallbackPoll();
229 processNextBatch();
230 }
231
232 function processNextBatch() {
233 if (!batchActive) return;
234
235 fetch(ajaxUrl, {
236 method: 'POST',
237 headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
238 body: 'action=metasync_process_batch_tick&nonce=' + nonce
239 })
240 .then(function(r) { return r.json(); })
241 .then(function(data) {
242 if (!data.success || !batchActive) return;
243
244 var progress = data.data;
245 showBatchProgress(progress);
246 applyStats(progress.stats);
247
248 if (progress.status === 'running') {
249 setTimeout(processNextBatch, 500);
250 } else {
251 onBatchFinished(progress);
252 }
253 })
254 .catch(function() {
255 if (batchActive) {
256 setTimeout(processNextBatch, 5000);
257 }
258 });
259 }
260
261 function onBatchFinished(progress) {
262 batchActive = false;
263 stopFallbackPoll();
264 document.getElementById('metasync-batch-progress').style.display = 'none';
265
266 if (progress.status === 'completed') {
267 var completeEl = document.getElementById('metasync-batch-complete');
268 var textEl = document.getElementById('metasync-batch-complete-text');
269 textEl.textContent = (i18n.batchComplete || 'Batch optimization complete!') + ' ' +
270 progress.processed + ' ' + (i18n.imagesProcessed || 'images processed') +
271 (progress.failed > 0 ? ', ' + progress.failed + ' ' + (i18n.failed || 'failed') : '') + '.';
272 completeEl.style.display = 'flex';
273 }
274
275 if (optimizeAllBtn) optimizeAllBtn.disabled = false;
276 setTimeout(function() { location.reload(); }, 1500);
277 }
278
279 function startFallbackPoll() {
280 if (fallbackPoll) return;
281 fallbackPoll = setInterval(function() {
282 fetch(ajaxUrl, {
283 method: 'POST',
284 headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
285 body: 'action=metasync_batch_progress&nonce=' + nonce
286 })
287 .then(function(r) { return r.json(); })
288 .then(function(data) {
289 if (!data.success) return;
290 showBatchProgress(data.data);
291 applyStats(data.data.stats);
292 if (data.data.status !== 'running') {
293 onBatchFinished(data.data);
294 }
295 });
296 }, 10000);
297 }
298
299 function stopFallbackPoll() {
300 if (fallbackPoll) {
301 clearInterval(fallbackPoll);
302 fallbackPoll = null;
303 }
304 }
305
306 function showBatchProgress(progress) {
307 var el = document.getElementById('metasync-batch-progress');
308 el.style.display = '';
309
310 var pct = progress.total > 0 ? Math.round((progress.processed / progress.total) * 100) : 0;
311 document.getElementById('metasync-batch-fill').style.width = pct + '%';
312 document.getElementById('metasync-batch-text').textContent =
313 (i18n.optimizingOf || 'Optimizing') + ' ' + progress.processed + ' ' + (i18n.of || 'of') + ' ' + progress.total + ' ' + (i18n.images || 'images...');
314 document.getElementById('metasync-batch-processed').textContent = progress.processed;
315 document.getElementById('metasync-batch-total').textContent = progress.total;
316 }
317
318 function applyStats(stats) {
319 if (!stats) return;
320 var statNumbers = document.querySelectorAll('.metasync-stat-number');
321 if (statNumbers.length >= 3) {
322 statNumbers[0].textContent = stats.total;
323 statNumbers[1].textContent = stats.optimized;
324 statNumbers[2].textContent = stats.unoptimized;
325 }
326 var pctEl = document.querySelector('.metasync-stat-percentage');
327 if (pctEl) pctEl.textContent = stats.percentage + '%';
328 var fillEl = document.querySelector('.metasync-stat-progress-fill');
329 if (fillEl) fillEl.style.width = stats.percentage + '%';
330 }
331
332 function updateStats() {
333 fetch(ajaxUrl, {
334 method: 'POST',
335 headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
336 body: 'action=metasync_batch_progress&nonce=' + nonce
337 })
338 .then(function(r) { return r.json(); })
339 .then(function(data) {
340 if (data.success) applyStats(data.data.stats);
341 });
342 }
343
344 // Auto-resume AJAX chain if batch is already running (e.g. page reload)
345 if (config.batchRunning) {
346 startBatchProcessing();
347 }
348
349 // ── Bulk Optimize Selected ──
350 var bulkForm = document.getElementById('metasync-image-library-form');
351 if (bulkForm) {
352 bulkForm.querySelectorAll('input[type="submit"], button[type="submit"]').forEach(function(btn) {
353 btn.addEventListener('click', function() {
354 bulkForm._lastSubmitter = btn;
355 });
356 });
357
358 bulkForm.addEventListener('submit', function(e) {
359 var submitter = e.submitter || bulkForm._lastSubmitter;
360 var applyBtnTop = bulkForm.querySelector('#doaction');
361 var applyBtnBottom = bulkForm.querySelector('#doaction2');
362
363 var isTopApply = submitter && submitter === applyBtnTop;
364 var isBottomApply = submitter && submitter === applyBtnBottom;
365 if (!isTopApply && !isBottomApply) return;
366
367 var actionName = isTopApply ? 'action' : 'action2';
368 var action = bulkForm.querySelector('[name="' + actionName + '"]');
369 if (!action || action.value !== 'bulk_optimize') return;
370
371 e.preventDefault();
372
373 var checked = bulkForm.querySelectorAll('input[name="image_ids[]"]:checked');
374 if (checked.length === 0) {
375 alert(i18n.selectImages || 'Please select at least one image.');
376 return;
377 }
378
379 var ids = [];
380 checked.forEach(function(cb) { ids.push(cb.value); });
381
382 var clickedBtn = isTopApply ? applyBtnTop : applyBtnBottom;
383 clickedBtn.disabled = true;
384 clickedBtn.value = i18n.optimizing || 'Optimizing...';
385
386 fetch(ajaxUrl, {
387 method: 'POST',
388 headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
389 body: 'action=metasync_bulk_optimize_selected&nonce=' + nonce + '&ids=' + ids.join(',')
390 })
391 .then(function(r) { return r.json(); })
392 .then(function(data) {
393 if (data.success) {
394 location.reload();
395 } else {
396 clickedBtn.disabled = false;
397 clickedBtn.value = i18n.apply || 'Apply';
398 alert(data.data || (i18n.bulkFailed || 'Bulk optimization failed.'));
399 }
400 })
401 .catch(function() {
402 clickedBtn.disabled = false;
403 clickedBtn.value = i18n.apply || 'Apply';
404 });
405 });
406 }
407 })();
408