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

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