PluginProbe
WP-Optimize – Cache, Compress images, Minify & Clean database to boost page speed & performance / 3.4.1
WP-Optimize – Cache, Compress images, Minify & Clean database to boost page speed & performance v3.4.1
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.4.1, at js/heartbeat.js

261 lines 7.1 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) {
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 command_not_sent_yet = false === agent1.sent;
138
139 return command_matches && command_not_sent_yet;
140 }
141
142 /**
143 * Add a heartbeat agent that will be sent to backend and has a callback to receive the response
144 *
145 * @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`
146 * @returns {string|null}
147 */
148 function add_agent(data) {
149 var already_scheduled = Object.values(WP_Optimize_Heartbeat_Agents).some(function(agent) { return do_agents_match(agent, data); });
150 if (already_scheduled && ('undefined' == typeof(data._unique) || (true == data._unique))) {
151 return null;
152 }
153
154 var agent_id = 'wpo-heartbeat-' + guid();
155 data.sent = false;
156 WP_Optimize_Heartbeat_Agents[agent_id] = data;
157
158 if ('undefined' !== typeof(data._wait) && false === data._wait) {
159 trigger_heartbeat();
160 }
161
162 return agent_id;
163 }
164
165 /**
166 * Trigger a heartbeat by code
167 *
168 * @returns {void}
169 */
170 function trigger_heartbeat() {
171 if (is_heartbeat_api_disabled()) {
172 wpo_fallback.do_heartbeat();
173 } else {
174 setTimeout(function() { wp.heartbeat.connectNow(); }, 50);
175 }
176 }
177
178 /**
179 * Remove agent from list if `_keep:false`. Defaults to `true`, not cancel
180 * A method called cancel_agents that by default does not cancel anything is controversial,
181 * but in practice only things like informational requests can be really cancelled,
182 * otherwise you get strange inconsistent results when things get wiped out and callbacks are not being called.
183 *
184 * @param {string} agent_id The id of the agent to be removed
185 * @returns {void}
186 */
187 function cancel_agent(agent_id) {
188 var agent = WP_Optimize_Heartbeat_Agents[agent_id];
189 if('undefined' != typeof(agent)) {
190 if ('undefined' != typeof(agent._keep) && (false == agent._keep)) {
191 delete WP_Optimize_Heartbeat_Agents[agent_id];
192 }
193 }
194 }
195
196 return {
197 setup: setup,
198 add_agent: add_agent,
199 cancel_agents: cancel_agents,
200 cancel_agent: cancel_agent
201 };
202 }
203
204 /**
205 * Fallback for the heartbeat api
206 *
207 * @returns {object} WP_Optimize_Heartbeat_Fallback exports
208 */
209 var WP_Optimize_Heartbeat_Fallback = function() {
210 var timeout_handler;
211
212 var payload = {
213 "data":{},
214 "interval":wpo_heartbeat_ajax.interval,
215 "_nonce":wpo_heartbeat_ajax.nonce,
216 "action":"heartbeat",
217 "screen_id":window.pagenow,
218 "has_focus":false
219 };
220
221 /**
222 * Actually trigger the standard AJAX call to run a heartbeat event
223 *
224 * @param {int} interval How many seconds until next heartbeat
225 * @returns {void}
226 */
227 function do_heartbeat(interval) {
228 interval = 'undefined' == typeof(interval) ? payload.interval : interval;
229
230 var this_payload = Object.assign({}, payload);
231 var data = {};
232
233 jQuery(document).trigger('heartbeat-send', data);
234
235 this_payload.data = data;
236
237 jQuery.ajax({
238 type : "post",
239 dataType : "json",
240 url : wpo_heartbeat_ajax.ajaxurl,
241 data : this_payload,
242 success: function(response) {
243 if('undefined' != typeof(response.callbacks)) {
244 jQuery(document).trigger('heartbeat-tick', response);
245 }
246 }
247 });
248
249 if (timeout_handler) {
250 clearTimeout(timeout_handler);
251 }
252
253 timeout_handler = setTimeout(do_heartbeat, interval * 1000, interval);
254 }
255
256 timeout_handler = setTimeout(do_heartbeat, payload.interval * 1000, payload.interval);
257
258 return {
259 do_heartbeat: do_heartbeat
260 };
261 }