PluginProbe
Squeeze – Image Optimization & Compression, WEBP Conversion / 1.4.3
Squeeze – Image Optimization & Compression, WEBP Conversion v1.4.3
1.7.15 1.7.14 1.7.13 1.7.12 1.7.11 1.7.10 trunk 1.0 1.1 1.2 1.3 1.4 1.4.1 1.4.2 1.4.3 1.4.4 1.4.5 1.4.6 1.4.7 1.4.8 1.4.9 1.5 1.5.1 1.5.2 1.6 All 42 releases
squeeze / assets / js / script.js

script.js in Squeeze – Image Optimization & Compression, WEBP Conversion 1.4.3, at assets/js/script.js

544 lines 17.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 import * as avif from '@jsquash/avif';
2 import * as webp from '@jsquash/webp';
3 import * as jpeg from '@jsquash/jpeg';
4 import * as png from '@jsquash/png';
5 import optimise from '@jsquash/oxipng/optimise';
6 const { __ } = wp.i18n; // Import __() from wp.i18n
7
8 (function () {
9
10 const bulkBtn = document.querySelector("input[name='squeeze_bulk']")
11 const bulkAgainBtn = document.querySelector("input[name='squeeze_bulk_again']")
12 const bulkPathBtn = document.querySelector("input[name='squeeze_bulk_path_button']")
13 const squeeze_bulk_ids = document.querySelector("input[name='squeeze_bulk_ids']")?.value ?? null;
14 const squeeze_bulk_all_ids = document.querySelector("input[name='squeeze_bulk_all_ids']")?.value ?? null;
15 const uncompressedIDs = squeeze_bulk_ids ? squeeze_bulk_ids.split(",") : [];
16 const allIDs = squeeze_bulk_all_ids ? squeeze_bulk_all_ids.split(",") : [];
17 let bulkPathData = [];
18
19 async function decode(sourceType, fileBuffer) {
20 switch (sourceType) {
21 case 'avif':
22 return await avif.decode(fileBuffer);
23 case 'jpeg':
24 return await jpeg.decode(fileBuffer);
25 case 'png':
26 return await png.decode(fileBuffer);
27 case 'webp':
28 return await webp.decode(fileBuffer);
29 default:
30 throw new Error(`Unknown source type: ${sourceType}`);
31 }
32 }
33
34 async function encode(outputType, imageData) {
35 const options = JSON.parse(squeeze.options);
36
37 try {
38 switch (outputType) {
39 case 'avif':
40 const avifOptions = {}
41 for (const [key, value] of Object.entries(options)) {
42 if (key.includes('avif')) {
43 const keyName = key.replace('avif_', '')
44 avifOptions[keyName] = value
45 }
46 }
47 return await avif.encode(imageData, avifOptions);
48 case 'jpeg':
49 const jpegOptions = {}
50 for (const [key, value] of Object.entries(options)) {
51 if (key.includes('jpeg')) {
52 const keyName = key.replace('jpeg_', '')
53 jpegOptions[keyName] = value
54 }
55 }
56 return await jpeg.encode(imageData, jpegOptions);
57 case 'png':
58 const pngOptions = {}
59 for (const [key, value] of Object.entries(options)) {
60 if (key.includes('png')) {
61 const keyName = key.replace('png_', '')
62 pngOptions[keyName] = value
63 }
64 }
65 return await png.encode(imageData, pngOptions);
66 case 'webp':
67 const webpOptions = {}
68 for (const [key, value] of Object.entries(options)) {
69 if (key.includes('webp')) {
70 const keyName = key.replace('webp_', '')
71 webpOptions[keyName] = value
72 }
73 }
74 return await webp.encode(imageData, webpOptions);
75 default:
76 throw new Error(`Unknown output type: ${outputType}`);
77 }
78 } catch (error) {
79 console.error(error)
80 return false;
81 }
82
83 }
84
85 async function convert(sourceType, outputType, fileBuffer) {
86 const imageData = await decode(sourceType, fileBuffer);
87 return encode(outputType, imageData);
88 }
89
90 function blobToBase64(blob) {
91 return new Promise((resolve, _) => {
92 const reader = new FileReader();
93 reader.onloadend = () => resolve(reader.result);
94 reader.readAsDataURL(blob);
95 });
96 }
97
98 async function showOutput(imageBuffer, outputType) {
99 if (!imageBuffer) {
100 return false;
101 }
102 const imageBlob = new Blob([imageBuffer], { type: `image/${outputType}` });
103 const base64String = await blobToBase64(imageBlob);
104
105 return base64String;
106 }
107
108 const compressJPEG = async ({url, name, sourceType, outputType, mime}) => {
109 let response = await fetch(url);
110 let blob = await response.blob();
111 let metadata = {
112 type: mime
113 };
114 let imageObj = new File([blob], name, metadata);
115 const fileBuffer = await imageObj.arrayBuffer();
116 const imageBuffer = await convert(sourceType, outputType, fileBuffer);
117 const base64 = await showOutput(imageBuffer, outputType);
118 return base64
119 }
120
121 const compressPNG = async ({url, options, outputType}) => {
122 const pngOptions = {}
123 for (const [key, value] of Object.entries(options)) {
124 if (key.includes('png')) {
125 const keyName = key.replace('png_', '')
126 pngOptions[keyName] = value
127 }
128 }
129 const imageBuffer = await fetch(url).then(res => res.arrayBuffer()).then(pngImageBuffer => optimise(pngImageBuffer, pngOptions));
130 const base64 = await showOutput(imageBuffer, outputType);
131 return base64
132 }
133
134 const compressWEBP = async ({url, name, sourceType, outputType, mime}) => {
135 let webpResponse = await fetch(url);
136 let webpBlob = await webpResponse.blob();
137 let webpMetadata = {
138 type: mime
139 };
140 let webpImageObj = new File([webpBlob], name, webpMetadata);
141 const fileBuffer = await webpImageObj.arrayBuffer();
142 const imageBuffer = await convert(sourceType, outputType, fileBuffer);
143 const base64 = await showOutput(imageBuffer, outputType);
144 return base64
145 }
146
147 const compressAVIF = async ({url, name, sourceType, outputType, mime}) => {
148 let avifResponse = await fetch(url);
149 let avifBlob = await avifResponse.blob();
150 let avifMetadata = {
151 type: mime
152 };
153 let avifImageObj = new File([avifBlob], name, avifMetadata);
154 const fileBuffer = await avifImageObj.arrayBuffer();
155 const imageBuffer = await convert(sourceType, outputType, fileBuffer);
156 const base64 = await showOutput(imageBuffer, outputType);
157 return base64
158 }
159
160 async function handleUpload({ attachment, isBulk = false, target = null, type = 'uncompressed' }) {
161 const attachmentData = attachment.attributes;
162 const url = attachmentData?.originalImageURL ?? attachmentData.url;
163 const mime = attachmentData.mime;
164 const name = attachmentData.name;
165 const filename = attachmentData?.originalImageName ?? attachmentData.filename;
166 const attachmentID = attachmentData.id;
167 const format = mime.split("/")[1];
168 const sourceType = format;
169 const outputType = format;
170 const options = JSON.parse(squeeze.options);
171
172 let base64;
173
174 switch (format) {
175 case 'avif':
176 base64 = await compressAVIF({url, name, sourceType, outputType, mime});
177 break;
178 case 'jpeg':
179 base64 = await compressJPEG({url, name, sourceType, outputType, mime});
180 break;
181 case 'png':
182 base64 = await compressPNG({url, options, outputType});
183 break;
184 case 'webp':
185 base64 = await compressWEBP({url, name, sourceType, outputType, mime});
186 break;
187 }
188
189 if (!base64) {
190
191 if (isBulk) {
192 logMsg(__('An error has occured. Check the console for details.', 'squeeze'))
193 logMsg(`===============================\r\n`)
194 handleBulkUpload(type)
195 } else {
196 if (target) {
197 target.closest("td").querySelector(".squeeze_status").innerText = __('An error has occured. Check the console for details.', 'squeeze')
198 target.remove();
199 }
200 }
201
202 return;
203 }
204
205 let data = {
206 action: 'squeeze_update_attachment',
207 _ajax_nonce: squeeze.nonce,
208 filename: filename,
209 type: 'image',
210 format: format,
211 base64: base64,
212 attachmentID: attachmentID,
213 url: url,
214 process: type,
215 }
216
217 jQuery.ajax({
218 url: squeeze.ajaxUrl,
219 type: 'POST',
220 data: data,
221 beforeSend: function () {
222 console.log(data, 'squeeze data')
223 if (isBulk) {
224 logMsg(`#${attachmentID}: ` + __('Compressed successfully, updating...', 'squeeze'))
225 }
226 },
227 error: function (error) {
228 console.error(error)
229 if (target) {
230 target.closest("td").querySelector(".squeeze_status").innerText = __('An error has occured. Check the console for details.', 'squeeze')
231 target.remove();
232 }
233 },
234 success: function (response) {
235 if (isBulk) {
236 if (response.success) {
237 logMsg(`#${attachmentID}: ` + __('Updated successfully', 'squeeze') + `\r\n===============================\r\n`);
238 } else {
239 logMsg(`#${attachmentID}: ` + response.data + `\r\n===============================\r\n`);
240 }
241 handleBulkUpload(type) // continue bulk process
242 }
243 if (!isBulk && target) { // on single attachment compress
244 target.closest("td").querySelector(".squeeze_status").innerText = response.data;
245 target.remove();
246 }
247 if (!target && !isBulk) { // on upload process
248 attachment.set('uploading', false) // resume uploading process
249 }
250 }
251 });
252
253 }
254
255 const handleBulkUpload = (type = 'uncompressed') => {
256 let currentID;
257 switch (type) {
258 case 'uncompressed':
259 currentID = uncompressedIDs[0];
260 break;
261 case 'all':
262 currentID = allIDs[0];
263 break;
264 case 'path':
265 currentID = bulkPathData[0]?.filename;
266 break;
267 default:
268 currentID = 0;
269 break;
270 }
271 const data = {
272 action: 'squeeze_get_attachment',
273 _ajax_nonce: squeeze.nonce,
274 attachmentID: currentID,
275 }
276
277 if (type === 'uncompressed') {
278 if (uncompressedIDs.length === 0) {
279 alert(__('All images have been compressed!', 'squeeze'))
280 restoreBulkButtons()
281 location.reload();
282 return;
283 }
284 } else if (type === 'all') {
285 if (allIDs.length === 0) {
286 alert(__('All images have been re-compressed again!', 'squeeze'))
287 restoreBulkButtons()
288 //location.reload();
289 return;
290 }
291 } else if (type === 'path') {
292 if (bulkPathData.length === 0) {
293 alert(__('All images have been compressed!', 'squeeze'))
294 restoreBulkButtons()
295 //location.reload();
296 return;
297 }
298 }
299
300 logMsg(`attachment #${currentID}: start compressing...`)
301
302 if (type === 'path') {
303
304 const attachment = {
305 attributes: {
306 url: bulkPathData[0].url,
307 mime: bulkPathData[0].mime,
308 name: bulkPathData[0].name,
309 filename: bulkPathData[0].filename,
310 id: bulkPathData[0].id,
311 }
312 }
313 bulkPathData.shift();
314 handleUpload({ attachment, isBulk: true, type: type })
315
316 } else {
317
318 jQuery.ajax({
319 url: squeeze.ajaxUrl,
320 type: 'POST',
321 data: data,
322 error: function (error) {
323 console.error(error)
324 },
325 success: function (response) {
326 if (response.success) {
327 const responseData = response.data;
328 const attachment = {
329 attributes: {
330 url: responseData.url,
331 mime: responseData.mime,
332 name: responseData.name,
333 filename: responseData.filename,
334 id: responseData.id,
335 }
336 }
337
338 if (type === 'uncompressed') {
339 uncompressedIDs.shift();
340 } else if (type === 'all') {
341 allIDs.shift();
342 }
343 handleUpload({ attachment, isBulk: true, type: type })
344 } else {
345 console.error(response.data)
346 }
347 }
348 });
349
350 }
351 }
352
353 function handleRestore(attachmentID, target) {
354 let data = {
355 action: 'squeeze_restore_attachment',
356 _ajax_nonce: squeeze.nonce,
357 attachmentID: attachmentID,
358 }
359
360 jQuery.ajax({
361 url: squeeze.ajaxUrl,
362 type: 'POST',
363 data: data,
364 beforeSend: function () {
365 target.disabled = true;
366 target.innerText = __('Restore in process...', 'squeeze')
367 },
368 error: function (error) {
369 console.error(error)
370 target.closest("td").querySelector(".squeeze_status").innerText = __('An error has occured. Check the console for details.', 'squeeze')
371 target.remove();
372 },
373 success: function (response) {
374 target.closest("td").querySelector(".squeeze_status").innerText = response.data; //__('Restored successfully', 'squeeze')
375 target.remove();
376 }
377 });
378 }
379
380 // Handle single compress button click
381 const handleSingleBtnClick = (event) => {
382 const attachmentID = event.target.dataset.attachment;
383
384 wp?.media?.attachment(attachmentID).fetch().then(function (data) {
385 const attachment = {
386 attributes: data
387 }
388 handleUpload({ attachment, target: event.target })
389 });
390 }
391
392 // Handle restore button click
393 const handleRestoreBtnClick = (event) => {
394 const attachmentID = event.target.dataset.attachment;
395 handleRestore(attachmentID, event.target)
396 }
397
398 // Handle bulk path button click
399 const handlePathUpload = (path) => {
400
401 const data = {
402 action: 'squeeze_get_attachment_by_path',
403 path: path,
404 _ajax_nonce: squeeze.nonce,
405 }
406
407 jQuery.ajax({
408 url: squeeze.ajaxUrl,
409 type: 'POST',
410 data: data,
411 error: function (error) {
412 console.error(error)
413 },
414 success: function (response) {
415 if (response.success) {
416 const responseData = response.data;
417 bulkPathData = responseData;
418 handleBulkUpload('path')
419 } else {
420 console.error(response.data)
421 logMsg(response.data)
422 restoreBulkButtons()
423 }
424 }
425 });
426 }
427
428 /**
429 * Handle single buttons click
430 */
431 function handleSingleButtonsClick() {
432 document.addEventListener("click", (e) => {
433 //console.log(e.target, 'e.target')
434 const singleBtnName = 'squeeze_compress_single';
435 const compressAgainBtnName = 'squeeze_compress_again';
436 const restoreBtnName = 'squeeze_restore';
437 if (e.target.getAttribute("name") === singleBtnName || e.target.getAttribute("name") === compressAgainBtnName) {
438 e.target.disabled = true;
439
440 if (e.target.getAttribute("name") === compressAgainBtnName) {
441 e.target.closest('.field').querySelector(`[name='${restoreBtnName}']`).disabled = true;
442 }
443
444 e.target.innerText = __('Compressing...', 'squeeze')
445 handleSingleBtnClick(e)
446 }
447 if (e.target.getAttribute("name") === restoreBtnName) {
448 e.target.disabled = true;
449 e.target.closest('td').querySelector(`[name='${compressAgainBtnName}']`).disabled = true;
450 handleRestoreBtnClick(e)
451 }
452 })
453 }
454
455 handleSingleButtonsClick()
456
457 function logMsg(msg) {
458 const bulkLogInput = document.querySelector("[name='squeeze_bulk_log']")
459 bulkLogInput.value += msg + `\r\n`;
460 }
461
462 function restoreBulkButtons() {
463 bulkBtn.disabled = false;
464 bulkAgainBtn.disabled = false;
465 bulkPathBtn.disabled = false;
466 window.onbeforeunload = null;
467 }
468
469 /**
470 * Handle bulk button click
471 */
472 bulkBtn?.addEventListener("click", (event) => {
473 if (uncompressedIDs.length === 0) {
474 return;
475 }
476
477 bulkBtn.disabled = true;
478 bulkAgainBtn.disabled = true;
479 bulkPathBtn.disabled = true;
480 handleBulkUpload('uncompressed')
481 window.onbeforeunload = handleOnLeave;
482 })
483
484 /**
485 * Handle bulk again button click
486 */
487 bulkAgainBtn?.addEventListener("click", (event) => {
488 bulkBtn.disabled = true;
489 bulkAgainBtn.disabled = true;
490 bulkPathBtn.disabled = true;
491 handleBulkUpload('all')
492 window.onbeforeunload = handleOnLeave;
493 })
494
495 /**
496 * Handle bulk path button click
497 */
498 bulkPathBtn?.addEventListener("click", (event) => {
499 const path = document.querySelector("input[name='squeeze_bulk_path']").value;
500
501 if (!path) {
502 alert(__('Please enter a valid path!', 'squeeze'))
503 return;
504 }
505
506 bulkBtn.disabled = true;
507 bulkAgainBtn.disabled = true;
508 bulkPathBtn.disabled = true;
509 handlePathUpload(path)
510 window.onbeforeunload = handleOnLeave;
511 })
512
513 // https://wordpress.stackexchange.com/a/131295/186146 - override wp.Uploader.prototype.success
514 jQuery.extend(wp?.Uploader?.prototype, {
515 success: function (attachment) {
516 //console.log(attachment, 'success');
517 const options = JSON.parse(squeeze.options);
518 const isAutoCompress = options.auto_compress;
519 const allowedMimeTypes = ['jpeg', 'png', 'webp'];
520 let isImage = attachment.attributes.type === 'image' && allowedMimeTypes.includes(attachment.attributes.subtype)
521
522 if (isImage && isAutoCompress) {
523 // set 'uploading' param to true, to pause the uploading process
524 attachment.set('uploading', true)
525 handleUpload({ attachment })
526 }
527 },
528 });
529
530 /**
531 * Hadnle warning on page leave
532 */
533 function handleOnLeave() {
534 const urlParams = new URLSearchParams(window.location.search);
535 const page = urlParams.get('page');
536
537 if (page === 'squeeze-bulk') {
538 return __('Are you sure you want to leave this page? The compression process will be terminated!', 'squeeze');
539 }
540 };
541
542 })();
543
544 //console.log(JSON.parse(squeeze.options), 'squeeze.options')