Edit file File name : heartbeat.js Content :var WP_Optimize_Heartbeat_Agents = {}; /** * Attach to WordPress heartbeat API. Has a fallback method if core API is disabled * * @returns {object} WP_Optimize_Heartbeat exports */ var WP_Optimize_Heartbeat = function () { var $ = jQuery; var agent_idle_ttl_in_seconds = 60; // retry after 60 seconds without a response var wpo_fallback; var _setup = false; /** * Generate a unique ID to be used as agents IDs * * @returns {string} */ var guid = function() { var s4 = function() { return Math.floor((1 + Math.random()) * 0x10000) .toString(16) .substring(1); } //return id of format 'aaaaaaaa'-'aaaa'-'aaaa'-'aaaa'-'aaaaaaaaaaaa' return s4() + s4() + '-' + s4() + '-' + s4() + '-' + s4() + '-' + s4() + s4() + s4(); } /** * Configure the heartbeat events, if heartbeat API is missing, setup fallback * * @returns {void} */ function setup() { if (false === _setup) { _setup = true; $(document).on('heartbeat-send', function(event, data) { for(var uid in WP_Optimize_Heartbeat_Agents) { var agent = WP_Optimize_Heartbeat_Agents[uid]; if (!agent.sent) { if ('command_data' in agent) { data[uid] = {}; data[uid][agent.command] = agent.command_data; } else { data[uid] = agent.command; } agent.sent_time = new Date().getTime(); agent.sent = true; } // Retry after idle time is passed (no response in X seconds) var seconds = ((new Date()).getTime() - agent.sent_time) / 1000; if (seconds > agent_idle_ttl_in_seconds) { agent.sent = false; } } }); $(document).on('heartbeat-tick', function(event, data) { if ('object' == typeof(data.callbacks)) { for(var uid in data.callbacks) { if (is_wpo_heartbeat(uid)) { var response; try { response = JSON.parse(data.callbacks[uid]); } catch(e) { response = data.callbacks[uid]; } if ('undefined' != typeof(response.result) && false === response.result) { wp_optimize.notices.show_notice(response.error_code, response.error_message); } else { if ('undefined' !== typeof(WP_Optimize_Heartbeat_Agents[uid]) && WP_Optimize_Heartbeat_Agents[uid].callback instanceof Function) { WP_Optimize_Heartbeat_Agents[uid].callback(response); } } if ('undefined' !== typeof WP_Optimize_Heartbeat_Agents[uid]) { delete WP_Optimize_Heartbeat_Agents[uid]; } } } } }); if (is_heartbeat_api_disabled()) { wpo_fallback = WP_Optimize_Heartbeat_Fallback(); } else { // Some agents send `_wait:false` because the UI needs to execute that action quickly, `disableSuspend` allows for `connectNow` to trigger a heartbeat instantly wp.heartbeat.disableSuspend(); } } } /** * Cancel a group of agents all at once * * @param {array} agents_ids The list of agent ids to cancel */ function cancel_agents(agents_ids) { while(agent_id = agents_ids.shift()) { cancel_agent(agent_id); } } /** * Check if heartbeat action is a WP-Optimize action or something else that we should ignore * * @param {string} uid The UID of the agent * @returns {boolean} */ function is_wpo_heartbeat(uid) { return 0 === uid.indexOf('wpo-heartbeat-'); } /** * Check if native heartbeat API is available * * @returns {boolean} */ function is_heartbeat_api_disabled() { return 'undefined' === typeof(wp.heartbeat); } /** * Filter function to check if an agent is already scheduled * * @param {object} agent1 First comparison agent, usually already scheduled agents * @param {object} agent2 Second comparison agent, usually the one you are trying to check if it already exists * @returns {boolean} */ function do_agents_match(agent1, agent2) { var command_matches = agent1.command === agent2.command; var command_not_sent_yet = false === agent1.sent; return command_matches && command_not_sent_yet; } /** * Add a heartbeat agent that will be sent to backend and has a callback to receive the response * * @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` * @returns {string|null} */ function add_agent(data) { var already_scheduled = Object.values(WP_Optimize_Heartbeat_Agents).some(function(agent) { return do_agents_match(agent, data); }); if (already_scheduled && ('undefined' == typeof(data._unique) || (true == data._unique))) { return null; } var agent_id = 'wpo-heartbeat-' + guid(); data.sent = false; WP_Optimize_Heartbeat_Agents[agent_id] = data; if ('undefined' !== typeof(data._wait) && false === data._wait) { trigger_heartbeat(); } return agent_id; } /** * Trigger a heartbeat by code * * @returns {void} */ function trigger_heartbeat() { if (is_heartbeat_api_disabled()) { wpo_fallback.do_heartbeat(); } else { setTimeout(function() { wp.heartbeat.connectNow(); }, 50); } } /** * Remove agent from list if `_keep:false`. Defaults to `true`, not cancel * A method called cancel_agents that by default does not cancel anything is controversial, * but in practice only things like informational requests can be really cancelled, * otherwise you get strange inconsistent results when things get wiped out and callbacks are not being called. * * @param {string} agent_id The id of the agent to be removed * @returns {void} */ function cancel_agent(agent_id) { var agent = WP_Optimize_Heartbeat_Agents[agent_id]; if('undefined' != typeof(agent)) { if ('undefined' != typeof(agent._keep) && (false == agent._keep)) { delete WP_Optimize_Heartbeat_Agents[agent_id]; } } } return { setup: setup, add_agent: add_agent, cancel_agents: cancel_agents, cancel_agent: cancel_agent }; } /** * Fallback for the heartbeat api * * @returns {object} WP_Optimize_Heartbeat_Fallback exports */ var WP_Optimize_Heartbeat_Fallback = function() { var timeout_handler; var payload = { "data":{}, "interval":wpo_heartbeat_ajax.interval, "_nonce":wpo_heartbeat_ajax.nonce, "action":"heartbeat", "screen_id":window.pagenow, "has_focus":false }; /** * Actually trigger the standard AJAX call to run a heartbeat event * * @param {int} interval How many seconds until next heartbeat * @returns {void} */ function do_heartbeat(interval) { interval = 'undefined' == typeof(interval) ? payload.interval : interval; var this_payload = Object.assign({}, payload); var data = {}; jQuery(document).trigger('heartbeat-send', data); this_payload.data = data; jQuery.ajax({ type : "post", dataType : "json", url : wpo_heartbeat_ajax.ajaxurl, data : this_payload, success: function(response) { if('undefined' != typeof(response.callbacks)) { jQuery(document).trigger('heartbeat-tick', response); } } }); if (timeout_handler) { clearTimeout(timeout_handler); } timeout_handler = setTimeout(do_heartbeat, interval * 1000, interval); } timeout_handler = setTimeout(do_heartbeat, payload.interval * 1000, payload.interval); return { do_heartbeat: do_heartbeat }; } Save