PluginProbe
Squeeze – Image Optimization & Compression, WEBP Conversion / 1.3
Squeeze – Image Optimization & Compression, WEBP Conversion v1.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.3, at assets/js/script.js

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