PluginProbe
Imagify Image Optimization: Optimize Images | Compress & Convert to WebP/AVIF / 1.9.13
Imagify Image Optimization: Optimize Images | Compress & Convert to WebP/AVIF v1.9.13
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 / beat.js

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

873 lines 22.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 /**
2 * Imagify beat API
3 *
4 * This is a modified version of WordPress’ Heartbeat (WP 5.2.1).
5 * The main difference is that it allows to prevent suspension entirely.
6 * It uses the var imagifybeatSettings on init.
7 *
8 * Custom jQuery events:
9 * - imagifybeat-send
10 * - imagifybeat-tick
11 * - imagifybeat-error
12 * - imagifybeat-connection-lost
13 * - imagifybeat-connection-restored
14 * - imagifybeat-nonces-expired
15 *
16 * @since 1.9.3
17 */
18
19 window.imagify = window.imagify || {};
20
21 /* eslint-disable no-use-before-define */
22 (function($, d, w, undefined) { // eslint-disable-line no-unused-vars, no-shadow, no-shadow-restricted-names
23
24 /**
25 * Constructs the Imagifybeat API.
26 *
27 * @since 1.9.3
28 * @constructor
29 *
30 * @return {Imagifybeat} An instance of the Imagifybeat class.
31 */
32 var Imagifybeat = function() {
33 var $document = $( d ),
34 settings = {
35 // Suspend/resume.
36 suspend: false,
37
38 // Whether suspending is enabled.
39 suspendEnabled: true,
40
41 // Current screen id, defaults to the JS global 'pagenow' when present
42 // (in the admin) or 'front'.
43 screenId: '',
44
45 // XHR request URL, defaults to the JS global 'ajaxurl' when present.
46 url: '',
47
48 // Timestamp, start of the last connection request.
49 lastTick: 0,
50
51 // Container for the enqueued items.
52 queue: {},
53
54 // Connect interval (in seconds).
55 mainInterval: 60,
56
57 // Used when the interval is set to 5 sec. temporarily.
58 tempInterval: 0,
59
60 // Used when the interval is reset.
61 originalInterval: 0,
62
63 // Used to limit the number of AJAX requests.
64 minimalInterval: 0,
65
66 // Used together with tempInterval.
67 countdown: 0,
68
69 // Whether a connection is currently in progress.
70 connecting: false,
71
72 // Whether a connection error occurred.
73 connectionError: false,
74
75 // Used to track non-critical errors.
76 errorcount: 0,
77
78 // Whether at least one connection has been completed successfully.
79 hasConnected: false,
80
81 // Whether the current browser w is in focus and the user is active.
82 hasFocus: true,
83
84 // Timestamp, last time the user was active. Checked every 30 sec.
85 userActivity: 0,
86
87 // Flag whether events tracking user activity were set.
88 userActivityEvents: false,
89
90 // Timer that keeps track of how long a user has focus.
91 checkFocusTimer: 0,
92
93 // Timer that keeps track of how long needs to be waited before connecting to
94 // the server again.
95 beatTimer: 0
96 };
97
98 /**
99 * Sets local variables and events, then starts the beat.
100 *
101 * @since 1.9.3
102 * @access private
103 *
104 * @return {void}
105 */
106 function initialize() {
107 var options, hidden, visibilityState, visibilitychange;
108
109 if ( typeof w.pagenow === 'string' ) {
110 settings.screenId = w.pagenow;
111 }
112
113 if ( typeof w.ajaxurl === 'string' ) {
114 settings.url = w.ajaxurl;
115 }
116
117 // Pull in options passed from PHP.
118 if ( typeof w.imagifybeatSettings === 'object' ) {
119 options = w.imagifybeatSettings;
120
121 // The XHR URL can be passed as option when w.ajaxurl is not set.
122 if ( ! settings.url && options.ajaxurl ) {
123 settings.url = options.ajaxurl;
124 }
125
126 /*
127 * The interval can be from 15 to 120 sec. and can be set temporarily to 5 sec.
128 * It can be set in the initial options or changed later through JS and/or
129 * through PHP.
130 */
131 if ( options.interval ) {
132 settings.mainInterval = options.interval;
133
134 if ( settings.mainInterval < 15 ) {
135 settings.mainInterval = 15;
136 } else if ( settings.mainInterval > 120 ) {
137 settings.mainInterval = 120;
138 }
139 }
140
141 /*
142 * Used to limit the number of AJAX requests. Overrides all other intervals if
143 * they are shorter. Needed for some hosts that cannot handle frequent requests
144 * and the user may exceed the allocated server CPU time, etc. The minimal
145 * interval can be up to 600 sec. however setting it to longer than 120 sec.
146 * will limit or disable some of the functionality (like post locks). Once set
147 * at initialization, minimalInterval cannot be changed/overridden.
148 */
149 if ( options.minimalInterval ) {
150 options.minimalInterval = parseInt( options.minimalInterval, 10 );
151 settings.minimalInterval = options.minimalInterval > 0 && options.minimalInterval <= 600 ? options.minimalInterval * 1000 : 0;
152 }
153
154 if ( settings.minimalInterval && settings.mainInterval < settings.minimalInterval ) {
155 settings.mainInterval = settings.minimalInterval;
156 }
157
158 // 'screenId' can be added from settings on the front end where the JS global
159 // 'pagenow' is not set.
160 if ( ! settings.screenId ) {
161 settings.screenId = options.screenId || 'front';
162 }
163
164 if ( 'disable' === options.suspension ) {
165 disableSuspend();
166 }
167 }
168
169 // Convert to milliseconds.
170 settings.mainInterval = settings.mainInterval * 1000;
171 settings.originalInterval = settings.mainInterval;
172
173 /*
174 * Switch the interval to 120 seconds by using the Page Visibility API.
175 * If the browser doesn't support it (Safari < 7, Android < 4.4, IE < 10), the
176 * interval will be increased to 120 seconds after 5 minutes of mouse and keyboard
177 * inactivity.
178 */
179 if ( typeof document.hidden !== 'undefined' ) {
180 hidden = 'hidden';
181 visibilitychange = 'visibilitychange';
182 visibilityState = 'visibilityState';
183 } else if ( typeof document.msHidden !== 'undefined' ) { // IE10
184 hidden = 'msHidden';
185 visibilitychange = 'msvisibilitychange';
186 visibilityState = 'msVisibilityState';
187 } else if ( typeof document.webkitHidden !== 'undefined' ) { // Android
188 hidden = 'webkitHidden';
189 visibilitychange = 'webkitvisibilitychange';
190 visibilityState = 'webkitVisibilityState';
191 }
192
193 if ( hidden ) {
194 if ( document[ hidden ] ) {
195 settings.hasFocus = false;
196 }
197
198 $document.on( visibilitychange + '.imagifybeat', function() {
199 if ( 'hidden' === document[ visibilityState ] ) {
200 blurred();
201 w.clearInterval( settings.checkFocusTimer );
202 } else {
203 focused();
204 if ( document.hasFocus ) {
205 settings.checkFocusTimer = w.setInterval( checkFocus, 10000 );
206 }
207 }
208 });
209 }
210
211 // Use document.hasFocus() if available.
212 if ( document.hasFocus ) {
213 settings.checkFocusTimer = w.setInterval( checkFocus, 10000 );
214 }
215
216 $( w ).on( 'unload.imagifybeat', function() {
217 // Don't connect anymore.
218 settings.suspend = true;
219
220 // Abort the last request if not completed.
221 if ( settings.xhr && 4 !== settings.xhr.readyState ) {
222 settings.xhr.abort();
223 }
224 } );
225
226 // Check for user activity every 30 seconds.
227 w.setInterval( checkUserActivity, 30000 );
228
229 // Start one tick after DOM ready.
230 $document.ready( function() {
231 settings.lastTick = time();
232 scheduleNextTick();
233 } );
234 }
235
236 /**
237 * Returns the current time according to the browser.
238 *
239 * @since 1.9.3
240 * @access private
241 *
242 * @return {int} Returns the current time.
243 */
244 function time() {
245 return (new Date()).getTime();
246 }
247
248 /**
249 * Checks if the iframe is from the same origin.
250 *
251 * @since 1.9.3
252 * @access private
253 *
254 * @return {bool} Returns whether or not the iframe is from the same origin.
255 */
256 function isLocalFrame( frame ) {
257 var origin, src = frame.src; // eslint-disable-line no-shadow
258
259 /*
260 * Need to compare strings as WebKit doesn't throw JS errors when iframes have different origin. It throws uncatchable exceptions.
261 */
262 if ( src && /^https?:\/\//.test( src ) ) {
263 origin = w.location.origin ? w.location.origin : w.location.protocol + '//' + w.location.host;
264
265 if ( src.indexOf( origin ) !== 0 ) {
266 return false;
267 }
268 }
269
270 try {
271 if ( frame.contentWindow.document ) {
272 return true;
273 }
274 } catch ( e ) {} // eslint-disable-line no-empty
275
276 return false;
277 }
278
279 /**
280 * Checks if the document's focus has changed.
281 *
282 * @since 1.9.3
283 * @access private
284 *
285 * @return {void}
286 */
287 function checkFocus() {
288 if ( settings.hasFocus && ! document.hasFocus() ) {
289 blurred();
290 } else if ( ! settings.hasFocus && document.hasFocus() ) {
291 focused();
292 }
293 }
294
295 /**
296 * Sets error state and fires an event on XHR errors or timeout.
297 *
298 * @since 1.9.3
299 * @access private
300 *
301 * @param {string} error The error type passed from the XHR.
302 * @param {int} httpStatus The HTTP status code passed from jqXHR (200, 404, 500, etc.).
303 * @return {void}
304 */
305 function setErrorState( error, httpStatus ) {
306 var trigger;
307
308 if ( error ) {
309 switch ( error ) {
310 case 'abort':
311 // Do nothing.
312 break;
313 case 'timeout':
314 // No response for 30 sec.
315 trigger = true;
316 break;
317 case 'error':
318 if ( 503 === httpStatus && settings.hasConnected ) {
319 trigger = true;
320 break;
321 }
322 /* falls through */
323 case 'parsererror':
324 case 'empty':
325 case 'unknown':
326 settings.errorcount++;
327
328 if ( settings.errorcount > 2 && settings.hasConnected ) {
329 trigger = true;
330 }
331
332 break;
333 }
334
335 if ( trigger && ! hasConnectionError() ) {
336 settings.connectionError = true;
337 $document.trigger( 'imagifybeat-connection-lost', [ error, httpStatus ] );
338
339 if ( w.wp.hooks ) {
340 w.wp.hooks.doAction( 'imagifybeat.connection-lost', error, httpStatus );
341 }
342 }
343 }
344 }
345
346 /**
347 * Clears the error state and fires an event if there is a connection error.
348 *
349 * @since 1.9.3
350 * @access private
351 *
352 * @return {void}
353 */
354 function clearErrorState() {
355 // Has connected successfully.
356 settings.hasConnected = true;
357
358 if ( hasConnectionError() ) {
359 settings.errorcount = 0;
360 settings.connectionError = false;
361 $document.trigger( 'imagifybeat-connection-restored' );
362
363 if ( w.wp.hooks ) {
364 w.wp.hooks.doAction( 'imagifybeat.connection-restored' );
365 }
366 }
367 }
368
369 /**
370 * Gathers the data and connects to the server.
371 *
372 * @since 1.9.3
373 * @access private
374 *
375 * @return {void}
376 */
377 function connect() {
378 var ajaxData, imagifybeatData;
379
380 // If the connection to the server is slower than the interval,
381 // imagifybeat connects as soon as the previous connection's response is received.
382 if ( settings.connecting || settings.suspend ) {
383 return;
384 }
385
386 settings.lastTick = time();
387
388 imagifybeatData = $.extend( {}, settings.queue );
389 // Clear the data queue. Anything added after this point will be sent on the next tick.
390 settings.queue = {};
391
392 $document.trigger( 'imagifybeat-send', [ imagifybeatData ] );
393
394 if ( w.wp.hooks ) {
395 w.wp.hooks.doAction( 'imagifybeat.send', imagifybeatData );
396 }
397
398 ajaxData = {
399 data: imagifybeatData,
400 interval: settings.tempInterval ? settings.tempInterval / 1000 : settings.mainInterval / 1000,
401 _nonce: typeof w.imagifybeatSettings === 'object' ? w.imagifybeatSettings.nonce : '',
402 action: 'imagifybeat',
403 screen_id: settings.screenId,
404 has_focus: settings.hasFocus
405 };
406
407 if ( 'customize' === settings.screenId ) {
408 ajaxData.wp_customize = 'on';
409 }
410
411 settings.connecting = true;
412 settings.xhr = $.ajax( {
413 url: settings.url,
414 type: 'post',
415 timeout: 60000, // Throw an error if not completed after 60 sec.
416 data: ajaxData,
417 dataType: 'json'
418 } ).always( function() {
419 settings.connecting = false;
420 scheduleNextTick();
421 } ).done( function( response, textStatus, jqXHR ) {
422 var newInterval;
423
424 if ( ! response ) {
425 setErrorState( 'empty' );
426 return;
427 }
428
429 clearErrorState();
430
431 if ( response.nonces_expired ) {
432 $document.trigger( 'imagifybeat-nonces-expired' );
433
434 if ( w.wp.hooks ) {
435 w.wp.hooks.doAction( 'imagifybeat.nonces-expired' );
436 }
437 }
438
439 // Change the interval from PHP
440 if ( response.imagifybeat_interval ) {
441 newInterval = response.imagifybeat_interval;
442 delete response.imagifybeat_interval;
443 }
444
445 // Update the imagifybeat nonce if set.
446 if ( response.imagifybeat_nonce && typeof w.imagifybeatSettings === 'object' ) {
447 w.imagifybeatSettings.nonce = response.imagifybeat_nonce;
448 delete response.imagifybeat_nonce;
449 }
450
451 $document.trigger( 'imagifybeat-tick', [ response, textStatus, jqXHR ] );
452
453 if ( w.wp.hooks ) {
454 w.wp.hooks.doAction( 'imagifybeat.tick', response, textStatus, jqXHR );
455 }
456
457 // Do this last. Can trigger the next XHR if connection time > 5 sec. and newInterval == 'fast'.
458 if ( newInterval ) {
459 interval( newInterval );
460 }
461 } ).fail( function( jqXHR, textStatus, error ) {
462 setErrorState( textStatus || 'unknown', jqXHR.status );
463 $document.trigger( 'imagifybeat-error', [ jqXHR, textStatus, error ] );
464
465 if ( w.wp.hooks ) {
466 w.wp.hooks.doAction( 'imagifybeat.error', jqXHR, textStatus, error );
467 }
468 } );
469 }
470
471 /**
472 * Schedules the next connection.
473 *
474 * Fires immediately if the connection time is longer than the interval.
475 *
476 * @since 1.9.3
477 * @access private
478 *
479 * @return {void}
480 */
481 function scheduleNextTick() {
482 var delta = time() - settings.lastTick,
483 interv = settings.mainInterval;
484
485 if ( settings.suspend ) {
486 return;
487 }
488
489 if ( ! settings.hasFocus && settings.suspendEnabled ) {
490 // When no user activity or the window lost focus, increase polling interval to 120 seconds, but only if suspend is enabled.
491 interv = 120000; // 120 sec.
492 } else if ( settings.countdown > 0 && settings.tempInterval ) {
493 interv = settings.tempInterval;
494 settings.countdown--;
495
496 if ( settings.countdown < 1 ) {
497 settings.tempInterval = 0;
498 }
499 }
500
501 if ( settings.minimalInterval && interv < settings.minimalInterval ) {
502 interv = settings.minimalInterval;
503 }
504
505 w.clearTimeout( settings.beatTimer );
506
507 if ( delta < interv ) {
508 settings.beatTimer = w.setTimeout(
509 function() {
510 connect();
511 },
512 interv - delta
513 );
514 } else {
515 connect();
516 }
517 }
518
519 /**
520 * Sets the internal state when the browser w becomes hidden or loses focus.
521 *
522 * @since 1.9.3
523 * @access private
524 *
525 * @return {void}
526 */
527 function blurred() {
528 settings.hasFocus = false;
529 }
530
531 /**
532 * Sets the internal state when the browser w becomes visible or is in focus.
533 *
534 * @since 1.9.3
535 * @access private
536 *
537 * @return {void}
538 */
539 function focused() {
540 settings.userActivity = time();
541
542 // Resume if suspended
543 settings.suspend = false;
544
545 if ( ! settings.hasFocus ) {
546 settings.hasFocus = true;
547 scheduleNextTick();
548 }
549 }
550
551 /**
552 * Runs when the user becomes active after a period of inactivity.
553 *
554 * @since 1.9.3
555 * @access private
556 *
557 * @return {void}
558 */
559 function userIsActive() {
560 settings.userActivityEvents = false;
561 $document.off( '.imagifybeat-active' );
562
563 $( 'iframe' ).each( function( i, frame ) {
564 if ( isLocalFrame( frame ) ) {
565 $( frame.contentWindow ).off( '.imagifybeat-active' );
566 }
567 } );
568
569 focused();
570 }
571
572 /**
573 * Checks for user activity.
574 *
575 * Runs every 30 sec. Sets 'hasFocus = true' if user is active and the w is
576 * in the background. Sets 'hasFocus = false' if the user has been inactive
577 * (no mouse or keyboard activity) for 5 min. even when the w has focus.
578 *
579 * @since 1.9.3
580 * @access private
581 *
582 * @return {void}
583 */
584 function checkUserActivity() {
585 var lastActive = settings.userActivity ? time() - settings.userActivity : 0;
586
587 // Set hasFocus to false when no mouse or keyboard activity for 5 min.
588 if ( lastActive > 300000 && settings.hasFocus ) {
589 blurred();
590 }
591
592 // Suspend after 10 min. of inactivity.
593 if ( settings.suspendEnabled && lastActive > 600000 ) {
594 settings.suspend = true;
595 }
596
597 if ( ! settings.userActivityEvents ) {
598 $document.on( 'mouseover.imagifybeat-active keyup.imagifybeat-active touchend.imagifybeat-active', function() {
599 userIsActive();
600 } );
601
602 $( 'iframe' ).each( function( i, frame ) {
603 if ( isLocalFrame( frame ) ) {
604 $( frame.contentWindow ).on( 'mouseover.imagifybeat-active keyup.imagifybeat-active touchend.imagifybeat-active', function() {
605 userIsActive();
606 } );
607 }
608 } );
609
610 settings.userActivityEvents = true;
611 }
612 }
613
614 // Public methods.
615
616 /**
617 * Checks whether the w (or any local iframe in it) has focus, or the user
618 * is active.
619 *
620 * @since 1.9.3
621 * @memberOf imagify.beat.prototype
622 *
623 * @return {bool} True if the w or the user is active.
624 */
625 function hasFocus() {
626 return settings.hasFocus;
627 }
628
629 /**
630 * Checks whether there is a connection error.
631 *
632 * @since 1.9.3
633 * @memberOf imagify.beat.prototype
634 *
635 * @return {bool} True if a connection error was found.
636 */
637 function hasConnectionError() {
638 return settings.connectionError;
639 }
640
641 /**
642 * Connects as soon as possible regardless of 'hasFocus' state.
643 *
644 * Will not open two concurrent connections. If a connection is in progress,
645 * will connect again immediately after the current connection completes.
646 *
647 * @since 1.9.3
648 * @memberOf imagify.beat.prototype
649 *
650 * @return {void}
651 */
652 function connectNow() {
653 settings.lastTick = 0;
654 scheduleNextTick();
655 }
656
657 /**
658 * Disables suspending.
659 *
660 * Should be used only when Imagifybeat is performing critical tasks like
661 * autosave, post-locking, etc. Using this on many screens may overload the
662 * user's hosting account if several browser ws/tabs are left open for a
663 * long time.
664 *
665 * @since 1.9.3
666 * @memberOf imagify.beat.prototype
667 *
668 * @return {void}
669 */
670 function disableSuspend() {
671 settings.suspendEnabled = false;
672 }
673
674 /**
675 * Enables suspending.
676 *
677 * @since 1.9.3
678 * @memberOf imagify.beat.prototype
679 *
680 * @return {void}
681 */
682 function enableSuspend() {
683 settings.suspendEnabled = true;
684 }
685
686 /**
687 * Gets/Sets the interval.
688 *
689 * When setting to 'fast' or 5, the interval is 5 seconds for the next 30 ticks
690 * (for 2 minutes and 30 seconds) by default. In this case the number of 'ticks'
691 * can be passed as second argument. If the window doesn't have focus, the
692 * interval slows down to 2 min.
693 *
694 * @since 1.9.3
695 * @memberOf imagify.beat.prototype
696 *
697 * @param {string|int} speed Interval: 'fast' or 5, 15, 30, 60, 120. Fast equals 5.
698 * @param {string} ticks Tells how many ticks before the interval reverts back. Used with speed = 'fast' or 5.
699 * @return {int} Current interval in seconds.
700 */
701 function interval( speed, ticks ) {
702 var newInterval,
703 oldInterval = settings.tempInterval ? settings.tempInterval : settings.mainInterval;
704
705 if ( speed ) {
706 switch ( speed ) {
707 case 'fast':
708 case 5:
709 newInterval = 5000;
710 break;
711 case 15:
712 newInterval = 15000;
713 break;
714 case 30:
715 newInterval = 30000;
716 break;
717 case 60:
718 newInterval = 60000;
719 break;
720 case 120:
721 newInterval = 120000;
722 break;
723 case 'long-polling':
724 // Allow long polling, (experimental)
725 settings.mainInterval = 0;
726 return 0;
727 default:
728 newInterval = settings.originalInterval;
729 }
730
731 if ( settings.minimalInterval && newInterval < settings.minimalInterval ) {
732 newInterval = settings.minimalInterval;
733 }
734
735 if ( 5000 === newInterval ) {
736 ticks = parseInt( ticks, 10 ) || 30;
737 ticks = ticks < 1 || ticks > 30 ? 30 : ticks;
738
739 settings.countdown = ticks;
740 settings.tempInterval = newInterval;
741 } else {
742 settings.countdown = 0;
743 settings.tempInterval = 0;
744 settings.mainInterval = newInterval;
745 }
746
747 // Change the next connection time if new interval has been set.
748 // Will connect immediately if the time since the last connection
749 // is greater than the new interval.
750 if ( newInterval !== oldInterval ) {
751 scheduleNextTick();
752 }
753 }
754
755 return settings.tempInterval ? settings.tempInterval / 1000 : settings.mainInterval / 1000;
756 }
757
758 /**
759 * Resets the interval.
760 *
761 * @since 1.9.3
762 * @memberOf imagify.beat.prototype
763 *
764 * @return {int} Current interval in seconds.
765 */
766 function resetInterval() {
767 return interval( settings.originalInterval );
768 }
769
770 /**
771 * Enqueues data to send with the next XHR.
772 *
773 * As the data is send asynchronously, this function doesn't return the XHR
774 * response. To see the response, use the custom jQuery event 'imagifybeat-tick'
775 * on the document, example:
776 * $(document).on( 'imagifybeat-tick.myname', function( event, data, textStatus, jqXHR ) {
777 * // code
778 * });
779 * If the same 'handle' is used more than once, the data is not overwritten when
780 * the third argument is 'true'. Use `imagify.beat.isQueued('handle')` to see if
781 * any data is already queued for that handle.
782 *
783 * @since 1.9.3
784 * @memberOf imagify.beat.prototype
785 *
786 * @param {string} handle Unique handle for the data, used in PHP to receive the data.
787 * @param {mixed} data The data to send.
788 * @param {bool} noOverwrite Whether to overwrite existing data in the queue.
789 * @return {bool} True if the data was queued.
790 */
791 function enqueue( handle, data, noOverwrite ) {
792 if ( handle ) {
793 if ( noOverwrite && this.isQueued( handle ) ) {
794 return false;
795 }
796
797 settings.queue[handle] = data;
798 return true;
799 }
800 return false;
801 }
802
803 /**
804 * Checks if data with a particular handle is queued.
805 *
806 * @since 1.9.3
807 *
808 * @param {string} handle The handle for the data.
809 * @return {bool} True if the data is queued with this handle.
810 */
811 function isQueued( handle ) {
812 if ( handle ) {
813 return settings.queue.hasOwnProperty( handle );
814 }
815 }
816
817 /**
818 * Removes data with a particular handle from the queue.
819 *
820 * @since 1.9.3
821 * @memberOf imagify.beat.prototype
822 *
823 * @param {string} handle The handle for the data.
824 */
825 function dequeue( handle ) {
826 if ( handle ) {
827 delete settings.queue[handle];
828 }
829 }
830
831 /**
832 * Gets data that was enqueued with a particular handle.
833 *
834 * @since 1.9.3
835 * @memberOf imagify.beat.prototype
836 *
837 * @param {string} handle The handle for the data.
838 * @return {mixed} The data or undefined.
839 */
840 function getQueuedItem( handle ) {
841 if ( handle ) {
842 return this.isQueued( handle ) ? settings.queue[ handle ] : undefined;
843 }
844 }
845
846 initialize();
847
848 // Expose public methods.
849 return {
850 hasFocus: hasFocus,
851 connectNow: connectNow,
852 disableSuspend: disableSuspend,
853 enableSuspend: enableSuspend,
854 interval: interval,
855 resetInterval: resetInterval,
856 hasConnectionError: hasConnectionError,
857 enqueue: enqueue,
858 dequeue: dequeue,
859 isQueued: isQueued,
860 getQueuedItem: getQueuedItem
861 };
862 };
863
864 /**
865 * Contains the Imagifybeat API.
866 *
867 * @namespace imagify.beat
868 * @type {Imagifybeat}
869 */
870 w.imagify.beat = new Imagifybeat();
871
872 } )( jQuery, document, window );
873