PluginProbe
Imagify Image Optimization: Optimize Images | Compress & Convert to WebP/AVIF / 2.2.9
Imagify Image Optimization: Optimize Images | Compress & Convert to WebP/AVIF v2.2.9
2.3.4 2.3.3 2.3.2 2.3.1 2.3.0 2.2.9 2.2.8 trunk 1.10 1.3.3 1.3.4 1.3.5 1.3.5.1 1.3.5.2 1.3.6 1.3.6.1 1.4 1.4.1 1.4.2 1.4.3 1.4.4 1.4.5 1.4.6 1.4.7 1.5 All 103 releases
imagify / assets / js / options.js

options.js in Imagify Image Optimization: Optimize Images | Compress & Convert to WebP/AVIF 2.2.9, at assets/js/options.js

1,039 lines 30.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 window.imagify = window.imagify || {};
2
3 (function($, d, w, undefined) { // eslint-disable-line no-unused-vars, no-shadow, no-shadow-restricted-names
4 /*
5 * Process an API key check validity.
6 */
7 var busy = false,
8 xhr = false;
9
10 $( '#imagify-settings #api_key' ).on( 'blur.imagify', function() {
11 var obj = $( this ),
12 value = obj.val();
13
14 if ( String( value ).trim() === '' ) {
15 return false;
16 }
17
18 if ( $( '#check_api_key' ).val() === value ) {
19 $( '#imagify-check-api-container' ).html( '<span class="dashicons dashicons-yes"></span> ' + imagifyOptions.labels.ValidApiKeyText );
20 return false;
21 }
22
23 if ( true === busy ) {
24 xhr.abort();
25 } else {
26 $( '#imagify-check-api-container' ).remove();
27 obj.after( '<span id="imagify-check-api-container"><span class="imagify-spinner"></span>' + imagifyOptions.labels.waitApiKeyCheckText + '</span>' );
28 }
29
30 busy = true;
31
32 xhr = $.get( ajaxurl + w.imagify.concat + 'action=imagify_check_api_key_validity&api_key=' + obj.val() + '&imagifycheckapikeynonce=' + $( '#imagifycheckapikeynonce' ).val() )
33 .done( function( response ) {
34 if ( ! response.success ) {
35 $( '#imagify-check-api-container' ).html( '<span class="dashicons dashicons-no"></span> ' + response.data );
36 } else {
37 // Success, the API key is valid.
38 $( '#imagify-check-api-container' ).remove();
39 swal( {
40 title: imagifyOptions.labels.ApiKeyCheckSuccessTitle,
41 html: imagifyOptions.labels.ApiKeyCheckSuccessText,
42 type: 'success',
43 padding: 0,
44 customClass: 'imagify-sweet-alert'
45 } ).then( function() {
46 location.reload();
47 } );
48 }
49
50 busy = false;
51 } );
52 } );
53
54 /**
55 * Check the boxes by clicking "labels" (aria-describedby items).
56 */
57 $( '.imagify-options-line' ).css( 'cursor', 'pointer' ).on( 'click.imagify', function( e ) {
58 if ( 'INPUT' === e.target.nodeName ) {
59 return;
60 }
61 $( 'input[aria-describedby="' + $( this ).attr( 'id' ) + '"]' ).trigger( 'click.imagify' );
62 } );
63
64 $( '.imagify-settings th span' ).on( 'click.imagify', function() {
65 var $input = $( this ).parent().next( 'td' ).find( ':checkbox' );
66
67 if ( 1 === $input.length ) {
68 $input.trigger( 'click.imagify' );
69 }
70 } );
71
72 /**
73 * Auto check on options-line input value change.
74 */
75 $( '.imagify-options-line' ).find( 'input' ).on( 'change.imagify focus.imagify', function() {
76 var $checkbox;
77
78 if ( 'checkbox' === this.type && ! this.checked ) {
79 return;
80 }
81
82 $checkbox = $( this ).closest( '.imagify-options-line' ).prev( 'label' ).prev( ':checkbox' );
83
84 if ( $checkbox.length && ! $checkbox[0].checked ) {
85 $checkbox.prop( 'checked', true );
86 }
87 } );
88
89 /**
90 * Imagify Backup alert.
91 */
92 $( '[name="imagify_settings[backup]"]' ).on( 'change.imagify', function() {
93 var $_this = $( this ),
94 $backupMessage = $_this.siblings( '#backup-dir-is-writable' ),
95 params = {
96 'action': 'imagify_check_backup_dir_is_writable',
97 '_wpnonce': $backupMessage.data( 'nonce' )
98 };
99
100 if ( $_this.is( ':checked' ) ) {
101 $.getJSON( ajaxurl, params )
102 .done( function( r ) {
103 if ( $.isPlainObject( r ) && r.success ) {
104 if ( r.data.is_writable ) {
105 // Hide the error message.
106 $backupMessage.addClass( 'hidden' );
107 } else {
108 // Show the error message.
109 $backupMessage.removeClass( 'hidden' );
110 }
111 }
112 } );
113 return;
114 }
115
116 // Are you sure? No backup?
117 swal( {
118 title: imagifyOptions.labels.noBackupTitle,
119 html: imagifyOptions.labels.noBackupText,
120 type: 'warning',
121 customClass: 'imagify-sweet-alert',
122 padding: 0,
123 showCancelButton: true,
124 cancelButtonText: imagifySwal.labels.cancelButtonText,
125 reverseButtons: true
126 } ).then(
127 function() {
128 // Leave it unchecked, hide the error message.
129 $backupMessage.addClass( 'hidden' );
130 },
131 function() {
132 // Re-check.
133 $_this.prop( 'checked', true );
134 }
135 );
136 } );
137
138 /**
139 * Fade CDN URL field.
140 */
141 $( '[name="imagify_settings[display_nextgen_method]"]' ).on( 'change.imagify init.imagify', function( e ) {
142 if ( 'picture' === e.target.value ) {
143 $( e.target ).closest( '.imagify-radio-group' ).next( '.imagify-options-line' ).removeClass( 'imagify-faded' );
144 } else {
145 $( e.target ).closest( '.imagify-radio-group' ).next( '.imagify-options-line' ).addClass( 'imagify-faded' );
146 }
147 } ).filter( ':checked' ).trigger( 'init.imagify' );
148
149 /**
150 * Reset Internal State button.
151 */
152 $( '#imagify-reset-internal-state' ).on( 'click.imagify', function() {
153 var $button = $( this ),
154 nonce = $button.data( 'nonce' ),
155 $feedback = $( '#imagify-reset-internal-state-feedback' );
156
157 swal( {
158 title: imagifyOptions.resetInternalState.confirm,
159 type: 'warning',
160 customClass: 'imagify-sweet-alert',
161 padding: 0,
162 showCancelButton: true,
163 cancelButtonText: imagifySwal.labels.cancelButtonText,
164 reverseButtons: true
165 } ).then(
166 function() {
167 $button.prop( 'disabled', true );
168 $feedback.text( '' ).removeClass( 'imagify-success imagify-error' );
169
170 $.post( ajaxurl, {
171 'action': imagifyOptions.resetInternalState.action,
172 '_wpnonce': nonce
173 } )
174 .done( function( response ) {
175 if ( response && response.success ) {
176 $feedback.text( imagifyOptions.resetInternalState.success ).addClass( 'imagify-success' );
177 } else {
178 $feedback.text( imagifyOptions.resetInternalState.error ).addClass( 'imagify-error' );
179 }
180 } )
181 .fail( function() {
182 $feedback.text( imagifyOptions.resetInternalState.error ).addClass( 'imagify-error' );
183 } )
184 .always( function() {
185 $button.prop( 'disabled', false );
186 } );
187 },
188 function() {
189 // User cancelled — do nothing.
190 }
191 );
192 } );
193
194 } )(jQuery, document, window);
195
196
197 // Display Imagify User data =======================================================================
198 (function(w, d, $, undefined) { // eslint-disable-line no-unused-vars, no-shadow, no-shadow-restricted-names
199
200 if ( ! w.imagifyUser ) {
201 return;
202 }
203
204 $.getJSON( ajaxurl, w.imagifyUser )
205 .done( function( r ) {
206 if ( $.isPlainObject( r ) && r.success ) {
207 r.data.id = null;
208 r.data.plan_id = null;
209 r.data.is = [];
210
211 $.each( r.data, function( k, v ) {
212 var htmlClass = '.imagify-user-' + k.replace( /_/g, '-' );
213
214 if ( k.indexOf( 'is_' ) === 0 ) {
215 if ( v ) {
216 r.data.is.push( htmlClass );
217 }
218 } else if ( 'is' !== k ) {
219 $( htmlClass ).text( v );
220 }
221 } );
222
223 r.data.is.push( 'best-plan' );
224
225 $( r.data.is.join( ',' ) ).removeClass( 'hidden' );
226 }
227 } );
228
229 } )(window, document, jQuery);
230
231
232 // Files tree for "custom folders" =================================================================
233 (function(w, d, $, undefined) { // eslint-disable-line no-unused-vars, no-shadow, no-shadow-restricted-names
234
235 if ( ! imagifyOptions.getFilesTree ) {
236 return;
237 }
238
239 function imagifyInsertFolderRow( value ) {
240 var added = false,
241 prevPath = null,
242 valueTest, template, $wrap, $rows, $field;
243
244 if ( ! value ) {
245 return;
246 }
247
248 $wrap = $( '#imagify-custom-folders-selected' );
249 $rows = $wrap.find( '.imagify-custom-folder-line' );
250 $field = $rows.find( '[value="' + value + '"]' );
251
252 if ( $field.length ) {
253 // Shouldn't happen.
254 return;
255 }
256
257 // Path #///# Label.
258 value = value.split( '#///#' );
259 valueTest = value[1].replace( /\/+$/,'' ).toLowerCase();
260 template = w.imagify.template( 'imagify-custom-folder' );
261
262 $rows.each( function() {
263 var $this = $( this ),
264 thisValueTest = $this.data( 'path' ).replace( /\/+$/,'' ).toLowerCase();
265
266 if ( '' !== thisValueTest && valueTest.indexOf( thisValueTest ) === 0 ) {
267 // We try to add a sub-folder of an already selected folder. It shouldn't happen though, since it can't be selected.
268 added = true;
269 return false;
270 } else if ( valueTest < thisValueTest ) {
271 $this.before( template( {
272 value: value[0],
273 label: value[1]
274 } ) );
275 $rows = $wrap.find( '.imagify-custom-folder-line' );
276 added = true;
277 return false;
278 }
279 } );
280
281 if ( ! added ) {
282 $wrap.append( template( {
283 value: value[0],
284 label: value[1]
285 } ) );
286 $rows = $wrap.find( '.imagify-custom-folder-line' );
287 }
288
289 // Remove sub-paths: if 'a/b/' and 'a/b/c/' are in the array, we keep only the "parent" 'a/b/'.
290 if ( '' !== valueTest ) {
291 $rows.each( function() {
292 var $this = $( this ),
293 thisPath = $this.data( 'path' ).toLowerCase();
294
295 if ( null !== prevPath && thisPath.indexOf( prevPath ) === 0 ) {
296 $this.find( '.imagify-custom-folders-remove' ).trigger( 'click.imagify' );
297 } else {
298 prevPath = thisPath;
299 }
300 } );
301 }
302
303 // Display a message.
304 $wrap.next( '.hidden' ).removeClass( 'hidden' );
305 }
306
307 // Clicking the main button: fetch site's root folders and files, then display them in a modal.
308 $( '#imagify-add-custom-folder' ).on( 'click.imagify', function() {
309 var $button = $( this ),
310 selected = [],
311 $folders;
312
313 if ( $button.prop('disabled') ) {
314 return;
315 }
316
317 $button.prop( 'disabled', true ).next( 'img' ).attr( 'aria-hidden', 'false' );
318
319 $folders = $( '#imagify-custom-folders-selected' );
320
321 $folders.find( 'input' ).each( function() {
322 selected.push( this.value );
323 } );
324
325 $.post( imagifyOptions.getFilesTree, {
326 folder: '/',
327 selected: selected
328 }, null, 'json' )
329 .done( function( response ) {
330 if ( ! response.success ) {
331 swal( {
332 title: imagifyOptions.labels.error,
333 html: response.data || '',
334 type: 'error',
335 padding: 0,
336 customClass: 'imagify-sweet-alert'
337 } );
338 return;
339 }
340
341 swal( {
342 title: imagifyOptions.labels.filesTreeTitle,
343 html: '<div class="imagify-swal-subtitle">' + imagifyOptions.labels.filesTreeSubTitle + '</div><div class="imagify-swal-content"><p class="imagify-folders-information"><i class="dashicons dashicons-info" aria-hidden="true"></i>' + imagifyOptions.labels.cleaningInfo + '</p><ul id="imagify-folders-tree" class="imagify-folders-tree">' + response.data + '</ul></div>',
344 type: '',
345 customClass: 'imagify-sweet-alert imagify-swal-has-subtitle imagify-folders-selection',
346 showCancelButton: true,
347 padding: 0,
348 confirmButtonText: imagifyOptions.labels.confirmFilesTreeBtn,
349 cancelButtonText: imagifySwal.labels.cancelButtonText,
350 reverseButtons: true
351 } ).then( function() {
352 var values = $( '#imagify-folders-tree input' ).serializeArray(); // Don't do `$( '#imagify-folders-tree' ).find( 'input' )`, it won't work.
353
354 if ( ! values.length ) {
355 return;
356 }
357
358 $.each( values, function( i, v ) {
359 imagifyInsertFolderRow( v.value );
360 } );
361 } ).catch( swal.noop );
362 } )
363 .fail( function() {
364 swal( {
365 title: imagifyOptions.labels.error,
366 type: 'error',
367 customClass: 'imagify-sweet-alert',
368 padding: 0
369 } );
370 } )
371 .always( function(){
372 $button.prop( 'disabled', false ).next( 'img' ).attr( 'aria-hidden', 'true' );
373 } );
374 } );
375
376 // Clicking a folder icon in the modal: fetch the folder's sub-folders and files, then display them.
377 $( d ).on( 'click.imagify', '#imagify-folders-tree [data-folder]', function() {
378 var $button = $( this ),
379 $tree = $button.nextAll( '.imagify-folders-sub-tree' ),
380 selected = [];
381
382 if ( $button.prop('disabled') || $button.siblings( ':checkbox' ).is( ':checked' ) ) {
383 return;
384 }
385
386 $button.prop( 'disabled', true ).addClass( 'imagify-loading' );
387
388 if ( $tree.length ) {
389 if ( $button.hasClass( 'imagify-is-open' ) ) {
390 $tree.addClass( 'hidden' );
391 $button.removeClass(' imagify-is-open' );
392 } else {
393 $tree.removeClass( 'hidden' );
394 $button.addClass( 'imagify-is-open' );
395 }
396 $button.prop( 'disabled', false ).removeClass( 'imagify-loading' );
397 return;
398 }
399
400 $( '#imagify-custom-folders-selected' ).find( 'input' ).each( function() {
401 selected.push( this.value );
402 } );
403
404 $.post( imagifyOptions.getFilesTree, {
405 folder: $button.data( 'folder' ),
406 selected: selected
407 }, null, 'json' )
408 .done( function( response ) {
409 if ( ! response.success ) {
410 swal( {
411 title: imagifyOptions.labels.error,
412 html: response.data || '',
413 type: 'error',
414 padding: 0,
415 customClass: 'imagify-sweet-alert'
416 } );
417 return;
418 }
419
420 $button.addClass( 'imagify-is-open' ).parent().append( '<ul class="imagify-folders-sub-tree">' + response.data + '</ul>' );
421 } )
422 .fail( function(){
423 swal( {
424 title: imagifyOptions.labels.error,
425 type: 'error',
426 padding: 0,
427 customClass: 'imagify-sweet-alert'
428 } );
429 } )
430 .always( function(){
431 $button.prop( 'disabled', false ).removeClass( 'imagify-loading' );
432 } );
433 } );
434
435 // Clicking a Remove folder button make it disappear.
436 $( '#imagify-custom-folders' ).on( 'click.imagify', '.imagify-custom-folders-remove', function() {
437 var $row = $( this ).closest( '.imagify-custom-folder-line' ).addClass( 'imagify-will-remove' );
438
439 w.setTimeout( function() {
440 $row.remove();
441 // Display a message.
442 $( '#imagify-custom-folders-selected' ).siblings( '.imagify-success.hidden' ).removeClass( 'hidden' );
443 }, 750 );
444 } );
445
446 // Clicking the "add themes to folders" button.
447 $( '#imagify-add-themes-to-custom-folder' ).on( 'click.imagify', function() {
448 var $this = $( this );
449
450 imagifyInsertFolderRow( $this.data( 'theme' ) );
451 imagifyInsertFolderRow( $this.data( 'theme-parent' ) );
452
453 // Remove clicked button.
454 $this.replaceWith( '<p>' + imagifyOptions.labels.themesAdded + '</p>' );
455 } );
456
457 } )(window, document, jQuery);
458
459
460 // Generate missing WebP versions ==================================================================
461 /* eslint-disable no-underscore-dangle, consistent-this */
462 (function(w, d, $, undefined) { // eslint-disable-line no-unused-vars, no-shadow, no-shadow-restricted-names
463
464 if ( ! imagifyOptions.bulk ) {
465 return;
466 }
467
468 w.imagify.optionsBulk = {
469 // Properties ==============================================================================
470 /**
471 * The current error ID or message.
472 *
473 * @var {string|bool} error False if no error.
474 */
475 error: false,
476 /**
477 * Set to true at the beginning of the process.
478 *
479 * @var {bool} working
480 */
481 working: false,
482 /**
483 * Set to true to stop the whole thing.
484 *
485 * @var {bool} processIsStopped
486 */
487 processIsStopped: false,
488 /**
489 * The button.
490 *
491 * @var {jQuery}
492 */
493 $button: null,
494 /**
495 * The progress bar wrapper.
496 *
497 * @var {jQuery}
498 */
499 $progressWrap: null,
500 /**
501 * The progress bar.
502 *
503 * @var {jQuery}
504 */
505 $progressBar: null,
506 /**
507 * The progress bar text (the %).
508 *
509 * @var {jQuery}
510 */
511 $progressText: null,
512
513 // Methods =================================================================================
514
515 /*
516 * Init.
517 */
518 init: function () {
519 var processed, progress;
520 this.$missingWebpElement = $('.generate-missing-webp');
521 this.$missingWebpMessage = $('.generate-missing-webp p');
522
523 this.$button = $( '#imagify-generate-webp-versions' );
524 this.$progressWrap = this.$button.siblings( '.imagify-progress' );
525 this.$progressBar = this.$progressWrap.find( '.bar' );
526 this.$progressText = this.$progressBar.find( '.percent' );
527
528 // Enable/Disable the button when the "Convert to WebP" checkbox is checked/unchecked.
529 $( '#imagify_convert_to_webp' )
530 .on( 'change.imagify init.imagify', { imagifyOptionsBulk: this }, this.toggleButton )
531 .trigger( 'init.imagify' );
532
533 // Launch optimization.
534 this.$button.on( 'click.imagify', { imagifyOptionsBulk: this }, this.maybeLaunchMissingWebpProcess );
535
536 // Imagifybeat for optimization queue.
537 $( d )
538 .on( 'imagifybeat-send', { imagifyOptionsBulk: this }, this.addQueueImagifybeat )
539 .on( 'imagifybeat-tick', { imagifyOptionsBulk: this }, this.processQueueImagifybeat )
540 // Imagifybeat for requirements.
541 .on( 'imagifybeat-send', this.addRequirementsImagifybeat )
542 .on( 'imagifybeat-tick', { imagifyOptionsBulk: this }, this.processRequirementsImagifybeat );
543
544 if ( false !== imagifyOptions.bulk.progress_next_gen.total && false !== imagifyOptions.bulk.progress_next_gen.remaining ) {
545 // Reset properties.
546 w.imagify.optionsBulk.error = false;
547 w.imagify.optionsBulk.working = true;
548 w.imagify.optionsBulk.processIsStopped = false;
549
550 // Disable the button.
551 this.$button.prop( 'disabled', true ).find( '.dashicons' ).addClass( 'rotate' );
552
553 // Fasten Imagifybeat: 1 tick every 15 seconds, and disable suspend.
554 w.imagify.beat.interval( 15 );
555 w.imagify.beat.disableSuspend();
556
557 this.$missingWebpMessage.hide().attr('aria-hidden', 'true');
558
559 processed = imagifyOptions.bulk.progress_next_gen.total - imagifyOptions.bulk.progress_next_gen.remaining;
560 progress = Math.floor( processed / imagifyOptions.bulk.progress_next_gen.total * 100 );
561 this.$progressBar.css( 'width', progress + '%' );
562 this.$progressText.text( processed + '/' + imagifyOptions.bulk.progress_next_gen.total );
563
564 this.$progressWrap.slideDown().attr( 'aria-hidden', 'false' ).removeClass( 'hidden' );
565 }
566 },
567
568 // Event callbacks =========================================================================
569
570 /**
571 * Enable/Disable the button when the "Convert to WebP" checkbox is checked/unchecked.
572 *
573 * @param {object} e Event object.
574 */
575 toggleButton: function ( e ) {
576 if ( ! this.checked ) {
577 e.data.imagifyOptionsBulk.$button.prop( 'disabled', true );
578 } else {
579 e.data.imagifyOptionsBulk.$button.prop( 'disabled', false );
580 }
581 },
582
583 /*
584 * Maybe launch the missing WebP generation process.
585 *
586 * @param {object} e Event object.
587 */
588 maybeLaunchMissingWebpProcess: function ( e ) {
589 if ( ! e.data.imagifyOptionsBulk || e.data.imagifyOptionsBulk.working ) {
590 return;
591 }
592
593 if ( e.data.imagifyOptionsBulk.hasBlockingError( true ) ) {
594 return;
595 }
596
597 // Reset properties.
598 e.data.imagifyOptionsBulk.error = false;
599 e.data.imagifyOptionsBulk.working = true;
600 e.data.imagifyOptionsBulk.processIsStopped = false;
601
602 // Disable the button.
603 e.data.imagifyOptionsBulk.$button.prop( 'disabled', true ).find( '.dashicons' ).addClass( 'rotate' );
604
605 // Fasten Imagifybeat: 1 tick every 15 seconds, and disable suspend.
606 w.imagify.beat.interval( 15 );
607 w.imagify.beat.disableSuspend();
608
609 // Launch missing WebP generation process
610 e.data.imagifyOptionsBulk.launchProcess();
611 },
612
613 // Imagifybeat =============================================================================
614
615 /**
616 * Add a Imagifybeat ID on "imagifybeat-send" event to sync the optimization queue.
617 *
618 * @param {object} e Event object.
619 * @param {object} data Object containing all Imagifybeat IDs.
620 */
621 addQueueImagifybeat: function ( e, data ) {
622 data[ imagifyOptions.bulk.imagifybeatIDs.progress ] = imagifyOptions.bulk.contexts;
623 },
624
625 /**
626 * Listen for the custom event "imagifybeat-tick" on $(document).
627 * It allows to update various data periodically.
628 *
629 * @param {object} e Event object.
630 * @param {object} data Object containing all Imagifybeat IDs.
631 */
632 processQueueImagifybeat: function ( e, data ) {
633 var images_status, processed, progress;
634
635 if ( e.data.imagifyOptionsBulk && typeof data[ imagifyOptions.bulk.imagifybeatIDs.progress ] === 'undefined' ) {
636 return;
637 }
638
639 if ( e.data.imagifyOptionsBulk.processIsStopped ) {
640 e.data.imagifyOptionsBulk.processFinished();
641 return;
642 }
643
644 images_status = data[ imagifyOptions.bulk.imagifybeatIDs.progress ];
645
646 if ( images_status.remaining === 0 ) {
647 e.data.imagifyOptionsBulk.processFinished();
648 return;
649 }
650
651 processed = images_status.total - images_status.remaining;
652 progress = Math.floor( processed / images_status.total * 100 );
653 e.data.imagifyOptionsBulk.$progressBar.css( 'width', progress + '%' );
654 e.data.imagifyOptionsBulk.$progressText.text( processed + '/' + images_status.total );
655 },
656
657 /**
658 * Add a Imagifybeat ID for requirements on "imagifybeat-send" event.
659 *
660 * @param {object} e Event object.
661 * @param {object} data Object containing all Imagifybeat IDs.
662 */
663 addRequirementsImagifybeat: function ( e, data ) {
664 data[ imagifyOptions.bulk.imagifybeatIDs.requirements ] = 1;
665 },
666
667 /**
668 * Listen for the custom event "imagifybeat-tick" on $(document).
669 * It allows to update requirements status periodically.
670 *
671 * @param {object} e Event object.
672 * @param {object} data Object containing all Imagifybeat IDs.
673 */
674 processRequirementsImagifybeat: function ( e, data ) {
675 if ( e.data.imagifyOptionsBulk && typeof data[ imagifyOptions.bulk.imagifybeatIDs.requirements ] === 'undefined' ) {
676 return;
677 }
678
679 data = data[ imagifyOptions.bulk.imagifybeatIDs.requirements ];
680
681 imagifyOptions.bulk.curlMissing = data.curl_missing;
682 imagifyOptions.bulk.editorMissing = data.editor_missing;
683 imagifyOptions.bulk.extHttpBlocked = data.external_http_blocked;
684 imagifyOptions.bulk.apiDown = data.api_down;
685 imagifyOptions.bulk.keyIsValid = data.key_is_valid;
686 imagifyOptions.bulk.isOverQuota = data.is_over_quota;
687 },
688
689 // Optimization ============================================================================
690
691 /*
692 * launch the missing WebP generation process.
693 */
694 launchProcess: function () {
695 var _this;
696
697 if ( this.processIsStopped ) {
698 return;
699 }
700
701 _this = this;
702
703 $.get( this.getAjaxUrl( 'MissingNextGen', imagifyOptions.bulk.contexts ) )
704 .done( function( response ) {
705 var errorMessage;
706
707 if ( _this.processIsStopped ) {
708 return;
709 }
710
711 if ( response.data && response.data.message ) {
712 errorMessage = response.data.message;
713 } else {
714 errorMessage = imagifyOptions.bulk.ajaxErrorText;
715 }
716
717 if ( ! response.success ) {
718 // Error.
719 if ( ! _this.error ) {
720 _this.stopProcess( errorMessage );
721 }
722 return;
723 }
724
725 if ( 0 === response.data.total ) {
726 // No media to process.
727 _this.stopProcess( 'no-images' );
728
729 return;
730 }
731
732 _this.$missingWebpMessage.hide().attr('aria-hidden', 'true');
733
734 // Reset and display the progress bar.
735 _this.$progressText.text( '0' + ( response.data.total ? '/' + response.data.total : '' ) );
736 _this.$progressWrap.slideDown().attr( 'aria-hidden', 'false' ).removeClass( 'hidden' );
737 } )
738 .fail( function() {
739 // Error.
740 if ( ! _this.error ) {
741 _this.stopProcess( 'get-unoptimized-images' );
742 }
743 } );
744 },
745
746 /*
747 * End.
748 */
749 processFinished: function () {
750 var errorArgs = {};
751
752 // Maybe display an error.
753 if ( false !== this.error ) {
754 if ( 'invalid-api-key' === this.error ) {
755 errorArgs = {
756 title: imagifyOptions.bulk.labels.invalidAPIKeyTitle,
757 type: 'info'
758 };
759 }
760 else if ( 'over-quota' === this.error ) {
761 errorArgs = {
762 title: imagifyOptions.bulk.labels.overQuotaTitle,
763 html: $( '#tmpl-imagify-overquota-alert' ).html(),
764 type: 'info',
765 customClass: 'imagify-swal-has-subtitle imagify-swal-error-header',
766 showConfirmButton: false
767 };
768 }
769 else if ( 'get-unoptimized-images' === this.error ) {
770 errorArgs = {
771 title: imagifyOptions.bulk.labels.getUnoptimizedImagesErrorTitle,
772 html: imagifyOptions.bulk.labels.getUnoptimizedImagesErrorText,
773 type: 'info'
774 };
775 }
776 else if ( 'no-images' === this.error ) {
777 errorArgs = {
778 title: imagifyOptions.bulk.labels.nothingToDoTitle,
779 html: imagifyOptions.bulk.labels.nothingToDoText,
780 type: 'info'
781 };
782 }
783 else if ( 'no-backup' === this.error ) {
784 errorArgs = {
785 title: imagifyOptions.bulk.labels.nothingToDoTitle,
786 html: imagifyOptions.bulk.labels.nothingToDoNoBackupText,
787 type: 'info'
788 };
789 } else {
790 errorArgs = {
791 title: imagifyOptions.bulk.labels.error,
792 html: this.error,
793 type: 'info'
794 };
795 }
796
797 this.displayError( errorArgs );
798
799 // Reset the error.
800 this.error = false;
801 }
802
803 // Reset.
804 this.working = false;
805 this.processIsStopped = false;
806
807 // Reset Imagifybeat interval and enable suspend.
808 w.imagify.beat.resetInterval();
809 w.imagify.beat.enableSuspend();
810
811 // Reset the progress bar.
812 this.$progressWrap.slideUp().attr( 'aria-hidden', 'true' ).addClass( 'hidden' );
813 this.$progressText.text( '0' );
814 this.$missingWebpElement.hide().attr('aria-hidden', 'true');
815 this.$button.find( '.dashicons' ).removeClass( 'rotate' );
816 },
817
818 // Tools ===================================================================================
819
820 /*
821 * Tell if we have a blocking error. Can also display an error message in a swal.
822 *
823 * @param {bool} displayErrorMessage False to not display any error message.
824 * @return {bool}
825 */
826 hasBlockingError: function ( displayErrorMessage ) {
827 displayErrorMessage = undefined !== displayErrorMessage && displayErrorMessage;
828
829 if ( imagifyOptions.bulk.curlMissing ) {
830 if ( displayErrorMessage ) {
831 this.displayError( {
832 html: imagifyOptions.bulk.labels.curlMissing
833 } );
834 }
835 return true;
836 }
837
838 if ( imagifyOptions.bulk.editorMissing ) {
839 if ( displayErrorMessage ) {
840 this.displayError( {
841 html: imagifyOptions.bulk.labels.editorMissing
842 } );
843 }
844 return true;
845 }
846
847 if ( imagifyOptions.bulk.extHttpBlocked ) {
848 if ( displayErrorMessage ) {
849 this.displayError( {
850 html: imagifyOptions.bulk.labels.extHttpBlocked
851 } );
852 }
853 return true;
854 }
855
856 if ( imagifyOptions.bulk.apiDown ) {
857 if ( displayErrorMessage ) {
858 this.displayError( {
859 html: imagifyOptions.bulk.labels.apiDown
860 } );
861 }
862 return true;
863 }
864
865 if ( ! imagifyOptions.bulk.keyIsValid ) {
866 if ( displayErrorMessage ) {
867 this.displayError( {
868 title: imagifyOptions.bulk.labels.invalidAPIKeyTitle,
869 type: 'info'
870 } );
871 }
872 return true;
873 }
874
875 if ( imagifyOptions.bulk.isOverQuota ) {
876 if ( displayErrorMessage ) {
877 this.displayError( {
878 title: imagifyOptions.bulk.labels.overQuotaTitle,
879 html: $( '#tmpl-imagify-overquota-alert' ).html(),
880 type: 'info',
881 customClass: 'imagify-swal-has-subtitle imagify-swal-error-header',
882 showConfirmButton: false
883 } );
884 }
885 return true;
886 }
887
888 return false;
889 },
890
891 /*
892 * Display an error message in a modal.
893 *
894 * @param {string} title The modal title.
895 * @param {string} text The modal text.
896 * @param {object} args Other less common args.
897 */
898 displayError: function ( title, text, args ) {
899 var def = {
900 title: '',
901 html: '',
902 type: 'error',
903 customClass: '',
904 width: 620,
905 padding: 0,
906 showCloseButton: true,
907 showConfirmButton: true
908 };
909
910 if ( $.isPlainObject( title ) ) {
911 args = $.extend( {}, def, title );
912 } else {
913 args = args || {};
914 args = $.extend( {}, def, {
915 title: title || '',
916 html: text || ''
917 }, args );
918 }
919
920 args.title = args.title || imagifyOptions.bulk.labels.error;
921 args.customClass += ' imagify-sweet-alert';
922
923 swal( args ).catch( swal.noop );
924 },
925
926 /*
927 * Get the URL used for ajax requests.
928 *
929 * @param {string} action An ajax action, or part of it.
930 * @param {array} context The contexts.
931 * @return {string}
932 */
933 getAjaxUrl: function ( action, contexts ) {
934 var url;
935
936 url = ajaxurl + w.imagify.concat + '_wpnonce=' + imagifyOptions.bulk.ajaxNonce;
937 url += '&action=' + imagifyOptions.bulk.ajaxActions[ action ];
938 url += '&context=' + contexts.join('_');
939
940 return url;
941 },
942
943 /*
944 * Stop everything and set an error.
945 *
946 * @param {string} errorId An error ID.
947 */
948 stopProcess: function ( errorId ) {
949 this.processIsStopped = true;
950
951 this.error = errorId;
952
953 this.processFinished();
954 }
955 };
956
957 w.imagify.optionsBulk.init();
958
959 } )(window, document, jQuery);
960 /* eslint-enable no-underscore-dangle, consistent-this */
961
962
963 // "Select all" checkboxes =========================================================================
964 (function(w, d, $, undefined) { // eslint-disable-line no-unused-vars, no-shadow, no-shadow-restricted-names
965
966 var jqPropHookChecked = $.propHooks.checked;
967
968 // Force `.prop()` to trigger a `change` event.
969 $.propHooks.checked = {
970 set: function( elem, value, name ) {
971 var ret;
972
973 if ( undefined === jqPropHookChecked ) {
974 ret = ( elem[ name ] = value );
975 } else {
976 ret = jqPropHookChecked( elem, value, name );
977 }
978
979 $( elem ).trigger( 'change.imagify' );
980
981 return ret;
982 }
983 };
984
985 // Check all checkboxes.
986 $( '.imagify-select-all' ).on( 'click.imagify', function() {
987 var $_this = $(this),
988 action = $_this.data( 'action' ),
989 $btns = $_this.closest( '.imagify-select-all-buttons' ),
990 $group = $btns.prev( '.imagify-check-group' ),
991 inactive = 'imagify-is-inactive';
992
993 if ( $_this.hasClass( inactive ) ) {
994 return false;
995 }
996
997 $btns.find( '.imagify-select-all' ).removeClass( inactive ).attr( 'aria-disabled', 'false' );
998 $_this.addClass( inactive ).attr( 'aria-disabled', 'true' );
999
1000 $group.find( '.imagify-row-check' )
1001 .prop( 'checked', function() {
1002 var $this = $( this );
1003
1004 if ( $this.is( ':hidden,:disabled' ) ) {
1005 return false;
1006 }
1007
1008 if ( action === 'select' ) {
1009 return true;
1010 }
1011
1012 return false;
1013 } );
1014
1015 } );
1016
1017 // Change buttons status on checkboxes interation.
1018 $( '.imagify-check-group .imagify-row-check' ).on( 'change.imagify', function() {
1019 var $group = $( this ).closest( '.imagify-check-group' ),
1020 $checks = $group.find( '.imagify-row-check' ),
1021 could_be = $checks.filter( ':visible:enabled' ).length,
1022 are_checked = $checks.filter( ':visible:enabled:checked' ).length,
1023 $btns = $group.next( '.imagify-select-all-buttons' ),
1024 inactive = 'imagify-is-inactive';
1025
1026 // Toggle status of "check all" buttons.
1027 if ( are_checked === 0 ) {
1028 $btns.find( '[data-action="unselect"]' ).addClass( inactive ).attr( 'aria-disabled', 'true' );
1029 }
1030 if ( are_checked === could_be ) {
1031 $btns.find( '[data-action="select"]' ).addClass( inactive ).attr( 'aria-disabled', 'true' );
1032 }
1033 if ( are_checked !== could_be && are_checked > 0 ) {
1034 $btns.find( '.imagify-select-all' ).removeClass( inactive ).attr( 'aria-disabled', 'false' );
1035 }
1036 } );
1037
1038 } )(window, document, jQuery);
1039