PluginProbe ʕ •ᴥ•ʔ
Advanced Ads – Ad Manager & AdSense / 2.0.5
Advanced Ads – Ad Manager & AdSense v2.0.5
2.0.26 2.0.25 2.0.24 2.0.23 2.0.22 2.0.21 1.38.0 1.39.0 1.39.1 1.39.2 1.39.3 1.39.4 1.4.0 1.4.1 1.4.2 1.4.3 1.4.4 1.4.5 1.4.6 1.4.7 1.4.8 1.4.9 1.40.0 1.40.1 1.40.2 1.41.0 1.42.0 1.42.1 1.42.2 1.43.0 1.44.0 1.44.1 1.45.0 1.45.1 1.46.0 1.47.0 1.47.1 1.47.2 1.47.3 1.47.4 1.47.5 1.48.0 1.48.1 1.49.0 1.5.0 1.5.0.1 1.5.1 1.5.2 1.5.2.1 1.5.4 1.5.4.1 1.5.5 1.50.0 1.51.0 1.51.1 1.51.2 1.51.3 1.52.0 1.52.1 1.52.2 1.52.3 1.52.4 1.53.0 1.53.1 1.53.2 1.54.0 1.54.1 1.55.0 1.56.0 1.56.1 1.56.2 1.56.3 1.56.4 1.6 1.6.1 1.6.10 1.6.10.1 1.6.10.2 1.6.11 1.6.11.1 1.6.12 1.6.13 1.6.14 1.6.15 1.6.16 1.6.17 1.6.17.1 1.6.17.2 1.6.2 1.6.2.1 1.6.3 1.6.4 1.6.4.1 1.6.5 1.6.6 1.6.6.1 1.6.7 1.6.7.1 1.6.8 1.6.8.1 1.6.8.2 1.6.8.3 1.6.9 1.6.9.1 1.6.9.2 1.6.9.3 1.6.9.4 1.7 1.7.0.1 1.7.0.2 1.7.0.3 1.7.1 1.7.1.1 1.7.1.2 1.7.1.3 1.7.1.4 1.7.1.5 1.7.10 trunk 1.7.11 1.0.1 1.7.12 1.0.2 1.7.13 1.0.3 1.7.14 1.1.0 1.7.15 1.1.1 1.7.16 1.1.2 1.7.17 1.1.3 1.7.18 1.10 1.7.19 1.10.1 1.7.2 1.10.10 1.7.2.1 1.10.11 1.7.20 1.10.12 1.7.21 1.10.2 1.7.22 1.10.3 1.7.23 1.10.4 1.7.24 1.10.5 1.7.25 1.10.6 1.7.3 1.10.7 1.7.4 1.10.8 1.7.4.1 1.10.9 1.7.4.2 1.11 1.7.4.3 1.11.1 1.7.4.4 1.11.2 1.7.4.5 1.12 1.7.5 1.13 1.7.5.1 1.13.1 1.7.6 1.13.2 1.7.7 1.13.3 1.7.8 1.13.4 1.7.9 1.13.5 1.7.9.1 1.13.6 1.7.9.2 1.13.7 1.7.9.3 1.13.8 1.8 1.14 1.8.1 1.14.1 1.8.10 1.14.10 1.8.11 1.14.11 1.8.12 1.14.2 1.8.13 1.14.3 1.8.14 1.14.4 1.8.15 1.14.5 1.8.16 1.14.6 1.8.17 1.14.7 1.8.18 1.14.8 1.8.19 1.14.9 1.8.2 1.15 1.8.20 1.16 1.8.21 1.16.1 1.8.22 1.17 1.8.23 1.17.1 1.8.24 1.17.10 1.8.25 1.17.10-rc.1 1.8.26 1.17.11 1.8.27 1.17.12 1.8.28 1.17.12-rc.1 1.8.29 1.17.2 1.8.3 1.17.3 1.8.30 1.17.4 1.8.4 1.17.5 1.8.5 1.17.6 1.8.6 1.17.7 1.8.7 1.17.8 1.8.8 1.17.9 1.8.9 1.17.9-beta.1 1.9 1.18.0 2.0.0 1.19.0 2.0.1 1.19.1 2.0.10 1.2 2.0.11 1.2.1 2.0.12 1.2.2 2.0.13 1.2.3 2.0.14 1.2.4 2.0.15 1.2.5 2.0.16 1.2.6 2.0.17 1.2.7 2.0.18 1.20.0 2.0.19 1.20.0-rc.1 2.0.2 1.20.0-rc.2 2.0.20 1.20.1 2.0.3 1.20.2 2.0.4 1.20.3 2.0.5 1.21.0 2.0.6 1.21.1 2.0.7 1.22.0 2.0.8 1.22.1 2.0.9 1.22.2 1.23.0 1.23.1 1.23.2 1.24.0 1.24.1 1.24.2 1.25.0 1.25.1 1.26.0 1.27.0 1.28.0 1.29.0 1.29.1 1.3 1.3.1 1.3.10 1.3.11 1.3.12 1.3.13 1.3.14 1.3.15 1.3.16 1.3.17 1.3.18 1.3.2 1.3.3 1.3.4 1.3.5 1.3.6 1.3.7 1.3.8 1.3.9 1.30.0 1.30.1 1.30.2 1.30.2-rc.1 1.30.3 1.30.4 1.30.4-rc.1 1.30.5 1.31.0 1.31.1 1.32.0 1.32.0-rc.1 1.33.0 1.33.1 1.33.2 1.34.0 1.35.0 1.35.1 1.36.0 1.36.1 1.36.2 1.36.3 1.37.0 1.37.1 1.37.2
advanced-ads / admin / assets / js / admin.js
advanced-ads / admin / assets / js Last commit date
adblocker-image-data.js 1 year ago admin-global.js 1 year ago admin.js 1 year ago admin.min.js 1 year ago advertisement.js 9 years ago conditions.js 1 year ago dialog-advads-modal.js 1 year ago groups.js 1 year ago termination.js 1 year ago ui.js 3 years ago wizard.js 1 year ago
admin.js
1946 lines
1 /* eslint-disable */
2 jQuery(document).ready(function ($) {
3 $(document).on('click', '#switch-to-adsense-type', function (ev) {
4 ev.preventDefault();
5 AdvancedAdsAdmin.AdImporter.adsenseCode =
6 Advanced_Ads_Admin.get_ad_source_editor_text();
7 $('#advanced-ad-type-adsense').trigger('click');
8 $(this).closest('li').addClass('hidden');
9 });
10
11 // activate general buttons
12 $('.advads-buttonset').advads_buttonset();
13 // activate accordions
14 if ($.fn.accordion) {
15 $('.advads-accordion').accordion({
16 active: false,
17 collapsible: true,
18 });
19 }
20
21 // AD OVERVIEW PAGE
22
23 $('.advads-ad-list-tooltip').advads_tooltip({
24 content() {
25 return jQuery(this).find('.advads-ad-list-tooltip-content').html();
26 },
27 });
28 // show edit icon in the last head column
29 $('.post-type-advanced_ads .wp-list-table thead th:last-of-type')
30 .append('<span class="dashicons dashicons-edit"></span>')
31 .on('click', function () {
32 $('#show-settings-link').trigger('click');
33 });
34
35 /**
36 * Logic for placement list
37 */
38 (() => {
39 let selectedValue = '0';
40 let searchTerm = '';
41 const placementRows = jQuery('.advads-placements-table tbody tr');
42 const showHidePlacementRow = (callback) => {
43 placementRows.each((index, element) => {
44 const $row = jQuery(element);
45 const rowData = $row.data('order');
46
47 if (
48 typeof rowData === 'undefined' ||
49 typeof rowData.type === 'undefined' ||
50 typeof rowData.name === 'undefined'
51 ) {
52 $row.show();
53 return;
54 }
55
56 $row.toggle(
57 (selectedValue === '0' || rowData.type === selectedValue) &&
58 (searchTerm === '' ||
59 rowData.name
60 .toLowerCase()
61 .indexOf(searchTerm.toLowerCase()) !== -1)
62 );
63 });
64 };
65 // filter placement by type
66 jQuery('.advads_filter_placement_type').on('change', function () {
67 selectedValue = jQuery(this).val();
68 showHidePlacementRow();
69 });
70
71 // search placement by name
72 jQuery('.advads_search_placement_name').on('keyup', function () {
73 searchTerm = this.value;
74 showHidePlacementRow();
75 });
76 })();
77
78 /**
79 * Filter ad/ad group selection in new placement form.
80 */
81 (() => {
82 const placementTypeRadios = document.querySelectorAll(
83 '[name="advads[placement][type]"]'
84 );
85
86 placementTypeRadios.forEach((radio) => {
87 radio.addEventListener('input', (event) => {
88 jQuery('[name="advads[placement][item]"]').attr(
89 'disabled',
90 true
91 );
92
93 wp.ajax
94 .post(window.advadstxt.placements_allowed_ads.action, {
95 _ajax_nonce:
96 window.advadstxt.placements_allowed_ads.nonce,
97 placement_type: event.target.value,
98 })
99 .done((response) => {
100 jQuery('[name="advads[placement][item]"]').replaceWith(
101 wp.template('advads-placement-ad-select')({
102 items: Object.values(response.items),
103 })
104 );
105 });
106 });
107 });
108 })();
109
110 jQuery('.advads-delete-tag').each(function () {
111 jQuery(this).on('click', function () {
112 const r = confirm(window.advadstxt.delete_placement_confirmation);
113 if (r === true) {
114 const row = jQuery(this).parents('.advanced-ads-placement-row');
115 row.find('.advads-placements-item-delete').prop(
116 'checked',
117 true
118 );
119 row.data('touched', true);
120 jQuery('#advanced-ads-placements-form').submit();
121 }
122 });
123 });
124
125 // sort placement by type order or name
126 jQuery('.advads-sort').on('click', function (e) {
127 const sort = jQuery(this);
128 const orderBy = sort.data('orderby');
129 const table = jQuery('.advads-placements-table');
130 const rows = jQuery('> tbody > tr', table);
131 const links = jQuery('> thead th > a', table);
132 links.each(function () {
133 jQuery(this).removeClass('advads-placement-sorted');
134 });
135 sort.addClass('advads-placement-sorted');
136 rows.sort(function (a, b) {
137 const orderA = jQuery(a).data('order');
138 const orderB = jQuery(b).data('order');
139
140 if (orderBy === 'type') {
141 if (
142 orderA['words-between-repeats'] !==
143 orderB['words-between-repeats']
144 ) {
145 return orderA['words-between-repeats'] ? 1 : -1;
146 }
147
148 if (orderA.order === orderB.order) {
149 // Sort by index.
150 if (
151 orderA['post-content-index'] &&
152 orderB['post-content-index'] &&
153 orderA['post-content-index'] !==
154 orderB['post-content-index']
155 ) {
156 return orderA['post-content-index'] <
157 orderB['post-content-index']
158 ? -1
159 : 1;
160 }
161 // Sort by name.
162 return orderA.name.localeCompare(orderB.name, undefined, {
163 numeric: true,
164 });
165 }
166 return orderA.order - orderB.order;
167 }
168
169 return orderA.name.localeCompare(orderB.name, undefined, {
170 numeric: true,
171 });
172 });
173 jQuery.each(rows, function (index, row) {
174 table.append(row);
175 });
176 let url = window.location.pathname + window.location.search;
177
178 if (url.indexOf('orderby=') !== -1) {
179 url = url.replace(
180 /\borderby=[0-9a-zA-Z_@.#+-]{1,50}\b/,
181 'orderby=' + orderBy
182 );
183 } else {
184 url += '&orderby=' + orderBy;
185 }
186 window.history.replaceState({ orderby: orderBy }, document.title, url);
187 e.preventDefault();
188 });
189
190 // show warning if Container ID option contains invalid characters
191 $('#advads-output-wrapper-id').on('keyup', function () {
192 const id_value = $(this).val();
193 if (/^[a-z-0-9]*$/.test(id_value)) {
194 $('.advads-output-wrapper-id-error').addClass('hidden');
195 } else {
196 $('.advads-output-wrapper-id-error').removeClass('hidden');
197 }
198 });
199
200 // show more than 3 ads when clicked on a link
201 $('.advads-group-ads-list-show-more').on('click', function () {
202 $(this)
203 .hide()
204 .parent()
205 .siblings('.advads-ad-group-list-ads')
206 .children('div')
207 .show();
208 });
209
210 /**
211 * SETTINGS PAGE
212 */
213
214 // automatically copy the first entered license key into all other empty fields
215 $('.advads-settings-tab-main-form .advads-license-key').on(
216 'blur',
217 function () {
218 // get number of license fields
219
220 const license_key = $(this).val();
221
222 if ('' === license_key) {
223 return;
224 }
225
226 const license_fields = $(
227 '.advads-settings-tab-main-form .advads-license-key'
228 );
229 const license_fields_without_value = [];
230
231 // count license fields without value
232 license_fields.each(function (i, el) {
233 if ('' === $(el).val()) {
234 license_fields_without_value.push(el);
235 }
236 });
237
238 // if there is only one field filled then take its content (probably a license key) and add it into the other fields
239 if (
240 license_fields.length ===
241 license_fields_without_value.length + 1
242 ) {
243 $.each(license_fields_without_value, function (i, el) {
244 $(el).val(license_key);
245 });
246 }
247 }
248 );
249
250 // activate licenses
251 $('.advads-license-activate').on('click', function () {
252 const button = $(this);
253
254 if (!this.dataset.addon) {
255 return;
256 }
257
258 advads_disable_license_buttons(true);
259
260 const query = {
261 action: 'advads-activate-license',
262 addon: this.dataset.addon,
263 pluginname: this.dataset.pluginname,
264 optionslug: this.dataset.optionslug,
265 license: $(this).parents('td').find('.advads-license-key').val(),
266 security: $('#advads-licenses-ajax-referrer').val(),
267 };
268
269 // show loader
270 $('<span class="spinner advads-spinner"></span>').insertAfter(button);
271
272 // send and close message
273 $.post(ajaxurl, query, function (r) {
274 // remove spinner
275 $('span.spinner').remove();
276 const parent = button.parents('td');
277
278 if (r === '1') {
279 const key = 'advanced-ads-licenses[' + query.addon + ']';
280 advadsTermination.setInitialValue(
281 key,
282 document.querySelector('[name="' + key + '"]')
283 );
284 parent.find('.advads-license-activate-error').remove();
285 parent.find('.advads-license-deactivate').show();
286 button.fadeOut();
287 parent.find('.advads-license-activate-active').fadeIn();
288 parent.find('input').prop('readonly', 'readonly');
289 advads_disable_license_buttons(false);
290 } else if (r === 'ex') {
291 const input = parent.find('input.advads-license-key');
292 const link = parent.find('a.advads-renewal-link');
293 if (input && link) {
294 const license_key = input.val();
295 const href = link.prop('href');
296 link.prop(
297 'href',
298 href.replace('%LICENSE_KEY%', license_key)
299 );
300 }
301 parent.find('.advads-license-activate-error').remove();
302 parent.find('.advads-license-expired-error').show();
303 advads_disable_license_buttons(false);
304 } else {
305 parent.find('.advads-license-activate-error').show().html(r);
306 advads_disable_license_buttons(false);
307 }
308 });
309 });
310
311 // deactivate licenses
312 $('.advads-license-deactivate').on('click', function () {
313 const button = $(this);
314
315 if (!this.dataset.addon) {
316 return;
317 }
318
319 advads_disable_license_buttons(true);
320
321 const query = {
322 action: 'advads-deactivate-license',
323 addon: this.dataset.addon,
324 pluginname: this.dataset.pluginname,
325 optionslug: this.dataset.optionslug,
326 security: $('#advads-licenses-ajax-referrer').val(),
327 };
328
329 // show loader
330 $('<span class="spinner advads-spinner"></span>').insertAfter(button);
331
332 // send and close message
333 $.post(ajaxurl, query, function (r) {
334 // remove spinner
335 $('span.spinner').remove();
336
337 if (r === '1') {
338 button.siblings('.advads-license-activate-error').hide();
339 button.siblings('.advads-license-activate-active').hide();
340 button.siblings('.advads-license-activate').show();
341 button.siblings('input').prop('readonly', false);
342 button.fadeOut();
343 advads_disable_license_buttons(false);
344 } else if (r === 'ex') {
345 button.siblings('.advads-license-activate-error').hide();
346 button.siblings('.advads-license-activate-active').hide();
347 button.siblings('.advads-license-expired-error').show();
348 button.siblings('input').prop('readonly', false);
349 button.fadeOut();
350 advads_disable_license_buttons(false);
351 } else {
352 console.log(r);
353 button
354 .siblings('.advads-license-activate-error')
355 .show()
356 .html(r);
357 button.siblings('.advads-license-activate-active').hide();
358 advads_disable_license_buttons(false);
359 }
360 });
361 });
362
363 // toggle license buttons – disable or not
364 function advads_disable_license_buttons(disable = true) {
365 const buttons = $(
366 'button.advads-license-activate, button.advads-license-deactivate'
367 ); // all activation buttons
368 // disable all buttons to prevent issues when users try to enable multiple licenses at the same time
369 if (disable) {
370 buttons.attr('disabled', 'disabled');
371 } else {
372 buttons.removeAttr('disabled');
373 }
374 }
375
376 /**
377 * There are two formats of URL supported:
378 * admin.php?page=advanced-ads-settings#top#tab_id go to the `tab_id`
379 * admin.php?page=advanced-ads-settings#tab_id__anchor go to the `tab_id`, scroll to the `anchor`
380 */
381
382 /**
383 * Extract the active tab and anchor from the URL hash.
384 *
385 * @param hash
386 * @member {string} hash The URL hash.
387 *
388 * @return {{tab: string, anchor: string}}
389 */
390 function advads_extract_tab(hash) {
391 const hash_parts = hash
392 .replace(/^#top(#|%23)/, '')
393 .replace(/(#|%23)/, '')
394 .split('__');
395
396 return {
397 tab: hash_parts[0] || jQuery('.advads-tab').attr('id'),
398 anchor: hash_parts[1],
399 };
400 }
401
402 /**
403 * Set the active tab and optionally scroll to the anchor.
404 * @param tab
405 */
406 function advads_set_tab(tab) {
407 jQuery('#advads-tabs').find('a').removeClass('nav-tab-active');
408 jQuery('.advads-tab').removeClass('active');
409
410 jQuery('#' + tab.tab).addClass('active');
411 jQuery('#' + tab.tab + '-tab').addClass('nav-tab-active');
412
413 if (tab.anchor) {
414 const anchor_offset = document
415 .getElementById(tab.anchor)
416 .getBoundingClientRect().top;
417 const admin_bar = 48;
418 window.scrollTo(0, anchor_offset + window.scrollY - admin_bar);
419 }
420 }
421
422 // While user is already on the Settings page, find links (in admin menu,
423 // in the Checks at the top, in the notices at the top) to particular setting tabs and open them on click.
424 jQuery(document).on(
425 'click',
426 'a[href*="page=advanced-ads-settings"]:not(.nav-tab)',
427 function () {
428 // Already on the Settings page, so set the new tab.
429 // Extract the tab id from the url.
430 const url = jQuery(this)
431 .attr('href')
432 .split('advanced-ads-settings')[1];
433 const tab = advads_extract_tab(url);
434 advads_set_tab(tab);
435 }
436 );
437
438 /**
439 * Handle the hashchange event, this enables back/forward navigation in the settings page.
440 */
441 window.addEventListener('hashchange', (event) => {
442 const hash = advads_extract_tab(new URL(event.newURL).hash);
443 try {
444 document
445 .getElementById(hash.tab + '-tab')
446 .dispatchEvent(new Event('click'));
447 } catch (e) {
448 // fail silently if element does not exist.
449 }
450 });
451
452 // activate specific or first tab
453
454 const active_tab = advads_extract_tab(window.location.hash);
455 advads_set_tab(active_tab);
456
457 // set all tab urls
458 advads_set_tab_hashes();
459
460 // dynamically generate the sub-menu
461 jQuery('.advads-tab-sub-menu').each(function (key, e) {
462 // abort if scrollIntoView is not supported; we can’t use anchors because they are used for tabs already
463 if (typeof e.scrollIntoView !== 'function') {
464 return;
465 }
466 // get all h2 headlines
467 advads_settings_parent_tab = jQuery(e).parent('.advads-tab');
468 const headlines = advads_settings_parent_tab.find('h2');
469 // create list
470 if (headlines.length > 1) {
471 advads_submenu_list = jQuery('<ul>');
472 headlines.each(function (key, h) {
473 // create anchor for this headline
474 const headline_id =
475 'advads-tab-headline-' +
476 advads_settings_parent_tab.attr('id') +
477 key;
478 jQuery(h).attr('id', headline_id);
479 // place the link in the top menu
480 var text = (text = h.textContent || h.innerText);
481 jQuery(
482 '<li><a onclick="document.getElementById(\'' +
483 headline_id +
484 '\').scrollIntoView()">' +
485 text +
486 '</a></li>'
487 ).appendTo(advads_submenu_list);
488 });
489 // place the menu
490 advads_submenu_list.appendTo(e);
491 }
492 });
493
494 // AD OVERVIEW LIST
495
496 // show the bulk actions sticky, when some lines are selected
497 $('.post-type-advanced_ads .check-column input[type="checkbox"]').on(
498 'change',
499 function () {
500 $(
501 '.post-type-advanced_ads .tablenav.bottom .bulkactions'
502 ).toggleClass(
503 'fixed',
504 0 <
505 $(
506 '.post-type-advanced_ads .check-column input[type="checkbox"]:checked'
507 ).length
508 );
509 }
510 );
511 // show screen options when clicking on our custom link or the Close button
512 $('#advads-show-screen-options').on('click', function () {
513 $('#show-settings-link').trigger('click');
514 });
515 // Add a close button to the screen options
516 $(
517 '<button type="button" class="button advads-button-secondary">' +
518 advadstxt.close +
519 '</button>'
520 )
521 .appendTo($('.post-type-advanced_ads #adv-settings .submit'))
522 .on('click', function () {
523 $('#show-settings-link').trigger('click');
524 });
525
526 /**
527 * PLACEMENTS
528 */
529 const set_touched_placement = function () {
530 const tr = $(this).closest('tr.advanced-ads-placement-row');
531 if (tr) {
532 tr.data('touched', true);
533 }
534 };
535
536 // keep track of placements that were changed
537 $(
538 'form#advanced-ads-placements-form input, #advanced-ads-placements-form select'
539 ).on('change', set_touched_placement);
540 $('form#advanced-ads-placements-form button').on(
541 'click',
542 set_touched_placement
543 );
544
545 // some special form elements overwrite the jquery listeners (or render them unusable in some strange way)
546 // to counter that and make it more robust in general, we now listen for mouseover events, that will
547 // only occur, when the settings of a placement are expanded (let's just assume this means editing)
548 $('form#advanced-ads-placements-form .advads-modal').on(
549 'mouseover',
550 set_touched_placement
551 );
552
553 // if the modal is canceled, remove the "touched" data again, since the user discarded any changes.
554 $(document).on('advads-modal-canceled', (event) => {
555 const $placementRow = $('#' + event.detail.modal_id).parents(
556 '.advanced-ads-placement-row'
557 );
558 if (!$placementRow.length) {
559 return;
560 }
561 $placementRow.data('touched', false);
562 });
563
564 // on submit remove placements that were untouched
565 $('form#advanced-ads-placements-form').on('submit', function () {
566 const grouprows = jQuery(
567 'form#advanced-ads-placements-form tr.advanced-ads-placement-row'
568 );
569 jQuery(
570 'form#advanced-ads-placements-form tr.advanced-ads-placement-row'
571 ).each(function (k, v) {
572 v = jQuery(v);
573 if (!v.data('touched')) {
574 v.find('input, select').each(function (k2, v2) {
575 v2 = jQuery(v2);
576 v2.prop('disabled', true);
577 });
578 }
579 });
580 });
581
582 // show input field for custom xpath rule when "custom" option is selected for Content placement
583 // iterate through all tag options of all placements
584 $('.advads-placements-content-tag').each(function () {
585 advads_show_placement_content_xpath_field(this);
586 });
587 // update xpath field when tag option changes
588 $('.advads-placements-content-tag').on('change', function () {
589 advads_show_placement_content_xpath_field(this);
590 });
591 /**
592 * show / hide input field for xpath rule
593 *
594 * @param tag field
595 * @param tag_field
596 */
597 function advads_show_placement_content_xpath_field(tag_field) {
598 // get the value of the content tag option
599 const tag = $(tag_field).find('option:selected').val();
600 // show or hide the next following custom xpath option
601 if ('custom' === tag) {
602 $(tag_field).next('.advads-placements-content-custom-xpath').show();
603 } else {
604 $(tag_field).next('.advads-placements-content-custom-xpath').hide();
605 }
606 }
607
608 // show tooltips for group type or placement type in forms
609 $('.advads-form-type').advads_tooltip({
610 content() {
611 return jQuery(this).find('.advads-form-description').html();
612 },
613 parent: ($target) => {
614 const modal = $target.parents('.advads-modal');
615
616 return modal.length ? '#' + modal[0].id : 'body';
617 },
618 });
619
620 /**
621 * On the placements and ad edit page, check if the form values have changed on beforeunload.
622 * On the settings page, additionally check for a tab change.
623 */
624 const advadsTermination = (() => {
625 let termination,
626 form,
627 submitted = false;
628 if (
629 window.advadstxt.admin_page ===
630 'advanced-ads_page_advanced-ads-placements'
631 ) {
632 form = document.getElementById('advanced-ads-placements-form');
633 if (form !== null) {
634 termination = new Advads_Termination(form);
635 }
636 }
637
638 if (window.advadstxt.admin_page === 'advanced_ads') {
639 // prevent errors on back/forward navigation
640 form = document.getElementById('post');
641 if (form !== null) {
642 termination = new Advads_Termination(form);
643 }
644 }
645
646 if (
647 window.advadstxt.admin_page ===
648 'advanced-ads_page_advanced-ads-settings'
649 ) {
650 form = document.querySelector('.advads-tab.active > form');
651 if (form !== null) {
652 termination = new Advads_Termination(form);
653 }
654 [...document.getElementsByClassName('nav-tab')].forEach((tab) => {
655 tab.addEventListener('click', (event) => {
656 if (!termination.terminationNotice()) {
657 event.preventDefault();
658 return termination;
659 }
660
661 advads_set_tab(
662 advads_extract_tab(new URL(event.target.href).hash)
663 );
664
665 form = document.querySelector('.advads-tab.active > form');
666 if (form !== null) {
667 termination = new Advads_Termination(form);
668 termination.collectValues();
669 // if the form is submitted, don't fire the beforeunload handler.
670 form.addEventListener('submit', () => {
671 submitted = true;
672 });
673 }
674 });
675 });
676 }
677
678 if (typeof termination !== 'undefined') {
679 termination.collectValues();
680 const beforeUnloadHandler = (event) => {
681 if (!submitted && !termination.terminationNotice()) {
682 event.preventDefault();
683 event.returnValue = 'string';
684 return termination;
685 }
686 };
687
688 window.addEventListener('beforeunload', beforeUnloadHandler);
689
690 // if the form is submitted, don't fire the beforeunload handler.
691 form.addEventListener('submit', () => {
692 submitted = true;
693 });
694 }
695 return termination;
696 })();
697
698 advancedAds.termination = advadsTermination;
699
700 /**
701 * Image ad uploader
702 */
703 $('body').on('click', '.advads_image_upload', function (e) {
704 e.preventDefault();
705
706 const button = $(this);
707
708 // If the media frame already exists, reopen it.
709 if (file_frame) {
710 // file_frame.uploader.uploader.param( 'post_id', set_to_post_id );
711 file_frame.open();
712 return;
713 }
714
715 // Create the media frame.
716 file_frame = wp.media.frames.file_frame = wp.media({
717 id: 'advads_type_image_wp_media',
718 title: button.data('uploaderTitle'),
719 button: {
720 text: button.data('uploaderButtonText'),
721 },
722 library: {
723 type: 'image',
724 },
725 multiple: false, // only allow one file to be selected
726 });
727
728 // When an image is selected, run a callback.
729 file_frame.on('select', function () {
730 const selection = file_frame.state().get('selection');
731 selection.each(function (attachment, index) {
732 attachment = attachment.toJSON();
733 if (0 === index) {
734 // place first attachment in field
735 $('#advads-image-id').val(attachment.id);
736 $(
737 '#advanced-ads-ad-parameters-size input[name="advanced_ad[width]"]'
738 ).val(attachment.width);
739 $(
740 '#advanced-ads-ad-parameters-size input[name="advanced_ad[height]"]'
741 ).val(attachment.height);
742 // update image preview
743 const new_image =
744 '<img width="' +
745 attachment.width +
746 '" height="' +
747 attachment.height +
748 '" title="' +
749 attachment.title +
750 '" alt="' +
751 attachment.alt +
752 '" src="' +
753 attachment.url +
754 '"/>';
755 $('#advads-image-preview').html(new_image);
756 $('#advads-image-edit-link').attr(
757 'href',
758 attachment.editLink
759 );
760 $('#advads-image-edit-link').removeClass('hidden');
761 // process "reserve this space" checkbox
762 $(
763 '#advanced-ads-ad-parameters-size input[type=number]:first'
764 ).trigger('change');
765 }
766 });
767 });
768
769 // Finally, open the modal
770 file_frame.open();
771 });
772
773 // WP 3.5+ uploader
774 let file_frame;
775 window.formfield = '';
776
777 // adblocker related code
778 $('#advanced-ads-use-adblocker').on('change', function () {
779 advads_toggle_box(this, '#advads-adblocker-wrapper');
780 });
781
782 // processing of the rebuild asset form and the FTP/SSH credentials form
783 let $advads_adblocker_wrapper = $('#advads-adblocker-wrapper'),
784 $advads_adblocker_rebuild_button = $('#advads-adblocker-rebuild');
785
786 $advads_adblocker_rebuild_button.prop('disabled', false);
787
788 $(document).on('click', '#advads-adblocker-rebuild', function (event) {
789 event.preventDefault();
790 const $form = $('#advanced-ads-rebuild-assets-form');
791 $form.prev('.error').remove();
792 $advads_adblocker_rebuild_button
793 .prop('disabled', true)
794 .after('<span class="spinner advads-spinner"></span>');
795
796 const args = {
797 data: {
798 action: 'advads-adblock-rebuild-assets',
799 nonce: advadsglobal.ajax_nonce,
800 },
801 done(data) {
802 $advads_adblocker_wrapper.html(data);
803 $advads_adblocker_rebuild_button = $(
804 '#advads-adblocker-rebuild'
805 );
806 },
807 fail(jqXHR, textStatus, errorThrown) {
808 $form.before(
809 '<div class="error"><p>' +
810 textStatus +
811 ': ' +
812 errorThrown +
813 '</p></div>'
814 );
815 $advads_adblocker_rebuild_button
816 .prop('disabled', false)
817 .next('.advads-spinner')
818 .remove();
819 },
820 on_modal_close() {
821 $advads_adblocker_rebuild_button
822 .prop('disabled', false)
823 .next('.advads-spinner')
824 .remove();
825 },
826 };
827
828 if ($('[name="advads_ab_assign_new_folder"]').is(':checked')) {
829 args.data.advads_ab_assign_new_folder = true;
830 }
831
832 advanced_ads_admin.filesystem.ajax(args);
833 });
834
835 // process "reserve this space" checkbox
836 $('#advanced-ads-ad-parameters').on(
837 'change',
838 '#advanced-ads-ad-parameters-size input[type=number]',
839 function () {
840 // Check if width and/or height is set.
841 if (
842 $('#advanced-ads-ad-parameters-size input[type=number]').filter(
843 function () {
844 return parseInt(this.value, 10) > 0;
845 }
846 ).length >= 1
847 ) {
848 $('#advads-wrapper-add-sizes').prop('disabled', false);
849 } else {
850 $('#advads-wrapper-add-sizes')
851 .prop('disabled', true)
852 .prop('checked', false);
853 }
854 }
855 );
856 // process "reserve this space" checkbox - ad type changed
857 $('#advanced-ads-ad-parameters').on('paramloaded', function () {
858 $('#advanced-ads-ad-parameters-size input[type=number]:first').trigger(
859 'change'
860 );
861 });
862 // process "reserve this space" checkbox - on load
863 $('#advanced-ads-ad-parameters-size input[type=number]:first').trigger(
864 'change'
865 );
866
867 // move meta box markup to hndle headline
868 $('.advads-hndlelinks').each(function () {
869 $(this).appendTo($(this).parents('.postbox').find('h2.hndle'));
870 $(this).removeClass('hidden');
871 });
872 // Open tutorial link when clicked on it in targeting metabox.
873 $('.advads-video-link').on('click', function (event) {
874 event.preventDefault();
875 const parent = $(event.target).closest('.postbox');
876 parent.removeClass('closed');
877 const videoContainer = parent.find('.advads-video-link-container');
878 videoContainer.html(videoContainer.data('videolink'));
879 });
880
881 // Find Adsense Auto Ads inside ad content.
882 const ad_content =
883 jQuery('textarea[name=advanced_ad\\[content\\]]').text() || '';
884 if (
885 ad_content.indexOf('enable_page_level_ads') !== -1 ||
886 /script[^>]+(?:data-ad-client=|adsbygoogle\.js\?client=)/.test(
887 ad_content
888 )
889 ) {
890 advads_show_adsense_auto_ads_warning();
891 }
892
893 advads_ads_txt_find_issues();
894
895 jQuery('.advanced-ads-adsense-dashboard').each(function (key, elm) {
896 if (Advanced_Ads_Adsense_Report_Helper) {
897 Advanced_Ads_Adsense_Report_Helper.init(elm);
898 }
899 });
900 });
901
902 function modal_submit_form(event, ID, modalID, validation = '') {
903 if (validation !== '' && !window[validation](modalID)) {
904 event.preventDefault();
905 return;
906 }
907 document.getElementById(ID).submit();
908 }
909
910 /**
911 * Store the action hash in settings form action
912 * thanks for Yoast SEO for this idea
913 */
914 function advads_set_tab_hashes() {
915 // iterate through forms
916 jQuery('#advads-tabs')
917 .find('a')
918 .each(function () {
919 const id = jQuery(this).attr('id').replace('-tab', '');
920 const optiontab = jQuery('#' + id);
921
922 const form = optiontab.children('.advads-settings-tab-main-form');
923 if (form.length) {
924 const currentUrl = form.attr('action').split('#')[0];
925 form.attr('action', currentUrl + jQuery(this).attr('href'));
926 }
927 });
928 }
929
930 /**
931 * Scroll to position in backend minus admin bar height
932 *
933 * @param selector jQuery selector
934 */
935 function advads_scroll_to_element(selector) {
936 const height_of_admin_bar = jQuery('#wpadminbar').height();
937 jQuery('html, body').animate(
938 {
939 scrollTop: jQuery(selector).offset().top - height_of_admin_bar,
940 },
941 1000
942 );
943 }
944
945 /**
946 * toggle content elements (hide/show)
947 *
948 * @param selector jquery selector
949 */
950 function advads_toggle(selector) {
951 jQuery(selector).slideToggle();
952 }
953
954 /**
955 * toggle content elements with a checkbox (hide/show)
956 *
957 * @param e
958 * @param selector jquery selector
959 */
960 function advads_toggle_box(e, selector) {
961 if (jQuery(e).is(':checked')) {
962 jQuery(selector).slideDown();
963 } else {
964 jQuery(selector).slideUp();
965 }
966 }
967
968 /**
969 * disable content of one box when selecting another
970 * only grey/disable it, don’t hide it
971 *
972 * @param e
973 * @param selector jquery selector
974 */
975 function advads_toggle_box_enable(e, selector) {
976 if (jQuery(e).is(':checked')) {
977 jQuery(selector).find('input').removeAttr('disabled', '');
978 } else {
979 jQuery(selector).find('input').attr('disabled', 'disabled');
980 }
981 }
982
983 /**
984 * Validate the form that creates a new group or placement.
985 * @param modalID
986 */
987 function advads_validate_new_form(modalID) {
988 // Check if type was selected
989 if (!jQuery('.advads-form-type input:checked').length) {
990 jQuery('.advads-form-type-error').show();
991 return false;
992 }
993 jQuery('.advads-form-type-error').hide();
994
995 // Check if name was entered
996 if (jQuery('.advads-form-name').val() == '') {
997 jQuery('.advads-form-name-error').show();
998 return false;
999 }
1000 jQuery('.advads-form-name-error').hide();
1001
1002 return true;
1003 }
1004
1005 /**
1006 * Submit only the current group. Submitting the form with all groups could otherwise cause a server timeout or PHP limit error.
1007 *
1008 * @param {string} modalID
1009 * @return {boolean}
1010 */
1011 function advads_group_edit_submit(modalID) {
1012 jQuery('.advads-ad-group-form')
1013 .filter((i, element) => !jQuery(element).parents(modalID).length)
1014 .remove();
1015
1016 return true;
1017 }
1018
1019 /**
1020 * replace textarea with TinyMCE editor for Rich Content ad type
1021 * @param ad_type
1022 */
1023 function advads_maybe_textarea_to_tinymce(ad_type) {
1024 let textarea = jQuery('#advads-ad-content-plain'),
1025 textarea_html = textarea.val(),
1026 tinymce_id = 'advanced-ads-tinymce',
1027 tinymce_id_ws = jQuery('#' + tinymce_id),
1028 tinymce_wrapper_div = jQuery('#advanced-ads-tinymce-wrapper');
1029
1030 if (ad_type !== 'content') {
1031 tinymce_id_ws.prop('name', tinymce_id);
1032 tinymce_wrapper_div.hide();
1033 return false;
1034 }
1035
1036 if (typeof tinyMCE === 'object' && tinyMCE.get(tinymce_id) !== null) {
1037 // visual mode
1038 if (textarea_html) {
1039 // see BeforeSetContent in the wp-includes\js\tinymce\plugins\wordpress\plugin.js
1040 const wp = window.wp,
1041 hasWpautop =
1042 wp &&
1043 wp.editor &&
1044 wp.editor.autop &&
1045 tinyMCE.get(tinymce_id).getParam('wpautop', true);
1046 if (hasWpautop) {
1047 textarea_html = wp.editor.autop(textarea_html);
1048 }
1049 tinyMCE.get(tinymce_id).setContent(textarea_html);
1050 }
1051 textarea.remove();
1052 tinymce_id_ws.prop('name', textarea.prop('name'));
1053 tinymce_wrapper_div.show();
1054 } else if (tinymce_id_ws.length) {
1055 // text mode
1056 tinymce_id_ws.val(textarea_html);
1057 textarea.remove();
1058 tinymce_id_ws.prop('name', textarea.prop('name'));
1059 tinymce_wrapper_div.show();
1060 }
1061 }
1062
1063 /**
1064 * Show a message depending on whether Adsense Auto ads are enabled.
1065 */
1066 function advads_show_adsense_auto_ads_warning() {
1067 $msg = jQuery('.advads-auto-ad-in-ad-content').show();
1068 $msg.on('click', 'button', function () {
1069 $msg.hide();
1070 jQuery
1071 .ajax({
1072 type: 'POST',
1073 url: ajaxurl,
1074 data: {
1075 action: 'advads-adsense-enable-pla',
1076 nonce: advadsglobal.ajax_nonce,
1077 },
1078 })
1079 .done(function (data) {
1080 $msg.show().html(advadstxt.page_level_ads_enabled);
1081 })
1082 .fail(function (jqXHR, textStatus) {
1083 $msg.show();
1084 });
1085 });
1086 }
1087
1088 /**
1089 * Check if a third-party ads.txt file exists.
1090 */
1091 function advads_ads_txt_find_issues() {
1092 const $wrapper = jQuery('#advads-ads-txt-notice-wrapper');
1093 const $refresh = jQuery('#advads-ads-txt-notice-refresh');
1094 const $actions = jQuery('.advads-ads-txt-action');
1095
1096 /**
1097 * Toggle the visibility of the spinner.
1098 *
1099 * @param {Bool} state True to show, False to hide.
1100 */
1101 function set_loading(state) {
1102 $actions.toggle(!state);
1103 if (state) {
1104 $wrapper.html('<span class="spinner advads-spinner"></span>');
1105 }
1106 }
1107
1108 if (!$wrapper.length) {
1109 return;
1110 }
1111
1112 if (!$wrapper.find('ul').length) {
1113 // There are no notices. Fetch them using ajax.
1114 load('get_notices');
1115 }
1116
1117 $refresh.on('click', function () {
1118 load('get_notices');
1119 });
1120
1121 function done(response) {
1122 $wrapper.html(response.notices);
1123 set_loading(false);
1124 }
1125
1126 function fail(jqXHR) {
1127 $wrapper.html(
1128 '<p class="advads-notice-inline advads-error">' +
1129 jQuery('#advads-ads-txt-notice-error')
1130 .text()
1131 .replace('%s', parseInt(jqXHR.status, 10)),
1132 +'</p>'
1133 );
1134 set_loading(false);
1135 }
1136
1137 function load(type) {
1138 set_loading(true);
1139
1140 jQuery
1141 .ajax({
1142 type: 'POST',
1143 url: ajaxurl,
1144 data: {
1145 action: 'advads-ads-txt',
1146 nonce: advadsglobal.ajax_nonce,
1147 type,
1148 },
1149 })
1150 .done(done)
1151 .fail(fail);
1152 }
1153
1154 jQuery(document).on(
1155 'click',
1156 '#advads-ads-txt-remove-real',
1157 function (event) {
1158 event.preventDefault();
1159
1160 const args = {
1161 data: {
1162 action: 'advads-ads-txt',
1163 nonce: advadsglobal.ajax_nonce,
1164 type: 'remove_real_file',
1165 },
1166 done(response) {
1167 if (response.additional_content) {
1168 jQuery('#advads-ads-txt-additional-content').val(
1169 response.additional_content
1170 );
1171 }
1172 done(response);
1173 },
1174 fail,
1175 before_send() {
1176 set_loading(true);
1177 },
1178 };
1179
1180 advanced_ads_admin.filesystem.ajax(args);
1181 }
1182 );
1183
1184 jQuery(document).on(
1185 'click',
1186 '#advads-ads-txt-create-real',
1187 function (event) {
1188 event.preventDefault();
1189
1190 const args = {
1191 data: {
1192 action: 'advads-ads-txt',
1193 nonce: advadsglobal.ajax_nonce,
1194 type: 'create_real_file',
1195 },
1196 done,
1197 fail,
1198 before_send() {
1199 set_loading(true);
1200 },
1201 };
1202
1203 advanced_ads_admin.filesystem.ajax(args);
1204 }
1205 );
1206 }
1207
1208 window.advanced_ads_admin = window.advanced_ads_admin || {};
1209 advanced_ads_admin.filesystem = {
1210 /**
1211 * Holds the current job while the user writes data in the 'Connection Information' modal.
1212 *
1213 * @type {obj}
1214 */
1215 _locked_job: false,
1216
1217 /**
1218 * Toggle the 'Connection Information' modal.
1219 */
1220 _requestForCredentialsModalToggle() {
1221 this.$filesystemModal.toggle();
1222 jQuery('body').toggleClass('modal-open');
1223 },
1224
1225 _init() {
1226 this._init = function () {};
1227 const self = this;
1228
1229 self.$filesystemModal = jQuery('#advanced-ads-rfc-dialog');
1230 /**
1231 * Sends saved job.
1232 */
1233 self.$filesystemModal.on('submit', 'form', function (event) {
1234 event.preventDefault();
1235
1236 self.ajax(self._locked_job, true);
1237 self._requestForCredentialsModalToggle();
1238 });
1239
1240 /**
1241 * Closes the request credentials modal when clicking the 'Cancel' button.
1242 */
1243 self.$filesystemModal.on(
1244 'click',
1245 '[data-js-action="close"]',
1246 function () {
1247 if (
1248 jQuery.isPlainObject(self._locked_job) &&
1249 self._locked_job.on_modal_close
1250 ) {
1251 self._locked_job.on_modal_close();
1252 }
1253
1254 self._locked_job = false;
1255 self._requestForCredentialsModalToggle();
1256 }
1257 );
1258 },
1259
1260 /**
1261 * Sends AJAX request. Shows 'Connection Information' modal if needed.
1262 *
1263 * @param {Object} args
1264 * @param {bool} skip_modal
1265 */
1266 ajax(args, skip_modal) {
1267 this._init();
1268
1269 if (!skip_modal && this.$filesystemModal.length > 0) {
1270 this._requestForCredentialsModalToggle();
1271 this.$filesystemModal.find('input:enabled:first').focus();
1272
1273 // Do not send request.
1274 this._locked_job = args;
1275 return;
1276 }
1277
1278 const options = {
1279 method: 'POST',
1280 url: window.ajaxurl,
1281 data: {
1282 username: jQuery('#username').val(),
1283 password: jQuery('#password').val(),
1284 hostname: jQuery('#hostname').val(),
1285 connection_type: jQuery(
1286 'input[name="connection_type"]:checked'
1287 ).val(),
1288 public_key: jQuery('#public_key').val(),
1289 private_key: jQuery('#private_key').val(),
1290 _fs_nonce: jQuery('#_fs_nonce').val(),
1291 },
1292 };
1293
1294 if (args.before_send) {
1295 args.before_send();
1296 }
1297
1298 options.data = jQuery.extend(options.data, args.data);
1299 const request = jQuery.ajax(options);
1300
1301 if (args.done) {
1302 request.done(args.done);
1303 }
1304
1305 if (args.fail) {
1306 request.fail(args.fail);
1307 }
1308 },
1309 };
1310
1311 window.Advanced_Ads_Admin = window.Advanced_Ads_Admin || {
1312 /**
1313 * Change the user id to the current user and save the post.
1314 *
1315 * @param {int} user_id
1316 */
1317 reassign_ad(user_id) {
1318 let $authorBox = jQuery('#post_author_override');
1319 if (!$authorBox.length) {
1320 $authorBox = jQuery('#post_author');
1321 }
1322
1323 $authorBox.val(user_id).queue(() => {
1324 jQuery('#post').submit();
1325 });
1326 },
1327
1328 /**
1329 * Toggle placement advanced options.
1330 *
1331 * @param elm
1332 * @param state
1333 * @deprecated. Used only by add-ons when the base plugin version < 1.19.
1334 */
1335 toggle_placements_visibility(elm, state) {
1336 const advadsplacementformrow = jQuery(elm).next(
1337 '.advads-placements-advanced-options'
1338 );
1339
1340 const hide =
1341 typeof state !== 'undefined'
1342 ? !state
1343 : advadsplacementformrow.is(':visible');
1344 if (hide) {
1345 advadsplacementformrow.hide();
1346 // clear last edited id
1347 jQuery('#advads-last-edited-placement').val('');
1348 } else {
1349 const placement_id = jQuery(elm)
1350 .parents('.advads-placements-table-options')
1351 .find('.advads-placement-id')
1352 .val();
1353 advadsplacementformrow.show();
1354 jQuery('#advads-last-edited-placement').val(placement_id);
1355 // Some special elements (color picker) may not be detected with jquery.
1356 const tr = jQuery(elm).closest('tr.advanced-ads-placement-row');
1357 if (tr) {
1358 tr.data('touched', true);
1359 }
1360 }
1361 },
1362
1363 /**
1364 * get a cookie value
1365 *
1366 * @param {str} name of the cookie
1367 */
1368 get_cookie(name) {
1369 let i,
1370 x,
1371 y,
1372 ADVcookies = document.cookie.split(';');
1373 for (i = 0; i < ADVcookies.length; i++) {
1374 x = ADVcookies[i].substr(0, ADVcookies[i].indexOf('='));
1375 y = ADVcookies[i].substr(ADVcookies[i].indexOf('=') + 1);
1376 x = x.replace(/^\s+|\s+$/g, '');
1377 if (x === name) {
1378 return unescape(y);
1379 }
1380 }
1381 },
1382
1383 /**
1384 * set a cookie value
1385 *
1386 * @param {str} name of the cookie
1387 * @param {str} value of the cookie
1388 * @param {int} exdays days until cookie expires
1389 * set 0 to expire cookie immidiatelly
1390 * set null to expire cookie in the current session
1391 * @param path
1392 * @param domain
1393 * @param secure
1394 */
1395 set_cookie(name, value, exdays, path, domain, secure) {
1396 // days in seconds
1397 const expiry = exdays == null ? null : exdays * 24 * 60 * 60;
1398 this.set_cookie_sec(name, value, expiry, path, domain, secure);
1399 },
1400 /**
1401 * set a cookie with expiry given in seconds
1402 *
1403 * @param {str} name of the cookie
1404 * @param {str} value of the cookie
1405 * @param {int} expiry seconds until cookie expires
1406 * set 0 to expire cookie immidiatelly
1407 * set null to expire cookie in the current session
1408 * @param path
1409 * @param domain
1410 * @param secure
1411 */
1412 set_cookie_sec(name, value, expiry, path, domain, secure) {
1413 const exdate = new Date();
1414 exdate.setSeconds(exdate.getSeconds() + parseInt(expiry));
1415 document.cookie =
1416 name +
1417 '=' +
1418 escape(value) +
1419 (expiry == null ? '' : '; expires=' + exdate.toUTCString()) +
1420 (path == null ? '; path=/' : '; path=' + path) +
1421 (domain == null ? '' : '; domain=' + domain) +
1422 (secure == null ? '' : '; secure');
1423 },
1424 };
1425
1426 if (!window.AdvancedAdsAdmin) window.AdvancedAdsAdmin = {};
1427 if (!window.AdvancedAdsAdmin.AdImporter)
1428 window.AdvancedAdsAdmin.AdImporter = {
1429 /**
1430 * will highlight the currently selected ads in the list
1431 * @param hideInactive when true will hide inactive ad units
1432 * @return the selector of the selected row or false if no row was selected.
1433 */
1434 highlightSelectedRowInExternalAdsList(hideInactive) {
1435 if (typeof hideInactive === 'undefined')
1436 hideInactive = AdvancedAdsAdmin.AdImporter.adNetwork.hideIdle;
1437 const tbody = jQuery('#mapi-table-wrap tbody');
1438 const btn = jQuery('#mapi-toggle-idle');
1439
1440 // count the ad units to determine if there's a need for the overflow class (scrolling)
1441 const nbUnits = hideInactive
1442 ? jQuery('#mapi-table-wrap tbody tr[data-active=1]').length
1443 : jQuery('#mapi-table-wrap tbody tr').length;
1444 if (nbUnits > 8) jQuery('#mapi-table-wrap').addClass('overflow');
1445 else jQuery('#mapi-table-wrap').removeClass('overflow');
1446
1447 const selectedRow = AdvancedAdsAdmin.AdImporter.getSelectedRow();
1448 tbody.find('tr').removeClass('selected error');
1449 if (selectedRow) {
1450 //make sure, it is visible before applying the zebra stripes
1451 selectedRow.show();
1452 }
1453
1454 // make the table's rows striped.
1455 const visible = tbody.find('tr:visible');
1456 visible.filter(':odd').css('background-color', '#FFFFFF');
1457 visible.filter(':even').css('background-color', '#F9F9F9');
1458
1459 // highlight the selected row
1460 if (selectedRow) {
1461 // highlight the selected row
1462 selectedRow.css('background-color', '');
1463 selectedRow.addClass('selected');
1464
1465 this.scrollToSelectedRow(selectedRow);
1466 }
1467
1468 return selectedRow || false;
1469 },
1470
1471 scrollToSelectedRow($selectedRow) {
1472 const $wrap = jQuery('#mapi-table-wrap'),
1473 wrapHeight = $wrap.height(),
1474 wrapScrolled = $wrap.scrollTop();
1475
1476 // just in case this does not get passed a selected row, scroll to top of the table
1477 if (!$selectedRow) {
1478 $wrap.animate({ scrollTop: 0 }, 200);
1479 return;
1480 }
1481
1482 // get the position of the selectedRow within the table wrap
1483 let scroll = $selectedRow.position().top,
1484 bottom = +scroll + $selectedRow.height();
1485 // if the (top of the) element is not yet visible scroll to it
1486 if (
1487 scroll < wrapScrolled ||
1488 bottom > wrapScrolled ||
1489 scroll > wrapScrolled + wrapHeight
1490 ) {
1491 // scrolled element is below current scroll position, i.e. we need to scroll past it not to top
1492 if (bottom > $wrap.children('table').height() - wrapHeight) {
1493 scroll = bottom;
1494 }
1495
1496 // if the selected element is on the "first page" let's scroll all the way to the top
1497 if (scroll < wrapHeight) {
1498 scroll = 0;
1499 }
1500
1501 $wrap.animate({ scrollTop: scroll }, 200);
1502 }
1503 },
1504
1505 getSelectedRow() {
1506 const selectedId =
1507 AdvancedAdsAdmin.AdImporter.adNetwork.getSelectedId();
1508 const tbody = jQuery('#mapi-table-wrap tbody');
1509
1510 if (selectedId) {
1511 const selectedRows = tbody.find(
1512 'tr[data-slotid="' + selectedId + '"]'
1513 );
1514 if (selectedRows.length) {
1515 return selectedRows;
1516 }
1517 }
1518 return null;
1519 },
1520 openExternalAdsList() {
1521 const network = AdvancedAdsAdmin.AdImporter.adNetwork;
1522 network.openSelector();
1523
1524 jQuery('.mapi-insert-code').css('display', 'inline');
1525 jQuery('.mapi-open-selector').css('display', 'none');
1526 jQuery('.mapi-close-selector-link').css('display', 'inline');
1527 jQuery('.advads-adsense-code').css('display', 'none');
1528 jQuery('#remote-ad-unsupported-ad-type').css('display', 'none');
1529
1530 AdvancedAdsAdmin.AdImporter.highlightSelectedRowInExternalAdsList(
1531 network.hideIdle
1532 );
1533
1534 const SNT = network.getCustomInputs();
1535 SNT.css('display', 'none');
1536
1537 jQuery('#mapi-wrap').css('display', 'block');
1538
1539 if (!network.fetchedExternalAds) {
1540 network.fetchedExternalAds = true;
1541 const nbUnits = jQuery(
1542 '#mapi-table-wrap tbody tr[data-slotid]'
1543 ).length;
1544 if (nbUnits == 0) {
1545 //usually we start with a preloaded list.
1546 //only reload, when the count is zero (true for new accounts).
1547 AdvancedAdsAdmin.AdImporter.refreshAds();
1548 }
1549 }
1550 jQuery('#wpwrap').trigger('advads-mapi-adlist-opened');
1551 },
1552 /**
1553 * will be called every time the ad type is changed.
1554 * required for onBlur detection
1555 */
1556 onChangedAdType() {
1557 if (AdvancedAdsAdmin.AdImporter.adNetwork) {
1558 AdvancedAdsAdmin.AdImporter.adNetwork.onBlur();
1559 AdvancedAdsAdmin.AdImporter.adNetwork = null;
1560 }
1561 },
1562 setup(adNetwork) {
1563 AdvancedAdsAdmin.AdImporter.adNetwork = adNetwork;
1564 adNetwork.onSelected();
1565 if (AdvancedAdsAdmin.AdImporter.isSetup) {
1566 AdvancedAdsAdmin.AdImporter.highlightSelectedRowInExternalAdsList();
1567 return;
1568 }
1569 AdvancedAdsAdmin.AdImporter.isSetup = true;
1570
1571 jQuery(document).on('click', '.prevent-default', function (ev) {
1572 ev.preventDefault();
1573 });
1574
1575 // handle clicks for the "insert new ... code" anchor
1576 jQuery(document).on('click', '.mapi-insert-code', function (e) {
1577 e.preventDefault();
1578 jQuery('#remote-ad-unsupported-ad-type').css('display', 'none');
1579 jQuery('.advads-adsense-code').show();
1580 jQuery('.mapi-open-selector').css('display', 'inline');
1581 jQuery('.mapi-close-selector-link').css('display', 'inline');
1582 jQuery('.mapi-insert-code').css('display', 'none');
1583 jQuery('#mapi-wrap').css('display', 'none');
1584 const SNT =
1585 AdvancedAdsAdmin.AdImporter.adNetwork.getCustomInputs();
1586 SNT.css('display', 'none');
1587 });
1588
1589 // handle clicks for the "get ad code from your linked account" anchor
1590 jQuery(document).on('click', '.mapi-open-selector a', function () {
1591 AdvancedAdsAdmin.AdImporter.openExternalAdsList();
1592 });
1593
1594 // the close button of the ad unit list
1595 jQuery(document).on(
1596 'click',
1597 '#mapi-close-selector,.mapi-close-selector-link',
1598 function () {
1599 jQuery('#remote-ad-unsupported-ad-type').css(
1600 'display',
1601 'none'
1602 );
1603 AdvancedAdsAdmin.AdImporter.manualSetup();
1604 }
1605 );
1606
1607 //the individual rows of the ad units may contain elements with the mapiaction class
1608 jQuery(document).on('click', '.mapiaction', function (ev) {
1609 const action = jQuery(this).attr('data-mapiaction');
1610 switch (action) {
1611 case 'updateList':
1612 AdvancedAdsAdmin.AdImporter.refreshAds();
1613 break;
1614 case 'getCode':
1615 if (jQuery(this).hasClass('disabled')) {
1616 break;
1617 }
1618 var slotId = jQuery(this).attr('data-slotid');
1619 AdvancedAdsAdmin.AdImporter.adNetwork.selectAdFromList(
1620 slotId
1621 );
1622 break;
1623 case 'updateCode':
1624 var slotId = jQuery(this).attr('data-slotid');
1625 AdvancedAdsAdmin.AdImporter.adNetwork.updateAdFromList(
1626 slotId
1627 );
1628 break;
1629 case 'toggleidle':
1630 if (
1631 'undefined' !==
1632 typeof AdvancedAdsAdmin.AdImporter.adNetwork
1633 .getMapiAction &&
1634 'function' ===
1635 typeof AdvancedAdsAdmin.AdImporter.adNetwork.getMapiAction(
1636 'toggleidle'
1637 )
1638 ) {
1639 AdvancedAdsAdmin.AdImporter.adNetwork.getMapiAction(
1640 'toggleidle'
1641 )(ev, this);
1642 } else {
1643 AdvancedAdsAdmin.AdImporter.adNetwork.hideIdle =
1644 !AdvancedAdsAdmin.AdImporter.adNetwork.hideIdle;
1645 AdvancedAdsAdmin.AdImporter.toggleIdleAds(
1646 AdvancedAdsAdmin.AdImporter.adNetwork.hideIdle
1647 );
1648 const $inactiveNotice = jQuery(
1649 '#mapi-notice-inactive'
1650 );
1651 if ($inactiveNotice.length) {
1652 $inactiveNotice.toggle(
1653 AdvancedAdsAdmin.AdImporter.adNetwork
1654 .hideIdle
1655 );
1656 }
1657 break;
1658 }
1659 default:
1660 }
1661 });
1662
1663 AdvancedAdsAdmin.AdImporter.adNetwork.onDomReady();
1664 // AdvancedAdsAdmin.AdImporter.openExternalAdsList();
1665 },
1666
1667 /**
1668 * call this method to display the manual setup (if available for the current ad network)
1669 * this method is an equivalent to the close ad list button.
1670 */
1671 manualSetup() {
1672 jQuery('.mapi-open-selector,.advads-adsense-show-code').css(
1673 'display',
1674 'inline'
1675 );
1676 jQuery('.mapi-insert-code').css('display', 'inline');
1677 jQuery('.mapi-close-selector-link').css('display', 'none');
1678 jQuery('#mapi-wrap').css('display', 'none');
1679
1680 const SNT = AdvancedAdsAdmin.AdImporter.adNetwork.getCustomInputs();
1681 SNT.css('display', 'block');
1682 // hide custom layout key if type is not in-feed
1683 if (jQuery('#unit-type').val() !== 'in-feed') {
1684 jQuery('.advads-adsense-layout-key')
1685 .css('display', 'none')
1686 .next('div')
1687 .css('display', 'none');
1688 }
1689 AdvancedAdsAdmin.AdImporter.adNetwork.onManualSetup();
1690 },
1691
1692 setRemoteErrorMessage(msg) {
1693 if (!msg) jQuery('#remote-ad-code-msg').empty();
1694 else jQuery('#remote-ad-code-msg').html(msg);
1695 },
1696
1697 /**
1698 * legacy method
1699 */
1700 closeAdSelector() {
1701 // close the ad unit selector
1702 setTimeout(function () {
1703 jQuery('#mapi-wrap').animate({ height: 0 }, 360, function () {
1704 jQuery('.mapi-open-selector,.advads-adsense-show-code').css(
1705 'display',
1706 'inline'
1707 );
1708 jQuery('.mapi-close-selector-link').css('display', 'none');
1709 jQuery('#mapi-wrap').css({
1710 display: 'none',
1711 height: 'auto',
1712 });
1713 const SNT =
1714 AdvancedAdsAdmin.AdImporter.adNetwork.getCustomInputs();
1715 SNT.css('display', 'block');
1716 });
1717 }, 80);
1718 },
1719
1720 /**
1721 * legacy method
1722 * updates the UI, (call if the selected unit is supported)
1723 * @param slotID
1724 */
1725 unitIsNotSupported(slotID) {
1726 jQuery('#remote-ad-unsupported-ad-type').css('display', 'block');
1727 AdsenseMAPI.unsupportedUnits[slotID] = 1;
1728 jQuery('#unit-code').val('');
1729 jQuery('#unit-type').val('normal');
1730 jQuery('#ad-layout-key').val('');
1731 jQuery('tr[data-slotid^="ca-"]').removeClass('selected error');
1732 const $selectedRow = jQuery('tr[data-slotid="' + slotID + '"]');
1733 $selectedRow.addClass('selected error').css('background-color', '');
1734 this.scrollToSelectedRow($selectedRow);
1735 },
1736
1737 /**
1738 * legacy method
1739 * updates the UI, (call if the selected unit is NOT supported)
1740 * @param slotID
1741 */
1742 unitIsSupported(slotID) {
1743 jQuery('#remote-ad-unsupported-ad-type').css('display', 'none');
1744 if ('undefined' !== typeof AdsenseMAPI.unsupportedUnits[slotID]) {
1745 delete AdsenseMAPI.unsupportedUnits[slotID];
1746 }
1747 jQuery(
1748 'i[data-mapiaction="getCode"][data-slotid="' + slotID + '"]'
1749 ).removeClass('disabled');
1750 if (jQuery('tr[data-slotid="' + slotID + '"] .unittype a').length) {
1751 var td = jQuery('tr[data-slotid="' + slotID + '"] .unittype');
1752 var content = jQuery(
1753 'tr[data-slotid="' + slotID + '"] .unittype a'
1754 ).attr('data-type');
1755 td.text(content);
1756 }
1757 if (jQuery('tr[data-slotid="' + slotID + '"] .unitsize a').length) {
1758 var td = jQuery('tr[data-slotid="' + slotID + '"] .unitsize');
1759 var content = jQuery(
1760 'tr[data-slotid="' + slotID + '"] .unitsize a'
1761 ).attr('data-size');
1762 td.text(content);
1763 }
1764 },
1765
1766 /**
1767 * legacy method
1768 * updates the UI, (call if the selected unit is NOT supported)
1769 * @param msg
1770 */
1771 emptyMapiSelector(msg) {
1772 const nag =
1773 '<div class="advads-notice-inline advads-error"><p>' +
1774 msg +
1775 '</p></div>';
1776 jQuery('#mapi-loading-overlay').css('display', 'none');
1777 jQuery('#mapi-wrap').html(jQuery(nag));
1778 },
1779
1780 /**
1781 * legacy method
1782 */
1783 refreshAds() {
1784 const adNetwork = AdvancedAdsAdmin.AdImporter.adNetwork;
1785 jQuery('#mapi-loading-overlay').css('display', 'block');
1786 jQuery.ajax({
1787 type: 'post',
1788 url: ajaxurl,
1789 data: adNetwork.getRefreshAdsParameters(),
1790 success(response, status, XHR) {
1791 if ('undefined' !== typeof response.html) {
1792 jQuery('#mapi-wrap').replaceWith(jQuery(response.html));
1793 AdvancedAdsAdmin.AdImporter.openExternalAdsList();
1794 } else if ('undefined' !== typeof response.msg) {
1795 AdvancedAdsAdmin.AdImporter.emptyMapiSelector(
1796 response.msg
1797 );
1798 }
1799 if ('undefined' !== typeof response.raw) {
1800 console.log(response.raw);
1801 }
1802 jQuery('#mapi-loading-overlay').css('display', 'none');
1803 },
1804 error(request, status, err) {
1805 jQuery('#mapi-loading-overlay').css('display', 'none');
1806 },
1807 });
1808 },
1809
1810 toggleIdleAds(hide) {
1811 if ('undefined' === typeof hide) {
1812 hide = true;
1813 }
1814 AdvancedAdsAdmin.AdImporter.highlightSelectedRowInExternalAdsList(
1815 hide
1816 );
1817 },
1818 };
1819
1820 /**
1821 * The "abstract" base class for handling external ad units
1822 * Every ad unit will provide you with a set of methods to control the GUI and trigger requests to the server
1823 * while editing an ad that is backed by this network. The main logic takes place in admin/assets/admin.js,
1824 * and the methods in this class are the ones that needed abstraction, depending on the ad network. When you
1825 * need new network-dependant features in the frontend, this is the place to add new methods.
1826 *
1827 * An AdvancedAdsAdNetwork uses these fields:
1828 * id string The identifier, that is used for this network. Must match with the one used in the PHP code of Advanced Ads
1829 * units array Holds the ad units of this network.
1830 * vars map These are the variables that were transmitted from the underlying PHP class (method: append_javascript_data)
1831 * hideIdle Remembers, wheter idle ads should be displayed in the list;
1832 * fetchedExternalAds Remembers if the external ads list has already been loaded to prevent unneccesary requests
1833 */
1834 class AdvancedAdsAdNetwork {
1835 /**
1836 * @param id string representing the id of this network. has to match the identifier of the PHP class
1837 */
1838 constructor(id) {
1839 this.id = id;
1840 this.units = [];
1841 this.vars = window[id + 'AdvancedAdsJS'];
1842 this.hideIdle = true;
1843 this.fetchedExternalAds = false;
1844 }
1845
1846 /**
1847 * will be called when an ad network is selected (ad type in edit ad)
1848 */
1849 onSelected() {
1850 console.error('Please override onSelected.');
1851 }
1852
1853 /**
1854 * will be called when an ad network deselected (ad type in edit ad)
1855 */
1856 onBlur() {
1857 console.error('Please override onBlur.');
1858 }
1859
1860 /**
1861 * opens the selector list containing the external ad units
1862 */
1863 openSelector() {
1864 console.error('Please override openSelector.');
1865 }
1866
1867 /**
1868 * returns the network specific id of the currently selected ad unit
1869 */
1870 getSelectedId() {
1871 console.error('Please override getSelectedId.');
1872 }
1873
1874 /**
1875 * will be called when an external ad unit has been selected from the selector list
1876 * @param slotId string the external ad unit id
1877 */
1878 selectAdFromList(slotId) {
1879 console.error('Please override selectAdFromList.');
1880 }
1881
1882 /**
1883 * will be called when an the update button of an external ad unit has been clicked
1884 * TODO: decide wheter to remove this method. not required anymore - the button was removed.
1885 * @param slotId string the external ad unit id
1886 */
1887 updateAdFromList(slotId) {
1888 console.error('Please override updateAdFromList.');
1889 }
1890
1891 /**
1892 * return the POST params that you want to send to the server when requesting a refresh of the external ad units
1893 * (like nonce and action and everything else that is required)
1894 */
1895 getRefreshAdsParameters() {
1896 console.error('Please override getRefreshAdsParameters.');
1897 }
1898
1899 /**
1900 * return the jquery objects for all the custom html elements of this ad type
1901 */
1902 getCustomInputs() {
1903 console.error('Please override getCustomInputs.');
1904 }
1905
1906 /**
1907 * what to do when the DOM is ready
1908 */
1909 onDomReady() {
1910 console.error('Please override onDomReady.');
1911 }
1912
1913 /**
1914 * when you need custom behaviour for ad networks that support manual setup of ad units, override this method
1915 */
1916 onManualSetup() {
1917 //no console logging. this is optional
1918 }
1919 }
1920
1921 class AdvancedAdsExternalAdUnit {}
1922
1923 /**
1924 * todo: this looks like something we could use in general, but where to put it?
1925 */
1926 jQuery(document).ready(function () {
1927 // delete an existing row by removing the parent tr tag
1928 // todo: this could be moved to a general file
1929 jQuery(document).on('click', '.advads-tr-remove', function () {
1930 jQuery(this).closest('tr').remove();
1931 });
1932 });
1933
1934 /**
1935 * If the jQuery function `escapeSelector` does not exist (add in jQuery 3.0) then add it.
1936 */
1937 if (typeof jQuery.escapeSelector === 'undefined') {
1938 jQuery.escapeSelector = function (selector) {
1939 return selector.replace(
1940 // phpcs:ignore WordPress.WhiteSpace.OperatorSpacing.NoSpaceBefore,WordPress.WhiteSpace.OperatorSpacing.NoSpaceAfter,WordPress.WhiteSpace.OperatorSpacing.NoSpaceBeforeAmp,WordPress.WhiteSpace.OperatorSpacing.NoSpaceAfterAmp -- PHPCS incorrectly reports whitespace errors for this regex
1941 /([$%&()*+,./:;<=>?@\[\\\]^{|}~])/g,
1942 '\\$1'
1943 );
1944 };
1945 }
1946