PluginProbe
WP-Optimize – Cache, Compress images, Minify & Clean database to boost page speed & performance / 4.7.0
WP-Optimize – Cache, Compress images, Minify & Clean database to boost page speed & performance v4.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
← All changes | js/heartbeat.js +112 -37 3.3.2 → 4.7.0 View file →
@@ -8,8 +8,9 @@
8 8 var WP_Optimize_Heartbeat = function () {
9 9 var $ = jQuery;
10 10 var agent_idle_ttl_in_seconds = 60; // retry after 60 seconds without a response
11 11 var wpo_fallback;
12 + var _setup = false;
12 13
13 14 /**
14 15 * Generate a unique ID to be used as agents IDs
15 16 *
@@ -30,52 +31,87 @@
30 31 *
31 32 * @returns {void}
32 33 */
33 34 function setup() {
34 - $(document).on('heartbeat-send', function(event, data) {
35 - for(var uid in WP_Optimize_Heartbeat_Agents) {
36 - if (!WP_Optimize_Heartbeat_Agents[uid].sent) {
37 - data[uid] = WP_Optimize_Heartbeat_Agents[uid].command;
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];
38 41
39 - WP_Optimize_Heartbeat_Agents[uid].sent = true;
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 + }
40 59 }
60 + });
41 61
42 - // Retry after idle time is passed (no response in X seconds)
43 - var seconds = ((new Date()).getTime() - WP_Optimize_Heartbeat_Agents[uid].sent_time.getTime()) / 1000;
44 - if (seconds > agent_idle_ttl_in_seconds) {
45 - WP_Optimize_Heartbeat_Agents[uid].sent = false;
46 - }
47 - }
48 - });
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 + }
49 72
50 - $(document).on('heartbeat-tick', function(event, data) {
51 - if ('object' == typeof(data.callbacks)) {
52 - for(var uid in data.callbacks) {
53 - if (is_wpo_heartbeat(uid)) {
54 - var response = JSON.parse(data.callbacks[uid]);
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 + }
55 80
56 - if ('undefined' != typeof(response.result) && false == response.result) {
57 - wp_optimize.notices.show_notice(response.error_code, response.error_message);
58 - } else {
59 - WP_Optimize_Heartbeat_Agents[uid].callback(response);
81 + if ('undefined' !== typeof WP_Optimize_Heartbeat_Agents[uid]) {
82 + delete WP_Optimize_Heartbeat_Agents[uid];
83 + }
60 84 }
61 -
62 - delete WP_Optimize_Heartbeat_Agents[uid];
63 85 }
64 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();
65 94 }
66 - });
67 -
68 - if (is_heartbeat_api_disabled()) {
69 - wpo_fallback = WP_Optimize_Heartbeat_Fallback();
70 95 }
71 96 }
72 97
73 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 + /**
74 110 * Check if heartbeat action is a WP-Optimize action or something else that we should ignore
75 111 *
76 112 * @param {string} uid The UID of the agent
77 - * @returns {bool}
113 + * @returns {boolean}
78 114 */
79 115 function is_wpo_heartbeat(uid) {
80 116 return 0 === uid.indexOf('wpo-heartbeat-');
81 117 }
@@ -82,12 +118,12 @@
82 118
83 119 /**
84 120 * Check if native heartbeat API is available
85 121 *
86 - * @returns {bool}
122 + * @returns {boolean}
87 123 */
88 124 function is_heartbeat_api_disabled() {
89 - return 'undefined' == typeof(wp.heartbeat);
125 + return 'undefined' === typeof(wp.heartbeat);
90 126 }
91 127
92 128 /**
93 129 * Filter function to check if an agent is already scheduled
@@ -93,49 +129,78 @@
93 129 * Filter function to check if an agent is already scheduled
94 130 *
95 131 * @param {object} agent1 First comparison agent, usually already scheduled agents
96 132 * @param {object} agent2 Second comparison agent, usually the one you are trying to check if it already exists
97 - * @returns {bool}
133 + * @returns {boolean}
98 134 */
99 135 function do_agents_match(agent1, agent2) {
100 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;
101 140 var command_not_sent_yet = false === agent1.sent;
102 141
103 - return command_matches && command_not_sent_yet;
142 + return command_matches && subaction_matches && command_not_sent_yet;
104 143 }
105 144
106 145 /**
107 146 * Add a heartbeat agent that will be sent to backend and has a callback to receive the response
108 147 *
109 - * @param {object} data Expected an object like {command: string}
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`
110 149 * @returns {string|null}
111 150 */
112 151 function add_agent(data) {
113 152 var already_scheduled = Object.values(WP_Optimize_Heartbeat_Agents).some(function(agent) { return do_agents_match(agent, data); });
114 - if (already_scheduled) {
153 + if (already_scheduled && ('undefined' == typeof(data._unique) || (true == data._unique))) {
115 154 return null;
116 155 }
117 156
118 157 var agent_id = 'wpo-heartbeat-' + guid();
119 158 data.sent = false;
120 - data.sent_time = new Date();
121 159 WP_Optimize_Heartbeat_Agents[agent_id] = data;
160 +
161 + if ('undefined' !== typeof(data._wait) && false === data._wait) {
162 + trigger_heartbeat();
163 + }
164 +
122 165 return agent_id;
123 166 }
124 167
125 168 /**
126 - * Remove agent from list
169 + * Trigger a heartbeat by code
127 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 + *
128 187 * @param {string} agent_id The id of the agent to be removed
129 188 * @returns {void}
130 189 */
131 190 function cancel_agent(agent_id) {
132 - delete WP_Optimize_Heartbeat_Agents[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 + }
133 197 }
134 198
135 199 return {
136 200 setup: setup,
137 201 add_agent: add_agent,
202 + cancel_agents: cancel_agents,
138 203 cancel_agent: cancel_agent
139 204 };
140 205 }
141 206
@@ -162,8 +227,10 @@
162 227 * @param {int} interval How many seconds until next heartbeat
163 228 * @returns {void}
164 229 */
165 230 function do_heartbeat(interval) {
231 + interval = 'undefined' == typeof(interval) ? payload.interval : interval;
232 +
166 233 var this_payload = Object.assign({}, payload);
167 234 var data = {};
168 235
169 236 jQuery(document).trigger('heartbeat-send', data);
@@ -181,9 +248,17 @@
181 248 }
182 249 }
183 250 });
184 251
252 + if (timeout_handler) {
253 + clearTimeout(timeout_handler);
254 + }
255 +
185 256 timeout_handler = setTimeout(do_heartbeat, interval * 1000, interval);
186 257 }
187 258
188 259 timeout_handler = setTimeout(do_heartbeat, payload.interval * 1000, payload.interval);
260 +
261 + return {
262 + do_heartbeat: do_heartbeat
263 + };
189 264 }