/* eslint-disable */
/*
 * advanced ads functions to be used directly within ad codes
 */

/**
 * Polyfills
 */
( function () {
	if ( typeof window.CustomEvent !== 'function' ) {
		/**
		 * CustomEvent polyfill for IE11: https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent/CustomEvent
		 *
		 * @param {string} event  Event name.
		 * @param {Object} params Event parameters.
		 * @return {Object} Custom event.
		 */
		function CustomEvent( event, params ) {
			params = params || {
				bubbles: false,
				cancelable: false,
				detail: null,
			};
			const evt = document.createEvent( 'CustomEvent' );
			evt.initCustomEvent(
				event,
				params.bubbles,
				params.cancelable,
				params.detail
			);
			return evt;
		}

		window.CustomEvent = CustomEvent;
	}

	/**
	 * ReplaceWith polyfill for IE11: https://developer.mozilla.org/en-US/docs/Web/API/ChildNode/replaceWith
	 */
	function ReplaceWithPolyfill() {
		'use-strict'; // For safari, and IE > 10
		let parent = this.parentNode,
			i = arguments.length,
			currentNode;
		if ( ! parent ) {
			return;
		}
		if ( ! i ) {
			// if there are no arguments
			parent.removeChild( this );
		}
		while ( i-- ) {
			// i-- decrements i and returns the value of i before the decrement
			currentNode = arguments[ i ];
			if ( typeof currentNode !== 'object' ) {
				currentNode = this.ownerDocument.createTextNode( currentNode );
			} else if ( currentNode.parentNode ) {
				currentNode.parentNode.removeChild( currentNode );
			}
			// the value of "i" below is after the decrement
			if ( ! i ) {
				// if currentNode is the first argument (currentNode === arguments[0])
				parent.replaceChild( currentNode, this );
			} else {
				// if currentNode isn't the first
				parent.insertBefore( currentNode, this.nextSibling );
			}
		}
	}
	if ( ! Element.prototype.replaceWith ) {
		Element.prototype.replaceWith = ReplaceWithPolyfill;
	}
	if ( ! CharacterData.prototype.replaceWith ) {
		CharacterData.prototype.replaceWith = ReplaceWithPolyfill;
	}
	if ( ! DocumentType.prototype.replaceWith ) {
		DocumentType.prototype.replaceWith = ReplaceWithPolyfill;
	}

	/**
	 * Polyfill for NodeList.foreach() because we need to support IE11: https://developer.mozilla.org/en-US/docs/Web/API/NodeList/forEach
	 */
	if ( window.NodeList && ! NodeList.prototype.forEach ) {
		NodeList.prototype.forEach = function ( callback, thisArg ) {
			let i;
			const len = this.length;

			thisArg = thisArg || window;

			for ( i = 0; i < len; i++ ) {
				callback.call( thisArg, this[ i ], i, this );
			}
		};
	}
} )();

advads = {
	/**
	 * check if localstorage is supported/enabled by client
	 */
	supports_localstorage() {
		'use strict';
		try {
			if ( ! window || window.localStorage === undefined ) {
				return false;
			}
			// storage might be full or disabled
			window.localStorage.setItem( 'x', 'x' );
			window.localStorage.removeItem( 'x' );
			return true;
		} catch ( e ) {
			return false;
		}
	},
	/**
	 * check if the ad is displayed more than {max} times per session
	 * every check increases the counter
	 *
	 * @param {string} name (no id needed, just any id-formated string)
	 * @param {type}   max  number of maximum times the ad can be displayed within the period
	 * @return {bool} true if limit is reached
	 */
	max_per_session( name, max ) {
		let num = 1;
		if ( max === undefined || parseInt( max ) === 0 ) {
			max = 1;
		}

		// check if cookie exists and get the value
		if ( this.cookie_exists( name ) ) {
			if ( this.get_cookie( name ) >= max ) {
				return true;
			}
			num = num + parseInt( this.get_cookie( name ) );
		}
		this.set_cookie( name, num );
		return false;
	},
	/**
	 * increase a cookie with an integer value by 1
	 *
	 * @param {str} name   of the cookie
	 * @param {int} exdays days until cookie expires
	 */
	count_up( name, exdays ) {
		let num = 1;

		// check if cookie exists and get the value
		if ( this.cookie_exists( name ) ) {
			num = num + parseInt( this.get_cookie( name ) );
		}
		this.set_cookie( name, num );
	},
	/**
	 * return true, if cookie exists
	 * return false, if not
	 * if not exists, create it
	 * use case: to check if something already happened in this page impression
	 *
	 * @param {type} name
	 * @return {unresolved}
	 */
	set_cookie_exists( name ) {
		if ( get_cookie( name ) ) {
			return true;
		}
		set_cookie( name, '', 0 );
		return false;
	},
	/**
	 * get a cookie value
	 *
	 * @param {string} name of the cookie
	 * @return {string} decoded cookie value
	 */
	get_cookie( name ) {
		let i,
			x,
			y,
			ADVcookies = document.cookie.split( ';' );
		for ( i = 0; i < ADVcookies.length; i++ ) {
			x = ADVcookies[ i ].substr( 0, ADVcookies[ i ].indexOf( '=' ) );
			y = ADVcookies[ i ].substr( ADVcookies[ i ].indexOf( '=' ) + 1 );
			x = x.replace( /^\s+|\s+$/g, '' );
			if ( x === name ) {
				return decodeURIComponent( y );
			}
		}
	},
	/**
	 * set a cookie value
	 *
	 * @param {str} name   of the cookie
	 * @param {str} value  of the cookie
	 * @param {int} exdays days until cookie expires
	 *                     set 0 to expire cookie immidiatelly
	 *                     set null to expire cookie in the current session
	 * @param       path
	 * @param       domain
	 * @param       secure
	 */
	set_cookie( name, value, exdays, path, domain, secure ) {
		// days in seconds
		const expiry = exdays == null ? null : exdays * 24 * 60 * 60;
		this.set_cookie_sec( name, value, expiry, path, domain, secure );
	},
	/**
	 * set a cookie with expiry given in seconds
	 *
	 * @param {str} name   of the cookie
	 * @param {str} value  of the cookie
	 * @param {int} expiry seconds until cookie expires
	 *                     set 0 to expire cookie immidiatelly
	 *                     set null to expire cookie in the current session
	 * @param       path
	 * @param       domain
	 * @param       secure
	 */
	set_cookie_sec( name, value, expiry, path, domain, secure ) {
		const exdate = new Date();
		exdate.setSeconds( exdate.getSeconds() + parseInt( expiry ) );
		document.cookie =
			name +
			'=' +
			encodeURIComponent( value ) +
			( expiry == null ? '' : '; expires=' + exdate.toUTCString() ) +
			( path == null ? '; path=/' : '; path=' + path ) +
			( domain == null ? '' : '; domain=' + domain ) +
			( secure == null ? '' : '; secure' );
	},
	/**
	 * check if a cookie is set and contains a value
	 *
	 * @param {str} name of the cookie
	 * @return {bool} true, if cookie is set
	 */
	cookie_exists( name ) {
		const c_value = this.get_cookie( name );
		if ( c_value !== null && c_value !== '' && c_value !== undefined ) {
			return true;
		}
		return false;
	},
	/**
	 * move one element into another
	 *
	 * @param {str} element selector of the element that should be moved
	 * @param {str} target  selector of the element where to move
	 * @param {arr} options
	 */
	move( element, target, options ) {
		const el = jQuery( element );
		const target_string = target;

		if ( typeof options === 'undefined' ) {
			options = {};
		}
		if ( typeof options.css === 'undefined' ) {
			options.css = {};
		}
		if ( typeof options.method === 'undefined' ) {
			options.method = 'prependTo';
		}

		if (
			typeof options.offset !== 'undefined' &&
			( options.offset === 'right' || options.offset === 'left' )
		) {
			options.method = 'insertAfter';
		}

		// search for abstract target element
		if ( target === '' && typeof options.target !== 'undefined' ) {
			switch ( options.target ) {
				case 'wrapper': // wrapper
					var offset = 'left';
					if ( typeof options.offset !== 'undefined' ) {
						offset = options.offset;
					}
					target = this.find_wrapper( element, offset );
					break;
			}
		}

		// use only visible elements
		if ( typeof options.moveintohidden === 'undefined' ) {
			target = jQuery( target ).filter( ':visible' );
		} else {
			target = jQuery( target );
		}

		// print warning in console if the element appears multiple times
		if ( target.length > 1 ) {
			console.log(
				"Advanced Ads: element '" +
					target_string +
					"' found " +
					target.length +
					' times.'
			);
		}

		// switch insert method
		switch ( options.method ) {
			case 'insertBefore':
				el.insertBefore( target );
				break;
			case 'insertAfter':
				el.insertAfter( target );
				break;
			case 'appendTo':
				el.appendTo( target );
				break;
			case 'prependTo':
				el.prependTo( target );
				break;
			default:
				el.prependTo( target );
		}
	},

	/**
	 * Set 'relative' position for a parent element.
	 *
	 * @param {str} element selector
	 * @param       options
	 */
	set_parent_relative( element, options ) {
		var options = typeof options !== 'undefined' ? options : {};
		const el = jQuery( element );
		// give "position" style to parent element, if missing
		let parent = el.parent();

		if ( options.use_grandparent ) {
			parent = parent.parent();
		}

		if (
			parent.css( 'position' ) === 'static' ||
			parent.css( 'position' ) === ''
		) {
			parent.css( 'position', 'relative' );
		}
	},

	/**
	 * make an absolute position element fixed at the current position
	 * hint: use only after DOM is fully loaded in order to fix a wrong position
	 *
	 * @param {str} element selector
	 * @param {obj} options
	 */
	fix_element( element, options ) {
		var options = typeof options !== 'undefined' ? options : {};

		const el = jQuery( element );

		if ( options.use_grandparent ) {
			this.set_parent_relative( el.parent() );
		} else {
			this.set_parent_relative( el );
		}

		// fix element at current position
		// get position for hidden elements by showing them for a very short time
		if ( options.is_invisible ) {
			el.show();
		}
		const topoffset = parseInt( el.offset().top );
		const leftoffset = parseInt( el.offset().left );
		if ( options.is_invisible ) {
			el.hide();
		}
		if ( 'left' === options.offset ) {
			// Allow to scale the nested image down when it has `max-width: 100%` and touches the left edge of the viewport.
			const rightoffset =
				jQuery( window ).width() - leftoffset - el.outerWidth();
			el.css( 'position', 'fixed' )
				.css( 'top', topoffset + 'px' )
				.css( 'right', rightoffset + 'px' )
				.css( 'left', '' );
		} else {
			// reset "right" to prevent conflicts
			el.css( 'position', 'fixed' )
				.css( 'top', topoffset + 'px' )
				.css( 'left', leftoffset + 'px' )
				.css( 'right', '' );
		}
	},

	/**
	 * find the main wrapper
	 *  either id or first of its class
	 *
	 * @param {str} element selector
	 * @param {str} offset  which position of the offset to check (left or right)
	 *  @return {str} selector
	 */
	find_wrapper( element, offset ) {
		// first margin: auto element after body
		let returnValue;

		const excludes = [
			'script',
			'.screen-reader-text',
			'.skip-link',
			element,
			'.' + advancedAds.frontendPrefix + 'entity-placement',
		].join( ', ' );

		jQuery( 'body' )
			.children()
			.not( excludes )
			.each( function ( key, value ) {
				// check offset value
				const checkedelement = jQuery( value );
				// check if there is space left or right of the element
				if (
					( offset === 'right' &&
						checkedelement.offset().left +
							jQuery( checkedelement ).width() <
							jQuery( window ).width() ) ||
					( offset === 'left' && checkedelement.offset().left > 0 )
				) {
					// fix element
					if (
						checkedelement.css( 'position' ) === 'static' ||
						checkedelement.css( 'position' ) === ''
					) {
						checkedelement.css( 'position', 'relative' );
					}
					// set return value
					returnValue = value;
					return false;
				}
			} );
		return returnValue;
	},
	/**
	 * center fixed element on the screen
	 *
	 * @param {str} element selector
	 */
	center_fixed_element( element ) {
		const el = jQuery( element );
		// half window width minus half element width
		const left =
			jQuery( window ).width() / 2 - parseInt( el.css( 'width' ) ) / 2;
		el.css( 'left', left + 'px' );
	},
	/**
	 * center element vertically on the screen
	 *
	 * @param {str} element selector
	 */
	center_vertically( element ) {
		const el = jQuery( element );
		// half window height minus half element height
		let left =
			jQuery( window ).height() / 2 - parseInt( el.css( 'height' ) ) / 2;

		// Center correctly when the ad is attached to the element that begins lower.
		if ( el.css( 'position' ) !== 'fixed' ) {
			left -= topoffset = parseInt( el.offset().top );
		}
		el.css( 'top', left + 'px' );
	},
	/**
	 * close an ad and add a cookie
	 *
	 * @param {str} element selector
	 */
	close( element ) {
		const wrapper = jQuery( element );
		// remove the ad
		wrapper.remove();
	},
	/**
	 * Wait until images are ready.
	 *
	 * @param {obj}      $el            jQuery object.
	 * @param {Function} ready_callback Ready callback.
	 *                                  derrived from https://github.com/alexanderdickson/waitForImages/blob/master/dist/jquery.waitforimages.js
	 */
	wait_for_images( $el, ready_callback ) {
		let loaded_count = 0;
		const srcs = [];

		$el.find( 'img[src][src!=""]' ).each( function () {
			srcs.push( this.src );
		} );

		if ( srcs.length === 0 ) {
			ready_callback.call( $el );
		}

		jQuery.each( srcs, function ( i, src ) {
			const image = new Image();
			image.src = src;
			const events = 'load error';

			jQuery( image ).one( events, function me( event ) {
				// Remove remaining handler (either 'load' or 'error').
				jQuery( this ).off( events, me );
				loaded_count++;

				if ( loaded_count == srcs.length ) {
					ready_callback.call( $el[ 0 ] );
					return false;
				}
			} );
		} );
	},

	privacy: {
		state: 'unknown',
		state_executed: false,
		/**
		 * Get consent state.
		 * IIFE so the events fire only once per event.
		 *
		 * @return string
		 *     'not_needed' - consent is not needed.
		 *     'accepted' - consent was given.
		 *     'unknown' - consent was not given yet.
		 */
		get_state: ( function () {
			return function () {
				// if we already have a state, return that.
				if ( window.advads_options.privacy.state !== 'unknown' ) {
					// make sure this only gets executed once.
					if ( ! advads.privacy.state_executed ) {
						advads.privacy.state_executed = true;
						advads.privacy.dispatch_event(
							window.advads_options.privacy.state,
							false
						);
					}
					return advads.privacy.state;
				}

				// If using the cookie method, fire an initial event, regardless if cookie set or not.
				if (
					window.advads_options.privacy[ 'consent-method' ] ===
					'custom'
				) {
					var cookie_regex = new RegExp(
						'.*?' +
							window.advads_options.privacy[
								'custom-cookie-value'
							] +
							'[^;]*?'
					);
					const cookie =
						advads.get_cookie(
							window.advads_options.privacy[
								'custom-cookie-name'
							]
						) || '';

					// Force the event, if we haven't yet fired one.
					if ( ! advads.privacy.state_executed ) {
						advads.privacy.state_executed = true;
						advads.privacy.dispatch_event(
							cookie.match( cookie_regex )
								? 'accepted'
								: 'unknown',
							true
						);
					}
				}

				// make sure this only gets executed once.
				advads.privacy.state_executed = true;

				// Run this in an interval (every 0.3s) just in case we are still waiting for consent
				var cnt = 0,
					consentSetInterval = setInterval( function () {
						// Bail if we have not gotten a consent response after 60 seconds.
						if ( ++cnt === 181 ) {
							clearInterval( consentSetInterval );
						}
						switch (
							window.advads_options.privacy[ 'consent-method' ]
						) {
							case 'custom':
								const cookie =
									advads.get_cookie(
										window.advads_options.privacy[
											'custom-cookie-name'
										]
									) || '';

								// check if custom cookie is set and matches value.
								if ( cookie.match( cookie_regex ) ) {
									clearInterval( consentSetInterval );
									if ( advads.privacy.state !== 'accepted' ) {
										advads.privacy.dispatch_event(
											'accepted',
											true
										);
									}
								}
								break;

							case 'iab_tcf_20':
								// Check if window.__tcfapi has been set.
								if ( typeof window.__tcfapi === 'undefined' ) {
									return;
								}
								clearInterval( consentSetInterval );

								window.__tcfapi(
									'addEventListener',
									2,
									function ( TCData, listenerSuccess ) {
										if ( ! listenerSuccess ) {
											return;
										}
										if (
											TCData.eventStatus === 'tcloaded' ||
											TCData.eventStatus ===
												'useractioncomplete' ||
											// if this is google funding choices, eventStatus is not set. Check if either gdpr doesn't apply or if there is a purpose object.
											( TCData.eventStatus === null &&
												typeof window.googlefc !==
													'undefined' &&
												( typeof TCData.purpose !==
													'undefined' ||
													! TCData.gdprApplies ) )
										) {
											const userAction =
												TCData.eventStatus ===
												'useractioncomplete';
											if ( ! TCData.gdprApplies ) {
												if (
													advads.privacy.state !==
													'not_needed'
												) {
													advads.privacy.dispatch_event(
														'not_needed',
														userAction
													);
												}
												return;
											}

											if (
												TCData.purpose.consents[ 1 ]
											) {
												if (
													advads.privacy.state !==
													'accepted'
												) {
													advads.privacy.dispatch_event(
														'accepted',
														userAction
													);
												}
												return;
											}

											// fire another event, in case the user revokes the previous consent.
											if (
												advads.privacy.state !==
												'rejected'
											) {
												advads.privacy.dispatch_event(
													'rejected',
													userAction
												);
											}
										}
									}
								);
								break;
						}
					}, 333 );

				return advads.privacy.state;
			};
		} )(),
		/**
		 * Check if the privacy_method is custom cookie, and non personalized ads are allowed.
		 *
		 * @return {boolean}
		 */
		is_adsense_npa_enabled() {
			if ( ! window.advads_options || ! window.advads_options.privacy ) {
				return true;
			}
			return !! (
				window.advads_options.privacy[
					'show-non-personalized-adsense'
				] &&
				window.advads_options.privacy[ 'consent-method' ] === 'custom'
			);
		},
		/**
		 * Dispatch a custom event whenever the state changes.
		 *
		 * @param {string}  state      The current privacy state.
		 * @param {boolean} userAction This is result of action by user.
		 */
		dispatch_event( state, userAction ) {
			const previousState = advads.privacy.state,
				fire_event = function () {
					document.dispatchEvent(
						new CustomEvent( 'advanced_ads_privacy', {
							detail: {
								state,
								previousState,
								userAction,
							},
						} )
					);
				};

			advads.privacy.state = state;

			console.log( {
				state,
				previousState,
				userAction,
			} );

			window.advanced_ads_ready_queue.push( fire_event );
		},
		/**
		 * Check if ad is decoded.
		 *
		 * @param {integer} id
		 *
		 * @return {boolean}
		 */
		is_ad_decoded( id ) {
			return (
				document.querySelector(
					'script[data-tcf="waiting-for-consent"][data-id="' +
						id +
						'"]'
				) === null
			);
		},
		/**
		 * Decode ad content.
		 *
		 * @param {Element} el
		 * @param {boolean} [inject=true]
		 */
		decode_ad( el, inject ) {
			// this can also be a number if used in a foreach.
			inject = typeof inject === 'boolean' ? inject : true;
			const string = decodeURIComponent(
				Array.prototype.map
					.call( atob( el.textContent ), function ( c ) {
						return (
							'%' +
							( '00' + c.charCodeAt( 0 ).toString( 16 ) ).slice(
								-2
							)
						);
					} )
					.join( '' )
			);

			if ( ! inject ) {
				return string;
			}

			el.replaceWith(
				document.createRange().createContextualFragment( string )
			);
		},
	},
};

window.advanced_ads_ready_queue.push( advads.privacy.get_state );

document.addEventListener( 'advanced_ads_privacy', function ( event ) {
	if (
		( event.detail.state !== 'accepted' &&
			event.detail.state !== 'not_needed' ) ||
		event.detail.userAction ||
		document.readyState === 'loading'
	) {
		return;
	}

	// Find all scripts waiting for consent and decode them.
	document
		.querySelectorAll(
			'script[type="text/plain"][data-tcf="waiting-for-consent"]'
		)
		.forEach( advads.privacy.decode_ad );
} );
/* eslint-enable */
