- WordPress core y plugins - Tema Twenty Twenty-Four configurado - Plugin allow-unfiltered-html.php simplificado - .gitignore configurado para excluir wp-config.php y uploads 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
675 lines
19 KiB
JavaScript
Executable File
675 lines
19 KiB
JavaScript
Executable File
/*
|
|
* 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.
|
|
* @returne {object} Custom event.
|
|
*/
|
|
function CustomEvent ( event, params ) {
|
|
params = params || { bubbles: false, cancelable: false, detail: null };
|
|
var 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
|
|
var 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 ) {
|
|
var i;
|
|
var 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: function () {
|
|
"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
|
|
* @returns {bool} true if limit is reached
|
|
*/
|
|
max_per_session: function ( name, max ) {
|
|
var 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: function ( name, exdays ) {
|
|
var 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
|
|
* @returns {unresolved}
|
|
*/
|
|
set_cookie_exists: function ( 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: function ( name ) {
|
|
var 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
|
|
*/
|
|
set_cookie: function ( name, value, exdays, path, domain, secure ) {
|
|
// days in seconds
|
|
var 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
|
|
*/
|
|
set_cookie_sec: function ( name, value, expiry, path, domain, secure ) {
|
|
var 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
|
|
* @returns {bool} true, if cookie is set
|
|
*/
|
|
cookie_exists: function ( name ) {
|
|
var 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: function ( element, target, options ) {
|
|
|
|
var el = jQuery( element );
|
|
var target_string = target;
|
|
|
|
if ( typeof options === 'undefined' ) {
|
|
options = {};
|
|
}
|
|
if ( typeof options.css === 'undefined' ) {
|
|
options.css = {};
|
|
}
|
|
if ( typeof options.method === 'undefined' ) {
|
|
options.method = 'prependTo';
|
|
}
|
|
|
|
// 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
|
|
*/
|
|
set_parent_relative: function ( element, options ) {
|
|
var options = typeof options !== 'undefined' ? options : {};
|
|
var el = jQuery( element );
|
|
// give "position" style to parent element, if missing
|
|
var 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: function ( element, options ) {
|
|
var options = typeof options !== 'undefined' ? options : {};
|
|
|
|
var 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();
|
|
}
|
|
var topoffset = parseInt( el.offset().top );
|
|
var 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.
|
|
var 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: function ( element, offset ) {
|
|
// first margin: auto element after body
|
|
var returnValue;
|
|
|
|
jQuery( 'body' ).children().not('script, .screen-reader-text, .skip-link, ' + element).each( function ( key, value ) {
|
|
// check offset value
|
|
var 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: function ( element ) {
|
|
var el = jQuery( element );
|
|
// half window width minus half element width
|
|
var 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: function ( element ) {
|
|
var el = jQuery( element );
|
|
// half window height minus half element height
|
|
var 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: function ( element ) {
|
|
var 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: function ( $el, ready_callback ) {
|
|
var loaded_count = 0;
|
|
var 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 ) {
|
|
var image = new Image();
|
|
image.src = src;
|
|
var 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'] + '[^;]*?' );
|
|
let 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' :
|
|
let 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
|
|
) )
|
|
) {
|
|
var 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.
|
|
*
|
|
* @returns {boolean}
|
|
*/
|
|
is_adsense_npa_enabled: function () {
|
|
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: function ( state, userAction ) {
|
|
var previousState = advads.privacy.state,
|
|
fire_event = function () {
|
|
document.dispatchEvent( new CustomEvent( 'advanced_ads_privacy', {
|
|
detail: {
|
|
state: state,
|
|
previousState: previousState,
|
|
userAction: userAction
|
|
}
|
|
} ) );
|
|
};
|
|
|
|
advads.privacy.state = state;
|
|
|
|
console.log( {
|
|
state: state,
|
|
previousState: previousState,
|
|
userAction: userAction
|
|
} );
|
|
|
|
window.advanced_ads_ready_queue.push( fire_event );
|
|
},
|
|
/**
|
|
* Check if ad is decoded.
|
|
*
|
|
* @param {integer} id
|
|
*
|
|
* @returns {boolean}
|
|
*/
|
|
is_ad_decoded: function ( 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: function ( el, inject ) {
|
|
// this can also be a number if used in a foreach.
|
|
inject = typeof inject === 'boolean' ? inject : true;
|
|
var 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 );
|
|
} );
|