PluginProbe
King Addons for Elementor – 100+ Elementor Widgets, 4 000+ Elementor Templates, WooCommerce Builder, Mega Menu, Popup Builder / 51.1.86
King Addons for Elementor – 100+ Elementor Widgets, 4 000+ Elementor Templates, WooCommerce Builder, Mega Menu, Popup Builder v51.1.86
51.1.86 51.1.84 51.1.85 51.1.83 51.1.82 51.1.81 51.1.79 51.1.78 51.1.77 51.1.76 51.1.74 51.1.75 51.1.65 51.1.64 51.1.63 trunk 51.1.14 51.1.2 51.1.35 51.1.36 51.1.37 51.1.38 51.1.39 51.1.44 51.1.45 All 40 releases
king-addons / includes / extensions / Image_Optimizer / templates / attachment-metabox.php

attachment-metabox.php in King Addons for Elementor – 100+ Elementor Widgets, 4 000+ Elementor Templates, WooCommerce Builder, Mega Menu, Popup Builder 51.1.86, at includes/extensions/Image_Optimizer/templates/attachment-metabox.php

439 lines 15.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Attachment metabox template for Image Optimizer.
4 *
5 * @package King_Addons
6 * @var WP_Post $post
7 * @var array|null $meta
8 * @var int $file_size
9 */
10
11 if (!defined('ABSPATH')) {
12 exit;
13 }
14
15 use King_Addons\Image_Optimizer\Image_Optimizer;
16
17 $status = $meta['status'] ?? 'pending';
18 $is_optimized = $status === 'optimized';
19 $optimizer = Image_Optimizer::instance();
20 $settings = $optimizer->get_settings();
21
22 $full_size_meta = is_array($meta) && !empty($meta['sizes']['full']) ? $meta['sizes']['full'] : null;
23 $full_original_path = is_array($full_size_meta) ? ($full_size_meta['original_path'] ?? '') : '';
24 $full_optimized_path = is_array($full_size_meta) ? ($full_size_meta['optimized_path'] ?? ($full_size_meta['webp_path'] ?? '')) : '';
25
26 $full_original_bytes = 0;
27 if (!empty($full_original_path) && is_string($full_original_path) && file_exists($full_original_path)) {
28 $full_original_bytes = (int) filesize($full_original_path);
29 } elseif (is_array($full_size_meta)) {
30 $full_original_bytes = (int) ($full_size_meta['original_size'] ?? 0);
31 }
32
33 $full_optimized_bytes = 0;
34 if (!empty($full_optimized_path) && is_string($full_optimized_path) && file_exists($full_optimized_path)) {
35 $full_optimized_bytes = (int) filesize($full_optimized_path);
36 } elseif (is_array($full_size_meta)) {
37 $full_optimized_bytes = (int) ($full_size_meta['optimized_size'] ?? 0);
38 }
39
40 $full_saved_bytes = max(0, $full_original_bytes - $full_optimized_bytes);
41 $full_saved_percent = $full_original_bytes > 0 ? round(($full_saved_bytes / $full_original_bytes) * 100, 1) : 0;
42 $full_ratio_percent = $full_original_bytes > 0 ? round(($full_optimized_bytes / $full_original_bytes) * 100, 1) : 0;
43 ?>
44
45 <div class="king-img-opt-metabox">
46 <?php if ($is_optimized): ?>
47 <div class="king-img-opt-status optimized">
48 <span class="dashicons dashicons-yes-alt"></span>
49 <?php esc_html_e('Optimized', 'king-addons'); ?>
50 </div>
51
52 <div class="king-img-opt-stats">
53 <?php if ($full_original_bytes > 0 && $full_optimized_bytes > 0): ?>
54 <div class="king-img-opt-stat-row">
55 <span class="label"><?php esc_html_e('Full (original):', 'king-addons'); ?></span>
56 <span class="value"><?php echo esc_html(Image_Optimizer::format_bytes($full_original_bytes)); ?></span>
57 </div>
58 <div class="king-img-opt-stat-row">
59 <span class="label"><?php esc_html_e('Full (optimized):', 'king-addons'); ?></span>
60 <span class="value"><?php echo esc_html(Image_Optimizer::format_bytes($full_optimized_bytes)); ?></span>
61 </div>
62 <div class="king-img-opt-stat-row highlight">
63 <span class="label"><?php esc_html_e('Full saved:', 'king-addons'); ?></span>
64 <span class="value"><?php echo esc_html(Image_Optimizer::format_bytes($full_saved_bytes)); ?> (<?php echo esc_html($full_saved_percent); ?>%)</span>
65 </div>
66 <div class="king-img-opt-stat-row">
67 <span class="label"><?php esc_html_e('Compression ratio:', 'king-addons'); ?></span>
68 <span class="value"><?php echo esc_html($full_ratio_percent); ?>%</span>
69 </div>
70 <hr class="king-img-opt-divider" />
71 <?php endif; ?>
72
73 <div class="king-img-opt-stat-row">
74 <span class="label"><?php esc_html_e('Original:', 'king-addons'); ?></span>
75 <span class="value"><?php echo esc_html(Image_Optimizer::format_bytes($meta['total_original_bytes'] ?? 0)); ?></span>
76 </div>
77 <div class="king-img-opt-stat-row">
78 <span class="label"><?php esc_html_e('Optimized:', 'king-addons'); ?></span>
79 <span class="value"><?php echo esc_html(Image_Optimizer::format_bytes($meta['total_optimized_bytes'] ?? 0)); ?></span>
80 </div>
81 <div class="king-img-opt-stat-row highlight">
82 <span class="label"><?php esc_html_e('Saved:', 'king-addons'); ?></span>
83 <span class="value"><?php echo esc_html(Image_Optimizer::format_bytes($meta['total_saved_bytes'] ?? 0)); ?> (<?php echo esc_html($meta['savings_percent'] ?? 0); ?>%)</span>
84 </div>
85 <div class="king-img-opt-stat-row">
86 <span class="label"><?php esc_html_e('Format:', 'king-addons'); ?></span>
87 <span class="value"><?php echo esc_html(strtoupper($meta['format'] ?? 'webp')); ?></span>
88 </div>
89 </div>
90
91 <?php if (!empty($meta['sizes'])): ?>
92 <details class="king-img-opt-sizes">
93 <summary><?php printf(esc_html__('%d sizes optimized', 'king-addons'), count($meta['sizes'])); ?></summary>
94 <ul>
95 <?php foreach ($meta['sizes'] as $size_name => $size_data): ?>
96 <?php
97 $o = (int) ($size_data['original_size'] ?? 0);
98 $n = (int) ($size_data['optimized_size'] ?? 0);
99 $sb = max(0, $o - $n);
100 $sp = $o > 0 ? round(($sb / $o) * 100, 1) : 0;
101 ?>
102 <li>
103 <strong><?php echo esc_html($size_name); ?></strong>:
104 <?php echo esc_html(Image_Optimizer::format_bytes($size_data['original_size'] ?? 0)); ?> →
105 <?php echo esc_html(Image_Optimizer::format_bytes($size_data['optimized_size'] ?? 0)); ?>
106 <?php if ($o > 0 && $n > 0): ?>
107 (<?php echo esc_html(Image_Optimizer::format_bytes($sb)); ?>, <?php echo esc_html($sp); ?>%)
108 <?php endif; ?>
109 </li>
110 <?php endforeach; ?>
111 </ul>
112 </details>
113 <?php endif; ?>
114
115 <div class="king-img-opt-actions">
116 <button type="button" class="button king-img-reoptimize" data-id="<?php echo esc_attr($post->ID); ?>">
117 <span class="dashicons dashicons-update"></span>
118 <?php esc_html_e('Re-optimize', 'king-addons'); ?>
119 </button>
120 <button type="button" class="button king-img-restore" data-id="<?php echo esc_attr($post->ID); ?>">
121 <span class="dashicons dashicons-undo"></span>
122 <?php esc_html_e('Restore Original', 'king-addons'); ?>
123 </button>
124 </div>
125
126 <?php else: ?>
127 <div class="king-img-opt-status pending">
128 <span class="dashicons dashicons-clock"></span>
129 <?php esc_html_e('Not Optimized', 'king-addons'); ?>
130 </div>
131
132 <div class="king-img-opt-info">
133 <p><strong><?php esc_html_e('File size:', 'king-addons'); ?></strong> <?php echo esc_html(Image_Optimizer::format_bytes($file_size)); ?></p>
134 </div>
135
136 <div class="king-img-opt-actions">
137 <button type="button" class="button button-primary king-img-optimize" data-id="<?php echo esc_attr($post->ID); ?>">
138 <span class="dashicons dashicons-performance"></span>
139 <?php esc_html_e('Optimize Now', 'king-addons'); ?>
140 </button>
141 </div>
142 <?php endif; ?>
143
144 <div class="king-img-opt-spinner" style="display: none;">
145 <span class="spinner is-active"></span>
146 <span class="message"><?php esc_html_e('Processing...', 'king-addons'); ?></span>
147 </div>
148 </div>
149
150 <style>
151 .king-img-opt-metabox {
152 padding: 12px 0;
153 }
154
155 .king-img-opt-status {
156 display: flex;
157 align-items: center;
158 gap: 8px;
159 padding: 8px 12px;
160 border-radius: 8px;
161 font-weight: 500;
162 margin-bottom: 12px;
163 }
164
165 .king-img-opt-status.optimized {
166 background: rgba(52, 199, 89, 0.1);
167 color: #34c759;
168 }
169
170 .king-img-opt-status.pending {
171 background: rgba(255, 149, 0, 0.1);
172 color: #ff9500;
173 }
174
175 .king-img-opt-stats {
176 background: #f5f5f7;
177 border-radius: 8px;
178 padding: 12px;
179 margin-bottom: 12px;
180 }
181
182 .king-img-opt-stat-row {
183 display: flex;
184 justify-content: space-between;
185 padding: 4px 0;
186 font-size: 13px;
187 }
188
189 .king-img-opt-stat-row.highlight {
190 color: #34c759;
191 font-weight: 600;
192 }
193
194 .king-img-opt-sizes {
195 margin-bottom: 12px;
196 }
197
198 .king-img-opt-sizes summary {
199 cursor: pointer;
200 color: #0071e3;
201 font-size: 13px;
202 }
203
204 .king-img-opt-sizes ul {
205 margin: 8px 0 0 16px;
206 font-size: 12px;
207 }
208
209 .king-img-opt-actions {
210 display: flex;
211 flex-wrap: wrap;
212 gap: 8px;
213 }
214
215 .king-img-opt-actions .button {
216 display: inline-flex;
217 align-items: center;
218 gap: 4px;
219 }
220
221 .king-img-opt-actions .dashicons {
222 font-size: 16px;
223 width: 16px;
224 height: 16px;
225 }
226
227 .king-img-opt-spinner {
228 display: flex;
229 align-items: center;
230 gap: 8px;
231 margin-top: 12px;
232 padding: 8px;
233 background: #f5f5f7;
234 border-radius: 8px;
235 }
236
237 .king-img-opt-spinner .spinner {
238 float: none;
239 margin: 0;
240 }
241
242 .king-img-opt-divider {
243 border: 0;
244 border-top: 1px solid rgba(0,0,0,0.06);
245 margin: 10px 0;
246 }
247
248 body.ka-v3-dark .king-img-opt-divider {
249 border-top-color: rgba(255,255,255,0.12);
250 }
251
252 /* Dark mode */
253 body.ka-v3-dark .king-img-opt-stats {
254 background: #1c1c1e;
255 color: #f5f5f7;
256 }
257 </style>
258
259 <script>
260 jQuery(document).ready(function($) {
261 var attachmentId = <?php echo (int) $post->ID; ?>;
262 var $metabox = $('.king-img-opt-metabox');
263 var $spinner = $metabox.find('.king-img-opt-spinner');
264
265 // Optimize button
266 $metabox.on('click', '.king-img-optimize', function() {
267 var $btn = $(this);
268 $btn.prop('disabled', true);
269 $spinner.show();
270 $spinner.find('.message').text('<?php echo esc_js(__('Optimizing...', 'king-addons')); ?>');
271
272 optimizeSingleImage(attachmentId).then(function(result) {
273 if (result.success) {
274 location.reload();
275 } else {
276 alert(result.error || '<?php echo esc_js(__('Optimization failed', 'king-addons')); ?>');
277 $btn.prop('disabled', false);
278 $spinner.hide();
279 }
280 }).catch(function(err) {
281 alert(err.message || '<?php echo esc_js(__('Optimization failed', 'king-addons')); ?>');
282 $btn.prop('disabled', false);
283 $spinner.hide();
284 });
285 });
286
287 // Re-optimize button
288 $metabox.on('click', '.king-img-reoptimize', function() {
289 var $btn = $(this);
290 $btn.prop('disabled', true);
291 $spinner.show();
292 $spinner.find('.message').text('<?php echo esc_js(__('Re-optimizing...', 'king-addons')); ?>');
293
294 optimizeSingleImage(attachmentId).then(function(result) {
295 if (result.success) {
296 location.reload();
297 } else {
298 alert(result.error || '<?php echo esc_js(__('Optimization failed', 'king-addons')); ?>');
299 $btn.prop('disabled', false);
300 $spinner.hide();
301 }
302 }).catch(function(err) {
303 alert(err.message || '<?php echo esc_js(__('Optimization failed', 'king-addons')); ?>');
304 $btn.prop('disabled', false);
305 $spinner.hide();
306 });
307 });
308
309 // Restore button
310 $metabox.on('click', '.king-img-restore', function() {
311 if (!confirm('<?php echo esc_js(__('Are you sure you want to restore the original image? This will delete all WebP files.', 'king-addons')); ?>')) {
312 return;
313 }
314
315 var $btn = $(this);
316 $btn.prop('disabled', true);
317 $spinner.show();
318 $spinner.find('.message').text('<?php echo esc_js(__('Restoring...', 'king-addons')); ?>');
319
320 $.ajax({
321 url: ajaxurl,
322 type: 'POST',
323 data: {
324 action: 'king_img_full_restore',
325 nonce: '<?php echo wp_create_nonce('king_img_optimizer_nonce'); ?>',
326 attachment_id: attachmentId
327 },
328 success: function(response) {
329 if (response.success) {
330 location.reload();
331 } else {
332 alert(response.data.message || '<?php echo esc_js(__('Restore failed', 'king-addons')); ?>');
333 $btn.prop('disabled', false);
334 $spinner.hide();
335 }
336 },
337 error: function() {
338 alert('<?php echo esc_js(__('Restore failed', 'king-addons')); ?>');
339 $btn.prop('disabled', false);
340 $spinner.hide();
341 }
342 });
343 });
344
345 // Optimize single image function
346 async function optimizeSingleImage(id) {
347 // Get image data
348 var imageData = await $.ajax({
349 url: ajaxurl,
350 type: 'POST',
351 data: {
352 action: 'king_img_get_image_data',
353 nonce: '<?php echo wp_create_nonce('king_img_optimizer_nonce'); ?>',
354 attachment_id: id,
355 sizes: 'all'
356 }
357 });
358
359 if (!imageData.success) {
360 throw new Error(imageData.data.message || 'Failed to get image data');
361 }
362
363 var results = [];
364 var totalSaved = 0;
365
366 // Process each size
367 for (var sizeName in imageData.data.images) {
368 var sizeData = imageData.data.images[sizeName];
369
370 try {
371 // Create image element
372 var img = new Image();
373 img.crossOrigin = 'anonymous';
374
375 var loaded = await new Promise(function(resolve, reject) {
376 img.onload = function() { resolve(true); };
377 img.onerror = function() { reject(new Error('Failed to load')); };
378 img.src = sizeData.url;
379 });
380
381 // Create canvas
382 var canvas = document.createElement('canvas');
383 canvas.width = img.width;
384 canvas.height = img.height;
385 var ctx = canvas.getContext('2d');
386 ctx.drawImage(img, 0, 0);
387
388 // Convert to WebP
389 var quality = <?php echo (int) ($settings['quality'] ?? 82); ?> / 100;
390 var dataUrl = canvas.toDataURL('image/webp', quality);
391 var base64Data = dataUrl.split(',')[1];
392 var optimizedSize = Math.round(base64Data.length * 0.75);
393 var savedBytes = Math.max(0, sizeData.filesize - optimizedSize);
394
395 // Save to server
396 var saveResult = await $.ajax({
397 url: ajaxurl,
398 type: 'POST',
399 data: {
400 action: 'king_img_save_optimized',
401 nonce: '<?php echo wp_create_nonce('king_img_optimizer_nonce'); ?>',
402 attachment_id: id,
403 size: sizeName,
404 format: 'webp',
405 image_data: dataUrl,
406 original_size: sizeData.filesize,
407 optimized_size: optimizedSize,
408 method: 'canvas'
409 }
410 });
411
412 if (saveResult.success) {
413 results.push({ size: sizeName, saved: savedBytes });
414 totalSaved += savedBytes;
415 }
416
417 } catch (e) {
418 // console.error('Error optimizing size ' + sizeName, e);
419 }
420 }
421
422 // Apply WebP URLs
423 <?php if (!empty($settings['auto_replace_urls'])): ?>
424 await $.ajax({
425 url: ajaxurl,
426 type: 'POST',
427 data: {
428 action: 'king_img_apply_webp_urls',
429 nonce: '<?php echo wp_create_nonce('king_img_optimizer_nonce'); ?>',
430 attachment_id: id
431 }
432 });
433 <?php endif; ?>
434
435 return { success: true, results: results, totalSaved: totalSaved };
436 }
437 });
438 </script>
439