@@ -375,8 +375,10 @@ function apbctReplaceInputsValuesFromOtherForm(formSource, formTarget) {
375375 } ) ;
376376 } ) ;
377377}
378+
378379// clear protected iframes list
379- apbctLocalStorage . set ( 'apbct_iframes_protected' , [ ] ) ;
380+ ctProtectOutsideFunctionalClearLocalStorage ( ) ;
381+
380382window . addEventListener ( 'load' , function ( ) {
381383 if ( ! + ctPublic . settings__forms__check_external ) {
382384 return ;
@@ -386,120 +388,141 @@ window.addEventListener('load', function() {
386388 ctProtectExternal ( ) ;
387389 catchDynamicRenderedForm ( ) ;
388390 catchNextendSocialLoginForm ( ) ;
389- ctProtectOutsideIframe ( ) ;
391+ ctProtectOutsideFunctionalOnTagsType ( 'div' ) ;
392+ ctProtectOutsideFunctionalOnTagsType ( 'iframe' ) ;
390393 } , 2000 ) ;
391394
392395 ctProtectKlaviyoForm ( ) ;
393396} ) ;
394397
395- /**
396- * Protect klaviyo forms
397- */
398- function ctProtectKlaviyoForm ( ) {
399- if ( ! document . querySelector ( 'link[rel="dns-prefetch"][href="//static.klaviyo.com"]' ) ) {
400- return ;
401- }
402-
403- let i = setInterval ( ( ) => {
404- const klaviyoForms = document . querySelectorAll ( 'form.klaviyo-form' ) ;
405- if ( klaviyoForms . length ) {
406- clearInterval ( i ) ;
407- klaviyoForms . forEach ( ( form , index ) => {
408- apbctProcessExternalFormKlaviyo ( form , index , document ) ;
409- } ) ;
410- }
411- } , 500 ) ;
412- }
398+ let ctProtectOutsideFunctionalCheckPerformed ;
413399
414400/**
415- * Protect klaviyo forms
416- * @param {HTMLElement } form
417- * @param {int } iterator
418- * @param {HTMLElement } documentObject
401+ * Protect outside functional. Tags div and iframe supported.
402+ * @param {string } tagType
419403 */
420- function apbctProcessExternalFormKlaviyo ( form , iterator , documentObject ) {
421- const btn = form . querySelector ( 'button[type="button"].needsclick' ) ;
422- if ( ! btn ) {
423- return ;
404+ function ctProtectOutsideFunctionalOnTagsType ( tagType ) {
405+ let entityTagsAny = document . querySelectorAll ( tagType ) ;
406+ if ( entityTagsAny . length > 0 ) {
407+ entityTagsAny . forEach ( function ( entity ) {
408+ let protectedType = ctProtectOutsideFunctionalIsTagIntegrated ( entity ) ;
409+ if ( false !== protectedType ) {
410+ let lsStorageName = 'apbct_outside_functional_protected_tags__' + protectedType ;
411+ let lsUniqueName = entity . id !== '' ? entity . id : false ;
412+ lsUniqueName = false === lsUniqueName && entity . className !== '' ? entity . className : lsUniqueName ;
413+ // todo we can not protect any entity that has no id and class :(
414+ // pass if is already protected
415+ if (
416+ false === lsUniqueName ||
417+ (
418+ false !== apbctLocalStorage . get ( lsStorageName ) &&
419+ apbctLocalStorage . get ( lsStorageName ) . length > 0 &&
420+ apbctLocalStorage . get ( lsStorageName ) [ 0 ] . indexOf ( lsUniqueName ) !== - 1
421+ )
422+ ) {
423+ return ;
424+ }
425+ ctProtectOutsideFunctionalHandler ( entity , lsStorageName , lsUniqueName ) ;
426+ }
427+ } ) ;
424428 }
425- btn . disabled = true ;
426-
427- const forceAction = document . createElement ( 'input' ) ;
428- forceAction . name = 'action' ;
429- forceAction . value = 'cleantalk_force_ajax_check' ;
430- forceAction . type = 'hidden' ;
431- form . appendChild ( forceAction ) ;
432-
433- let cover = document . createElement ( 'div' ) ;
434- cover . id = 'apbct-klaviyo-cover' ;
435- cover . style . width = '100%' ;
436- cover . style . height = '100%' ;
437- cover . style . background = 'black' ;
438- cover . style . opacity = 0 ;
439- cover . style . position = 'absolute' ;
440- cover . style . top = 0 ;
441- cover . style . cursor = 'pointer' ;
442- cover . onclick = function ( e ) {
443- sendAjaxCheckingFormData ( form ) ;
444- } ;
445- btn . parentNode . style . position = 'relative' ;
446- btn . parentNode . appendChild ( cover ) ;
447429}
448430
449431/**
450- * Protect forms placed in iframe with outside src
432+ * Check if the entity must be protected
433+ *
434+ * @param {HTMLElement } entity
435+ * @return {string|false }
451436 */
452- function ctProtectOutsideIframe ( ) {
453- let iframes = document . querySelectorAll ( 'iframe' ) ;
454- if ( iframes . length > 0 ) {
455- iframes . forEach ( function ( iframe ) {
456- if ( iframe . src . indexOf ( 'form.typeform.com' ) !== - 1 ||
457- iframe . src . indexOf ( 'forms.zohopublic.com' ) !== - 1 ||
458- iframe . src . indexOf ( 'link.surepathconnect.com' ) !== - 1 ||
459- iframe . src . indexOf ( 'hello.dubsado.com' ) !== - 1 ||
460- iframe . classList . contains ( 'hs-form-iframe' ) ||
461- ( iframe . src . indexOf ( 'facebook.com' ) !== - 1 && iframe . src . indexOf ( 'plugins/comments.php' ) !== - 1 ) ||
462- iframe . id . indexOf ( 'chatway_widget_app' ) !== - 1
437+ function ctProtectOutsideFunctionalIsTagIntegrated ( entity ) {
438+ if ( entity . tagName !== undefined ) {
439+ if ( entity . tagName === 'IFRAME' ) {
440+ if ( entity . src . indexOf ( 'form.typeform.com' ) !== - 1 ||
441+ entity . src . indexOf ( 'forms.zohopublic.com' ) !== - 1 ||
442+ entity . src . indexOf ( 'link.surepathconnect.com' ) !== - 1 ||
443+ entity . src . indexOf ( 'hello.dubsado.com' ) !== - 1 ||
444+ entity . classList . contains ( 'hs-form-iframe' ) ||
445+ ( entity . src . indexOf ( 'facebook.com' ) !== - 1 && entity . src . indexOf ( 'plugins/comments.php' ) !== - 1 ) ||
446+ entity . id . indexOf ( 'chatway_widget_app' ) !== - 1
463447 ) {
464- // pass if is already protected
465- if ( false !== apbctLocalStorage . get ( 'apbct_iframes_protected' ) &&
466- apbctLocalStorage . get ( 'apbct_iframes_protected' ) . length > 0 &&
467- typeof iframe . id !== 'undefined' &&
468- apbctLocalStorage . get ( 'apbct_iframes_protected' ) . indexOf [ iframe . id ] !== - 1
469- ) {
470- return ;
471- }
472- ctProtectOutsideIframeHandler ( iframe ) ;
448+ return entity . tagName ;
473449 }
474- } ) ;
450+ }
451+ if ( entity . tagName === 'DIV' ) {
452+ if (
453+ entity . className . indexOf ( 'Window__Content-sc' ) !== - 1 || // all in one chat widget
454+ entity . className . indexOf ( 'WidgetBackground__Content-sc' ) !== - 1 // all in one contact form widget
455+ ) {
456+ return entity . tagName ;
457+ }
458+ }
475459 }
460+ return false ;
476461}
477462
478- let ctProtectOutsideIframeCheck ;
479463/**
480464 * Protect forms placed in iframe with outside src handler
481- * @param {HTMLElement } iframe
465+ *
466+ * @param {HTMLElement } entity
467+ * @param {string } lsStorageName
468+ * @param {string|false } lsUniqueName
482469 */
483- function ctProtectOutsideIframeHandler ( iframe ) {
470+ function ctProtectOutsideFunctionalHandler ( entity , lsStorageName , lsUniqueName ) {
484471 let originParentPosition = null ;
485- let iframeParent = null ;
472+ let entityParent = null ;
486473
487- if ( iframe . parentNode !== undefined ) {
488- iframeParent = iframe . parentNode ;
474+ if ( entity . parentNode !== undefined ) {
475+ entityParent = entity . parentNode ;
489476 } else {
490477 return ;
491478 }
492479
480+ if (
481+ entityParent . style !== undefined &&
482+ entityParent . style !== null &&
483+ entityParent . style . position !== undefined
484+ ) {
485+ entityParent . style . position = originParentPosition ;
486+ } else {
487+ entityParent . style . position = 'relative' ;
488+ }
489+ entityParent . appendChild ( ctProtectOutsideFunctionalGenerateCover ( ) ) ;
490+ let entitiesProtected = apbctLocalStorage . get ( lsStorageName ) ;
491+ if ( false === entitiesProtected ) {
492+ entitiesProtected = [ ] ;
493+ }
494+ if ( lsUniqueName ) {
495+ entitiesProtected . push ( lsUniqueName ) ;
496+ apbctLocalStorage . set ( lsStorageName , entitiesProtected ) ;
497+ }
498+ }
499+
500+ /**
501+ * Clear local storage for OutsideFunctional protection
502+ *
503+ */
504+ function ctProtectOutsideFunctionalClearLocalStorage ( ) {
505+ apbctLocalStorage . set ( 'apbct_outside_functional_protected_tags__DIV' , [ ] ) ;
506+ apbctLocalStorage . set ( 'apbct_outside_functional_protected_tags__IFRAME' , [ ] ) ;
507+ }
508+
509+ /**
510+ * Generate cover for outside functional protection
511+ *
512+ * @return {HTMLElement } cover
513+ */
514+ function ctProtectOutsideFunctionalGenerateCover ( ) {
493515 let cover = document . createElement ( 'div' ) ;
494516 cover . style . width = '100%' ;
495517 cover . style . height = '100%' ;
496518 cover . style . background = 'black' ;
497519 cover . style . opacity = 0 ;
498520 cover . style . position = 'absolute' ;
499521 cover . style . top = 0 ;
522+ cover . style . zIndex = 9999 ;
500523 cover . setAttribute ( 'name' , 'apbct_cover' ) ;
501524 cover . onclick = function ( e ) {
502- if ( ctProtectOutsideIframeCheck === undefined ) {
525+ if ( ctProtectOutsideFunctionalCheckPerformed === undefined ) {
503526 let currentDiv = e . currentTarget ;
504527 currentDiv . style . opacity = 0.5 ;
505528 let preloader = document . createElement ( 'div' ) ;
@@ -529,22 +552,22 @@ function ctProtectOutsideIframeHandler(iframe) {
529552 {
530553 async : false ,
531554 callback : function ( result ) {
532- ctProtectOutsideIframeCheck = true ;
555+ ctProtectOutsideFunctionalCheckPerformed = true ;
533556 let callbackError = false ;
534557 if (
535558 typeof result !== 'object' ||
536559 ! result . hasOwnProperty ( 'apbct' ) ||
537560 ! result . apbct . hasOwnProperty ( 'blocked' )
538561 ) {
539- console . warn ( 'APBCT outside iframe check error, skip check.' ) ;
562+ console . warn ( 'APBCT outside functional check error, skip check.' ) ;
540563 callbackError = true ;
541564 }
542565 const comment = result . apbct . comment !== undefined ?
543566 result . apbct . comment :
544567 'Blocked by CleanTalk Anti-Spam' ;
545568 if ( result . apbct . blocked === false || callbackError ) {
546- document . querySelectorAll ( 'div.apbct-iframe-preloader ' ) . forEach ( function ( el ) {
547- el . parentNode . remove ( ) ;
569+ document . querySelectorAll ( 'div[name="apbct_cover"] ' ) . forEach ( function ( el ) {
570+ el . remove ( ) ;
548571 } ) ;
549572 } else {
550573 document . querySelectorAll ( '.apbct-iframe-preloader-text' ) . forEach ( ( el ) => {
@@ -559,24 +582,61 @@ function ctProtectOutsideIframeHandler(iframe) {
559582 ) ;
560583 }
561584 } ;
562- if (
563- iframeParent . style !== undefined &&
564- iframeParent . style !== null &&
565- iframeParent . style . position !== undefined
566- ) {
567- iframeParent . style . position = originParentPosition ;
568- } else {
569- iframeParent . style . position = 'relative' ;
570- }
571- iframeParent . appendChild ( cover ) ;
572- let iframes = apbctLocalStorage . get ( 'apbct_iframes_protected' ) ;
573- if ( false === iframes ) {
574- iframes = [ ] ;
585+ return cover ;
586+ }
587+
588+ /**
589+ * Protect klaviyo forms
590+ */
591+ function ctProtectKlaviyoForm ( ) {
592+ if ( ! document . querySelector ( 'link[rel="dns-prefetch"][href="//static.klaviyo.com"]' ) ) {
593+ return ;
575594 }
576- if ( typeof iframe . id !== 'undefined' ) {
577- iframes . push ( iframe . id ) ;
578- apbctLocalStorage . set ( 'apbct_iframes_protected' , iframes ) ;
595+
596+ let i = setInterval ( ( ) => {
597+ const klaviyoForms = document . querySelectorAll ( 'form.klaviyo-form' ) ;
598+ if ( klaviyoForms . length ) {
599+ clearInterval ( i ) ;
600+ klaviyoForms . forEach ( ( form , index ) => {
601+ apbctProcessExternalFormKlaviyo ( form , index , document ) ;
602+ } ) ;
603+ }
604+ } , 500 ) ;
605+ }
606+
607+ /**
608+ * Protect klaviyo forms
609+ * @param {HTMLElement } form
610+ * @param {int } iterator
611+ * @param {HTMLElement } documentObject
612+ */
613+ function apbctProcessExternalFormKlaviyo ( form , iterator , documentObject ) {
614+ const btn = form . querySelector ( 'button[type="button"].needsclick' ) ;
615+ if ( ! btn ) {
616+ return ;
579617 }
618+ btn . disabled = true ;
619+
620+ const forceAction = document . createElement ( 'input' ) ;
621+ forceAction . name = 'action' ;
622+ forceAction . value = 'cleantalk_force_ajax_check' ;
623+ forceAction . type = 'hidden' ;
624+ form . appendChild ( forceAction ) ;
625+
626+ let cover = document . createElement ( 'div' ) ;
627+ cover . id = 'apbct-klaviyo-cover' ;
628+ cover . style . width = '100%' ;
629+ cover . style . height = '100%' ;
630+ cover . style . background = 'black' ;
631+ cover . style . opacity = 0 ;
632+ cover . style . position = 'absolute' ;
633+ cover . style . top = 0 ;
634+ cover . style . cursor = 'pointer' ;
635+ cover . onclick = function ( e ) {
636+ sendAjaxCheckingFormData ( form ) ;
637+ } ;
638+ btn . parentNode . style . position = 'relative' ;
639+ btn . parentNode . appendChild ( cover ) ;
580640}
581641
582642/**
0 commit comments