PluginProbe
WP-Optimize – Cache, Compress images, Minify & Clean database to boost page speed & performance / 3.7.0
WP-Optimize – Cache, Compress images, Minify & Clean database to boost page speed & performance v3.7.0
4.7.0 4.6.1 4.6.0 4.5.5 4.5.4 4.5.3 4.5.2 3.2.20 3.2.21 3.2.22 3.2.3 3.2.5 3.2.6 3.2.7 3.2.9 3.3.0 3.3.1 3.3.2 3.4.0 3.4.1 3.4.2 3.5.0 3.6.0 3.7.0 3.7.1 All 111 releases
wp-optimize / js / heartbeat.js

heartbeat.js in WP-Optimize – Cache, Compress images, Minify & Clean database to boost page speed & performance 3.7.0, at js/heartbeat.js

264 lines 7.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 var WP_Optimize_Heartbeat_Agents = {};
2
3 /**
4 * Attach to WordPress heartbeat API. Has a fallback method if core API is disabled
5 *
6 * @returns {object} WP_Optimize_Heartbeat exports
7 */
8 var WP_Optimize_Heartbeat = function () {
9 var $ = jQuery;
10 var agent_idle_ttl_in_seconds = 60; // retry after 60 seconds without a response
11 var wpo_fallback;
12 var _setup = false;
13
14 /**
15 * Generate a unique ID to be used as agents IDs
16 *
17 * @returns {string}
18 */
19 var guid = function() {
20 var s4 = function() {
21 return Math.floor((1 + Math.random()) * 0x10000)
22 .toString(16)
23 .substring(1);
24 }
25 //return id of format 'aaaaaaaa'-'aaaa'-'aaaa'-'aaaa'-'aaaaaaaaaaaa'
26 return s4() + s4() + '-' + s4() + '-' + s4() + '-' + s4() + '-' + s4() + s4() + s4();
27 }
28
29 /**
30 * Configure the heartbeat events, if heartbeat API is missing, setup fallback
31 *
32 * @returns {void}
33 */
34 function setup() {
35 if (false === _setup) {
36 _setup = true;
37
38 $(document).on('heartbeat-send', function(event, data) {
39 for(var uid in WP_Optimize_Heartbeat_Agents) {
40 var agent = WP_Optimize_Heartbeat_Agents[uid];
41
42 if (!agent.sent) {
43 if ('command_data' in agent) {
44 data[uid] = {};
45 data[uid][agent.command] = agent.command_data;
46 } else {
47 data[uid] = agent.command;
48 }
49
50 agent.sent_time = new Date().getTime();
51 agent.sent = true;
52 }
53
54 // Retry after idle time is passed (no response in X seconds)
55 var seconds = ((new Date()).getTime() - agent.sent_time) / 1000;
56 if (seconds > agent_idle_ttl_in_seconds) {
57 agent.sent = false;
58 }
59 }
60 });
61
62 $(document).on('heartbeat-tick', function(event, data) {
63 if ('object' == typeof(data.callbacks)) {
64 for(var uid in data.callbacks) {
65 if (is_wpo_heartbeat(uid)) {
66 var response;
67 try {
68 response = JSON.parse(data.callbacks[uid]);
69 } catch(e) {
70 response = data.callbacks[uid];
71 }
72
73 if ('undefined' != typeof(response.result) && false === response.result && ('undefined' == typeof(response.skip_notice) || false === response.skip_notice)) {
74 wp_optimize.notices.show_notice(response.error_code, response.error_message);
75 } else {
76 if ('undefined' !== typeof(WP_Optimize_Heartbeat_Agents[uid]) && WP_Optimize_Heartbeat_Agents[uid].callback instanceof Function) {
77 WP_Optimize_Heartbeat_Agents[uid].callback(response);
78 }
79 }
80
81 if ('undefined' !== typeof WP_Optimize_Heartbeat_Agents[uid]) {
82 delete WP_Optimize_Heartbeat_Agents[uid];
83 }
84 }
85 }
86 }
87 });
88
89 if (is_heartbeat_api_disabled()) {
90 wpo_fallback = WP_Optimize_Heartbeat_Fallback();
91 } else {
92 // Some agents send `_wait:false` because the UI needs to execute that action quickly, `disableSuspend` allows for `connectNow` to trigger a heartbeat instantly
93 wp.heartbeat.disableSuspend();
94 }
95 }
96 }
97
98 /**
99 * Cancel a group of agents all at once
100 *
101 * @param {array} agents_ids The list of agent ids to cancel
102 */
103 function cancel_agents(agents_ids) {
104 while(agent_id = agents_ids.shift()) {
105 cancel_agent(agent_id);
106 }
107 }
108
109 /**
110 * Check if heartbeat action is a WP-Optimize action or something else that we should ignore
111 *
112 * @param {string} uid The UID of the agent
113 * @returns {boolean}
114 */
115 function is_wpo_heartbeat(uid) {
116 return 0 === uid.indexOf('wpo-heartbeat-');
117 }
118
119 /**
120 * Check if native heartbeat API is available
121 *
122 * @returns {boolean}
123 */
124 function is_heartbeat_api_disabled() {
125 return 'undefined' === typeof(wp.heartbeat);
126 }
127
128 /**
129 * Filter function to check if an agent is already scheduled
130 *
131 * @param {object} agent1 First comparison agent, usually already scheduled agents
132 * @param {object} agent2 Second comparison agent, usually the one you are trying to check if it already exists
133 * @returns {boolean}
134 */
135 function do_agents_match(agent1, agent2) {
136 var command_matches = agent1.command === agent2.command;
137 var subaction1 = agent1.command_data && agent1.command_data.subaction ? agent1.command_data.subaction : undefined;
138 var subaction2 = agent2.command_data && agent2.command_data.subaction ? agent2.command_data.subaction : undefined;
139 var subaction_matches = subaction1 === subaction2;
140 var command_not_sent_yet = false === agent1.sent;
141
142 return command_matches && subaction_matches && command_not_sent_yet;
143 }
144
145 /**
146 * Add a heartbeat agent that will be sent to backend and has a callback to receive the response
147 *
148 * @param {object} data Expected an object like {command: string}. Commands will be treated as `data._unique:true` by default. Some commands may need permission to schedule multiple times, by sending `_unique:false`
149 * @returns {string|null}
150 */
151 function add_agent(data) {
152 var already_scheduled = Object.values(WP_Optimize_Heartbeat_Agents).some(function(agent) { return do_agents_match(agent, data); });
153 if (already_scheduled && ('undefined' == typeof(data._unique) || (true == data._unique))) {
154 return null;
155 }
156
157 var agent_id = 'wpo-heartbeat-' + guid();
158 data.sent = false;
159 WP_Optimize_Heartbeat_Agents[agent_id] = data;
160
161 if ('undefined' !== typeof(data._wait) && false === data._wait) {
162 trigger_heartbeat();
163 }
164
165 return agent_id;
166 }
167
168 /**
169 * Trigger a heartbeat by code
170 *
171 * @returns {void}
172 */
173 function trigger_heartbeat() {
174 if (is_heartbeat_api_disabled()) {
175 wpo_fallback.do_heartbeat();
176 } else {
177 setTimeout(function() { wp.heartbeat.connectNow(); }, 50);
178 }
179 }
180
181 /**
182 * Remove agent from list if `_keep:false`. Defaults to `true`, not cancel
183 * A method called cancel_agents that by default does not cancel anything is controversial,
184 * but in practice only things like informational requests can be really cancelled,
185 * otherwise you get strange inconsistent results when things get wiped out and callbacks are not being called.
186 *
187 * @param {string} agent_id The id of the agent to be removed
188 * @returns {void}
189 */
190 function cancel_agent(agent_id) {
191 var agent = WP_Optimize_Heartbeat_Agents[agent_id];
192 if('undefined' != typeof(agent)) {
193 if ('undefined' != typeof(agent._keep) && (false == agent._keep)) {
194 delete WP_Optimize_Heartbeat_Agents[agent_id];
195 }
196 }
197 }
198
199 return {
200 setup: setup,
201 add_agent: add_agent,
202 cancel_agents: cancel_agents,
203 cancel_agent: cancel_agent
204 };
205 }
206
207 /**
208 * Fallback for the heartbeat api
209 *
210 * @returns {object} WP_Optimize_Heartbeat_Fallback exports
211 */
212 var WP_Optimize_Heartbeat_Fallback = function() {
213 var timeout_handler;
214
215 var payload = {
216 "data":{},
217 "interval":wpo_heartbeat_ajax.interval,
218 "_nonce":wpo_heartbeat_ajax.nonce,
219 "action":"heartbeat",
220 "screen_id":window.pagenow,
221 "has_focus":false
222 };
223
224 /**
225 * Actually trigger the standard AJAX call to run a heartbeat event
226 *
227 * @param {int} interval How many seconds until next heartbeat
228 * @returns {void}
229 */
230 function do_heartbeat(interval) {
231 interval = 'undefined' == typeof(interval) ? payload.interval : interval;
232
233 var this_payload = Object.assign({}, payload);
234 var data = {};
235
236 jQuery(document).trigger('heartbeat-send', data);
237
238 this_payload.data = data;
239
240 jQuery.ajax({
241 type : "post",
242 dataType : "json",
243 url : wpo_heartbeat_ajax.ajaxurl,
244 data : this_payload,
245 success: function(response) {
246 if('undefined' != typeof(response.callbacks)) {
247 jQuery(document).trigger('heartbeat-tick', response);
248 }
249 }
250 });
251
252 if (timeout_handler) {
253 clearTimeout(timeout_handler);
254 }
255
256 timeout_handler = setTimeout(do_heartbeat, interval * 1000, interval);
257 }
258
259 timeout_handler = setTimeout(do_heartbeat, payload.interval * 1000, payload.interval);
260
261 return {
262 do_heartbeat: do_heartbeat
263 };
264 }