/*
	RSOC TRACKER JS file
*/


(function (window) {
	'use strict';

	var ENDPOINT = 'https://rsoc.d4t4.io/SearchInsights';
	var BATCH_INTERVAL = 2000;
	var MAX_BATCH_SIZE = 10;

	var pageLoadTime = Date.now();
	var pageType = 'other';
	var registeredContainers = [];
	var eventQueue = [];
	var flushTimer = null;
	var clickCount = 0;
	var impressions = {};
	var googleConfig = {};
	var lastFocusedContainer = null;
	var lastClickKey = null;
	var lastClickAt = 0;

	var SEARCH_CONVERSION_NAMES = {
		'google-csa-top': 'SerpGoogleSearchAdsClick',
		'google-csa-bottom': 'SerpGoogleSearchAdsClick',
		'related-searches-infeed': 'SerpGoogleRelatedClick',
		'related-searches-container': 'SerpGoogleRelatedClick',
		'keywords-results-container-top': 'KeywordsGoogleRelatedClick',
		'grs-top': 'SponsoredGoogleRelatedClick',
		'grs-mid': 'SponsoredGoogleRelatedClick',
		'grs-bottom': 'SponsoredGoogleRelatedClick',
	};

	function getRootWindow() {
		try {
			return window.top || window;
		} catch (e) {
			return window;
		}
	}

	function getSearchConversionName(containerId) {
		if (!containerId) {
			return 'fallbackClick';
		}

		return SEARCH_CONVERSION_NAMES[containerId] || 'fallbackClick';
	}

	var FUNNEL_CONTAINER_IDS = [
		'related-searches-container',
		'related-searches-infeed',
		'grs-top',
		'grs-mid',
		'grs-bottom',
		'keywords-results-container-top',
	];

	function isFunnelContainer(containerId) {
		if (!containerId) {
			return false;
		}

		var normalized = String(containerId).trim();

		if (normalized.indexOf('relatedsearches') === 0) {
			return true;
		}

		for (var i = 0; i < FUNNEL_CONTAINER_IDS.length; i++) {
			if (normalized === FUNNEL_CONTAINER_IDS[i]) {
				return true;
			}
		}

		return false;
	}

	function resolveTrackedContainer(data) {
		data = data || {};

		return (
			data.container ||
			lastFocusedContainer ||
			null
		);
	}

	function parseContainerFromIframeName(iframe) {
		if (!iframe || !iframe.name) {
			return null;
		}

		try {
			var iframeId = iframe.id;
			var parsedName = iframe.name;

			if (iframeId && parsedName.indexOf(iframeId + '|') === 0) {
				parsedName = parsedName.replace(iframeId + '|', '');
			}

			var nameData = JSON.parse(parsedName);

			if (
				iframeId &&
				nameData[iframeId] &&
				nameData[iframeId].container
			) {
				return nameData[iframeId].container;
			}

			for (var key in nameData) {
				if (
					Object.prototype.hasOwnProperty.call(nameData, key) &&
					nameData[key] &&
					nameData[key].container
				) {
					return nameData[key].container;
				}
			}
		} catch (e) {}

		return null;
	}

	function isGoogleInnerContainerId(id) {
		return (
			id.indexOf('afscontainer') === 0 || id.indexOf('relatedsearches') === 0
		);
	}

	function resolveClickContainer(el) {
		if (!el) {
			return null;
		}

		var node = el.parentElement;
		var depth = 0;
		var innerGoogleContainer = null;

		while (node && node !== document.body && depth < 25) {
			if (node.id) {
				if (registeredContainers.indexOf(node.id) !== -1) {
					return node.id;
				}

				if (isGoogleInnerContainerId(node.id)) {
					innerGoogleContainer = node.id;
				}
			}

			node = node.parentElement;
			depth++;
		}

		if (innerGoogleContainer) {
			node = el.parentElement;
			depth = 0;

			while (node && node !== document.body && depth < 25) {
				if (
					node.id &&
					registeredContainers.indexOf(node.id) !== -1 &&
					node.querySelector('#' + innerGoogleContainer)
				) {
					return node.id;
				}

				node = node.parentElement;
				depth++;
			}

			return innerGoogleContainer;
		}

		return null;
	}

	function rememberIframeFocus(iframe) {
		var container =
			parseContainerFromIframeName(iframe) || resolveClickContainer(iframe);

		if (container) {
			lastFocusedContainer = container;
		}

		return container;
	}

	function shouldDedupeClick(container) {
		var now = Date.now();
		var key = (container || 'unknown') + '_' + Math.floor(now / 500);

		if (lastClickKey === key && now - lastClickAt < 500) {
			return true;
		}

		lastClickKey = key;
		lastClickAt = now;
		return false;
	}

	function emitTrackedClick(clickData, source) {
		var container =
			clickData.container || lastFocusedContainer || 'unknown';

		if (shouldDedupeClick(container)) {
			return;
		}

		clickData.container = container;
		clickData.source = source || null;
		clickData.search_conversion_name = getSearchConversionName(container);

		if (pageType === 'serp' && !isFunnelContainer(container)) {
			trackSerpAdsClick(clickData);
		} else {
			trackFunnelClick(clickData);
		}

		flushEvents(false);
	}

	function buildClickPayload(extra) {
		return extend(
			{
				container: lastFocusedContainer,
				pub_id: googleConfig.pubId,
				style_id: googleConfig.styleId,
				channel: googleConfig.channel,
				query: googleConfig.query,
			},
			extra || {}
		);
	}

	function generateId() {
		return Date.now() + '_' + Math.random().toString(36).substr(2, 9);
	}

	function getSessionId() {
		try {
			var id = sessionStorage.getItem('rsoc_sid');
			if (!id) {
				id = 'sess_' + generateId();
				sessionStorage.setItem('rsoc_sid', id);
			}
			return id;
		} catch (e) {
			return 'sess_' + generateId();
		}
	}

	function getVisitorId() {
		try {
			var id = localStorage.getItem('rsoc_vid');
			if (!id) {
				id = 'vis_' + generateId();
				localStorage.setItem('rsoc_vid', id);
			}
			return id;
		} catch (e) {
			return 'vis_' + generateId();
		}
	}

	function parseParams(url) {
		var params = {};

		try {
			var parser = document.createElement('a');
			parser.href = url;
			var query = parser.search.substring(1);
			var pairs = query.split('&');

			for (var i = 0; i < pairs.length; i++) {
				var pair = pairs[i].split('=');
				if (pair[0]) {
					params[decodeURIComponent(pair[0])] = pair[1]
						? decodeURIComponent(pair[1].replace(/\+/g, ' '))
						: '';
				}
			}
		} catch (e) {}

		return params;
	}

	function extend(target, source) {
		if (!source) {
			return target;
		}

		for (var key in source) {
			if (Object.prototype.hasOwnProperty.call(source, key)) {
				target[key] = source[key];
			}
		}

		return target;
	}

	function getCommonData() {
		var params = parseParams(location.href);

		return {
			event_id: 'rsoc_' + generateId(),
			session_id: getSessionId(),
			visitor_id: getVisitorId(),
			timestamp: new Date().toISOString(),
			page_url: location.href,
			page_path: location.pathname,
			page_domain: location.hostname,
			page_title: document.title,
			page_type: pageType,
			referrer: document.referrer,
			utm_source: params.utm_source || null,
			utm_medium: params.utm_medium || null,
			utm_campaign: params.utm_campaign || null,
			campaign_id: params.campaign_id || null,
			sub_id: params.sub_id || null,
			apid: params.apid || null,
			kwb: params.kwb || null,
			fbpx: params.fbpx || null,
			ens: params.ens || null,
			spcid: params.spcid || null,
			us: params.us || null,
			gclid: params.gclid || null,
			search_query:
				params.kw || params.q || params.query || params.search || null,
			time_on_page: Math.round((Date.now() - pageLoadTime) / 1000),
			google_pub_id: googleConfig.pubId || null,
			google_style_id: googleConfig.styleId || null,
			google_channel: googleConfig.channel || null,
			google_query: googleConfig.query || null,
			google_afd_token: googleConfig.afdToken || null,
			google_fexp: googleConfig.fexp || null,
		};
	}

	function sendViaXhr(body) {
		try {
			var xhr = new XMLHttpRequest();
			xhr.open('POST', ENDPOINT, true);
			xhr.setRequestHeader('Content-Type', 'application/json');
			xhr.withCredentials = false;
			xhr.send(body);
		} catch (e) {}
	}

	function flushEvents(isUnload) {
		if (flushTimer) {
			clearTimeout(flushTimer);
			flushTimer = null;
		}

		if (!eventQueue.length) {
			return;
		}

		var eventsToSend = eventQueue.slice();
		eventQueue = [];
		var body = JSON.stringify({
			events: eventsToSend,
			sent_at: new Date().toISOString(),
		});

		if (typeof fetch === 'function') {
			fetch(ENDPOINT, {
				method: 'POST',
				headers: { 'Content-Type': 'application/json' },
				body: body,
				credentials: 'omit',
				mode: 'cors',
				keepalive: true,
			}).catch(function () {
				if (!isUnload) {
					sendViaXhr(body);
				}
			});
		} else {
			sendViaXhr(body);
		}
	}

	function sendEvent(eventName, eventData) {
		var payload = getCommonData();
		payload.event_name = eventName;
		payload.event_category = 'RSOC';
		extend(payload, eventData);

		eventQueue.push(payload);

		if (eventQueue.length >= MAX_BATCH_SIZE) {
			flushEvents(false);
			return payload;
		}

		if (!flushTimer) {
			flushTimer = setTimeout(function () {
				flushTimer = null;
				if (eventQueue.length) {
					flushEvents(false);
				}
			}, BATCH_INTERVAL);
		}

		return payload;
	}

	function setupClickTracking() {
		var rootWindow = getRootWindow();
		var rootDocument = rootWindow.document;

		try {
			rootWindow.focus();
		} catch (e) {}

		rootWindow.addEventListener('blur', function () {
			setTimeout(function () {
				var active = rootDocument.activeElement;

				if (active && active.nodeName === 'IFRAME') {
					rememberIframeFocus(active);

					setTimeout(function () {
						try {
							rootWindow.focus();
						} catch (e) {}
					}, 300);
				}
			}, 0);
		});

		window.addEventListener('message', function (event) {
			var data = event.data;

			if (
				!event ||
				!data ||
				typeof data !== 'string' ||
				data.indexOf('FSXDC,.aCS') !== 0
			) {
				return;
			}

			emitTrackedClick(
				buildClickPayload({
					origin: event.origin || null,
				}),
				'google_postmessage'
			);
		});
	}

	function containersToString(data) {
		if (data.containers && data.containers.length) {
			return data.containers.join('|');
		}

		return data.container || null;
	}

	function trackPageView(data) {
		data = data || {};

		sendEvent('rsoc_page_view', {
			is_serp: data.is_serp != null ? data.is_serp : pageType === 'serp',
			is_landing:
				data.is_landing != null ? data.is_landing : pageType === 'landing',
		});
	}

	function trackFunnelRequest(data) {
		data = data || {};

		sendEvent('rsoc_funnel_request', {
			container: containersToString(data),
			containers: data.containers || null,
			container_count: data.containers
				? data.containers.length
				: data.container
					? 1
					: 0,
			pub_id: data.pubId || data.pub_id || googleConfig.pubId,
			style_id: data.styleId || data.style_id || googleConfig.styleId,
			channel: data.channel || googleConfig.channel,
			query: data.query || data.kw || googleConfig.query,
			requested_terms: data.terms || data.requested_terms || null,
			max_terms: data.maxTerms || data.relatedSearches || null,
		});
	}

	function trackFunnelImpression(data) {
		data = data || {};
		var key = data.container || data.widget_id || 'default';

		if (impressions['funnel_' + key]) {
			return;
		}

		impressions['funnel_' + key] = true;

		var termPositions = data.term_positions || null;
		var returnedTerms = data.returned_terms || null;

		if (!returnedTerms && termPositions) {
			returnedTerms = Object.keys(termPositions);
		}

		sendEvent('rsoc_funnel_impression', {
			container: data.container || null,
			widget_id: data.widget_id || null,
			pub_id: data.pubId || data.pub_id || googleConfig.pubId,
			style_id: data.styleId || data.style_id || googleConfig.styleId,
			channel: data.channel || googleConfig.channel,
			query: data.query || data.kw || googleConfig.query,
			requested_terms: data.requested_terms || null,
			returned_terms: returnedTerms,
			term_positions: termPositions,
			terms_count:
				data.terms_count ||
				(returnedTerms ? returnedTerms.length : null),
			source: data.source || 'adLoadedCallback',
		});
	}

	function trackFunnelClick(data) {
		data = data || {};
		clickCount++;

		sendEvent('rsoc_funnel_click', {
			container: data.container || lastFocusedContainer || null,
			search_conversion_name:
				data.search_conversion_name ||
				getSearchConversionName(data.container || lastFocusedContainer),
			pub_id: data.pub_id || googleConfig.pubId,
			style_id: data.style_id || googleConfig.styleId,
			channel: data.channel || googleConfig.channel,
			clicked_keyword:
				data.clicked_keyword || data.keyword || googleConfig.query || null,
			keyword_position: data.keyword_position || data.position || null,
			query: data.query || googleConfig.query,
			afd_token: data.afd_token || googleConfig.afdToken,
			fexp: data.fexp || googleConfig.fexp,
			click_number: clickCount,
			time_to_click: Math.round((Date.now() - pageLoadTime) / 1000),
			origin: data.origin || null,
		});
	}

	function trackSerpAdsRequest(data) {
		data = data || {};

		if (data.containers && data.containers.length) {
			var serpContainers = [];
			var funnelContainers = [];

			for (var i = 0; i < data.containers.length; i++) {
				if (isFunnelContainer(data.containers[i])) {
					funnelContainers.push(data.containers[i]);
				} else {
					serpContainers.push(data.containers[i]);
				}
			}

			for (var j = 0; j < funnelContainers.length; j++) {
				trackFunnelRequest(
					extend({}, data, {
						container: funnelContainers[j],
						containers: null,
					})
				);
			}

			if (!serpContainers.length) {
				return;
			}

			data = extend({}, data, {
				containers: serpContainers,
			});
		} else if (isFunnelContainer(resolveTrackedContainer(data))) {
			trackFunnelRequest(data);
			return;
		}

		sendEvent('rsoc_serp_ads_request', {
			container: containersToString(data),
			containers: data.containers || null,
			container_count: data.containers
				? data.containers.length
				: data.container
					? 1
					: 0,
			pub_id: data.pubId || data.pub_id || googleConfig.pubId,
			style_id: data.styleId || data.style_id || googleConfig.styleId,
			channel: data.channel || googleConfig.channel,
			search_query:
				data.search_query ||
				data.query ||
				parseParams(location.href).kw ||
				parseParams(location.href).q ||
				null,
			max_top: data.maxTop || data.max_top || null,
			number: data.number || null,
		});
	}

	function trackSerpAdsImpression(data) {
		data = data || {};

		if (isFunnelContainer(data.container || data.widget_id)) {
			trackFunnelImpression(data);
			return;
		}

		var key = data.container || 'default';

		if (impressions['serp_' + key]) {
			return;
		}

		impressions['serp_' + key] = true;

		sendEvent('rsoc_serp_ads_impression', {
			container: data.container || null,
			widget_id: data.widget_id || null,
			pub_id: data.pubId || data.pub_id || googleConfig.pubId,
			style_id: data.styleId || data.style_id || googleConfig.styleId,
			channel: data.channel || googleConfig.channel,
			search_query:
				data.search_query ||
				data.query ||
				parseParams(location.href).kw ||
				parseParams(location.href).q ||
				null,
			ads_count: data.ads_count || null,
			source: data.source || 'adLoadedCallback',
		});
	}

	function trackSerpAdsClick(data) {
		data = data || {};

		if (isFunnelContainer(resolveTrackedContainer(data))) {
			trackFunnelClick(data);
			return;
		}

		clickCount++;

		sendEvent('rsoc_serp_ads_click', {
			container: data.container || lastFocusedContainer || null,
			search_conversion_name:
				data.search_conversion_name ||
				getSearchConversionName(data.container || lastFocusedContainer),
			pub_id: data.pub_id || googleConfig.pubId,
			style_id: data.style_id || googleConfig.styleId,
			channel: data.channel || googleConfig.channel,
			search_query:
				data.search_query ||
				data.query ||
				parseParams(location.href).kw ||
				parseParams(location.href).q ||
				null,
			ad_position: data.ad_position || data.position || null,
			advertiser: data.advertiser || null,
			advertiser_domain: data.advertiser_domain || null,
			ad_url: data.ad_url || null,
			ad_title: data.ad_title || null,
			afd_token: data.afd_token || googleConfig.afdToken,
			fexp: data.fexp || googleConfig.fexp,
			click_number: clickCount,
			time_to_click: Math.round((Date.now() - pageLoadTime) / 1000),
			origin: data.origin || null,
		});
	}

	function setupUnloadHandlers() {
		document.addEventListener('visibilitychange', function () {
			if (document.visibilityState === 'hidden') {
				flushEvents(true);
			}
		});
		window.addEventListener('pagehide', function () {
			flushEvents(true);
		});
	}

	function registerContainers(containers) {
		if (!containers || !containers.length) {
			return;
		}

		for (var i = 0; i < containers.length; i++) {
			if (registeredContainers.indexOf(containers[i]) === -1) {
				registeredContainers.push(containers[i]);
			}
		}
	}

	function init(options) {
		options = options || {};
		pageType = options.pageType || 'other';
		registeredContainers = options.containers || [];
		setupUnloadHandlers();
		setupClickTracking();
	}

	function setGoogleConfig(config) {
		extend(googleConfig, config || {});
	}

	window.RSOCInsights = {
		init: init,
		registerContainers: registerContainers,
		setGoogleConfig: setGoogleConfig,
		trackPageView: trackPageView,
		trackFunnelRequest: trackFunnelRequest,
		trackFunnelImpression: trackFunnelImpression,
		trackFunnelClick: trackFunnelClick,
		trackSerpAdsRequest: trackSerpAdsRequest,
		trackSerpAdsImpression: trackSerpAdsImpression,
		trackSerpAdsClick: trackSerpAdsClick,
		flush: function () {
			flushEvents(false);
		},
	};
})(window);

