PluginProbe
Imagify Image Optimization: Optimize Images | Compress & Convert to WebP/AVIF / 2.3.1
Imagify Image Optimization: Optimize Images | Compress & Convert to WebP/AVIF v2.3.1
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.3.1, at assets/js/options.js

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